(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 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. * * The views and conclusions contained in the software and documentation * are those of the authors and should not be interpreted as representing * official policies, either expressed or implied, of the authors. */ /** * Expand the S-box tables. * * @private */ '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 precompute = function precompute() { var tables = [[[], [], [], [], []], [[], [], [], [], []]]; var encTable = tables[0]; var decTable = tables[1]; var sbox = encTable[4]; var sboxInv = decTable[4]; var i = undefined; var x = undefined; var xInv = undefined; var d = []; var th = []; var x2 = undefined; var x4 = undefined; var x8 = undefined; var s = undefined; var tEnc = undefined; var tDec = undefined; // Compute double and third tables for (i = 0; i < 256; i++) { th[(d[i] = i << 1 ^ (i >> 7) * 283) ^ i] = i; } for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) { // Compute sbox s = xInv ^ xInv << 1 ^ xInv << 2 ^ xInv << 3 ^ xInv << 4; s = s >> 8 ^ s & 255 ^ 99; sbox[x] = s; sboxInv[s] = x; // Compute MixColumns x8 = d[x4 = d[x2 = d[x]]]; tDec = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100; tEnc = d[s] * 0x101 ^ s * 0x1010100; for (i = 0; i < 4; i++) { encTable[i][x] = tEnc = tEnc << 24 ^ tEnc >>> 8; decTable[i][s] = tDec = tDec << 24 ^ tDec >>> 8; } } // Compactify. Considerable speedup on Firefox. for (i = 0; i < 5; i++) { encTable[i] = encTable[i].slice(0); decTable[i] = decTable[i].slice(0); } return tables; }; var aesTables = null; /** * Schedule out an AES key for both encryption and decryption. This * is a low-level class. Use a cipher mode to do bulk encryption. * * @class AES * @param key {Array} The key as an array of 4, 6 or 8 words. */ var AES = (function () { function AES(key) { _classCallCheck(this, AES); /** * The expanded S-box and inverse S-box tables. These will be computed * on the client so that we don't have to send them down the wire. * * There are two tables, _tables[0] is for encryption and * _tables[1] is for decryption. * * The first 4 sub-tables are the expanded S-box with MixColumns. The * last (_tables[01][4]) is the S-box itself. * * @private */ // if we have yet to precompute the S-box tables // do so now if (!aesTables) { aesTables = precompute(); } // then make a copy of that object for use this._tables = [[aesTables[0][0].slice(), aesTables[0][1].slice(), aesTables[0][2].slice(), aesTables[0][3].slice(), aesTables[0][4].slice()], [aesTables[1][0].slice(), aesTables[1][1].slice(), aesTables[1][2].slice(), aesTables[1][3].slice(), aesTables[1][4].slice()]]; var i = undefined; var j = undefined; var tmp = undefined; var encKey = undefined; var decKey = undefined; var sbox = this._tables[0][4]; var decTable = this._tables[1]; var keyLen = key.length; var rcon = 1; if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) { throw new Error('Invalid aes key size'); } encKey = key.slice(0); decKey = []; this._key = [encKey, decKey]; // schedule encryption keys for (i = keyLen; i < 4 * keyLen + 28; i++) { tmp = encKey[i - 1]; // apply sbox if (i % keyLen === 0 || keyLen === 8 && i % keyLen === 4) { tmp = sbox[tmp >>> 24] << 24 ^ sbox[tmp >> 16 & 255] << 16 ^ sbox[tmp >> 8 & 255] << 8 ^ sbox[tmp & 255]; // shift rows and add rcon if (i % keyLen === 0) { tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24; rcon = rcon << 1 ^ (rcon >> 7) * 283; } } encKey[i] = encKey[i - keyLen] ^ tmp; } // schedule decryption keys for (j = 0; i; j++, i--) { tmp = encKey[j & 3 ? i : i - 4]; if (i <= 4 || j < 4) { decKey[j] = tmp; } else { decKey[j] = decTable[0][sbox[tmp >>> 24]] ^ decTable[1][sbox[tmp >> 16 & 255]] ^ decTable[2][sbox[tmp >> 8 & 255]] ^ decTable[3][sbox[tmp & 255]]; } } } /** * Decrypt 16 bytes, specified as four 32-bit words. * * @param {Number} encrypted0 the first word to decrypt * @param {Number} encrypted1 the second word to decrypt * @param {Number} encrypted2 the third word to decrypt * @param {Number} encrypted3 the fourth word to decrypt * @param {Int32Array} out the array to write the decrypted words * into * @param {Number} offset the offset into the output array to start * writing results * @return {Array} The plaintext. */ _createClass(AES, [{ key: 'decrypt', value: function decrypt(encrypted0, encrypted1, encrypted2, encrypted3, out, offset) { var key = this._key[1]; // state variables a,b,c,d are loaded with pre-whitened data var a = encrypted0 ^ key[0]; var b = encrypted3 ^ key[1]; var c = encrypted2 ^ key[2]; var d = encrypted1 ^ key[3]; var a2 = undefined; var b2 = undefined; var c2 = undefined; // key.length === 2 ? var nInnerRounds = key.length / 4 - 2; var i = undefined; var kIndex = 4; var table = this._tables[1]; // load up the tables var table0 = table[0]; var table1 = table[1]; var table2 = table[2]; var table3 = table[3]; var sbox = table[4]; // Inner rounds. Cribbed from OpenSSL. for (i = 0; i < nInnerRounds; i++) { a2 = table0[a >>> 24] ^ table1[b >> 16 & 255] ^ table2[c >> 8 & 255] ^ table3[d & 255] ^ key[kIndex]; b2 = table0[b >>> 24] ^ table1[c >> 16 & 255] ^ table2[d >> 8 & 255] ^ table3[a & 255] ^ key[kIndex + 1]; c2 = table0[c >>> 24] ^ table1[d >> 16 & 255] ^ table2[a >> 8 & 255] ^ table3[b & 255] ^ key[kIndex + 2]; d = table0[d >>> 24] ^ table1[a >> 16 & 255] ^ table2[b >> 8 & 255] ^ table3[c & 255] ^ key[kIndex + 3]; kIndex += 4; a = a2;b = b2;c = c2; } // Last round. for (i = 0; i < 4; i++) { out[(3 & -i) + offset] = sbox[a >>> 24] << 24 ^ sbox[b >> 16 & 255] << 16 ^ sbox[c >> 8 & 255] << 8 ^ sbox[d & 255] ^ key[kIndex++]; a2 = a;a = b;b = c;c = d;d = a2; } } }]); return AES; })(); exports['default'] = AES; module.exports = exports['default']; },{}],2:[function(require,module,exports){ /** * @file async-stream.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 _stream = require('./stream'); var _stream2 = _interopRequireDefault(_stream); /** * A wrapper around the Stream class to use setTiemout * and run stream "jobs" Asynchronously * * @class AsyncStream * @extends Stream */ var AsyncStream = (function (_Stream) { _inherits(AsyncStream, _Stream); function AsyncStream() { _classCallCheck(this, AsyncStream); _get(Object.getPrototypeOf(AsyncStream.prototype), 'constructor', this).call(this, _stream2['default']); this.jobs = []; this.delay = 1; this.timeout_ = null; } /** * process an async job * * @private */ _createClass(AsyncStream, [{ key: 'processJob_', value: function processJob_() { this.jobs.shift()(); if (this.jobs.length) { this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay); } else { this.timeout_ = null; } } /** * push a job into the stream * * @param {Function} job the job to push into the stream */ }, { key: 'push', value: function push(job) { this.jobs.push(job); if (!this.timeout_) { this.timeout_ = setTimeout(this.processJob_.bind(this), this.delay); } } }]); return AsyncStream; })(_stream2['default']); exports['default'] = AsyncStream; module.exports = exports['default']; },{"./stream":5}],3:[function(require,module,exports){ /** * @file decrypter.js * * An asynchronous implementation of AES-128 CBC decryption with * PKCS#7 padding. */ '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 _aes = require('./aes'); var _aes2 = _interopRequireDefault(_aes); var _asyncStream = require('./async-stream'); var _asyncStream2 = _interopRequireDefault(_asyncStream); var _pkcs7 = require('pkcs7'); /** * Convert network-order (big-endian) bytes into their little-endian * representation. */ var ntoh = function ntoh(word) { return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24; }; /** * Decrypt bytes using AES-128 with CBC and PKCS#7 padding. * * @param {Uint8Array} encrypted the encrypted bytes * @param {Uint32Array} key the bytes of the decryption key * @param {Uint32Array} initVector the initialization vector (IV) to * use for the first round of CBC. * @return {Uint8Array} the decrypted bytes * * @see http://en.wikipedia.org/wiki/Advanced_Encryption_Standard * @see http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_.28CBC.29 * @see https://tools.ietf.org/html/rfc2315 */ var decrypt = function decrypt(encrypted, key, initVector) { // word-level access to the encrypted bytes var encrypted32 = new Int32Array(encrypted.buffer, encrypted.byteOffset, encrypted.byteLength >> 2); var decipher = new _aes2['default'](Array.prototype.slice.call(key)); // byte and word-level access for the decrypted output var decrypted = new Uint8Array(encrypted.byteLength); var decrypted32 = new Int32Array(decrypted.buffer); // temporary variables for working with the IV, encrypted, and // decrypted data var init0 = undefined; var init1 = undefined; var init2 = undefined; var init3 = undefined; var encrypted0 = undefined; var encrypted1 = undefined; var encrypted2 = undefined; var encrypted3 = undefined; // iteration variable var wordIx = undefined; // pull out the words of the IV to ensure we don't modify the // passed-in reference and easier access init0 = initVector[0]; init1 = initVector[1]; init2 = initVector[2]; init3 = initVector[3]; // decrypt four word sequences, applying cipher-block chaining (CBC) // to each decrypted block for (wordIx = 0; wordIx < encrypted32.length; wordIx += 4) { // convert big-endian (network order) words into little-endian // (javascript order) encrypted0 = ntoh(encrypted32[wordIx]); encrypted1 = ntoh(encrypted32[wordIx + 1]); encrypted2 = ntoh(encrypted32[wordIx + 2]); encrypted3 = ntoh(encrypted32[wordIx + 3]); // decrypt the block decipher.decrypt(encrypted0, encrypted1, encrypted2, encrypted3, decrypted32, wordIx); // XOR with the IV, and restore network byte-order to obtain the // plaintext decrypted32[wordIx] = ntoh(decrypted32[wordIx] ^ init0); decrypted32[wordIx + 1] = ntoh(decrypted32[wordIx + 1] ^ init1); decrypted32[wordIx + 2] = ntoh(decrypted32[wordIx + 2] ^ init2); decrypted32[wordIx + 3] = ntoh(decrypted32[wordIx + 3] ^ init3); // setup the IV for the next round init0 = encrypted0; init1 = encrypted1; init2 = encrypted2; init3 = encrypted3; } return decrypted; }; exports.decrypt = decrypt; /** * The `Decrypter` class that manages decryption of AES * data through `AsyncStream` objects and the `decrypt` * function * * @param {Uint8Array} encrypted the encrypted bytes * @param {Uint32Array} key the bytes of the decryption key * @param {Uint32Array} initVector the initialization vector (IV) to * @param {Function} done the function to run when done * @class Decrypter */ var Decrypter = (function () { function Decrypter(encrypted, key, initVector, done) { _classCallCheck(this, Decrypter); var step = Decrypter.STEP; var encrypted32 = new Int32Array(encrypted.buffer); var decrypted = new Uint8Array(encrypted.byteLength); var i = 0; this.asyncStream_ = new _asyncStream2['default'](); // split up the encryption job and do the individual chunks asynchronously this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted)); for (i = step; i < encrypted32.length; i += step) { initVector = new Uint32Array([ntoh(encrypted32[i - 4]), ntoh(encrypted32[i - 3]), ntoh(encrypted32[i - 2]), ntoh(encrypted32[i - 1])]); this.asyncStream_.push(this.decryptChunk_(encrypted32.subarray(i, i + step), key, initVector, decrypted)); } // invoke the done() callback when everything is finished this.asyncStream_.push(function () { // remove pkcs#7 padding from the decrypted bytes done(null, (0, _pkcs7.unpad)(decrypted)); }); } /** * a getter for step the maximum number of bytes to process at one time * * @return {Number} the value of step 32000 */ _createClass(Decrypter, [{ key: 'decryptChunk_', /** * @private */ value: function decryptChunk_(encrypted, key, initVector, decrypted) { return function () { var bytes = decrypt(encrypted, key, initVector); decrypted.set(bytes, encrypted.byteOffset); }; } }], [{ key: 'STEP', get: function get() { // 4 * 8000; return 32000; } }]); return Decrypter; })(); exports.Decrypter = Decrypter; exports['default'] = { Decrypter: Decrypter, decrypt: decrypt }; },{"./aes":1,"./async-stream":2,"pkcs7":210}],4:[function(require,module,exports){ /** * @file index.js * * Index module to easily import the primary components of AES-128 * decryption. Like this: * * ```js * import {Decrypter, decrypt, AsyncStream} from 'aes-decrypter'; * ``` */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _decrypter = require('./decrypter'); var _asyncStream = require('./async-stream'); var _asyncStream2 = _interopRequireDefault(_asyncStream); exports['default'] = { decrypt: _decrypter.decrypt, Decrypter: _decrypter.Decrypter, AsyncStream: _asyncStream2['default'] }; module.exports = exports['default']; },{"./async-stream":2,"./decrypter":3}],5:[function(require,module,exports){ /** * @file stream.js */ /** * A lightweight readable stream implemention that handles event dispatching. * * @class Stream */ '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 Stream = (function () { function Stream() { _classCallCheck(this, Stream); this.listeners = {}; } /** * Add a listener for a specified event type. * * @param {String} type the event name * @param {Function} listener the callback to be invoked when an event of * the specified type occurs */ _createClass(Stream, [{ key: 'on', value: function on(type, listener) { if (!this.listeners[type]) { this.listeners[type] = []; } this.listeners[type].push(listener); } /** * Remove a listener for a specified event type. * * @param {String} type the event name * @param {Function} listener a function previously registered for this * type of event through `on` * @return {Boolean} if we could turn it off or not */ }, { key: 'off', value: function off(type, listener) { var index = undefined; if (!this.listeners[type]) { return false; } index = this.listeners[type].indexOf(listener); this.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 {String} type the event name */ }, { key: 'trigger', value: function trigger(type) { var callbacks = undefined; var i = undefined; var length = undefined; var args = undefined; callbacks = this.listeners[type]; if (!callbacks) { return; } // 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 = Array.prototype.slice.call(arguments, 1); length = callbacks.length; for (i = 0; i < length; ++i) { callbacks[i].apply(this, args); } } } /** * Destroys the stream and cleans up. */ }, { key: 'dispose', value: function dispose() { this.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 {Stream} destination the stream that will receive all `data` events * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options */ }, { key: 'pipe', value: function pipe(destination) { this.on('data', function (data) { destination.push(data); }); } }]); return Stream; })(); exports['default'] = Stream; module.exports = exports['default']; },{}],6:[function(require,module,exports){ },{}],7:[function(require,module,exports){ /*! codem-isoboxer v0.2.7 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; 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: var s1 = this._raw.getUint16(this._cursor.offset - this._raw.byteOffset); var 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 var s1 = this._raw.getUint32(this._cursor.offset - this._raw.byteOffset); var 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._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() { this.version = this._readUint(16); this.revision_level = this._readUint(16); this.vendor = this._readUint(32); this.temporal_quality = this._readUint(32); this.spatial_quality = this._readUint(32); this.width = this._readUint(16); this.height = this._readUint(16); this.horizontal_resolution = this._readUint(32); this.vertical_resolution = this._readUint(32); this.data_size = this._readUint(32); this.frame_count = this._readUint(16); this.compressor_name = this._readUint(32); this.depth = this._readUint(16); this.color_table_id = this._readUint(16); }; // 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_versions = 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 = new DataView(this._raw.buffer, this._cursor.offset, this._raw.byteLength - (this._cursor.offset - this._offset)); }; // 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() { this.reserved1 = [this._readUint(32), this._readUint(32)]; this.channelcount = this._readUint(16); this.samplesize = this._readUint(16); this.pre_defined = this._readUint(16); this.reserved2 = this._readUint(16); this.sample_rate = this._readUint(32); }; // ISO/IEC 14496-12:2012 - 8.2.2 Movie Header Box ISOBox.prototype._boxParsers['mvhd'] = 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); } 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 (var i=0; i<9; i++) { this.matrix.push(this._readTemplate(32)); } this.pre_defined = []; for (var 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> 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 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._readUint(32); this.height = this._readUint(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> 6); u.push(0x80 | 63 & c); } else if (c < 0x10000) { u.push(0xE0 | c >> 12); u.push(0x80 | 63 & c >> 6); u.push(0x80 | 63 & c); } else { u.push(0xF0 | c >> 18); u.push(0x80 | 63 & c >> 12); u.push(0x80 | 63 & c >> 6); u.push(0x80 | 63 & c); } } return u; }; UTF8.decode = function (u) { var a = []; var i = 0; while (i < u.length) { var v = u[i++]; if (v < 0x80) { // no need to mask byte } else if (v < 0xE0) { v = (31 & v) << 6; v |= 63 & u[i++]; } else if (v < 0xF0) { v = (15 & v) << 12; v |= (63 & u[i++]) << 6; v |= 63 & u[i++]; } else { v = (7 & v) << 18; v |= (63 & u[i++]) << 12; v |= (63 & u[i++]) << 6; v |= 63 & u[i++]; } a.push(String.fromCharCode(v)); } return a.join(''); }; var BASE64 = {}; (function (T) { var encodeArray = function encodeArray(u) { var i = 0; var a = []; var n = 0 | u.length / 3; while (0 < n--) { var v = (u[i] << 16) + (u[i + 1] << 8) + u[i + 2]; i += 3; a.push(T.charAt(63 & v >> 18)); a.push(T.charAt(63 & v >> 12)); a.push(T.charAt(63 & v >> 6)); a.push(T.charAt(63 & v)); } if (2 == u.length - i) { var v = (u[i] << 16) + (u[i + 1] << 8); a.push(T.charAt(63 & v >> 18)); a.push(T.charAt(63 & v >> 12)); a.push(T.charAt(63 & v >> 6)); a.push('='); } else if (1 == u.length - i) { var v = u[i] << 16; a.push(T.charAt(63 & v >> 18)); a.push(T.charAt(63 & v >> 12)); a.push('=='); } return a.join(''); }; var R = (function () { var a = []; for (var i = 0; i < T.length; ++i) { a[T.charCodeAt(i)] = i; } a['='.charCodeAt(0)] = 0; return a; })(); var decodeArray = function decodeArray(s) { var i = 0; var u = []; var n = 0 | s.length / 4; while (0 < n--) { var v = (R[s.charCodeAt(i)] << 18) + (R[s.charCodeAt(i + 1)] << 12) + (R[s.charCodeAt(i + 2)] << 6) + R[s.charCodeAt(i + 3)]; u.push(255 & v >> 16); u.push(255 & v >> 8); u.push(255 & v); i += 4; } if (u) { if ('=' == s.charAt(i - 2)) { u.pop(); u.pop(); } else if ('=' == s.charAt(i - 1)) { u.pop(); } } return u; }; var ASCII = {}; ASCII.encode = function (s) { var u = []; for (var i = 0; i < s.length; ++i) { u.push(s.charCodeAt(i)); } return u; }; ASCII.decode = function (u) { for (var i = 0; i < s.length; ++i) { a[i] = String.fromCharCode(a[i]); } return a.join(''); }; BASE64.decodeArray = function (s) { var u = decodeArray(s); return new Uint8Array(u); }; BASE64.encodeASCII = function (s) { var u = ASCII.encode(s); return encodeArray(u); }; BASE64.decodeASCII = function (s) { var a = decodeArray(s); return ASCII.decode(a); }; BASE64.encode = function (s) { var u = UTF8.encode(s); return encodeArray(u); }; BASE64.decode = function (s) { var u = decodeArray(s); return UTF8.decode(u); }; })("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); /*The following polyfills are not used in dash.js but have caused multiplayer integration issues. Therefore commenting them out. if (undefined === btoa) { var btoa = BASE64.encode; } if (undefined === atob) { var atob = BASE64.decode; } */ if (typeof exports !== 'undefined') { exports.decode = BASE64.decode; exports.decodeArray = BASE64.decodeArray; } },{}],9:[function(require,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); },{}],10:[function(require,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.children = []; * parent.properties = properties; * * child = {}; * child.name = "ChildNode"; * child.isRoor = false; * child.isArray = true; * 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']; },{}],11:[function(require,module,exports){ /* Copyright 2011-2013 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. */ /* Further modified for dashjs to: - keep track of children nodes in order in attribute __children. - add type conversion matchers - re-add ignoreRoot - allow zero-length attributePrefix - don't add white-space text nodes - remove explicit RequireJS support */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function X2JS(config) { 'use strict'; var VERSION = "1.2.0"; config = config || {}; initConfigDefaults(); initRequiredPolyfills(); function initConfigDefaults() { if (config.escapeMode === undefined) { config.escapeMode = true; } if (config.attributePrefix === undefined) { config.attributePrefix = "_"; } config.arrayAccessForm = config.arrayAccessForm || "none"; config.emptyNodeForm = config.emptyNodeForm || "text"; if (config.enableToStringFunc === undefined) { config.enableToStringFunc = true; } config.arrayAccessFormPaths = config.arrayAccessFormPaths || []; if (config.skipEmptyTextNodesForObj === undefined) { config.skipEmptyTextNodesForObj = true; } if (config.stripWhitespaces === undefined) { config.stripWhitespaces = true; } config.datetimeAccessFormPaths = config.datetimeAccessFormPaths || []; if (config.useDoubleQuotes === undefined) { config.useDoubleQuotes = false; } config.xmlElementsFilter = config.xmlElementsFilter || []; config.jsonPropertiesFilter = config.jsonPropertiesFilter || []; if (config.keepCData === undefined) { config.keepCData = false; } if (config.ignoreRoot === undefined) { config.ignoreRoot = false; } } var DOMNodeTypes = { ELEMENT_NODE: 1, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, COMMENT_NODE: 8, DOCUMENT_NODE: 9 }; function initRequiredPolyfills() {} 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, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''');else return str; } function unescapeXmlChars(str) { return str.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, '&'); } function checkInStdFiltersArrayForm(stdFiltersArrayForm, obj, name, path) { var idx = 0; for (; idx < stdFiltersArrayForm.length; idx++) { var filterPath = stdFiltersArrayForm[idx]; if (typeof filterPath === "string") { if (filterPath == path) break; } else if (filterPath instanceof RegExp) { if (filterPath.test(path)) break; } else if (typeof filterPath === "function") { if (filterPath(obj, name, path)) break; } } return idx != stdFiltersArrayForm.length; } function toArrayAccessForm(obj, childName, path) { switch (config.arrayAccessForm) { case "property": if (!(obj[childName] instanceof Array)) obj[childName + "_asArray"] = [obj[childName]];else obj[childName + "_asArray"] = obj[childName]; break; /*case "none": break;*/ } if (!(obj[childName] instanceof Array) && config.arrayAccessFormPaths.length > 0) { if (checkInStdFiltersArrayForm(config.arrayAccessFormPaths, obj, childName, path)) { obj[childName] = [obj[childName]]; } } } function fromXmlDateTime(prop) { // Implementation based up on http://stackoverflow.com/questions/8178598/xml-datetime-to-javascript-date-object // Improved to support full spec and optional parts var bits = prop.split(/[-T:+Z]/g); var d = new Date(bits[0], bits[1] - 1, bits[2]); var secondBits = bits[5].split("\."); d.setHours(bits[3], bits[4], secondBits[0]); if (secondBits.length > 1) d.setMilliseconds(secondBits[1]); // Get supplied time zone offset in minutes if (bits[6] && bits[7]) { var offsetMinutes = bits[6] * 60 + Number(bits[7]); var sign = /\d\d-\d\d:\d\d$/.test(prop) ? '-' : '+'; // Apply the sign offsetMinutes = 0 + (sign == '-' ? -1 * offsetMinutes : offsetMinutes); // Apply offset and local timezone d.setMinutes(d.getMinutes() - offsetMinutes - d.getTimezoneOffset()); } else if (prop.indexOf("Z", prop.length - 1) !== -1) { d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds())); } // d is now a local time equivalent to the supplied time return d; } function checkFromXmlDateTimePaths(value, childName, fullPath) { if (config.datetimeAccessFormPaths.length > 0) { var path = fullPath.split("\.#")[0]; if (checkInStdFiltersArrayForm(config.datetimeAccessFormPaths, value, childName, path)) { return fromXmlDateTime(value); } else return value; } else return value; } function checkXmlElementsFilter(obj, childType, childName, childPath) { if (childType == DOMNodeTypes.ELEMENT_NODE && config.xmlElementsFilter.length > 0) { return checkInStdFiltersArrayForm(config.xmlElementsFilter, obj, childName, childPath); } else return true; } function parseDOMChildren(node, path) { if (node.nodeType == DOMNodeTypes.DOCUMENT_NODE) { var result = new Object(); var nodeChildren = node.childNodes; // Alternative for firstElementChild which is not supported in some environments for (var cidx = 0; cidx < nodeChildren.length; cidx++) { var child = nodeChildren.item(cidx); if (child.nodeType == DOMNodeTypes.ELEMENT_NODE) { if (config.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); if (child.nodeType != DOMNodeTypes.COMMENT_NODE) { var childPath = path + "." + childName; if (checkXmlElementsFilter(result, child.nodeType, childName, childPath)) { result.__cnt++; if (result[childName] == null) { var c = parseDOMChildren(child, childPath); if (childName != "#text" || /[^\s]/.test(c)) { var o = {}; o[childName] = c; children.push(o); } result[childName] = c; toArrayAccessForm(result, childName, childPath); } else { if (result[childName] != null) { if (!(result[childName] instanceof Array)) { result[childName] = [result[childName]]; toArrayAccessForm(result, childName, childPath); } } var c = parseDOMChildren(child, childPath); if (childName != "#text" || /[^\s]/.test(c)) { // Don't add white-space text nodes var o = {}; o[childName] = c; children.push(o); } result[childName][result[childName].length] = c; } } } } result.__children = children; // Attributes var nodeLocalName = getNodeLocalName(node); 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 = config.matchers.length; m < ml; m++) { var matchobj = config.matchers[m]; if (matchobj.test(attr, nodeLocalName)) value2 = matchobj.converter(attr.value); } result[config.attributePrefix + attr.name] = value2; } // Node namespace prefix var nodePrefix = getNodePrefix(node); if (nodePrefix != null && nodePrefix != "") { result.__cnt++; result.__prefix = nodePrefix; } if (result["#text"] != null) { result.__text = result["#text"]; if (result.__text instanceof Array) { result.__text = result.__text.join("\n"); } //if(config.escapeMode) // result.__text = unescapeXmlChars(result.__text); if (config.stripWhitespaces) result.__text = result.__text.trim(); delete result["#text"]; if (config.arrayAccessForm == "property") delete result["#text_asArray"]; result.__text = checkFromXmlDateTimePaths(result.__text, childName, path + "." + childName); } if (result["#cdata-section"] != null) { result.__cdata = result["#cdata-section"]; delete result["#cdata-section"]; if (config.arrayAccessForm == "property") delete result["#cdata-section_asArray"]; } if (result.__cnt == 0 && config.emptyNodeForm == "text") { result = ''; } else if (result.__cnt == 1 && result.__text != null) { result = result.__text; } else if (result.__cnt == 1 && result.__cdata != null && !config.keepCData) { result = result.__cdata; } else if (result.__cnt > 1 && result.__text != null && config.skipEmptyTextNodesForObj) { if (config.stripWhitespaces && result.__text == "" || result.__text.trim() == "") { delete result.__text; } } delete result.__cnt; if (config.enableToStringFunc && (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; } } 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]; if (config.escapeMode) attrVal = escapeXmlChars(attrVal); resultStr += " " + attrName.substr(config.attributePrefix.length) + "="; if (config.useDoubleQuotes) resultStr += '"' + attrVal + '"';else resultStr += "'" + attrVal + "'"; } } if (!closed) resultStr += ">";else resultStr += "/>"; return resultStr; } function endTag(jsonObj, elementName) { return ""; } function endsWith(str, suffix) { return str.indexOf(suffix, str.length - suffix.length) !== -1; } function jsonXmlSpecialElem(jsonObj, jsonObjField) { if (config.arrayAccessForm == "property" && endsWith(jsonObjField.toString(), "_asArray") || jsonObjField.toString().indexOf(config.attributePrefix) == 0 || 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 checkJsonObjPropertiesFilter(jsonObj, propertyName, jsonObjPath) { return config.jsonPropertiesFilter.length == 0 || jsonObjPath == "" || checkInStdFiltersArrayForm(config.jsonPropertiesFilter, jsonObj, propertyName, jsonObjPath); } function parseJSONAttributes(jsonObj) { var attrList = []; if (jsonObj instanceof Object) { for (var ait in jsonObj) { if (ait.toString().indexOf("__") == -1 && ait.toString().indexOf(config.attributePrefix) == 0) { attrList.push(ait); } } } return attrList; } function parseJSONTextAttrs(jsonTxtObj) { var result = ""; if (jsonTxtObj.__cdata != null) { result += ""; } if (jsonTxtObj.__text != null) { if (config.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 (config.escapeMode) result += escapeXmlChars(jsonTxtObj);else result += jsonTxtObj; } return result; } function getJsonPropertyPath(jsonObjPath, jsonPropName) { if (jsonObjPath === "") { return jsonPropName; } else return jsonObjPath + "." + jsonPropName; } function parseJSONArray(jsonArrRoot, jsonArrObj, attrList, jsonObjPath) { 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], getJsonPropertyPath(jsonObjPath, jsonArrObj)); result += endTag(jsonArrRoot[arIdx], jsonArrObj); } } return result; } function parseJSONObject(jsonObj, jsonObjPath) { var result = ""; var elementsCnt = jsonXmlElemCount(jsonObj); if (elementsCnt > 0) { for (var it in jsonObj) { if (jsonXmlSpecialElem(jsonObj, it) || jsonObjPath != "" && !checkJsonObjPropertiesFilter(jsonObj, it, getJsonPropertyPath(jsonObjPath, 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, jsonObjPath); } else if (subObj instanceof Date) { result += startTag(subObj, it, attrList, false); result += subObj.toISOString(); result += endTag(subObj, it); } else { var subObjElementsCnt = jsonXmlElemCount(subObj); if (subObjElementsCnt > 0 || subObj.__text != null || subObj.__cdata != null) { result += startTag(subObj, it, attrList, false); result += parseJSONObject(subObj, getJsonPropertyPath(jsonObjPath, it)); 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 isIEParser = window.ActiveXObject || "ActiveXObject" in window; if (xmlDocStr === undefined) { return null; } var xmlDoc; if (window.DOMParser) { var parser = new window.DOMParser(); var parsererrorNS = null; // IE9+ now is here if (!isIEParser) { try { parsererrorNS = parser.parseFromString("INVALID", "text/xml").getElementsByTagName("parsererror")[0].namespaceURI; } catch (err) { parsererrorNS = null; } } try { xmlDoc = parser.parseFromString(xmlDocStr, "text/xml"); if (parsererrorNS != null && xmlDoc.getElementsByTagNameNS(parsererrorNS, "parsererror").length > 0) { //throw new Error('Error parsing XML: '+xmlDocStr); xmlDoc = null; } } catch (err) { xmlDoc = null; } } else { // IE :( if (xmlDocStr.indexOf("") + 2); } xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = "false"; xmlDoc.loadXML(xmlDocStr); } return xmlDoc; }; this.asArray = function (prop) { if (prop === undefined || prop == null) return [];else if (prop instanceof Array) return prop;else return [prop]; }; this.toXmlDateTime = function (dt) { if (dt instanceof Date) return dt.toISOString();else if (typeof dt === 'number') return new Date(dt).toISOString();else return null; }; this.asDateTime = function (prop) { if (typeof prop == "string") { return fromXmlDateTime(prop); } else return prop; }; this.xml2json = function (xmlDoc) { return parseDOMChildren(xmlDoc); }; this.xml_str2json = function (xmlDocStr) { var xmlDoc = this.parseXmlString(xmlDocStr); if (xmlDoc != null) return this.xml2json(xmlDoc);else return null; }; 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; }; } exports["default"] = X2JS; module.exports = exports["default"]; },{}],12:[function(require,module,exports){ (function (global){ /** * 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 _srcStreamingMediaPlayer = require('./src/streaming/MediaPlayer'); var _srcStreamingMediaPlayer2 = _interopRequireDefault(_srcStreamingMediaPlayer); var _srcStreamingProtectionProtection = require('./src/streaming/protection/Protection'); var _srcStreamingProtectionProtection2 = _interopRequireDefault(_srcStreamingProtectionProtection); var _srcStreamingMetricsMetricsReporting = require('./src/streaming/metrics/MetricsReporting'); var _srcStreamingMetricsMetricsReporting2 = _interopRequireDefault(_srcStreamingMetricsMetricsReporting); var _srcStreamingMediaPlayerFactory = require('./src/streaming/MediaPlayerFactory'); var _srcStreamingMediaPlayerFactory2 = _interopRequireDefault(_srcStreamingMediaPlayerFactory); var _srcCoreVersion = require('./src/core/Version'); // Shove both of these into the global scope var context = typeof window !== 'undefined' && window || global; var dashjs = context.dashjs; if (!dashjs) { dashjs = context.dashjs = {}; } dashjs.MediaPlayer = _srcStreamingMediaPlayer2['default']; dashjs.Protection = _srcStreamingProtectionProtection2['default']; dashjs.MetricsReporting = _srcStreamingMetricsMetricsReporting2['default']; dashjs.MediaPlayerFactory = _srcStreamingMediaPlayerFactory2['default']; dashjs.Version = (0, _srcCoreVersion.getVersionString)(); exports['default'] = dashjs; exports.MediaPlayer = _srcStreamingMediaPlayer2['default']; exports.Protection = _srcStreamingProtectionProtection2['default']; exports.MetricsReporting = _srcStreamingMetricsMetricsReporting2['default']; exports.MediaPlayerFactory = _srcStreamingMediaPlayerFactory2['default']; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./src/core/Version":16,"./src/streaming/MediaPlayer":57,"./src/streaming/MediaPlayerFactory":59,"./src/streaming/metrics/MetricsReporting":81,"./src/streaming/protection/Protection":112}],13:[function(require,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 _EventBus = require('./EventBus'); var _EventBus2 = _interopRequireDefault(_EventBus); var _eventsEvents = require('./events/Events'); var _eventsEvents2 = _interopRequireDefault(_eventsEvents); var _FactoryMaker = require('./FactoryMaker'); var _FactoryMaker2 = _interopRequireDefault(_FactoryMaker); /** * @module Debug */ function Debug() { var context = this.context; var eventBus = (0, _EventBus2['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(_eventsEvents2['default'].LOG, { message: message }); } instance = { log: log, setLogTimestampVisible: setLogTimestampVisible, setLogToBrowserConsole: setLogToBrowserConsole, getLogToBrowserConsole: getLogToBrowserConsole }; setup(); return instance; } Debug.__dashjs_factory_name = 'Debug'; exports['default'] = _FactoryMaker2['default'].getSingletonFactory(Debug); module.exports = exports['default']; },{"./EventBus":14,"./FactoryMaker":15,"./events/Events":18}],14:[function(require,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 _FactoryMaker = require('./FactoryMaker'); var _FactoryMaker2 = _interopRequireDefault(_FactoryMaker); var EVENT_PRIORITY_LOW = 0; var EVENT_PRIORITY_HIGH = 5000; function EventBus() { var handlers = {}; function on(type, listener, scope) { var priority = arguments.length <= 3 || arguments[3] === undefined ? EVENT_PRIORITY_LOW : arguments[3]; 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; handlers[type] = handlers[type] || []; var handler = { callback: listener, scope: scope, priority: priority }; var inserted = handlers[type].some(function (item, idx) { if (priority > item.priority) { handlers[type].splice(idx, 0, handler); return true; } }); if (!inserted) { 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, payload) { if (!type || !handlers[type]) return; payload = payload || {}; if (payload.hasOwnProperty('type')) throw new Error('\'type\' is a reserved word for event dispatching'); payload.type = type; handlers[type].forEach(function (handler) { return handler.callback.call(handler.scope, payload); }); } function getHandlerIdx(type, listener, scope) { var idx = -1; if (!handlers[type]) return idx; handlers[type].some(function (item, index) { if (item.callback === listener && (!scope || scope === item.scope)) { idx = index; return true; } }); return idx; } function reset() { handlers = {}; } var instance = { on: on, off: off, trigger: trigger, reset: reset }; return instance; } EventBus.__dashjs_factory_name = 'EventBus'; var factory = _FactoryMaker2['default'].getSingletonFactory(EventBus); factory.EVENT_PRIORITY_LOW = EVENT_PRIORITY_LOW; factory.EVENT_PRIORITY_HIGH = EVENT_PRIORITY_HIGH; exports['default'] = factory; module.exports = exports['default']; },{"./FactoryMaker":15}],15:[function(require,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 {Object} context - injected into extended object as this.context * @param {string} className - 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 {Object} context * @param {string} className * @param {Object} 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"]; },{}],16:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.getVersionString = getVersionString; var VERSION = '2.4.1'; function getVersionString() { return VERSION; } },{}],17:[function(require,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 _EventsBase2 = require('./EventsBase'); var _EventsBase3 = _interopRequireDefault(_EventsBase2); /** * These are internal events that should not be needed at the player level. * If you find and event in here that you would like access to from MediaPlayer level * please add an issue at https://github.com/Dash-Industry-Forum/dash.js/issues/new * @class * @ignore */ var CoreEvents = (function (_EventsBase) { _inherits(CoreEvents, _EventsBase); function CoreEvents() { _classCallCheck(this, CoreEvents); _get(Object.getPrototypeOf(CoreEvents.prototype), 'constructor', this).call(this); this.BUFFERING_COMPLETED = 'bufferingCompleted'; this.BUFFER_CLEARED = 'bufferCleared'; this.BUFFER_LEVEL_UPDATED = 'bufferLevelUpdated'; this.BYTES_APPENDED = 'bytesAppended'; this.CHECK_FOR_EXISTENCE_COMPLETED = 'checkForExistenceCompleted'; this.CURRENT_TRACK_CHANGED = 'currentTrackChanged'; this.DATA_UPDATE_COMPLETED = 'dataUpdateCompleted'; this.DATA_UPDATE_STARTED = 'dataUpdateStarted'; 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.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_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_ELEMENT_LOADED = 'xlinkElementLoaded'; this.XLINK_READY = 'xlinkReady'; } return CoreEvents; })(_EventsBase3['default']); exports['default'] = CoreEvents; module.exports = exports['default']; },{"./EventsBase":19}],18:[function(require,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 _CoreEvents2 = require('./CoreEvents'); var _CoreEvents3 = _interopRequireDefault(_CoreEvents2); var Events = (function (_CoreEvents) { _inherits(Events, _CoreEvents); function Events() { _classCallCheck(this, Events); _get(Object.getPrototypeOf(Events.prototype), 'constructor', this).apply(this, arguments); } return Events; })(_CoreEvents3['default']); var events = new Events(); exports['default'] = events; module.exports = exports['default']; },{"./CoreEvents":17}],19:[function(require,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']; },{}],20:[function(require,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 _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 _streamingVoTrackInfo = require('../streaming/vo/TrackInfo'); var _streamingVoTrackInfo2 = _interopRequireDefault(_streamingVoTrackInfo); var _streamingVoMediaInfo = require('../streaming/vo/MediaInfo'); var _streamingVoMediaInfo2 = _interopRequireDefault(_streamingVoMediaInfo); var _streamingVoStreamInfo = require('../streaming/vo/StreamInfo'); var _streamingVoStreamInfo2 = _interopRequireDefault(_streamingVoStreamInfo); var _streamingVoManifestInfo = require('../streaming/vo/ManifestInfo'); var _streamingVoManifestInfo2 = _interopRequireDefault(_streamingVoManifestInfo); var _voEvent = require('./vo/Event'); var _voEvent2 = _interopRequireDefault(_voEvent); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _externalsCea608Parser = require('../../externals/cea608-parser'); var _externalsCea608Parser2 = _interopRequireDefault(_externalsCea608Parser); var _constantsDashMetricsList = require('./constants/DashMetricsList'); var METRIC_LIST = _interopRequireWildcard(_constantsDashMetricsList); 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) { if (!adaptations) return null; 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 _streamingVoTrackInfo2['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 _streamingVoMediaInfo2['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 _externalsCea608Parser2['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 _streamingVoStreamInfo2['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 _streamingVoManifestInfo2['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 data = dashManifestModel.getAdaptationForType(manifest, streamInfo.index, type, streamInfo); if (!data) return null; var periodInfo = getPeriodForStreamInfo(streamInfo); var periodId = periodInfo.id; var 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) { if (!manifest) return null; var streams = []; var mpd = dashManifestModel.getMpd(manifest); periods = dashManifestModel.getRegularPeriods(manifest, mpd); adaptations = {}; for (var i = 0, ln = periods.length; 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 _voEvent2['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 _streamingVoStreamInfo2['default']) { events = dashManifestModel.getEventsForPeriod(manifest, getPeriodForStreamInfo(info)); } else if (info instanceof _streamingVoMediaInfo2['default']) { events = dashManifestModel.getEventStreamForAdaptationSet(manifest, getAdaptationForMediaInfo(info)); } else if (info instanceof _streamingVoTrackInfo2['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'] = _coreFactoryMaker2['default'].getSingletonFactory(DashAdapter); module.exports = exports['default']; },{"../../externals/cea608-parser":9,"../core/FactoryMaker":15,"../streaming/vo/ManifestInfo":172,"../streaming/vo/MediaInfo":173,"../streaming/vo/StreamInfo":175,"../streaming/vo/TrackInfo":178,"./constants/DashMetricsList":25,"./vo/Event":47}],21:[function(require,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 _streamingVoFragmentRequest = require('../streaming/vo/FragmentRequest'); var _streamingVoFragmentRequest2 = _interopRequireDefault(_streamingVoFragmentRequest); var _streamingVoError = require('../streaming/vo/Error'); var _streamingVoError2 = _interopRequireDefault(_streamingVoError); var _streamingVoMetricsHTTPRequest = require('../streaming/vo/metrics/HTTPRequest'); var _coreEventsEvents = require('../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreEventBus = require('../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _streamingUtilsURLUtils = require('../streaming/utils/URLUtils'); var _streamingUtilsURLUtils2 = _interopRequireDefault(_streamingUtilsURLUtils); var _voRepresentation = require('./vo/Representation'); var _voRepresentation2 = _interopRequireDefault(_voRepresentation); var _utilsSegmentsUtils = require('./utils/SegmentsUtils'); var _utilsSegmentsGetter = require('./utils/SegmentsGetter'); var _utilsSegmentsGetter2 = _interopRequireDefault(_utilsSegmentsGetter); var SEGMENTS_UNAVAILABLE_ERROR_CODE = 1; function DashHandler(config) { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var urlUtils = (0, _streamingUtilsURLUtils2['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(_coreEventsEvents2['default'].INITIALIZATION_LOADED, onInitializationLoaded, instance); eventBus.on(_coreEventsEvents2['default'].SEGMENTS_LOADED, onSegmentsLoaded, instance); } function initialize(StreamProcessor) { streamProcessor = StreamProcessor; type = streamProcessor.getType(); isDynamic = streamProcessor.isDynamic(); segmentsGetter = (0, _utilsSegmentsGetter2['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(_coreEventsEvents2['default'].INITIALIZATION_LOADED, onInitializationLoaded, instance); eventBus.off(_coreEventsEvents2['default'].SEGMENTS_LOADED, onSegmentsLoaded, instance); } function unescapeDollarsInTemplate(url) { return url ? url.split('$$').join('$') : url; } function replaceIDForTemplate(url, value) { if (value === null || url === 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; serviceLocation = baseURL.serviceLocation; if (destination) { url = urlUtils.resolve(destination, url); } } if (urlUtils.isRelative(url)) { return false; } request.url = url; request.serviceLocation = serviceLocation; return true; } function generateInitRequest(representation, mediaType) { var request = new _streamingVoFragmentRequest2['default'](); var period = representation.adaptation.period; var presentationStartTime = period.start; request.mediaType = mediaType; request.type = _streamingVoMetricsHTTPRequest.HTTPRequest.INIT_SEGMENT_TYPE; request.range = representation.range; request.availabilityStartTime = timelineConverter.calcAvailabilityStartTimeFromPresentationTime(presentationStartTime, 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) { if (!representation) return null; var request = generateInitRequest(representation, type); return request; } function isMediaFinished(representation) { var isFinished = false; if (!isDynamic && index === representation.availableSegmentsNumber) { isFinished = true; } else { var seg = (0, _utilsSegmentsUtils.getSegmentByIndex)(index, representation); if (seg) { var time = seg.presentationStartTime - representation.adaptation.period.start; var duration = representation.adaptation.period.duration; log(representation.segmentInfoType + ': ' + time + ' / ' + duration); isFinished = representation.segmentInfoType === 'SegmentTimeline' && isDynamic ? false : time >= duration; } } 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 lastSegment = segments[segments.length - 1]; var liveEdge = lastSegment.presentationStartTime; var metrics = metricsModel.getMetricsFor('stream'); // the last segment is the Expected, not calculated, live edge. timelineConverter.setExpectedLiveEdge(liveEdge); metricsModel.updateManifestUpdateInfo(dashMetrics.getCurrentManifestUpdate(metrics), { presentationStartTime: liveEdge }); } } function updateSegmentList(representation) { if (!representation) { throw new _streamingVoError2['default']('no representation'); } representation.segments = null; updateSegments(representation); return representation; } function updateRepresentation(representation, keepIdx) { var hasInitialization = _voRepresentation2['default'].hasInitialization(representation); var hasSegments = _voRepresentation2['default'].hasSegments(representation); 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 _streamingVoError2['default'](SEGMENTS_UNAVAILABLE_ERROR_CODE, 'no segments are available yet', { availabilityDelay: representation.segmentAvailabilityRange.start - representation.segmentAvailabilityRange.end }); eventBus.trigger(_coreEventsEvents2['default'].REPRESENTATION_UPDATED, { sender: this, representation: representation, error: error }); return; } if (!keepIdx) index = -1; if (representation.segmentDuration) { updateSegmentList(representation); } if (!hasInitialization) { segmentBaseLoader.loadInitialization(representation); } if (!hasSegments) { segmentBaseLoader.loadSegments(representation, type, representation.indexRange); } if (hasInitialization && hasSegments) { eventBus.trigger(_coreEventsEvents2['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 _streamingVoFragmentRequest2['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, _utilsSegmentsUtils.replaceTokenForTemplate)(url, 'Number', segment.replacementNumber); url = (0, _utilsSegmentsUtils.replaceTokenForTemplate)(url, 'Time', segment.replacementTime); url = (0, _utilsSegmentsUtils.replaceTokenForTemplate)(url, 'Bandwidth', bandwidth); url = replaceIDForTemplate(url, representation.id); url = unescapeDollarsInTemplate(url); request.mediaType = type; request.type = _streamingVoMetricsHTTPRequest.HTTPRequest.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); } updateSegments(representation); index = getIndexForSegments(time, representation, timeThreshold); //Index may be -1 if getSegments needs to update again. So after getSegments is called and updated then try to get index again. if (index < 0) { updateSegments(representation); 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 _streamingVoFragmentRequest2['default'](); request.action = _streamingVoFragmentRequest2['default'].ACTION_COMPLETE; request.index = index; request.mediaType = type; request.mediaInfo = streamProcessor.getMediaInfo(); log('Signal complete.', request); } else { segment = (0, _utilsSegmentsUtils.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 _streamingVoFragmentRequest2['default'](); request.action = _streamingVoFragmentRequest2['default'].ACTION_COMPLETE; request.index = index; request.mediaType = type; request.mediaInfo = streamProcessor.getMediaInfo(); log('Signal complete.'); } else { updateSegments(representation); segment = (0, _utilsSegmentsUtils.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(_coreEventsEvents2['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, _utilsSegmentsUtils.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 (!_voRepresentation2['default'].hasInitialization(representation)) return; eventBus.trigger(_coreEventsEvents2['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 = _coreFactoryMaker2['default'].getClassFactory(DashHandler); factory.SEGMENTS_UNAVAILABLE_ERROR_CODE = SEGMENTS_UNAVAILABLE_ERROR_CODE; exports['default'] = factory; module.exports = exports['default']; },{"../core/Debug":13,"../core/EventBus":14,"../core/FactoryMaker":15,"../core/events/Events":18,"../streaming/utils/URLUtils":164,"../streaming/vo/Error":168,"../streaming/vo/FragmentRequest":169,"../streaming/vo/metrics/HTTPRequest":185,"./utils/SegmentsGetter":40,"./utils/SegmentsUtils":41,"./vo/Representation":51}],22:[function(require,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 _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 _streamingVoMetricsHTTPRequest = require('../streaming/vo/metrics/HTTPRequest'); var _streamingModelsManifestModel = require('../streaming/models/ManifestModel'); var _streamingModelsManifestModel2 = _interopRequireDefault(_streamingModelsManifestModel); var _modelsDashManifestModel = require('./models/DashManifestModel'); var _modelsDashManifestModel2 = _interopRequireDefault(_modelsDashManifestModel); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _constantsDashMetricsList = require('./constants/DashMetricsList'); var MetricsList = _interopRequireWildcard(_constantsDashMetricsList); var _round10 = require('round10'); /** * @module DashMetrics */ function DashMetrics() { var instance = undefined; var context = this.context; var manifestModel = (0, _streamingModelsManifestModel2['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 {string} representationId * @param {number} 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 {string} bufferType - String 'audio' or 'video', * @param {number} periodIdx - Make sure this is the period index not id * @return {number} * @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; } /** * @param {MetricsList} metrics * @returns {*} * @memberof module:DashMetrics * @instance */ function getCurrentRepresentationSwitch(metrics) { return getCurrent(metrics, MetricsList.TRACK_SWITCH); } /** * @param {MetricsList} metrics * @returns {*} * @memberof module:DashMetrics * @instance */ function getLatestBufferLevelVO(metrics) { return getCurrent(metrics, MetricsList.BUFFER_LEVEL); } /** * @param {MetricsList} metrics * @returns {number} * @memberof module:DashMetrics * @instance */ function getCurrentBufferLevel(metrics) { var vo = getLatestBufferLevelVO(metrics); if (vo) { return (0, _round10.round10)(vo.level / 1000, -3); } return 0; } /** * @param {MetricsList} metrics * @returns {null|*|vo} * @memberof module:DashMetrics * @instance */ function getRequestsQueue(metrics) { return metrics.RequestsQueue; } /** * @param {MetricsList} 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 {MetricsList} metrics * @returns {*} * @memberof module:DashMetrics * @instance */ function getHttpRequests(metrics) { if (metrics === null) { return []; } return !!metrics.HttpList ? metrics.HttpList : []; } /** * @param {MetricsList} metrics * @param {string} metricName * @returns {*} * @memberof module:DashMetrics * @instance */ function getCurrent(metrics, metricName) { if (metrics === null) { return null; } var list = metrics[metricName]; if (list === null) { return null; } var length = list.length; if (length <= 0) { return null; } return list[length - 1]; } /** * @param {MetricsList} metrics * @returns {*} * @memberof module:DashMetrics * @instance */ function getCurrentDroppedFrames(metrics) { return getCurrent(metrics, MetricsList.DROPPED_FRAMES); } /** * @param {MetricsList} metrics * @returns {*} * @memberof module:DashMetrics * @instance */ function getCurrentSchedulingInfo(metrics) { return getCurrent(metrics, MetricsList.SCHEDULING_INFO); } /** * @param {MetricsList} metrics * @returns {*} * @memberof module:DashMetrics * @instance */ function getCurrentManifestUpdate(metrics) { return getCurrent(metrics, MetricsList.MANIFEST_UPDATE); } /** * @param {MetricsList} metrics * @returns {*} * @memberof module:DashMetrics * @instance */ function getCurrentDVRInfo(metrics) { return getCurrent(metrics, MetricsList.DVR_INFO); } /** * @param {MetricsList} metrics * @param {string} 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 === _streamingVoMetricsHTTPRequest.HTTPRequest.MPD_TYPE) { headers = parseResponseHeaders(httpRequest._responseHeaders); break; } } return headers[id] === undefined ? null : headers[id]; } /** * @param {MetricsList} metrics * @param {string} 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; } // Trim headerStr to fix a MS Edge bug with xhr.getAllResponseHeaders method // which send a string starting with a "\n" character var headerPairs = headerStr.trim().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 index = findRepresentation(period, representationId, true); if (index !== null) { return index; } return -1; } function findRepresentation(period, representationId, returnIndex) { 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) { if (returnIndex) { return representationArrayIndex; } else { return representation; } } } } return null; } function adaptationIsType(adaptation, bufferType) { return (0, _modelsDashManifestModel2['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, 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'] = _coreFactoryMaker2['default'].getSingletonFactory(DashMetrics); module.exports = exports['default']; },{"../core/FactoryMaker":15,"../streaming/models/ManifestModel":106,"../streaming/vo/metrics/HTTPRequest":185,"./constants/DashMetricsList":25,"./models/DashManifestModel":27,"round10":212}],23:[function(require,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 _streamingUtilsRequestModifier = require('../streaming/utils/RequestModifier'); var _streamingUtilsRequestModifier2 = _interopRequireDefault(_streamingUtilsRequestModifier); var _voSegment = require('./vo/Segment'); var _voSegment2 = _interopRequireDefault(_voSegment); var _streamingVoError = require('../streaming/vo/Error'); var _streamingVoError2 = _interopRequireDefault(_streamingVoError); var _streamingUtilsErrorHandler = require('../streaming/utils/ErrorHandler'); var _streamingUtilsErrorHandler2 = _interopRequireDefault(_streamingUtilsErrorHandler); var _coreEventsEvents = require('../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreEventBus = require('../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _streamingUtilsBoxParser = require('../streaming/utils/BoxParser'); var _streamingUtilsBoxParser2 = _interopRequireDefault(_streamingUtilsBoxParser); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _streamingVoMetricsHTTPRequest = require('../streaming/vo/metrics/HTTPRequest'); var _streamingVoFragmentRequest = require('../streaming/vo/FragmentRequest'); var _streamingVoFragmentRequest2 = _interopRequireDefault(_streamingVoFragmentRequest); var _streamingXHRLoader = require('../streaming/XHRLoader'); var _streamingXHRLoader2 = _interopRequireDefault(_streamingXHRLoader); function SegmentBaseLoader() { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, errHandler = undefined, boxParser = undefined, requestModifier = undefined, metricsModel = undefined, xhrLoader = undefined, baseURLController = undefined; function initialize() { errHandler = (0, _streamingUtilsErrorHandler2['default'])(context).getInstance(); boxParser = (0, _streamingUtilsBoxParser2['default'])(context).getInstance(); requestModifier = (0, _streamingUtilsRequestModifier2['default'])(context).getInstance(); xhrLoader = (0, _streamingXHRLoader2['default'])(context).create({ errHandler: errHandler, metricsModel: metricsModel, requestModifier: requestModifier }); } function setConfig(config) { if (config.baseURLController) { baseURLController = config.baseURLController; } if (config.metricsModel) { metricsModel = config.metricsModel; } } function loadInitialization(representation, loadingInfo) { var initRange = null; var isoFile = null; var baseUrl = baseURLController.resolve(representation.path); var info = loadingInfo || { init: true, url: baseUrl ? baseUrl.url : undefined, range: { start: 0, end: 1500 }, searching: false, bytesLoaded: 0, bytesToLoad: 1500 }; log('Start searching for initialization.'); var request = getFragmentRequest(info); var onload = function onload(response) { info.bytesLoaded = info.range.end; isoFile = boxParser.parse(response); initRange = findInitRange(isoFile); if (initRange) { representation.range = initRange; // note that we don't explicitly set rep.initialization as this // will be computed when all BaseURLs are resolved later eventBus.trigger(_coreEventsEvents2['default'].INITIALIZATION_LOADED, { representation: representation }); } else { info.range.end = info.bytesLoaded + info.bytesToLoad; loadInitialization(representation, info); } }; var onerror = function onerror() { eventBus.trigger(_coreEventsEvents2['default'].INITIALIZATION_LOADED, { representation: representation }); }; xhrLoader.load({ request: request, success: onload, error: onerror }); 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 isoFile = null; var sidx = null; var hasRange = !!range; var baseUrl = baseURLController.resolve(representation.path); var info = { init: false, url: baseUrl ? baseUrl.url : undefined, range: hasRange ? range : { start: 0, end: 1500 }, searching: !hasRange, bytesLoaded: loadingInfo ? loadingInfo.bytesLoaded : 0, bytesToLoad: 1500 }; var request = getFragmentRequest(info); var onload = function onload(response) { var extraBytes = info.bytesToLoad; var loadedLength = response.byteLength; info.bytesLoaded = info.range.end - info.range.start; isoFile = boxParser.parse(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); } } }; var onerror = function onerror() { callback(null, representation, type); }; xhrLoader.load({ request: request, success: onload, error: onerror }); log('Perform SIDX load: ' + info.url); } function reset() { xhrLoader.abort(); xhrLoader = null; errHandler = null; boxParser = null; requestModifier = 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.offset + 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 _voSegment2['default'](); // note that we don't explicitly set segment.media as this will be // computed when all BaseURLs are resolved later segment.duration = duration; 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 getFragmentRequest(info) { if (!info.url) { return; } var request = new _streamingVoFragmentRequest2['default'](); request.type = info.init ? _streamingVoMetricsHTTPRequest.HTTPRequest.INIT_SEGMENT_TYPE : _streamingVoMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE; request.url = info.url; request.range = info.range.start + '-' + info.range.end; return request; } function onLoaded(segments, representation, type) { if (segments) { eventBus.trigger(_coreEventsEvents2['default'].SEGMENTS_LOADED, { segments: segments, representation: representation, mediaType: type }); } else { eventBus.trigger(_coreEventsEvents2['default'].SEGMENTS_LOADED, { segments: null, representation: representation, mediaType: type, error: new _streamingVoError2['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'] = _coreFactoryMaker2['default'].getSingletonFactory(SegmentBaseLoader); module.exports = exports['default']; },{"../core/Debug":13,"../core/EventBus":14,"../core/FactoryMaker":15,"../core/events/Events":18,"../streaming/XHRLoader":64,"../streaming/utils/BoxParser":152,"../streaming/utils/ErrorHandler":157,"../streaming/utils/RequestModifier":162,"../streaming/vo/Error":168,"../streaming/vo/FragmentRequest":169,"../streaming/vo/metrics/HTTPRequest":185,"./vo/Segment":52}],24:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreEventsEvents = require('../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreEventBus = require('../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _streamingUtilsEBMLParser = require('../streaming/utils/EBMLParser'); var _streamingUtilsEBMLParser2 = _interopRequireDefault(_streamingUtilsEBMLParser); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _streamingUtilsErrorHandler = require('../streaming/utils/ErrorHandler'); var _streamingUtilsErrorHandler2 = _interopRequireDefault(_streamingUtilsErrorHandler); var _streamingUtilsRequestModifier = require('../streaming/utils/RequestModifier'); var _streamingUtilsRequestModifier2 = _interopRequireDefault(_streamingUtilsRequestModifier); var _voSegment = require('./vo/Segment'); var _voSegment2 = _interopRequireDefault(_voSegment); var _streamingVoMetricsHTTPRequest = require('../streaming/vo/metrics/HTTPRequest'); var _streamingVoFragmentRequest = require('../streaming/vo/FragmentRequest'); var _streamingVoFragmentRequest2 = _interopRequireDefault(_streamingVoFragmentRequest); var _streamingXHRLoader = require('../streaming/XHRLoader'); var _streamingXHRLoader2 = _interopRequireDefault(_streamingXHRLoader); function WebmSegmentBaseLoader() { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, WebM = undefined, errHandler = undefined, requestModifier = undefined, metricsModel = undefined, xhrLoader = undefined, baseURLController = undefined; function setup() { WebM = { EBML: { tag: 0x1A45DFA3, required: true }, Segment: { tag: 0x18538067, required: true, SeekHead: { tag: 0x114D9B74, required: true }, Info: { tag: 0x1549A966, required: true, TimecodeScale: { tag: 0x2AD7B1, required: true, parse: 'getMatroskaUint' }, Duration: { tag: 0x4489, required: true, parse: 'getMatroskaFloat' } }, Tracks: { tag: 0x1654AE6B, required: true }, Cues: { tag: 0x1C53BB6B, required: true, CuePoint: { tag: 0xBB, required: true, CueTime: { tag: 0xB3, required: true, parse: 'getMatroskaUint' }, CueTrackPositions: { tag: 0xB7, required: true, CueTrack: { tag: 0xF7, required: true, parse: 'getMatroskaUint' }, CueClusterPosition: { tag: 0xF1, required: true, parse: 'getMatroskaUint' }, CueBlockNumber: { tag: 0x5378 } } } } }, Void: { tag: 0xEC, required: true } }; } function initialize() { errHandler = (0, _streamingUtilsErrorHandler2['default'])(context).getInstance(); requestModifier = (0, _streamingUtilsRequestModifier2['default'])(context).getInstance(); xhrLoader = (0, _streamingXHRLoader2['default'])(context).create({ errHandler: errHandler, metricsModel: metricsModel, requestModifier: requestModifier }); } function setConfig(config) { if (!config.baseURLController || !config.metricsModel) { throw new Error('Missing config parameter(s)'); } baseURLController = config.baseURLController; metricsModel = config.metricsModel; } function parseCues(ab) { var cues = []; var cue = undefined; var cueSize = undefined; var cueTrack = undefined; var ebmlParser = (0, _streamingUtilsEBMLParser2['default'])(context).create({ data: ab }); var numSize = undefined; ebmlParser.consumeTag(WebM.Segment.Cues); cueSize = ebmlParser.getMatroskaCodedNum(); while (ebmlParser.moreData() && ebmlParser.consumeTagAndSize(WebM.Segment.Cues.CuePoint, true)) { cue = {}; cue.CueTime = ebmlParser.parseTag(WebM.Segment.Cues.CuePoint.CueTime); cue.CueTracks = []; while (ebmlParser.moreData() && ebmlParser.consumeTagAndSize(WebM.Segment.Cues.CuePoint.CueTrackPositions, true)) { cueTrack = {}; cueTrack.Track = ebmlParser.parseTag(WebM.Segment.Cues.CuePoint.CueTrackPositions.CueTrack); if (cueTrack.Track === 0) { throw new Error('Cue track cannot be 0'); } cueTrack.ClusterPosition = ebmlParser.parseTag(WebM.Segment.Cues.CuePoint.CueTrackPositions.CueClusterPosition); // block number is strictly optional. // we also have to make sure we don't go beyond the end // of the cues if (ebmlParser.getPos() + 4 > cueSize || !ebmlParser.consumeTag(WebM.Segment.Cues.CuePoint.CueTrackPositions.CueBlockNumber, true)) { cue.CueTracks.push(cueTrack); } else { // since we have already consumed the tag, get the size of // the tag's payload, and manually parse an unsigned int // from the bit stream numSize = ebmlParser.getMatroskaCodedNum(); cueTrack.BlockNumber = ebmlParser.getMatroskaUint(numSize); cue.CueTracks.push(cueTrack); } } if (cue.CueTracks.length === 0) { throw new Error('Mandatory cuetrack not found'); } cues.push(cue); } if (cues.length === 0) { throw new Error('mandatory cuepoint not found'); } return cues; } function parseSegments(data, segmentStart, segmentEnd, segmentDuration) { var duration = undefined; var parsed = undefined; var segments = undefined; var segment = undefined; var i = undefined; var len = undefined; var start = undefined; var end = undefined; parsed = parseCues(data); segments = []; // we are assuming one cue track per cue point // both duration and media range require the i + 1 segment // the final segment has to use global segment parameters for (i = 0, len = parsed.length; i < len; i += 1) { segment = new _voSegment2['default'](); duration = 0; if (i < parsed.length - 1) { duration = parsed[i + 1].CueTime - parsed[i].CueTime; } else { duration = segmentDuration - parsed[i].CueTime; } // note that we don't explicitly set segment.media as this will be // computed when all BaseURLs are resolved later segment.duration = duration; segment.startTime = parsed[i].CueTime; segment.timescale = 1000; // hardcoded for ms start = parsed[i].CueTracks[0].ClusterPosition + segmentStart; if (i < parsed.length - 1) { end = parsed[i + 1].CueTracks[0].ClusterPosition + segmentStart - 1; } else { end = segmentEnd - 1; } segment.mediaRange = start + '-' + end; segments.push(segment); } log('Parsed cues: ' + segments.length + ' cues.'); return segments; } function parseEbmlHeader(data, media, theRange, callback) { var ebmlParser = (0, _streamingUtilsEBMLParser2['default'])(context).create({ data: data }); var duration = undefined; var segments = undefined; var parts = theRange.split('-'); var request = null; var info = { url: media, range: { start: parseFloat(parts[0]), end: parseFloat(parts[1]) }, request: request }; var segmentEnd = undefined; var segmentStart = undefined; log('Parse EBML header: ' + info.url); // skip over the header itself ebmlParser.skipOverElement(WebM.EBML); ebmlParser.consumeTag(WebM.Segment); // segments start here segmentEnd = ebmlParser.getMatroskaCodedNum(); segmentEnd += ebmlParser.getPos(); segmentStart = ebmlParser.getPos(); // skip over any top level elements to get to the segment info while (ebmlParser.moreData() && !ebmlParser.consumeTagAndSize(WebM.Segment.Info, true)) { if (!(ebmlParser.skipOverElement(WebM.Segment.SeekHead, true) || ebmlParser.skipOverElement(WebM.Segment.Tracks, true) || ebmlParser.skipOverElement(WebM.Segment.Cues, true) || ebmlParser.skipOverElement(WebM.Void, true))) { throw new Error('no valid top level element found'); } } // we only need one thing in segment info, duration while (duration === undefined) { var infoTag = ebmlParser.getMatroskaCodedNum(true); var infoElementSize = ebmlParser.getMatroskaCodedNum(); switch (infoTag) { case WebM.Segment.Info.Duration.tag: duration = ebmlParser[WebM.Segment.Info.Duration.parse](infoElementSize); break; default: ebmlParser.setPos(ebmlParser.getPos() + infoElementSize); break; } } // once we have what we need from segment info, we jump right to the // cues request = getFragmentRequest(info); var onload = function onload(response) { segments = parseSegments(response, segmentStart, segmentEnd, duration); callback(segments); }; var onloadend = function onloadend() { log('Download Error: Cues ' + info.url); callback(null); }; xhrLoader.load({ request: request, success: onload, error: onloadend }); log('Perform cues load: ' + info.url + ' bytes=' + info.range.start + '-' + info.range.end); } function loadInitialization(representation, loadingInfo) { var request = null; var baseUrl = baseURLController.resolve(representation.path); var media = baseUrl ? baseUrl.url : undefined; var initRange = representation.range.split('-'); var info = loadingInfo || { range: { start: parseFloat(initRange[0]), end: parseFloat(initRange[1]) }, request: request, url: media, init: true }; log('Start loading initialization.'); request = getFragmentRequest(info); var onload = function onload() { // note that we don't explicitly set rep.initialization as this // will be computed when all BaseURLs are resolved later eventBus.trigger(_coreEventsEvents2['default'].INITIALIZATION_LOADED, { representation: representation }); }; var onloadend = function onloadend() { eventBus.trigger(_coreEventsEvents2['default'].INITIALIZATION_LOADED, { representation: representation }); }; xhrLoader.load({ request: request, success: onload, error: onloadend }); log('Perform init load: ' + info.url); } function loadSegments(representation, type, theRange, callback) { var request = null; var baseUrl = baseURLController.resolve(representation.path); var media = baseUrl ? baseUrl.url : undefined; var bytesToLoad = 8192; var info = { bytesLoaded: 0, bytesToLoad: bytesToLoad, range: { start: 0, end: bytesToLoad }, request: request, url: media, init: false }; callback = !callback ? onLoaded : callback; request = getFragmentRequest(info); // first load the header, but preserve the manifest range so we can // load the cues after parsing the header // NOTE: we expect segment info to appear in the first 8192 bytes log('Parsing ebml header'); var onload = function onload(response) { parseEbmlHeader(response, media, theRange, function (segments) { callback(segments, representation, type); }); }; var onloadend = function onloadend() { callback(null, representation, type); }; xhrLoader.load({ request: request, success: onload, error: onloadend }); } function onLoaded(segments, representation, type) { if (segments) { eventBus.trigger(_coreEventsEvents2['default'].SEGMENTS_LOADED, { segments: segments, representation: representation, mediaType: type }); } else { eventBus.trigger(_coreEventsEvents2['default'].SEGMENTS_LOADED, { segments: null, representation: representation, mediaType: type, error: new Error(null, 'error loading segments', null) }); } } function getFragmentRequest(info) { var request = new _streamingVoFragmentRequest2['default'](); request.type = info.init ? _streamingVoMetricsHTTPRequest.HTTPRequest.INIT_SEGMENT_TYPE : _streamingVoMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE; request.url = info.url; request.range = info.range.start + '-' + info.range.end; return request; } function reset() { errHandler = null; requestModifier = null; log = null; } instance = { setConfig: setConfig, initialize: initialize, loadInitialization: loadInitialization, loadSegments: loadSegments, reset: reset }; setup(); return instance; } WebmSegmentBaseLoader.__dashjs_factory_name = 'WebmSegmentBaseLoader'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(WebmSegmentBaseLoader); module.exports = exports['default']; },{"../core/Debug":13,"../core/EventBus":14,"../core/FactoryMaker":15,"../core/events/Events":18,"../streaming/XHRLoader":64,"../streaming/utils/EBMLParser":156,"../streaming/utils/ErrorHandler":157,"../streaming/utils/RequestModifier":162,"../streaming/vo/FragmentRequest":169,"../streaming/vo/metrics/HTTPRequest":185,"./vo/Segment":52}],25:[function(require,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 TCP_CONNECTION = 'TcpList'; exports.TCP_CONNECTION = TCP_CONNECTION; var HTTP_REQUEST = 'HttpList'; exports.HTTP_REQUEST = HTTP_REQUEST; var TRACK_SWITCH = 'RepSwitchList'; exports.TRACK_SWITCH = TRACK_SWITCH; var BUFFER_LEVEL = 'BufferLevel'; exports.BUFFER_LEVEL = BUFFER_LEVEL; var BUFFER_STATE = 'BufferState'; exports.BUFFER_STATE = BUFFER_STATE; var DVR_INFO = 'DVRInfo'; exports.DVR_INFO = DVR_INFO; var DROPPED_FRAMES = 'DroppedFrames'; exports.DROPPED_FRAMES = DROPPED_FRAMES; var SCHEDULING_INFO = 'SchedulingInfo'; exports.SCHEDULING_INFO = SCHEDULING_INFO; var REQUESTS_QUEUE = 'RequestsQueue'; exports.REQUESTS_QUEUE = REQUESTS_QUEUE; var MANIFEST_UPDATE = 'ManifestUpdate'; exports.MANIFEST_UPDATE = MANIFEST_UPDATE; var MANIFEST_UPDATE_STREAM_INFO = 'ManifestUpdatePeriodInfo'; exports.MANIFEST_UPDATE_STREAM_INFO = MANIFEST_UPDATE_STREAM_INFO; var MANIFEST_UPDATE_TRACK_INFO = 'ManifestUpdateRepresentationInfo'; exports.MANIFEST_UPDATE_TRACK_INFO = MANIFEST_UPDATE_TRACK_INFO; var PLAY_LIST = 'PlayList'; exports.PLAY_LIST = PLAY_LIST; var DVB_ERRORS = 'DVBErrors'; exports.DVB_ERRORS = DVB_ERRORS; },{}],26:[function(require,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 _modelsDashManifestModel = require('../models/DashManifestModel'); var _modelsDashManifestModel2 = _interopRequireDefault(_modelsDashManifestModel); var _DashMetrics = require('../DashMetrics'); var _DashMetrics2 = _interopRequireDefault(_DashMetrics); var _utilsTimelineConverter = require('../utils/TimelineConverter'); var _utilsTimelineConverter2 = _interopRequireDefault(_utilsTimelineConverter); var _streamingControllersAbrController = require('../../streaming/controllers/AbrController'); var _streamingControllersAbrController2 = _interopRequireDefault(_streamingControllersAbrController); var _streamingControllersPlaybackController = require('../../streaming/controllers/PlaybackController'); var _streamingControllersPlaybackController2 = _interopRequireDefault(_streamingControllersPlaybackController); var _streamingControllersStreamController = require('../../streaming/controllers/StreamController'); var _streamingControllersStreamController2 = _interopRequireDefault(_streamingControllersStreamController); var _streamingModelsManifestModel = require('../../streaming/models/ManifestModel'); var _streamingModelsManifestModel2 = _interopRequireDefault(_streamingModelsManifestModel); var _streamingModelsMetricsModel = require('../../streaming/models/MetricsModel'); var _streamingModelsMetricsModel2 = _interopRequireDefault(_streamingModelsMetricsModel); var _streamingModelsMediaPlayerModel = require('../../streaming/models/MediaPlayerModel'); var _streamingModelsMediaPlayerModel2 = _interopRequireDefault(_streamingModelsMediaPlayerModel); var _streamingUtilsDOMStorage = require('../../streaming/utils/DOMStorage'); var _streamingUtilsDOMStorage2 = _interopRequireDefault(_streamingUtilsDOMStorage); var _streamingVoError = require('../../streaming/vo/Error'); var _streamingVoError2 = _interopRequireDefault(_streamingVoError); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _streamingMediaPlayerEvents = require('../../streaming/MediaPlayerEvents'); var _streamingMediaPlayerEvents2 = _interopRequireDefault(_streamingMediaPlayerEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _voRepresentation = require('../vo/Representation'); var _voRepresentation2 = _interopRequireDefault(_voRepresentation); function RepresentationController() { var SEGMENTS_UPDATE_FAILED_ERROR_CODE = 1; var context = this.context; var eventBus = (0, _coreEventBus2['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, _streamingControllersAbrController2['default'])(context).getInstance(); streamController = (0, _streamingControllersStreamController2['default'])(context).getInstance(); playbackController = (0, _streamingControllersPlaybackController2['default'])(context).getInstance(); manifestModel = (0, _streamingModelsManifestModel2['default'])(context).getInstance(); metricsModel = (0, _streamingModelsMetricsModel2['default'])(context).getInstance(); domStorage = (0, _streamingUtilsDOMStorage2['default'])(context).getInstance(); timelineConverter = (0, _utilsTimelineConverter2['default'])(context).getInstance(); dashManifestModel = (0, _modelsDashManifestModel2['default'])(context).getInstance(); dashMetrics = (0, _DashMetrics2['default'])(context).getInstance(); mediaPlayerModel = (0, _streamingModelsMediaPlayerModel2['default'])(context).getInstance(); eventBus.on(_coreEventsEvents2['default'].QUALITY_CHANGE_REQUESTED, onQualityChanged, instance); eventBus.on(_coreEventsEvents2['default'].REPRESENTATION_UPDATED, onRepresentationUpdated, instance); eventBus.on(_coreEventsEvents2['default'].WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, instance); eventBus.on(_coreEventsEvents2['default'].BUFFER_LEVEL_UPDATED, onBufferLevelUpdated, 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(_coreEventsEvents2['default'].QUALITY_CHANGE_REQUESTED, onQualityChanged, instance); eventBus.off(_coreEventsEvents2['default'].REPRESENTATION_UPDATED, onRepresentationUpdated, instance); eventBus.off(_coreEventsEvents2['default'].WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, instance); eventBus.off(_coreEventsEvents2['default'].BUFFER_LEVEL_UPDATED, onBufferLevelUpdated, 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(_coreEventsEvents2['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(_coreEventsEvents2['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 || !_voRepresentation2['default'].hasInitialization(availableRepresentations[i]) || (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(_coreEventsEvents2['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(_streamingMediaPlayerEvents2['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 alreadyAdded = false; var postponeTimePeriod = 0; var repInfo; var err; var repSwitch; 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 = playbackController.computeLiveDelay(currentRepresentation.segmentDuration, streamProcessor.getStreamInfo().manifestInfo.DVRWindowSize); postponeTimePeriod = (liveDelay - segmentAvailabilityTimePeriod) * 1000; } if (postponeTimePeriod > 0) { addDVRMetric(); postponeUpdate(postponeTimePeriod); err = new _streamingVoError2['default'](SEGMENTS_UPDATE_FAILED_ERROR_CODE, 'Segments update failed', null); eventBus.trigger(_coreEventsEvents2['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(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, { sender: this, data: data, currentRepresentation: currentRepresentation }); } } function onWallclockTimeUpdated(e) { if (e.isDynamic) { updateAvailabilityWindow(e.isDynamic); } } 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'] = _coreFactoryMaker2['default'].getClassFactory(RepresentationController); module.exports = exports['default']; },{"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../../streaming/MediaPlayerEvents":58,"../../streaming/controllers/AbrController":66,"../../streaming/controllers/PlaybackController":74,"../../streaming/controllers/StreamController":77,"../../streaming/models/ManifestModel":106,"../../streaming/models/MediaPlayerModel":107,"../../streaming/models/MetricsModel":108,"../../streaming/utils/DOMStorage":155,"../../streaming/vo/Error":168,"../DashMetrics":22,"../models/DashManifestModel":27,"../utils/TimelineConverter":43,"../vo/Representation":51}],27:[function(require,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 _voRepresentation = require('../vo/Representation'); var _voRepresentation2 = _interopRequireDefault(_voRepresentation); var _voAdaptationSet = require('../vo/AdaptationSet'); var _voAdaptationSet2 = _interopRequireDefault(_voAdaptationSet); var _voPeriod = require('../vo/Period'); var _voPeriod2 = _interopRequireDefault(_voPeriod); var _voMpd = require('../vo/Mpd'); var _voMpd2 = _interopRequireDefault(_voMpd); var _voUTCTiming = require('../vo/UTCTiming'); var _voUTCTiming2 = _interopRequireDefault(_voUTCTiming); var _utilsTimelineConverter = require('../utils/TimelineConverter'); var _utilsTimelineConverter2 = _interopRequireDefault(_utilsTimelineConverter); var _streamingControllersMediaController = require('../../streaming/controllers/MediaController'); var _streamingControllersMediaController2 = _interopRequireDefault(_streamingControllersMediaController); var _DashAdapter = require('../DashAdapter'); var _DashAdapter2 = _interopRequireDefault(_DashAdapter); var _voEvent = require('../vo/Event'); var _voEvent2 = _interopRequireDefault(_voEvent); var _voBaseURL = require('../vo/BaseURL'); var _voBaseURL2 = _interopRequireDefault(_voBaseURL); var _voEventStream = require('../vo/EventStream'); var _voEventStream2 = _interopRequireDefault(_voEventStream); var _streamingUtilsURLUtils = require('../../streaming/utils/URLUtils'); var _streamingUtilsURLUtils2 = _interopRequireDefault(_streamingUtilsURLUtils); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function DashManifestModel() { var instance = undefined; var context = this.context; var timelineConverter = (0, _utilsTimelineConverter2['default'])(context).getInstance(); //TODO Need to pass this in not bake in var mediaController = (0, _streamingControllersMediaController2['default'])(context).getInstance(); var adaptor = (0, _DashAdapter2['default'])(context).getInstance(); var urlUtils = (0, _streamingUtilsURLUtils2['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')) { // Just check the start of the codecs string var codecs = adaptation.Representation_asArray[0].codecs; if (codecs.search('stpp') === 0 || codecs.search('wvtt') === 0) { 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, streamInfo) { var adaptations = getAdaptationsForType(manifest, periodIndex, type); if (!adaptations || adaptations.length === 0) return null; if (adaptations.length > 1 && streamInfo) { var currentTrack = mediaController.getCurrentTrackFor(type, streamInfo); var allMediaInfoForType = adaptor.getAllMediaInfoForType(manifest, streamInfo, type); for (var i = 0, ln = adaptations.length; i < ln; i++) { if (currentTrack && mediaController.isTracksEqual(currentTrack, allMediaInfoForType[i])) { return adaptations[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 getManifestUpdatePeriod(manifest) { var latencyOfLastUpdate = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; var delay = NaN; if (manifest.hasOwnProperty('minimumUpdatePeriod')) { delay = manifest.minimumUpdatePeriod; } return isNaN(delay) ? delay : Math.max(delay - latencyOfLastUpdate, 1); } 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 _voRepresentation2['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; 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 = '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 { representation.segmentInfoType = 'BaseURL'; } if (segmentInfo) { if (segmentInfo.hasOwnProperty('Initialization')) { initialization = segmentInfo.Initialization; if (initialization.hasOwnProperty('sourceURL')) { representation.initialization = initialization.sourceURL; } else if (initialization.hasOwnProperty('range')) { representation.range = initialization.range; // initialization source url will be determined from // BaseURL when resolved at load time. } } 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 _voAdaptationSet2['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 _voPeriod2['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 _voPeriod2['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 _voPeriod2['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, i); } 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, i) { if (!p) { throw new Error('Period cannot be null or undefined'); } var id = _voPeriod2['default'].DEFAULT_ID + '_' + i; if (p.hasOwnProperty('id') && p.id !== '__proto__') { id = p.id; } return id; } function getMpd(manifest) { var mpd = new _voMpd2['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('minimumUpdatePeriod')) { mpd.minimumUpdatePeriod = manifest.minimumUpdatePeriod; } if (manifest.hasOwnProperty('mediaPresentationDuration')) { mpd.mediaPresentationDuration = manifest.mediaPresentationDuration; } 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 getEndTimeForLastPeriod(manifest, period) { var isDynamic = getIsDynamic(manifest); var periodEnd = undefined; if (manifest.mediaPresentationDuration) { periodEnd = manifest.mediaPresentationDuration; } else if (period.duration) { periodEnd = period.duration; } else if (isDynamic) { periodEnd = Number.POSITIVE_INFINITY; } else { throw new Error('Must have @mediaPresentationDuratio 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 _voEventStream2['default'](); eventStream.period = period; eventStream.timescale = 1; if (eventStreams[i].hasOwnProperty('schemeIdUri')) { eventStream.schemeIdUri = eventStreams[i].schemeIdUri; } else { throw new Error('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 _voEvent2['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 _voEventStream2['default'](); eventStream.timescale = 1; eventStream.representation = representation; if (inbandStreams[i].hasOwnProperty('schemeIdUri')) { eventStream.schemeIdUri = inbandStreams[i].schemeIdUri; } else { throw new Error('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 _voUTCTiming2['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 = []; // if node.BaseURL_asArray and node.baseUri are undefined entries // will be [undefined] which entries.some will just skip var entries = node.BaseURL_asArray || [node.baseUri]; var earlyReturn = false; entries.some(function (entry) { if (entry) { var baseUrl = new _voBaseURL2['default'](); var text = entry.__text || entry; 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 earlyReturn = true; // deal with the specific case where the MPD@BaseURL // is specified and is relative. when no MPD@BaseURL // entries exist, that case is handled by the // [node.baseUri] in the entries definition. if (node.baseUri) { text = node.baseUri + text; } } 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); return earlyReturn; } }); return baseUrls; } function getLocation(manifest) { if (manifest.hasOwnProperty('Location')) { // for now, do not support multiple Locations - // just set Location to the first Location. manifest.Location = manifest.Location_asArray[0]; } // may well be undefined return manifest.Location; } 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, getManifestUpdatePeriod: getManifestUpdatePeriod, getRepresentationCount: getRepresentationCount, getBitrateListForAdaptation: getBitrateListForAdaptation, getRepresentationFor: getRepresentationFor, getRepresentationsForAdaptation: getRepresentationsForAdaptation, getAdaptationsForPeriod: getAdaptationsForPeriod, getRegularPeriods: getRegularPeriods, getMpd: getMpd, getEventsForPeriod: getEventsForPeriod, getEventStreams: getEventStreams, getEventStreamForAdaptationSet: getEventStreamForAdaptationSet, getEventStreamForRepresentation: getEventStreamForRepresentation, getUTCTimingSources: getUTCTimingSources, getBaseURLsFromElement: getBaseURLsFromElement, getRepresentationSortFunction: getRepresentationSortFunction, getLocation: getLocation }; return instance; } DashManifestModel.__dashjs_factory_name = 'DashManifestModel'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(DashManifestModel); module.exports = exports['default']; },{"../../core/FactoryMaker":15,"../../streaming/controllers/MediaController":72,"../../streaming/utils/URLUtils":164,"../DashAdapter":20,"../utils/TimelineConverter":43,"../vo/AdaptationSet":45,"../vo/BaseURL":46,"../vo/Event":47,"../vo/EventStream":48,"../vo/Mpd":49,"../vo/Period":50,"../vo/Representation":51,"../vo/UTCTiming":53}],28:[function(require,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 _streamingUtilsErrorHandler = require('../../streaming/utils/ErrorHandler'); var _streamingUtilsErrorHandler2 = _interopRequireDefault(_streamingUtilsErrorHandler); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _externalsObjectiron = require('../../../externals/objectiron'); var _externalsObjectiron2 = _interopRequireDefault(_externalsObjectiron); var _externalsXml2json = require('../../../externals/xml2json'); var _externalsXml2json2 = _interopRequireDefault(_externalsXml2json); var _matchersStringMatcher = require('./matchers/StringMatcher'); var _matchersStringMatcher2 = _interopRequireDefault(_matchersStringMatcher); var _matchersDurationMatcher = require('./matchers/DurationMatcher'); var _matchersDurationMatcher2 = _interopRequireDefault(_matchersDurationMatcher); var _matchersDateTimeMatcher = require('./matchers/DateTimeMatcher'); var _matchersDateTimeMatcher2 = _interopRequireDefault(_matchersDateTimeMatcher); var _matchersNumericMatcher = require('./matchers/NumericMatcher'); var _matchersNumericMatcher2 = _interopRequireDefault(_matchersNumericMatcher); var _mapsRepresentationBaseValuesMap = require('./maps/RepresentationBaseValuesMap'); var _mapsRepresentationBaseValuesMap2 = _interopRequireDefault(_mapsRepresentationBaseValuesMap); var _mapsSegmentValuesMap = require('./maps/SegmentValuesMap'); var _mapsSegmentValuesMap2 = _interopRequireDefault(_mapsSegmentValuesMap); function DashParser() /*config*/{ var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var errorHandler = (0, _streamingUtilsErrorHandler2['default'])(context).getInstance(); var instance = undefined, matchers = undefined, converter = undefined, objectIron = undefined; function setup() { matchers = [new _matchersDurationMatcher2['default'](), new _matchersDateTimeMatcher2['default'](), new _matchersNumericMatcher2['default'](), new _matchersStringMatcher2['default']() // last in list to take precedence over NumericMatcher ]; converter = new _externalsXml2json2['default']({ escapeMode: false, attributePrefix: '', arrayAccessForm: 'property', emptyNodeForm: 'object', stripWhitespaces: false, enableToStringFunc: false, ignoreRoot: true, matchers: matchers }); objectIron = new _externalsObjectiron2['default']([new _mapsRepresentationBaseValuesMap2['default'](), new _mapsSegmentValuesMap2['default']()]); } function parse(data, xlinkController) { var manifest; try { var startTime = window.performance.now(); manifest = converter.xml_str2json(data); if (!manifest) { throw new Error('parser error'); } var jsonTime = window.performance.now(); objectIron.run(manifest); var ironedTime = window.performance.now(); xlinkController.setMatchers(matchers); xlinkController.setIron(objectIron); log('Parsing complete: ( xml2json: ' + (jsonTime - startTime).toPrecision(3) + 'ms, objectiron: ' + (ironedTime - jsonTime).toPrecision(3) + 'ms, total: ' + ((ironedTime - startTime) / 1000).toPrecision(3) + 's)'); } catch (err) { errorHandler.manifestError('parsing the manifest failed', 'parse', data, err); return null; } return manifest; } instance = { parse: parse }; setup(); return instance; } DashParser.__dashjs_factory_name = 'DashParser'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(DashParser); module.exports = exports['default']; },{"../../../externals/objectiron":10,"../../../externals/xml2json":11,"../../core/Debug":13,"../../core/FactoryMaker":15,"../../streaming/utils/ErrorHandler":157,"./maps/RepresentationBaseValuesMap":31,"./maps/SegmentValuesMap":32,"./matchers/DateTimeMatcher":34,"./matchers/DurationMatcher":35,"./matchers/NumericMatcher":36,"./matchers/StringMatcher":37}],29:[function(require,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. */ /** * @classdesc a property belonging to a MapNode */ "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 CommonProperty = (function () { function CommonProperty(name, merge) { _classCallCheck(this, CommonProperty); var getDefaultMergeForName = function getDefaultMergeForName(n) { return n && n.length && n.charAt(0) === n.charAt(0).toUpperCase(); }; this._name = name; this._merge = merge !== undefined ? merge : getDefaultMergeForName(name); } _createClass(CommonProperty, [{ key: "name", get: function get() { return this._name; } }, { key: "merge", get: function get() { return this._merge; } }]); return CommonProperty; })(); exports["default"] = CommonProperty; module.exports = exports["default"]; },{}],30:[function(require,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. */ /** * @classdesc a node at some level in a ValueMap */ '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 _CommonProperty = require('./CommonProperty'); var _CommonProperty2 = _interopRequireDefault(_CommonProperty); var MapNode = (function () { function MapNode(name, properties, children, isRoot, isArray) { var _this = this; _classCallCheck(this, MapNode); this._name = name || ''; this._properties = []; this._children = children || []; this._isRoot = isRoot || false; this._isArray = isArray || true; if (Array.isArray(properties)) { properties.forEach(function (p) { _this._properties.push(new _CommonProperty2['default'](p)); }); } } _createClass(MapNode, [{ key: 'name', get: function get() { return this._name; } }, { key: 'isRoot', get: function get() { return this._isRoot; } }, { key: 'isArray', get: function get() { return this._isArray; } }, { key: 'children', get: function get() { return this._children; } }, { key: 'properties', get: function get() { return this._properties; } }]); return MapNode; })(); exports['default'] = MapNode; module.exports = exports['default']; },{"./CommonProperty":29}],31:[function(require,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. */ /** * @classdesc a RepresentationBaseValuesMap type for input to objectiron */ '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 _MapNode2 = require('./MapNode'); var _MapNode3 = _interopRequireDefault(_MapNode2); var RepresentationBaseValuesMap = (function (_MapNode) { _inherits(RepresentationBaseValuesMap, _MapNode); function RepresentationBaseValuesMap() { _classCallCheck(this, RepresentationBaseValuesMap); var commonProperties = ['profiles', 'width', 'height', 'sar', 'frameRate', 'audioSamplingRate', 'mimeType', 'segmentProfiles', 'codecs', 'maximumSAPPeriod', 'startWithSAP', 'maxPlayoutRate', 'codingDependency', 'scanType', 'FramePacking', 'AudioChannelConfiguration', 'ContentProtection', 'EssentialProperty', 'SupplementalProperty', 'InbandEventStream']; _get(Object.getPrototypeOf(RepresentationBaseValuesMap.prototype), 'constructor', this).call(this, 'AdaptationSet', commonProperties, [new _MapNode3['default']('Representation', commonProperties, [new _MapNode3['default']('SubRepresentation', commonProperties)])]); } return RepresentationBaseValuesMap; })(_MapNode3['default']); exports['default'] = RepresentationBaseValuesMap; module.exports = exports['default']; },{"./MapNode":30}],32:[function(require,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. */ /** * @classdesc a SegmentValuesMap type for input to objectiron */ '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 _MapNode2 = require('./MapNode'); var _MapNode3 = _interopRequireDefault(_MapNode2); var SegmentValuesMap = (function (_MapNode) { _inherits(SegmentValuesMap, _MapNode); function SegmentValuesMap() { _classCallCheck(this, SegmentValuesMap); var commonProperties = ['SegmentBase', 'SegmentTemplate', 'SegmentList']; _get(Object.getPrototypeOf(SegmentValuesMap.prototype), 'constructor', this).call(this, 'Period', commonProperties, [new _MapNode3['default']('AdaptationSet', commonProperties, [new _MapNode3['default']('Representation', commonProperties)])]); } return SegmentValuesMap; })(_MapNode3['default']); exports['default'] = SegmentValuesMap; module.exports = exports['default']; },{"./MapNode":30}],33:[function(require,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. */ /** * @classdesc a base type for matching and converting types in manifest to * something more useful */ "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 BaseMatcher = (function () { function BaseMatcher(test, converter) { _classCallCheck(this, BaseMatcher); this._test = test; this._converter = converter; } _createClass(BaseMatcher, [{ key: "test", get: function get() { return this._test; } }, { key: "converter", get: function get() { return this._converter; } }]); return BaseMatcher; })(); exports["default"] = BaseMatcher; module.exports = exports["default"]; },{}],34:[function(require,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. */ /** * @classdesc matches and converts xs:datetime to Date */ '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 _BaseMatcher2 = require('./BaseMatcher'); var _BaseMatcher3 = _interopRequireDefault(_BaseMatcher2); 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 DateTimeMatcher = (function (_BaseMatcher) { _inherits(DateTimeMatcher, _BaseMatcher); function DateTimeMatcher() { _classCallCheck(this, DateTimeMatcher); _get(Object.getPrototypeOf(DateTimeMatcher.prototype), 'constructor', this).call(this, function (attr) { return datetimeRegex.test(attr.value); }, function (str) { var match = datetimeRegex.exec(str); var utcDate = undefined; // 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); }); } return DateTimeMatcher; })(_BaseMatcher3['default']); exports['default'] = DateTimeMatcher; module.exports = exports['default']; },{"./BaseMatcher":33}],35:[function(require,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. */ /** * @classdesc matches and converts xs:duration to seconds */ '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 _BaseMatcher2 = require('./BaseMatcher'); var _BaseMatcher3 = _interopRequireDefault(_BaseMatcher2); var durationRegex = /^([-])?P(([\d.]*)Y)?(([\d.]*)M)?(([\d.]*)D)?T?(([\d.]*)H)?(([\d.]*)M)?(([\d.]*)S)?/; 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 DurationMatcher = (function (_BaseMatcher) { _inherits(DurationMatcher, _BaseMatcher); function DurationMatcher() { _classCallCheck(this, DurationMatcher); _get(Object.getPrototypeOf(DurationMatcher.prototype), 'constructor', this).call(this, function (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; }, function (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; }); } return DurationMatcher; })(_BaseMatcher3['default']); exports['default'] = DurationMatcher; module.exports = exports['default']; },{"./BaseMatcher":33}],36:[function(require,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. */ /** * @classdesc Matches and converts xs:numeric to float */ '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 _BaseMatcher2 = require('./BaseMatcher'); var _BaseMatcher3 = _interopRequireDefault(_BaseMatcher2); var numericRegex = /^[-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?$/; var NumericMatcher = (function (_BaseMatcher) { _inherits(NumericMatcher, _BaseMatcher); function NumericMatcher() { _classCallCheck(this, NumericMatcher); _get(Object.getPrototypeOf(NumericMatcher.prototype), 'constructor', this).call(this, function (attr) { return numericRegex.test(attr.value); }, function (str) { return parseFloat(str); }); } return NumericMatcher; })(_BaseMatcher3['default']); exports['default'] = NumericMatcher; module.exports = exports['default']; },{"./BaseMatcher":33}],37:[function(require,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. */ /** * @classdesc Matches and converts xs:string to string, but only for specific attributes on specific nodes */ '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 _BaseMatcher2 = require('./BaseMatcher'); var _BaseMatcher3 = _interopRequireDefault(_BaseMatcher2); var StringMatcher = (function (_BaseMatcher) { _inherits(StringMatcher, _BaseMatcher); function StringMatcher() { _classCallCheck(this, StringMatcher); _get(Object.getPrototypeOf(StringMatcher.prototype), 'constructor', this).call(this, function (attr, nodeName) { var stringAttrsInElements = { 'MPD': ['id', 'profiles'], 'Period': ['id'], 'BaseURL': ['serviceLocation', 'byteRange'], 'SegmentBase': ['indexRange'], 'Initialization': ['range'], 'RepresentationIndex': ['range'], 'SegmentList': ['indexRange'], 'BitstreamSwitching': ['range'], 'SegmentURL': ['mediaRange', 'indexRange'], 'SegmentTemplate': ['indexRange', 'media', 'index', 'initialization', 'bitstreamSwitching'], 'AssetIdentifier': ['value', 'id'], 'EventStream': ['value'], 'AdaptationSet': ['profiles', 'mimeType', 'segmentProfiles', 'codecs', 'contentType'], 'FramePacking': ['value', 'id'], 'AudioChannelConfiguration': ['value', 'id'], 'ContentProtection': ['value', 'id'], 'EssentialProperty': ['value', 'id'], 'SupplementalProperty': ['value', 'id'], 'InbandEventStream': ['value', 'id'], 'Accessibility': ['value', 'id'], 'Role': ['value', 'id'], 'Rating': ['value', 'id'], 'Viewpoint': ['value', 'id'], 'ContentComponent': ['contentType'], 'Representation': ['id', 'dependencyId', 'mediaStreamStructureId'], 'Subset': ['id'], 'Metrics': ['metrics'], 'Reporting': ['value', 'id'] }; if (stringAttrsInElements.hasOwnProperty(nodeName)) { var attrNames = stringAttrsInElements[nodeName]; if (attrNames !== undefined) { return attrNames.indexOf(attr.name) >= 0; } else { return false; } } return false; }, function (str) { return String(str); }); } return StringMatcher; })(_BaseMatcher3['default']); exports['default'] = StringMatcher; module.exports = exports['default']; },{"./BaseMatcher":33}],38:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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 subsBox = isoFile.getBox('subs'); var sampleDuration, sampleCompositionTimeOffset, sampleCount, sampleSize, sampleDts, sampleList, sample, i, j, dataOffset, sequenceNumber, totalDuration, sampleWithSubsIndex; sequenceNumber = mfhdBox.sequence_number; sampleCount = trunBox.sample_count; sampleDts = tfdtBox.baseMediaDecodeTime; dataOffset = (tfhdBox.base_data_offset || 0) + (trunBox.data_offset || 0); sampleList = []; sampleWithSubsIndex = 0; 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; var sampleData = { 'dts': sampleDts, 'cts': sampleDts + sampleCompositionTimeOffset, 'duration': sampleDuration, 'offset': moofBox.offset + dataOffset, 'size': sampleSize, 'subSizes': [sampleSize] }; if (subsBox && sampleWithSubsIndex < subsBox.samples_with_subsamples.length && subsBox.samples_with_subsamples[sampleWithSubsIndex].nr == i + 1) { sampleData.subSizes = []; for (j = 0; j < subsBox.samples_with_subsamples[sampleWithSubsIndex].subsamples.length; j++) { var subSize = subsBox.samples_with_subsamples[sampleWithSubsIndex].subsamples[j].size; sampleData.subSizes.push(subSize); } sampleWithSubsIndex++; } sampleList.push(sampleData); 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'] = _coreFactoryMaker2['default'].getSingletonFactory(FragmentedTextBoxParser); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],39:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _SegmentsUtils = require('./SegmentsUtils'); 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 len = list.SegmentURL_asArray.length; var segments = []; var periodSegIdx, seg, s, range, startIdx, endIdx, start; start = representation.startNumber; range = (0, _SegmentsUtils.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, _SegmentsUtils.getIndexBasedSegment)(timelineConverter, isDynamic, representation, periodSegIdx); seg.replacementTime = (start + periodSegIdx - 1) * representation.segmentDuration; seg.media = s.media ? s.media : ''; 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 = _coreFactoryMaker2['default'].getClassFactory(ListSegmentsGetter); exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15,"./SegmentsUtils":41}],40:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _TimelineSegmentsGetter = require('./TimelineSegmentsGetter'); var _TimelineSegmentsGetter2 = _interopRequireDefault(_TimelineSegmentsGetter); var _TemplateSegmentsGetter = require('./TemplateSegmentsGetter'); var _TemplateSegmentsGetter2 = _interopRequireDefault(_TemplateSegmentsGetter); var _ListSegmentsGetter = require('./ListSegmentsGetter'); var _ListSegmentsGetter2 = _interopRequireDefault(_ListSegmentsGetter); function SegmentsGetter(config, isDynamic) { var context = this.context; var instance = undefined, timelineSegmentsGetter = undefined, templateSegmentsGetter = undefined, listSegmentsGetter = undefined; function setup() { timelineSegmentsGetter = (0, _TimelineSegmentsGetter2['default'])(context).create(config, isDynamic); templateSegmentsGetter = (0, _TemplateSegmentsGetter2['default'])(context).create(config, isDynamic); listSegmentsGetter = (0, _ListSegmentsGetter2['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 = _coreFactoryMaker2['default'].getClassFactory(SegmentsGetter); exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15,"./ListSegmentsGetter":39,"./TemplateSegmentsGetter":42,"./TimelineSegmentsGetter":44}],41:[function(require,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 _voSegment = require('./../vo/Segment'); var _voSegment2 = _interopRequireDefault(_voSegment); 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; if (!url) { return url; } // keep looping round until all instances of 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 $...$ 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 _voSegment2['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 _voSegment2['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":52}],42:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _SegmentsUtils = require('./SegmentsUtils'); 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, _SegmentsUtils.decideSegmentListRangeForTemplate)(timelineConverter, isDynamic, representation, requestedTime, index, availabilityUpperLimit); } startIdx = segmentRange.start; endIdx = segmentRange.end; for (periodSegIdx = startIdx; periodSegIdx <= endIdx; periodSegIdx++) { seg = (0, _SegmentsUtils.getIndexBasedSegment)(timelineConverter, isDynamic, representation, periodSegIdx); seg.replacementTime = (start + periodSegIdx - 1) * representation.segmentDuration; url = template.media; url = (0, _SegmentsUtils.replaceTokenForTemplate)(url, 'Number', seg.replacementNumber); url = (0, _SegmentsUtils.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 = _coreFactoryMaker2['default'].getClassFactory(TemplateSegmentsGetter); exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15,"./SegmentsUtils":41}],43:[function(require,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 _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function TimelineConverter() { var context = this.context; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, clientServerTimeShift = undefined, isClientServerTimeSyncCompleted = undefined, expectedLiveEdge = undefined; function initialize() { clientServerTimeShift = 0; isClientServerTimeSyncCompleted = false; expectedLiveEdge = NaN; eventBus.on(_coreEventsEvents2['default'].TIME_SYNCHRONIZATION_COMPLETED, onTimeSyncComplete, this); } function isTimeSyncCompleted() { return isClientServerTimeSyncCompleted; } function setTimeSyncCompleted(value) { isClientServerTimeSyncCompleted = value; } function getClientTimeOffset() { return clientServerTimeShift; } function setClientTimeOffset(value) { clientServerTimeShift = value; } 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) { //console.log("XXX", wallTime.getTime() - period.mpd.availabilityStartTime.getTime(), clientServerTimeShift * 1000, clientServerTimeShift, period.mpd.availabilityStartTime.getTime()) 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) { // Static Range Finder var period = representation.adaptation.period; var range = { start: period.start, end: period.start + period.duration }; if (!isDynamic) return range; if (!isClientServerTimeSyncCompleted && representation.segmentAvailabilityRange) { return representation.segmentAvailabilityRange; } //Dyanmic Range Finder var d = representation.segmentDuration || (representation.segments && representation.segments.length ? representation.segments[representation.segments.length - 1].duration : 0); var now = calcPresentationTimeFromWallTime(new Date(), period); var periodEnd = period.start + period.duration; range.start = Math.max(now - period.mpd.timeShiftBufferDepth, period.start); range.end = now >= periodEnd && now - d < periodEnd ? periodEnd - d : now - d; 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; } /* * We need to figure out if we want to timesync for segmentTimeine where useCalculatedLiveEdge = true * seems we figure out client offset based on logic in liveEdgeFinder getLiveEdge timelineConverter.setClientTimeOffset(liveEdge - representationInfo.DVRWindow.end); * FYI StreamController's onManifestUpdated entry point to timeSync * */ function onTimeSyncComplete(e) { if (isClientServerTimeSyncCompleted) return; if (e.offset !== undefined) { setClientTimeOffset(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(_coreEventsEvents2['default'].TIME_SYNCHRONIZATION_COMPLETED, onTimeSyncComplete, this); clientServerTimeShift = 0; isClientServerTimeSyncCompleted = false; expectedLiveEdge = NaN; } instance = { initialize: initialize, isTimeSyncCompleted: isTimeSyncCompleted, setTimeSyncCompleted: setTimeSyncCompleted, getClientTimeOffset: getClientTimeOffset, setClientTimeOffset: setClientTimeOffset, 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'] = _coreFactoryMaker2['default'].getSingletonFactory(TimelineConverter); module.exports = exports['default']; },{"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18}],44:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _SegmentsUtils = require('./SegmentsUtils'); function TimelineSegmentsGetter(config, isDynamic) { var timelineConverter = config.timelineConverter; var instance = undefined; function getSegmentsFromTimeline(representation, requestedTime, index, availabilityUpperLimit) { var base = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentTemplate || representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentList; var timeline = base.SegmentTimeline; var list = base.SegmentURL_asArray; 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, i) { var media = base.media; var mediaRange = s.mediaRange; if (list) { media = list[i].media || ''; mediaRange = list[i].mediaRange; } return (0, _SegmentsUtils.getTimeBasedSegment)(timelineConverter, isDynamic, representation, time, s.d, fTimescale, media, mediaRange, availabilityIdx); }; fTimescale = representation.timescale; fragments = timeline.S_asArray; calculatedRange = (0, _SegmentsUtils.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, availabilityIdx)); } } 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, availabilityIdx)); } else if (scaledTime >= requiredMediaTime - frag.d / fTimescale * 1.5) { isStartSegmentForRequestedTimeFound = true; segments.push(createSegment(frag, availabilityIdx)); } } 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 = _coreFactoryMaker2['default'].getClassFactory(TimelineSegmentsGetter); exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15,"./SegmentsUtils":41}],45:[function(require,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"]; },{}],46:[function(require,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']; },{}],47:[function(require,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']; },{}],48:[function(require,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']; },{}],49:[function(require,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.minimumUpdatePeriod = NaN; this.mediaPresentationDuration = NaN; }; exports["default"] = Mpd; module.exports = exports["default"]; },{}],50:[function(require,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']; },{}],51:[function(require,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 Representation = (function () { 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; } _createClass(Representation, null, [{ key: 'hasInitialization', value: function hasInitialization(r) { return r.initialization !== null || (r.segmentInfoType !== 'BaseURL' || r.segmentInfoType !== 'SegmentBase') && r.range !== null; } }, { key: 'hasSegments', value: function hasSegments(r) { return r.segmentInfoType !== 'BaseURL' && r.segmentInfoType !== 'SegmentBase' && !r.indexRange; } }]); return Representation; })(); exports['default'] = Representation; module.exports = exports['default']; },{}],52:[function(require,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"]; },{}],53:[function(require,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']; },{}],54:[function(require,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 _XHRLoader = require('./XHRLoader'); var _XHRLoader2 = _interopRequireDefault(_XHRLoader); var _voHeadRequest = require('./vo/HeadRequest'); var _voHeadRequest2 = _interopRequireDefault(_voHeadRequest); var _voError = require('./vo/Error'); var _voError2 = _interopRequireDefault(_voError); var _coreEventBus = require('./../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('./../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, xhrLoader = undefined; function setup() { xhrLoader = (0, _XHRLoader2['default'])(context).create({ errHandler: config.errHandler, metricsModel: config.metricsModel, requestModifier: config.requestModifier }); } function checkForExistence(request) { var report = function report(success) { eventBus.trigger(_coreEventsEvents2['default'].CHECK_FOR_EXISTENCE_COMPLETED, { request: request, exists: success }); }; if (request) { var headRequest = new _voHeadRequest2['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(_coreEventsEvents2['default'].LOADING_COMPLETED, { request: request, response: data || null, error: error || null, sender: instance }); }; if (request) { xhrLoader.load({ request: request, progress: function progress() { eventBus.trigger(_coreEventsEvents2['default'].LOADING_PROGRESS, { request: request }); }, success: function success(data) { report(data); }, error: function error(xhr, statusText, errorText) { report(undefined, new _voError2['default'](FRAGMENT_LOADER_ERROR_LOADING_FAILURE, errorText, statusText)); } }); } else { report(undefined, new _voError2['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 = _coreFactoryMaker2['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":15,"./../core/EventBus":14,"./../core/events/Events":18,"./XHRLoader":64,"./vo/Error":168,"./vo/HeadRequest":170}],55:[function(require,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 _controllersXlinkController = require('./controllers/XlinkController'); var _controllersXlinkController2 = _interopRequireDefault(_controllersXlinkController); var _XHRLoader = require('./XHRLoader'); var _XHRLoader2 = _interopRequireDefault(_XHRLoader); var _utilsURLUtils = require('./utils/URLUtils'); var _utilsURLUtils2 = _interopRequireDefault(_utilsURLUtils); var _voTextRequest = require('./vo/TextRequest'); var _voTextRequest2 = _interopRequireDefault(_voTextRequest); var _voError = require('./vo/Error'); var _voError2 = _interopRequireDefault(_voError); var _voMetricsHTTPRequest = require('./vo/metrics/HTTPRequest'); var _coreEventBus = require('../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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, _coreEventBus2['default'])(context).getInstance(); var urlUtils = (0, _utilsURLUtils2['default'])(context).getInstance(); var parser = config.parser; var instance = undefined, xhrLoader = undefined, xlinkController = undefined; function setup() { eventBus.on(_coreEventsEvents2['default'].XLINK_READY, onXlinkReady, instance); xhrLoader = (0, _XHRLoader2['default'])(context).create({ errHandler: config.errHandler, metricsModel: config.metricsModel, requestModifier: config.requestModifier }); xlinkController = (0, _controllersXlinkController2['default'])(context).create({ errHandler: config.errHandler, metricsModel: config.metricsModel, requestModifier: config.requestModifier }); } function onXlinkReady(event) { eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_MANIFEST_LOADED, { manifest: event.manifest }); } function load(url) { var request = new _voTextRequest2['default'](url, _voMetricsHTTPRequest.HTTPRequest.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 // also handily resolves relative MPD URLs to absolute if (xhr.responseURL && xhr.responseURL !== url) { baseUri = urlUtils.parseBaseUrl(xhr.responseURL); actualUrl = xhr.responseURL; } else { // usually this case will be caught and resolved by // xhr.responseURL above but it is not available for IE11 // baseUri must be absolute for BaseURL resolution later if (urlUtils.isRelative(url)) { url = urlUtils.resolve(url, window.location.href); } 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(_coreEventsEvents2['default'].INTERNAL_MANIFEST_LOADED, { manifest: null, error: new _voError2['default'](MANIFEST_LOADER_ERROR_PARSING_FAILURE, MANIFEST_LOADER_MESSAGE_PARSING_FAILURE) }); } }, error: function error(xhr, statusText, errorText) { eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_MANIFEST_LOADED, { manifest: null, error: new _voError2['default'](MANIFEST_LOADER_ERROR_LOADING_FAILURE, 'Failed loading manifest: ' + url + ', ' + errorText) }); } }); } function reset() { eventBus.off(_coreEventsEvents2['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 = _coreFactoryMaker2['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":14,"../core/FactoryMaker":15,"../core/events/Events":18,"./XHRLoader":64,"./controllers/XlinkController":80,"./utils/URLUtils":164,"./vo/Error":168,"./vo/TextRequest":176,"./vo/metrics/HTTPRequest":185}],56:[function(require,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 _coreEventBus = require('../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _modelsMediaPlayerModel = require('./models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _coreDebug = require('../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); function ManifestUpdater() { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, refreshDelay = undefined, refreshTimer = undefined, isPaused = undefined, isUpdating = undefined, manifestLoader = undefined, manifestModel = undefined, dashManifestModel = undefined, mediaPlayerModel = 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; isPaused = true; mediaPlayerModel = (0, _modelsMediaPlayerModel2['default'])(context).getInstance(); eventBus.on(_coreEventsEvents2['default'].STREAMS_COMPOSED, onStreamsComposed, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_STARTED, onPlaybackStarted, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_PAUSED, onPlaybackPaused, this); eventBus.on(_coreEventsEvents2['default'].INTERNAL_MANIFEST_LOADED, onManifestLoaded, this); } function setManifest(manifest) { update(manifest); } function getManifestLoader() { return manifestLoader; } function reset() { eventBus.off(_coreEventsEvents2['default'].PLAYBACK_STARTED, onPlaybackStarted, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_PAUSED, onPlaybackPaused, this); eventBus.off(_coreEventsEvents2['default'].STREAMS_COMPOSED, onStreamsComposed, this); eventBus.off(_coreEventsEvents2['default'].INTERNAL_MANIFEST_LOADED, onManifestLoaded, this); stopManifestRefreshTimer(); isPaused = true; isUpdating = false; refreshDelay = NaN; mediaPlayerModel = null; } function stopManifestRefreshTimer() { if (refreshTimer !== null) { clearInterval(refreshTimer); refreshTimer = null; } } function startManifestRefreshTimer() { stopManifestRefreshTimer(); if (!isNaN(refreshDelay)) { log('Refresh manifest in ' + refreshDelay + ' seconds.'); refreshTimer = setTimeout(onRefreshTimer, refreshDelay * 1000); } } function refreshManifest() { isUpdating = true; var manifest = manifestModel.getValue(); var url = manifest.url; var location = dashManifestModel.getLocation(manifest); if (location) { url = location; } manifestLoader.load(url); } function update(manifest) { manifestModel.setValue(manifest); var date = new Date(); var latencyOfLastUpdate = (date.getTime() - manifest.loadedTime.getTime()) / 1000; refreshDelay = dashManifestModel.getManifestUpdatePeriod(manifest, latencyOfLastUpdate); eventBus.trigger(_coreEventsEvents2['default'].MANIFEST_UPDATED, { manifest: manifest }); log('Manifest has been refreshed at ' + date + '[' + date.getTime() / 1000 + '] '); if (!isPaused) { startManifestRefreshTimer(); } } function onRefreshTimer() { if (isPaused && !mediaPlayerModel.getScheduleWhilePaused() || isUpdating) return; refreshManifest(); } function onManifestLoaded(e) { if (!e.error) { update(e.manifest); } } function onPlaybackStarted() /*e*/{ isPaused = false; startManifestRefreshTimer(); } function onPlaybackPaused() /*e*/{ isPaused = true; stopManifestRefreshTimer(); } 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, refreshManifest: refreshManifest, setConfig: setConfig, reset: reset }; return instance; } ManifestUpdater.__dashjs_factory_name = 'ManifestUpdater'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(ManifestUpdater); module.exports = exports['default']; },{"../core/Debug":13,"../core/EventBus":14,"../core/FactoryMaker":15,"../core/events/Events":18,"./models/MediaPlayerModel":107}],57:[function(require,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 _dashVoUTCTiming = require('../dash/vo/UTCTiming'); var _dashVoUTCTiming2 = _interopRequireDefault(_dashVoUTCTiming); var _controllersPlaybackController = require('./controllers/PlaybackController'); var _controllersPlaybackController2 = _interopRequireDefault(_controllersPlaybackController); var _controllersStreamController = require('./controllers/StreamController'); var _controllersStreamController2 = _interopRequireDefault(_controllersStreamController); var _controllersMediaController = require('./controllers/MediaController'); var _controllersMediaController2 = _interopRequireDefault(_controllersMediaController); var _ManifestLoader = require('./ManifestLoader'); var _ManifestLoader2 = _interopRequireDefault(_ManifestLoader); var _utilsLiveEdgeFinder = require('./utils/LiveEdgeFinder'); var _utilsLiveEdgeFinder2 = _interopRequireDefault(_utilsLiveEdgeFinder); var _utilsErrorHandler = require('./utils/ErrorHandler'); var _utilsErrorHandler2 = _interopRequireDefault(_utilsErrorHandler); var _utilsCapabilities = require('./utils/Capabilities'); var _utilsCapabilities2 = _interopRequireDefault(_utilsCapabilities); var _TextTracks = require('./TextTracks'); var _TextTracks2 = _interopRequireDefault(_TextTracks); var _controllersSourceBufferController = require('./controllers/SourceBufferController'); var _controllersSourceBufferController2 = _interopRequireDefault(_controllersSourceBufferController); var _utilsRequestModifier = require('./utils/RequestModifier'); var _utilsRequestModifier2 = _interopRequireDefault(_utilsRequestModifier); var _TextSourceBuffer = require('./TextSourceBuffer'); var _TextSourceBuffer2 = _interopRequireDefault(_TextSourceBuffer); var _modelsURIQueryAndFragmentModel = require('./models/URIQueryAndFragmentModel'); var _modelsURIQueryAndFragmentModel2 = _interopRequireDefault(_modelsURIQueryAndFragmentModel); var _modelsManifestModel = require('./models/ManifestModel'); var _modelsManifestModel2 = _interopRequireDefault(_modelsManifestModel); var _modelsMediaPlayerModel = require('./models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _modelsMetricsModel = require('./models/MetricsModel'); var _modelsMetricsModel2 = _interopRequireDefault(_modelsMetricsModel); var _controllersAbrController = require('./controllers/AbrController'); var _controllersAbrController2 = _interopRequireDefault(_controllersAbrController); var _controllersTimeSyncController = require('./controllers/TimeSyncController'); var _controllersTimeSyncController2 = _interopRequireDefault(_controllersTimeSyncController); var _rulesAbrABRRulesCollection = require('./rules/abr/ABRRulesCollection'); var _rulesAbrABRRulesCollection2 = _interopRequireDefault(_rulesAbrABRRulesCollection); var _modelsVideoModel = require('./models/VideoModel'); var _modelsVideoModel2 = _interopRequireDefault(_modelsVideoModel); var _rulesRulesController = require('./rules/RulesController'); var _rulesRulesController2 = _interopRequireDefault(_rulesRulesController); var _controllersMediaSourceController = require('./controllers/MediaSourceController'); var _controllersMediaSourceController2 = _interopRequireDefault(_controllersMediaSourceController); var _controllersBaseURLController = require('./controllers/BaseURLController'); var _controllersBaseURLController2 = _interopRequireDefault(_controllersBaseURLController); var _coreDebug = require('./../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _coreEventBus = require('./../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('./../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _MediaPlayerEvents = require('./MediaPlayerEvents'); var _MediaPlayerEvents2 = _interopRequireDefault(_MediaPlayerEvents); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreVersion = require('./../core/Version'); //Dash var _dashDashAdapter = require('../dash/DashAdapter'); var _dashDashAdapter2 = _interopRequireDefault(_dashDashAdapter); var _dashParserDashParser = require('../dash/parser/DashParser'); var _dashParserDashParser2 = _interopRequireDefault(_dashParserDashParser); var _dashModelsDashManifestModel = require('../dash/models/DashManifestModel'); var _dashModelsDashManifestModel2 = _interopRequireDefault(_dashModelsDashManifestModel); var _dashDashMetrics = require('../dash/DashMetrics'); var _dashDashMetrics2 = _interopRequireDefault(_dashDashMetrics); var _dashUtilsTimelineConverter = require('../dash/utils/TimelineConverter'); var _dashUtilsTimelineConverter2 = _interopRequireDefault(_dashUtilsTimelineConverter); /** * @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 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, _coreEventBus2['default'])(context).getInstance(); var debug = (0, _coreDebug2['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, videoModel = undefined, textSourceBuffer = undefined; function setup() { mediaPlayerInitialized = false; playbackInitialized = false; autoPlay = true; protectionController = null; protectionData = null; adapter = null; _coreEventsEvents2['default'].extend(_MediaPlayerEvents2['default']); mediaPlayerModel = (0, _modelsMediaPlayerModel2['default'])(context).getInstance(); } /** * 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, _utilsCapabilities2['default'])(context).getInstance(); errHandler = (0, _utilsErrorHandler2['default'])(context).getInstance(); if (!capabilities.supportsMediaSource()) { errHandler.capabilityError('mediasource'); return; } if (mediaPlayerInitialized) return; mediaPlayerInitialized = true; abrController = (0, _controllersAbrController2['default'])(context).getInstance(); playbackController = (0, _controllersPlaybackController2['default'])(context).getInstance(); mediaController = (0, _controllersMediaController2['default'])(context).getInstance(); mediaController.initialize(); dashManifestModel = (0, _dashModelsDashManifestModel2['default'])(context).getInstance(); dashMetrics = (0, _dashDashMetrics2['default'])(context).getInstance(); metricsModel = (0, _modelsMetricsModel2['default'])(context).getInstance(); metricsModel.setConfig({ adapter: createAdaptor() }); restoreDefaultUTCTimingSources(); setAutoPlay(AutoPlay !== undefined ? AutoPlay : true); if (view) { attachView(view); } if (source) { attachSource(source); } log('[dash.js ' + getVersion() + '] ' + '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(); } /** * Returns a Boolean that indicates whether the media is in the process of dynamic. * @return {boolean} * @memberof module:MediaPlayer * @instance */ function isDynamic() { if (!playbackInitialized) { throw PLAYBACK_NOT_INITIALIZED_ERROR; } return playbackController.getIsDynamic(); } /** * 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 video, audio and fragmentedText buffer length is * returned. NaN is returned if an invalid type is requested, the * presentation does not contain that type, or if no arguments are passed * and the presentation does not include any adaption sets of valid media * type. * * @param {string} type - the media type of the buffer * @returns {number} The length of the buffer for the given media type, in * seconds, or NaN * @memberof module:MediaPlayer * @instance */ function getBufferLength(type) { var types = ['video', 'audio', 'fragmentedText']; if (!type) { return types.map(function (t) { return getTracksFor(t).length > 0 ? getDashMetrics().getCurrentBufferLevel(getMetricsFor(t)) : Number.MAX_VALUE; }).reduce(function (p, c) { return Math.min(p, c); }); } else { if (types.indexOf(type) !== -1) { var buffer = getDashMetrics().getCurrentBufferLevel(getMetricsFor(type)); return buffer ? buffer : 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 {number} value - 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 {number} value - 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, or if it is a live stream, then the time will be based on the return value of the {@link module:MediaPlayer#duration duration()} method. * 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 {string} 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); } else 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. * @param {boolean} withDate - default is false. Set to true to append current date to UTC time format. * @returns {string} A formatted time and date string. * @memberof module:MediaPlayer * @instance */ function formatUTC(time, locales, hour12) { var withDate = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3]; var dt = new Date(time * 1000); var d = dt.toLocaleDateString(locales); var t = dt.toLocaleTimeString(locales, { hour12: hour12 }); return withDate ? t + ' ' + d : t; } /** * A utility method which converts seconds into TimeCode (i.e. 300 --> 05:00). * * @param {number} value - 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): *
    *
  1. 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 * dash.js parent module will still be created.
  2. * *
  3. 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).
  4. *
* When you extend you get access to this.context, this.factory and this.parent to operate with in your custom object. *
    *
  • this.context - can be used to pass context for singleton access.
  • *
  • this.factory - can be used to call factory.getSingletonInstance().
  • *
  • this.parent - 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)
  • *
* You must call extend before you call initialize * @see {@link module:MediaPlayer#initialize initialize()} * @param {string} parentNameString - name of parent module * @param {Object} childInstance - overriding object * @param {boolean} override - replace only some methods (true) or the whole object (false) * @memberof module:MediaPlayer * @instance */ function extend(parentNameString, childInstance, override) { _coreFactoryMaker2['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 (0, _coreVersion.getVersionString)(); } /** * Use this method to access the dash.js logging class. * * @returns {Debug} * @memberof module:MediaPlayer * @instance */ function getDebug() { return debug; } /** * @deprecated Since version 2.1.0. Instead use: *
    *
  • {@link module:MediaPlayer#getVideoElement getVideoElement()}
  • *
  • {@link module:MediaPlayer#getSource getSource()}
  • *
  • {@link module:MediaPlayer#getVideoContainer getVideoContainer()}
  • *
  • {@link module:MediaPlayer#getTTMLRenderingDiv getTTMLRenderingDiv()}
  • *
* * @returns {VideoModel} * @memberof module:MediaPlayer * @instance */ function getVideoModel() { if (!videoModel) { throw ELEMENT_NOT_ATTACHED_ERROR; } return videoModel; } /** *

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.

*

Lowering this value will lower latency but may decrease the player's ability to build a stable buffer.

* * @param {number} value - 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); } /** *

Equivalent in seconds of setLiveDelayFragmentCount

*

Lowering this value will lower latency but may decrease the player's ability to build a stable buffer.

*

This value should be less than the manifest duration by a couple of segment durations to avoid playback issues

*

If set, this parameter will take precedence over setLiveDelayFragmentCount and manifest info

* * @param {number} value - 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); } /** * @memberof module:MediaPlayer * @see {@link module:MediaPlayer#setLiveDelay setLiveDelay()} * @instance * @returns {number|undefined} Current live stream delay in seconds when previously set, or `undefined` */ function getLiveDelay() { return mediaPlayerModel.getLiveDelay(); } /** *

Set to true if you would like to override the default live delay and honor the SuggestedPresentationDelay attribute in by the manifest.

* @param {boolean} value * @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 {boolean} enable - Will toggle if feature is enabled. True to enable, False to disable. * @param {number=} ttl - (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 {boolean} enable - Will toggle if feature is enabled. True to enable, False to disable. * @param {number=} [ttl] - (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 {string} type - 'video' or 'audio' are the type options. * @param {number} value - Value in kbps representing the maximum bitrate allowed. * @memberof module:MediaPlayer * @instance */ function setMaxAllowedBitrateFor(type, value) { abrController.setMaxAllowedBitrateFor(type, value); } /** * @param {string} type - '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 {string} type - 'video' or 'audio' are the type options. * @param {number} 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 {string} type - '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); } /** *

Set to false to prevent stream from auto-playing when the view is attached.

* * @param {boolean} value * @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 true * @param {boolean} 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 {string} type * @returns {Object} * @memberof module:MediaPlayer * @instance */ function getMetricsFor(type) { return metricsModel.getReadOnlyMetricsFor(type); } /** * @param {string} 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 {string} type * @param {number} value * @memberof module:MediaPlayer * @instance */ function setQualityFor(type, value) { if (!playbackInitialized) { throw PLAYBACK_NOT_INITIALIZED_ERROR; } abrController.setPlaybackQuality(type, streamController.getActiveStreamInfo(), value); } /** * Update the video element size variables * Should be called on window resize (or any other time player is resized). Fullscreen does trigger a window resize event. * * Once windowResizeEventCalled = true, abrController.checkPortalSize() will use element size variables rather than querying clientWidth every time. * * @memberof module:MediaPlayer * @instance */ function updatePortalSize() { abrController.setElementSize(); abrController.setWindowResizeEventCalled(true); } /** * @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 {boolean} value * @memberof module:MediaPlayer * @instance */ function setLimitBitrateByPortal(value) { abrController.setLimitBitrateByPortal(value); } /** * @memberof module:MediaPlayer * @instance */ function getUsePixelRatioInLimitBitrateByPortal() { return abrController.getUsePixelRatioInLimitBitrateByPortal(); } /** * Sets whether to take into account the device's pixel ratio when defining the portal dimensions. * Useful on, for example, retina displays. * * @param {boolean} value * @memberof module:MediaPlayer * @instance * @default {boolean} false */ function setUsePixelRatioInLimitBitrateByPortal(value) { abrController.setUsePixelRatioInLimitBitrateByPortal(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 {number} 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, _TextSourceBuffer2['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(); } function getCurrentTextTrackIndex() { var idx = NaN; if (textSourceBuffer) { idx = textSourceBuffer.getCurrentTrackIdx(); } return idx; } /** * @param {string} 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 {string} type * @param {number} value - A value of the initial bitrate, kbps * @memberof module:MediaPlayer * @instance */ function setInitialBitrateFor(type, value) { abrController.setInitialBitrateFor(type, value); } /** * @param {string} 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 {string} type * @param {number} value - A value of the initial Representation Ratio * @memberof module:MediaPlayer * @instance */ function setInitialRepresentationRatioFor(type, value) { abrController.setInitialRepresentationRatioFor(type, value); } /** * @param {string} 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 {Object} 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 {string} 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 {string} type * @param {Object} manifest * @param {Object} 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 {string} type * @returns {Object|null} {@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 {string} type * @param {Object} value * @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 {string} type * @returns {Object} * @memberof module:MediaPlayer * @instance */ function getInitialMediaSettingsFor(type) { return mediaController.getInitialSettings(type); } /** * @param {MediaInfo} 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 {string} type * @returns {string} 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 {string} type * @param {string} 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: * * MediaController.TRACK_SELECTION_MODE_HIGHEST_BITRATE * this mode makes the player select the track with a highest bitrate. This mode is a default mode. * * MediaController.TRACK_SELECTION_MODE_WIDEST_RANGE * this mode makes the player select the track with a widest range of bitrates * * @param {string} mode * @memberof module:MediaPlayer * @instance */ function setSelectionModeForInitialTrack(mode) { mediaController.setSelectionModeForInitialTrack(mode); } /** * This method returns the track selection mode. * * @returns {string} 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 {boolean} value * @default {boolean} true * @memberof module:MediaPlayer * @instance */ function setAutoSwitchQuality(value) { abrController.setAutoSwitchBitrateFor('video', value); abrController.setAutoSwitchBitrateFor('audio', value); } /** * @param {string} type - '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 {string} type - 'audio' | 'video' * @param {boolean} value * @default {boolean} true * @memberof module:MediaPlayer * @instance */ function setAutoSwitchQualityFor(type, value) { abrController.setAutoSwitchBitrateFor(type, value); } /** * When enabled, after an ABR up-switch in quality, instead of requesting and appending the next fragment * at the end of the current buffer range it is requested and appended closer to the current time * When enabled, The maximum time to render a higher quality is current time + (1.5 * fragment duration). * * Note, WHen ABR down-switch is detected, we appended the lower quality at the end of the buffer range to preserve the * higher quality media for as long as possible. * * If enabled, it should be noted there are a few cases when the client will not replace inside buffer range but rather * just append at the end. 1. When the buffer level is less than one fragment duration 2. The client * is in an Abandonment State due to recent fragment abandonment event. * * Known issues: * 1. In IE11 with auto switching off, if a user switches to a quality they can not downloaded in time the * fragment may be appended in the same range as the playhead or even in past, in IE11 it may cause a stutter * or stall in playback. * * * @param {boolean} value * @default {boolean} false * @memberof module:MediaPlayer * @instance */ function setFastSwitchEnabled(value) { //TODO we need to look at track switches for adaptation sets. If always replace it works much like this but clears buffer. Maybe too many ways to do same thing. mediaPlayerModel.setFastSwitchEnabled(value); } /** * Enabled by default. Will return the current state of Fast Switch. * @return {boolean} Returns true if FastSwitch ABR is enabled. * @see {@link module:MediaPlayer#setFastSwitchEnabled setFastSwitchEnabled()} * @memberof module:MediaPlayer * @instance */ function getFastSwitchEnabled() { return mediaPlayerModel.getFastSwitchEnabled(); } /** * 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 {boolean} value * @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 {string} url - url the manifest url * @param {function} callback - 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(_coreEventsEvents2['default'].INTERNAL_MANIFEST_LOADED, handler, self); manifestLoader.reset(); }; eventBus.on(_coreEventsEvents2['default'].INTERNAL_MANIFEST_LOADED, handler, self); var uriQueryFragModel = (0, _modelsURIQueryAndFragmentModel2['default'])(context).getInstance(); uriQueryFragModel.initialize(); manifestLoader.load(uriQueryFragModel.parseURI(url)); } /** *

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.

*

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

* @param {string} schemeIdUri -
    *
  • urn:mpeg:dash:utc:http-head:2014
  • *
  • urn:mpeg:dash:utc:http-xsdate:2014
  • *
  • urn:mpeg:dash:utc:http-iso:2014
  • *
  • urn:mpeg:dash:utc:direct:2014
  • *
*

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
  • *
  • urn:mpeg:dash:utc:http-xsdate:2012
  • *
  • urn:mpeg:dash:utc:http-iso:2012
  • *
  • urn:mpeg:dash:utc:direct:2012
  • *
* @param {string} value - Path to a time source. * @default *
    *
  • schemeIdUri:urn:mpeg:dash:utc:http-xsdate:2014
  • *
  • value:http://time.akamai.com
  • *
* @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 _dashVoUTCTiming2['default'](); vo.schemeIdUri = schemeIdUri; vo.value = value; mediaPlayerModel.getUTCTimingSources().push(vo); } /** *

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

* @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); } }); } /** *

Allows you to clear the stored array of time sources.

*

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.

*

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

* * @memberof module:MediaPlayer * @see {@link module:MediaPlayer#restoreDefaultUTCTimingSources restoreDefaultUTCTimingSources()} * @instance */ function clearDefaultUTCTimingSources() { mediaPlayerModel.setUTCTimingSources([]); } /** *

Allows you to restore the default time sources after calling {@link module:MediaPlayer#clearDefaultUTCTimingSources clearDefaultUTCTimingSources()}

* * @default *
    *
  • schemeIdUri:urn:mpeg:dash:utc:http-xsdate:2014
  • *
  • value:http://time.akamai.com
  • *
* * @memberof module:MediaPlayer * @see {@link module:MediaPlayer#addUTCTimingSource addUTCTimingSource()} * @instance */ function restoreDefaultUTCTimingSources() { addUTCTimingSource(_modelsMediaPlayerModel2['default'].DEFAULT_UTC_TIMING_SOURCE.scheme, _modelsMediaPlayerModel2['default'].DEFAULT_UTC_TIMING_SOURCE.value); } /** *

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()}

* * @param {boolean} value - true to enable * @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 {number} 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 {number} 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 {number} 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); } /** * Sets whether withCredentials on all XHR requests is true or false * * @default false * @param {boolean} value * @memberof module:MediaPlayer * @instance * @deprecated since version 2.4 - use setXHRWithCredentialsForType */ function setXHRWithCredentials(value) { setXHRWithCredentialsForType(undefined, value); } /** * Sets whether withCredentials on XHR requests for a particular request * type is true or false * * @default false * @param {string} type - one of HTTPRequest.*_TYPE * @param {boolean} value * @memberof module:MediaPlayer * @instance */ function setXHRWithCredentialsForType(type, value) { mediaPlayerModel.setXHRWithCredentialsForType(type, value); } /** * Gets whether withCredentials on XHR requests for a particular request * type is true or false * * @param {string} type - one of HTTPRequest.*_TYPE * @return {boolean} * @memberof module:MediaPlayer * @instance */ function getXHRWithCredentialsForType(type) { return mediaPlayerModel.getXHRWithCredentialsForType(type); } /** * 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 {boolean} value * @memberof module:MediaPlayer * @instance */ function displayCaptionsOnTop(value) { var textTracks = (0, _TextTracks2['default'])(context).getInstance(); textTracks.setConfig({ videoModel: videoModel }); textTracks.initialize(); textTracks.displayCConTop(value); } /** * Returns instance of Video Container that was attached by calling attachVideoContainer() * @returns {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 {HTMLElement} 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 {Object} element - An HTMLMediaElement that has already been defined in the DOM (or equivalent stub). * @memberof module:MediaPlayer * @instance */ function attachView(element) { if (!mediaPlayerInitialized) { throw MEDIA_PLAYER_NOT_INITIALIZED_ERROR; } videoModel = null; if (element) { videoModel = (0, _modelsVideoModel2['default'])(context).getInstance(); videoModel.initialize(); videoModel.setElement(element); detectProtection(); detectMetricsReporting(); } resetAndInitializePlayback(); } /** * Returns instance of Div that was attached by calling attachTTMLRenderingDiv() * @returns {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 {HTMLDivElement} 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. * * * @throws "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, _modelsURIQueryAndFragmentModel2['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); protectionData = null; protectionController = 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; if (isReady()) { initializePlayback(); } } else if (isReady()) { initializePlayback(); } } function createControllers() { var abrRulesCollection = (0, _rulesAbrABRRulesCollection2['default'])(context).getInstance(); abrRulesCollection.initialize(); var sourceBufferController = (0, _controllersSourceBufferController2['default'])(context).getInstance(); sourceBufferController.setConfig({ dashManifestModel: dashManifestModel }); mediaController.initialize(); mediaController.setConfig({ errHandler: errHandler }); rulesController = (0, _rulesRulesController2['default'])(context).getInstance(); rulesController.initialize(); rulesController.setConfig({ abrRulesCollection: abrRulesCollection }); streamController = (0, _controllersStreamController2['default'])(context).getInstance(); streamController.setConfig({ capabilities: capabilities, manifestLoader: createManifestLoader(), manifestModel: (0, _modelsManifestModel2['default'])(context).getInstance(), dashManifestModel: dashManifestModel, protectionController: protectionController, adapter: adapter, metricsModel: metricsModel, dashMetrics: dashMetrics, liveEdgeFinder: (0, _utilsLiveEdgeFinder2['default'])(context).getInstance(), mediaSourceController: (0, _controllersMediaSourceController2['default'])(context).getInstance(), timeSyncController: (0, _controllersTimeSyncController2['default'])(context).getInstance(), baseURLController: (0, _controllersBaseURLController2['default'])(context).getInstance(), errHandler: errHandler, timelineConverter: (0, _dashUtilsTimelineConverter2['default'])(context).getInstance() }); streamController.initialize(autoPlay, protectionData); abrController.setConfig({ abrRulesCollection: abrRulesCollection, rulesController: rulesController, streamController: streamController }); } function createManifestLoader() { return (0, _ManifestLoader2['default'])(context).create({ errHandler: errHandler, parser: createManifestParser(), metricsModel: metricsModel, requestModifier: (0, _utilsRequestModifier2['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, _dashParserDashParser2['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, _dashDashAdapter2['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(); _coreEventsEvents2['default'].extend(Protection.events); _MediaPlayerEvents2['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 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, isDynamic: isDynamic, 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, getLiveDelay: getLiveDelay, 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, updatePortalSize: updatePortalSize, getLimitBitrateByPortal: getLimitBitrateByPortal, setLimitBitrateByPortal: setLimitBitrateByPortal, getUsePixelRatioInLimitBitrateByPortal: getUsePixelRatioInLimitBitrateByPortal, setUsePixelRatioInLimitBitrateByPortal: setUsePixelRatioInLimitBitrateByPortal, 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, setFastSwitchEnabled: setFastSwitchEnabled, getFastSwitchEnabled: getFastSwitchEnabled, 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, setXHRWithCredentials: setXHRWithCredentials, setXHRWithCredentialsForType: setXHRWithCredentialsForType, getXHRWithCredentialsForType: getXHRWithCredentialsForType, setBufferTimeAtTopQualityLongForm: setBufferTimeAtTopQualityLongForm, setLongFormContentDurationThreshold: setLongFormContentDurationThreshold, setRichBufferThreshold: setRichBufferThreshold, getProtectionController: getProtectionController, attachProtectionController: attachProtectionController, setProtectionData: setProtectionData, enableManifestDateHeaderTimeSource: enableManifestDateHeaderTimeSource, displayCaptionsOnTop: displayCaptionsOnTop, attachVideoContainer: attachVideoContainer, attachTTMLRenderingDiv: attachTTMLRenderingDiv, getCurrentTextTrackIndex: getCurrentTextTrackIndex, reset: reset }; setup(); return instance; } MediaPlayer.__dashjs_factory_name = 'MediaPlayer'; var factory = _coreFactoryMaker2['default'].getClassFactory(MediaPlayer); factory.events = _MediaPlayerEvents2['default']; exports['default'] = factory; module.exports = exports['default']; },{"../core/FactoryMaker":15,"../dash/DashAdapter":20,"../dash/DashMetrics":22,"../dash/models/DashManifestModel":27,"../dash/parser/DashParser":28,"../dash/utils/TimelineConverter":43,"../dash/vo/UTCTiming":53,"./../core/Debug":13,"./../core/EventBus":14,"./../core/Version":16,"./../core/events/Events":18,"./ManifestLoader":55,"./MediaPlayerEvents":58,"./TextSourceBuffer":62,"./TextTracks":63,"./controllers/AbrController":66,"./controllers/BaseURLController":67,"./controllers/MediaController":72,"./controllers/MediaSourceController":73,"./controllers/PlaybackController":74,"./controllers/SourceBufferController":76,"./controllers/StreamController":77,"./controllers/TimeSyncController":79,"./models/ManifestModel":106,"./models/MediaPlayerModel":107,"./models/MetricsModel":108,"./models/URIQueryAndFragmentModel":109,"./models/VideoModel":110,"./rules/RulesController":136,"./rules/abr/ABRRulesCollection":139,"./utils/Capabilities":153,"./utils/ErrorHandler":157,"./utils/LiveEdgeFinder":160,"./utils/RequestModifier":162}],58:[function(require,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 _coreEventsEventsBase = require('../core/events/EventsBase'); var _coreEventsEventsBase2 = _interopRequireDefault(_coreEventsEventsBase); /** * @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 playback will not start yet * as the MPD's availabilityStartTime is in the future. * Check delay property in payload to determine time before playback will start. */ this.AST_IN_FUTURE = 'astInFuture'; /** * 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_LEVEL_STATE_CHANGED */ this.BUFFER_LEVEL_STATE_CHANGED = 'bufferStateChanged'; /** * Triggered when there is an error from the element or MSE source buffer. * @event MediaPlayerEvents#ERROR */ this.ERROR = 'error'; /** * Triggered when a fragment download has completed. * @event MediaPlayerEvents#FRAGMENT_LOADING_COMPLETED */ this.FRAGMENT_LOADING_COMPLETED = 'fragmentLoadingCompleted'; /** * Triggered when a fragment download has started. * @event MediaPlayerEvents#FRAGMENT_LOADING_STARTED */ this.FRAGMENT_LOADING_STARTED = 'fragmentLoadingStarted'; /** * Triggered when a fragment download is abandoned due to detection of slow download base on the ABR abandon rule.. * @event MediaPlayerEvents#FRAGMENT_LOADING_ABANDONED */ this.FRAGMENT_LOADING_ABANDONED = 'fragmentLoadingAbandoned'; /** * 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 = 'periodSwitchCompleted'; /** * Triggered when a new period starts. * @event MediaPlayerEvents#PERIOD_SWITCH_STARTED */ this.PERIOD_SWITCH_STARTED = 'periodSwitchStarted'; /** * Triggered when an ABR up /down switch is initialed; either by user in manual mode or auto mode via ABR rules. * @event MediaPlayerEvents#QUALITY_CHANGE_REQUESTED */ this.QUALITY_CHANGE_REQUESTED = 'qualityChangeRequested'; /** * Triggered when the new ABR quality is being rendered on-screen. * @event MediaPlayerEvents#QUALITY_CHANGE_RENDERED */ this.QUALITY_CHANGE_RENDERED = 'qualityChangeRendered'; /** * 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'; /** * Sent when playback is not allowed (for example if user gesture is needed). * @event MediaPlayerEvents#PLAYBACK_NOT_ALLOWED */ this.PLAYBACK_NOT_ALLOWED = 'playbackNotAllowed'; /** * 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'; } return MediaPlayerEvents; })(_coreEventsEventsBase2['default']); var mediaPlayerEvents = new MediaPlayerEvents(); exports['default'] = mediaPlayerEvents; module.exports = exports['default']; },{"../core/events/EventsBase":19}],59:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _MediaPlayer = require('./MediaPlayer'); var _MediaPlayer2 = _interopRequireDefault(_MediaPlayer); function MediaPlayerFactory() { /** * mime-type identifier for any source content to be accepted as a dash manifest by the create() method. * @type {string} */ var SUPPORTED_MIME_TYPE = 'application/dash+xml'; /** * A new MediaPlayer is instantiated for the supplied videoElement and optional source and context. If no context is provided, * a default DashContext is used. If no source is provided, the videoElement is interrogated to extract the first source whose * type is application/dash+xml. * The autoplay property of the videoElement is preserved. Any preload attribute is ignored. This method should be called after the page onLoad event is dispatched. * @param {HTMLMediaElement} video * @param {HTMLSourceElement} source * @param {Object} context * @returns {MediaPlayer|null} */ function create(video, source, context) { if (!video || video.nodeName !== 'VIDEO') return null; if (video._dashjs_player) return video._dashjs_player; var player; var videoID = video.id || video.name || 'video element'; source = source || [].slice.call(video.querySelectorAll('source')).filter(function (s) { return s.type == SUPPORTED_MIME_TYPE; })[0]; if (!source && video.src) { source = document.createElement('source'); source.src = video.src; } else if (!source && !video.src) { return null; } context = context || {}; player = (0, _MediaPlayer2['default'])(context).create(); player.initialize(video, source.src, video.autoplay); player.getDebug().log('Converted ' + videoID + ' to dash.js player and added content: ' + source.src); // Store a reference to the player on the video element so it can be gotten at for debugging and so we know its // already been setup. video._dashjs_player = player; return player; } /** * Searches the provided scope for all instances of the indicated selector. If no scope is provided, document is used. If no selector is * specified, [data-dashjs-player] is used. The declarative setup also looks for source elements with the type attribute set to 'application/dash+xml'. * It then looks for those video elements which have a source element defined with a type matching 'application/dash+xml'. * A new MediaPlayer is instantiated for each matching video element and the appropriate source is assigned. * The autoplay property of the video element is preserved. Any preload attribute is ignored. This method should be called after the page onLoad event is dispatched. * Returns an array holding all the MediaPlayer instances that were added by this method. * @param {string} selector - CSS selector * @param {Object} scope * @returns {Array} an array of MediaPlayer objects */ function createAll(selector, scope) { var aPlayers = []; selector = selector || '[data-dashjs-player]'; scope = scope || document; var videos = scope.querySelectorAll(selector); for (var i = 0; i < videos.length; i++) { var player = create(videos[i], null); aPlayers.push(player); } var sources = scope.querySelectorAll('source[type="' + SUPPORTED_MIME_TYPE + '"]'); for (var i = 0; i < sources.length; i++) { var video = findVideo(sources[i]); var player = create(video, null); aPlayers.push(player); } return aPlayers; } function findVideo(_x) { var _again = true; _function: while (_again) { var el = _x; _again = false; if (el.nodeName.toLowerCase() === 'video') { return el; } else { _x = el.parentNode; _again = true; continue _function; } } } return { create: create, createAll: createAll }; } var instance = MediaPlayerFactory(); var loadInterval = undefined; function loadHandler() { window.removeEventListener('load', loadHandler); instance.createAll(); } function loadIntervalHandler() { if (window.dashjs) { window.clearInterval(loadInterval); instance.createAll(); } } var avoidAutoCreate = typeof window !== 'undefined' && window && window.dashjs && window.dashjs.skipAutoCreate; if (!avoidAutoCreate && typeof window !== 'undefined' && window && window.addEventListener) { if (window.document.readyState === 'complete') { if (window.dashjs) { instance.createAll(); } else { // If loaded asynchronously, window.readyState may be 'complete' even if dashjs hasn't loaded yet loadInterval = window.setInterval(loadIntervalHandler, 500); } } else { window.addEventListener('load', loadHandler); } } exports['default'] = instance; module.exports = exports['default']; },{"./MediaPlayer":57}],60:[function(require,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 _utilsLiveEdgeFinder = require('./utils/LiveEdgeFinder'); var _utilsLiveEdgeFinder2 = _interopRequireDefault(_utilsLiveEdgeFinder); var _StreamProcessor = require('./StreamProcessor'); var _StreamProcessor2 = _interopRequireDefault(_StreamProcessor); var _controllersMediaController = require('./controllers/MediaController'); var _controllersMediaController2 = _interopRequireDefault(_controllersMediaController); var _controllersEventController = require('./controllers/EventController'); var _controllersEventController2 = _interopRequireDefault(_controllersEventController); var _controllersFragmentController = require('./controllers/FragmentController'); var _controllersFragmentController2 = _interopRequireDefault(_controllersFragmentController); var _controllersAbrController = require('./controllers/AbrController'); var _controllersAbrController2 = _interopRequireDefault(_controllersAbrController); var _modelsVideoModel = require('./models/VideoModel'); var _modelsVideoModel2 = _interopRequireDefault(_modelsVideoModel); var _modelsMetricsModel = require('./models/MetricsModel'); var _modelsMetricsModel2 = _interopRequireDefault(_modelsMetricsModel); var _controllersPlaybackController = require('./controllers/PlaybackController'); var _controllersPlaybackController2 = _interopRequireDefault(_controllersPlaybackController); var _dashDashHandler = require('../dash/DashHandler'); var _dashDashHandler2 = _interopRequireDefault(_dashDashHandler); var _dashSegmentBaseLoader = require('../dash/SegmentBaseLoader'); var _dashSegmentBaseLoader2 = _interopRequireDefault(_dashSegmentBaseLoader); var _dashWebmSegmentBaseLoader = require('../dash/WebmSegmentBaseLoader'); var _dashWebmSegmentBaseLoader2 = _interopRequireDefault(_dashWebmSegmentBaseLoader); var _dashDashMetrics = require('../dash/DashMetrics'); var _dashDashMetrics2 = _interopRequireDefault(_dashDashMetrics); var _coreEventBus = require('../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreDebug = require('../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _TextSourceBuffer = require('./TextSourceBuffer'); var _TextSourceBuffer2 = _interopRequireDefault(_TextSourceBuffer); function Stream(config) { var DATA_UPDATE_FAILED_ERROR_CODE = 1; var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['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, _utilsLiveEdgeFinder2['default'])(context).getInstance(); playbackController = (0, _controllersPlaybackController2['default'])(context).getInstance(); abrController = (0, _controllersAbrController2['default'])(context).getInstance(); mediaController = (0, _controllersMediaController2['default'])(context).getInstance(); fragmentController = (0, _controllersFragmentController2['default'])(context).create(); textSourceBuffer = (0, _TextSourceBuffer2['default'])(context).getInstance(); eventBus.on(_coreEventsEvents2['default'].BUFFERING_COMPLETED, onBufferingCompleted, instance); eventBus.on(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, instance); } function initialize(StreamInfo, ProtectionController) { streamInfo = StreamInfo; protectionController = ProtectionController; if (protectionController) { eventBus.on(_coreEventsEvents2['default'].KEY_ERROR, onProtectionError, instance); eventBus.on(_coreEventsEvents2['default'].SERVER_CERTIFICATE_UPDATED, onProtectionError, instance); eventBus.on(_coreEventsEvents2['default'].LICENSE_REQUEST_COMPLETE, onProtectionError, instance); eventBus.on(_coreEventsEvents2['default'].KEY_SYSTEM_SELECTED, onProtectionError, instance); eventBus.on(_coreEventsEvents2['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(_coreEventsEvents2['default'].CURRENT_TRACK_CHANGED, onCurrentTrackChanged, instance); initializeMedia(mediaSource); isStreamActivated = true; } //else { // TODO Check track change mode but why is this here. commented it out for now to check. // 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(_coreEventsEvents2['default'].CURRENT_TRACK_CHANGED, onCurrentTrackChanged, instance); } function reset() { if (playbackController) { playbackController.pause(); playbackController = null; } if (fragmentController) { fragmentController.reset(); fragmentController = null; } deactivate(); mediaController = null; abrController = null; manifestUpdater = null; manifestModel = null; adapter = null; capabilities = null; log = null; errHandler = null; isUpdating = false; initialized = false; updateError = {}; eventBus.off(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, instance); eventBus.off(_coreEventsEvents2['default'].BUFFERING_COMPLETED, onBufferingCompleted, instance); eventBus.off(_coreEventsEvents2['default'].KEY_ERROR, onProtectionError, instance); eventBus.off(_coreEventsEvents2['default'].SERVER_CERTIFICATE_UPDATED, onProtectionError, instance); eventBus.off(_coreEventsEvents2['default'].LICENSE_REQUEST_COMPLETE, onProtectionError, instance); eventBus.off(_coreEventsEvents2['default'].KEY_SYSTEM_SELECTED, onProtectionError, instance); eventBus.off(_coreEventsEvents2['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 {string} 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, _modelsVideoModel2['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 isWebM(mimeType) { var type = mimeType.split('/')[1]; return 'webm' === type.toLowerCase(); } function createIndexHandler(mediaInfo) { var segmentBaseLoader = isWebM(mediaInfo.mimeType) ? (0, _dashWebmSegmentBaseLoader2['default'])(context).getInstance() : (0, _dashSegmentBaseLoader2['default'])(context).getInstance(); segmentBaseLoader.setConfig({ baseURLController: baseURLController, metricsModel: (0, _modelsMetricsModel2['default'])(context).getInstance() }); segmentBaseLoader.initialize(); var handler = (0, _dashDashHandler2['default'])(context).create({ segmentBaseLoader: segmentBaseLoader, timelineConverter: timelineConverter, dashMetrics: (0, _dashDashMetrics2['default'])(context).getInstance(), metricsModel: (0, _modelsMetricsModel2['default'])(context).getInstance(), baseURLController: baseURLController }); return handler; } function createStreamProcessor(mediaInfo, manifest, mediaSource, optionalSettings) { var streamProcessor = (0, _StreamProcessor2['default'])(context).create({ indexHandler: createIndexHandler(mediaInfo), 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); streamProcessor.getIndexHandler().setCurrentTime(optionalSettings.currentTime); 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, _controllersEventController2['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; if (!isMediaInitialized) return; if (protectionController) { protectionController.initialize(manifestModel.getValue(), getMediaInfo('audio'), getMediaInfo('video')); } eventBus.trigger(_coreEventsEvents2['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(_coreEventsEvents2['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) { log('Manifest updated... updating data system wide.'); var manifest = manifestModel.getValue(); isStreamActivated = false; isUpdating = true; initialized = false; streamInfo = updatedStreamInfo; if (eventController) { var events = adapter.getEventsFor(manifest, streamInfo); eventController.addInlineEvents(events); } for (var i = 0, ln = streamProcessors.length; i < ln; i++) { var streamProcessor = streamProcessors[i]; var mediaInfo = adapter.getMediaInfoForType(manifest, streamInfo, streamProcessor.getType()); abrController.updateTopQualityIndex(mediaInfo); streamProcessor.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'] = _coreFactoryMaker2['default'].getClassFactory(Stream); module.exports = exports['default']; },{"../core/Debug":13,"../core/EventBus":14,"../core/FactoryMaker":15,"../core/events/Events":18,"../dash/DashHandler":21,"../dash/DashMetrics":22,"../dash/SegmentBaseLoader":23,"../dash/WebmSegmentBaseLoader":24,"./StreamProcessor":61,"./TextSourceBuffer":62,"./controllers/AbrController":66,"./controllers/EventController":70,"./controllers/FragmentController":71,"./controllers/MediaController":72,"./controllers/PlaybackController":74,"./models/MetricsModel":108,"./models/VideoModel":110,"./utils/LiveEdgeFinder":160}],61:[function(require,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 _controllersAbrController = require('./controllers/AbrController'); var _controllersAbrController2 = _interopRequireDefault(_controllersAbrController); var _controllersBufferController = require('./controllers/BufferController'); var _controllersBufferController2 = _interopRequireDefault(_controllersBufferController); var _controllersStreamController = require('./controllers/StreamController'); var _controllersStreamController2 = _interopRequireDefault(_controllersStreamController); var _controllersMediaController = require('./controllers/MediaController'); var _controllersMediaController2 = _interopRequireDefault(_controllersMediaController); var _controllersTextController = require('./controllers/TextController'); var _controllersTextController2 = _interopRequireDefault(_controllersTextController); var _controllersScheduleController = require('./controllers/ScheduleController'); var _controllersScheduleController2 = _interopRequireDefault(_controllersScheduleController); var _rulesRulesController = require('./rules/RulesController'); var _rulesRulesController2 = _interopRequireDefault(_rulesRulesController); var _modelsMediaPlayerModel = require('./models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _modelsMetricsModel = require('./models/MetricsModel'); var _modelsMetricsModel2 = _interopRequireDefault(_modelsMetricsModel); var _FragmentLoader = require('./FragmentLoader'); var _FragmentLoader2 = _interopRequireDefault(_FragmentLoader); var _utilsRequestModifier = require('./utils/RequestModifier'); var _utilsRequestModifier2 = _interopRequireDefault(_utilsRequestModifier); var _controllersSourceBufferController = require('./controllers/SourceBufferController'); var _controllersSourceBufferController2 = _interopRequireDefault(_controllersSourceBufferController); var _TextSourceBuffer = require('./TextSourceBuffer'); var _TextSourceBuffer2 = _interopRequireDefault(_TextSourceBuffer); var _dashModelsDashManifestModel = require('../dash/models/DashManifestModel'); var _dashModelsDashManifestModel2 = _interopRequireDefault(_dashModelsDashManifestModel); var _dashDashMetrics = require('../dash/DashMetrics'); var _dashDashMetrics2 = _interopRequireDefault(_dashDashMetrics); var _dashControllersRepresentationController = require('../dash/controllers/RepresentationController'); var _dashControllersRepresentationController2 = _interopRequireDefault(_dashControllersRepresentationController); var _utilsErrorHandler = require('./utils/ErrorHandler'); var _utilsErrorHandler2 = _interopRequireDefault(_utilsErrorHandler); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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, _controllersAbrController2['default'])(context).getInstance(); abrController.initialize(type, this); bufferController = createBufferControllerForType(Type); scheduleController = (0, _controllersScheduleController2['default'])(context).create({ metricsModel: (0, _modelsMetricsModel2['default'])(context).getInstance(), manifestModel: manifestModel, adapter: adapter, dashMetrics: (0, _dashDashMetrics2['default'])(context).getInstance(), dashManifestModel: (0, _dashModelsDashManifestModel2['default'])(context).getInstance(), timelineConverter: timelineConverter, rulesController: (0, _rulesRulesController2['default'])(context).getInstance(), mediaPlayerModel: (0, _modelsMediaPlayerModel2['default'])(context).getInstance() }); bufferController.initialize(type, mediaSource, this); scheduleController.initialize(type, this); fragmentLoader = (0, _FragmentLoader2['default'])(context).create({ metricsModel: (0, _modelsMetricsModel2['default'])(context).getInstance(), errHandler: (0, _utilsErrorHandler2['default'])(context).getInstance(), requestModifier: (0, _utilsRequestModifier2['default'])(context).getInstance() }); fragmentModel = scheduleController.getFragmentModel(); fragmentModel.setLoader(fragmentLoader); representationController = (0, _dashControllersRepresentationController2['default'])(context).create(); representationController.initialize(this); } function reset(errored) { 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 ? stream.getStreamInfo() : null; } 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, _controllersBufferController2['default'])(context).create({ metricsModel: (0, _modelsMetricsModel2['default'])(context).getInstance(), manifestModel: manifestModel, sourceBufferController: (0, _controllersSourceBufferController2['default'])(context).getInstance(), errHandler: (0, _utilsErrorHandler2['default'])(context).getInstance(), streamController: (0, _controllersStreamController2['default'])(context).getInstance(), mediaController: (0, _controllersMediaController2['default'])(context).getInstance(), adapter: adapter, textSourceBuffer: (0, _TextSourceBuffer2['default'])(context).getInstance() }); } else { controller = (0, _controllersTextController2['default'])(context).create({ errHandler: (0, _utilsErrorHandler2['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'] = _coreFactoryMaker2['default'].getClassFactory(StreamProcessor); module.exports = exports['default']; },{"../core/FactoryMaker":15,"../dash/DashMetrics":22,"../dash/controllers/RepresentationController":26,"../dash/models/DashManifestModel":27,"./FragmentLoader":54,"./TextSourceBuffer":62,"./controllers/AbrController":66,"./controllers/BufferController":69,"./controllers/MediaController":72,"./controllers/ScheduleController":75,"./controllers/SourceBufferController":76,"./controllers/StreamController":77,"./controllers/TextController":78,"./models/MediaPlayerModel":107,"./models/MetricsModel":108,"./rules/RulesController":136,"./utils/ErrorHandler":157,"./utils/RequestModifier":162}],62:[function(require,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 _voTextTrackInfo = require('./vo/TextTrackInfo'); var _voTextTrackInfo2 = _interopRequireDefault(_voTextTrackInfo); var _dashUtilsFragmentedTextBoxParser = require('../dash/utils/FragmentedTextBoxParser'); var _dashUtilsFragmentedTextBoxParser2 = _interopRequireDefault(_dashUtilsFragmentedTextBoxParser); var _utilsBoxParser = require('./utils/BoxParser'); var _utilsBoxParser2 = _interopRequireDefault(_utilsBoxParser); var _utilsCustomTimeRanges = require('./utils/CustomTimeRanges'); var _utilsCustomTimeRanges2 = _interopRequireDefault(_utilsCustomTimeRanges); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _modelsVideoModel = require('./models/VideoModel'); var _modelsVideoModel2 = _interopRequireDefault(_modelsVideoModel); var _TextTracks = require('./TextTracks'); var _TextTracks2 = _interopRequireDefault(_TextTracks); var _codemIsoboxer = require('codem-isoboxer'); var _codemIsoboxer2 = _interopRequireDefault(_codemIsoboxer); var _externalsCea608Parser = require('../../externals/cea608-parser'); var _externalsCea608Parser2 = _interopRequireDefault(_externalsCea608Parser); function TextSourceBuffer() { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var embeddedInitialized = false; var captionId = 0; var instance = undefined, boxParser = undefined, errHandler = 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, _utilsBoxParser2['default'])(context).getInstance(); fragmentedTextBoxParser = (0, _dashUtilsFragmentedTextBoxParser2['default'])(context).getInstance(); fragmentedTextBoxParser.setConfig({ boxParser: boxParser }); if (isFragmented) { fragmentModel = streamProcessor.getFragmentModel(); this.buffered = (0, _utilsCustomTimeRanges2['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, _modelsVideoModel2['default'])(context).getInstance(); textTracks = (0, _TextTracks2['default'])(context).getInstance(); textTracks.setConfig({ videoModel: videoModel }); textTracks.initialize(); boxParser = (0, _utilsBoxParser2['default'])(context).getInstance(); fragmentedTextBoxParser = (0, _dashUtilsFragmentedTextBoxParser2['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, j, k, 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 _voTextTrackInfo2['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.subSizes[0]); ccContent = _codemIsoboxer2['default'].Utils.dataViewToString(dataView, 'utf-8'); var images = []; var subOffset = _sample.offset + _sample.subSizes[0]; for (j = 1; j < _sample.subSizes.length; j++) { var inData = new Uint8Array(bytes, subOffset, _sample.subSizes[j]); var raw = String.fromCharCode.apply(null, inData); images.push(raw); subOffset += _sample.subSizes[j]; } try { result = parser.parse(ccContent, sampleStart / timescale, (sampleStart + _sample.duration) / timescale, images); 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 (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 (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 _externalsCea608Parser2['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. * @param {ArrayBuffer} data * @returns {Object|null} 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]; if (sample.sample_duration === undefined) { sample.sample_duration = tfhd.default_sample_duration; } if (sample.sample_size === undefined) { sample.sample_size = tfhd.default_sample_size; } if (sample.sample_composition_time_offset === undefined) { sample.sample_composition_time_offset = 0; } var sampleTime = baseSampleTime + accDuration + sample.sample_composition_time_offset; var cea608Ranges = _externalsCea608Parser2['default'].findCea608Nalus(raw, startPos, sample.sample_size); for (var j = 0; j < cea608Ranges.length; j++) { var ccData = _externalsCea608Parser2['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_cea608_' + captionId++; var finalDiv = document.createElement('div'); finalDiv.id = 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, 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.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 && mediaInfo.isEmbedded) { 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 if (embeddedTracks.length === 0) { 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; } function getCurrentTrackIdx() { return textTracks.getCurrentTrackIdx(); } instance = { initialize: initialize, append: append, abort: abort, getCurrentTrackIdx: getCurrentTrackIdx, getAllTracksAreDisabled: getAllTracksAreDisabled, setTextTrack: setTextTrack, setConfig: setConfig, addEmbeddedTrack: addEmbeddedTrack, resetEmbedded: resetEmbedded }; return instance; } TextSourceBuffer.__dashjs_factory_name = 'TextSourceBuffer'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(TextSourceBuffer); module.exports = exports['default']; },{"../../externals/cea608-parser":9,"../core/Debug":13,"../core/FactoryMaker":15,"../dash/utils/FragmentedTextBoxParser":38,"./TextTracks":63,"./models/VideoModel":110,"./utils/BoxParser":152,"./utils/CustomTimeRanges":154,"./vo/TextTrackInfo":177,"codem-isoboxer":7}],63:[function(require,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 _coreEventBus = require('../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); function TextTracks() { var context = this.context; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var log = (0, _coreDebug2['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(_coreEventsEvents2['default'].TEXT_TRACK_ADDED); } setCurrentTrackIdx.call(this, defaultIndex); if (defaultIndex >= 0) { for (var idx = 0; idx < video.textTracks.length; idx++) { video.textTracks[idx].mode = idx === defaultIndex ? 'showing' : 'hidden'; } this.addCaptions(defaultIndex, 0, null); } eventBus.trigger(_coreEventsEvents2['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 videoPictureWidth = 0; var videoPictureHeight = 0; if (viewAspectRatio > videoAspectRatio) { videoPictureHeight = viewHeight; videoPictureWidth = videoPictureHeight / videoHeight * videoWidth; } else { videoPictureWidth = viewWidth; videoPictureHeight = videoPictureWidth / videoWidth * videoHeight; } 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') { var aspectRatio = video.clientWidth / video.clientHeight; 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 convertToPixels(percentage, pixelMeasure) { var percentString = Math.round(0.01 * percentage * pixelMeasure).toString() + 'px'; return percentString; } function scaleImageCue(activeCue) { var videoWidth = actualVideoWidth; var videoHeight = actualVideoHeight; if (videoWidth * videoHeight === 0) { return; //At least one of the measures is still zero } if (activeCue.layout) { var layout = activeCue.layout; var left = convertToPixels(layout.left, videoWidth); var _top = convertToPixels(layout.top, videoHeight); var width = convertToPixels(layout.width, videoWidth); var height = convertToPixels(layout.height, videoHeight); captionContainer.style.left = left; captionContainer.style.top = _top; captionContainer.style.width = width; captionContainer.style.height = height; var image = captionContainer.firstChild; if (image && image.style) { image.style.left = '0px'; image.style.top = '0px'; image.style.width = width; image.style.height = height; } } } function scaleCue(activeCue) { var videoWidth = actualVideoWidth; var videoHeight = actualVideoHeight; var key, replaceValue, valueFontSize, valueLineHeight, 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)) { if (activeCue.fontSize[key][0] === '%') { valueFontSize = activeCue.fontSize[key][1] / 100; } else if (activeCue.fontSize[key][0] === 'c') { valueFontSize = activeCue.fontSize[key][1]; } 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)) { if (activeCue.lineHeight[key][0] === '%') { valueLineHeight = activeCue.lineHeight[key][1] / 100; } else if (activeCue.fontSize[key][0] === 'c') { valueLineHeight = activeCue.lineHeight[key][1]; } 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' || currentItem.type === 'image')) { 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.layout = currentItem.layout; cue.scaleCue = scaleImageCue.bind(self); cue.onenter = function () { if (!captionContainer) { // Does not support image captions without a container return; } if (track.mode === 'showing') { var img = new Image(); img.id = 'ttmlImage_' + this.id; img.src = this.image; //img.className = 'cue-image'; img.style.cssText = 'z-index: 2147483648; pointer-events: none; display: block; visibility: visible !important; position: relative !important;'; captionContainer.appendChild(img); scaleImageCue.call(self, this); } }; cue.onexit = function () { if (!captionContainer) { return; } var imgs = captionContainer.childNodes; for (var i = 0; i < imgs.length; i++) { if (imgs[i].id === 'ttmlImage_' + this.id) { captionContainer.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') { log('Cue ' + this.startTime + '-' + this.endTime + ' : ' + this.cueHTMLElement.id + ' : ' + this.cueHTMLElement.innerText); 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 === 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; clearCaptionContainer.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; } clearCaptionContainer.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 clearCaptionContainer() { 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'] = _coreFactoryMaker2['default'].getSingletonFactory(TextTracks); module.exports = exports['default']; },{"../core/Debug":13,"../core/EventBus":14,"../core/FactoryMaker":15,"../core/events/Events":18}],64:[function(require,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 _voMetricsHTTPRequest = require('./vo/metrics/HTTPRequest'); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _modelsMediaPlayerModel = require('./models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _utilsErrorHandlerJs = require('./utils/ErrorHandler.js'); var _utilsErrorHandlerJs2 = _interopRequireDefault(_utilsErrorHandlerJs); /** * @module XHRLoader * @description Manages download of resources via HTTP. * @param {Object} cfg - dependancies from parent */ function XHRLoader(cfg) { var context = this.context; //const log = Debug(context).getInstance().log; var mediaPlayerModel = (0, _modelsMediaPlayerModel2['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, _voMetricsHTTPRequest.HTTPRequest.MPD_TYPE, _utilsErrorHandlerJs2['default'].DOWNLOAD_ERROR_ID_MANIFEST), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequest.HTTPRequest.XLINK_EXPANSION_TYPE, _utilsErrorHandlerJs2['default'].DOWNLOAD_ERROR_ID_XLINK), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequest.HTTPRequest.INIT_SEGMENT_TYPE, _utilsErrorHandlerJs2['default'].DOWNLOAD_ERROR_ID_INITIALIZATION), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE, _utilsErrorHandlerJs2['default'].DOWNLOAD_ERROR_ID_CONTENT), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequest.HTTPRequest.INDEX_SEGMENT_TYPE, _utilsErrorHandlerJs2['default'].DOWNLOAD_ERROR_ID_CONTENT), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequest.HTTPRequest.BITSTREAM_SWITCHING_SEGMENT_TYPE, _utilsErrorHandlerJs2['default'].DOWNLOAD_ERROR_ID_CONTENT), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequest.HTTPRequest.OTHER_TYPE, _utilsErrorHandlerJs2['default'].DOWNLOAD_ERROR_ID_CONTENT), _downloadErrorToRequestTypeMap); } function internalLoad(config, remainingAttempts) { var request = config.request; var xhr = 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, xhr); } 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.withCredentials = mediaPlayerModel.getXHRWithCredentialsForType(request.type); 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 = x.onprogress = undefined; x.abort(); }); xhrs = []; } instance = { load: load, abort: abort }; setup(); return instance; } XHRLoader.__dashjs_factory_name = 'XHRLoader'; var factory = _coreFactoryMaker2['default'].getClassFactory(XHRLoader); exports['default'] = factory; module.exports = exports['default']; },{"../core/FactoryMaker":15,"./models/MediaPlayerModel":107,"./utils/ErrorHandler.js":157,"./vo/metrics/HTTPRequest":185}],65:[function(require,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 _voError = require('./vo/Error'); var _voError2 = _interopRequireDefault(_voError); var _XHRLoader = require('./XHRLoader'); var _XHRLoader2 = _interopRequireDefault(_XHRLoader); var _voMetricsHTTPRequest = require('./vo/metrics/HTTPRequest'); var _voTextRequest = require('./vo/TextRequest'); var _voTextRequest2 = _interopRequireDefault(_voTextRequest); var _coreEventBus = require('../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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, _coreEventBus2['default'])(context).getInstance(); var xhrLoader = (0, _XHRLoader2['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(_coreEventsEvents2['default'].XLINK_ELEMENT_LOADED, { element: element, resolveObject: resolveObject, error: content || resolveToZero ? null : new _voError2['default'](XLINK_LOADER_ERROR_LOADING_FAILURE, 'Failed loading Xlink element: ' + url) }); }; if (url === RESOLVE_TO_ZERO) { report(null, true); } else { var request = new _voTextRequest2['default'](url, _voMetricsHTTPRequest.HTTPRequest.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 = _coreFactoryMaker2['default'].getClassFactory(XlinkLoader); factory.XLINK_LOADER_ERROR_LOADING_FAILURE = XLINK_LOADER_ERROR_LOADING_FAILURE; exports['default'] = factory; module.exports = exports['default']; },{"../core/EventBus":14,"../core/FactoryMaker":15,"../core/events/Events":18,"./XHRLoader":64,"./vo/Error":168,"./vo/TextRequest":176,"./vo/metrics/HTTPRequest":185}],66:[function(require,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 _voBitrateInfo = require('../vo/BitrateInfo'); var _voBitrateInfo2 = _interopRequireDefault(_voBitrateInfo); var _utilsDOMStorage = require('../utils/DOMStorage'); var _utilsDOMStorage2 = _interopRequireDefault(_utilsDOMStorage); var _modelsMediaPlayerModel = require('../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _modelsFragmentModel = require('../models/FragmentModel'); var _modelsFragmentModel2 = _interopRequireDefault(_modelsFragmentModel); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _MediaPlayerEventsJs = require('../MediaPlayerEvents.js'); var _MediaPlayerEventsJs2 = _interopRequireDefault(_MediaPlayerEventsJs); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _modelsManifestModel = require('../models/ManifestModel'); var _modelsManifestModel2 = _interopRequireDefault(_modelsManifestModel); var _dashModelsDashManifestModel = require('../../dash/models/DashManifestModel'); var _dashModelsDashManifestModel2 = _interopRequireDefault(_dashModelsDashManifestModel); var _modelsVideoModel = require('../models/VideoModel'); var _modelsVideoModel2 = _interopRequireDefault(_modelsVideoModel); var _rulesRulesContextJs = require('../rules/RulesContext.js'); var _rulesRulesContextJs2 = _interopRequireDefault(_rulesRulesContextJs); var _rulesSwitchRequestJs = require('../rules/SwitchRequest.js'); var _rulesSwitchRequestJs2 = _interopRequireDefault(_rulesSwitchRequestJs); var _rulesSwitchRequestHistoryJs = require('../rules/SwitchRequestHistory.js'); var _rulesSwitchRequestHistoryJs2 = _interopRequireDefault(_rulesSwitchRequestHistoryJs); var _rulesDroppedFramesHistoryJs = require('../rules/DroppedFramesHistory.js'); var _rulesDroppedFramesHistoryJs2 = _interopRequireDefault(_rulesDroppedFramesHistoryJs); var _modelsMetricsModelJs = require('../models/MetricsModel.js'); var _modelsMetricsModelJs2 = _interopRequireDefault(_modelsMetricsModelJs); var _dashDashMetricsJs = require('../../dash/DashMetrics.js'); var _dashDashMetricsJs2 = _interopRequireDefault(_dashDashMetricsJs); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var ABANDON_LOAD = 'abandonload'; var ALLOW_LOAD = 'allowload'; var DEFAULT_VIDEO_BITRATE = 1000; var DEFAULT_AUDIO_BITRATE = 100; var QUALITY_DEFAULT = 0; function AbrController() { var context = this.context; var debug = (0, _coreDebug2['default'])(context).getInstance(); var log = debug.log; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, abrRulesCollection = undefined, streamController = undefined, autoSwitchBitrate = undefined, topQualities = undefined, qualityDict = undefined, bitrateDict = undefined, ratioDict = undefined, averageThroughputDict = undefined, streamProcessorDict = undefined, abandonmentStateDict = undefined, abandonmentTimeout = undefined, limitBitrateByPortal = undefined, usePixelRatioInLimitBitrateByPortal = undefined, windowResizeEventCalled = undefined, elementWidth = undefined, elementHeight = undefined, manifestModel = undefined, dashManifestModel = undefined, videoModel = undefined, mediaPlayerModel = undefined, domStorage = undefined, playbackIndex = undefined, switchHistoryDict = undefined, droppedFramesHistory = undefined, metricsModel = undefined, dashMetrics = undefined, lastSwitchTime = undefined; function setup() { autoSwitchBitrate = { video: true, audio: true }; topQualities = {}; qualityDict = {}; bitrateDict = {}; ratioDict = {}; averageThroughputDict = {}; abandonmentStateDict = {}; streamProcessorDict = {}; switchHistoryDict = {}; limitBitrateByPortal = false; usePixelRatioInLimitBitrateByPortal = false; if (windowResizeEventCalled === undefined) { windowResizeEventCalled = false; } domStorage = (0, _utilsDOMStorage2['default'])(context).getInstance(); mediaPlayerModel = (0, _modelsMediaPlayerModel2['default'])(context).getInstance(); manifestModel = (0, _modelsManifestModel2['default'])(context).getInstance(); dashManifestModel = (0, _dashModelsDashManifestModel2['default'])(context).getInstance(); videoModel = (0, _modelsVideoModel2['default'])(context).getInstance(); metricsModel = (0, _modelsMetricsModelJs2['default'])(context).getInstance(); dashMetrics = (0, _dashDashMetricsJs2['default'])(context).getInstance(); lastSwitchTime = new Date().getTime() / 1000; } function initialize(type, streamProcessor) { switchHistoryDict[type] = (0, _rulesSwitchRequestHistoryJs2['default'])(context).create(); streamProcessorDict[type] = streamProcessor; abandonmentStateDict[type] = abandonmentStateDict[type] || {}; abandonmentStateDict[type].state = ALLOW_LOAD; eventBus.on(_coreEventsEvents2['default'].LOADING_PROGRESS, onFragmentLoadProgress, this); if (type == 'video') { eventBus.on(_MediaPlayerEventsJs2['default'].QUALITY_CHANGE_RENDERED, onQualityChangeRendered, this); droppedFramesHistory = (0, _rulesDroppedFramesHistoryJs2['default'])(context).create(); setElementSize(); } } function reset() { eventBus.off(_coreEventsEvents2['default'].LOADING_PROGRESS, onFragmentLoadProgress, this); eventBus.off(_MediaPlayerEventsJs2['default'].QUALITY_CHANGE_RENDERED, onQualityChangeRendered, this); playbackIndex = undefined; droppedFramesHistory = undefined; clearTimeout(abandonmentTimeout); abandonmentTimeout = null; setup(); } function setConfig(config) { if (!config) return; if (config.abrRulesCollection) { abrRulesCollection = config.abrRulesCollection; } if (config.streamController) { streamController = config.streamController; } } function onQualityChangeRendered(e) { if (e.mediaType === 'video') { playbackIndex = e.newQuality; droppedFramesHistory.push(playbackIndex, videoModel.getPlaybackQuality()); } } 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 {string} 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)) { var repIdx = Math.max(Math.round(representation.length * ratioDict[type]) - 1, 0); bitrateDict[type] = representation[repIdx].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 {string} 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 getUsePixelRatioInLimitBitrateByPortal() { return usePixelRatioInLimitBitrateByPortal; } function setUsePixelRatioInLimitBitrateByPortal(value) { usePixelRatioInLimitBitrateByPortal = value; } function getPlaybackQuality(streamProcessor) { var type = streamProcessor.getType(); var streamInfo = streamProcessor.getStreamInfo(); var streamId = streamInfo.id; var oldQuality = getQualityFor(type, streamInfo); var rulesContext = (0, _rulesRulesContextJs2['default'])(context).create({ streamProcessor: streamProcessor, currentValue: oldQuality, playbackIndex: playbackIndex, switchHistory: switchHistoryDict[type], droppedFramesHistory: droppedFramesHistory, hasRichBuffer: hasRichBuffer(type) }); if (droppedFramesHistory) { droppedFramesHistory.push(playbackIndex, videoModel.getPlaybackQuality()); } //log("ABR enabled? (" + autoSwitchBitrate + ")"); if (getAutoSwitchBitrateFor(type)) { var topQualityIdx = getTopQualityIndexFor(type, streamId); var switchRequest = abrRulesCollection.getMaxQuality(rulesContext); var newQuality = switchRequest.value; if (newQuality > topQualityIdx) { newQuality = topQualityIdx; } switchHistoryDict[type].push({ oldValue: oldQuality, newValue: newQuality }); if (newQuality > _rulesSwitchRequestJs2['default'].NO_CHANGE && newQuality != oldQuality) { if (abandonmentStateDict[type].state === ALLOW_LOAD || newQuality > oldQuality) { changeQuality(type, streamInfo, oldQuality, newQuality, topQualityIdx, switchRequest.reason); } } else if (debug.getLogToBrowserConsole()) { var bufferLevel = dashMetrics.getCurrentBufferLevel(metricsModel.getReadOnlyMetricsFor(type)); log('AbrController (' + type + ') stay on ' + oldQuality + '/' + topQualityIdx + ' (buffer: ' + bufferLevel + ')'); } } } function setPlaybackQuality(type, streamInfo, newQuality, reason) { var id = streamInfo.id; var oldQuality = getQualityFor(type, streamInfo); var isInt = newQuality !== null && !isNaN(newQuality) && newQuality % 1 === 0; if (!isInt) throw new Error('argument is not an integer'); var topQualityIdx = getTopQualityIndexFor(type, id); if (newQuality !== oldQuality && newQuality >= 0 && newQuality <= topQualityIdx) { changeQuality(type, streamInfo, oldQuality, newQuality, topQualityIdx, reason); } } function changeQuality(type, streamInfo, oldQuality, newQuality, topQualityIdx, reason) { if (debug.getLogToBrowserConsole()) { var bufferLevel = dashMetrics.getCurrentBufferLevel(metricsModel.getReadOnlyMetricsFor(type)); log('AbrController (' + type + ') switch from ' + oldQuality + ' to ' + newQuality + '/' + topQualityIdx + ' (buffer: ' + bufferLevel + ')\n' + JSON.stringify(reason)); } setQualityFor(type, streamInfo.id, newQuality); eventBus.trigger(_coreEventsEvents2['default'].QUALITY_CHANGE_REQUESTED, { mediaType: type, streamInfo: streamInfo, oldQuality: oldQuality, newQuality: newQuality, reason: reason }); } function setAbandonmentStateFor(type, state) { abandonmentStateDict[type].state = state; } function getAbandonmentStateFor(type) { return abandonmentStateDict[type].state; } /** * @param {MediaInfo} mediaInfo * @param {number} bitrate A bitrate value, kbps * @param {number} latency Expected latency of connection, ms * @returns {number} A quality index <= for the given bitrate * @memberof AbrController# */ function getQualityForBitrate(mediaInfo, bitrate, latency) { if (latency && streamProcessorDict[mediaInfo.type].getCurrentRepresentationInfo() && streamProcessorDict[mediaInfo.type].getCurrentRepresentationInfo().fragmentDuration) { latency = latency / 1000; var fragmentDuration = streamProcessorDict[mediaInfo.type].getCurrentRepresentationInfo().fragmentDuration; if (latency > fragmentDuration) { return 0; } else { var deadTimeRatio = latency / fragmentDuration; bitrate = bitrate * (1 - deadTimeRatio); } } var bitrateList = getBitrateList(mediaInfo); if (!bitrateList || bitrateList.length === 0) { return QUALITY_DEFAULT; } for (var i = bitrateList.length - 1; i >= 0; i--) { var bitrateInfo = bitrateList[i]; if (bitrate * 1000 >= bitrateInfo.bitrate) { return i; } } return 0; } /** * @param {MediaInfo} mediaInfo * @returns {Array|null} 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 _voBitrateInfo2['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 hasRichBuffer(type) { var metrics = metricsModel.getReadOnlyMetricsFor(type); var bufferLevel = dashMetrics.getCurrentBufferLevel(metrics); var bufferState = metrics.BufferState.length > 0 ? metrics.BufferState[metrics.BufferState.length - 1] : null; var isBufferRich = false; // This will happen when another rule tries to switch down from highest quality index // If there is enough buffer why not try to stay at high level if (bufferState && bufferLevel > bufferState.target) { // Are we currently over the buffer target by at least RICH_BUFFER_THRESHOLD? isBufferRich = bufferLevel > bufferState.target + mediaPlayerModel.getRichBufferThreshold(); } return isBufferRich; } 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 getQualityFor(type, streamInfo) { var id = streamInfo.id; var quality; qualityDict[id] = qualityDict[id] || {}; if (!qualityDict[id].hasOwnProperty(type)) { qualityDict[id][type] = QUALITY_DEFAULT; } quality = qualityDict[id][type]; return quality; } function setQualityFor(type, id, value) { qualityDict[id] = qualityDict[id] || {}; qualityDict[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 setWindowResizeEventCalled(value) { windowResizeEventCalled = value; } function setElementSize() { var element = videoModel.getElement(); if (element !== undefined) { var hasPixelRatio = usePixelRatioInLimitBitrateByPortal && window.hasOwnProperty('devicePixelRatio'); var pixelRatio = hasPixelRatio ? window.devicePixelRatio : 1; elementWidth = element.clientWidth * pixelRatio; elementHeight = element.clientHeight * pixelRatio; } } function checkPortalSize(idx, type) { if (type !== 'video' || !limitBitrateByPortal || !streamProcessorDict[type]) { return idx; } if (!windowResizeEventCalled) { setElementSize(); } 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)) { var scheduleController = streamProcessorDict[type].getScheduleController(); if (!scheduleController) return; // There may be a fragment load in progress when we switch periods and recreated some controllers. var rulesContext = (0, _rulesRulesContextJs2['default'])(context).create({ streamProcessor: streamProcessorDict[type], currentRequest: e.request, currentValue: getQualityFor(type, streamController.getActiveStreamInfo()), hasRichBuffer: hasRichBuffer(type) }); var switchRequest = abrRulesCollection.shouldAbandonFragment(rulesContext); //Removed overrideFunc // function (currentValue, newValue) { // return newValue; // }); if (switchRequest.value > _rulesSwitchRequestJs2['default'].NO_CHANGE) { var fragmentModel = scheduleController.getFragmentModel(); var request = fragmentModel.getRequests({ state: _modelsFragmentModel2['default'].FRAGMENT_MODEL_LOADING, index: e.request.index })[0]; if (request) { //TODO Check if we should abort or if better to finish download. check bytesLoaded/Total fragmentModel.abortRequests(); setAbandonmentStateFor(type, ABANDON_LOAD); switchHistoryDict[type].reset(); switchHistoryDict[type].push({ oldValue: getQualityFor(type, streamController.getActiveStreamInfo()), newValue: switchRequest.value, confidence: 1, reason: switchRequest.reason }); setPlaybackQuality(type, streamController.getActiveStreamInfo(), switchRequest.value, switchRequest.reason); eventBus.trigger(_coreEventsEvents2['default'].FRAGMENT_LOADING_ABANDONED, { streamProcessor: streamProcessorDict[type], request: request, mediaType: type }); clearTimeout(abandonmentTimeout); abandonmentTimeout = setTimeout(function () { setAbandonmentStateFor(type, ALLOW_LOAD);abandonmentTimeout = null; }, mediaPlayerModel.getAbandonLoadTimeout()); } } } } 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, getUsePixelRatioInLimitBitrateByPortal: getUsePixelRatioInLimitBitrateByPortal, setUsePixelRatioInLimitBitrateByPortal: setUsePixelRatioInLimitBitrateByPortal, getQualityFor: getQualityFor, getAbandonmentStateFor: getAbandonmentStateFor, setAbandonmentStateFor: setAbandonmentStateFor, setPlaybackQuality: setPlaybackQuality, getPlaybackQuality: getPlaybackQuality, setAverageThroughput: setAverageThroughput, getTopQualityIndexFor: getTopQualityIndexFor, setElementSize: setElementSize, setWindowResizeEventCalled: setWindowResizeEventCalled, initialize: initialize, setConfig: setConfig, reset: reset }; setup(); return instance; } AbrController.__dashjs_factory_name = 'AbrController'; var factory = _coreFactoryMaker2['default'].getSingletonFactory(AbrController); factory.ABANDON_LOAD = ABANDON_LOAD; factory.QUALITY_DEFAULT = QUALITY_DEFAULT; exports['default'] = factory; module.exports = exports['default']; },{"../../core/Debug":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../../dash/DashMetrics.js":22,"../../dash/models/DashManifestModel":27,"../MediaPlayerEvents.js":58,"../models/FragmentModel":105,"../models/ManifestModel":106,"../models/MediaPlayerModel":107,"../models/MetricsModel.js":108,"../models/VideoModel":110,"../rules/DroppedFramesHistory.js":134,"../rules/RulesContext.js":135,"../rules/SwitchRequest.js":137,"../rules/SwitchRequestHistory.js":138,"../utils/DOMStorage":155,"../vo/BitrateInfo":166}],67:[function(require,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 _modelsBaseURLTreeModel = require('../models/BaseURLTreeModel'); var _modelsBaseURLTreeModel2 = _interopRequireDefault(_modelsBaseURLTreeModel); var _utilsBaseURLSelector = require('../utils/BaseURLSelector'); var _utilsBaseURLSelector2 = _interopRequireDefault(_utilsBaseURLSelector); var _utilsURLUtils = require('../utils/URLUtils'); var _utilsURLUtils2 = _interopRequireDefault(_utilsURLUtils); var _dashVoBaseURL = require('../../dash/vo/BaseURL'); var _dashVoBaseURL2 = _interopRequireDefault(_dashVoBaseURL); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); function BaseURLController() { var instance = undefined; var context = this.context; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var urlUtils = (0, _utilsURLUtils2['default'])(context).getInstance(); var baseURLTreeModel = undefined, baseURLSelector = undefined; function onBlackListChanged(e) { baseURLTreeModel.invalidateSelectedIndexes(e.entry); } function setup() { baseURLTreeModel = (0, _modelsBaseURLTreeModel2['default'])(context).create(); baseURLSelector = (0, _utilsBaseURLSelector2['default'])(context).create(); eventBus.on(_coreEventsEvents2['default'].SERVICE_LOCATION_BLACKLIST_CHANGED, onBlackListChanged, instance); } function setConfig(config) { if (config.baseURLTreeModel) { baseURLTreeModel = config.baseURLTreeModel; } if (config.baseURLSelector) { baseURLSelector = config.baseURLSelector; } } 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 = urlUtils.resolve(b.url, p.url); } } else { return new _dashVoBaseURL2['default'](); } return p; }, new _dashVoBaseURL2['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, setConfig: setConfig }; setup(); return instance; } BaseURLController.__dashjs_factory_name = 'BaseURLController'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(BaseURLController); module.exports = exports['default']; },{"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../../dash/vo/BaseURL":46,"../models/BaseURLTreeModel":104,"../utils/BaseURLSelector":151,"../utils/URLUtils":164}],68:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); function BlackListController(config) { var blacklist = []; var eventBus = (0, _coreEventBus2['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'] = _coreFactoryMaker2['default'].getClassFactory(BlackListController); module.exports = exports['default']; },{"../../core/EventBus":14,"../../core/FactoryMaker":15}],69:[function(require,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 _modelsFragmentModel = require('../models/FragmentModel'); var _modelsFragmentModel2 = _interopRequireDefault(_modelsFragmentModel); var _modelsMediaPlayerModel = require('../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _SourceBufferController = require('./SourceBufferController'); var _SourceBufferController2 = _interopRequireDefault(_SourceBufferController); var _AbrController = require('./AbrController'); var _AbrController2 = _interopRequireDefault(_AbrController); var _PlaybackController = require('./PlaybackController'); var _PlaybackController2 = _interopRequireDefault(_PlaybackController); var _MediaController = require('./MediaController'); var _MediaController2 = _interopRequireDefault(_MediaController); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _utilsBoxParser = require('../utils/BoxParser'); var _utilsBoxParser2 = _interopRequireDefault(_utilsBoxParser); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _utilsInitCache = require('../utils/InitCache'); var _utilsInitCache2 = _interopRequireDefault(_utilsInitCache); var BUFFER_LOADED = 'bufferLoaded'; var BUFFER_EMPTY = 'bufferStalled'; var STALL_THRESHOLD = 0.5; function BufferController(config) { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var metricsModel = config.metricsModel; var manifestModel = config.manifestModel; var sourceBufferController = config.sourceBufferController; var errHandler = config.errHandler; var streamController = config.streamController; var mediaController = config.mediaController; var adapter = config.adapter; var textSourceBuffer = config.textSourceBuffer; var instance = undefined, requiredQuality = undefined, isBufferingCompleted = undefined, bufferLevel = 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, scheduleController = undefined, mediaPlayerModel = undefined, initCache = undefined; function setup() { requiredQuality = _AbrController2['default'].QUALITY_DEFAULT; isBufferingCompleted = false; bufferLevel = 0; criticalBufferLevel = Number.POSITIVE_INFINITY; maxAppendedIndex = 0; lastIndex = Number.POSITIVE_INFINITY; buffer = null; bufferState = BUFFER_EMPTY; wallclockTicked = 0; appendingMediaChunk = false; isAppendingInProgress = false; isPruningInProgress = false; inbandEventFound = false; } function initialize(Type, Source, StreamProcessor) { type = Type; setMediaSource(Source); streamProcessor = StreamProcessor; mediaPlayerModel = (0, _modelsMediaPlayerModel2['default'])(context).getInstance(); playbackController = (0, _PlaybackController2['default'])(context).getInstance(); abrController = (0, _AbrController2['default'])(context).getInstance(); initCache = (0, _utilsInitCache2['default'])(context).getInstance(); scheduleController = streamProcessor.getScheduleController(); requiredQuality = abrController.getQualityFor(type, streamProcessor.getStreamInfo()); eventBus.on(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this); eventBus.on(_coreEventsEvents2['default'].INIT_FRAGMENT_LOADED, onInitFragmentLoaded, this); eventBus.on(_coreEventsEvents2['default'].MEDIA_FRAGMENT_LOADED, onMediaFragmentLoaded, this); eventBus.on(_coreEventsEvents2['default'].QUALITY_CHANGE_REQUESTED, onQualityChanged, this); eventBus.on(_coreEventsEvents2['default'].STREAM_COMPLETED, onStreamCompleted, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_PROGRESS, onPlaybackProgression, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED, onPlaybackProgression, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_RATE_CHANGED, onPlaybackRateChanged, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this); eventBus.on(_coreEventsEvents2['default'].WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, this); eventBus.on(_coreEventsEvents2['default'].CURRENT_TRACK_CHANGED, onCurrentTrackChanged, this, _coreEventBus2['default'].EVENT_PRIORITY_HIGH); eventBus.on(_coreEventsEvents2['default'].SOURCEBUFFER_APPEND_COMPLETED, onAppended, this); eventBus.on(_coreEventsEvents2['default'].SOURCEBUFFER_REMOVE_COMPLETED, onRemoved, 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); return sourceBuffer; } function isActive() { return streamProcessor.getStreamInfo().id === streamController.getActiveStreamInfo().id; } function onInitFragmentLoaded(e) { if (e.fragmentModel !== streamProcessor.getFragmentModel()) return; log('Init fragment finished loading saving to', type + '\'s init cache'); initCache.save(e.chunk); appendToBuffer(e.chunk); } function switchInitData(streamId, quality) { var chunk = initCache.extract(streamId, type, quality); if (chunk) { appendToBuffer(chunk); } else { eventBus.trigger(_coreEventsEvents2['default'].INIT_REQUESTED, { sender: instance }); } } function onMediaFragmentLoaded(e) { if (e.fragmentModel !== streamProcessor.getFragmentModel()) return; var chunk = e.chunk; var bytes = chunk.bytes; var quality = chunk.quality; 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 && eventStreamMedia.length > 0 || eventStreamTrack && eventStreamTrack.length > 0) { var request = streamProcessor.getFragmentModel().getRequests({ state: _modelsFragmentModel2['default'].FRAGMENT_MODEL_EXECUTED, quality: quality, index: chunk.index })[0]; var events = handleInbandEvents(bytes, request, eventStreamMedia, eventStreamTrack); streamProcessor.getEventController().addInbandEvents(events); } chunk.bytes = deleteInbandEvents(bytes); appendToBuffer(chunk); } 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; if (e.error || !hasEnoughSpaceToAppend()) { if (e.error.code === _SourceBufferController2['default'].QUOTA_EXCEEDED_ERROR_CODE) { criticalBufferLevel = sourceBufferController.getTotalBufferedTime(buffer) * 0.8; } if (e.error.code === _SourceBufferController2['default'].QUOTA_EXCEEDED_ERROR_CODE || !hasEnoughSpaceToAppend()) { eventBus.trigger(_coreEventsEvents2['default'].QUOTA_EXCEEDED, { sender: instance, criticalBufferLevel: criticalBufferLevel }); //Tells ScheduleController to stop scheduling. clearBuffer(getClearRange()); // Then we clear the buffer and onCleared event will tell ScheduleController to start scheduling again. } return; } if (!isNaN(appendedBytesInfo.index)) { maxAppendedIndex = Math.max(appendedBytesInfo.index, maxAppendedIndex); checkIfBufferingCompleted(); } var ranges = sourceBufferController.getAllRanges(buffer); if (ranges && ranges.length > 0) { for (var i = 0, len = ranges.length; i < len; i++) { log('Buffered Range for type:', type, ':', ranges.start(i), ' - ', ranges.end(i)); } } onPlaybackProgression(); isAppendingInProgress = false; eventBus.trigger(_coreEventsEvents2['default'].BYTES_APPENDED, { sender: instance, quality: appendedBytesInfo.quality, startTime: appendedBytesInfo.start, index: appendedBytesInfo.index, bufferedRanges: ranges }); } function onQualityChanged(e) { if (requiredQuality === e.newQuality || type !== e.mediaType || streamProcessor.getStreamInfo().id !== e.streamInfo.id) return; updateBufferTimestampOffset(streamProcessor.getRepresentationInfoForQuality(e.newQuality).MSETimeOffset); requiredQuality = e.newQuality; } //********************************************************************** // START Buffer Level, State & Sufficiency Handling. //********************************************************************** function onPlaybackSeeking() { lastIndex = Number.POSITIVE_INFINITY; isBufferingCompleted = false; onPlaybackProgression(); } function onPlaybackProgression() { updateBufferLevel(); addBufferMetrics(); } function updateBufferLevel() { bufferLevel = sourceBufferController.getBufferLength(buffer, playbackController.getTime()); eventBus.trigger(_coreEventsEvents2['default'].BUFFER_LEVEL_UPDATED, { sender: instance, bufferLevel: bufferLevel }); checkIfSufficientBuffer(); } function addBufferMetrics() { if (!isActive()) return; metricsModel.addBufferState(type, bufferState, scheduleController.getBufferTarget()); metricsModel.addBufferLevel(type, new Date(), bufferLevel * 1000); } function checkIfBufferingCompleted() { var isLastIdxAppended = maxAppendedIndex >= lastIndex - 1; // Handles 0 and non 0 based request index if (isLastIdxAppended && !isBufferingCompleted) { isBufferingCompleted = true; eventBus.trigger(_coreEventsEvents2['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(_coreEventsEvents2['default'].BUFFER_LEVEL_STATE_CHANGED, { sender: instance, state: state, mediaType: type, streamInfo: streamProcessor.getStreamInfo() }); eventBus.trigger(state === BUFFER_LOADED ? _coreEventsEvents2['default'].BUFFER_LOADED : _coreEventsEvents2['default'].BUFFER_EMPTY, { 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 = []; inbandEventFound = false; //TODO Discuss why this is hear! /* 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 */ var inbandEvents = mediaInbandEvents.concat(trackInbandEvents); for (var i = 0, ln = inbandEvents.length; i < ln; i++) { eventStreams[inbandEvents[i].schemeIdUri] = inbandEvents[i]; } var isoFile = (0, _utilsBoxParser2['default'])(context).getInstance().parse(data); var eventBoxes = isoFile.getBoxes('emsg'); for (var i = 0, ln = eventBoxes.length; i < ln; i++) { var _event = adapter.getEvent(eventBoxes[i], eventStreams, fragmentStartTime); if (_event) { events.push(_event); } } return events; } function deleteInbandEvents(data) { if (!inbandEventFound) { //TODO Discuss why this is here. inbandEventFound is never set to true!! 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 i = 0; var j = 0; while (i < length) { var identifier = String.fromCharCode(data[i + 4], data[i + 5], data[i + 6], data[i + 7]); var 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 (!buffer) return; if (type === 'fragmentedText') return; var start = buffer.buffered.length ? buffer.buffered.start(0) : 0; var bufferToPrune = playbackController.getTime() - start - mediaPlayerModel.getBufferToKeep(); if (bufferToPrune > 0) { log('pruning buffer: ' + bufferToPrune + ' seconds.'); isPruningInProgress = true; sourceBufferController.remove(buffer, 0, Math.round(start + bufferToPrune), mediaSource); } } function getClearRange() { if (!buffer) return null; // we need to remove data that is more than one fragment before the video currentTime var currentTime = playbackController.getTime(); var req = streamProcessor.getFragmentModel().getRequests({ state: _modelsFragmentModel2['default'].FRAGMENT_MODEL_EXECUTED, time: currentTime })[0]; var range = sourceBufferController.getBufferRange(buffer, currentTime); var removeEnd = req && !isNaN(req.startTime) ? req.startTime : Math.floor(currentTime); if (range === null && buffer.buffered.length > 0) { removeEnd = buffer.buffered.end(buffer.buffered.length - 1); } return { start: buffer.buffered.start(0), end: removeEnd }; } function clearBuffer(range) { if (!range || !buffer) return; sourceBufferController.remove(buffer, range.start, range.end, mediaSource); } function onRemoved(e) { if (buffer !== e.buffer) return; if (isPruningInProgress) { isPruningInProgress = false; } updateBufferLevel(); eventBus.trigger(_coreEventsEvents2['default'].BUFFER_CLEARED, { sender: instance, from: e.from, to: e.to, hasEnoughSpaceToAppend: hasEnoughSpaceToAppend() }); //TODO - REMEMBER removed a timerout hack calling clearBuffer after manifestInfo.minBufferTime * 1000 if !hasEnoughSpaceToAppend() Aug 04 2016 } 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 onDataUpdateCompleted(e) { if (e.sender.getStreamProcessor() !== streamProcessor || e.error) return; updateBufferTimestampOffset(e.currentRepresentation.MSETimeOffset); } function onStreamCompleted(e) { if (e.fragmentModel !== streamProcessor.getFragmentModel()) return; lastIndex = e.request.index; checkIfBufferingCompleted(); } function onCurrentTrackChanged(e) { if (!buffer || e.newMediaInfo.type !== type || e.newMediaInfo.streamInfo.id !== streamProcessor.getStreamInfo().id) return; if (mediaController.getSwitchMode(type) === _MediaController2['default'].TRACK_SWITCH_MODE_ALWAYS_REPLACE) { clearBuffer(getClearRange()); } } function onWallclockTimeUpdated() { wallclockTicked++; var 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 reset(errored) { eventBus.off(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this); eventBus.off(_coreEventsEvents2['default'].QUALITY_CHANGE_REQUESTED, onQualityChanged, this); eventBus.off(_coreEventsEvents2['default'].INIT_FRAGMENT_LOADED, onInitFragmentLoaded, this); eventBus.off(_coreEventsEvents2['default'].MEDIA_FRAGMENT_LOADED, onMediaFragmentLoaded, this); eventBus.off(_coreEventsEvents2['default'].STREAM_COMPLETED, onStreamCompleted, this); eventBus.off(_coreEventsEvents2['default'].CURRENT_TRACK_CHANGED, onCurrentTrackChanged, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_PROGRESS, onPlaybackProgression, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED, onPlaybackProgression, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_RATE_CHANGED, onPlaybackRateChanged, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this); eventBus.off(_coreEventsEvents2['default'].WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, this); eventBus.off(_coreEventsEvents2['default'].SOURCEBUFFER_APPEND_COMPLETED, onAppended, this); eventBus.off(_coreEventsEvents2['default'].SOURCEBUFFER_REMOVE_COMPLETED, onRemoved, this); criticalBufferLevel = Number.POSITIVE_INFINITY; bufferState = BUFFER_EMPTY; requiredQuality = _AbrController2['default'].QUALITY_DEFAULT; lastIndex = Number.POSITIVE_INFINITY; maxAppendedIndex = 0; appendedBytesInfo = null; appendingMediaChunk = false; isBufferingCompleted = false; isAppendingInProgress = false; isPruningInProgress = false; playbackController = null; streamProcessor = null; abrController = 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, switchInitData: switchInitData, reset: reset }; setup(); return instance; } BufferController.__dashjs_factory_name = 'BufferController'; var factory = _coreFactoryMaker2['default'].getClassFactory(BufferController); factory.BUFFER_LOADED = BUFFER_LOADED; factory.BUFFER_EMPTY = BUFFER_EMPTY; exports['default'] = factory; module.exports = exports['default']; },{"../../core/Debug":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../models/FragmentModel":105,"../models/MediaPlayerModel":107,"../utils/BoxParser":152,"../utils/InitCache":158,"./AbrController":66,"./MediaController":72,"./PlaybackController":74,"./SourceBufferController":76}],70:[function(require,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 _controllersPlaybackController = require('../controllers/PlaybackController'); var _controllersPlaybackController2 = _interopRequireDefault(_controllersPlaybackController); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); function EventController() { var MPD_RELOAD_SCHEME = 'urn:mpeg:dash:event:2012'; var MPD_RELOAD_VALUE = 1; var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['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, _controllersPlaybackController2['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 {Array.} 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 {Array.} 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'] = _coreFactoryMaker2['default'].getSingletonFactory(EventController); module.exports = exports['default']; },{"../../core/Debug":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../controllers/PlaybackController":74}],71:[function(require,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 _voMetricsHTTPRequest = require('../vo/metrics/HTTPRequest'); var _voDataChunk = require('../vo/DataChunk'); var _voDataChunk2 = _interopRequireDefault(_voDataChunk); var _modelsFragmentModel = require('../models/FragmentModel'); var _modelsFragmentModel2 = _interopRequireDefault(_modelsFragmentModel); var _modelsMetricsModel = require('../models/MetricsModel'); var _modelsMetricsModel2 = _interopRequireDefault(_modelsMetricsModel); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); function FragmentController() /*config*/{ var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, fragmentModels = undefined; function setup() { fragmentModels = {}; eventBus.on(_coreEventsEvents2['default'].FRAGMENT_LOADING_COMPLETED, onFragmentLoadingCompleted, instance); } function getModel(type) { var model = fragmentModels[type]; if (!model) { model = (0, _modelsFragmentModel2['default'])(context).create({ metricsModel: (0, _modelsMetricsModel2['default'])(context).getInstance() }); fragmentModels[type] = model; } return model; } function isInitializationRequest(request) { return request && request.type && request.type === _voMetricsHTTPRequest.HTTPRequest.INIT_SEGMENT_TYPE; } function reset() { eventBus.off(_coreEventsEvents2['default'].FRAGMENT_LOADING_COMPLETED, onFragmentLoadingCompleted, this); for (var model in fragmentModels) { fragmentModels[model].reset(); } fragmentModels = {}; } function createDataChunk(bytes, request, streamId) { var chunk = new _voDataChunk2['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) { if (fragmentModels[e.request.mediaType] !== e.sender) return; var scheduleController = e.sender.getScheduleController(); var request = e.request; var bytes = e.response; var isInit = isInitializationRequest(request); var streamInfo = scheduleController.getStreamProcessor().getStreamInfo(); if (!bytes || !streamInfo) { log('No ' + request.mediaType + ' bytes to push or stream is inactive.'); return; } var chunk = createDataChunk(bytes, request, streamInfo.id); eventBus.trigger(isInit ? _coreEventsEvents2['default'].INIT_FRAGMENT_LOADED : _coreEventsEvents2['default'].MEDIA_FRAGMENT_LOADED, { chunk: chunk, fragmentModel: e.sender }); } instance = { getModel: getModel, isInitializationRequest: isInitializationRequest, reset: reset }; setup(); return instance; } FragmentController.__dashjs_factory_name = 'FragmentController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(FragmentController); module.exports = exports['default']; },{"../../core/Debug":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../models/FragmentModel":105,"../models/MetricsModel":108,"../vo/DataChunk":167,"../vo/metrics/HTTPRequest":185}],72:[function(require,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 _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _TextSourceBuffer = require('../TextSourceBuffer'); var _TextSourceBuffer2 = _interopRequireDefault(_TextSourceBuffer); var _utilsDOMStorage = require('../utils/DOMStorage'); var _utilsDOMStorage2 = _interopRequireDefault(_utilsDOMStorage); 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, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var textSourceBuffer = (0, _TextSourceBuffer2['default'])(context).getInstance(); var domStorage = (0, _utilsDOMStorage2['default'])(context).getInstance(); var instance = undefined, tracks = undefined, initialSettings = undefined, selectionMode = undefined, switchMode = undefined, errHandler = undefined; var validTrackSwitchModes = [TRACK_SWITCH_MODE_ALWAYS_REPLACE, TRACK_SWITCH_MODE_NEVER_REPLACE]; var validTrackSelectionModes = [TRACK_SELECTION_MODE_HIGHEST_BITRATE, TRACK_SELECTION_MODE_WIDEST_RANGE]; function initialize() { tracks = {}; resetInitialSettings(); resetSwitchMode(); } /** * @param {string} type * @param {StreamInfo} 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 {MediaInfo} 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 {string} type * @param {StreamInfo} 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 {string} type * @param {StreamInfo} streamInfo * @returns {Object|null} * @memberof MediaController# */ function getCurrentTrackFor(type, streamInfo) { if (!type || !streamInfo || streamInfo && !tracks[streamInfo.id]) return null; return tracks[streamInfo.id][type].current; } /** * @param {MediaInfo} 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 {MediaInfo} 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(_coreEventsEvents2['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 {string} type * @param {Object} value * @memberof MediaController# */ function setInitialSettings(type, value) { if (!type || !value) return; initialSettings[type] = value; } /** * @param {string} type * @returns {Object|null} * @memberof MediaController# */ function getInitialSettings(type) { if (!type) return null; return initialSettings[type]; } /** * @param {string} type * @param {string} mode * @memberof MediaController# */ function setSwitchMode(type, mode) { var isModeSupported = validTrackSwitchModes.indexOf(mode) !== -1; if (!isModeSupported) { log('track switch mode is not supported: ' + mode); return; } switchMode[type] = mode; } /** * @param {string} type * @returns {string} mode * @memberof MediaController# */ function getSwitchMode(type) { return switchMode[type]; } /** * @param {string} mode * @memberof MediaController# */ function setSelectionModeForInitialTrack(mode) { var isModeSupported = validTrackSelectionModes.indexOf(mode) !== -1; if (!isModeSupported) { log('track selection mode is not supported: ' + mode); return; } selectionMode = mode; } /** * @returns {string} mode * @memberof MediaController# */ function getSelectionModeForInitialTrack() { return selectionMode || DEFAULT_INIT_TRACK_SELECTION_MODE; } /** * @param {string} type * @returns {boolean} * @memberof MediaController# */ function isMultiTrackSupportedByType(type) { return type === 'audio' || type === 'video' || type === 'text' || type === 'fragmentedText'; } /** * @param {MediaInfo} t1 - first track to compare * @param {MediaInfo} 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 = _coreFactoryMaker2['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":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../TextSourceBuffer":62,"../utils/DOMStorage":155}],73:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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'] = _coreFactoryMaker2['default'].getSingletonFactory(MediaSourceController); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],74:[function(require,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 _BufferController = require('./BufferController'); var _BufferController2 = _interopRequireDefault(_BufferController); var _modelsURIQueryAndFragmentModel = require('../models/URIQueryAndFragmentModel'); var _modelsURIQueryAndFragmentModel2 = _interopRequireDefault(_modelsURIQueryAndFragmentModel); var _streamingModelsMediaPlayerModel = require('../../streaming/models/MediaPlayerModel'); var _streamingModelsMediaPlayerModel2 = _interopRequireDefault(_streamingModelsMediaPlayerModel); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); function PlaybackController() { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['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, _streamingModelsMediaPlayerModel2['default'])(context).getInstance(); } function initialize(StreamInfo) { streamInfo = StreamInfo; element = videoModel.getElement(); addAllListeners(); isDynamic = streamInfo.manifestInfo.isDynamic; liveStartTime = streamInfo.start; eventBus.on(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this); eventBus.on(_coreEventsEvents2['default'].BYTES_APPENDED, onBytesAppended, this); eventBus.on(_coreEventsEvents2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this); eventBus.on(_coreEventsEvents2['default'].PERIOD_SWITCH_STARTED, onPeriodSwitchStarted, this); if (playOnceInitialized) { playOnceInitialized = false; play(); } } function onPeriodSwitchStarted(e) { if (!isDynamic && e.fromStreamInfo && commonEarliestTime[e.fromStreamInfo.id]) { delete commonEarliestTime[e.fromStreamInfo.id]; } } function getTimeToStreamEnd() { var startTime = getStreamStartTime(true); var offset = isDynamic ? startTime - streamInfo.start : 0; return startTime + (streamInfo.duration - offset) - getTime(); } function isPlaybackStarted() { return getTime() > 0; } function getStreamId() { return streamInfo.id; } function play() { if (element) { element.autoplay = true; var p = element.play(); if (p && typeof Promise !== 'undefined' && p instanceof Promise) { p['catch'](function (e) { if (e.name === 'NotAllowedError') { eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_NOT_ALLOWED); } log('Caught pending play exception - continuing (' + e + ')'); }); } } 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 * @param {number} fragmentDuration - seconds? * @param {number} dvrWindowSize - seconds? * @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(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this); eventBus.off(_coreEventsEvents2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this); eventBus.off(_coreEventsEvents2['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 {boolean} ignoreStartOffset - ignore URL fragment start offset if true * @returns {number} object * @memberof PlaybackController# */ function getStreamStartTime(ignoreStartOffset) { var presentationStartTime = undefined; var fragData = (0, _modelsURIQueryAndFragmentModel2['default'])(context).getInstance().getURIFragmentData(); var fragS = parseInt(fragData.s, 10); var fragT = parseInt(fragData.t, 10); var startTimeOffset = NaN; if (!ignoreStartOffset) { startTimeOffset = !isNaN(fragS) ? fragS : fragT; } 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.end) { actualTime = Math.max(DVRWindow.end - streamInfo.manifestInfo.minBufferTime * 2, DVRWindow.start); } else if (currentTime < DVRWindow.start) { actualTime = DVRWindow.start; } else { return currentTime; } 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 onCanPlay() { eventBus.trigger(_coreEventsEvents2['default'].CAN_PLAY); } function onPlaybackStart() { log('Native video element event: play'); updateCurrentTime(); startUpdatingWallclockTime(); eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_STARTED, { startTime: getTime() }); } function onPlaybackPlaying() { log('Native video element event: playing'); eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_PLAYING, { playingTime: getTime() }); } function onPlaybackPaused() { log('Native video element event: pause'); eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_PAUSED, { ended: getEnded() }); } function onPlaybackSeeking() { var seekTime = getTime(); log('Seeking to: ' + seekTime); startUpdatingWallclockTime(); eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_SEEKING, { seekTime: seekTime }); } function onPlaybackSeeked() { log('Native video element event: seeked'); eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_SEEKED); } function onPlaybackTimeUpdated() { //log("Native video element event: timeupdate"); var time = getTime(); if (time === currentTime) return; currentTime = time; eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED, { timeToEnd: getTimeToStreamEnd(), time: time }); } function onPlaybackProgress() { //log("Native video element event: progress"); eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_PROGRESS); } function onPlaybackRateChanged() { var rate = getPlaybackRate(); log('Native video element event: ratechange: ', rate); eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_RATE_CHANGED, { playbackRate: rate }); } function onPlaybackMetaDataLoaded() { log('Native video element event: loadedmetadata'); if (!isDynamic && streamInfo.isFirst || timelineConverter.isTimeSyncCompleted()) { seekToStartTimeOffset(); } eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_METADATA_LOADED); startUpdatingWallclockTime(); } function onPlaybackEnded() { log('Native video element event: ended'); pause(); stopUpdatingWallclockTime(); eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_ENDED); } function onPlaybackError(event) { var target = event.target || event.srcElement; eventBus.trigger(_coreEventsEvents2['default'].PLAYBACK_ERROR, { error: target.error }); } function onWallclockTime() { eventBus.trigger(_coreEventsEvents2['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 && getStreamStartTime(true) < earliestTime && getTime() < 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 === _BufferController2['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, 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'] = _coreFactoryMaker2['default'].getSingletonFactory(PlaybackController); module.exports = exports['default']; },{"../../core/Debug":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../../streaming/models/MediaPlayerModel":107,"../models/URIQueryAndFragmentModel":109,"./BufferController":69}],75:[function(require,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 _voMetricsPlayList = require('../vo/metrics/PlayList'); var _PlaybackController = require('./PlaybackController'); var _PlaybackController2 = _interopRequireDefault(_PlaybackController); var _AbrController = require('./AbrController'); var _AbrController2 = _interopRequireDefault(_AbrController); var _BufferController = require('./BufferController'); var _BufferController2 = _interopRequireDefault(_BufferController); var _MediaController = require('./MediaController'); var _MediaController2 = _interopRequireDefault(_MediaController); var _rulesSchedulingBufferLevelRule = require('../rules/scheduling/BufferLevelRule'); var _rulesSchedulingBufferLevelRule2 = _interopRequireDefault(_rulesSchedulingBufferLevelRule); var _rulesSchedulingNextFragmentRequestRule = require('../rules/scheduling/NextFragmentRequestRule'); var _rulesSchedulingNextFragmentRequestRule2 = _interopRequireDefault(_rulesSchedulingNextFragmentRequestRule); var _TextSourceBuffer = require('../TextSourceBuffer'); var _TextSourceBuffer2 = _interopRequireDefault(_TextSourceBuffer); var _modelsMetricsModel = require('../models/MetricsModel'); var _modelsMetricsModel2 = _interopRequireDefault(_modelsMetricsModel); var _modelsFragmentModel = require('../models/FragmentModel'); var _modelsFragmentModel2 = _interopRequireDefault(_modelsFragmentModel); var _dashDashMetrics = require('../../dash/DashMetrics'); var _dashDashMetrics2 = _interopRequireDefault(_dashDashMetrics); var _dashDashAdapter = require('../../dash/DashAdapter'); var _dashDashAdapter2 = _interopRequireDefault(_dashDashAdapter); var _controllersSourceBufferController = require('../controllers/SourceBufferController'); var _controllersSourceBufferController2 = _interopRequireDefault(_controllersSourceBufferController); var _utilsLiveEdgeFinder = require('../utils/LiveEdgeFinder'); var _utilsLiveEdgeFinder2 = _interopRequireDefault(_utilsLiveEdgeFinder); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _controllersStreamController = require('../controllers/StreamController'); var _controllersStreamController2 = _interopRequireDefault(_controllersStreamController); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); function ScheduleController(config) { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['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, fragmentModel = undefined, isDynamic = undefined, currentRepresentationInfo = undefined, initialRequest = undefined, isStopped = undefined, playListMetrics = undefined, playListTraceMetrics = undefined, playListTraceMetricsClosed = undefined, isFragmentProcessingInProgress = undefined, timeToLoadDelay = undefined, scheduleTimeout = undefined, seekTarget = undefined, playbackController = undefined, mediaController = undefined, abrController = undefined, streamProcessor = undefined, streamController = undefined, fragmentController = undefined, bufferController = undefined, bufferLevelRule = undefined, nextFragmentRequestRule = undefined, scheduleWhilePaused = undefined, lastQualityIndex = undefined, topQualityIndex = undefined, lastInitQuality = undefined, replaceRequestArray = undefined; function setup() { initialRequest = true; lastInitQuality = NaN; lastQualityIndex = NaN; topQualityIndex = {}; replaceRequestArray = []; isStopped = false; playListMetrics = null; playListTraceMetrics = null; playListTraceMetricsClosed = true; isFragmentProcessingInProgress = false; timeToLoadDelay = 0; seekTarget = NaN; } function initialize(Type, StreamProcessor) { type = Type; streamProcessor = StreamProcessor; playbackController = (0, _PlaybackController2['default'])(context).getInstance(); mediaController = (0, _MediaController2['default'])(context).getInstance(); abrController = (0, _AbrController2['default'])(context).getInstance(); streamController = (0, _controllersStreamController2['default'])(context).getInstance(); fragmentController = streamProcessor.getFragmentController(); bufferController = streamProcessor.getBufferController(); fragmentModel = fragmentController.getModel(type); fragmentModel.setScheduleController(this); isDynamic = streamProcessor.isDynamic(); scheduleWhilePaused = mediaPlayerModel.getScheduleWhilePaused(); bufferLevelRule = (0, _rulesSchedulingBufferLevelRule2['default'])(context).create({ dashMetrics: (0, _dashDashMetrics2['default'])(context).getInstance(), metricsModel: (0, _modelsMetricsModel2['default'])(context).getInstance(), textSourceBuffer: (0, _TextSourceBuffer2['default'])(context).getInstance() }); nextFragmentRequestRule = (0, _rulesSchedulingNextFragmentRequestRule2['default'])(context).create({ adapter: (0, _dashDashAdapter2['default'])(context).getInstance(), sourceBufferController: (0, _controllersSourceBufferController2['default'])(context).getInstance(), textSourceBuffer: (0, _TextSourceBuffer2['default'])(context).getInstance() }); if (dashManifestModel.getIsTextTrack(type)) { eventBus.on(_coreEventsEvents2['default'].TIMED_TEXT_REQUESTED, onTimedTextRequested, this); } //eventBus.on(Events.LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this); eventBus.on(_coreEventsEvents2['default'].QUALITY_CHANGE_REQUESTED, onQualityChanged, this); eventBus.on(_coreEventsEvents2['default'].DATA_UPDATE_STARTED, onDataUpdateStarted, this); eventBus.on(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this); eventBus.on(_coreEventsEvents2['default'].FRAGMENT_LOADING_COMPLETED, onFragmentLoadingCompleted, this); eventBus.on(_coreEventsEvents2['default'].STREAM_COMPLETED, onStreamCompleted, this); eventBus.on(_coreEventsEvents2['default'].STREAM_INITIALIZED, onStreamInitialized, this); eventBus.on(_coreEventsEvents2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this); eventBus.on(_coreEventsEvents2['default'].BUFFER_CLEARED, onBufferCleared, this); eventBus.on(_coreEventsEvents2['default'].BYTES_APPENDED, onBytesAppended, this); eventBus.on(_coreEventsEvents2['default'].INIT_REQUESTED, onInitRequested, this); eventBus.on(_coreEventsEvents2['default'].QUOTA_EXCEEDED, onQuotaExceeded, this); eventBus.on(_coreEventsEvents2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_STARTED, onPlaybackStarted, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_RATE_CHANGED, onPlaybackRateChanged, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED, onPlaybackTimeUpdated, this); eventBus.on(_coreEventsEvents2['default'].URL_RESOLUTION_FAILED, onURLResolutionFailed, this); eventBus.on(_coreEventsEvents2['default'].FRAGMENT_LOADING_ABANDONED, onFragmentLoadingAbandoned, this); } function start() { if (!currentRepresentationInfo || bufferController.getIsBufferingCompleted()) return; addPlaylistTraceMetrics(); isStopped = false; if (initialRequest) { initialRequest = false; getInitRequest(currentRepresentationInfo.quality); } else { startScheduleTimer(0); } log('Schedule controller starting for ' + type); } function stop() { if (isStopped) return; isStopped = true; clearTimeout(scheduleTimeout); log('Schedule controller stopping for ' + type); } function hasTopQualityChanged(type, id) { topQualityIndex[id] = topQualityIndex[id] || {}; var newTopQualityIndex = abrController.getTopQualityIndexFor(type, id); if (topQualityIndex[id][type] != newTopQualityIndex) { log('Top quality' + type + ' index has changed from ' + topQualityIndex[id][type] + ' to ' + newTopQualityIndex); topQualityIndex[id][type] = newTopQualityIndex; return true; } return false; } function schedule() { if (isStopped || isFragmentProcessingInProgress || !bufferController || playbackController.isPaused() && !scheduleWhilePaused) return; validateExecutedFragmentRequest(); var isReplacement = replaceRequestArray.length > 0; if (isReplacement || hasTopQualityChanged(currentRepresentationInfo.mediaInfo.type, streamProcessor.getStreamInfo().id) || bufferLevelRule.execute(streamProcessor, type, streamController.isVideoTrackPresent())) { var getNextFragment = function getNextFragment() { if (currentRepresentationInfo.quality !== lastInitQuality) { lastInitQuality = currentRepresentationInfo.quality; bufferController.switchInitData(streamProcessor.getStreamInfo().id, currentRepresentationInfo.quality); } else { var replacement = replaceRequestArray.shift(); if (fragmentController.isInitializationRequest(replacement)) { getInitRequest(replacement.quality); } else { var request = nextFragmentRequestRule.execute(streamProcessor, replacement); if (request) { fragmentModel.executeRequest(request); } else { //Use case - Playing at the bleeding live edge and frag is not available yet. Cycle back around. isFragmentProcessingInProgress = false; startScheduleTimer(500); } } } }; isFragmentProcessingInProgress = true; if (isReplacement) { getNextFragment(); } else { abrController.getPlaybackQuality(streamProcessor); getNextFragment(); } } else { startScheduleTimer(500); } } function validateExecutedFragmentRequest() { //Validate that the fragment request executed and appended into the source buffer is as // good of quality as the current quality and is the correct media track. var safeBufferLevel = currentRepresentationInfo.fragmentDuration * 1.5; var request = fragmentModel.getRequests({ state: _modelsFragmentModel2['default'].FRAGMENT_MODEL_EXECUTED, time: playbackController.getTime() + safeBufferLevel, threshold: 0 })[0]; if (request && replaceRequestArray.indexOf(request) === -1 && !dashManifestModel.getIsTextTrack(type)) { if (!mediaController.isCurrentTrack(request.mediaInfo) || mediaPlayerModel.getFastSwitchEnabled() && request.quality < currentRepresentationInfo.quality && bufferController.getBufferLevel() >= safeBufferLevel && abrController.getAbandonmentStateFor(type) !== _AbrController2['default'].ABANDON_LOAD) { replaceRequest(request); log('Reloading outdated fragment at index: ', request.index); } else if (request.quality > currentRepresentationInfo.quality) { //The buffer has better quality it in then what we would request so set append point to end of buffer!! setSeekTarget(playbackController.getTime() + bufferController.getBufferLevel()); } } } function startScheduleTimer(value) { clearTimeout(scheduleTimeout); scheduleTimeout = setTimeout(schedule, value); } function onInitRequested(e) { if (e.sender.getStreamProcessor() !== streamProcessor) return; getInitRequest(currentRepresentationInfo.quality); } function getInitRequest(quality) { lastInitQuality = quality; var request = adapter.getInitRequest(streamProcessor, quality); if (request) { isFragmentProcessingInProgress = true; fragmentModel.executeRequest(request); } } function replaceRequest(request) { replaceRequestArray.push(request); } 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 new Error('Unexpected error! - currentRepresentationInfo is null or undefined'); } clearPlayListTraceMetrics(new Date(), _voMetricsPlayList.PlayListTrace.REPRESENTATION_SWITCH_STOP_REASON); addPlaylistTraceMetrics(); } function completeQualityChange(trigger) { var item = fragmentModel.getRequests({ state: _modelsFragmentModel2['default'].FRAGMENT_MODEL_EXECUTED, time: playbackController.getTime(), threshold: 0 })[0]; if (item && playbackController.getTime() >= item.startTime) { if (item.quality !== lastQualityIndex && trigger) { eventBus.trigger(_coreEventsEvents2['default'].QUALITY_CHANGE_RENDERED, { mediaType: type, oldQuality: lastQualityIndex, newQuality: item.quality }); } lastQualityIndex = item.quality; } } function onDataUpdateCompleted(e) { if (e.error || e.sender.getStreamProcessor() !== streamProcessor) return; currentRepresentationInfo = adapter.convertDataToTrack(manifestModel.getValue(), e.currentRepresentation); } function onStreamInitialized(e) { if (e.error || streamProcessor.getStreamInfo().id !== e.streamInfo.id) return; currentRepresentationInfo = streamProcessor.getCurrentRepresentationInfo(); if (isDynamic && initialRequest) { timelineConverter.setTimeSyncCompleted(true); setLiveEdgeSeekTarget(); } if (isStopped) { start(); } } function setLiveEdgeSeekTarget() { var liveEdge = (0, _utilsLiveEdgeFinder2['default'])(context).getInstance().getLiveEdge(); var dvrWindowSize = currentRepresentationInfo.mediaInfo.streamInfo.manifestInfo.DVRWindowSize / 2; var startTime = liveEdge - playbackController.computeLiveDelay(currentRepresentationInfo.fragmentDuration, dvrWindowSize); var request = adapter.getFragmentRequestForTime(streamProcessor, currentRepresentationInfo, startTime, { ignoreIsFinished: true }); seekTarget = playbackController.getLiveStartTime(); if (isNaN(seekTarget) || request.startTime > seekTarget) { playbackController.setLiveStartTime(request.startTime); seekTarget = request.startTime; } var manifestUpdateInfo = dashMetrics.getCurrentManifestUpdate(metricsModel.getMetricsFor('stream')); metricsModel.updateManifestUpdateInfo(manifestUpdateInfo, { currentTime: seekTarget, presentationStartTime: liveEdge, latency: liveEdge - seekTarget, clientTimeOffset: timelineConverter.getClientTimeOffset() }); } function onStreamCompleted(e) { if (e.fragmentModel !== fragmentModel) return; stop(); isFragmentProcessingInProgress = false; log('Stream is complete'); } function onFragmentLoadingCompleted(e) { if (e.sender !== fragmentModel) return; if (dashManifestModel.getIsTextTrack(type)) { isFragmentProcessingInProgress = false; } if (e.error && e.request.serviceLocation && !isStopped) { replaceRequest(e.request); isFragmentProcessingInProgress = false; startScheduleTimer(0); } } function onPlaybackTimeUpdated() { completeQualityChange(true); } function onBytesAppended(e) { if (e.sender.getStreamProcessor() !== streamProcessor) return; isFragmentProcessingInProgress = false; startScheduleTimer(0); } function onFragmentLoadingAbandoned(e) { if (e.streamProcessor !== streamProcessor) return; replaceRequest(e.request); isFragmentProcessingInProgress = false; startScheduleTimer(0); } function onDataUpdateStarted(e) { if (e.sender.getStreamProcessor() !== streamProcessor) return; stop(); } 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 && isStopped) { start(); } } function onBufferLevelStateChanged(e) { if (e.sender.getStreamProcessor() === streamProcessor && e.state === _BufferController2['default'].BUFFER_EMPTY && !playbackController.isSeeking()) { log('Buffer is empty! Stalling!'); clearPlayListTraceMetrics(new Date(), _voMetricsPlayList.PlayListTrace.REBUFFERING_REASON); } } function onQuotaExceeded(e) { if (e.sender.getStreamProcessor() !== streamProcessor) return; stop(); } function onURLResolutionFailed() { fragmentModel.abortRequests(); stop(); } function onTimedTextRequested(e) { if (e.sender.getStreamProcessor() !== streamProcessor) return; getInitRequest(e.index); } function onPlaybackStarted() { if (isStopped || !scheduleWhilePaused) { start(); } } function onPlaybackSeeking(e) { seekTarget = e.seekTime; setTimeToLoadDelay(0); if (isStopped) { start(); } var manifestUpdateInfo = dashMetrics.getCurrentManifestUpdate(metricsModel.getMetricsFor('stream')); 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 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 getBufferTarget() { return bufferLevelRule.getBufferTarget(streamProcessor, type, streamController.isVideoTrackPresent()); } function setPlayList(playList) { playListMetrics = playList; } function finalisePlayList(time, reason) { clearPlayListTraceMetrics(time, reason); playListMetrics = null; } function clearPlayListTraceMetrics(endTime, stopreason) { if (playListMetrics && playListTraceMetricsClosed === false) { var startTime = playListTraceMetrics.start; var duration = endTime.getTime() - startTime.getTime(); playListTraceMetrics.duration = duration; playListTraceMetrics.stopreason = stopreason; playListMetrics.trace.push(playListTraceMetrics); playListTraceMetricsClosed = true; } } function addPlaylistTraceMetrics() { if (playListMetrics && playListTraceMetricsClosed === true && currentRepresentationInfo) { playListTraceMetricsClosed = false; playListTraceMetrics = new _voMetricsPlayList.PlayListTrace(); playListTraceMetrics.representationid = currentRepresentationInfo.id; playListTraceMetrics.start = new Date(); playListTraceMetrics.mstart = playbackController.getTime() * 1000; playListTraceMetrics.playbackspeed = playbackController.getPlaybackRate().toString(); } } function reset() { //eventBus.off(Events.LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this); eventBus.off(_coreEventsEvents2['default'].DATA_UPDATE_STARTED, onDataUpdateStarted, this); eventBus.off(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this); eventBus.off(_coreEventsEvents2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this); eventBus.off(_coreEventsEvents2['default'].QUALITY_CHANGE_REQUESTED, onQualityChanged, this); eventBus.off(_coreEventsEvents2['default'].FRAGMENT_LOADING_COMPLETED, onFragmentLoadingCompleted, this); eventBus.off(_coreEventsEvents2['default'].STREAM_COMPLETED, onStreamCompleted, this); eventBus.off(_coreEventsEvents2['default'].STREAM_INITIALIZED, onStreamInitialized, this); eventBus.off(_coreEventsEvents2['default'].QUOTA_EXCEEDED, onQuotaExceeded, this); eventBus.off(_coreEventsEvents2['default'].BYTES_APPENDED, onBytesAppended, this); eventBus.off(_coreEventsEvents2['default'].BUFFER_CLEARED, onBufferCleared, this); eventBus.off(_coreEventsEvents2['default'].INIT_REQUESTED, onInitRequested, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_RATE_CHANGED, onPlaybackRateChanged, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_STARTED, onPlaybackStarted, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED, onPlaybackTimeUpdated, this); eventBus.off(_coreEventsEvents2['default'].URL_RESOLUTION_FAILED, onURLResolutionFailed, this); eventBus.off(_coreEventsEvents2['default'].FRAGMENT_LOADING_ABANDONED, onFragmentLoadingAbandoned, this); if (dashManifestModel.getIsTextTrack(type)) { eventBus.off(_coreEventsEvents2['default'].TIMED_TEXT_REQUESTED, onTimedTextRequested, this); } stop(); completeQualityChange(false); isFragmentProcessingInProgress = false; timeToLoadDelay = 0; seekTarget = NaN; playbackController = null; playListMetrics = null; } instance = { initialize: initialize, getStreamProcessor: getStreamProcessor, getSeekTarget: getSeekTarget, setSeekTarget: setSeekTarget, getFragmentModel: getFragmentModel, setTimeToLoadDelay: setTimeToLoadDelay, getTimeToLoadDelay: getTimeToLoadDelay, replaceRequest: replaceRequest, start: start, stop: stop, reset: reset, setPlayList: setPlayList, getBufferTarget: getBufferTarget, finalisePlayList: finalisePlayList }; setup(); return instance; } ScheduleController.__dashjs_factory_name = 'ScheduleController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(ScheduleController); module.exports = exports['default']; },{"../../core/Debug":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../../dash/DashAdapter":20,"../../dash/DashMetrics":22,"../TextSourceBuffer":62,"../controllers/SourceBufferController":76,"../controllers/StreamController":77,"../models/FragmentModel":105,"../models/MetricsModel":108,"../rules/scheduling/BufferLevelRule":149,"../rules/scheduling/NextFragmentRequestRule":150,"../utils/LiveEdgeFinder":160,"../vo/metrics/PlayList":187,"./AbrController":66,"./BufferController":69,"./MediaController":72,"./PlaybackController":74}],76:[function(require,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 _TextSourceBuffer = require('../TextSourceBuffer'); var _TextSourceBuffer2 = _interopRequireDefault(_TextSourceBuffer); var _MediaController = require('./MediaController'); var _MediaController2 = _interopRequireDefault(_MediaController); var _dashDashAdapter = require('../../dash/DashAdapter'); var _dashDashAdapter2 = _interopRequireDefault(_dashDashAdapter); var _utilsErrorHandler = require('../utils/ErrorHandler'); var _utilsErrorHandler2 = _interopRequireDefault(_utilsErrorHandler); var _StreamController = require('./StreamController'); var _StreamController2 = _interopRequireDefault(_StreamController); var _TextTracks = require('../TextTracks'); var _TextTracks2 = _interopRequireDefault(_TextTracks); var _utilsVTTParser = require('../utils/VTTParser'); var _utilsVTTParser2 = _interopRequireDefault(_utilsVTTParser); var _utilsTTMLParser = require('../utils/TTMLParser'); var _utilsTTMLParser2 = _interopRequireDefault(_utilsTTMLParser); var _modelsVideoModel = require('../models/VideoModel'); var _modelsVideoModel2 = _interopRequireDefault(_modelsVideoModel); var _voError = require('../vo/Error'); var _voError2 = _interopRequireDefault(_voError); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var QUOTA_EXCEEDED_ERROR_CODE = 22; function SourceBufferController() { var context = this.context; var eventBus = (0, _coreEventBus2['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 _voError2['default']('not really supported'); } buffer = mediaSource.addSourceBuffer(codec); } catch (ex) { // Note that in the following, the quotes are open to allow for extra text after stpp and wvtt if (mediaInfo.isText || codec.indexOf('codecs="stpp') !== -1 || codec.indexOf('codecs="wvtt') !== -1) { buffer = (0, _TextSourceBuffer2['default'])(context).getInstance(); buffer.setConfig({ errHandler: (0, _utilsErrorHandler2['default'])(context).getInstance(), adapter: (0, _dashDashAdapter2['default'])(context).getInstance(), dashManifestModel: dashManifestModel, mediaController: (0, _MediaController2['default'])(context).getInstance(), videoModel: (0, _modelsVideoModel2['default'])(context).getInstance(), streamController: (0, _StreamController2['default'])(context).getInstance(), textTracks: (0, _TextTracks2['default'])(context).getInstance(), VTTParser: (0, _utilsVTTParser2['default'])(context).getInstance(), TTMLParser: (0, _utilsTTMLParser2['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(_coreEventsEvents2['default'].SOURCEBUFFER_APPEND_COMPLETED, { buffer: buffer, bytes: bytes }); }); }); } catch (err) { eventBus.trigger(_coreEventsEvents2['default'].SOURCEBUFFER_APPEND_COMPLETED, { buffer: buffer, bytes: bytes, error: new _voError2['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(_coreEventsEvents2['default'].SOURCEBUFFER_REMOVE_COMPLETED, { buffer: buffer, from: start, to: end }); }); }); } catch (err) { eventBus.trigger(_coreEventsEvents2['default'].SOURCEBUFFER_REMOVE_COMPLETED, { buffer: buffer, from: start, to: end, error: new _voError2['default'](err.code, err.message, null) }); } } function abort(mediaSource, buffer) { try { if (mediaSource.readyState === 'open') { buffer.abort(); } else if (buffer.setTextTrack && mediaSource.readyState === 'ended') { buffer.abort(); //The cues need to be removed from the TextSourceBuffer via a call to 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 = _coreFactoryMaker2['default'].getSingletonFactory(SourceBufferController); factory.QUOTA_EXCEEDED_ERROR_CODE = QUOTA_EXCEEDED_ERROR_CODE; exports['default'] = factory; module.exports = exports['default']; },{"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../../dash/DashAdapter":20,"../TextSourceBuffer":62,"../TextTracks":63,"../models/VideoModel":110,"../utils/ErrorHandler":157,"../utils/TTMLParser":163,"../utils/VTTParser":165,"../vo/Error":168,"./MediaController":72,"./StreamController":77}],77:[function(require,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 _PlaybackController = require('./PlaybackController'); var _PlaybackController2 = _interopRequireDefault(_PlaybackController); var _Stream = require('../Stream'); var _Stream2 = _interopRequireDefault(_Stream); var _ManifestUpdater = require('../ManifestUpdater'); var _ManifestUpdater2 = _interopRequireDefault(_ManifestUpdater); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _modelsURIQueryAndFragmentModel = require('../models/URIQueryAndFragmentModel'); var _modelsURIQueryAndFragmentModel2 = _interopRequireDefault(_modelsURIQueryAndFragmentModel); var _modelsVideoModel = require('../models/VideoModel'); var _modelsVideoModel2 = _interopRequireDefault(_modelsVideoModel); var _modelsMediaPlayerModel = require('../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _voMetricsPlayList = require('../vo/metrics/PlayList'); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _utilsInitCache = require('../utils/InitCache'); var _utilsInitCache2 = _interopRequireDefault(_utilsInitCache); function StreamController() { var STREAM_END_THRESHOLD = 0.1; var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['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, initCache = 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, videoTrackDetected = undefined; function setup() { protectionController = null; streams = []; mediaPlayerModel = (0, _modelsMediaPlayerModel2['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(); initCache = (0, _utilsInitCache2['default'])(context).getInstance(); manifestUpdater = (0, _ManifestUpdater2['default'])(context).getInstance(); manifestUpdater.setConfig({ log: log, manifestModel: manifestModel, dashManifestModel: dashManifestModel }); manifestUpdater.initialize(manifestLoader); videoModel = (0, _modelsVideoModel2['default'])(context).getInstance(); playbackController = (0, _PlaybackController2['default'])(context).getInstance(); playbackController.setConfig({ streamController: instance, timelineConverter: timelineConverter, metricsModel: metricsModel, dashMetrics: dashMetrics, manifestModel: manifestModel, dashManifestModel: dashManifestModel, adapter: adapter, videoModel: videoModel }); eventBus.on(_coreEventsEvents2['default'].TIME_SYNCHRONIZATION_COMPLETED, onTimeSyncCompleted, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED, onPlaybackTimeUpdated, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_ENDED, onEnded, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_ERROR, onPlaybackError, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_STARTED, onPlaybackStarted, this); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_PAUSED, onPlaybackPaused, this); eventBus.on(_coreEventsEvents2['default'].MANIFEST_UPDATED, onManifestUpdated, this); eventBus.on(_coreEventsEvents2['default'].STREAM_BUFFERING_COMPLETED, onStreamBufferingCompleted, this); } /* * 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) { if (isVideoTrackPresent()) { 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; if (e.timeToEnd <= STREAM_END_THRESHOLD) { //only needed for multiple period content when the native event does not fire due to duration manipulation. onEnded(); } } function onPlaybackSeeking(e) { var seekingStream = getStreamForTime(e.seekTime); if (seekingStream && seekingStream !== activeStream) { flushPlaylistMetrics(_voMetricsPlayList.PlayListTrace.END_OF_PERIOD_STOP_REASON); switchStream(activeStream, seekingStream, e.seekTime); } else { flushPlaylistMetrics(_voMetricsPlayList.PlayListTrace.USER_REQUEST_STOP_REASON); } addPlaylistMetrics(_voMetricsPlayList.PlayList.SEEK_START_REASON); } function onPlaybackStarted() /*e*/{ if (initialPlayback) { initialPlayback = false; addPlaylistMetrics(_voMetricsPlayList.PlayList.INITIAL_PLAYOUT_START_REASON); } else { if (isPaused) { isPaused = false; addPlaylistMetrics(_voMetricsPlayList.PlayList.RESUME_FROM_PAUSE_START_REASON); } } } function onPlaybackPaused(e) { if (!e.ended) { isPaused = true; flushPlaylistMetrics(_voMetricsPlayList.PlayListTrace.USER_REQUEST_STOP_REASON); } } function onStreamBufferingCompleted() { var isLast = getActiveStreamInfo().isLast; if (mediaSource && isLast) { mediaSourceController.signalEndOfStream(mediaSource); } } 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 * @param {number} time * @param {string} id * @returns {number|null} */ 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 onEnded() { var nextStream = getNextStream(); if (nextStream) { switchStream(activeStream, nextStream, NaN); } flushPlaylistMetrics(nextStream ? _voMetricsPlayList.PlayListTrace.END_OF_PERIOD_STOP_REASON : _voMetricsPlayList.PlayListTrace.END_OF_CONTENT_STOP_REASON); } function getNextStream() { if (activeStream) { var _ret = (function () { var start = activeStream.getStreamInfo().start; var duration = activeStream.getStreamInfo().duration; return { v: streams.filter(function (stream) { return stream.getStreamInfo().start === start + duration; })[0] }; })(); if (typeof _ret === 'object') return _ret.v; } } function switchStream(oldStream, newStream, seekTime) { if (isStreamSwitchingInProgress || !newStream || oldStream === newStream) return; isStreamSwitchingInProgress = true; eventBus.trigger(_coreEventsEvents2['default'].PERIOD_SWITCH_STARTED, { fromStreamInfo: oldStream ? oldStream.getStreamInfo() : null, toStreamInfo: newStream.getStreamInfo() }); if (oldStream) oldStream.deactivate(); activeStream = newStream; playbackController.initialize(activeStream.getStreamInfo()); videoTrackDetected = checkVideoPresence(); //TODO detect if we should close and repose or jump to activateStream. openMediaSource(seekTime); } function openMediaSource(seekTime) { var sourceUrl = undefined; function onMediaSourceOpen() { log('MediaSource is open!'); window.URL.revokeObjectURL(sourceUrl); mediaSource.removeEventListener('sourceopen', onMediaSourceOpen); mediaSource.removeEventListener('webkitsourceopen', onMediaSourceOpen); setMediaDuration(); activateStream(seekTime); } 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 activateStream(seekTime) { activeStream.activate(mediaSource); if (!initialPlayback) { 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 })(); } } activeStream.startEventController(); if (autoPlay || !initialPlayback) { playbackController.play(); } isStreamSwitchingInProgress = false; eventBus.trigger(_coreEventsEvents2['default'].PERIOD_SWITCH_COMPLETED, { toStreamInfo: activeStream.getStreamInfo() }); } function setMediaDuration() { var manifestDuration = activeStream.getStreamInfo().manifestInfo.duration; var mediaDuration = mediaSourceController.setDuration(mediaSource, manifestDuration); log('Duration successfully set to: ' + mediaDuration); } function getComposedStream(streamInfo) { for (var i = 0, ln = streams.length; i < ln; i++) { if (streams[i].getId() === streamInfo.id) { return streams[i]; } } return null; } function composeStreams(manifest) { try { var streamsInfo = adapter.getStreamsInfo(manifest); if (streamsInfo.length === 0) { throw new Error('There are no streams'); } var manifestUpdateInfo = dashMetrics.getCurrentManifestUpdate(metricsModel.getMetricsFor('stream')); metricsModel.updateManifestUpdateInfo(manifestUpdateInfo, { currentTime: playbackController.getTime(), buffered: videoModel.getElement().buffered, presentationStartTime: streamsInfo[0].start, clientTimeOffset: timelineConverter.getClientTimeOffset() }); for (var i = 0, ln = streamsInfo.length; i < ln; i++) { // 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 var streamInfo = streamsInfo[i]; var stream = getComposedStream(streamInfo); if (!stream) { stream = (0, _Stream2['default'])(context).create({ manifestModel: manifestModel, manifestUpdater: manifestUpdater, adapter: adapter, timelineConverter: timelineConverter, capabilities: capabilities, errHandler: errHandler, baseURLController: baseURLController }); streams.push(stream); stream.initialize(streamInfo, protectionController); } else { stream.updateData(streamInfo); } metricsModel.addManifestUpdateStreamInfo(manifestUpdateInfo, streamInfo.id, streamInfo.index, streamInfo.start, streamInfo.duration); } if (!activeStream) { //const initStream = streamsInfo[0].manifestInfo.isDynamic ? streams[streams.length -1] : streams[0]; //TODO we need to figure out what the correct starting period is here and not just go to first or last in array. switchStream(null, streams[0], NaN); } eventBus.trigger(_coreEventsEvents2['default'].STREAMS_COMPOSED); } catch (e) { errHandler.manifestError(e.message, 'nostreamscomposed', manifest); hasInitialisationError = true; reset(); } } function onTimeSyncCompleted() /*e*/{ var manifest = manifestModel.getValue(); //TODO check if we can move this to initialize?? if (protectionController) { eventBus.trigger(_coreEventsEvents2['default'].PROTECTION_CREATED, { controller: protectionController, manifest: manifest }); protectionController.setMediaElement(videoModel.getElement()); if (protectionData) { protectionController.setProtectionData(protectionData); } } composeStreams(manifest); } 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, _modelsURIQueryAndFragmentModel2['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, '') === _modelsMediaPlayerModel2['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 isVideoTrackPresent() { if (videoTrackDetected === undefined) { videoTrackDetected = checkVideoPresence(); } return videoTrackDetected; } function checkVideoPresence() { var isVideoDetected = false; activeStream.getProcessors().forEach(function (p) { if (p.getMediaInfo().type === 'video') { isVideoDetected = true; } }); return isVideoDetected; } 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 _voMetricsPlayList.PlayList(); 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); } }); } } function onPlaybackError(e) { if (!e.error) return; var msg = ''; switch (e.error.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.message) { msg += ' (' + e.error.message + ')'; } 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(); } 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.errHandler) { errHandler = config.errHandler; } if (config.timelineConverter) { timelineConverter = config.timelineConverter; } } function reset() { timeSyncController.reset(); flushPlaylistMetrics(hasMediaError || hasInitialisationError ? _voMetricsPlayList.PlayListTrace.FAILURE_STOP_REASON : _voMetricsPlayList.PlayListTrace.USER_REQUEST_STOP_REASON); for (var i = 0, ln = streams.length; i < ln; i++) { var stream = streams[i]; stream.reset(hasMediaError); } streams = []; eventBus.off(_coreEventsEvents2['default'].PLAYBACK_TIME_UPDATED, onPlaybackTimeUpdated, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_ERROR, onPlaybackError, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_STARTED, onPlaybackStarted, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_PAUSED, onPlaybackPaused, this); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_ENDED, onEnded, this); eventBus.off(_coreEventsEvents2['default'].MANIFEST_UPDATED, onManifestUpdated, this); eventBus.off(_coreEventsEvents2['default'].STREAM_BUFFERING_COMPLETED, onStreamBufferingCompleted, this); baseURLController.reset(); manifestUpdater.reset(); metricsModel.clearAllCurrentMetrics(); manifestModel.setValue(null); manifestLoader.reset(); timelineConverter.reset(); liveEdgeFinder.reset(); adapter.reset(); initCache.reset(); isStreamSwitchingInProgress = false; isUpdating = false; activeStream = null; hasMediaError = false; hasInitialisationError = false; videoTrackDetected = undefined; 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(_coreEventsEvents2['default'].PROTECTION_DESTROYED, { data: manifestModel.getValue().url }); } } eventBus.trigger(_coreEventsEvents2['default'].STREAM_TEARDOWN_COMPLETE); } instance = { initialize: initialize, getAutoPlay: getAutoPlay, getActiveStreamInfo: getActiveStreamInfo, isStreamActive: isStreamActive, isVideoTrackPresent: isVideoTrackPresent, getStreamById: getStreamById, getTimeRelativeToStreamId: getTimeRelativeToStreamId, load: load, loadWithManifest: loadWithManifest, getActiveStreamCommonEarliestTime: getActiveStreamCommonEarliestTime, setConfig: setConfig, reset: reset }; setup(); return instance; } StreamController.__dashjs_factory_name = 'StreamController'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(StreamController); module.exports = exports['default']; },{"../../core/Debug":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../ManifestUpdater":56,"../Stream":60,"../models/MediaPlayerModel":107,"../models/URIQueryAndFragmentModel":109,"../models/VideoModel":110,"../utils/InitCache":158,"../vo/metrics/PlayList":187,"./PlaybackController":74}],78:[function(require,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 _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function TextController(config) { var context = this.context; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var sourceBufferController = config.sourceBufferController; var errHandler = config.errHandler; var instance = undefined, isBufferingCompleted = 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; isBufferingCompleted = false; eventBus.on(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this); eventBus.on(_coreEventsEvents2['default'].INIT_FRAGMENT_LOADED, onInitFragmentLoaded, this); } function initialize(Type, source, StreamProcessor) { type = Type; setMediaSource(source); streamProcessor = StreamProcessor; representationController = streamProcessor.getRepresentationController(); } /** * @param {MediaInfo }mediaInfo * @returns {Object} 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(_coreEventsEvents2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this); eventBus.off(_coreEventsEvents2['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(_coreEventsEvents2['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); } function getIsBufferingCompleted() { return isBufferingCompleted; } instance = { initialize: initialize, createBuffer: createBuffer, getBuffer: getBuffer, setBuffer: setBuffer, getStreamProcessor: getStreamProcessor, getIsBufferingCompleted: getIsBufferingCompleted, setMediaSource: setMediaSource, reset: reset }; setup(); return instance; } TextController.__dashjs_factory_name = 'TextController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(TextController); module.exports = exports['default']; },{"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18}],79:[function(require,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 _voError = require('./../vo/Error'); var _voError2 = _interopRequireDefault(_voError); var _coreEventBus = require('./../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('./../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var TIME_SYNC_FAILED_ERROR_CODE = 1; var HTTP_TIMEOUT_MS = 5000; function TimeSyncController() { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['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(_coreEventsEvents2['default'].TIME_SYNCHRONIZATION_COMPLETED, { time: time, offset: offset, error: failed ? new _voError2['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 = _coreFactoryMaker2['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":13,"../../core/FactoryMaker":15,"./../../core/EventBus":14,"./../../core/events/Events":18,"./../vo/Error":168}],80:[function(require,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 _XlinkLoader = require('../XlinkLoader'); var _XlinkLoader2 = _interopRequireDefault(_XlinkLoader); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _externalsXml2json = require('../../../externals/xml2json'); var _externalsXml2json2 = _interopRequireDefault(_externalsXml2json); var _utilsURLUtils = require('../utils/URLUtils'); var _utilsURLUtils2 = _interopRequireDefault(_utilsURLUtils); 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, _coreEventBus2['default'])(context).getInstance(); var urlUtils = (0, _utilsURLUtils2['default'])(context).getInstance(); var instance = undefined, matchers = undefined, iron = undefined, manifest = undefined, converter = undefined, xlinkLoader = undefined; function setup() { eventBus.on(_coreEventsEvents2['default'].XLINK_ELEMENT_LOADED, onXlinkElementLoaded, instance); xlinkLoader = (0, _XlinkLoader2['default'])(context).create({ errHandler: config.errHandler, metricsModel: config.metricsModel, requestModifier: config.requestModifier }); } function setMatchers(value) { matchers = value; } function setIron(value) { iron = value; } /** *

Triggers the resolution of the xlink.onLoad attributes in the manifest file

* @param {Object} mpd - the manifest */ function resolveManifestOnLoad(mpd) { var elements; // First resolve all periods, so unnecessary requests inside onLoad Periods with Default content are avoided converter = new _externalsXml2json2['default']({ escapeMode: false, attributePrefix: '', arrayAccessForm: 'property', emptyNodeForm: 'object', stripWhitespaces: false, enableToStringFunc: false, ignoreRoot: true, matchers: matchers }); 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(_coreEventsEvents2['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 (urlUtils.isHTTPURL(element.url)) { url = element.url; } else { url = element.originalContent.BaseURL + element.url; } xlinkLoader.load(url, element, resolveObject); } } function onXlinkElementLoaded(event) { var element, resolveObject, index; var openingTag = ''; var closingTag = ''; 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(_coreEventsEvents2['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(_coreEventsEvents2['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'] = _coreFactoryMaker2['default'].getClassFactory(XlinkController); module.exports = exports['default']; },{"../../../externals/xml2json":11,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../XlinkLoader":65,"../utils/URLUtils":164}],81:[function(require,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 _utilsDVBErrorsTranslator = require('./utils/DVBErrorsTranslator'); var _utilsDVBErrorsTranslator2 = _interopRequireDefault(_utilsDVBErrorsTranslator); var _MetricsReportingEvents = require('./MetricsReportingEvents'); var _MetricsReportingEvents2 = _interopRequireDefault(_MetricsReportingEvents); var _controllersMetricsCollectionController = require('./controllers/MetricsCollectionController'); var _controllersMetricsCollectionController2 = _interopRequireDefault(_controllersMetricsCollectionController); var _metricsMetricsHandlerFactory = require('./metrics/MetricsHandlerFactory'); var _metricsMetricsHandlerFactory2 = _interopRequireDefault(_metricsMetricsHandlerFactory); var _reportingReportingFactory = require('./reporting/ReportingFactory'); var _reportingReportingFactory2 = _interopRequireDefault(_reportingReportingFactory); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function MetricsReporting() { var context = this.context; var instance = undefined; var dvbErrorsTranslator = undefined; /** * Create a MetricsCollectionController, and a DVBErrorsTranslator * @param {Object} config - dependancies from owner * @return {MetricsCollectionController} Metrics Collection Controller */ function createMetricsReporting(config) { dvbErrorsTranslator = (0, _utilsDVBErrorsTranslator2['default'])(context).getInstance({ eventBus: config.eventBus, metricsModel: config.metricsModel }); return (0, _controllersMetricsCollectionController2['default'])(context).create(config); } /** * Get the ReportingFactory to allow new reporters to be registered * @return {ReportingFactory} Reporting Factory */ function getReportingFactory() { return (0, _reportingReportingFactory2['default'])(context).getInstance(); } /** * Get the MetricsHandlerFactory to allow new handlers to be registered * @return {MetricsHandlerFactory} Metrics Handler Factory */ function getMetricsHandlerFactory() { return (0, _metricsMetricsHandlerFactory2['default'])(context).getInstance(); } instance = { createMetricsReporting: createMetricsReporting, getReportingFactory: getReportingFactory, getMetricsHandlerFactory: getMetricsHandlerFactory }; return instance; } MetricsReporting.__dashjs_factory_name = 'MetricsReporting'; var factory = _coreFactoryMaker2['default'].getClassFactory(MetricsReporting); factory.events = _MetricsReportingEvents2['default']; exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15,"./MetricsReportingEvents":82,"./controllers/MetricsCollectionController":83,"./metrics/MetricsHandlerFactory":88,"./reporting/ReportingFactory":93,"./utils/DVBErrorsTranslator":95}],82:[function(require,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 _coreEventsEventsBase = require('../../core/events/EventsBase'); var _coreEventsEventsBase2 = _interopRequireDefault(_coreEventsEventsBase); var MetricsReportingEvents = (function (_EventsBase) { _inherits(MetricsReportingEvents, _EventsBase); function MetricsReportingEvents() { _classCallCheck(this, MetricsReportingEvents); _get(Object.getPrototypeOf(MetricsReportingEvents.prototype), 'constructor', this).call(this); this.METRICS_INITIALISATION_COMPLETE = 'internal_metricsReportingInitialized'; this.BECAME_REPORTING_PLAYER = 'internal_becameReportingPlayer'; } return MetricsReportingEvents; })(_coreEventsEventsBase2['default']); var metricsReportingEvents = new MetricsReportingEvents(); exports['default'] = metricsReportingEvents; module.exports = exports['default']; },{"../../core/events/EventsBase":19}],83:[function(require,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 _MetricsController = require('./MetricsController'); var _MetricsController2 = _interopRequireDefault(_MetricsController); var _utilsManifestParsing = require('../utils/ManifestParsing'); var _utilsManifestParsing2 = _interopRequireDefault(_utilsManifestParsing); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _MetricsReportingEvents = require('../MetricsReportingEvents'); var _MetricsReportingEvents2 = _interopRequireDefault(_MetricsReportingEvents); var _coreEventsEvents = require('../../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); function MetricsCollectionController(config) { var metricsControllers = {}; var context = this.context; var eventBus = config.eventBus; function update(e) { if (e.error) { return; } // start by assuming all existing controllers need removing var controllersToRemove = Object.keys(metricsControllers); var metrics = (0, _utilsManifestParsing2['default'])(context).getInstance({ dashManifestModel: config.dashManifestModel }).getMetrics(e.manifest); metrics.forEach(function (m) { var key = JSON.stringify(m); if (!metricsControllers.hasOwnProperty(key)) { try { var controller = (0, _MetricsController2['default'])(context).create(config); controller.initialize(m); metricsControllers[key] = controller; } catch (e) { // fail quietly } } else { // we still need this controller - delete from removal list controllersToRemove.splice(key, 1); } }); // now remove the unwanted controllers controllersToRemove.forEach(function (c) { metricsControllers[c].reset(); delete metricsControllers[c]; }); eventBus.trigger(_MetricsReportingEvents2['default'].METRICS_INITIALISATION_COMPLETE); } function reset() { Object.keys(metricsControllers).forEach(function (key) { metricsControllers[key].reset(); }); metricsControllers = {}; } function setup() { eventBus.on(_coreEventsEvents2['default'].MANIFEST_UPDATED, update); eventBus.on(_coreEventsEvents2['default'].STREAM_TEARDOWN_COMPLETE, reset); } setup(); // don't export any actual methods return {}; } MetricsCollectionController.__dashjs_factory_name = 'MetricsCollectionController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(MetricsCollectionController); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../../core/events/Events":18,"../MetricsReportingEvents":82,"../utils/ManifestParsing":97,"./MetricsController":84}],84:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _RangeController = require('./RangeController'); var _RangeController2 = _interopRequireDefault(_RangeController); var _ReportingController = require('./ReportingController'); var _ReportingController2 = _interopRequireDefault(_ReportingController); var _MetricsHandlersController = require('./MetricsHandlersController'); var _MetricsHandlersController2 = _interopRequireDefault(_MetricsHandlersController); function MetricsController(config) { var metricsHandlersController = undefined, reportingController = undefined, rangeController = undefined, instance = undefined; var context = this.context; function initialize(metricsEntry) { try { rangeController = (0, _RangeController2['default'])(context).create({ mediaElement: config.mediaElement }); rangeController.initialize(metricsEntry.Range); reportingController = (0, _ReportingController2['default'])(context).create({ log: config.log }); reportingController.initialize(metricsEntry.Reporting, rangeController); metricsHandlersController = (0, _MetricsHandlersController2['default'])(context).create({ log: config.log, eventBus: config.eventBus }); metricsHandlersController.initialize(metricsEntry.metrics, reportingController); } catch (e) { reset(); throw e; } } function reset() { if (metricsHandlersController) { metricsHandlersController.reset(); } if (reportingController) { reportingController.reset(); } if (rangeController) { rangeController.reset(); } } instance = { initialize: initialize, reset: reset }; return instance; } MetricsController.__dashjs_factory_name = 'MetricsController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(MetricsController); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"./MetricsHandlersController":85,"./RangeController":86,"./ReportingController":87}],85:[function(require,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 _metricsMetricsHandlerFactory = require('../metrics/MetricsHandlerFactory'); var _metricsMetricsHandlerFactory2 = _interopRequireDefault(_metricsMetricsHandlerFactory); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _MediaPlayerEvents = require('../../MediaPlayerEvents'); var _MediaPlayerEvents2 = _interopRequireDefault(_MediaPlayerEvents); function MetricsHandlersController(config) { var handlers = []; var instance = undefined; var context = this.context; var eventBus = config.eventBus; var metricsHandlerFactory = (0, _metricsMetricsHandlerFactory2['default'])(context).getInstance({ log: config.log, eventBus: config.eventBus }); function handle(e) { handlers.forEach(function (handler) { handler.handleNewMetric(e.metric, e.value, e.mediaType); }); } function initialize(metrics, reportingController) { metrics.split(',').forEach(function (m, midx, ms) { var handler; // there is a bug in ISO23009-1 where the metrics attribute // is a comma-separated list but HttpList key can contain a // comma enclosed by (). if (m.indexOf('(') !== -1 && m.indexOf(')') === -1) { var nextm = ms[midx + 1]; if (nextm && nextm.indexOf('(') === -1 && nextm.indexOf(')') !== -1) { m += ',' + nextm; // delete the next metric so forEach does not visit. delete ms[midx + 1]; } } handler = metricsHandlerFactory.create(m, reportingController); if (handler) { handlers.push(handler); } }); eventBus.on(_MediaPlayerEvents2['default'].METRIC_ADDED, handle, instance); eventBus.on(_MediaPlayerEvents2['default'].METRIC_UPDATED, handle, instance); } function reset() { eventBus.off(_MediaPlayerEvents2['default'].METRIC_ADDED, handle, instance); eventBus.off(_MediaPlayerEvents2['default'].METRIC_UPDATED, handle, instance); handlers.forEach(function (handler) { return handler.reset(); }); handlers = []; } instance = { initialize: initialize, reset: reset }; return instance; } MetricsHandlersController.__dashjs_factory_name = 'MetricsHandlersController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(MetricsHandlersController); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../MediaPlayerEvents":58,"../metrics/MetricsHandlerFactory":88}],86:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _utilsCustomTimeRanges = require('../../utils/CustomTimeRanges'); var _utilsCustomTimeRanges2 = _interopRequireDefault(_utilsCustomTimeRanges); function RangeController(config) { var useWallClockTime = false; var context = this.context; var instance = undefined, ranges = undefined; var mediaElement = config.mediaElement; function initialize(rs) { if (rs && rs.length) { rs.forEach(function (r) { var start = r.starttime; var end = start + r.duration; ranges.add(start, end); }); useWallClockTime = !!rs[0]._useWallClockTime; } } function reset() { ranges.clear(); } function setup() { ranges = (0, _utilsCustomTimeRanges2['default'])(context).create(); } function isEnabled() { var numRanges = ranges.length; var time; if (!numRanges) { return true; } // When not present, DASH Metrics reporting is requested // for the whole duration of the content. time = useWallClockTime ? new Date().getTime() / 1000 : mediaElement.currentTime; for (var i = 0; i < numRanges; i += 1) { var start = ranges.start(i); var end = ranges.end(i); if (start <= time && time < end) { return true; } } return false; } instance = { initialize: initialize, reset: reset, isEnabled: isEnabled }; setup(); return instance; } RangeController.__dashjs_factory_name = 'RangeController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(RangeController); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../utils/CustomTimeRanges":154}],87:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _reportingReportingFactory = require('../reporting/ReportingFactory'); var _reportingReportingFactory2 = _interopRequireDefault(_reportingReportingFactory); function ReportingController(config) { var reporters = []; var instance = undefined; var reportingFactory = (0, _reportingReportingFactory2['default'])(this.context).getInstance({ log: config.log }); function initialize(reporting, rangeController) { // "if multiple Reporting elements are present, it is expected that // the client processes one of the recognized reporting schemes." // to ignore this, and support multiple Reporting per Metric, // simply change the 'some' below to 'forEach' reporting.some(function (r) { var reporter = reportingFactory.create(r, rangeController); if (reporter) { reporters.push(reporter); return true; } }); } function reset() { reporters.forEach(function (r) { return r.reset(); }); reporters = []; } function report(type, vos) { reporters.forEach(function (r) { return r.report(type, vos); }); } instance = { initialize: initialize, reset: reset, report: report }; return instance; } ReportingController.__dashjs_factory_name = 'ReportingController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(ReportingController); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../reporting/ReportingFactory":93}],88:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _handlersBufferLevelHandler = require('./handlers/BufferLevelHandler'); var _handlersBufferLevelHandler2 = _interopRequireDefault(_handlersBufferLevelHandler); var _handlersDVBErrorsHandler = require('./handlers/DVBErrorsHandler'); var _handlersDVBErrorsHandler2 = _interopRequireDefault(_handlersDVBErrorsHandler); var _handlersHttpListHandler = require('./handlers/HttpListHandler'); var _handlersHttpListHandler2 = _interopRequireDefault(_handlersHttpListHandler); var _handlersGenericMetricHandler = require('./handlers/GenericMetricHandler'); var _handlersGenericMetricHandler2 = _interopRequireDefault(_handlersGenericMetricHandler); function MetricsHandlerFactory(config) { var instance = undefined; var log = config.log; // group 1: key, [group 3: n [, group 5: type]] var keyRegex = /([a-zA-Z]*)(\(([0-9]*)(\,\s*([a-zA-Z]*))?\))?/; var context = this.context; var knownFactoryProducts = { BufferLevel: _handlersBufferLevelHandler2['default'], DVBErrors: _handlersDVBErrorsHandler2['default'], HttpList: _handlersHttpListHandler2['default'], PlayList: _handlersGenericMetricHandler2['default'], RepSwitchList: _handlersGenericMetricHandler2['default'], TcpList: _handlersGenericMetricHandler2['default'] }; function create(listType, reportingController) { var matches = listType.match(keyRegex); var handler; if (!matches) { return; } try { handler = knownFactoryProducts[matches[1]](context).create({ eventBus: config.eventBus }); handler.initialize(matches[1], reportingController, matches[3], matches[5]); } catch (e) { handler = null; log('MetricsHandlerFactory: Could not create handler for type ' + matches[1] + ' with args ' + matches[3] + ', ' + matches[5] + ' (' + e.message + ')'); } return handler; } function register(key, handler) { knownFactoryProducts[key] = handler; } function unregister(key) { delete knownFactoryProducts[key]; } instance = { create: create, register: register, unregister: unregister }; return instance; } MetricsHandlerFactory.__dashjs_factory_name = 'MetricsHandlerFactory'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(MetricsHandlerFactory); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"./handlers/BufferLevelHandler":89,"./handlers/DVBErrorsHandler":90,"./handlers/GenericMetricHandler":91,"./handlers/HttpListHandler":92}],89:[function(require,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 _coreFactoryMaker = require('../../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _utilsHandlerHelpers = require('../../utils/HandlerHelpers'); var _utilsHandlerHelpers2 = _interopRequireDefault(_utilsHandlerHelpers); function BufferLevelHandler() { var instance = undefined, reportingController = undefined, n = undefined, name = undefined, interval = undefined, lastReportedTime = undefined; var context = this.context; var handlerHelpers = (0, _utilsHandlerHelpers2['default'])(context).getInstance(); var storedVOs = []; function getLowestBufferLevelVO() { try { return Object.keys(storedVOs).map(function (key) { return storedVOs[key]; }).reduce(function (a, b) { return a.level < b.level ? a : b; }); } catch (e) { return; } } function intervalCallback() { var vo = getLowestBufferLevelVO(); if (vo) { if (lastReportedTime !== vo.t) { lastReportedTime = vo.t; reportingController.report(name, vo); } } } function initialize(basename, rc, n_ms) { if (rc) { // this will throw if n is invalid, to be // caught by the initialize caller. n = handlerHelpers.validateN(n_ms); reportingController = rc; name = handlerHelpers.reconstructFullMetricName(basename, n_ms); interval = setInterval(intervalCallback, n); } } function reset() { clearInterval(interval); interval = null; n = 0; reportingController = null; lastReportedTime = null; } function handleNewMetric(metric, vo, type) { if (metric === 'BufferLevel') { storedVOs[type] = vo; } } instance = { initialize: initialize, reset: reset, handleNewMetric: handleNewMetric }; return instance; } BufferLevelHandler.__dashjs_factory_name = 'BufferLevelHandler'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(BufferLevelHandler); module.exports = exports['default']; },{"../../../../core/FactoryMaker":15,"../../utils/HandlerHelpers":96}],90:[function(require,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 _coreFactoryMaker = require('../../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _MetricsReportingEvents = require('../../MetricsReportingEvents'); var _MetricsReportingEvents2 = _interopRequireDefault(_MetricsReportingEvents); function DVBErrorsHandler(config) { var instance = undefined, reportingController = undefined; var eventBus = config.eventBus; function onInitialisationComplete() { // we only want to report this once per call to initialize eventBus.off(_MetricsReportingEvents2['default'].METRICS_INITIALISATION_COMPLETE, onInitialisationComplete, this); // Note: A Player becoming a reporting Player is itself // something which is recorded by the DVBErrors metric. eventBus.trigger(_MetricsReportingEvents2['default'].BECAME_REPORTING_PLAYER); } function initialize(unused, rc) { if (rc) { reportingController = rc; eventBus.on(_MetricsReportingEvents2['default'].METRICS_INITIALISATION_COMPLETE, onInitialisationComplete, this); } } function reset() { reportingController = null; } function handleNewMetric(metric, vo) { // simply pass metric straight through if (metric === 'DVBErrors') { if (reportingController) { reportingController.report(metric, vo); } } } instance = { initialize: initialize, reset: reset, handleNewMetric: handleNewMetric }; return instance; } exports['default'] = _coreFactoryMaker2['default'].getClassFactory(DVBErrorsHandler); module.exports = exports['default']; },{"../../../../core/FactoryMaker":15,"../../MetricsReportingEvents":82}],91:[function(require,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 _coreFactoryMaker = require('../../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function GenericMetricHandler() { var instance = undefined, metricName = undefined, reportingController = undefined; function initialize(name, rc) { metricName = name; reportingController = rc; } function reset() { reportingController = null; metricName = undefined; } function handleNewMetric(metric, vo) { // simply pass metric straight through if (metric === metricName) { if (reportingController) { reportingController.report(metricName, vo); } } } instance = { initialize: initialize, reset: reset, handleNewMetric: handleNewMetric }; return instance; } GenericMetricHandler.__dashjs_factory_name = 'GenericMetricHandler'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(GenericMetricHandler); module.exports = exports['default']; },{"../../../../core/FactoryMaker":15}],92:[function(require,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 _coreFactoryMaker = require('../../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _utilsHandlerHelpers = require('../../utils/HandlerHelpers'); var _utilsHandlerHelpers2 = _interopRequireDefault(_utilsHandlerHelpers); function HttpListHandler() { var instance = undefined, reportingController = undefined, n = undefined, type = undefined, name = undefined, interval = undefined; var storedVos = []; var handlerHelpers = (0, _utilsHandlerHelpers2['default'])(this.context).getInstance(); function intervalCallback() { var vos = storedVos; if (vos.length) { if (reportingController) { reportingController.report(name, vos); } } storedVos = []; } function initialize(basename, rc, n_ms, requestType) { if (rc) { // this will throw if n is invalid, to be // caught by the initialize caller. n = handlerHelpers.validateN(n_ms); reportingController = rc; if (requestType && requestType.length) { type = requestType; } name = handlerHelpers.reconstructFullMetricName(basename, n_ms, requestType); interval = setInterval(intervalCallback, n); } } function reset() { clearInterval(interval); interval = null; n = null; type = null; storedVos = []; reportingController = null; } function handleNewMetric(metric, vo) { if (metric === 'HttpList') { if (!type || type === vo.type) { storedVos.push(vo); } } } instance = { initialize: initialize, reset: reset, handleNewMetric: handleNewMetric }; return instance; } HttpListHandler.__dashjs_factory_name = 'HttpListHandler'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(HttpListHandler); module.exports = exports['default']; },{"../../../../core/FactoryMaker":15,"../../utils/HandlerHelpers":96}],93:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _reportersDVBReporting = require('./reporters/DVBReporting'); var _reportersDVBReporting2 = _interopRequireDefault(_reportersDVBReporting); function ReportingFactory(config) { var knownReportingSchemeIdUris = { 'urn:dvb:dash:reporting:2014': _reportersDVBReporting2['default'] }; var context = this.context; var log = config.log; var instance = undefined; function create(entry, rangeController) { var reporting; try { reporting = knownReportingSchemeIdUris[entry.schemeIdUri](context).create(); reporting.initialize(entry, rangeController); } catch (e) { reporting = null; log('ReportingFactory: could not create Reporting with schemeIdUri ' + entry.schemeIdUri + ' (' + e.message + ')'); } return reporting; } function register(schemeIdUri, moduleName) { knownReportingSchemeIdUris[schemeIdUri] = moduleName; } function unregister(schemeIdUri) { delete knownReportingSchemeIdUris[schemeIdUri]; } instance = { create: create, register: register, unregister: unregister }; return instance; } ReportingFactory.__dashjs_factory_name = 'ReportingFactory'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(ReportingFactory); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"./reporters/DVBReporting":94}],94:[function(require,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 _coreFactoryMaker = require('../../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _utilsMetricSerialiser = require('../../utils/MetricSerialiser'); var _utilsMetricSerialiser2 = _interopRequireDefault(_utilsMetricSerialiser); var _utilsRNG = require('../../utils/RNG'); var _utilsRNG2 = _interopRequireDefault(_utilsRNG); function DVBReporting() { var instance = undefined; var context = this.context; var metricSerialiser = (0, _utilsMetricSerialiser2['default'])(context).getInstance(); var randomNumberGenerator = (0, _utilsRNG2['default'])(context).getInstance(); var USE_DRAFT_DVB_SPEC = true; var isReportingPlayer = false; var reportingPlayerStatusDecided = false; var reportingUrl = null; var rangeController = null; var allowPendingRequestsToCompleteOnReset = true; var pendingRequests = []; function doGetRequest(url, successCB, failureCB) { var req = new XMLHttpRequest(); var oncomplete = function oncomplete() { var reqIndex = pendingRequests.indexOf(req); if (reqIndex === -1) { return; } else { pendingRequests.splice(reqIndex, 1); } if (req.status >= 200 && req.status < 300) { if (successCB) { successCB(); } } else { if (failureCB) { failureCB(); } } }; pendingRequests.push(req); try { req.open('GET', url); req.onloadend = oncomplete; req.onerror = oncomplete; req.send(); } catch (e) { req.onerror(); } } function report(type, vos) { if (!Array.isArray(vos)) { vos = [vos]; } // If the Player is not a reporting Player, then the Player shall // not report any errors. // ... In addition to any time restrictions specified by a Range // element within the Metrics element. if (isReportingPlayer && rangeController.isEnabled()) { // This reporting mechanism operates by creating one HTTP GET // request for every entry in the top level list of the metric. vos.forEach(function (vo) { var url = metricSerialiser.serialise(vo); // this has been proposed for errata if (USE_DRAFT_DVB_SPEC && type !== 'DVBErrors') { url = 'metricname=' + type + '&' + url; } // Take the value of the @reportingUrl attribute, append a // question mark ('?') character and then append the string // created in the previous step. url = reportingUrl + '?' + url; // Make an HTTP GET request to the URL contained within the // string created in the previous step. doGetRequest(url, null, function () { // If the Player is unable to make the report, for // example because the @reportingUrl is invalid, the // host cannot be reached, or an HTTP status code other // than one in the 200 series is received, the Player // shall cease being a reporting Player for the // duration of the MPD. isReportingPlayer = false; }); }); } } function initialize(entry, rc) { var probability; rangeController = rc; reportingUrl = entry['dvb:reportingUrl']; // If a required attribute is missing, the Reporting descriptor may // be ignored by the Player if (!reportingUrl) { throw new Error('required parameter missing (dvb:reportingUrl)'); } // A Player's status, as a reporting Player or not, shall remain // static for the duration of the MPD, regardless of MPD updates. // (i.e. only calling reset (or failure) changes this state) if (!reportingPlayerStatusDecided) { // NOTE: DVB spec has a typo where it incorrectly references the // priority attribute, which should be probability probability = entry['dvb:probability'] || entry['dvb:priority'] || 0; // If the @priority attribute is set to 1000, it shall be a reporting Player. // If the @priority attribute is missing, the Player shall not be a reporting Player. // For any other value of the @probability attribute, it shall decide at random whether to be a // reporting Player, such that the probability of being one is @probability/1000. if (probability && (probability === 1000 || probability / 1000 >= randomNumberGenerator.random())) { isReportingPlayer = true; } reportingPlayerStatusDecided = true; } } function reset() { if (!allowPendingRequestsToCompleteOnReset) { pendingRequests.forEach(function (req) { return req.abort(); }); pendingRequests = []; } reportingPlayerStatusDecided = false; isReportingPlayer = false; reportingUrl = null; rangeController = null; } instance = { report: report, initialize: initialize, reset: reset }; return instance; } DVBReporting.__dashjs_factory_name = 'DVBReporting'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(DVBReporting); module.exports = exports['default']; },{"../../../../core/FactoryMaker":15,"../../utils/MetricSerialiser":98,"../../utils/RNG":99}],95:[function(require,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 _voDVBErrors = require('../vo/DVBErrors'); var _voDVBErrors2 = _interopRequireDefault(_voDVBErrors); var _coreEventsEvents = require('../../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _MediaPlayerEvents = require('../../MediaPlayerEvents'); var _MediaPlayerEvents2 = _interopRequireDefault(_MediaPlayerEvents); var _MetricsReportingEvents = require('../MetricsReportingEvents'); var _MetricsReportingEvents2 = _interopRequireDefault(_MetricsReportingEvents); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function DVBErrorsTranslator(config) { var instance = undefined; var eventBus = config.eventBus; var metricModel = config.metricsModel; var mpd = undefined; function report(vo) { var o = new _voDVBErrors2['default'](); if (!mpd) { return; } for (var key in vo) { if (vo.hasOwnProperty(key)) { o[key] = vo[key]; } } if (!o.mpdurl) { o.mpdurl = mpd.originalUrl || mpd.url; } if (!o.terror) { o.terror = new Date(); } metricModel.addDVBErrors(o); } function onManifestUpdate(e) { if (e.error) { return; } mpd = e.manifest; } function onServiceLocationChanged(e) { report({ errorcode: _voDVBErrors2['default'].BASE_URL_CHANGED, servicelocation: e.entry }); } function onBecameReporter() { report({ errorcode: _voDVBErrors2['default'].BECAME_REPORTER }); } function handleHttpMetric(vo) { if (vo.responsecode === 0 || // connection failure - unknown vo.responsecode >= 400 || // HTTP error status code vo.responsecode < 100 || // unknown status codes vo.responsecode >= 600) { // unknown status codes report({ errorcode: vo.responsecode || _voDVBErrors2['default'].CONNECTION_ERROR, url: vo.url, terror: vo.tresponse, servicelocation: vo._serviceLocation }); } } function onMetricEvent(e) { switch (e.metric) { case 'HttpList': handleHttpMetric(e.value); break; default: break; } } function onPlaybackError(e) { var reason = e.error ? e.error.code : 0; var errorcode; switch (reason) { case MediaError.MEDIA_ERR_NETWORK: errorcode = _voDVBErrors2['default'].CONNECTION_ERROR; break; case MediaError.MEDIA_ERR_DECODE: errorcode = _voDVBErrors2['default'].CORRUPT_MEDIA_OTHER; break; default: return; } report({ errorcode: errorcode }); } function initialise() { eventBus.on(_coreEventsEvents2['default'].MANIFEST_UPDATED, onManifestUpdate, instance); eventBus.on(_coreEventsEvents2['default'].SERVICE_LOCATION_BLACKLIST_CHANGED, onServiceLocationChanged, instance); eventBus.on(_MediaPlayerEvents2['default'].METRIC_ADDED, onMetricEvent, instance); eventBus.on(_MediaPlayerEvents2['default'].METRIC_UPDATED, onMetricEvent, instance); eventBus.on(_MediaPlayerEvents2['default'].PLAYBACK_ERROR, onPlaybackError, instance); eventBus.on(_MetricsReportingEvents2['default'].BECAME_REPORTING_PLAYER, onBecameReporter, instance); } function reset() { eventBus.off(_coreEventsEvents2['default'].MANIFEST_UPDATED, onManifestUpdate, instance); eventBus.off(_coreEventsEvents2['default'].SERVICE_LOCATION_BLACKLIST_CHANGED, onServiceLocationChanged, instance); eventBus.off(_MediaPlayerEvents2['default'].METRIC_ADDED, onMetricEvent, instance); eventBus.off(_MediaPlayerEvents2['default'].METRIC_UPDATED, onMetricEvent, instance); eventBus.off(_MediaPlayerEvents2['default'].PLAYBACK_ERROR, onPlaybackError, instance); eventBus.off(_MetricsReportingEvents2['default'].BECAME_REPORTING_PLAYER, onBecameReporter, instance); } instance = { initialise: initialise, reset: reset }; initialise(); return instance; } DVBErrorsTranslator.__dashjs_factory_name = 'DVBErrorsTranslator'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(DVBErrorsTranslator); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../../core/events/Events":18,"../../MediaPlayerEvents":58,"../MetricsReportingEvents":82,"../vo/DVBErrors":100}],96:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function HandlerHelpers() { return { reconstructFullMetricName: function reconstructFullMetricName(key, n, type) { var mn = key; if (n) { mn += '(' + n; if (type && type.length) { mn += ',' + type; } mn += ')'; } return mn; }, validateN: function validateN(n_ms) { if (!n_ms) { throw new Error('missing n'); } if (isNaN(n_ms)) { throw new Error('n is NaN'); } // n is a positive integer is defined to refer to the metric // in which the buffer level is recorded every n ms. if (n_ms < 0) { throw new Error('n must be positive'); } return n_ms; } }; } HandlerHelpers.__dashjs_factory_name = 'HandlerHelpers'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(HandlerHelpers); module.exports = exports['default']; },{"../../../core/FactoryMaker":15}],97:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _voMetrics = require('../vo/Metrics'); var _voMetrics2 = _interopRequireDefault(_voMetrics); var _voRange = require('../vo/Range'); var _voRange2 = _interopRequireDefault(_voRange); var _voReporting = require('../vo/Reporting'); var _voReporting2 = _interopRequireDefault(_voReporting); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function ManifestParsing(config) { var instance = undefined; var dashManifestModel = config.dashManifestModel; function getMetricsRangeStartTime(manifest, dynamic, range) { var mpd = dashManifestModel.getMpd(manifest); var periods; var presentationStartTime = 0; var reportingStartTime; if (dynamic) { // For services with MPD@type='dynamic', the start time is // indicated in wall clock time by adding the value of this // attribute to the value of the MPD@availabilityStartTime // attribute. presentationStartTime = mpd.availabilityStartTime.getTime() / 1000; } else { // For services with MPD@type='static', the start time is indicated // in Media Presentation time and is relative to the PeriodStart // time of the first Period in this MPD. periods = this.getRegularPeriods(manifest, mpd); if (periods.length) { presentationStartTime = periods[0].start; } } // When not present, DASH Metrics collection is // requested from the beginning of content // consumption. reportingStartTime = presentationStartTime; if (range && range.hasOwnProperty('starttime')) { reportingStartTime += range.starttime; } return reportingStartTime; } function getMetrics(manifest) { var metrics = []; if (manifest.Metrics_asArray) { manifest.Metrics_asArray.forEach(function (metric) { var metricEntry = new _voMetrics2['default'](); var isDynamic = dashManifestModel.getIsDynamic(manifest); if (metric.hasOwnProperty('metrics')) { metricEntry.metrics = metric.metrics; } else { //console.log("Invalid Metrics. metrics must be set. Ignoring."); return; } if (metric.Range_asArray) { metric.Range_asArray.forEach(function (range) { var rangeEntry = new _voRange2['default'](); rangeEntry.starttime = getMetricsRangeStartTime(manifest, isDynamic, range); if (range.hasOwnProperty('duration')) { rangeEntry.duration = range.duration; } else { // if not present, the value is identical to the // Media Presentation duration. rangeEntry.duration = dashManifestModel.getDuration(manifest); } rangeEntry._useWallClockTime = isDynamic; metricEntry.Range.push(rangeEntry); }); } if (metric.Reporting_asArray) { metric.Reporting_asArray.forEach(function (reporting) { var reportingEntry = new _voReporting2['default'](); if (reporting.hasOwnProperty('schemeIdUri')) { reportingEntry.schemeIdUri = reporting.schemeIdUri; } else { // Invalid Reporting. schemeIdUri must be set. Ignore. return; } for (var prop in reporting) { if (reporting.hasOwnProperty(prop)) { reportingEntry[prop] = reporting[prop]; } } metricEntry.Reporting.push(reportingEntry); }); } else { // Invalid Metrics. At least one reporting must be present. Ignore return; } metrics.push(metricEntry); }); } return metrics; } instance = { getMetrics: getMetrics }; return instance; } ManifestParsing.__dashjs_factory_name = 'ManifestParsing'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(ManifestParsing); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../vo/Metrics":101,"../vo/Range":102,"../vo/Reporting":103}],98:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function MetricSerialiser() { // For each entry in the top level list within the metric (in the case // of the DVBErrors metric each entry corresponds to an "error event" // described in clause 10.8.4) the Player shall: function serialise(metric) { var pairs = []; var obj = []; var key, value; // Take each (key, value) pair from the metric entry and create a // string consisting of the name of the key, followed by an equals // ('=') character, followed by the string representation of the // value. The string representation of the value is created based // on the type of the value following the instructions in Table 22. for (key in metric) { if (metric.hasOwnProperty(key) && key.indexOf('_') !== 0) { value = metric[key]; // we want to ensure that keys still end up in the report // even if there is no value if (value === undefined || value === null) { value = ''; } // DVB A168 10.12.4 Table 22 if (Array.isArray(value)) { // if trace or similar is null, do not include in output if (!value.length) { continue; } obj = []; value.forEach(function (v) { var isBuiltIn = Object.prototype.toString.call(v).slice(8, -1) !== 'Object'; obj.push(isBuiltIn ? v : serialise(v)); }); value = obj.map(encodeURIComponent).join(','); } else if (typeof value === 'string') { value = encodeURIComponent(value); } else if (value instanceof Date) { value = value.toISOString(); } else if (typeof value === 'number') { value = Math.round(value); } pairs.push(key + '=' + value); } } // Concatenate the strings created in the previous step with an // ampersand ('&') character between each one. return pairs.join('&'); } return { serialise: serialise }; } MetricSerialiser.__dashjs_factory_name = 'MetricSerialiser'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(MetricSerialiser); module.exports = exports['default']; },{"../../../core/FactoryMaker":15}],99:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function RNG() { // check whether secure random numbers are available. if not, revert to // using Math.random var crypto = window.crypto || window.msCrypto; // could just as easily use any other array type by changing line below var ArrayType = Uint32Array; var MAX_VALUE = Math.pow(2, ArrayType.BYTES_PER_ELEMENT * 8) - 1; // currently there is only one client for this code, and that only uses // a single random number per initialisation. may want to increase this // number if more consumers in the future var NUM_RANDOM_NUMBERS = 10; var randomNumbers = undefined, index = undefined, instance = undefined; function initialise() { if (crypto) { if (!randomNumbers) { randomNumbers = new ArrayType(NUM_RANDOM_NUMBERS); } crypto.getRandomValues(randomNumbers); index = 0; } } function rand(min, max) { var r; if (!min) { min = 0; } if (!max) { max = 1; } if (crypto) { if (index === randomNumbers.length) { initialise(); } r = randomNumbers[index] / MAX_VALUE; index += 1; } else { r = Math.random(); } return r * (max - min) + min; } instance = { random: rand }; initialise(); return instance; } RNG.__dashjs_factory_name = 'RNG'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(RNG); module.exports = exports['default']; },{"../../../core/FactoryMaker":15}],100:[function(require,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 DVBErrors = function DVBErrors() { _classCallCheck(this, DVBErrors); this.mpdurl = null; // String - Absolute URL from which the MPD was originally // retrieved (MPD updates will not change this value). this.errorcode = null; // String - The value of errorcode depends upon the type // of error being reported. For an error listed in the // ErrorType column below the value is as described in the // Value column. // // ErrorType Value // --------- ----- // HTTP error status code HTTP status code // Unknown HTTP status code HTTP status code // SSL connection failed "SSL" followed by SSL alert value // DNS resolution failed "C00" // Host unreachable "C01" // Connection refused "C02" // Connection error – Not otherwise specified "C03" // Corrupt media – ISO BMFF container cannot be parsed "M00" // Corrupt media – Not otherwise specified "M01" // Changing Base URL in use due to errors "F00" // Becoming an error reporting Player "S00" this.terror = null; // Real-Time - Date and time at which error occurred in UTC, // formatted as a combined date and time according to ISO 8601. this.url = null; // String - Absolute URL from which data was being requested // when this error occurred. If the error report is in relation // to corrupt media or changing BaseURL, this may be a null // string if the URL from which the media was obtained or // which led to the change of BaseURL is no longer known. this.ipaddress = null; // String - IP Address which the host name in "url" resolved to. // If the error report is in relation to corrupt media or // changing BaseURL, this may be a null string if the URL // from which the media was obtained or which led to the // change of BaseURL is no longer known. this.servicelocation = null; // String - The value of the serviceLocation field in the // BaseURL being used. In the event of this report indicating // a change of BaseURL this is the value from the BaseURL // being moved from. }; DVBErrors.SSL_CONNECTION_FAILED_PREFIX = 'SSL'; DVBErrors.DNS_RESOLUTION_FAILED = 'C00'; DVBErrors.HOST_UNREACHABLE = 'C01'; DVBErrors.CONNECTION_REFUSED = 'C02'; DVBErrors.CONNECTION_ERROR = 'C03'; DVBErrors.CORRUPT_MEDIA_ISOBMFF = 'M00'; DVBErrors.CORRUPT_MEDIA_OTHER = 'M01'; DVBErrors.BASE_URL_CHANGED = 'F00'; DVBErrors.BECAME_REPORTER = 'S00'; exports['default'] = DVBErrors; module.exports = exports['default']; },{}],101:[function(require,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 Metrics = function Metrics() { _classCallCheck(this, Metrics); this.metrics = ''; this.Range = []; this.Reporting = []; }; exports['default'] = Metrics; module.exports = exports['default']; },{}],102:[function(require,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 Range = function Range() { _classCallCheck(this, Range); // as defined in ISO23009-1 this.starttime = 0; this.duration = Infinity; // for internal use this._useWallClockTime = false; }; exports["default"] = Range; module.exports = exports["default"]; },{}],103:[function(require,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 Reporting = function Reporting() { _classCallCheck(this, Reporting); // Reporting is a DescriptorType and doesn't have any additional fields this.schemeIdUri = ''; this.value = ''; }; exports['default'] = Reporting; module.exports = exports['default']; },{}],104:[function(require,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 _dashModelsDashManifestModel = require('../../dash/models/DashManifestModel'); var _dashModelsDashManifestModel2 = _interopRequireDefault(_dashModelsDashManifestModel); var _utilsObjectUtils = require('../utils/ObjectUtils'); var _utilsObjectUtils2 = _interopRequireDefault(_utilsObjectUtils); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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, _dashModelsDashManifestModel2['default'])(context).getInstance(); var objectUtils = (0, _utilsObjectUtils2['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'] = _coreFactoryMaker2['default'].getClassFactory(BaseURLTreeModel); module.exports = exports['default']; },{"../../core/FactoryMaker":15,"../../dash/models/DashManifestModel":27,"../utils/ObjectUtils":161}],105:[function(require,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 _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _voFragmentRequest = require('../vo/FragmentRequest'); var _voFragmentRequest2 = _interopRequireDefault(_voFragmentRequest); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); 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, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['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(_coreEventsEvents2['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 === _voFragmentRequest2['default'].ACTION_COMPLETE && req1.action === req2.action; }; var isEqualMedia = function isEqualMedia(req1, req2) { return !isNaN(req1.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(requests) { var isLoaded = false; requests.some(function (req) { if (isEqualMedia(request, req) || isEqualInit(request, req) || isEqualComplete(request, req)) { isLoaded = true; return isLoaded; } }); return isLoaded; }; return 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 states = filter.state instanceof Array ? filter.state : [filter.state]; var filteredRequests = []; states.forEach(function (state) { var requests = getRequestsForState(state); filteredRequests = filteredRequests.concat(filterRequests(requests, filter)); }); return filteredRequests; } function removeExecutedRequestsBeforeTime(time) { executedRequests = executedRequests.filter(function (req) { return isNaN(req.startTime) || req.startTime >= time; }); } function abortRequests() { fragmentLoader.abort(); loadingRequests = []; } function executeRequest(request) { switch (request.action) { case _voFragmentRequest2['default'].ACTION_COMPLETE: executedRequests.push(request); addSchedulingInfoMetrics(request, FRAGMENT_MODEL_EXECUTED); eventBus.trigger(_coreEventsEvents2['default'].STREAM_COMPLETED, { request: request, fragmentModel: this }); break; case _voFragmentRequest2['default'].ACTION_DOWNLOAD: addSchedulingInfoMetrics(request, FRAGMENT_MODEL_LOADING); loadingRequests.push(request); loadCurrentFragment(request); break; default: log('Unknown request action.'); } } function loadCurrentFragment(request) { eventBus.trigger(_coreEventsEvents2['default'].FRAGMENT_LOADING_STARTED, { sender: instance, request: request }); fragmentLoader.load(request); } function getRequestForTime(arr, time, threshold) { // loop through the executed requests and pick the one for which the playback interval matches the given time var lastIdx = arr.length - 1; for (var i = lastIdx; i >= 0; i--) { var req = arr[i]; var start = req.startTime; var end = start + req.duration; threshold = threshold !== undefined ? 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) { // for time use a specific filtration function if (filter.hasOwnProperty('time')) { return [getRequestForTime(arr, filter.time, filter.threshold)]; } return arr.filter(function (request) { 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 = undefined; switch (state) { case FRAGMENT_MODEL_LOADING: requests = loadingRequests; break; case FRAGMENT_MODEL_EXECUTED: requests = executedRequests; break; default: requests = []; } return requests; } function addSchedulingInfoMetrics(request, state) { metricsModel.addSchedulingInfo(request.mediaType, new Date(), request.type, request.startTime, request.availabilityStartTime, request.duration, request.quality, request.range, state); metricsModel.addRequestsQueue(request.mediaType, loadingRequests, executedRequests); } function onLoadingCompleted(e) { if (e.sender !== fragmentLoader) return; loadingRequests.splice(loadingRequests.indexOf(e.request), 1); if (e.response && !e.error) { executedRequests.push(e.request); } addSchedulingInfoMetrics(e.request, e.error ? FRAGMENT_MODEL_FAILED : FRAGMENT_MODEL_EXECUTED); eventBus.trigger(_coreEventsEvents2['default'].FRAGMENT_LOADING_COMPLETED, { request: e.request, response: e.response, error: e.error, sender: this }); } function reset() { eventBus.off(_coreEventsEvents2['default'].LOADING_COMPLETED, onLoadingCompleted, this); if (fragmentLoader) { fragmentLoader.reset(); fragmentLoader = null; } executedRequests = []; loadingRequests = []; } 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 = _coreFactoryMaker2['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":13,"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../vo/FragmentRequest":169}],106:[function(require,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 _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function ManifestModel() { var context = this.context; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, manifest = undefined; function getValue() { return manifest; } function setValue(value) { manifest = value; if (value) { eventBus.trigger(_coreEventsEvents2['default'].MANIFEST_LOADED, { data: value }); } } instance = { getValue: getValue, setValue: setValue }; return instance; } ManifestModel.__dashjs_factory_name = 'ManifestModel'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(ManifestModel); module.exports = exports['default']; },{"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18}],107:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _voMetricsHTTPRequest = require('../vo/metrics/HTTPRequest'); 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 = 30; var BUFFER_PRUNING_INTERVAL = 30; var DEFAULT_MIN_BUFFER_TIME = 12; var DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH = 20; var BUFFER_TIME_AT_TOP_QUALITY = 30; 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 = 500; 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; var DEFAULT_XHR_WITH_CREDENTIALS = false; 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, xhrWithCredentials = undefined, fastSwitchEnabled = undefined; function setup() { var _retryAttempts, _retryIntervals; UTCTimingSources = []; useSuggestedPresentationDelay = false; useManifestDateHeaderTimeSource = true; scheduleWhilePaused = true; bufferOccupancyABREnabled = false; fastSwitchEnabled = 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 = NaN; 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; xhrWithCredentials = { 'default': DEFAULT_XHR_WITH_CREDENTIALS }; retryAttempts = (_retryAttempts = {}, _defineProperty(_retryAttempts, _voMetricsHTTPRequest.HTTPRequest.MPD_TYPE, MANIFEST_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequest.HTTPRequest.XLINK_EXPANSION_TYPE, XLINK_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE, FRAGMENT_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequest.HTTPRequest.INIT_SEGMENT_TYPE, FRAGMENT_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequest.HTTPRequest.BITSTREAM_SWITCHING_SEGMENT_TYPE, FRAGMENT_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequest.HTTPRequest.INDEX_SEGMENT_TYPE, FRAGMENT_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequest.HTTPRequest.OTHER_TYPE, FRAGMENT_RETRY_ATTEMPTS), _retryAttempts); retryIntervals = (_retryIntervals = {}, _defineProperty(_retryIntervals, _voMetricsHTTPRequest.HTTPRequest.MPD_TYPE, MANIFEST_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequest.HTTPRequest.XLINK_EXPANSION_TYPE, XLINK_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE, FRAGMENT_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequest.HTTPRequest.INIT_SEGMENT_TYPE, FRAGMENT_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequest.HTTPRequest.BITSTREAM_SWITCHING_SEGMENT_TYPE, FRAGMENT_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequest.HTTPRequest.INDEX_SEGMENT_TYPE, FRAGMENT_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequest.HTTPRequest.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 !isNaN(stableBufferTime) ? stableBufferTime : fastSwitchEnabled ? DEFAULT_MIN_BUFFER_TIME_FAST_SWITCH : DEFAULT_MIN_BUFFER_TIME; } 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[_voMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE] = value; } function setRetryAttemptsForType(type, value) { retryAttempts[type] = value; } function getFragmentRetryAttempts() { return retryAttempts[_voMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE]; } function getRetryAttemptsForType(type) { return retryAttempts[type]; } function setFragmentRetryInterval(value) { retryIntervals[_voMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE] = value; } function setRetryIntervalForType(type, value) { retryIntervals[type] = value; } function getFragmentRetryInterval() { return retryIntervals[_voMetricsHTTPRequest.HTTPRequest.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 setXHRWithCredentialsForType(type, value) { if (!type) { Object.keys(xhrWithCredentials).forEach(function (key) { setXHRWithCredentialsForType(key, value); }); } else { xhrWithCredentials[type] = !!value; } } function getXHRWithCredentialsForType(type) { var useCreds = xhrWithCredentials[type]; if (useCreds === undefined) { return xhrWithCredentials['default']; } return useCreds; } function getFastSwitchEnabled() { return fastSwitchEnabled; } function setFastSwitchEnabled(value) { fastSwitchEnabled = value; } 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, setXHRWithCredentialsForType: setXHRWithCredentialsForType, getXHRWithCredentialsForType: getXHRWithCredentialsForType, setFastSwitchEnabled: setFastSwitchEnabled, getFastSwitchEnabled: getFastSwitchEnabled, 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 = _coreFactoryMaker2['default'].getSingletonFactory(MediaPlayerModel); factory.DEFAULT_UTC_TIMING_SOURCE = DEFAULT_UTC_TIMING_SOURCE; exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15,"../vo/metrics/HTTPRequest":185}],108:[function(require,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 _voMetricsList = require('../vo/MetricsList'); var _voMetricsList2 = _interopRequireDefault(_voMetricsList); var _voMetricsTCPConnection = require('../vo/metrics/TCPConnection'); var _voMetricsTCPConnection2 = _interopRequireDefault(_voMetricsTCPConnection); var _voMetricsHTTPRequest = require('../vo/metrics/HTTPRequest'); var _voMetricsRepresentationSwitch = require('../vo/metrics/RepresentationSwitch'); var _voMetricsRepresentationSwitch2 = _interopRequireDefault(_voMetricsRepresentationSwitch); var _voMetricsBufferLevel = require('../vo/metrics/BufferLevel'); var _voMetricsBufferLevel2 = _interopRequireDefault(_voMetricsBufferLevel); var _voMetricsBufferState = require('../vo/metrics/BufferState'); var _voMetricsBufferState2 = _interopRequireDefault(_voMetricsBufferState); var _voMetricsDVRInfo = require('../vo/metrics/DVRInfo'); var _voMetricsDVRInfo2 = _interopRequireDefault(_voMetricsDVRInfo); var _voMetricsDroppedFrames = require('../vo/metrics/DroppedFrames'); var _voMetricsDroppedFrames2 = _interopRequireDefault(_voMetricsDroppedFrames); var _voMetricsManifestUpdate = require('../vo/metrics/ManifestUpdate'); var _voMetricsSchedulingInfo = require('../vo/metrics/SchedulingInfo'); var _voMetricsSchedulingInfo2 = _interopRequireDefault(_voMetricsSchedulingInfo); var _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _voMetricsRequestsQueue = require('../vo/metrics/RequestsQueue'); var _voMetricsRequestsQueue2 = _interopRequireDefault(_voMetricsRequestsQueue); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _voMetricsBolaState = require('../vo/metrics/BolaState'); var _voMetricsBolaState2 = _interopRequireDefault(_voMetricsBolaState); function MetricsModel() { var context = this.context; var eventBus = (0, _coreEventBus2['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(_coreEventsEvents2['default'].METRICS_CHANGED); } function metricChanged(mediaType) { eventBus.trigger(_coreEventsEvents2['default'].METRIC_CHANGED, { mediaType: mediaType }); metricsChanged(); } function metricUpdated(mediaType, metricType, vo) { eventBus.trigger(_coreEventsEvents2['default'].METRIC_UPDATED, { mediaType: mediaType, metric: metricType, value: vo }); metricChanged(mediaType); } function metricAdded(mediaType, metricType, vo) { eventBus.trigger(_coreEventsEvents2['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 _voMetricsList2['default'](); streamMetrics[type] = metrics; } return metrics; } function addTcpConnection(mediaType, tcpid, dest, topen, tclose, tconnect) { var vo = new _voMetricsTCPConnection2['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 _voMetricsHTTPRequest.HTTPRequestTrace(); 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 _voMetricsHTTPRequest.HTTPRequest(); // 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 _voMetricsRepresentationSwitch2['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 _voMetricsBufferLevel2['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 _voMetricsBufferState2['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 _voMetricsDVRInfo2['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 _voMetricsDroppedFrames2['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 _voMetricsSchedulingInfo2['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 _voMetricsRequestsQueue2['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 _voMetricsManifestUpdate.ManifestUpdate(); 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 _voMetricsManifestUpdate.ManifestUpdateStreamInfo(); 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 _voMetricsManifestUpdate.ManifestUpdateTrackInfo(); 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 _voMetricsBolaState2['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'] = _coreFactoryMaker2['default'].getSingletonFactory(MetricsModel); module.exports = exports['default']; },{"../../core/EventBus":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../vo/MetricsList":174,"../vo/metrics/BolaState":180,"../vo/metrics/BufferLevel":181,"../vo/metrics/BufferState":182,"../vo/metrics/DVRInfo":183,"../vo/metrics/DroppedFrames":184,"../vo/metrics/HTTPRequest":185,"../vo/metrics/ManifestUpdate":186,"../vo/metrics/RepresentationSwitch":188,"../vo/metrics/RequestsQueue":189,"../vo/metrics/SchedulingInfo":190,"../vo/metrics/TCPConnection":191}],109:[function(require,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 _voURIFragmentData = require('../vo/URIFragmentData'); var _voURIFragmentData2 = _interopRequireDefault(_voURIFragmentData); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function URIQueryAndFragmentModel() { var instance = undefined, URIFragmentDataVO = undefined, URIQueryData = undefined, isHTTPS = undefined; function initialize() { URIFragmentDataVO = new _voURIFragmentData2['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'] = _coreFactoryMaker2['default'].getSingletonFactory(URIQueryAndFragmentModel); module.exports = exports['default']; },{"../../core/FactoryMaker":15,"../vo/URIFragmentData":179}],110:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function VideoModel() { var instance = undefined, element = undefined, TTMLRenderingDiv = undefined, videoContainer = undefined, stalledStreams = undefined, previousPlaybackRate = undefined; function initialize() { stalledStreams = []; } function onPlaybackCanPlay() { element.playbackRate = previousPlaybackRate || 1; element.removeEventListener('canplay', onPlaybackCanPlay); } function setPlaybackRate(value) { if (!element) return; if (element.readyState <= 2 && value > 0) { // If media element hasn't loaded enough data to play yet, wait until it has element.addEventListener('canplay', onPlaybackCanPlay); } else { 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) { setPlaybackRate(previousPlaybackRate || 1); if (!element.paused) { event = document.createEvent('Event'); event.initEvent('playing', true, false); element.dispatchEvent(event); } } } function stallStream(type, isStalled) { if (isStalled) { addStalledStream(type); } else { removeStalledStream(type); } } function getPlaybackQuality() { var hasWebKit = 'webkitDroppedFrameCount' in element && 'webkitDecodedFrameCount' in element; var hasQuality = ('getVideoPlaybackQuality' in element); var result = null; if (hasQuality) { result = element.getVideoPlaybackQuality(); } else if (hasWebKit) { result = { droppedVideoFrames: element.webkitDroppedFrameCount, totalVideoFrames: element.webkitDroppedFrameCount + element.webkitDecodedFrameCount, 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'] = _coreFactoryMaker2['default'].getSingletonFactory(VideoModel); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],111:[function(require,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 _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 _externalsBase64 = require('../../../externals/base64'); var _externalsBase642 = _interopRequireDefault(_externalsBase64); var CommonEncryption = (function () { function CommonEncryption() { _classCallCheck(this, CommonEncryption); } _createClass(CommonEncryption, null, [{ key: 'findCencContentProtection', /** * Find and return the ContentProtection element in the given array * that indicates support for MPEG Common Encryption * * @param {Array} cpArray array of content protection elements * @returns {Object|null} the Common Encryption content protection element or * null if one was not found */ value: function findCencContentProtection(cpArray) { var retVal = null; for (var i = 0; i < cpArray.length; ++i) { var cp = cpArray[i]; if (cp.schemeIdUri.toLowerCase() === 'urn:mpeg:dash:mp4protection:2011' && cp.value.toLowerCase() === 'cenc') retVal = cp; } return retVal; } /** * Returns just the data portion of a single PSSH * * @param {ArrayBuffer} pssh - the PSSH * @return {ArrayBuffer} data portion of the PSSH */ }, { key: 'getPSSHData', value: function getPSSHData(pssh) { var offset = 8; // Box size and type fields var view = new DataView(pssh); // Read version var version = view.getUint8(offset); offset += 20; // Version (1), flags (3), system ID (16) if (version > 0) { offset += 4 + 16 * view.getUint32(offset); // Key ID count (4) and All key IDs (16*count) } offset += 4; // Data size return pssh.slice(offset); } /** * Returns the PSSH associated with the given key system from the concatenated * list of PSSH boxes in the given initData * * @param {KeySystem} keySystem the desired * key system * @param {ArrayBuffer} initData 'cenc' initialization data. Concatenated list of PSSH. * @returns {ArrayBuffer|null} The PSSH box data corresponding to the given key system, null if not found * or null if a valid association could not be found. */ }, { key: 'getPSSHForKeySystem', value: function getPSSHForKeySystem(keySystem, initData) { var psshList = CommonEncryption.parsePSSHList(initData); if (psshList.hasOwnProperty(keySystem.uuid.toLowerCase())) { return psshList[keySystem.uuid.toLowerCase()]; } return null; } /** * Parse a standard common encryption PSSH which contains a simple * base64-encoding of the init data * * @param {Object} cpData the ContentProtection element * @returns {ArrayBuffer|null} the init data or null if not found */ }, { key: 'parseInitDataFromContentProtection', value: function parseInitDataFromContentProtection(cpData) { if ('pssh' in cpData) { return _externalsBase642['default'].decodeArray(cpData.pssh.__text).buffer; } return null; } /** * Parses list of PSSH boxes into keysystem-specific PSSH data * * @param {ArrayBuffer} data - the concatenated list of PSSH boxes as provided by * CDM as initialization data when CommonEncryption content is detected * @returns {Object|Array} an object that has a property named according to each of * the detected key system UUIDs (e.g. 00000000-0000-0000-0000-0000000000) * and a ArrayBuffer (the entire PSSH box) as the property value */ }, { key: 'parsePSSHList', value: function parsePSSHList(data) { if (data === null) return []; var dv = new DataView(data); var done = false; var pssh = {}; // TODO: Need to check every data read for end of buffer var byteCursor = 0; while (!done) { var size, nextBox, version, systemID, psshDataSize; var boxStart = byteCursor; if (byteCursor >= dv.buffer.byteLength) break; /* Box size */ size = dv.getUint32(byteCursor); nextBox = byteCursor + size; byteCursor += 4; /* Verify PSSH */ if (dv.getUint32(byteCursor) !== 0x70737368) { byteCursor = nextBox; continue; } byteCursor += 4; /* Version must be 0 or 1 */ version = dv.getUint8(byteCursor); if (version !== 0 && version !== 1) { byteCursor = nextBox; continue; } byteCursor++; byteCursor += 3; /* skip flags */ // 16-byte UUID/SystemID systemID = ''; var i, val; for (i = 0; i < 4; i++) { val = dv.getUint8(byteCursor + i).toString(16); systemID += val.length === 1 ? '0' + val : val; } byteCursor += 4; systemID += '-'; for (i = 0; i < 2; i++) { val = dv.getUint8(byteCursor + i).toString(16); systemID += val.length === 1 ? '0' + val : val; } byteCursor += 2; systemID += '-'; for (i = 0; i < 2; i++) { val = dv.getUint8(byteCursor + i).toString(16); systemID += val.length === 1 ? '0' + val : val; } byteCursor += 2; systemID += '-'; for (i = 0; i < 2; i++) { val = dv.getUint8(byteCursor + i).toString(16); systemID += val.length === 1 ? '0' + val : val; } byteCursor += 2; systemID += '-'; for (i = 0; i < 6; i++) { val = dv.getUint8(byteCursor + i).toString(16); systemID += val.length === 1 ? '0' + val : val; } byteCursor += 6; systemID = systemID.toLowerCase(); /* PSSH Data Size */ psshDataSize = dv.getUint32(byteCursor); byteCursor += 4; /* PSSH Data */ pssh[systemID] = dv.buffer.slice(boxStart, nextBox); byteCursor = nextBox; } return pssh; } }]); return CommonEncryption; })(); exports['default'] = CommonEncryption; module.exports = exports['default']; },{"../../../externals/base64":8}],112:[function(require,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 _controllersProtectionController = require('./controllers/ProtectionController'); var _controllersProtectionController2 = _interopRequireDefault(_controllersProtectionController); var _controllersProtectionKeyController = require('./controllers/ProtectionKeyController'); var _controllersProtectionKeyController2 = _interopRequireDefault(_controllersProtectionKeyController); var _ProtectionEvents = require('./ProtectionEvents'); var _ProtectionEvents2 = _interopRequireDefault(_ProtectionEvents); var _modelsProtectionModel_21Jan2015 = require('./models/ProtectionModel_21Jan2015'); var _modelsProtectionModel_21Jan20152 = _interopRequireDefault(_modelsProtectionModel_21Jan2015); var _modelsProtectionModel_3Feb2014 = require('./models/ProtectionModel_3Feb2014'); var _modelsProtectionModel_3Feb20142 = _interopRequireDefault(_modelsProtectionModel_3Feb2014); var _modelsProtectionModel_01b = require('./models/ProtectionModel_01b'); var _modelsProtectionModel_01b2 = _interopRequireDefault(_modelsProtectionModel_01b); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var APIS_ProtectionModel_01b = [ // Un-prefixed as per spec { // Video Element generateKeyRequest: 'generateKeyRequest', addKey: 'addKey', cancelKeyRequest: 'cancelKeyRequest', // Events needkey: 'needkey', keyerror: 'keyerror', keyadded: 'keyadded', keymessage: 'keymessage' }, // Webkit-prefixed (early Chrome versions and Chrome with EME disabled in chrome://flags) { // Video Element generateKeyRequest: 'webkitGenerateKeyRequest', addKey: 'webkitAddKey', cancelKeyRequest: 'webkitCancelKeyRequest', // Events needkey: 'webkitneedkey', keyerror: 'webkitkeyerror', keyadded: 'webkitkeyadded', keymessage: 'webkitkeymessage' }]; var APIS_ProtectionModel_3Feb2014 = [ // Un-prefixed as per spec // Chrome 38-39 (and some earlier versions) with chrome://flags -- Enable Encrypted Media Extensions { // Video Element setMediaKeys: 'setMediaKeys', // MediaKeys MediaKeys: 'MediaKeys', // MediaKeySession release: 'close', // Events needkey: 'needkey', error: 'keyerror', message: 'keymessage', ready: 'keyadded', close: 'keyclose' }, // MS-prefixed (IE11, Windows 8.1) { // Video Element setMediaKeys: 'msSetMediaKeys', // MediaKeys MediaKeys: 'MSMediaKeys', // MediaKeySession release: 'close', // Events needkey: 'msneedkey', error: 'mskeyerror', message: 'mskeymessage', ready: 'mskeyadded', close: 'mskeyclose' }]; function Protection() { var instance = undefined; var context = this.context; /** * Create a ProtectionController and associated ProtectionModel for use with * a single piece of content. * * @param {Object} config * @return {ProtectionController} protection controller * */ function createProtectionSystem(config) { var controller = null; var protectionKeyController = (0, _controllersProtectionKeyController2['default'])(context).getInstance(); protectionKeyController.setConfig({ log: config.log }); protectionKeyController.initialize(); var protectionModel = getProtectionModel(config); if (!controller && protectionModel) { //TODO add ability to set external controller if still needed at all? controller = (0, _controllersProtectionController2['default'])(context).create({ protectionModel: protectionModel, protectionKeyController: protectionKeyController, adapter: config.adapter, eventBus: config.eventBus, log: config.log }); config.capabilities.setEncryptedMediaSupported(true); } return controller; } function getProtectionModel(config) { var log = config.log; var eventBus = config.eventBus; var videoElement = config.videoModel.getElement(); if (videoElement.onencrypted !== undefined && videoElement.mediaKeys !== undefined && navigator.requestMediaKeySystemAccess !== undefined && typeof navigator.requestMediaKeySystemAccess === 'function') { log('EME detected on this user agent! (ProtectionModel_21Jan2015)'); return (0, _modelsProtectionModel_21Jan20152['default'])(context).create({ log: log, eventBus: eventBus }); } else if (getAPI(videoElement, APIS_ProtectionModel_3Feb2014)) { log('EME detected on this user agent! (ProtectionModel_3Feb2014)'); return (0, _modelsProtectionModel_3Feb20142['default'])(context).create({ log: log, eventBus: eventBus, api: getAPI(videoElement, APIS_ProtectionModel_3Feb2014) }); } else if (getAPI(videoElement, APIS_ProtectionModel_01b)) { log('EME detected on this user agent! (ProtectionModel_01b)'); return (0, _modelsProtectionModel_01b2['default'])(context).create({ log: log, eventBus: eventBus, api: getAPI(videoElement, APIS_ProtectionModel_01b) }); } else { log('No supported version of EME detected on this user agent! - Attempts to play encrypted content will fail!'); return null; } } function getAPI(videoElement, apis) { for (var i = 0; i < apis.length; i++) { var api = apis[i]; // detect if api is supported by browser // check only first function in api -> should be fine if (typeof videoElement[api[Object.keys(api)[0]]] !== 'function') { continue; } return api; } return null; } instance = { createProtectionSystem: createProtectionSystem }; return instance; } Protection.__dashjs_factory_name = 'Protection'; var factory = _coreFactoryMaker2['default'].getClassFactory(Protection); factory.events = _ProtectionEvents2['default']; exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15,"./ProtectionEvents":113,"./controllers/ProtectionController":114,"./controllers/ProtectionKeyController":115,"./models/ProtectionModel_01b":119,"./models/ProtectionModel_21Jan2015":120,"./models/ProtectionModel_3Feb2014":121}],113:[function(require,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 _coreEventsEventsBase = require('../../core/events/EventsBase'); var _coreEventsEventsBase2 = _interopRequireDefault(_coreEventsEventsBase); /** * @class * */ var ProtectionEvents = (function (_EventsBase) { _inherits(ProtectionEvents, _EventsBase); /** * @description Public facing external events to be used when including protection package. * All public events will be aggregated into the MediaPlayerEvents Class and can be accessed * via MediaPlayer.events. public_ is the prefix that we use to move event names to MediaPlayerEvents. */ function ProtectionEvents() { _classCallCheck(this, ProtectionEvents); _get(Object.getPrototypeOf(ProtectionEvents.prototype), 'constructor', this).call(this); /** * Event ID for events delivered when the protection set receives * a key message from the CDM * * @ignore */ this.INTERNAL_KEY_MESSAGE = 'internalKeyMessage'; /** * Event ID for events delivered when a key system selection procedure * completes * @ignore */ this.INTERNAL_KEY_SYSTEM_SELECTED = 'internalKeySystemSelected'; /** * Event ID for events delivered when a new key has been added * * @constant * @deprecated The latest versions of the EME specification no longer * use this event. {@MediaPlayer.models.protectionModel.eventList.KEY_STATUSES_CHANGED} * is preferred. * @event ProtectionEvents#KEY_ADDED */ this.KEY_ADDED = 'public_keyAdded'; /** * Event ID for events delivered when an error is encountered by the CDM * while processing a license server response message * @event ProtectionEvents#KEY_ERROR */ this.KEY_ERROR = 'public_keyError'; /** * Event ID for events delivered when the protection set receives * a key message from the CDM * @event ProtectionEvents#KEY_MESSAGE */ this.KEY_MESSAGE = 'public_keyMessage'; /** * Event ID for events delivered when a key session close * process has completed * @event ProtectionEvents#KEY_SESSION_CLOSED */ this.KEY_SESSION_CLOSED = 'public_keySessionClosed'; /** * Event ID for events delivered when a new key sessions creation * process has completed * @event ProtectionEvents#KEY_SESSION_CREATED */ this.KEY_SESSION_CREATED = 'public_keySessionCreated'; /** * Event ID for events delivered when a key session removal * process has completed * @event ProtectionEvents#KEY_SESSION_REMOVED */ this.KEY_SESSION_REMOVED = 'public_keySessionRemoved'; /** * Event ID for events delivered when the status of one or more * decryption keys has changed * @event ProtectionEvents#KEY_STATUSES_CHANGED */ this.KEY_STATUSES_CHANGED = 'public_keyStatusesChanged'; /** * Event ID for events delivered when a key system access procedure * has completed * @ignore */ this.KEY_SYSTEM_ACCESS_COMPLETE = 'keySystemAccessComplete'; /** * Event ID for events delivered when a key system selection procedure * completes * @event ProtectionEvents#KEY_SYSTEM_SELECTED */ this.KEY_SYSTEM_SELECTED = 'public_keySystemSelected'; /** * Event ID for events delivered when a license request procedure * has completed * @event ProtectionEvents#LICENSE_REQUEST_COMPLETE */ this.LICENSE_REQUEST_COMPLETE = 'public_licenseRequestComplete'; /** * Event ID for needkey/encrypted events * @ignore */ this.NEED_KEY = 'needkey'; /** * Event ID for events delivered when the Protection system is detected and created. * @event ProtectionEvents#PROTECTION_CREATED */ this.PROTECTION_CREATED = 'public_protectioncreated'; /** * Event ID for events delivered when the Protection system is destroyed. * @event ProtectionEvents#PROTECTION_DESTROYED */ this.PROTECTION_DESTROYED = 'public_protectiondestroyed'; /** * Event ID for events delivered when a new server certificate has * been delivered to the CDM * @ignore */ this.SERVER_CERTIFICATE_UPDATED = 'serverCertificateUpdated'; /** * Event ID for events delivered when the process of shutting down * a protection set has completed * @ignore */ this.TEARDOWN_COMPLETE = 'protectionTeardownComplete'; /** * Event ID for events delivered when a HTMLMediaElement has been * associated with the protection set * @ignore */ this.VIDEO_ELEMENT_SELECTED = 'videoElementSelected'; } return ProtectionEvents; })(_coreEventsEventsBase2['default']); var protectionEvents = new ProtectionEvents(); exports['default'] = protectionEvents; module.exports = exports['default']; },{"../../core/events/EventsBase":19}],114:[function(require,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 _CommonEncryption = require('../CommonEncryption'); var _CommonEncryption2 = _interopRequireDefault(_CommonEncryption); var _coreEventsEvents = require('../../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _voMediaCapability = require('../vo/MediaCapability'); var _voMediaCapability2 = _interopRequireDefault(_voMediaCapability); var _voKeySystemConfiguration = require('../vo/KeySystemConfiguration'); var _voKeySystemConfiguration2 = _interopRequireDefault(_voKeySystemConfiguration); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _Protection = require('../Protection'); var _Protection2 = _interopRequireDefault(_Protection); /** * @module ProtectionController * @description Provides access to media protection information and functionality. Each * ProtectionController manages a single {@link MediaPlayer.models.ProtectionModel} * which encapsulates a set of protection information (EME APIs, selected key system, * key sessions). The APIs of ProtectionController mostly align with the latest EME * APIs. Key system selection is mostly automated when combined with app-overrideable * functionality provided in {@link ProtectionKeyController}. * @todo ProtectionController does almost all of its tasks automatically after init() is * called. Applications might want more control over this process and want to go through * each step manually (key system selection, session creation, session maintenance). * @param {Object} config */ function ProtectionController(config) { var protectionKeyController = config.protectionKeyController; var protectionModel = config.protectionModel; var adapter = config.adapter; var eventBus = config.eventBus; var log = config.log; var instance = undefined, keySystems = undefined, pendingNeedKeyData = undefined, audioInfo = undefined, videoInfo = undefined, protDataSet = undefined, initialized = undefined, sessionType = undefined, robustnessLevel = undefined, keySystem = undefined; function setup() { keySystems = protectionKeyController.getKeySystems(); pendingNeedKeyData = []; initialized = false; sessionType = 'temporary'; robustnessLevel = ''; _coreEventsEvents2['default'].extend(_Protection2['default'].events); } /** * Initialize this protection system with a given manifest and optional audio * and video stream information. * * @param {Object} manifest the json version of the manifest XML document for the * desired content. Applications can download their manifest using * {@link module:MediaPlayer#retrieveManifest} * @param {StreamInfo} [aInfo] audio stream information * @param {StreamInfo} [vInfo] video stream information * @memberof module:ProtectionController * @instance * @todo This API will change when we have better support for allowing applications * to select different adaptation sets for playback. Right now it is clunky for * applications to create {@link StreamInfo} with the right information, */ function initialize(manifest, aInfo, vInfo) { // TODO: We really need to do much more here... We need to be smarter about knowing // which adaptation sets for which we have initialized, including the default key ID // value from the ContentProtection elements so we know whether or not we still need to // select key systems and acquire keys. if (!initialized) { var streamInfo; if (!aInfo && !vInfo) { // Look for ContentProtection elements. InitData can be provided by either the // dash264drm:Pssh ContentProtection format or a DRM-specific format. streamInfo = adapter.getStreamsInfo(manifest)[0]; // TODO: Single period only for now. See TODO above } audioInfo = aInfo || (streamInfo ? adapter.getMediaInfoForType(manifest, streamInfo, 'audio') : null); videoInfo = vInfo || (streamInfo ? adapter.getMediaInfoForType(manifest, streamInfo, 'video') : null); var mediaInfo = videoInfo ? videoInfo : audioInfo; // We could have audio or video only // ContentProtection elements are specified at the AdaptationSet level, so the CP for audio // and video will be the same. Just use one valid MediaInfo object var supportedKS = protectionKeyController.getSupportedKeySystemsFromContentProtection(mediaInfo.contentProtection); if (supportedKS && supportedKS.length > 0) { selectKeySystem(supportedKS, true); } initialized = true; } } /** * Create a new key session associated with the given initialization data from * the MPD or from the PSSH box in the media * * @param {ArrayBuffer} initData the initialization data * @memberof module:ProtectionController * @instance * @fires ProtectionController#KeySessionCreated * @todo In older versions of the EME spec, there was a one-to-one relationship between * initialization data and key sessions. That is no longer true in the latest APIs. This * API will need to modified (and a new "generateRequest(keySession, initData)" API created) * to come up to speed with the latest EME standard */ function createKeySession(initData) { var initDataForKS = _CommonEncryption2['default'].getPSSHForKeySystem(keySystem, initData); if (initDataForKS) { // Check for duplicate initData var currentInitData = protectionModel.getAllInitData(); for (var i = 0; i < currentInitData.length; i++) { if (protectionKeyController.initDataEquals(initDataForKS, currentInitData[i])) { log('DRM: Ignoring initData because we have already seen it!'); return; } } try { protectionModel.createKeySession(initDataForKS, sessionType); } catch (error) { eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CREATED, { data: null, error: 'Error creating key session! ' + error.message }); } } else { eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CREATED, { data: null, error: 'Selected key system is ' + keySystem.systemString + '. needkey/encrypted event contains no initData corresponding to that key system!' }); } } /** * Loads a key session with the given session ID from persistent storage. This * essentially creates a new key session * * @param {string} sessionID * @memberof module:ProtectionController * @instance * @fires ProtectionController#KeySessionCreated */ function loadKeySession(sessionID) { protectionModel.loadKeySession(sessionID); } /** * Removes the given key session from persistent storage and closes the session * as if {@link ProtectionController#closeKeySession} * was called * * @param {SessionToken} sessionToken the session * token * @memberof module:ProtectionController * @instance * @fires ProtectionController#KeySessionRemoved * @fires ProtectionController#KeySessionClosed */ function removeKeySession(sessionToken) { protectionModel.removeKeySession(sessionToken); } /** * Closes the key session and releases all associated decryption keys. These * keys will no longer be available for decrypting media * * @param {SessionToken} sessionToken the session * token * @memberof module:ProtectionController * @instance * @fires ProtectionController#KeySessionClosed */ function closeKeySession(sessionToken) { protectionModel.closeKeySession(sessionToken); } /** * Sets a server certificate for use by the CDM when signing key messages * intended for a particular license server. This will fire * an error event if a key system has not yet been selected. * * @param {ArrayBuffer} serverCertificate a CDM-specific license server * certificate * @memberof module:ProtectionController * @instance * @fires ProtectionController#ServerCertificateUpdated */ function setServerCertificate(serverCertificate) { protectionModel.setServerCertificate(serverCertificate); } /** * Associate this protection system with the given HTMLMediaElement. This * causes the system to register for needkey/encrypted events from the given * element and provides a destination for setting of MediaKeys * * @param {HTMLMediaElement} element the media element to which the protection * system should be associated * @memberof module:ProtectionController * @instance */ function setMediaElement(element) { if (element) { protectionModel.setMediaElement(element); eventBus.on(_coreEventsEvents2['default'].NEED_KEY, onNeedKey, this); eventBus.on(_coreEventsEvents2['default'].INTERNAL_KEY_MESSAGE, onKeyMessage, this); } else if (element === null) { protectionModel.setMediaElement(element); eventBus.off(_coreEventsEvents2['default'].NEED_KEY, onNeedKey, this); eventBus.off(_coreEventsEvents2['default'].INTERNAL_KEY_MESSAGE, onKeyMessage, this); } } /** * Sets the session type to use when creating key sessions. Either "temporary" or * "persistent-license". Default is "temporary". * * @param {string} value the session type * @memberof module:ProtectionController * @instance */ function setSessionType(value) { sessionType = value; } /** * Sets the robustness level for video and audio capabilities. Optional to remove Chrome warnings. * Possible values are SW_SECURE_CRYPTO, SW_SECURE_DECODE, HW_SECURE_CRYPTO, HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL. * * @param {string} level the robustness level * @memberof module:ProtectionController * @instance */ function setRobustnessLevel(level) { robustnessLevel = level; } /** * Attach KeySystem-specific data to use for license acquisition with EME * * @param {Object} data an object containing property names corresponding to * key system name strings (e.g. "org.w3.clearkey") and associated values * being instances of {@link ProtectionData} * @memberof module:ProtectionController * @instance */ function setProtectionData(data) { protDataSet = data; } /** * Destroys all protection data associated with this protection set. This includes * deleting all key sessions. In the case of persistent key sessions, the sessions * will simply be unloaded and not deleted. Additionally, if this protection set is * associated with a HTMLMediaElement, it will be detached from that element. * * @memberof module:ProtectionController * @instance */ function reset() { setMediaElement(null); keySystem = undefined; //TODO-Refactor look at why undefined is needed for this. refactor if (protectionModel) { protectionModel.reset(); protectionModel = null; } } /////////////// // Private /////////////// function getProtData(keySystem) { var protData = null; var keySystemString = keySystem.systemString; if (protDataSet) { protData = keySystemString in protDataSet ? protDataSet[keySystemString] : null; } return protData; } function selectKeySystem(supportedKS, fromManifest) { var self = this; // Build our request object for requestKeySystemAccess var audioCapabilities = []; var videoCapabilities = []; if (videoInfo) { videoCapabilities.push(new _voMediaCapability2['default'](videoInfo.codec, robustnessLevel)); } if (audioInfo) { audioCapabilities.push(new _voMediaCapability2['default'](audioInfo.codec, robustnessLevel)); } var ksConfig = new _voKeySystemConfiguration2['default'](audioCapabilities, videoCapabilities, 'optional', sessionType === 'temporary' ? 'optional' : 'required', [sessionType]); var requestedKeySystems = []; var ksIdx; if (keySystem) { // We have a key system for (ksIdx = 0; ksIdx < supportedKS.length; ksIdx++) { if (keySystem === supportedKS[ksIdx].ks) { var _ret = (function () { requestedKeySystems.push({ ks: supportedKS[ksIdx].ks, configs: [ksConfig] }); // Ensure that we would be granted key system access using the key // system and codec information var onKeySystemAccessComplete = function onKeySystemAccessComplete(event) { eventBus.off(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, onKeySystemAccessComplete, self); if (event.error) { if (!fromManifest) { eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_SELECTED, { error: 'DRM: KeySystem Access Denied! -- ' + event.error }); } } else { log('DRM: KeySystem Access Granted'); eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_SELECTED, { data: event.data }); createKeySession(supportedKS[ksIdx].initData); } }; eventBus.on(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, onKeySystemAccessComplete, self); protectionModel.requestKeySystemAccess(requestedKeySystems); return 'break'; })(); if (_ret === 'break') break; } } } else if (keySystem === undefined) { // First time through, so we need to select a key system keySystem = null; pendingNeedKeyData.push(supportedKS); // Add all key systems to our request list since we have yet to select a key system for (var i = 0; i < supportedKS.length; i++) { requestedKeySystems.push({ ks: supportedKS[i].ks, configs: [ksConfig] }); } var keySystemAccess; var onKeySystemAccessComplete = function onKeySystemAccessComplete(event) { eventBus.off(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, onKeySystemAccessComplete, self); if (event.error) { keySystem = undefined; eventBus.off(_coreEventsEvents2['default'].INTERNAL_KEY_SYSTEM_SELECTED, onKeySystemSelected, self); if (!fromManifest) { eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_SELECTED, { data: null, error: 'DRM: KeySystem Access Denied! -- ' + event.error }); } } else { keySystemAccess = event.data; log('DRM: KeySystem Access Granted (' + keySystemAccess.keySystem.systemString + ')! Selecting key system...'); protectionModel.selectKeySystem(keySystemAccess); } }; var onKeySystemSelected = function onKeySystemSelected(event) { eventBus.off(_coreEventsEvents2['default'].INTERNAL_KEY_SYSTEM_SELECTED, onKeySystemSelected, self); eventBus.off(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, onKeySystemAccessComplete, self); if (!event.error) { keySystem = protectionModel.getKeySystem(); eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_SELECTED, { data: keySystemAccess }); for (var i = 0; i < pendingNeedKeyData.length; i++) { for (ksIdx = 0; ksIdx < pendingNeedKeyData[i].length; ksIdx++) { if (keySystem === pendingNeedKeyData[i][ksIdx].ks) { createKeySession(pendingNeedKeyData[i][ksIdx].initData); break; } } } } else { keySystem = undefined; if (!fromManifest) { eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_SELECTED, { data: null, error: 'DRM: Error selecting key system! -- ' + event.error }); } } }; eventBus.on(_coreEventsEvents2['default'].INTERNAL_KEY_SYSTEM_SELECTED, onKeySystemSelected, self); eventBus.on(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, onKeySystemAccessComplete, self); protectionModel.requestKeySystemAccess(requestedKeySystems); } else { // We are in the process of selecting a key system, so just save the data pendingNeedKeyData.push(supportedKS); } } function sendLicenseRequestCompleteEvent(data, error) { eventBus.trigger(_coreEventsEvents2['default'].LICENSE_REQUEST_COMPLETE, { data: data, error: error }); } function onKeyMessage(e) { log('DRM: onKeyMessage'); if (e.error) { log(e.error); return; } // Dispatch event to applications indicating we received a key message var keyMessage = e.data; eventBus.trigger(_coreEventsEvents2['default'].KEY_MESSAGE, { data: keyMessage }); var messageType = keyMessage.messageType ? keyMessage.messageType : 'license-request'; var message = keyMessage.message; var sessionToken = keyMessage.sessionToken; var protData = getProtData(keySystem); var keySystemString = keySystem.systemString; var licenseServerData = protectionKeyController.getLicenseServer(keySystem, protData, messageType); var eventData = { sessionToken: sessionToken, messageType: messageType }; // Message not destined for license server if (!licenseServerData) { log('DRM: License server request not required for this message (type = ' + e.data.messageType + '). Session ID = ' + sessionToken.getSessionID()); sendLicenseRequestCompleteEvent(eventData); return; } // Perform any special handling for ClearKey if (protectionKeyController.isClearKey(keySystem)) { var clearkeys = protectionKeyController.processClearKeyLicenseRequest(protData, message); if (clearkeys) { log('DRM: ClearKey license request handled by application!'); sendLicenseRequestCompleteEvent(eventData); protectionModel.updateKeySession(sessionToken, clearkeys); return; } } // All remaining key system scenarios require a request to a remote license server var xhr = new XMLHttpRequest(); // Determine license server URL var url = null; if (protData && protData.serverURL) { var serverURL = protData.serverURL; if (typeof serverURL === 'string' && serverURL !== '') { url = serverURL; } else if (typeof serverURL === 'object' && serverURL.hasOwnProperty(messageType)) { url = serverURL[messageType]; } } else if (protData && protData.laURL && protData.laURL !== '') { // TODO: Deprecated! url = protData.laURL; } else { url = keySystem.getLicenseServerURLFromInitData(_CommonEncryption2['default'].getPSSHData(sessionToken.initData)); if (!url) { url = e.data.laURL; } } // Possibly update or override the URL based on the message url = licenseServerData.getServerURLFromMessage(url, message, messageType); // Ensure valid license server URL if (!url) { sendLicenseRequestCompleteEvent(eventData, 'DRM: No license server URL specified!'); return; } xhr.open(licenseServerData.getHTTPMethod(messageType), url, true); xhr.responseType = licenseServerData.getResponseType(keySystemString, messageType); xhr.onload = function () { if (this.status == 200) { sendLicenseRequestCompleteEvent(eventData); protectionModel.updateKeySession(sessionToken, licenseServerData.getLicenseMessage(this.response, keySystemString, messageType)); } else { sendLicenseRequestCompleteEvent(eventData, 'DRM: ' + keySystemString + ' update, XHR status is "' + this.statusText + '" (' + this.status + '), expected to be 200. readyState is ' + this.readyState + '. Response is ' + (this.response ? licenseServerData.getErrorResponse(this.response, keySystemString, messageType) : 'NONE')); } }; xhr.onabort = function () { sendLicenseRequestCompleteEvent(eventData, 'DRM: ' + keySystemString + ' update, XHR aborted. status is "' + this.statusText + '" (' + this.status + '), readyState is ' + this.readyState); }; xhr.onerror = function () { sendLicenseRequestCompleteEvent(eventData, 'DRM: ' + keySystemString + ' update, XHR error. status is "' + this.statusText + '" (' + this.status + '), readyState is ' + this.readyState); }; // Set optional XMLHttpRequest headers from protection data and message var updateHeaders = function updateHeaders(headers) { var key; if (headers) { for (key in headers) { if ('authorization' === key.toLowerCase()) { xhr.withCredentials = true; } xhr.setRequestHeader(key, headers[key]); } } }; if (protData) { updateHeaders(protData.httpRequestHeaders); } updateHeaders(keySystem.getRequestHeadersFromMessage(message)); // Set withCredentials property from protData if (protData && protData.withCredentials) { xhr.withCredentials = true; } xhr.send(keySystem.getLicenseRequestFromMessage(message)); } function onNeedKey(event) { log('DRM: onNeedKey'); // Ignore non-cenc initData if (event.key.initDataType !== 'cenc') { log('DRM: Only \'cenc\' initData is supported! Ignoring initData of type: ' + event.key.initDataType); return; } // Some browsers return initData as Uint8Array (IE), some as ArrayBuffer (Chrome). // Convert to ArrayBuffer var abInitData = event.key.initData; if (ArrayBuffer.isView(abInitData)) { abInitData = abInitData.buffer; } log('DRM: initData:', String.fromCharCode.apply(null, new Uint8Array(abInitData))); var supportedKS = protectionKeyController.getSupportedKeySystems(abInitData, protDataSet); if (supportedKS.length === 0) { log('DRM: Received needkey event with initData, but we don\'t support any of the key systems!'); return; } selectKeySystem(supportedKS, false); } instance = { initialize: initialize, createKeySession: createKeySession, loadKeySession: loadKeySession, removeKeySession: removeKeySession, closeKeySession: closeKeySession, setServerCertificate: setServerCertificate, setMediaElement: setMediaElement, setSessionType: setSessionType, setRobustnessLevel: setRobustnessLevel, setProtectionData: setProtectionData, reset: reset }; setup(); return instance; } ProtectionController.__dashjs_factory_name = 'ProtectionController'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(ProtectionController); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../../core/events/Events":18,"../CommonEncryption":111,"../Protection":112,"../vo/KeySystemConfiguration":131,"../vo/MediaCapability":132}],115:[function(require,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 _CommonEncryption = require('./../CommonEncryption'); var _CommonEncryption2 = _interopRequireDefault(_CommonEncryption); var _drmKeySystemClearKey = require('./../drm/KeySystemClearKey'); var _drmKeySystemClearKey2 = _interopRequireDefault(_drmKeySystemClearKey); var _drmKeySystemWidevine = require('./../drm/KeySystemWidevine'); var _drmKeySystemWidevine2 = _interopRequireDefault(_drmKeySystemWidevine); var _drmKeySystemPlayReady = require('./../drm/KeySystemPlayReady'); var _drmKeySystemPlayReady2 = _interopRequireDefault(_drmKeySystemPlayReady); var _serversDRMToday = require('./../servers/DRMToday'); var _serversDRMToday2 = _interopRequireDefault(_serversDRMToday); var _serversPlayReady = require('./../servers/PlayReady'); var _serversPlayReady2 = _interopRequireDefault(_serversPlayReady); var _serversWidevine = require('./../servers/Widevine'); var _serversWidevine2 = _interopRequireDefault(_serversWidevine); var _serversClearKey = require('./../servers/ClearKey'); var _serversClearKey2 = _interopRequireDefault(_serversClearKey); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); /** * @module ProtectionKeyController * @description Media protection key system functionality that can be modified/overridden by applications */ function ProtectionKeyController() { var context = this.context; var instance = undefined, log = undefined, keySystems = undefined, clearkeyKeySystem = undefined; function setConfig(config) { if (!config) return; if (config.log) { log = config.log; } } function initialize() { keySystems = []; var keySystem; // PlayReady keySystem = (0, _drmKeySystemPlayReady2['default'])(context).getInstance(); keySystems.push(keySystem); // Widevine keySystem = (0, _drmKeySystemWidevine2['default'])(context).getInstance(); keySystems.push(keySystem); // ClearKey keySystem = (0, _drmKeySystemClearKey2['default'])(context).getInstance(); keySystems.push(keySystem); clearkeyKeySystem = keySystem; } /** * Returns a prioritized list of key systems supported * by this player (not necessarily those supported by the * user agent) * * @returns {Array.} a prioritized * list of key systems * @memberof module:ProtectionKeyController * @instance */ function getKeySystems() { return keySystems; } /** * Returns the key system associated with the given key system string * name (i.e. 'org.w3.clearkey') * * @param {string} systemString the system string * @returns {KeySystem|null} the key system * or null if no supported key system is associated with the given key * system string * @memberof module:ProtectionKeyController * @instance */ function getKeySystemBySystemString(systemString) { for (var i = 0; i < keySystems.length; i++) { if (keySystems[i].systemString === systemString) { return keySystems[i]; } } return null; } /** * Determines whether the given key system is ClearKey. This is * necessary because the EME spec defines ClearKey and its method * for providing keys to the key session; and this method has changed * between the various API versions. Our EME-specific ProtectionModels * must know if the system is ClearKey so that it can format the keys * according to the particular spec version. * * @param {Object} keySystem the key * @returns {boolean} true if this is the ClearKey key system, false * otherwise * @memberof module:ProtectionKeyController * @instance */ function isClearKey(keySystem) { return keySystem === clearkeyKeySystem; } /** * Check equality of initData array buffers. * * @param {ArrayBuffer} initData1 - first initData * @param {ArrayBuffer} initData2 - second initData * @returns {boolean} true if the initData arrays are equal in size and * contents, false otherwise * @memberof module:ProtectionKeyController * @instance */ function initDataEquals(initData1, initData2) { if (initData1.byteLength === initData2.byteLength) { var data1 = new Uint8Array(initData1); var data2 = new Uint8Array(initData2); for (var j = 0; j < data1.length; j++) { if (data1[j] !== data2[j]) { return false; } } return true; } return false; } /** * Returns a set of supported key systems and CENC initialization data * from the given array of ContentProtection elements. Only * key systems that are supported by this player will be returned. * Key systems are returned in priority order (highest first). * * @param {Array.} cps - array of content protection elements parsed * from the manifest * @returns {Array.} array of objects indicating which supported key * systems were found. Empty array is returned if no * supported key systems were found * @memberof module:ProtectionKeyController * @instance */ function getSupportedKeySystemsFromContentProtection(cps) { var cp, ks, ksIdx, cpIdx; var supportedKS = []; if (cps) { for (ksIdx = 0; ksIdx < keySystems.length; ++ksIdx) { ks = keySystems[ksIdx]; for (cpIdx = 0; cpIdx < cps.length; ++cpIdx) { cp = cps[cpIdx]; if (cp.schemeIdUri.toLowerCase() === ks.schemeIdURI) { // Look for DRM-specific ContentProtection var initData = ks.getInitData(cp); if (!!initData) { supportedKS.push({ ks: keySystems[ksIdx], initData: initData }); } } } } } return supportedKS; } /** * Returns key systems supported by this player for the given PSSH * initializationData. Only key systems supported by this player * that have protection data present will be returned. Key systems are returned in priority order * (highest priority first) * * @param {ArrayBuffer} initData Concatenated PSSH data for all DRMs * supported by the content * @param {ProtectionData} protDataSet user specified protection data - license server url etc * supported by the content * @returns {Array.} array of objects indicating which supported key * systems were found. Empty array is returned if no * supported key systems were found * @memberof module:ProtectionKeyController * @instance */ function getSupportedKeySystems(initData, protDataSet) { var ksIdx; var supportedKS = []; var pssh = _CommonEncryption2['default'].parsePSSHList(initData); for (ksIdx = 0; ksIdx < keySystems.length; ++ksIdx) { var keySystemString = keySystems[ksIdx].systemString; var shouldNotFilterOutKeySystem = protDataSet ? keySystemString in protDataSet : true; if (keySystems[ksIdx].uuid in pssh && shouldNotFilterOutKeySystem) { supportedKS.push({ ks: keySystems[ksIdx], initData: pssh[keySystems[ksIdx].uuid] }); } } return supportedKS; } /** * Returns the license server implementation data that should be used for this request. * * @param {KeySystem} keySystem the key system * associated with this license request * @param {ProtectionData} protData protection data to use for the * request * @param {string} [messageType="license-request"] the message type associated with this * request. Supported message types can be found * {@link https://w3c.github.io/encrypted-media/#idl-def-MediaKeyMessageType|here}. * @returns {LicenseServer|null} the license server * implementation that should be used for this request or null if the player should not * pass messages of the given type to a license server * @memberof module:ProtectionKeyController * @instance * */ function getLicenseServer(keySystem, protData, messageType) { // Our default server implementations do not do anything with "license-release" or // "individualization-request" messages, so we just send a success event if (messageType === 'license-release' || messageType === 'individualization-request') { return null; } var licenseServerData = null; if (protData && protData.hasOwnProperty('drmtoday')) { licenseServerData = (0, _serversDRMToday2['default'])(context).getInstance(); } else if (keySystem.systemString === 'com.widevine.alpha') { licenseServerData = (0, _serversWidevine2['default'])(context).getInstance(); } else if (keySystem.systemString === 'com.microsoft.playready') { licenseServerData = (0, _serversPlayReady2['default'])(context).getInstance(); } else if (keySystem.systemString === 'org.w3.clearkey') { licenseServerData = (0, _serversClearKey2['default'])(context).getInstance(); } return licenseServerData; } /** * Allows application-specific retrieval of ClearKey keys. * * @param {ProtectionData} protData protection data to use for the * request * @param {ArrayBuffer} message the key message from the CDM * @return {ClearKeyKeySet|null} the clear keys associated with * the request or null if no keys can be returned by this function * @memberof module:ProtectionKeyController * @instance */ function processClearKeyLicenseRequest(protData, message) { try { return clearkeyKeySystem.getClearKeysFromProtectionData(protData, message); } catch (error) { log('Failed to retrieve clearkeys from ProtectionData'); return null; } } instance = { initialize: initialize, isClearKey: isClearKey, initDataEquals: initDataEquals, getKeySystems: getKeySystems, getKeySystemBySystemString: getKeySystemBySystemString, getSupportedKeySystemsFromContentProtection: getSupportedKeySystemsFromContentProtection, getSupportedKeySystems: getSupportedKeySystems, getLicenseServer: getLicenseServer, processClearKeyLicenseRequest: processClearKeyLicenseRequest, setConfig: setConfig }; return instance; } ProtectionKeyController.__dashjs_factory_name = 'ProtectionKeyController'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(ProtectionKeyController); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"./../CommonEncryption":111,"./../drm/KeySystemClearKey":116,"./../drm/KeySystemPlayReady":117,"./../drm/KeySystemWidevine":118,"./../servers/ClearKey":122,"./../servers/DRMToday":123,"./../servers/PlayReady":124,"./../servers/Widevine":125}],116:[function(require,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 _voKeyPair = require('../vo/KeyPair'); var _voKeyPair2 = _interopRequireDefault(_voKeyPair); var _voClearKeyKeySet = require('../vo/ClearKeyKeySet'); var _voClearKeyKeySet2 = _interopRequireDefault(_voClearKeyKeySet); var _CommonEncryption = require('../CommonEncryption'); var _CommonEncryption2 = _interopRequireDefault(_CommonEncryption); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var uuid = '1077efec-c0b2-4d02-ace3-3c1e52e2fb4b'; var systemString = 'org.w3.clearkey'; var schemeIdURI = 'urn:uuid:' + uuid; function KeySystemClearKey() { var instance = undefined; /** * Returns desired clearkeys (as specified in the CDM message) from protection data * * @param {ProtectionData} protectionData the protection data * @param {ArrayBuffer} message the ClearKey CDM message * @returns {ClearKeyKeySet} the key set or null if none found * @throws {Error} if a keyID specified in the CDM message was not found in the * protection data * @memberof KeySystemClearKey */ function getClearKeysFromProtectionData(protectionData, message) { var clearkeySet = null; if (protectionData) { // ClearKey is the only system that does not require a license server URL, so we // handle it here when keys are specified in protection data var jsonMsg = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(message))); var keyPairs = []; for (var i = 0; i < jsonMsg.kids.length; i++) { var clearkeyID = jsonMsg.kids[i]; var clearkey = protectionData.clearkeys.hasOwnProperty(clearkeyID) ? protectionData.clearkeys[clearkeyID] : null; if (!clearkey) { throw new Error('DRM: ClearKey keyID (' + clearkeyID + ') is not known!'); } // KeyIDs from CDM are not base64 padded. Keys may or may not be padded keyPairs.push(new _voKeyPair2['default'](clearkeyID, clearkey)); } clearkeySet = new _voClearKeyKeySet2['default'](keyPairs); } return clearkeySet; } function getInitData(cp) { return _CommonEncryption2['default'].parseInitDataFromContentProtection(cp); } function getRequestHeadersFromMessage() /*message*/{ return null; } function getLicenseRequestFromMessage(message) { return new Uint8Array(message); } function getLicenseServerURLFromInitData() /*initData*/{ return null; } instance = { uuid: uuid, schemeIdURI: schemeIdURI, systemString: systemString, getInitData: getInitData, getRequestHeadersFromMessage: getRequestHeadersFromMessage, getLicenseRequestFromMessage: getLicenseRequestFromMessage, getLicenseServerURLFromInitData: getLicenseServerURLFromInitData, getClearKeysFromProtectionData: getClearKeysFromProtectionData }; return instance; } KeySystemClearKey.__dashjs_factory_name = 'KeySystemClearKey'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(KeySystemClearKey); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../CommonEncryption":111,"../vo/ClearKeyKeySet":126,"../vo/KeyPair":129}],117:[function(require,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. */ /** * Microsoft PlayReady DRM * * @class * @implements KeySystem */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _CommonEncryption = require('../CommonEncryption'); var _CommonEncryption2 = _interopRequireDefault(_CommonEncryption); var _voError = require('../../vo/Error'); var _voError2 = _interopRequireDefault(_voError); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _externalsBase64 = require('../../../../externals/base64'); var _externalsBase642 = _interopRequireDefault(_externalsBase64); var uuid = '9a04f079-9840-4286-ab92-e65be0885f95'; var systemString = 'com.microsoft.playready'; var schemeIdURI = 'urn:uuid:' + uuid; function KeySystemPlayReady() { var instance = undefined; var messageFormat = 'utf16'; function getRequestHeadersFromMessage(message) { var msg, xmlDoc; var headers = {}; var parser = new DOMParser(); var dataview = messageFormat === 'utf16' ? new Uint16Array(message) : new Uint8Array(message); msg = String.fromCharCode.apply(null, dataview); xmlDoc = parser.parseFromString(msg, 'application/xml'); var headerNameList = xmlDoc.getElementsByTagName('name'); var headerValueList = xmlDoc.getElementsByTagName('value'); for (var i = 0; i < headerNameList.length; i++) { headers[headerNameList[i].childNodes[0].nodeValue] = headerValueList[i].childNodes[0].nodeValue; } // some versions of the PlayReady CDM return 'Content' instead of 'Content-Type'. // this is NOT w3c conform and license servers may reject the request! // -> rename it to proper w3c definition! if (headers.hasOwnProperty('Content')) { headers['Content-Type'] = headers.Content; delete headers.Content; } return headers; } function getLicenseRequestFromMessage(message) { var msg, xmlDoc; var licenseRequest = null; var parser = new DOMParser(); var dataview = messageFormat === 'utf16' ? new Uint16Array(message) : new Uint8Array(message); msg = String.fromCharCode.apply(null, dataview); xmlDoc = parser.parseFromString(msg, 'application/xml'); if (xmlDoc.getElementsByTagName('Challenge')[0]) { var Challenge = xmlDoc.getElementsByTagName('Challenge')[0].childNodes[0].nodeValue; if (Challenge) { licenseRequest = _externalsBase642['default'].decode(Challenge); } } return licenseRequest; } function getLicenseServerURLFromInitData(initData) { if (initData) { var data = new DataView(initData); var numRecords = data.getUint16(4, true); var offset = 6; var parser = new DOMParser(); for (var i = 0; i < numRecords; i++) { // Parse the PlayReady Record header var recordType = data.getUint16(offset, true); offset += 2; var recordLength = data.getUint16(offset, true); offset += 2; if (recordType !== 0x0001) { offset += recordLength; continue; } var recordData = initData.slice(offset, offset + recordLength); var record = String.fromCharCode.apply(null, new Uint16Array(recordData)); var xmlDoc = parser.parseFromString(record, 'application/xml'); // First try if (xmlDoc.getElementsByTagName('LA_URL')[0]) { var laurl = xmlDoc.getElementsByTagName('LA_URL')[0].childNodes[0].nodeValue; if (laurl) { return laurl; } } // Optionally, try if (xmlDoc.getElementsByTagName('LUI_URL')[0]) { var luiurl = xmlDoc.getElementsByTagName('LUI_URL')[0].childNodes[0].nodeValue; if (luiurl) { return luiurl; } } } } return null; } function getInitData(cpData) { // * desc@ getInitData // * generate PSSH data from PROHeader defined in MPD file // * PSSH format: // * size (4) // * box type(PSSH) (8) // * Protection SystemID (16) // * protection system data size (4) - length of decoded PROHeader // * decoded PROHeader data from MPD file var PSSHBoxType = new Uint8Array([0x70, 0x73, 0x73, 0x68, 0x00, 0x00, 0x00, 0x00]); //'PSSH' 8 bytes var playreadySystemID = new Uint8Array([0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95]); var byteCursor = 0; var uint8arraydecodedPROHeader = null; var PROSize, PSSHSize, PSSHBoxBuffer, PSSHBox, PSSHData; // Handle common encryption PSSH if ('pssh' in cpData) { return _CommonEncryption2['default'].parseInitDataFromContentProtection(cpData); } // Handle native MS PlayReady ContentProtection elements if ('pro' in cpData) { uint8arraydecodedPROHeader = _externalsBase642['default'].decodeArray(cpData.pro.__text); } else if ('prheader' in cpData) { uint8arraydecodedPROHeader = _externalsBase642['default'].decodeArray(cpData.prheader.__text); } else { return null; } PROSize = uint8arraydecodedPROHeader.length; PSSHSize = 0x4 + PSSHBoxType.length + playreadySystemID.length + 0x4 + PROSize; PSSHBoxBuffer = new ArrayBuffer(PSSHSize); PSSHBox = new Uint8Array(PSSHBoxBuffer); PSSHData = new DataView(PSSHBoxBuffer); PSSHData.setUint32(byteCursor, PSSHSize); byteCursor += 0x4; PSSHBox.set(PSSHBoxType, byteCursor); byteCursor += PSSHBoxType.length; PSSHBox.set(playreadySystemID, byteCursor); byteCursor += playreadySystemID.length; PSSHData.setUint32(byteCursor, PROSize); byteCursor += 0x4; PSSHBox.set(uint8arraydecodedPROHeader, byteCursor); byteCursor += PROSize; return PSSHBox.buffer; } /** * It seems that some PlayReady implementations return their XML-based CDM * messages using UTF16, while others return them as UTF8. Use this function * to modify the message format to expect when parsing CDM messages. * * @param {string} format the expected message format. Either "utf8" or "utf16". * @throws {Error} Specified message format is not one of "utf8" or "utf16" */ function setPlayReadyMessageFormat(format) { if (format !== 'utf8' && format !== 'utf16') { throw new _voError2['default']('Illegal PlayReady message format! -- ' + format); } messageFormat = format; } instance = { uuid: uuid, schemeIdURI: schemeIdURI, systemString: systemString, getInitData: getInitData, getRequestHeadersFromMessage: getRequestHeadersFromMessage, getLicenseRequestFromMessage: getLicenseRequestFromMessage, getLicenseServerURLFromInitData: getLicenseServerURLFromInitData, setPlayReadyMessageFormat: setPlayReadyMessageFormat }; return instance; } KeySystemPlayReady.__dashjs_factory_name = 'KeySystemPlayReady'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(KeySystemPlayReady); module.exports = exports['default']; },{"../../../../externals/base64":8,"../../../core/FactoryMaker":15,"../../vo/Error":168,"../CommonEncryption":111}],118:[function(require,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. */ /** * Google Widevine DRM * * @class * @implements MediaPlayer.dependencies.protection.KeySystem */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _CommonEncryption = require('../CommonEncryption'); var _CommonEncryption2 = _interopRequireDefault(_CommonEncryption); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var uuid = 'edef8ba9-79d6-4ace-a3c8-27dcd51d21ed'; var systemString = 'com.widevine.alpha'; var schemeIdURI = 'urn:uuid:' + uuid; function KeySystemWidevine() { var instance = undefined; function getInitData(cp) { return _CommonEncryption2['default'].parseInitDataFromContentProtection(cp); } function getRequestHeadersFromMessage() /*message*/{ return null; } function getLicenseRequestFromMessage(message) { return new Uint8Array(message); } function getLicenseServerURLFromInitData() /*initData*/{ return null; } instance = { uuid: uuid, schemeIdURI: schemeIdURI, systemString: systemString, getInitData: getInitData, getRequestHeadersFromMessage: getRequestHeadersFromMessage, getLicenseRequestFromMessage: getLicenseRequestFromMessage, getLicenseServerURLFromInitData: getLicenseServerURLFromInitData }; return instance; } KeySystemWidevine.__dashjs_factory_name = 'KeySystemWidevine'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(KeySystemWidevine); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../CommonEncryption":111}],119:[function(require,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. */ /** * Initial implementation of EME * * Implemented by Google Chrome prior to v36 * * @implements ProtectionModel * @class */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _controllersProtectionKeyController = require('../controllers/ProtectionKeyController'); var _controllersProtectionKeyController2 = _interopRequireDefault(_controllersProtectionKeyController); var _voNeedKey = require('../vo/NeedKey'); var _voNeedKey2 = _interopRequireDefault(_voNeedKey); var _voKeyError = require('../vo/KeyError'); var _voKeyError2 = _interopRequireDefault(_voKeyError); var _voKeyMessage = require('../vo/KeyMessage'); var _voKeyMessage2 = _interopRequireDefault(_voKeyMessage); var _voKeySystemConfiguration = require('../vo/KeySystemConfiguration'); var _voKeySystemConfiguration2 = _interopRequireDefault(_voKeySystemConfiguration); var _voKeySystemAccess = require('../vo/KeySystemAccess'); var _voKeySystemAccess2 = _interopRequireDefault(_voKeySystemAccess); var _coreEventsEvents = require('../../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _utilsErrorHandler = require('../../utils/ErrorHandler'); var _utilsErrorHandler2 = _interopRequireDefault(_utilsErrorHandler); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function ProtectionModel_01b(config) { var context = this.context; var eventBus = config.eventBus; //Need to pass in here so we can use same instance since this is optional module var log = config.log; var api = config.api; var instance = undefined, videoElement = undefined, keySystem = undefined, protectionKeyController = undefined, errHandler = undefined, // With this version of the EME APIs, sessionIDs are not assigned to // sessions until the first key message is received. We are assuming // that in the case of multiple sessions, key messages will be received // in the order that generateKeyRequest() is called. // Holding spot for newly-created sessions until we determine whether or // not the CDM supports sessionIDs pendingSessions = undefined, // List of sessions that have been initialized. Only the first position will // be used in the case that the CDM does not support sessionIDs sessions = undefined, // Not all CDMs support the notion of sessionIDs. Without sessionIDs // there is no way for us to differentiate between sessions, therefore // we must only allow a single session. Once we receive the first key // message we can set this flag to determine if more sessions are allowed moreSessionsAllowed = undefined, // This is our main event handler for all desired HTMLMediaElement events // related to EME. These events are translated into our API-independent // versions of the same events eventHandler = undefined; function setup() { videoElement = null; keySystem = null; pendingSessions = []; sessions = []; protectionKeyController = (0, _controllersProtectionKeyController2['default'])(context).getInstance(); errHandler = (0, _utilsErrorHandler2['default'])(context).getInstance(); eventHandler = createEventHandler(); } function reset() { if (videoElement) { removeEventListeners(); } for (var i = 0; i < sessions.length; i++) { closeKeySession(sessions[i]); } eventBus.trigger(_coreEventsEvents2['default'].TEARDOWN_COMPLETE); } function getKeySystem() { return keySystem; } function getAllInitData() { var retVal = []; for (var i = 0; i < pendingSessions.length; i++) { retVal.push(pendingSessions[i].initData); } for (var i = 0; i < sessions.length; i++) { retVal.push(sessions[i].initData); } return retVal; } function requestKeySystemAccess(ksConfigurations) { var ve = videoElement; if (!ve) { // Must have a video element to do this capability tests ve = document.createElement('video'); } // Try key systems in order, first one with supported key system configuration // is used var found = false; for (var ksIdx = 0; ksIdx < ksConfigurations.length; ksIdx++) { var systemString = ksConfigurations[ksIdx].ks.systemString; var configs = ksConfigurations[ksIdx].configs; var supportedAudio = null; var supportedVideo = null; // Try key system configs in order, first one with supported audio/video // is used for (var configIdx = 0; configIdx < configs.length; configIdx++) { //var audios = configs[configIdx].audioCapabilities; var videos = configs[configIdx].videoCapabilities; // Look for supported video container/codecs if (videos && videos.length !== 0) { supportedVideo = []; // Indicates that we have a requested video config for (var videoIdx = 0; videoIdx < videos.length; videoIdx++) { if (ve.canPlayType(videos[videoIdx].contentType, systemString) !== '') { supportedVideo.push(videos[videoIdx]); } } } // No supported audio or video in this configuration OR we have // requested audio or video configuration that is not supported if (!supportedAudio && !supportedVideo || supportedAudio && supportedAudio.length === 0 || supportedVideo && supportedVideo.length === 0) { continue; } // This configuration is supported found = true; var ksConfig = new _voKeySystemConfiguration2['default'](supportedAudio, supportedVideo); var ks = protectionKeyController.getKeySystemBySystemString(systemString); eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, { data: new _voKeySystemAccess2['default'](ks, ksConfig) }); break; } } if (!found) { eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, { error: 'Key system access denied! -- No valid audio/video content configurations detected!' }); } } function selectKeySystem(keySystemAccess) { keySystem = keySystemAccess.keySystem; eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_KEY_SYSTEM_SELECTED); } function setMediaElement(mediaElement) { if (videoElement === mediaElement) { return; } // Replacing the previous element if (videoElement) { removeEventListeners(); } videoElement = mediaElement; // Only if we are not detaching from the existing element if (videoElement) { videoElement.addEventListener(api.keyerror, eventHandler); videoElement.addEventListener(api.needkey, eventHandler); videoElement.addEventListener(api.keymessage, eventHandler); videoElement.addEventListener(api.keyadded, eventHandler); eventBus.trigger(_coreEventsEvents2['default'].VIDEO_ELEMENT_SELECTED); } } function createKeySession(initData /*, keySystemType */) { if (!keySystem) { throw new Error('Can not create sessions until you have selected a key system'); } // Determine if creating a new session is allowed if (moreSessionsAllowed || sessions.length === 0) { var newSession = { // Implements SessionToken sessionID: null, initData: initData, getSessionID: function getSessionID() { return this.sessionID; }, getExpirationTime: function getExpirationTime() { return NaN; }, getSessionType: function getSessionType() { return 'temporary'; } }; pendingSessions.push(newSession); // Send our request to the CDM videoElement[api.generateKeyRequest](keySystem.systemString, new Uint8Array(initData)); return newSession; } else { throw new Error('Multiple sessions not allowed!'); } } function updateKeySession(sessionToken, message) { var sessionID = sessionToken.sessionID; if (!protectionKeyController.isClearKey(keySystem)) { // Send our request to the CDM videoElement[api.addKey](keySystem.systemString, new Uint8Array(message), sessionToken.initData, sessionID); } else { // For clearkey, message is a ClearKeyKeySet for (var i = 0; i < message.keyPairs.length; i++) { videoElement[api.addKey](keySystem.systemString, message.keyPairs[i].key, message.keyPairs[i].keyID, sessionID); } } } function closeKeySession(sessionToken) { // Send our request to the CDM videoElement[api.cancelKeyRequest](keySystem.systemString, sessionToken.sessionID); } function setServerCertificate() /*serverCertificate*/{/* Not supported */} function loadKeySession() /*sessionID*/{/* Not supported */} function removeKeySession() /*sessionToken*/{/* Not supported */} function createEventHandler() { return { handleEvent: function handleEvent(event) { var sessionToken = null; switch (event.type) { case api.needkey: var initData = ArrayBuffer.isView(event.initData) ? event.initData.buffer : event.initData; eventBus.trigger(_coreEventsEvents2['default'].NEED_KEY, { key: new _voNeedKey2['default'](initData, 'cenc') }); break; case api.keyerror: sessionToken = findSessionByID(sessions, event.sessionId); if (!sessionToken) { sessionToken = findSessionByID(pendingSessions, event.sessionId); } if (sessionToken) { var msg = ''; switch (event.errorCode.code) { case 1: msg += 'MEDIA_KEYERR_UNKNOWN - An unspecified error occurred. This value is used for errors that don\'t match any of the other codes.'; break; case 2: msg += 'MEDIA_KEYERR_CLIENT - The Key System could not be installed or updated.'; break; case 3: msg += 'MEDIA_KEYERR_SERVICE - The message passed into update indicated an error from the license service.'; break; case 4: msg += 'MEDIA_KEYERR_OUTPUT - There is no available output device with the required characteristics for the content protection system.'; break; case 5: msg += 'MEDIA_KEYERR_HARDWARECHANGE - A hardware configuration change caused a content protection error.'; break; case 6: msg += 'MEDIA_KEYERR_DOMAIN - An error occurred in a multi-device domain licensing configuration. The most common error is a failure to join the domain.'; break; } msg += ' System Code = ' + event.systemCode; // TODO: Build error string based on key error eventBus.trigger(_coreEventsEvents2['default'].KEY_ERROR, { data: new _voKeyError2['default'](sessionToken, msg) }); } else { log('No session token found for key error'); } break; case api.keyadded: sessionToken = findSessionByID(sessions, event.sessionId); if (!sessionToken) { sessionToken = findSessionByID(pendingSessions, event.sessionId); } if (sessionToken) { log('DRM: Key added.'); eventBus.trigger(_coreEventsEvents2['default'].KEY_ADDED, { data: sessionToken }); //TODO not sure anything is using sessionToken? why there? } else { log('No session token found for key added'); } break; case api.keymessage: // If this CDM does not support session IDs, we will be limited // to a single session moreSessionsAllowed = event.sessionId !== null && event.sessionId !== undefined; // SessionIDs supported if (moreSessionsAllowed) { // Attempt to find an uninitialized token with this sessionID sessionToken = findSessionByID(sessions, event.sessionId); if (!sessionToken && pendingSessions.length > 0) { // This is the first message for our latest session, so set the // sessionID and add it to our list sessionToken = pendingSessions.shift(); sessions.push(sessionToken); sessionToken.sessionID = event.sessionId; } } else if (pendingSessions.length > 0) { // SessionIDs not supported sessionToken = pendingSessions.shift(); sessions.push(sessionToken); if (pendingSessions.length !== 0) { errHandler.mediaKeyMessageError('Multiple key sessions were creates with a user-agent that does not support sessionIDs!! Unpredictable behavior ahead!'); } } if (sessionToken) { var message = ArrayBuffer.isView(event.message) ? event.message.buffer : event.message; // For ClearKey, the spec mandates that you pass this message to the // addKey method, so we always save it to the token since there is no // way to tell which key system is in use sessionToken.keyMessage = message; eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_KEY_MESSAGE, { data: new _voKeyMessage2['default'](sessionToken, message, event.defaultURL) }); } else { log('No session token found for key message'); } break; } } }; } /** * Helper function to retrieve the stored session token based on a given * sessionID value * * @param {Array} sessionArray - the array of sessions to search * @param {*} sessionID - the sessionID to search for * @returns {*} the session token with the given sessionID */ function findSessionByID(sessionArray, sessionID) { if (!sessionID || !sessionArray) { return null; } else { var len = sessionArray.length; for (var i = 0; i < len; i++) { if (sessionArray[i].sessionID == sessionID) { return sessionArray[i]; } } return null; } } function removeEventListeners() { videoElement.removeEventListener(api.keyerror, eventHandler); videoElement.removeEventListener(api.needkey, eventHandler); videoElement.removeEventListener(api.keymessage, eventHandler); videoElement.removeEventListener(api.keyadded, eventHandler); } instance = { getAllInitData: getAllInitData, requestKeySystemAccess: requestKeySystemAccess, getKeySystem: getKeySystem, selectKeySystem: selectKeySystem, setMediaElement: setMediaElement, createKeySession: createKeySession, updateKeySession: updateKeySession, closeKeySession: closeKeySession, setServerCertificate: setServerCertificate, loadKeySession: loadKeySession, removeKeySession: removeKeySession, reset: reset }; setup(); return instance; } ProtectionModel_01b.__dashjs_factory_name = 'ProtectionModel_01b'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(ProtectionModel_01b); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../../core/events/Events":18,"../../utils/ErrorHandler":157,"../controllers/ProtectionKeyController":115,"../vo/KeyError":127,"../vo/KeyMessage":128,"../vo/KeySystemAccess":130,"../vo/KeySystemConfiguration":131,"../vo/NeedKey":133}],120:[function(require,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. */ /** * Most recent EME implementation * * Implemented by Google Chrome v36+ (Windows, OSX, Linux) * * @implements ProtectionModel * @class */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _controllersProtectionKeyController = require('../controllers/ProtectionKeyController'); var _controllersProtectionKeyController2 = _interopRequireDefault(_controllersProtectionKeyController); var _voNeedKey = require('../vo/NeedKey'); var _voNeedKey2 = _interopRequireDefault(_voNeedKey); var _voKeyError = require('../vo/KeyError'); var _voKeyError2 = _interopRequireDefault(_voKeyError); var _voKeyMessage = require('../vo/KeyMessage'); var _voKeyMessage2 = _interopRequireDefault(_voKeyMessage); var _voKeySystemAccess = require('../vo/KeySystemAccess'); var _voKeySystemAccess2 = _interopRequireDefault(_voKeySystemAccess); var _coreEventsEvents = require('../../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function ProtectionModel_21Jan2015(config) { var context = this.context; var eventBus = config.eventBus; //Need to pass in here so we can use same instance since this is optional module var log = config.log; var instance, keySystem, videoElement, mediaKeys, sessions, eventHandler, protectionKeyController; function setup() { keySystem = null; videoElement = null; mediaKeys = null; sessions = []; protectionKeyController = (0, _controllersProtectionKeyController2['default'])(context).getInstance(); eventHandler = createEventHandler(); } function reset() { var numSessions = sessions.length; var session; if (numSessions !== 0) { // Called when we are done closing a session. Success or fail var done = function done(session) { removeSession(session); if (sessions.length === 0) { if (videoElement) { videoElement.removeEventListener('encrypted', eventHandler); videoElement.setMediaKeys(null).then(function () { eventBus.trigger(_coreEventsEvents2['default'].TEARDOWN_COMPLETE); }); } else { eventBus.trigger(_coreEventsEvents2['default'].TEARDOWN_COMPLETE); } } }; for (var i = 0; i < numSessions; i++) { session = sessions[i]; (function (s) { // Override closed promise resolver session.session.closed.then(function () { done(s); }); // Close the session and handle errors, otherwise promise // resolver above will be called closeKeySessionInternal(session)['catch'](function () { done(s); }); })(session); } } else { eventBus.trigger(_coreEventsEvents2['default'].TEARDOWN_COMPLETE); } } function getKeySystem() { return keySystem; } function getAllInitData() { var retVal = []; for (var i = 0; i < sessions.length; i++) { retVal.push(sessions[i].initData); } return retVal; } function requestKeySystemAccess(ksConfigurations) { requestKeySystemAccessInternal(ksConfigurations, 0); } function selectKeySystem(keySystemAccess) { keySystemAccess.mksa.createMediaKeys().then(function (mkeys) { keySystem = keySystemAccess.keySystem; mediaKeys = mkeys; if (videoElement) { videoElement.setMediaKeys(mediaKeys); } eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_KEY_SYSTEM_SELECTED); })['catch'](function () { eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_KEY_SYSTEM_SELECTED, { error: 'Error selecting keys system (' + keySystemAccess.keySystem.systemString + ')! Could not create MediaKeys -- TODO' }); }); } function setMediaElement(mediaElement) { if (videoElement === mediaElement) return; // Replacing the previous element if (videoElement) { videoElement.removeEventListener('encrypted', eventHandler); videoElement.setMediaKeys(null); } videoElement = mediaElement; // Only if we are not detaching from the existing element if (videoElement) { videoElement.addEventListener('encrypted', eventHandler); if (mediaKeys) { videoElement.setMediaKeys(mediaKeys); } } } function setServerCertificate(serverCertificate) { if (!keySystem || !mediaKeys) { throw new Error('Can not set server certificate until you have selected a key system'); } mediaKeys.setServerCertificate(serverCertificate).then(function () { log('DRM: License server certificate successfully updated.'); eventBus.trigger(_coreEventsEvents2['default'].SERVER_CERTIFICATE_UPDATED); })['catch'](function (error) { eventBus.trigger(_coreEventsEvents2['default'].SERVER_CERTIFICATE_UPDATED, { error: 'Error updating server certificate -- ' + error.name }); }); } function createKeySession(initData, sessionType) { if (!keySystem || !mediaKeys) { throw new Error('Can not create sessions until you have selected a key system'); } var session = mediaKeys.createSession(sessionType); var sessionToken = createSessionToken(session, initData, sessionType); // Generate initial key request session.generateRequest('cenc', initData).then(function () { log('DRM: Session created. SessionID = ' + sessionToken.getSessionID()); eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CREATED, { data: sessionToken }); })['catch'](function (error) { // TODO: Better error string removeSession(sessionToken); eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CREATED, { data: null, error: 'Error generating key request -- ' + error.name }); }); } function updateKeySession(sessionToken, message) { var session = sessionToken.session; // Send our request to the key session if (protectionKeyController.isClearKey(keySystem)) { message = message.toJWK(); } session.update(message)['catch'](function (error) { eventBus.trigger(_coreEventsEvents2['default'].KEY_ERROR, { data: new _voKeyError2['default'](sessionToken, 'Error sending update() message! ' + error.name) }); }); } function loadKeySession(sessionID) { if (!keySystem || !mediaKeys) { throw new Error('Can not load sessions until you have selected a key system'); } var session = mediaKeys.createSession(); // Load persisted session data into our newly created session object session.load(sessionID).then(function (success) { if (success) { var sessionToken = createSessionToken(session); log('DRM: Session created. SessionID = ' + sessionToken.getSessionID()); eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CREATED, { data: sessionToken }); } else { eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CREATED, { data: null, error: 'Could not load session! Invalid Session ID (' + sessionID + ')' }); } })['catch'](function (error) { eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CREATED, { data: null, error: 'Could not load session (' + sessionID + ')! ' + error.name }); }); } function removeKeySession(sessionToken) { var session = sessionToken.session; session.remove().then(function () { log('DRM: Session removed. SessionID = ' + sessionToken.getSessionID()); eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_REMOVED, { data: sessionToken.getSessionID() }); }, function (error) { eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_REMOVED, { data: null, error: 'Error removing session (' + sessionToken.getSessionID() + '). ' + error.name }); }); } function closeKeySession(sessionToken) { // Send our request to the key session closeKeySessionInternal(sessionToken)['catch'](function (error) { removeSession(sessionToken); eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CLOSED, { data: null, error: 'Error closing session (' + sessionToken.getSessionID() + ') ' + error.name }); }); } function requestKeySystemAccessInternal(ksConfigurations, idx) { (function (i) { var keySystem = ksConfigurations[i].ks; var configs = ksConfigurations[i].configs; navigator.requestMediaKeySystemAccess(keySystem.systemString, configs).then(function (mediaKeySystemAccess) { // Chrome 40 does not currently implement MediaKeySystemAccess.getConfiguration() var configuration = typeof mediaKeySystemAccess.getConfiguration === 'function' ? mediaKeySystemAccess.getConfiguration() : null; var keySystemAccess = new _voKeySystemAccess2['default'](keySystem, configuration); keySystemAccess.mksa = mediaKeySystemAccess; eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, { data: keySystemAccess }); })['catch'](function () { if (++i < ksConfigurations.length) { requestKeySystemAccessInternal(ksConfigurations, i); } else { eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, { error: 'Key system access denied!' }); } }); })(idx); } function closeKeySessionInternal(sessionToken) { var session = sessionToken.session; // Remove event listeners session.removeEventListener('keystatuseschange', sessionToken); session.removeEventListener('message', sessionToken); // Send our request to the key session return session.close(); } // This is our main event handler for all desired HTMLMediaElement events // related to EME. These events are translated into our API-independent // versions of the same events function createEventHandler() { return { handleEvent: function handleEvent(event) { switch (event.type) { case 'encrypted': if (event.initData) { var initData = ArrayBuffer.isView(event.initData) ? event.initData.buffer : event.initData; eventBus.trigger(_coreEventsEvents2['default'].NEED_KEY, { key: new _voNeedKey2['default'](initData, event.initDataType) }); } break; } } }; } function removeSession(token) { // Remove from our session list for (var i = 0; i < sessions.length; i++) { if (sessions[i] === token) { sessions.splice(i, 1); break; } } } // Function to create our session token objects which manage the EME // MediaKeySession and session-specific event handler function createSessionToken(session, initData, sessionType) { var token = { // Implements SessionToken session: session, initData: initData, // This is our main event handler for all desired MediaKeySession events // These events are translated into our API-independent versions of the // same events handleEvent: function handleEvent(event) { switch (event.type) { case 'keystatuseschange': eventBus.trigger(_coreEventsEvents2['default'].KEY_STATUSES_CHANGED, { data: this }); break; case 'message': var message = ArrayBuffer.isView(event.message) ? event.message.buffer : event.message; eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_KEY_MESSAGE, { data: new _voKeyMessage2['default'](this, message, undefined, event.messageType) }); break; } }, getSessionID: function getSessionID() { return session.sessionId; }, getExpirationTime: function getExpirationTime() { return session.expiration; }, getKeyStatuses: function getKeyStatuses() { return session.keyStatuses; }, getSessionType: function getSessionType() { return sessionType; } }; // Add all event listeners session.addEventListener('keystatuseschange', token); session.addEventListener('message', token); // Register callback for session closed Promise session.closed.then(function () { removeSession(token); log('DRM: Session closed. SessionID = ' + token.getSessionID()); eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CLOSED, { data: token.getSessionID() }); }); // Add to our session list sessions.push(token); return token; } instance = { getAllInitData: getAllInitData, requestKeySystemAccess: requestKeySystemAccess, getKeySystem: getKeySystem, selectKeySystem: selectKeySystem, setMediaElement: setMediaElement, setServerCertificate: setServerCertificate, createKeySession: createKeySession, updateKeySession: updateKeySession, loadKeySession: loadKeySession, removeKeySession: removeKeySession, closeKeySession: closeKeySession, reset: reset }; setup(); return instance; } ProtectionModel_21Jan2015.__dashjs_factory_name = 'ProtectionModel_21Jan2015'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(ProtectionModel_21Jan2015); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../../core/events/Events":18,"../controllers/ProtectionKeyController":115,"../vo/KeyError":127,"../vo/KeyMessage":128,"../vo/KeySystemAccess":130,"../vo/NeedKey":133}],121:[function(require,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. */ /** * Implementation of the EME APIs as of the 3 Feb 2014 state of the specification. * * Implemented by Internet Explorer 11 (Windows 8.1) * * @implements ProtectionModel * @class */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _controllersProtectionKeyController = require('../controllers/ProtectionKeyController'); var _controllersProtectionKeyController2 = _interopRequireDefault(_controllersProtectionKeyController); var _voNeedKey = require('../vo/NeedKey'); var _voNeedKey2 = _interopRequireDefault(_voNeedKey); var _voKeyError = require('../vo/KeyError'); var _voKeyError2 = _interopRequireDefault(_voKeyError); var _voKeyMessage = require('../vo/KeyMessage'); var _voKeyMessage2 = _interopRequireDefault(_voKeyMessage); var _voKeySystemConfiguration = require('../vo/KeySystemConfiguration'); var _voKeySystemConfiguration2 = _interopRequireDefault(_voKeySystemConfiguration); var _voKeySystemAccess = require('../vo/KeySystemAccess'); var _voKeySystemAccess2 = _interopRequireDefault(_voKeySystemAccess); var _coreEventsEvents = require('../../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function ProtectionModel_3Feb2014(config) { var context = this.context; var eventBus = config.eventBus; //Need to pass in here so we can use same instance since this is optional module var log = config.log; var api = config.api; var instance = undefined, videoElement = undefined, keySystem = undefined, mediaKeys = undefined, keySystemAccess = undefined, sessions = undefined, eventHandler = undefined, protectionKeyController = undefined; function setup() { videoElement = null; keySystem = null; mediaKeys = null; keySystemAccess = null; sessions = []; protectionKeyController = (0, _controllersProtectionKeyController2['default'])(context).getInstance(); eventHandler = createEventHandler(); } function reset() { try { for (var i = 0; i < sessions.length; i++) { closeKeySession(sessions[i]); } if (videoElement) { videoElement.removeEventListener(api.needkey, eventHandler); } eventBus.trigger(_coreEventsEvents2['default'].TEARDOWN_COMPLETE); } catch (error) { eventBus.trigger(_coreEventsEvents2['default'].TEARDOWN_COMPLETE, { error: 'Error tearing down key sessions and MediaKeys! -- ' + error.message }); } } function getKeySystem() { return keySystem; } function getAllInitData() { var retVal = []; for (var i = 0; i < sessions.length; i++) { retVal.push(sessions[i].initData); } return retVal; } function requestKeySystemAccess(ksConfigurations) { // Try key systems in order, first one with supported key system configuration // is used var found = false; for (var ksIdx = 0; ksIdx < ksConfigurations.length; ksIdx++) { var systemString = ksConfigurations[ksIdx].ks.systemString; var configs = ksConfigurations[ksIdx].configs; var supportedAudio = null; var supportedVideo = null; // Try key system configs in order, first one with supported audio/video // is used for (var configIdx = 0; configIdx < configs.length; configIdx++) { var audios = configs[configIdx].audioCapabilities; var videos = configs[configIdx].videoCapabilities; // Look for supported audio container/codecs if (audios && audios.length !== 0) { supportedAudio = []; // Indicates that we have a requested audio config for (var audioIdx = 0; audioIdx < audios.length; audioIdx++) { if (window[api.MediaKeys].isTypeSupported(systemString, audios[audioIdx].contentType)) { supportedAudio.push(audios[audioIdx]); } } } // Look for supported video container/codecs if (videos && videos.length !== 0) { supportedVideo = []; // Indicates that we have a requested video config for (var videoIdx = 0; videoIdx < videos.length; videoIdx++) { if (window[api.MediaKeys].isTypeSupported(systemString, videos[videoIdx].contentType)) { supportedVideo.push(videos[videoIdx]); } } } // No supported audio or video in this configuration OR we have // requested audio or video configuration that is not supported if (!supportedAudio && !supportedVideo || supportedAudio && supportedAudio.length === 0 || supportedVideo && supportedVideo.length === 0) { continue; } // This configuration is supported found = true; var ksConfig = new _voKeySystemConfiguration2['default'](supportedAudio, supportedVideo); var ks = protectionKeyController.getKeySystemBySystemString(systemString); eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, { data: new _voKeySystemAccess2['default'](ks, ksConfig) }); break; } } if (!found) { eventBus.trigger(_coreEventsEvents2['default'].KEY_SYSTEM_ACCESS_COMPLETE, { error: 'Key system access denied! -- No valid audio/video content configurations detected!' }); } } function selectKeySystem(ksAccess) { try { mediaKeys = ksAccess.mediaKeys = new window[api.MediaKeys](ksAccess.keySystem.systemString); keySystem = ksAccess.keySystem; keySystemAccess = ksAccess; if (videoElement) { setMediaKeys(); } eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_KEY_SYSTEM_SELECTED); } catch (error) { eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_KEY_SYSTEM_SELECTED, { error: 'Error selecting keys system (' + keySystem.systemString + ')! Could not create MediaKeys -- TODO' }); } } function setMediaElement(mediaElement) { if (videoElement === mediaElement) return; // Replacing the previous element if (videoElement) { videoElement.removeEventListener(api.needkey, eventHandler); } videoElement = mediaElement; // Only if we are not detaching from the existing element if (videoElement) { videoElement.addEventListener(api.needkey, eventHandler); if (mediaKeys) { setMediaKeys(); } } } function createKeySession(initData /*, keySystemType */) { if (!keySystem || !mediaKeys || !keySystemAccess) { throw new Error('Can not create sessions until you have selected a key system'); } // Use the first video capability for the contentType. // TODO: Not sure if there is a way to concatenate all capability data into a RFC6386-compatible format // If player is trying to playback Audio only stream - don't error out. var capabilities = null; if (keySystemAccess.ksConfiguration.videoCapabilities !== null && keySystemAccess.ksConfiguration.videoCapabilities.length > 0) capabilities = keySystemAccess.ksConfiguration.videoCapabilities[0]; if (capabilities === null && keySystemAccess.ksConfiguration.audioCapabilities !== null && keySystemAccess.ksConfiguration.audioCapabilities.length > 0) capabilities = keySystemAccess.ksConfiguration.audioCapabilities[0]; if (capabilities === null) throw new Error('Can not create sessions for unknown content types.'); var contentType = capabilities.contentType; var session = mediaKeys.createSession(contentType, new Uint8Array(initData)); var sessionToken = createSessionToken(session, initData); // Add all event listeners session.addEventListener(api.error, sessionToken); session.addEventListener(api.message, sessionToken); session.addEventListener(api.ready, sessionToken); session.addEventListener(api.close, sessionToken); // Add to our session list sessions.push(sessionToken); log('DRM: Session created. SessionID = ' + sessionToken.getSessionID()); eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CREATED, { data: sessionToken }); } function updateKeySession(sessionToken, message) { var session = sessionToken.session; if (!protectionKeyController.isClearKey(keySystem)) { // Send our request to the key session session.update(new Uint8Array(message)); } else { // For clearkey, message is a ClearKeyKeySet session.update(new Uint8Array(message.toJWK())); } } /** * Close the given session and release all associated keys. Following * this call, the sessionToken becomes invalid * * @param {Object} sessionToken - the session token */ function closeKeySession(sessionToken) { var session = sessionToken.session; // Remove event listeners session.removeEventListener(api.error, sessionToken); session.removeEventListener(api.message, sessionToken); session.removeEventListener(api.ready, sessionToken); session.removeEventListener(api.close, sessionToken); // Remove from our session list for (var i = 0; i < sessions.length; i++) { if (sessions[i] === sessionToken) { sessions.splice(i, 1); break; } } // Send our request to the key session session[api.release](); } function setServerCertificate() /*serverCertificate*/{/* Not supported */} function loadKeySession() /*sessionID*/{/* Not supported */} function removeKeySession() /*sessionToken*/{/* Not supported */} function createEventHandler() { return { handleEvent: function handleEvent(event) { switch (event.type) { case api.needkey: if (event.initData) { var initData = ArrayBuffer.isView(event.initData) ? event.initData.buffer : event.initData; eventBus.trigger(_coreEventsEvents2['default'].NEED_KEY, { key: new _voNeedKey2['default'](initData, 'cenc') }); } break; } } }; } // IE11 does not let you set MediaKeys until it has entered a certain // readyState, so we need this logic to ensure we don't set the keys // too early function setMediaKeys() { var boundDoSetKeys = null; var doSetKeys = function doSetKeys() { videoElement.removeEventListener('loadedmetadata', boundDoSetKeys); videoElement[api.setMediaKeys](mediaKeys); eventBus.trigger(_coreEventsEvents2['default'].VIDEO_ELEMENT_SELECTED); }; if (videoElement.readyState >= 1) { doSetKeys(); } else { boundDoSetKeys = doSetKeys.bind(this); videoElement.addEventListener('loadedmetadata', boundDoSetKeys); } } // Function to create our session token objects which manage the EME // MediaKeySession and session-specific event handler function createSessionToken(keySession, initData) { return { // Implements SessionToken session: keySession, initData: initData, getSessionID: function getSessionID() { return this.session.sessionId; }, getExpirationTime: function getExpirationTime() { return NaN; }, getSessionType: function getSessionType() { return 'temporary'; }, // This is our main event handler for all desired MediaKeySession events // These events are translated into our API-independent versions of the // same events handleEvent: function handleEvent(event) { switch (event.type) { case api.error: var errorStr = 'KeyError'; // TODO: Make better string from event eventBus.trigger(_coreEventsEvents2['default'].KEY_ERROR, { data: new _voKeyError2['default'](this, errorStr) }); break; case api.message: var message = ArrayBuffer.isView(event.message) ? event.message.buffer : event.message; eventBus.trigger(_coreEventsEvents2['default'].INTERNAL_KEY_MESSAGE, { data: new _voKeyMessage2['default'](this, message, event.destinationURL) }); break; case api.ready: log('DRM: Key added.'); eventBus.trigger(_coreEventsEvents2['default'].KEY_ADDED); break; case api.close: log('DRM: Session closed. SessionID = ' + this.getSessionID()); eventBus.trigger(_coreEventsEvents2['default'].KEY_SESSION_CLOSED, { data: this.getSessionID() }); break; } } }; } instance = { getAllInitData: getAllInitData, requestKeySystemAccess: requestKeySystemAccess, getKeySystem: getKeySystem, selectKeySystem: selectKeySystem, setMediaElement: setMediaElement, createKeySession: createKeySession, updateKeySession: updateKeySession, closeKeySession: closeKeySession, setServerCertificate: setServerCertificate, loadKeySession: loadKeySession, removeKeySession: removeKeySession, reset: reset }; setup(); return instance; } ProtectionModel_3Feb2014.__dashjs_factory_name = 'ProtectionModel_3Feb2014'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(ProtectionModel_3Feb2014); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../../core/events/Events":18,"../controllers/ProtectionKeyController":115,"../vo/KeyError":127,"../vo/KeyMessage":128,"../vo/KeySystemAccess":130,"../vo/KeySystemConfiguration":131,"../vo/NeedKey":133}],122:[function(require,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. */ /** * CableLabs ClearKey license server implementation * * For testing purposes and evaluating potential uses for ClearKey, we have developed * a dirt-simple API for requesting ClearKey licenses from a remote server. * * @implements LicenseServer * @class */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _voKeyPair = require('../vo/KeyPair'); var _voKeyPair2 = _interopRequireDefault(_voKeyPair); var _voClearKeyKeySet = require('../vo/ClearKeyKeySet'); var _voClearKeyKeySet2 = _interopRequireDefault(_voClearKeyKeySet); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function ClearKey() { var instance = undefined; function getServerURLFromMessage(url, message /*, messageType*/) { // Build ClearKey server query string var jsonMsg = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(message))); url += '/?'; for (var i = 0; i < jsonMsg.kids.length; i++) { url += jsonMsg.kids[i] + '&'; } url = url.substring(0, url.length - 1); return url; } function getHTTPMethod() /*messageType*/{ return 'GET'; } function getResponseType() /*keySystemStr*/{ return 'json'; } function getLicenseMessage(serverResponse /*, keySystemStr, messageType*/) { if (!serverResponse.hasOwnProperty('keys')) { return null; } var keyPairs = []; for (var i = 0; i < serverResponse.keys.length; i++) { var keypair = serverResponse.keys[i]; var keyid = keypair.kid.replace(/=/g, ''); var key = keypair.k.replace(/=/g, ''); keyPairs.push(new _voKeyPair2['default'](keyid, key)); } return new _voClearKeyKeySet2['default'](keyPairs); } function getErrorResponse(serverResponse /*, keySystemStr, messageType*/) { return String.fromCharCode.apply(null, new Uint8Array(serverResponse)); } instance = { getServerURLFromMessage: getServerURLFromMessage, getHTTPMethod: getHTTPMethod, getResponseType: getResponseType, getLicenseMessage: getLicenseMessage, getErrorResponse: getErrorResponse }; return instance; } ClearKey.__dashjs_factory_name = 'ClearKey'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(ClearKey); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../vo/ClearKeyKeySet":126,"../vo/KeyPair":129}],123:[function(require,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. */ /** * CastLabs DRMToday License Server implementation * * @implements LicenseServer * @class */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _externalsBase64 = require('../../../../externals/base64'); var _externalsBase642 = _interopRequireDefault(_externalsBase64); function DRMToday() { var keySystems = { 'com.widevine.alpha': { responseType: 'json', getLicenseMessage: function getLicenseMessage(response) { return _externalsBase642['default'].decodeArray(response.license); }, getErrorResponse: function getErrorResponse(response) { return response; } }, 'com.microsoft.playready': { responseType: 'arraybuffer', getLicenseMessage: function getLicenseMessage(response) { return response; }, getErrorResponse: function getErrorResponse(response) { return String.fromCharCode.apply(null, new Uint8Array(response)); } } }; var instance = undefined; function getServerURLFromMessage(url /*, message, messageType*/) { return url; } function getHTTPMethod() /*messageType*/{ return 'POST'; } function getResponseType(keySystemStr /*, messageType*/) { return keySystems[keySystemStr].responseType; } function getLicenseMessage(serverResponse, keySystemStr /*, messageType*/) { return keySystems[keySystemStr].getLicenseMessage(serverResponse); } function getErrorResponse(serverResponse, keySystemStr /*, messageType*/) { return keySystems[keySystemStr].getErrorResponse(serverResponse); } instance = { getServerURLFromMessage: getServerURLFromMessage, getHTTPMethod: getHTTPMethod, getResponseType: getResponseType, getLicenseMessage: getLicenseMessage, getErrorResponse: getErrorResponse }; return instance; } DRMToday.__dashjs_factory_name = 'DRMToday'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(DRMToday); module.exports = exports['default']; },{"../../../../externals/base64":8,"../../../core/FactoryMaker":15}],124:[function(require,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. */ /** * Microsoft PlayReady Test License Server * * For testing content that uses the PlayReady test server at * * @implements LicenseServer * @class */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function PlayReady() { var instance = undefined; function getServerURLFromMessage(url /*, message, messageType*/) { return url; } function getHTTPMethod() /*messageType*/{ return 'POST'; } function getResponseType() /*keySystemStr, messageType*/{ return 'arraybuffer'; } function getLicenseMessage(serverResponse /*, keySystemStr, messageType*/) { return serverResponse; } function getErrorResponse(serverResponse /*, keySystemStr, messageType*/) { return String.fromCharCode.apply(null, new Uint8Array(serverResponse)); } instance = { getServerURLFromMessage: getServerURLFromMessage, getHTTPMethod: getHTTPMethod, getResponseType: getResponseType, getLicenseMessage: getLicenseMessage, getErrorResponse: getErrorResponse }; return instance; } PlayReady.__dashjs_factory_name = 'PlayReady'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(PlayReady); module.exports = exports['default']; },{"../../../core/FactoryMaker":15}],125:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function Widevine() { var instance = undefined; function getServerURLFromMessage(url /*, message, messageType*/) { return url; } function getHTTPMethod() /*messageType*/{ return 'POST'; } function getResponseType() /*keySystemStr, messageType*/{ return 'arraybuffer'; } function getLicenseMessage(serverResponse /*, keySystemStr, messageType*/) { return serverResponse; } function getErrorResponse(serverResponse /*, keySystemStr, messageType*/) { return String.fromCharCode.apply(null, new Uint8Array(serverResponse)); } instance = { getServerURLFromMessage: getServerURLFromMessage, getHTTPMethod: getHTTPMethod, getResponseType: getResponseType, getLicenseMessage: getLicenseMessage, getErrorResponse: getErrorResponse }; return instance; } Widevine.__dashjs_factory_name = 'Widevine'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(Widevine); module.exports = exports['default']; },{"../../../core/FactoryMaker":15}],126:[function(require,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. */ /** * @classdesc A collection of ClearKey encryption keys with an (optional) associated * type * @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 ClearKeyKeySet = (function () { /** * @param {Array.} keyPairs * @param {string} type the type of keys in this set. One of either 'persistent' * or 'temporary'. Can also be null or undefined. * @class * @ignore */ function ClearKeyKeySet(keyPairs, type) { _classCallCheck(this, ClearKeyKeySet); if (type && type !== 'persistent' && type !== 'temporary') throw new Error('Invalid ClearKey key set type! Must be one of \'persistent\' or \'temporary\''); this.keyPairs = keyPairs; this.type = type; } /** * Convert this key set to its JSON Web Key (JWK) representation * * @return {ArrayBuffer} JWK object UTF-8 encoded as ArrayBuffer */ _createClass(ClearKeyKeySet, [{ key: 'toJWK', value: function toJWK() { var i; var numKeys = this.keyPairs.length; var jwk = { keys: [] }; for (i = 0; i < numKeys; i++) { var key = { kty: 'oct', alg: 'A128KW', kid: this.keyPairs[i].keyID, k: this.keyPairs[i].key }; jwk.keys.push(key); } if (this.type) { jwk.type = this.type; } var jwkString = JSON.stringify(jwk); var len = jwkString.length; // Convert JSON string to ArrayBuffer var buf = new ArrayBuffer(len); var bView = new Uint8Array(buf); for (i = 0; i < len; i++) bView[i] = jwkString.charCodeAt(i); return buf; } }]); return ClearKeyKeySet; })(); exports['default'] = ClearKeyKeySet; module.exports = exports['default']; },{}],127:[function(require,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. */ /** * @classdesc EME-independent KeyError * @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 KeyError = /** * @param {Object} sessionToken the key session to which this error is associated * @param {string} errorString an informational error message * @class * @deprecated Newest versions of EME APIs will not use this error object */ function KeyError(sessionToken, errorString) { _classCallCheck(this, KeyError); this.sessionToken = sessionToken; this.error = errorString; }; exports["default"] = KeyError; module.exports = exports["default"]; },{}],128:[function(require,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. */ /** * @classdesc EME-independent KeyMessage * @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 KeyMessage = /** * @param {SessionToken} sessionToken the session * to which the key message is associated * @param {ArrayBuffer} message the key message * @param {string} defaultURL license acquisition URL provided by the CDM * @param {string} messageType Supported message types can be found * {@link https://w3c.github.io/encrypted-media/#idl-def-MediaKeyMessageType|here}. * @class */ function KeyMessage(sessionToken, message, defaultURL, messageType) { _classCallCheck(this, KeyMessage); this.sessionToken = sessionToken; this.message = message; this.defaultURL = defaultURL; this.messageType = messageType ? messageType : 'license-request'; }; exports['default'] = KeyMessage; module.exports = exports['default']; },{}],129:[function(require,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. */ /** * @classdesc Represents a 128-bit keyID and 128-bit encryption key * @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 KeyPair = /** * @param {string} keyID 128-bit key ID, base64 encoded, with no padding * @param {string} key 128-bit encryption key, base64 encoded, with no padding * @class * @ignore */ function KeyPair(keyID, key) { _classCallCheck(this, KeyPair); this.keyID = keyID; this.key = key; }; exports["default"] = KeyPair; module.exports = exports["default"]; },{}],130:[function(require,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. */ /** * @classdesc Creates a new key system access token. Represents a valid key system for * given piece of content and key system requirements. Used to initialize license * acquisition operations. * @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 KeySystemAccess = /** * @param {MediaPlayer.dependencies.protection.KeySystem} keySystem the key system * @param {KeySystemConfiguration} ksConfiguration the * subset of configurations passed to the key system access request that are supported * by this user agent * @class * @ignore */ function KeySystemAccess(keySystem, ksConfiguration) { _classCallCheck(this, KeySystemAccess); this.keySystem = keySystem; this.ksConfiguration = ksConfiguration; }; exports["default"] = KeySystemAccess; module.exports = exports["default"]; },{}],131:[function(require,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. */ /** * @classdesc Represents a set of configurations that describe the capabilities desired for * support by a given CDM * @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 KeySystemConfiguration = /** * @param {Array.} audioCapabilities array of * desired audio capabilities. Higher preference capabilities should be placed earlier * in the array. * @param {Array.} videoCapabilities array of * desired video capabilities. Higher preference capabilities should be placed earlier * in the array. * @param {string} distinctiveIdentifier desired use of distinctive identifiers. * One of "required", "optional", or "not-allowed" * @param {string} persistentState desired support for persistent storage of * key systems. One of "required", "optional", or "not-allowed" * @param {Array.} sessionTypes List of session types that must * be supported by the key system * @class */ function KeySystemConfiguration(audioCapabilities, videoCapabilities, distinctiveIdentifier, persistentState, sessionTypes) { _classCallCheck(this, KeySystemConfiguration); this.initDataTypes = ['cenc']; if (audioCapabilities && audioCapabilities.length) { this.audioCapabilities = audioCapabilities; } if (videoCapabilities && videoCapabilities.length) { this.videoCapabilities = videoCapabilities; } this.distinctiveIdentifier = distinctiveIdentifier; this.persistentState = persistentState; this.sessionTypes = sessionTypes; }; exports['default'] = KeySystemConfiguration; module.exports = exports['default']; },{}],132:[function(require,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. */ /** * @classdesc A media capability * @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 MediaCapability = /** * @param {string} contentType MIME type and codecs (RFC6386) * @param {string} robustness * @class * @ignore */ function MediaCapability(contentType, robustness) { _classCallCheck(this, MediaCapability); this.contentType = contentType; this.robustness = robustness; }; exports["default"] = MediaCapability; module.exports = exports["default"]; },{}],133:[function(require,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. */ /** * @classdesc NeedKey * @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 NeedKey = /** * @param {ArrayBuffer} initData the initialization data * @param {string} initDataType initialization data type * @class */ function NeedKey(initData, initDataType) { _classCallCheck(this, NeedKey); this.initData = initData; this.initDataType = initDataType; }; exports["default"] = NeedKey; module.exports = exports["default"]; },{}],134:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreFactoryMakerJs = require('../../core/FactoryMaker.js'); var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs); function DroppedFramesHistory() { var values = []; var lastDroppedFrames = 0; var lastTotalFrames = 0; function push(index, playbackQuality) { var intervalDroppedFrames = playbackQuality.droppedVideoFrames - lastDroppedFrames; lastDroppedFrames = playbackQuality.droppedVideoFrames; var intervalTotalFrames = playbackQuality.totalVideoFrames - lastTotalFrames; lastTotalFrames = playbackQuality.totalVideoFrames; if (!values[index]) { values[index] = { droppedVideoFrames: intervalDroppedFrames, totalVideoFrames: intervalTotalFrames }; } else { values[index].droppedVideoFrames += intervalDroppedFrames; values[index].totalVideoFrames += intervalTotalFrames; } } function getDroppedFrameHistory() { return values; } function reset(playbackQuality) { values = []; lastDroppedFrames = playbackQuality.droppedVideoFrames; lastTotalFrames = playbackQuality.totalVideoFrames; } return { push: push, getFrameHistory: getDroppedFrameHistory, reset: reset }; } DroppedFramesHistory.__dashjs_factory_name = 'DroppedFramesHistory'; var factory = _coreFactoryMakerJs2['default'].getClassFactory(DroppedFramesHistory); exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker.js":15}],135:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function RulesContext(config) { var instance = undefined; var representationInfo = config.streamProcessor.getCurrentRepresentationInfo(); var sp = config.streamProcessor; var currentValue = config.currentValue; var playbackIndex = config.playbackIndex; var switchHistory = config.switchHistory; var droppedFramesHistory = config.droppedFramesHistory; var currentRequest = config.currentRequest; var richBuffer = config.hasRichBuffer; 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; } function getPlaybackIndex() { return playbackIndex; } function getSwitchHistory() { return switchHistory; } function getDroppedFramesHistory() { return droppedFramesHistory; } function getCurrentRequest() { return currentRequest; } function hasRichBuffer() { return richBuffer; } instance = { getCurrentValue: getCurrentValue, getManifestInfo: getManifestInfo, getMediaInfo: getMediaInfo, getPlaybackIndex: getPlaybackIndex, getDroppedFramesHistory: getDroppedFramesHistory, getCurrentRequest: getCurrentRequest, getSwitchHistory: getSwitchHistory, getStreamInfo: getStreamInfo, getStreamProcessor: getStreamProcessor, getTrackInfo: getTrackInfo, hasRichBuffer: hasRichBuffer }; return instance; } RulesContext.__dashjs_factory_name = 'RulesContext'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(RulesContext); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],136:[function(require,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 _RulesContext = require('./RulesContext'); var _RulesContext2 = _interopRequireDefault(_RulesContext); var _SwitchRequest = require('./SwitchRequest'); var _SwitchRequest2 = _interopRequireDefault(_SwitchRequest); var _abrABRRulesCollection = require('./abr/ABRRulesCollection'); var _abrABRRulesCollection2 = _interopRequireDefault(_abrABRRulesCollection); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var ABR_RULE = 0; 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; } } function applyRules(rulesArr, streamProcessor, callback, current, playbackQuality, overrideFunc) { var values = {}; var reasons = {}; var rule, i; var rulesCount = rulesArr.length; var ln = rulesCount; var rulesContext = getRulesContext(streamProcessor, current); var callbackFunc = function callbackFunc(result) { var value, reason, confidence; if (result.value !== _SwitchRequest2['default'].NO_CHANGE) { var newValue = overrideFunc(values[result.priority], result.value); if (newValue !== values[result.priority]) { // change in value values[result.priority] = newValue; // === result.value reasons[result.priority] = result.reason; } } if (--rulesCount) return; if (values[_SwitchRequest2['default'].WEAK] !== _SwitchRequest2['default'].NO_CHANGE) { confidence = _SwitchRequest2['default'].WEAK; value = values[_SwitchRequest2['default'].WEAK]; reason = reasons[_SwitchRequest2['default'].WEAK]; } if (values[_SwitchRequest2['default'].DEFAULT] !== _SwitchRequest2['default'].NO_CHANGE) { confidence = _SwitchRequest2['default'].DEFAULT; value = values[_SwitchRequest2['default'].DEFAULT]; reason = reasons[_SwitchRequest2['default'].DEFAULT]; } if (values[_SwitchRequest2['default'].STRONG] !== _SwitchRequest2['default'].NO_CHANGE) { confidence = _SwitchRequest2['default'].STRONG; value = values[_SwitchRequest2['default'].STRONG]; reason = reasons[_SwitchRequest2['default'].STRONG]; } if (confidence != _SwitchRequest2['default'].STRONG && confidence != _SwitchRequest2['default'].WEAK) { confidence = _SwitchRequest2['default'].DEFAULT; } if (value !== undefined) { callback({ value: value, confidence: confidence, reason: reason }); } else { callback({ value: current, confidence: confidence, reason: { name: 'NO_CHANGE' } }); } }; values[_SwitchRequest2['default'].STRONG] = _SwitchRequest2['default'].NO_CHANGE; values[_SwitchRequest2['default'].WEAK] = _SwitchRequest2['default'].NO_CHANGE; values[_SwitchRequest2['default'].DEFAULT] = _SwitchRequest2['default'].NO_CHANGE; for (i = 0; i < ln; i++) { rule = rulesArr[i]; rule.execute(rulesContext, callbackFunc); } } function reset() { var abrRules = rules[ABR_RULE]; var allRules = (abrRules.getRules(_abrABRRulesCollection2['default'].QUALITY_SWITCH_RULES) || []).concat(abrRules.getRules(_abrABRRulesCollection2['default'].ABANDON_FRAGMENT_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, _RulesContext2['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 = _coreFactoryMaker2['default'].getSingletonFactory(RulesController); factory.ABR_RULE = ABR_RULE; exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15,"./RulesContext":135,"./SwitchRequest":137,"./abr/ABRRulesCollection":139}],137:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var NO_CHANGE = -1; function SwitchRequest(v, r) { //TODO refactor all the calls to this to use config to be like everything else. var value = v === undefined ? NO_CHANGE : v; var reason = r === undefined ? null : r; var instance = { value: value, reason: reason }; return instance; } SwitchRequest.__dashjs_factory_name = 'SwitchRequest'; var factory = _coreFactoryMaker2['default'].getClassFactory(SwitchRequest); factory.NO_CHANGE = NO_CHANGE; exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15}],138:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreFactoryMakerJs = require('../../core/FactoryMaker.js'); var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs); var SWITCH_REQUEST_HISTORY_DEPTH = 8; // must be > SwitchHistoryRule SAMPLE_SIZE to enable rule function SwitchRequestHistory() { var switchRequests = []; // running total var srHistory = []; // history of each switch function push(switchRequest) { if (!switchRequests[switchRequest.oldValue]) { switchRequests[switchRequest.oldValue] = { noDrops: 0, drops: 0, dropSize: 0 }; } // Set switch details var indexDiff = switchRequest.newValue - switchRequest.oldValue; var drop = indexDiff < 0 ? 1 : 0; var dropSize = drop ? -indexDiff : 0; var noDrop = drop ? 0 : 1; // Update running totals switchRequests[switchRequest.oldValue].drops += drop; switchRequests[switchRequest.oldValue].dropSize += dropSize; switchRequests[switchRequest.oldValue].noDrops += noDrop; // Save to history srHistory.push({ idx: switchRequest.oldValue, noDrop: noDrop, drop: drop, dropSize: dropSize }); // Shift earliest switch off srHistory and readjust to keep depth of running totals constant if (srHistory.length > SWITCH_REQUEST_HISTORY_DEPTH) { var srHistoryFirst = srHistory.shift(); switchRequests[srHistoryFirst.idx].drops -= srHistoryFirst.drop; switchRequests[srHistoryFirst.idx].dropSize -= srHistoryFirst.dropSize; switchRequests[srHistoryFirst.idx].noDrops -= srHistoryFirst.noDrop; } } function getSwitchRequests() { return switchRequests; } function reset() { switchRequests = []; srHistory = []; } return { push: push, getSwitchRequests: getSwitchRequests, reset: reset }; } SwitchRequestHistory.__dashjs_factory_name = 'SwitchRequestHistory'; var factory = _coreFactoryMakerJs2['default'].getClassFactory(SwitchRequestHistory); exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker.js":15}],139:[function(require,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 _ThroughputRule = require('./ThroughputRule'); var _ThroughputRule2 = _interopRequireDefault(_ThroughputRule); var _InsufficientBufferRule = require('./InsufficientBufferRule'); var _InsufficientBufferRule2 = _interopRequireDefault(_InsufficientBufferRule); var _AbandonRequestsRule = require('./AbandonRequestsRule'); var _AbandonRequestsRule2 = _interopRequireDefault(_AbandonRequestsRule); var _DroppedFramesRuleJs = require('./DroppedFramesRule.js'); var _DroppedFramesRuleJs2 = _interopRequireDefault(_DroppedFramesRuleJs); var _SwitchHistoryRuleJs = require('./SwitchHistoryRule.js'); var _SwitchHistoryRuleJs2 = _interopRequireDefault(_SwitchHistoryRuleJs); var _BolaRule = require('./BolaRule'); var _BolaRule2 = _interopRequireDefault(_BolaRule); var _BolaAbandonRule = require('./BolaAbandonRule'); var _BolaAbandonRule2 = _interopRequireDefault(_BolaAbandonRule); var _modelsMediaPlayerModel = require('../../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _modelsMetricsModel = require('../../models/MetricsModel'); var _modelsMetricsModel2 = _interopRequireDefault(_modelsMetricsModel); var _dashDashMetrics = require('../../../dash/DashMetrics'); var _dashDashMetrics2 = _interopRequireDefault(_dashDashMetrics); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _SwitchRequestJs = require('../SwitchRequest.js'); var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs); var QUALITY_SWITCH_RULES = 'qualitySwitchRules'; var ABANDON_FRAGMENT_RULES = 'abandonFragmentRules'; function ABRRulesCollection() { var context = this.context; var instance = undefined, qualitySwitchRules = undefined, abandonFragmentRules = undefined; function initialize() { qualitySwitchRules = []; abandonFragmentRules = []; var metricsModel = (0, _modelsMetricsModel2['default'])(context).getInstance(); var dashMetrics = (0, _dashDashMetrics2['default'])(context).getInstance(); var mediaPlayerModel = (0, _modelsMediaPlayerModel2['default'])(context).getInstance(); if (mediaPlayerModel.getBufferOccupancyABREnabled()) { qualitySwitchRules.push((0, _BolaRule2['default'])(context).create({ metricsModel: metricsModel, dashMetrics: (0, _dashDashMetrics2['default'])(context).getInstance() })); abandonFragmentRules.push((0, _BolaAbandonRule2['default'])(context).create({ metricsModel: metricsModel, dashMetrics: (0, _dashDashMetrics2['default'])(context).getInstance() })); } else { qualitySwitchRules.push((0, _ThroughputRule2['default'])(context).create({ metricsModel: metricsModel, dashMetrics: dashMetrics })); qualitySwitchRules.push((0, _InsufficientBufferRule2['default'])(context).create({ metricsModel: metricsModel })); qualitySwitchRules.push((0, _SwitchHistoryRuleJs2['default'])(context).create()); qualitySwitchRules.push((0, _DroppedFramesRuleJs2['default'])(context).create()); abandonFragmentRules.push((0, _AbandonRequestsRule2['default'])(context).create()); } } function getRules(type) { switch (type) { case QUALITY_SWITCH_RULES: return qualitySwitchRules; case ABANDON_FRAGMENT_RULES: return abandonFragmentRules; default: return null; } } function getActiveRules(srArray) { return srArray.filter(function (sr) { return sr.value > _SwitchRequestJs2['default'].NO_CHANGE; }); } function getMinSwitchRequest(srArray) { if (srArray.length === 0) { return; } return srArray.reduce(function (a, b) { return a.value < b.value ? a : b; }); } function getMaxQuality(rulesContext) { var switchRequestArray = qualitySwitchRules.map(function (rule) { return rule.getMaxIndex(rulesContext); }); var activeRules = getActiveRules(switchRequestArray); var maxQuality = getMinSwitchRequest(activeRules); return maxQuality || (0, _SwitchRequestJs2['default'])(context).create(); } function shouldAbandonFragment(rulesContext) { var abandonRequestArray = abandonFragmentRules.map(function (rule) { return rule.shouldAbandon(rulesContext); }); var activeRules = getActiveRules(abandonRequestArray); var shouldAbandon = getMinSwitchRequest(activeRules); return shouldAbandon || (0, _SwitchRequestJs2['default'])(context).create(); } instance = { initialize: initialize, getRules: getRules, getMaxQuality: getMaxQuality, shouldAbandonFragment: shouldAbandonFragment }; return instance; } ABRRulesCollection.__dashjs_factory_name = 'ABRRulesCollection'; var factory = _coreFactoryMaker2['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":15,"../../../dash/DashMetrics":22,"../../models/MediaPlayerModel":107,"../../models/MetricsModel":108,"../SwitchRequest.js":137,"./AbandonRequestsRule":140,"./BolaAbandonRule":141,"./BolaRule":142,"./DroppedFramesRule.js":143,"./InsufficientBufferRule":144,"./SwitchHistoryRule.js":145,"./ThroughputRule":146}],140:[function(require,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 _SwitchRequest = require('../SwitchRequest'); var _SwitchRequest2 = _interopRequireDefault(_SwitchRequest); var _modelsMediaPlayerModel = require('../../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _dashDashMetrics = require('../../../dash/DashMetrics'); var _dashDashMetrics2 = _interopRequireDefault(_dashDashMetrics); var _modelsMetricsModel = require('../../models/MetricsModel'); var _modelsMetricsModel2 = _interopRequireDefault(_modelsMetricsModel); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); function AbandonRequestsRule() { var ABANDON_MULTIPLIER = 1.8; var GRACE_TIME_THRESHOLD = 500; var MIN_LENGTH_TO_AVERAGE = 5; var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var fragmentDict = undefined, abandonDict = undefined, throughputArray = undefined, mediaPlayerModel = undefined, dashMetrics = undefined, metricsModel = undefined; function setup() { fragmentDict = {}; abandonDict = {}; throughputArray = []; mediaPlayerModel = (0, _modelsMediaPlayerModel2['default'])(context).getInstance(); dashMetrics = (0, _dashDashMetrics2['default'])(context).getInstance(); metricsModel = (0, _modelsMetricsModel2['default'])(context).getInstance(); } function setFragmentRequestDict(type, id) { fragmentDict[type] = fragmentDict[type] || {}; fragmentDict[type][id] = fragmentDict[type][id] || {}; } function storeLastRequestThroughputByType(type, throughput) { throughputArray[type] = throughputArray[type] || []; throughputArray[type].push(throughput); } function shouldAbandon(rulesContext) { var mediaInfo = rulesContext.getMediaInfo(); var mediaType = mediaInfo.type; var req = rulesContext.getCurrentRequest(); var switchRequest = (0, _SwitchRequest2['default'])(context).create(_SwitchRequest2['default'].NO_CHANGE, { name: AbandonRequestsRule.__dashjs_factory_name }); if (!isNaN(req.index)) { setFragmentRequestDict(mediaType, req.index); var stableBufferTime = mediaPlayerModel.getStableBufferTime(); var bufferLevel = dashMetrics.getCurrentBufferLevel(metricsModel.getReadOnlyMetricsFor(mediaType)); if (bufferLevel > stableBufferTime) { return switchRequest; } var fragmentInfo = fragmentDict[mediaType][req.index]; if (fragmentInfo === null || req.firstByteDate === null || abandonDict.hasOwnProperty(fragmentInfo.id)) { return switchRequest; } //setup some init info based on first progress event if (fragmentInfo.firstByteTime === undefined) { throughputArray[mediaType] = []; fragmentInfo.firstByteTime = req.firstByteDate.getTime(); fragmentInfo.segmentDuration = req.duration; fragmentInfo.bytesTotal = req.bytesTotal; fragmentInfo.id = req.index; } fragmentInfo.bytesLoaded = req.bytesLoaded; fragmentInfo.elapsedTime = new Date().getTime() - fragmentInfo.firstByteTime; if (fragmentInfo.bytesLoaded > 0 && fragmentInfo.elapsedTime > 0) { storeLastRequestThroughputByType(mediaType, Math.round(fragmentInfo.bytesLoaded * 8 / fragmentInfo.elapsedTime)); } if (throughputArray[mediaType].length >= MIN_LENGTH_TO_AVERAGE && fragmentInfo.elapsedTime > GRACE_TIME_THRESHOLD && fragmentInfo.bytesLoaded < fragmentInfo.bytesTotal) { var totalSampledValue = throughputArray[mediaType].reduce(function (a, b) { return a + b; }, 0); fragmentInfo.measuredBandwidthInKbps = Math.round(totalSampledValue / throughputArray[mediaType].length); fragmentInfo.estimatedTimeOfDownload = +(fragmentInfo.bytesTotal * 8 / fragmentInfo.measuredBandwidthInKbps / 1000).toFixed(2); //log("id:",fragmentInfo.id, "kbps:", fragmentInfo.measuredBandwidthInKbps, "etd:",fragmentInfo.estimatedTimeOfDownload, fragmentInfo.bytesLoaded); if (fragmentInfo.estimatedTimeOfDownload < fragmentInfo.segmentDuration * ABANDON_MULTIPLIER || rulesContext.getTrackInfo().quality === 0) { return switchRequest; } else if (!abandonDict.hasOwnProperty(fragmentInfo.id)) { var abrController = rulesContext.getStreamProcessor().getABRController(); var bytesRemaining = fragmentInfo.bytesTotal - fragmentInfo.bytesLoaded; var bitrateList = abrController.getBitrateList(mediaInfo); var newQuality = abrController.getQualityForBitrate(mediaInfo, fragmentInfo.measuredBandwidthInKbps * mediaPlayerModel.getBandwidthSafetyFactor()); var estimateOtherBytesTotal = fragmentInfo.bytesTotal * bitrateList[newQuality].bitrate / bitrateList[abrController.getQualityFor(mediaType, mediaInfo.streamInfo)].bitrate; if (bytesRemaining > estimateOtherBytesTotal) { switchRequest.value = newQuality; switchRequest.reason.throughput = fragmentInfo.measuredBandwidthInKbps; switchRequest.reason.fragmentID = fragmentInfo.id; 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]; } } return switchRequest; } function reset() { setup(); } var instance = { shouldAbandon: shouldAbandon, reset: reset }; setup(); return instance; } AbandonRequestsRule.__dashjs_factory_name = 'AbandonRequestsRule'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(AbandonRequestsRule); module.exports = exports['default']; },{"../../../core/Debug":13,"../../../core/FactoryMaker":15,"../../../dash/DashMetrics":22,"../../models/MediaPlayerModel":107,"../../models/MetricsModel":108,"../SwitchRequest":137}],141:[function(require,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 _SwitchRequest = require('../SwitchRequest'); var _SwitchRequest2 = _interopRequireDefault(_SwitchRequest); var _modelsMediaPlayerModel = require('../../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _BolaRule = require('./BolaRule'); var _BolaRule2 = _interopRequireDefault(_BolaRule); function BolaAbandonRule(config) { // do not abandon during the grace period var GRACE_PERIOD_MS = 500; var POOR_LATENCY_MS = 200; var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var dashMetrics = config.dashMetrics; var metricsModel = config.metricsModel; var instance = undefined, abandonDict = undefined, mediaPlayerModel = undefined; function setup() { abandonDict = {}; mediaPlayerModel = (0, _modelsMediaPlayerModel2['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 canAbandon(mediaType, index, quality) { var a = abandonDict[mediaType]; if (!a) return true; return index !== a.index || quality < a.quality; } function shouldAbandon(rulesContext) { var mediaInfo = rulesContext.getMediaInfo(); var mediaType = mediaInfo.type; var metrics = metricsModel.getReadOnlyMetricsFor(mediaType); var request = rulesContext.getCurrentRequest(); var switchRequest = (0, _SwitchRequest2['default'])(context).create(_SwitchRequest2['default'].NO_CHANGE, { name: BolaAbandonRule.__dashjs_factory_name }); if (metrics.BolaState.length === 0) { // should not arrive here - we shouldn't be downloading a fragment before BOLA is initialized log('WARNING: executing BolaAbandonRule before initializing BolaRule'); abandonDict[mediaType] = null; return switchRequest; } 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 || !canAbandon(mediaType, index, quality) || !request.firstByteDate) { return switchRequest; } var nowMs = Date.now(); var elapsedTimeMs = nowMs - request.firstByteDate.getTime(); var bytesLoaded = request.bytesLoaded; var bytesTotal = request.bytesTotal; var bytesRemaining = bytesTotal - bytesLoaded; var durationS = request.duration; var bufferLevel = dashMetrics.getCurrentBufferLevel(metrics) ? dashMetrics.getCurrentBufferLevel(metrics) : 0.0; var effectiveBufferLevel = bufferLevel + bolaState.placeholderBuffer; var estimateThroughput = 8 * bytesLoaded / (0.001 * elapsedTimeMs); // throughput in bits per second var estimateThroughputBSF = bolaState.bandwidthSafetyFactor * estimateThroughput; var latencyS = 0.001 * (request.firstByteDate.getTime() - request.requestStartDate.getTime()); if (latencyS < 0.001 * POOR_LATENCY_MS) { latencyS = 0.001 * POOR_LATENCY_MS; } var estimateTotalTimeS = latencyS + 8 * bytesTotal / estimateThroughputBSF; var diagnosticMessage = ''; if (_BolaRule2['default'].BOLA_DEBUG) diagnosticMessage = 'index=' + index + ' quality=' + quality + ' bytesLoaded/bytesTotal=' + bytesLoaded + '/' + bytesTotal + ' bufferLevel=' + bufferLevel + ' timeSince1stByte=' + (elapsedTimeMs / 1000).toFixed(3) + ' estThroughput=' + (estimateThroughputBSF / 1000000).toFixed(3) + ' latency=' + latencyS.toFixed(3); var estimateOtherBytesTotal = bytesTotal * bolaState.bitrates[0] / bolaState.bitrates[quality]; var estimateBytesRemainingAfterLatency = bytesRemaining - latencyS * estimateThroughputBSF / 8; if (estimateBytesRemainingAfterLatency < 1) { estimateBytesRemainingAfterLatency = 1; } if (elapsedTimeMs < GRACE_PERIOD_MS || bytesRemaining <= estimateOtherBytesTotal || bufferLevel > bolaState.bufferTarget || estimateBytesRemainingAfterLatency <= estimateOtherBytesTotal || estimateTotalTimeS <= durationS) { // 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 latencyS bytesRemaining is estimated to drop below size of lowest quality fragment. // Do not abandon if fragment takes less than 1 fragment duration to download. return switchRequest; } // If we abandon, there will be latencyS 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 latencyS cushion also helps avoid extra abandonment, especially with close bitrates. var effectiveBufferAfterLatency = effectiveBufferLevel - latencyS; if (effectiveBufferAfterLatency < 0) { effectiveBufferAfterLatency = 0; } // if we end up abandoning, we should not consider starting a download that would require more bytes than the remaining bytes in currently downloading fragment var maxDroppedQuality = 0; while (maxDroppedQuality + 1 < quality && bytesTotal * bolaState.bitrates[maxDroppedQuality + 1] / bolaState.bitrates[quality] < estimateBytesRemainingAfterLatency) { ++maxDroppedQuality; } var newQuality = quality; if (bolaState.state === _BolaRule2['default'].BOLA_STATE_STARTUP) { // We are not yet using the BOLA buffer rules - use different abandonment logic. // if we are here then we failed the test that estimateTotalTimeS <= durationS, so we abandon // search for quality that matches the throughput newQuality = 0; for (var i = 0; i <= maxDroppedQuality; ++i) { estimateOtherBytesTotal = bytesTotal * bolaState.bitrates[i] / bolaState.bitrates[quality]; if (8 * estimateOtherBytesTotal / durationS > estimateThroughputBSF) { // chunks at quality i or higher need a greater throughput break; } newQuality = i; } } else { // bolaState.state === BolaRule.BOLA_STATE_STEADY // check if we should abandon using BOLA utility criteria var score = (bolaState.Vp * (bolaState.utilities[quality] + bolaState.gp) - effectiveBufferAfterLatency) / estimateBytesRemainingAfterLatency; for (var i = 0; i <= maxDroppedQuality; ++i) { estimateOtherBytesTotal = bytesTotal * bolaState.bitrates[i] / bolaState.bitrates[quality]; var s = (bolaState.Vp * (bolaState.utilities[i] + bolaState.gp) - effectiveBufferAfterLatency) / estimateOtherBytesTotal; if (s > score) { newQuality = i; score = s; } } } // Perform check for rebuffer avoidance - now use real buffer level as opposed to effective buffer level. var safeByteSize = bolaState.rebufferSafetyFactor * estimateThroughput * (bufferLevel - latencyS) / 8; if (newQuality === quality && estimateBytesRemainingAfterLatency > safeByteSize) { newQuality = maxDroppedQuality; } if (newQuality === quality) { // no change return switchRequest; } // newQuality < quality, we are abandoning while (newQuality > 0) { estimateOtherBytesTotal = bytesTotal * bolaState.bitrates[newQuality] / bolaState.bitrates[quality]; if (estimateOtherBytesTotal <= safeByteSize) { break; } --newQuality; } // deflate placeholder buffer - we want to be conservative after abandoning var wantBufferLevel = NaN; if (newQuality > 0) { // deflate to point where score for newQuality is just getting better than for (newQuality - 1) var u = bolaState.utilities[newQuality]; var u1 = bolaState.utilities[newQuality - 1]; var s = bolaState.bitrates[newQuality]; var s1 = bolaState.bitrates[newQuality - 1]; wantBufferLevel = bolaState.Vp * ((s * u1 - s1 * u) / (s - s1) + bolaState.gp); } else { // deflate to point where score for (newQuality + 1) is just getting better than for newQuality var u = bolaState.utilities[0]; var u1 = bolaState.utilities[1]; var s = bolaState.bitrates[0]; var s1 = bolaState.bitrates[1]; wantBufferLevel = bolaState.Vp * ((s * u1 - s1 * u) / (s - s1) + bolaState.gp); // then reduce one fragment duration to be conservative wantBufferLevel -= durationS; } if (effectiveBufferLevel > wantBufferLevel) { bolaState.placeholderBuffer = wantBufferLevel - bufferLevel; if (bolaState.placeholderBuffer < 0) bolaState.placeholderBuffer = 0; } bolaState.lastQuality = newQuality; metricsModel.updateBolaState(mediaType, bolaState); if (_BolaRule2['default'].BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaAbandonRule abandon to ' + newQuality + ' - ' + diagnosticMessage); rememberAbandon(mediaType, index, quality); switchRequest.value = newQuality; switchRequest.reason.state = bolaState.state; switchRequest.reason.throughput = estimateThroughput; switchRequest.reason.bufferLevel = bufferLevel; // following entries used for tuning algorithm switchRequest.reason.bytesLoaded = request.bytesLoaded; switchRequest.reason.bytesTotal = request.bytesTotal; switchRequest.reason.elapsedTimeMs = elapsedTimeMs; return switchRequest; } function reset() { abandonDict = {}; } instance = { shouldAbandon: shouldAbandon, reset: reset }; setup(); return instance; } BolaAbandonRule.__dashjs_factory_name = 'BolaAbandonRule'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(BolaAbandonRule); module.exports = exports['default']; },{"../../../core/Debug":13,"../../../core/FactoryMaker":15,"../../models/MediaPlayerModel":107,"../SwitchRequest":137,"./BolaRule":142}],142:[function(require,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 _SwitchRequest = require('../SwitchRequest'); var _SwitchRequest2 = _interopRequireDefault(_SwitchRequest); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _modelsMediaPlayerModel = require('../../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _controllersPlaybackController = require('../../controllers/PlaybackController'); var _controllersPlaybackController2 = _interopRequireDefault(_controllersPlaybackController); var _voMetricsHTTPRequest = require('../../vo/metrics/HTTPRequest'); var _dashDashAdapter = require('../../../dash/DashAdapter'); var _dashDashAdapter2 = _interopRequireDefault(_dashDashAdapter); var _coreEventBus = require('../../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreDebug = require('../../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); // BOLA_STATE_ONE_BITRATE : If there is only one bitrate (or initialization failed), always return NO_CHANGE. // BOLA_STATE_STARTUP : Set placeholder buffer such that we download fragments at most recently measured throughput. // 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_STEADY = 2; var BOLA_DEBUG = false; // TODO: remove var MINIMUM_BUFFER_S = 10; // BOLA should never add artificial delays if buffer is less than MINIMUM_BUFFER_S. var BUFFER_TARGET_S = 30; // If Schedule Controller does not allow buffer level to reach BUFFER_TARGET_S, this can be a placeholder buffer level. var REBUFFER_SAFETY_FACTOR = 0.5; // Used when buffer level is dangerously low, might happen often in live streaming. function BolaRule(config) { var AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_LIVE = 2; var AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD = 3; var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var dashMetrics = config.dashMetrics; var metricsModel = config.metricsModel; var eventBus = (0, _coreEventBus2['default'])(context).getInstance(); var instance = undefined, lastCallTimeDict = undefined, lastFragmentLoadedDict = undefined, lastFragmentWasSwitchDict = undefined, eventMediaTypes = undefined, mediaPlayerModel = undefined, playbackController = undefined, adapter = undefined; function setup() { lastCallTimeDict = {}; lastFragmentLoadedDict = {}; lastFragmentWasSwitchDict = {}; eventMediaTypes = []; mediaPlayerModel = (0, _modelsMediaPlayerModel2['default'])(context).getInstance(); playbackController = (0, _controllersPlaybackController2['default'])(context).getInstance(); adapter = (0, _dashDashAdapter2['default'])(context).getInstance(); eventBus.on(_coreEventsEvents2['default'].BUFFER_EMPTY, onBufferEmpty, instance); eventBus.on(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, instance); eventBus.on(_coreEventsEvents2['default'].PERIOD_SWITCH_STARTED, onPeriodSwitchStarted, instance); eventBus.on(_coreEventsEvents2['default'].MEDIA_FRAGMENT_LOADED, onMediaFragmentLoaded, instance); } function utilitiesFromBitrates(bitrates) { return bitrates.map(function (b) { return Math.log(b); }); // no need to worry about offset, any offset will be compensated for by gp } // NOTE: in live streaming, the real buffer level can drop below minimumBufferS, but bola should not stick to lowest bitrate by using a placeholder buffer level function calculateParameters(minimumBufferS, bufferTargetS, bitrates, utilities) { var highestUtilityIndex = NaN; if (!utilities) { utilities = utilitiesFromBitrates(bitrates); highestUtilityIndex = utilities.length - 1; } else { highestUtilityIndex = 0; utilities.forEach(function (u, i) { if (u > utilities[highestUtilityIndex]) highestUtilityIndex = i; }); } if (highestUtilityIndex === 0) { // if highestUtilityIndex === 0, then always use lowest bitrate return null; } // TODO: Investigate if following can be better if utilities are not the default Math.log utilities. // If using Math.log utilities, we can choose Vp and gp to always prefer bitrates[0] at minimumBufferS and bitrates[max] at bufferTargetS. // (Vp * (utility + gp) - bufferLevel) / bitrate has the maxima described when: // Vp * (utilities[0] + gp - 1) = minimumBufferS and Vp * (utilities[max] + gp - 1) = bufferTargetS // giving: var gp = 1 - utilities[0] + (utilities[highestUtilityIndex] - utilities[0]) / (bufferTargetS / minimumBufferS - 1); var Vp = minimumBufferS / (utilities[0] + gp - 1); return { utilities: utilities, gp: gp, Vp: Vp }; } function calculateInitialState(rulesContext) { var initialState = {}; var mediaInfo = rulesContext.getMediaInfo(); 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 bitrates = mediaInfo.bitrateList.map(function (b) { return b.bandwidth; }); var params = calculateParameters(MINIMUM_BUFFER_S, BUFFER_TARGET_S, bitrates, null); if (params === null) { // The best soloution is to always use the lowest bitrate... initialState.state = BOLA_STATE_ONE_BITRATE; return initialState; } initialState.state = BOLA_STATE_STARTUP; initialState.bitrates = bitrates; initialState.utilities = params.utilities; initialState.Vp = params.Vp; initialState.gp = params.gp; initialState.isDynamic = isDynamic; initialState.movieDuration = duration; initialState.fragmentDuration = fragmentDuration; initialState.bandwidthSafetyFactor = mediaPlayerModel.getBandwidthSafetyFactor(); initialState.rebufferSafetyFactor = REBUFFER_SAFETY_FACTOR; initialState.bufferTarget = mediaPlayerModel.getStableBufferTime(); initialState.lastQuality = 0; initialState.placeholderBuffer = 0; initialState.throughputCount = isDynamic ? AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_LIVE : AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD; if (BOLA_DEBUG) { var info = ''; for (var i = 0; i < bitrates.length; ++i) { var u = params.utilities[i]; var b = bitrates[i]; var th = 0; if (i > 0) { var u1 = params.utilities[i - 1]; var b1 = bitrates[i - 1]; th = params.Vp * ((u1 * b - u * b1) / (b - b1) + params.gp); } var z = params.Vp * (u + params.gp); info += '\n' + i + ':' + (0.000001 * bitrates[i]).toFixed(3) + 'Mbps ' + th.toFixed(3) + '/' + z.toFixed(3); } log('BolaDebug ' + mediaInfo.type + ' bitrates' + info); } return initialState; } function getQualityFromBufferLevel(bolaState, bufferLevel) { var bitrateCount = bolaState.bitrates.length; var quality = NaN; var score = NaN; for (var i = 0; i < bitrateCount; ++i) { var s = (bolaState.Vp * (bolaState.utilities[i] + bolaState.gp) - bufferLevel) / bolaState.bitrates[i]; if (isNaN(score) || 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 && httpRequests.length < count; --i) { var request = allHttpRequests[i]; if (request.type === _voMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE && request._tfinish && request.tresponse && request.trace) { httpRequests.push(request); } } return httpRequests; } function getRecentThroughput(metrics, count, mediaType) { // TODO: mediaType only used for debugging, remove it var lastRequests = getLastHttpRequests(metrics, count); if (lastRequests.length === 0) { return 0; } var totalInverse = 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 (prev, cur) { return prev + cur.b[0]; }, 0); if (BOLA_DEBUG) msg += ' ' + (0.000001 * downloadBits).toFixed(3) + '/' + downloadSeconds.toFixed(3) + '=' + (0.000001 * downloadBits / downloadSeconds).toFixed(3) + 'Mbps'; totalInverse += downloadSeconds / downloadBits; } if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule recent throughput = ' + (lastRequests.length / (1000000 * totalInverse)).toFixed(3) + 'Mbps:' + msg); return lastRequests.length / totalInverse; } function getQualityFromThroughput(bolaState, throughput) { // do not factor in bandwidthSafetyFactor here - it is factored at point of function invocation var q = 0; bolaState.bitrates.some(function (value, index) { if (value > throughput) { return true; } q = index; return false; }); return q; } function getPlaceholderIncrementInSeconds(metrics, mediaType) { // find out if there was delay because of // 1. lack of availability in live streaming or // 2. bufferLevel > bufferTarget or // 3. fast switching var nowMs = Date.now(); var lctMs = lastCallTimeDict[mediaType]; var wasSwitch = lastFragmentWasSwitchDict[mediaType]; var lastRequestFinishMs = NaN; lastCallTimeDict[mediaType] = nowMs; lastFragmentWasSwitchDict[mediaType] = false; if (!wasSwitch) { var lastRequests = getLastHttpRequests(metrics, 1); if (lastRequests.length > 0) { lastRequestFinishMs = lastRequests[0]._tfinish.getTime(); if (lastRequestFinishMs > nowMs) { // this shouldn't happen, try to handle gracefully lastRequestFinishMs = nowMs; } } } // return the time since the finish of the last request. // The return will be added cumulatively to the placeholder buffer, so we must be sure not to add the same delay twice. var delayMs = 0; if (wasSwitch || lctMs > lastRequestFinishMs) { delayMs = nowMs - lctMs; } else { delayMs = nowMs - lastRequestFinishMs; } if (isNaN(delayMs) || delayMs <= 0) return 0; return 0.001 * delayMs; } function onBufferEmpty() { if (BOLA_DEBUG) log('BolaDebug BUFFER_EMPTY'); // if we rebuffer, we don't want the placeholder buffer to artificially raise BOLA quality eventMediaTypes.forEach(function (mediaType) { var metrics = metricsModel.getReadOnlyMetricsFor(mediaType); if (metrics.BolaState.length !== 0) { var bolaState = metrics.BolaState[0]._s; if (bolaState.state === BOLA_STATE_STEADY) { bolaState.placeholderBuffer = 0; metricsModel.updateBolaState(mediaType, bolaState); } } }); } function onPlaybackSeeking(e) { if (BOLA_DEBUG) log('BolaDebug PLAYBACK_SEEKING ' + e.seekTime.toFixed(3)); // TODO: 1. Verify what happens if we seek mid-fragment. // TODO: 2. If e.g. we have 10s fragments and seek, we might want to download the first fragment at a lower quality to restart playback quickly. eventMediaTypes.forEach(function (mediaType) { 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); } }); lastFragmentLoadedDict = {}; lastFragmentWasSwitchDict = {}; } function onPeriodSwitchStarted() { // TODO } function onMediaFragmentLoaded(e) { if (e && e.chunk && e.chunk.mediaInfo) { var type = e.chunk.mediaInfo.type; var start = e.chunk.start; if (type !== undefined && !isNaN(start)) { if (start <= lastFragmentLoadedDict[type]) { lastFragmentWasSwitchDict[type] = true; // keep lastFragmentLoadedDict[type] e.g. last fragment start 10, switch fragment 8, last is still 10 } else { // isNaN(lastFragmentLoadedDict[type]) also falls here lastFragmentWasSwitchDict[type] = false; lastFragmentLoadedDict[type] = start; } } } } function getMaxIndex(rulesContext) { var streamProcessor = rulesContext.getStreamProcessor(); streamProcessor.getScheduleController().setTimeToLoadDelay(0); var switchRequest = (0, _SwitchRequest2['default'])(context).create(_SwitchRequest2['default'].NO_CHANGE, { name: BolaRule.__dashjs_factory_name }); 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 eventMediaTypes.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 = getRecentThroughput(metrics, initState.throughputCount, mediaType); if (initThroughput === 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'); return switchRequest; } q = getQualityFromThroughput(initState, initThroughput * initState.bandwidthSafetyFactor); initState.lastQuality = q; switchRequest.value = q; switchRequest.reason.state = initState.state; switchRequest.reason.throughput = initThroughput; } if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule quality ' + q + ' for INITIALIZE'); return switchRequest; } // initialization // 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'); return switchRequest; } var bitrates = bolaState.bitrates; var utilities = bolaState.utilities; 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; var recentThroughput = getRecentThroughput(metrics, bolaState.throughputCount, mediaType); if (bufferLevel <= 0.1) { // rebuffering occurred, reset placeholder buffer bolaState.placeholderBuffer = 0; } // find out if there was delay because of lack of availability or because buffer level > bufferTarget or because of fast switching var placeholderInc = getPlaceholderIncrementInSeconds(metrics, mediaType); if (placeholderInc > 0) { // TODO: maybe we should set some positive threshold here bolaState.placeholderBuffer += placeholderInc; } if (bolaState.placeholderBuffer < 0) { bolaState.placeholderBuffer = 0; } var effectiveBufferLevel = bufferLevel + bolaState.placeholderBuffer; var bolaQuality = getQualityFromBufferLevel(bolaState, effectiveBufferLevel); if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule bufferLevel=' + bufferLevel.toFixed(3) + '(+' + bolaState.placeholderBuffer.toFixed(3) + '=' + effectiveBufferLevel.toFixed(3) + ') recentThroughput=' + (0.000001 * recentThroughput).toFixed(3) + ' tentativeQuality=' + bolaQuality); if (bolaState.state === BOLA_STATE_STARTUP) { // in startup phase, use some throughput estimation var q = getQualityFromThroughput(bolaState, recentThroughput * bolaState.bandwidthSafetyFactor); if (bufferLevel > bolaState.fragmentDuration / REBUFFER_SAFETY_FACTOR) { // only switch to steady state if we believe we have enough buffer to not trigger quality drop to a safeBitrate bolaState.state = BOLA_STATE_STEADY; var wantEffectiveBuffer = 0; for (var i = 0; i < q; ++i) { // We want minimum effective buffer (bufferLevel + placeholderBuffer) that gives a higher score for q when compared with any other i < q. // We want // (Vp * (utilities[q] + gp) - bufferLevel) / bitrates[q] // to be >= any score for i < q. // We get score equality for q and i when: var b = bolaState.Vp * (bolaState.gp + (bitrates[q] * utilities[i] - bitrates[i] * utilities[q]) / (bitrates[q] - bitrates[i])); if (b > wantEffectiveBuffer) { wantEffectiveBuffer = b; } } if (wantEffectiveBuffer > bufferLevel) { bolaState.placeholderBuffer = wantEffectiveBuffer - bufferLevel; } } if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule quality ' + q + ' for STARTUP'); bolaState.lastQuality = q; metricsModel.updateBolaState(mediaType, bolaState); switchRequest.value = q; switchRequest.reason.state = BOLA_STATE_STARTUP; switchRequest.reason.throughput = recentThroughput; return switchRequest; } // 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. 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, recentThroughput); 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; } // We are dropping to an encoding 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. This delay will be added below. bolaQuality = q; } } // Try to make sure that we can download a chunk without rebuffering. This is especially important for live streaming. if (recentThroughput > 0) { // We can only perform this check if we have a throughput estimate. var safeBitrate = REBUFFER_SAFETY_FACTOR * recentThroughput * bufferLevel / bolaState.fragmentDuration; while (bolaQuality > 0 && bitrates[bolaQuality] > safeBitrate) { --bolaQuality; } } // We do not want to overfill buffer with low quality chunks. // Note that there will be no delay if buffer level is below MINIMUM_BUFFER_S, probably even with some margin higher than MINIMUM_BUFFER_S. var delaySeconds = 0; var wantBufferLevel = bolaState.Vp * (utilities[bolaQuality] + bolaState.gp); delaySeconds = effectiveBufferLevel - wantBufferLevel; if (delaySeconds > 0) { // First reduce placeholder buffer. // Note that this "delay" is the main mechanism of depleting placeholderBuffer - the real buffer is depleted by playback. if (delaySeconds > bolaState.placeholderBuffer) { delaySeconds -= bolaState.placeholderBuffer; bolaState.placeholderBuffer = 0; } else { bolaState.placeholderBuffer -= delaySeconds; delaySeconds = 0; } } if (delaySeconds > 0) { // After depleting all placeholder buffer, set delay. if (bolaQuality === bitrates.length - 1) { // At top quality, allow schedule controller to decide how far to fill buffer. delaySeconds = 0; } else { streamProcessor.getScheduleController().setTimeToLoadDelay(1000 * delaySeconds); } } else { delaySeconds = 0; } bolaState.lastQuality = bolaQuality; metricsModel.updateBolaState(mediaType, bolaState); switchRequest.value = bolaQuality; switchRequest.reason.state = bolaState.state; switchRequest.reason.throughput = recentThroughput; switchRequest.reason.bufferLevel = bufferLevel; if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule quality ' + bolaQuality + ' delay=' + delaySeconds.toFixed(3) + ' for STEADY'); return switchRequest; } function reset() { eventBus.off(_coreEventsEvents2['default'].BUFFER_EMPTY, onBufferEmpty, instance); eventBus.off(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, instance); eventBus.off(_coreEventsEvents2['default'].PERIOD_SWITCH_STARTED, onPeriodSwitchStarted, instance); eventBus.off(_coreEventsEvents2['default'].MEDIA_FRAGMENT_LOADED, onMediaFragmentLoaded, instance); setup(); } instance = { getMaxIndex: getMaxIndex, reset: reset }; setup(); return instance; } BolaRule.__dashjs_factory_name = 'BolaRule'; var factory = _coreFactoryMaker2['default'].getClassFactory(BolaRule); factory.BOLA_STATE_ONE_BITRATE = BOLA_STATE_ONE_BITRATE; factory.BOLA_STATE_STARTUP = BOLA_STATE_STARTUP; factory.BOLA_STATE_STEADY = BOLA_STATE_STEADY; factory.BOLA_DEBUG = BOLA_DEBUG; // TODO: remove exports['default'] = factory; module.exports = exports['default']; },{"../../../core/Debug":13,"../../../core/EventBus":14,"../../../core/FactoryMaker":15,"../../../core/events/Events":18,"../../../dash/DashAdapter":20,"../../controllers/PlaybackController":74,"../../models/MediaPlayerModel":107,"../../vo/metrics/HTTPRequest":185,"../SwitchRequest":137}],143:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreFactoryMakerJs = require('../../../core/FactoryMaker.js'); var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs); var _SwitchRequestJs = require('../SwitchRequest.js'); var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs); var _coreDebug = require('../../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); function DroppedFramesRule() { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var DROPPED_PERCENTAGE_FORBID = 0.15; var GOOD_SAMPLE_SIZE = 375; //Don't apply the rule until this many frames have been rendered(and counted under those indices). function getMaxIndex(rulesContext) { var droppedFramesHistory = rulesContext.getDroppedFramesHistory(); if (droppedFramesHistory) { var dfh = droppedFramesHistory.getFrameHistory(); var droppedFrames = 0; var totalFrames = 0; var maxIndex = _SwitchRequestJs2['default'].NO_CHANGE; for (var i = 1; i < dfh.length; i++) { //No point in measuring dropped frames for the zeroeth index. if (dfh[i]) { droppedFrames = dfh[i].droppedVideoFrames; totalFrames = dfh[i].totalVideoFrames; if (totalFrames > GOOD_SAMPLE_SIZE && droppedFrames / totalFrames > DROPPED_PERCENTAGE_FORBID) { maxIndex = i - 1; log('DroppedFramesRule, index: ' + maxIndex + ' Dropped Frames: ' + droppedFrames + ' Total Frames: ' + totalFrames); break; } } } return (0, _SwitchRequestJs2['default'])(context).create(maxIndex, { droppedFrames: droppedFrames }); } return (0, _SwitchRequestJs2['default'])(context).create(); } return { getMaxIndex: getMaxIndex }; } DroppedFramesRule.__dashjs_factory_name = 'DroppedFramesRule'; var factory = _coreFactoryMakerJs2['default'].getClassFactory(DroppedFramesRule); exports['default'] = factory; module.exports = exports['default']; },{"../../../core/Debug":13,"../../../core/FactoryMaker.js":15,"../SwitchRequest.js":137}],144:[function(require,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 _controllersBufferController = require('../../controllers/BufferController'); var _controllersBufferController2 = _interopRequireDefault(_controllersBufferController); var _coreEventBus = require('../../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _SwitchRequestJs = require('../SwitchRequest.js'); var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs); function InsufficientBufferRule(config) { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var eventBus = (0, _coreEventBus2['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(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, instance); } function getMaxIndex(rulesContext) { var now = new Date().getTime(); var mediaType = rulesContext.getMediaInfo().type; 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(); if (now - lastSwitchTime < waitToSwitchTime || lastBufferStateVO === null) { return switchRequest; } 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 === _controllersBufferController2['default'].BUFFER_EMPTY && bufferStateDict[mediaType].firstBufferLoadedEvent !== undefined) { log('Switch to index 0; buffer is empty.'); switchRequest.value = 0; switchRequest.reason = 'InsufficientBufferRule: Buffer is empty'; } lastSwitchTime = now; return switchRequest; } function setBufferInfo(type, state) { bufferStateDict[type] = bufferStateDict[type] || {}; bufferStateDict[type].state = state; if (state === _controllersBufferController2['default'].BUFFER_LOADED && !bufferStateDict[type].firstBufferLoadedEvent) { bufferStateDict[type].firstBufferLoadedEvent = true; } } function onPlaybackSeeking() { bufferStateDict = {}; } function reset() { eventBus.off(_coreEventsEvents2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, instance); bufferStateDict = {}; lastSwitchTime = 0; } instance = { getMaxIndex: getMaxIndex, reset: reset }; setup(); return instance; } InsufficientBufferRule.__dashjs_factory_name = 'InsufficientBufferRule'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(InsufficientBufferRule); module.exports = exports['default']; },{"../../../core/Debug":13,"../../../core/EventBus":14,"../../../core/FactoryMaker":15,"../../../core/events/Events":18,"../../controllers/BufferController":69,"../SwitchRequest.js":137}],145:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreFactoryMakerJs = require('../../../core/FactoryMaker.js'); var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs); var _coreDebug = require('../../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _SwitchRequestJs = require('../SwitchRequest.js'); var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs); function SwitchHistoryRule() { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; //MAX_SWITCH is the number of drops made. It doesn't consider the size of the drop. var MAX_SWITCH = 0.075; //Before this number of switch requests(no switch or actual), don't apply the rule. //must be < SwitchRequestHistory SWITCH_REQUEST_HISTORY_DEPTH to enable rule var SAMPLE_SIZE = 6; function getMaxIndex(rulesContext) { var switchRequestHistory = rulesContext.getSwitchHistory(); var switchRequests = switchRequestHistory.getSwitchRequests(); var drops = 0; var noDrops = 0; var dropSize = 0; var switchRequest = (0, _SwitchRequestJs2['default'])(context).create(); for (var i = 0; i < switchRequests.length; i++) { if (switchRequests[i] !== undefined) { drops += switchRequests[i].drops; noDrops += switchRequests[i].noDrops; dropSize += switchRequests[i].dropSize; if (drops + noDrops >= SAMPLE_SIZE && drops / noDrops > MAX_SWITCH) { switchRequest.value = i > 0 ? i - 1 : 0; switchRequest.reason = { index: switchRequest.value, drops: drops, noDrops: noDrops, dropSize: dropSize }; log('Switch history rule index: ' + switchRequest.value + ' samples: ' + (drops + noDrops) + ' drops: ' + drops); break; } } } return switchRequest; } return { getMaxIndex: getMaxIndex }; } SwitchHistoryRule.__dashjs_factory_name = 'SwitchRequest'; var factory = _coreFactoryMakerJs2['default'].getClassFactory(SwitchHistoryRule); exports['default'] = factory; module.exports = exports['default']; },{"../../../core/Debug":13,"../../../core/FactoryMaker.js":15,"../SwitchRequest.js":137}],146:[function(require,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 _controllersBufferController = require('../../controllers/BufferController'); var _controllersBufferController2 = _interopRequireDefault(_controllersBufferController); var _controllersAbrController = require('../../controllers/AbrController'); var _controllersAbrController2 = _interopRequireDefault(_controllersAbrController); var _modelsMediaPlayerModel = require('../../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _voMetricsHTTPRequest = require('../../vo/metrics/HTTPRequest'); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _SwitchRequestJs = require('../SwitchRequest.js'); var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs); function ThroughputRule(config) { var MAX_MEASUREMENTS_TO_KEEP = 20; var AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_LIVE = 3; var AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD = 4; var AVERAGE_LATENCY_SAMPLES = AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD; var CACHE_LOAD_THRESHOLD_VIDEO = 50; var CACHE_LOAD_THRESHOLD_AUDIO = 5; var CACHE_LOAD_THRESHOLD_LATENCY = 50; var THROUGHPUT_DECREASE_SCALE = 1.3; var THROUGHPUT_INCREASE_SCALE = 1.3; var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var dashMetrics = config.dashMetrics; var metricsModel = config.metricsModel; var throughputArray = undefined, latencyArray = undefined, mediaPlayerModel = undefined; function setup() { throughputArray = []; latencyArray = []; mediaPlayerModel = (0, _modelsMediaPlayerModel2['default'])(context).getInstance(); } function storeLastRequestThroughputByType(type, throughput) { throughputArray[type] = throughputArray[type] || []; throughputArray[type].push(throughput); } function storeLatency(mediaType, latency) { if (!latencyArray[mediaType]) { latencyArray[mediaType] = []; } latencyArray[mediaType].push(latency); if (latencyArray[mediaType].length > AVERAGE_LATENCY_SAMPLES) { return latencyArray[mediaType].shift(); } return undefined; } function getAverageLatency(mediaType) { var average = undefined; if (latencyArray[mediaType] && latencyArray[mediaType].length > 0) { average = latencyArray[mediaType].reduce(function (a, b) { return a + b; }) / latencyArray[mediaType].length; } return average; } function getSample(type, isDynamic) { var size = Math.min(throughputArray[type].length, isDynamic ? AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_LIVE : AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD); var sampleArray = throughputArray[type].slice(size * -1, throughputArray[type].length); if (sampleArray.length > 1) { sampleArray.reduce(function (a, b) { if (a * THROUGHPUT_INCREASE_SCALE <= b || a >= b * THROUGHPUT_DECREASE_SCALE) { size++; } return b; }); } size = Math.min(throughputArray[type].length, size); return throughputArray[type].slice(size * -1, throughputArray[type].length); } function getAverageThroughput(type, isDynamic) { var sample = getSample(type, isDynamic); var averageThroughput = 0; if (sample.length > 0) { var totalSampledValue = sample.reduce(function (a, b) { return a + b; }, 0); averageThroughput = totalSampledValue / sample.length; } if (throughputArray[type].length >= MAX_MEASUREMENTS_TO_KEEP) { throughputArray[type].shift(); } return averageThroughput / 1000 * mediaPlayerModel.getBandwidthSafetyFactor(); } function isCachedResponse(latency, downloadTime, mediaType) { var ret = false; if (latency < CACHE_LOAD_THRESHOLD_LATENCY) { ret = true; } if (!ret) { switch (mediaType) { case 'video': ret = downloadTime < CACHE_LOAD_THRESHOLD_VIDEO; break; case 'audio': ret = downloadTime < CACHE_LOAD_THRESHOLD_AUDIO; break; default: break; } } return ret; } function getMaxIndex(rulesContext) { var mediaInfo = rulesContext.getMediaInfo(); var mediaType = mediaInfo.type; 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 hasRichBuffer = rulesContext.hasRichBuffer(); var switchRequest = (0, _SwitchRequestJs2['default'])(context).create(); if (!metrics || !lastRequest || lastRequest.type !== _voMetricsHTTPRequest.HTTPRequest.MEDIA_SEGMENT_TYPE || !bufferStateVO || hasRichBuffer) { return switchRequest; } var downloadTimeInMilliseconds = undefined; var latencyTimeInMilliseconds = undefined; if (lastRequest.trace && lastRequest.trace.length) { latencyTimeInMilliseconds = lastRequest.tresponse.getTime() - lastRequest.trequest.getTime() || 1; downloadTimeInMilliseconds = lastRequest._tfinish.getTime() - lastRequest.tresponse.getTime() || 1; //Make sure never 0 we divide by this value. Avoid infinity! var bytes = lastRequest.trace.reduce(function (a, b) { return a + b.b[0]; }, 0); var lastRequestThroughput = Math.round(bytes * 8 / (downloadTimeInMilliseconds / 1000)); var throughput = undefined; var latency = undefined; //Prevent cached fragment loads from skewing the average throughput value - allow first even if cached to set allowance for ABR rules.. if (isCachedResponse(latencyTimeInMilliseconds, downloadTimeInMilliseconds, mediaType)) { if (!throughputArray[mediaType] || !latencyArray[mediaType]) { throughput = lastRequestThroughput / 1000; latency = latencyTimeInMilliseconds; } else { throughput = getAverageThroughput(mediaType, isDynamic); latency = getAverageLatency(mediaType); } } else { storeLastRequestThroughputByType(mediaType, lastRequestThroughput); throughput = getAverageThroughput(mediaType, isDynamic); storeLatency(mediaType, latencyTimeInMilliseconds); latency = getAverageLatency(mediaType, isDynamic); } abrController.setAverageThroughput(mediaType, throughput); if (abrController.getAbandonmentStateFor(mediaType) !== _controllersAbrController2['default'].ABANDON_LOAD) { if (bufferStateVO.state === _controllersBufferController2['default'].BUFFER_LOADED || isDynamic) { switchRequest.value = abrController.getQualityForBitrate(mediaInfo, throughput, latency); streamProcessor.getScheduleController().setTimeToLoadDelay(0); log('ThroughputRule requesting switch to index: ', switchRequest.value, 'type: ', mediaType, 'Average throughput', Math.round(throughput), 'kbps'); switchRequest.reason = { throughput: throughput, latency: latency }; } } } return switchRequest; } function reset() { setup(); } var instance = { getMaxIndex: getMaxIndex, reset: reset }; setup(); return instance; } ThroughputRule.__dashjs_factory_name = 'ThroughputRule'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(ThroughputRule); module.exports = exports['default']; },{"../../../core/Debug":13,"../../../core/FactoryMaker":15,"../../controllers/AbrController":66,"../../controllers/BufferController":69,"../../models/MediaPlayerModel":107,"../../vo/metrics/HTTPRequest":185,"../SwitchRequest.js":137}],147:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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'] = _coreFactoryMaker2['default'].getClassFactory(BasicSelector); module.exports = exports['default']; },{"../../../core/FactoryMaker":15}],148:[function(require,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 _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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'] = _coreFactoryMaker2['default'].getClassFactory(DVBSelector); module.exports = exports['default']; },{"../../../core/FactoryMaker":15}],149:[function(require,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 _modelsMediaPlayerModel = require('../../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _controllersPlaybackController = require('../../controllers/PlaybackController'); var _controllersPlaybackController2 = _interopRequireDefault(_controllersPlaybackController); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function BufferLevelRule(config) { 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, _modelsMediaPlayerModel2['default'])(context).getInstance(); playbackController = (0, _controllersPlaybackController2['default'])(context).getInstance(); } function execute(streamProcessor, type, videoTrackPresent) { var bufferLevel = dashMetrics.getCurrentBufferLevel(metricsModel.getReadOnlyMetricsFor(type)); return bufferLevel < getBufferTarget(streamProcessor, type, videoTrackPresent); } function getBufferTarget(streamProcessor, type, videoTrackPresent) { var bufferTarget = NaN; var representationInfo = streamProcessor.getCurrentRepresentationInfo(); if (type === 'fragmentedText') { bufferTarget = textSourceBuffer.getAllTracksAreDisabled() ? 0 : representationInfo.fragmentDuration; } else if (type === 'audio' && videoTrackPresent) { var videoBufferLevel = dashMetrics.getCurrentBufferLevel(metricsModel.getReadOnlyMetricsFor('video')); bufferTarget = Math.floor(Math.max(videoBufferLevel, representationInfo.fragmentDuration)); } else { var streamInfo = representationInfo.mediaInfo.streamInfo; var abrController = streamProcessor.getABRController(); if (abrController.isPlayingAtTopQuality(streamInfo)) { var isLongFormContent = streamInfo.manifestInfo.duration >= mediaPlayerModel.getLongFormContentDurationThreshold(); bufferTarget = isLongFormContent ? mediaPlayerModel.getBufferTimeAtTopQualityLongForm() : mediaPlayerModel.getBufferTimeAtTopQuality(); } else { bufferTarget = mediaPlayerModel.getStableBufferTime(); } } return bufferTarget; } var instance = { execute: execute, getBufferTarget: getBufferTarget }; setup(); return instance; } BufferLevelRule.__dashjs_factory_name = 'BufferLevelRule'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(BufferLevelRule); module.exports = exports['default']; },{"../../../core/FactoryMaker":15,"../../controllers/PlaybackController":74,"../../models/MediaPlayerModel":107}],150:[function(require,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 _coreDebug = require('../../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); var _coreFactoryMaker = require('../../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function NextFragmentRequestRule(config) { var context = this.context; var log = (0, _coreDebug2['default'])(context).getInstance().log; var adapter = config.adapter; var sourceBufferController = config.sourceBufferController; var textSourceBuffer = config.textSourceBuffer; function execute(streamProcessor, requestToReplace) { var representationInfo = streamProcessor.getCurrentRepresentationInfo(); var mediaInfo = representationInfo.mediaInfo; var mediaType = mediaInfo.type; var scheduleController = streamProcessor.getScheduleController(); var seekTarget = scheduleController.getSeekTarget(); var hasSeekTarget = !isNaN(seekTarget); var buffer = streamProcessor.getBuffer(); var time = hasSeekTarget ? seekTarget : adapter.getIndexHandlerTime(streamProcessor); if (isNaN(time) || mediaType === 'fragmentedText' && textSourceBuffer.getAllTracksAreDisabled()) { return null; } if (hasSeekTarget) { scheduleController.setSeekTarget(NaN); } /** * This is critical for IE/Safari/EDGE * */ if (buffer) { var range = sourceBufferController.getBufferRange(streamProcessor.getBuffer(), time); if (range !== null) { log('Prior to making a request for time, NextFragmentRequestRule is aligning index handler\'s currentTime with bufferedRange.end.', time, ' was changed to ', range.end); time = range.end; } } var request = undefined; if (requestToReplace) { time = requestToReplace.startTime + requestToReplace.duration / 2; request = adapter.getFragmentRequestForTime(streamProcessor, representationInfo, time, { timeThreshold: 0, ignoreIsFinished: true }); } else { request = adapter.getFragmentRequestForTime(streamProcessor, representationInfo, time, { keepIdx: !hasSeekTarget }); if (request && streamProcessor.getFragmentModel().isFragmentLoaded(request)) { request = adapter.getNextFragmentRequest(streamProcessor, representationInfo); } if (request) { adapter.setIndexHandlerTime(streamProcessor, request.startTime + request.duration); request.delayLoadingTime = new Date().getTime() + scheduleController.getTimeToLoadDelay(); scheduleController.setTimeToLoadDelay(0); } } return request; } var instance = { execute: execute }; return instance; } NextFragmentRequestRule.__dashjs_factory_name = 'NextFragmentRequestRule'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(NextFragmentRequestRule); module.exports = exports['default']; },{"../../../core/Debug":13,"../../../core/FactoryMaker":15}],151:[function(require,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 _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _dashModelsDashManifestModel = require('../../dash/models/DashManifestModel'); var _dashModelsDashManifestModel2 = _interopRequireDefault(_dashModelsDashManifestModel); var _controllersBlacklistController = require('../controllers/BlacklistController'); var _controllersBlacklistController2 = _interopRequireDefault(_controllersBlacklistController); var _rulesBaseUrlResolutionDVBSelector = require('../rules/baseUrlResolution/DVBSelector'); var _rulesBaseUrlResolutionDVBSelector2 = _interopRequireDefault(_rulesBaseUrlResolutionDVBSelector); var _rulesBaseUrlResolutionBasicSelector = require('../rules/baseUrlResolution/BasicSelector'); var _rulesBaseUrlResolutionBasicSelector2 = _interopRequireDefault(_rulesBaseUrlResolutionBasicSelector); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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, _coreEventBus2['default'])(context).getInstance(); var dashManifestModel = (0, _dashModelsDashManifestModel2['default'])(context).getInstance(); var instance = undefined, serviceLocationBlacklistController = undefined, basicSelector = undefined, dvbSelector = undefined, selector = undefined; function setup() { serviceLocationBlacklistController = (0, _controllersBlacklistController2['default'])(context).create({ updateEventName: _coreEventsEvents2['default'].SERVICE_LOCATION_BLACKLIST_CHANGED, loadFailedEventName: _coreEventsEvents2['default'].FRAGMENT_LOADING_COMPLETED }); basicSelector = (0, _rulesBaseUrlResolutionBasicSelector2['default'])(context).create({ blacklistController: serviceLocationBlacklistController }); dvbSelector = (0, _rulesBaseUrlResolutionDVBSelector2['default'])(context).create({ blacklistController: serviceLocationBlacklistController }); selector = basicSelector; } function setConfig(config) { if (config.selector) { selector = config.selector; } } 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(_coreEventsEvents2['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, setConfig: setConfig }; setup(); return instance; } BaseURLSelector.__dashjs_factory_name = 'BaseURLSelector'; var factory = _coreFactoryMaker2['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":14,"../../core/FactoryMaker":15,"../../core/events/Events":18,"../../dash/models/DashManifestModel":27,"../controllers/BlacklistController":68,"../rules/baseUrlResolution/BasicSelector":147,"../rules/baseUrlResolution/DVBSelector":148}],152:[function(require,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 _IsoFile = require('./IsoFile'); var _IsoFile2 = _interopRequireDefault(_IsoFile); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _codemIsoboxer = require('codem-isoboxer'); var _codemIsoboxer2 = _interopRequireDefault(_codemIsoboxer); function BoxParser() /*config*/{ var instance = undefined; var context = this.context; /** * @param {ArrayBuffer} data * @returns {IsoFile|null} * @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, _IsoFile2['default'])(context).create(); dashIsoFile.setData(parsedFile); return dashIsoFile; } instance = { parse: parse }; return instance; } BoxParser.__dashjs_factory_name = 'BoxParser'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(BoxParser); module.exports = exports['default']; },{"../../core/FactoryMaker":15,"./IsoFile":159,"codem-isoboxer":7}],153:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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) { 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'] = _coreFactoryMaker2['default'].getSingletonFactory(Capabilities); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],154:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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'] = _coreFactoryMaker2['default'].getClassFactory(CustomTimeRanges); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],155:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _modelsMediaPlayerModel = require('../models/MediaPlayerModel'); var _modelsMediaPlayerModel2 = _interopRequireDefault(_modelsMediaPlayerModel); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); 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, _coreDebug2['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, _modelsMediaPlayerModel2['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 = _coreFactoryMaker2['default'].getSingletonFactory(DOMStorage); exports['default'] = factory; module.exports = exports['default']; },{"../../core/Debug":13,"../../core/FactoryMaker":15,"../models/MediaPlayerModel":107}],156:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _voError = require('../vo/Error'); var _voError2 = _interopRequireDefault(_voError); /** * Creates an instance of an EBMLParser class which implements a large subset * of the functionality required to parse Matroska EBML * * @param {Object} config object with data member which is the buffer to parse */ function EBMLParser(config) { var instance = undefined; var data = new DataView(config.data); var pos = 0; function getPos() { return pos; } function setPos(value) { pos = value; } /** * Consumes an EBML tag from the data stream. * * @param {Object} tag to parse, A tag is an object with at least a {number} tag and * {boolean} required flag. * @param {boolean} test whether or not the function should throw if a required * tag is not found * @return {boolean} whether or not the tag was found * @throws will throw an exception if a required tag is not found and test * param is false or undefined, or if the stream is malformed. * @memberof EBMLParser */ function consumeTag(tag, test) { var found = true; var bytesConsumed = 0; var p1 = undefined; var p2 = undefined; if (test === undefined) { test = false; } if (tag.tag > 0xFFFFFF) { if (data.getUint32(pos) !== tag.tag) { found = false; } bytesConsumed = 4; } else if (tag.tag > 0xFFFF) { // 3 bytes p1 = data.getUint16(pos); p2 = data.getUint8(pos + 2); // shift p1 over a byte and add p2 if (p1 * 256 + p2 !== tag.tag) { found = false; } bytesConsumed = 3; } else if (tag.tag > 0xFF) { if (data.getUint16(pos) !== tag.tag) { found = false; } bytesConsumed = 2; } else { if (data.getUint8(pos) !== tag.tag) { found = false; } bytesConsumed = 1; } if (!found && tag.required && !test) { throw new _voError2['default']('required tag not found'); } if (found) { pos += bytesConsumed; } return found; } /** * Consumes an EBML tag from the data stream. If the tag is found then this * function will also remove the size field which follows the tag from the * data stream. * * @param {Object} tag to parse, A tag is an object with at least a {number} tag and * {boolean} required flag. * @param {boolean} test whether or not the function should throw if a required * tag is not found * @return {boolean} whether or not the tag was found * @throws will throw an exception if a required tag is not found and test * param is false or undefined, or if the stream is malformedata. * @memberof EBMLParser */ function consumeTagAndSize(tag, test) { var found = consumeTag(tag, test); if (found) { getMatroskaCodedNum(); } return found; } /** * Consumes an EBML tag from the data stream. If the tag is found then this * function will also remove the size field which follows the tag from the * data stream. It will use the value of the size field to parse a binary * field, using a parser defined in the tag itself * * @param {Object} tag to parse, A tag is an object with at least a {number} tag, * {boolean} required flag, and a parse function which takes a size parameter * @return {boolean} whether or not the tag was found * @throws will throw an exception if a required tag is not found, * or if the stream is malformed * @memberof EBMLParser */ function parseTag(tag) { var size = undefined; consumeTag(tag); size = getMatroskaCodedNum(); return instance[tag.parse](size); } /** * Consumes an EBML tag from the data stream. If the tag is found then this * function will also remove the size field which follows the tag from the * data stream. It will use the value of the size field to skip over the * entire section of EBML encapsulated by the tag. * * @param {Object} tag to parse, A tag is an object with at least a {number} tag, and * {boolean} required flag * @param {boolean} test a flag to indicate if an exception should be thrown * if a required tag is not found * @return {boolean} whether or not the tag was found * @throws will throw an exception if a required tag is not found and test is * false or undefined or if the stream is malformed * @memberof EBMLParser */ function skipOverElement(tag, test) { var found = consumeTag(tag, test); var headerSize = undefined; if (found) { headerSize = getMatroskaCodedNum(); pos += headerSize; } return found; } /** * Returns and consumes a number encoded according to the Matroska EBML * specification from the bitstream. * * @param {boolean} retainMSB whether or not to retain the Most Significant Bit (the * first 1). this is usually true when reading Tag IDs. * @return {number} the decoded number * @throws will throw an exception if the bit stream is malformed or there is * not enough data * @memberof EBMLParser */ function getMatroskaCodedNum(retainMSB) { var bytesUsed = 1; var mask = 0x80; var maxBytes = 8; var extraBytes = -1; var num = 0; var ch = data.getUint8(pos); var i = undefined; for (i = 0; i < maxBytes; i += 1) { if ((ch & mask) === mask) { num = retainMSB === undefined ? ch & ~mask : ch; extraBytes = i; break; } mask >>= 1; } for (i = 0; i < extraBytes; i += 1, bytesUsed += 1) { num = num << 8 | 0xff & data.getUint8(pos + bytesUsed); } pos += bytesUsed; return num; } /** * Returns and consumes a float from the bitstream. * * @param {number} size 4 or 8 byte floats are supported * @return {number} the decoded number * @throws will throw an exception if the bit stream is malformed or there is * not enough data * @memberof EBMLParser */ function getMatroskaFloat(size) { var outFloat = undefined; switch (size) { case 4: outFloat = data.getFloat32(pos); pos += 4; break; case 8: outFloat = data.getFloat64(pos); pos += 8; break; } return outFloat; } /** * Consumes and returns an unsigned int from the bitstream. * * @param {number} size 1 to 8 bytes * @return {number} the decoded number * @throws will throw an exception if the bit stream is malformed or there is * not enough data * @memberof EBMLParser */ function getMatroskaUint(size) { var val = 0; var i = undefined; for (i = 0; i < size; i += 1) { val <<= 8; val |= data.getUint8(pos + i) & 0xff; } pos += size; return val; } /** * Tests whether there is more data in the bitstream for parsing * * @return {boolean} whether there is more data to parse * @memberof EBMLParser */ function moreData() { return pos < data.byteLength; } instance = { getPos: getPos, setPos: setPos, consumeTag: consumeTag, consumeTagAndSize: consumeTagAndSize, parseTag: parseTag, skipOverElement: skipOverElement, getMatroskaCodedNum: getMatroskaCodedNum, getMatroskaFloat: getMatroskaFloat, getMatroskaUint: getMatroskaUint, moreData: moreData }; return instance; } EBMLParser.__dashjs_factory_name = 'EBMLParser'; exports['default'] = _coreFactoryMaker2['default'].getClassFactory(EBMLParser); module.exports = exports['default']; },{"../../core/FactoryMaker":15,"../vo/Error":168}],157:[function(require,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 _coreEventBus = require('../../core/EventBus'); var _coreEventBus2 = _interopRequireDefault(_coreEventBus); var _coreEventsEvents = require('../../core/events/Events'); var _coreEventsEvents2 = _interopRequireDefault(_coreEventsEvents); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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, _coreEventBus2['default'])(context).getInstance(); // "mediasource"|"mediakeys" function capabilityError(err) { eventBus.trigger(_coreEventsEvents2['default'].ERROR, { error: 'capability', event: err }); } // {id: "manifest"|"SIDX"|"content"|"initialization"|"xlink", url: "", request: {XMLHttpRequest instance}} function downloadError(id, url, request) { eventBus.trigger(_coreEventsEvents2['default'].ERROR, { error: 'download', event: { id: id, url: url, request: request } }); } // {message: "", id: "codec"|"parse"|"nostreams", manifest: {parsed manifest}} function manifestError(message, id, manifest, err) { eventBus.trigger(_coreEventsEvents2['default'].ERROR, { error: 'manifestError', event: { message: message, id: id, manifest: manifest, event: err } }); } // {message: '', id: 'parse', cc: ''} function timedTextError(message, id, ccContent) { eventBus.trigger(_coreEventsEvents2['default'].ERROR, { error: 'cc', event: { message: message, id: id, cc: ccContent } }); } function mediaSourceError(err) { eventBus.trigger(_coreEventsEvents2['default'].ERROR, { error: 'mediasource', event: err }); } function mediaKeySessionError(err) { eventBus.trigger(_coreEventsEvents2['default'].ERROR, { error: 'key_session', event: err }); } function mediaKeyMessageError(err) { eventBus.trigger(_coreEventsEvents2['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 = _coreFactoryMaker2['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":14,"../../core/FactoryMaker":15,"../../core/events/Events":18}],158:[function(require,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 {DataChunk} */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function InitCache() { var data = {}; function save(chunk) { var id = chunk.streamId; var type = chunk.mediaInfo.type; var quality = chunk.quality; data[id] = data[id] || {}; data[id][type] = data[id][type] || {}; data[id][type][quality] = chunk; } function extract(streamId, mediaType, quality) { return data[streamId][mediaType][quality]; } function reset() { data = {}; } var instance = { save: save, extract: extract, reset: reset }; return instance; } InitCache.__dashjs_factory_name = 'InitCache'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(InitCache); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],159:[function(require,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 _voIsoBox = require('../vo/IsoBox'); var _voIsoBox2 = _interopRequireDefault(_voIsoBox); var _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); function IsoFile() { var instance = undefined, parsedIsoFile = undefined, commonProps = undefined, sidxProps = undefined, sidxRefProps = undefined, emsgProps = undefined, mdhdProps = undefined, mfhdProps = undefined, subsProps = undefined, tfhdProps = undefined, tfdtProps = undefined, trunProps = undefined, trunSampleProps = undefined; /** * @param {string} type * @returns {IsoBox|null} * @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 {IsoBox|null} * @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' }; 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' }; subsProps = { samples_with_subsamples: 'samples_with_subsamples' }; 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 _voIsoBox2['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 'emsg': copyProps(boxData, box, emsgProps); break; case 'mdhd': copyProps(boxData, box, mdhdProps); break; case 'mfhd': copyProps(boxData, box, mfhdProps); break; case 'subs': copyProps(boxData, box, subsProps); 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'] = _coreFactoryMaker2['default'].getClassFactory(IsoFile); module.exports = exports['default']; },{"../../core/FactoryMaker":15,"../vo/IsoBox":171}],160:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); /** * * @returns {{initialize: initialize, getLiveEdge: getLiveEdge, reset: reset}|*} * @constructor */ function LiveEdgeFinder() { var instance = undefined, timelineConverter = undefined, streamProcessor = undefined; function initialize(TimelineConverter, StreamProcessor) { timelineConverter = TimelineConverter; streamProcessor = StreamProcessor; } function getLiveEdge() { var representationInfo = streamProcessor.getCurrentRepresentationInfo(); var liveEdge = representationInfo.DVRWindow.end; if (representationInfo.useCalculatedLiveEdgeTime) { liveEdge = timelineConverter.getExpectedLiveEdge(); timelineConverter.setClientTimeOffset(liveEdge - representationInfo.DVRWindow.end); } return liveEdge; } function reset() { timelineConverter = null; streamProcessor = null; } instance = { initialize: initialize, getLiveEdge: getLiveEdge, reset: reset }; return instance; } LiveEdgeFinder.__dashjs_factory_name = 'LiveEdgeFinder'; var factory = _coreFactoryMaker2['default'].getSingletonFactory(LiveEdgeFinder); exports['default'] = factory; module.exports = exports['default']; },{"../../core/FactoryMaker":15}],161:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); /** * @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} * @param {object} obj1 * @param {object} obj2 * @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'] = _coreFactoryMaker2['default'].getSingletonFactory(ObjectUtils); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],162:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); 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'] = _coreFactoryMaker2['default'].getSingletonFactory(RequestModifier); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],163:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _externalsXml2json = require('../../../externals/xml2json'); var _externalsXml2json2 = _interopRequireDefault(_externalsXml2json); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); 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, _coreDebug2['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, converter = undefined; var cueCounter = 0; // Used to give every cue a unique ID. function setConfig(config) { if (!config) return; if (config.videoModel) { videoModel = config.videoModel; } } /** * Get the begin-end interval if present, or null otherwise. * * @param {Object} element - TTML element which may have begin and end attributes */ function getInterval(element) { if (element.hasOwnProperty('begin') && element.hasOwnProperty('end')) { var beginTime = parseTimings(element.begin); var endTime = parseTimings(element.end); return [beginTime, endTime]; } else { return null; } } function getCueID() { var id = 'cue_TTML_' + cueCounter; cueCounter++; return id; } /* * Create list of intervals where spans start and end. Empty list if no times. * Clip to interval using startInterval and endInterval and add these two times. * Also support case when startInterval/endInteval not given (sideloaded file) * * @param {Array} spans - array of span elements */ function createSpanIntervalList(spans, startInterval, endInterval) { var spanChangeTimes = []; var spanChangeTimeStrings = []; var cue_intervals = []; function addSpanTime(span, name) { if (span.hasOwnProperty(name)) { var timeString = span[name]; if (spanChangeTimeStrings.indexOf(timeString) < 0) { spanChangeTimeStrings.push(timeString); } } } for (var i = 0; i < spans.length; i++) { var span = spans[i]; addSpanTime(span, 'begin'); addSpanTime(span, 'end'); } if (spanChangeTimeStrings.length === 0) { return cue_intervals; // No span timing so no intervals. } if (typeof startInterval !== 'undefined' && typeof endInterval !== 'undefined') { for (var i = 0; i < spanChangeTimeStrings.length; i++) { var changeTime = parseTimings(spanChangeTimeStrings[i]); if (startInterval < changeTime && changeTime < endInterval) { spanChangeTimes.push(changeTime); } } spanChangeTimes.push(startInterval); spanChangeTimes.push(endInterval); } else { for (var i = 0; i < spanChangeTimeStrings.length; i++) { spanChangeTimes.push(parseTimings(spanChangeTimeStrings[i])); } } spanChangeTimes.sort(function (a, b) { return a - b; }); for (var i = 0; i < spanChangeTimes.length - 1; i++) { cue_intervals.push([spanChangeTimes[i], spanChangeTimes[i + 1]]); } return cue_intervals; } function clipStartTime(startTime, intervalStart) { if (typeof startInterval !== 'undefined') { if (startTime < intervalStart) { startTime = intervalStart; } } return startTime; } function clipEndTime(endTime, intervalEnd) { if (typeof intervalEnd !== 'undefined') { if (endTime > intervalEnd) { endTime = intervalEnd; } } return endTime; } /* * Get interval from entity that has begin and end properties. * If intervalStart and intervalEnd defined, use them to clip the interval. * Return null if no overlap with interval */ function getClippedInterval(entity, intervalStart, intervalEnd) { var startTime = parseTimings(entity.begin); var endTime = parseTimings(entity.end); startTime = clipStartTime(startTime, intervalStart); endTime = clipEndTime(endTime, intervalEnd); if (typeof intervalStart !== 'undefined' && typeof intervalEnd !== 'undefined') { if (endTime < intervalStart || startTime > intervalEnd) { log('TTML: Cue ' + startTime + '-' + endTime + ' outside interval ' + intervalStart + '-' + intervalEnd); return null; } } return [startTime, endTime]; } /* * Check if entity timing has some overlap with interval */ function inIntervalOrNoTiming(entity, interval) { var inInterval = true; if (entity.hasOwnProperty('span')) { var entityInterval = getInterval(entity.span); if (entityInterval !== null) { //Timing inInterval = entityInterval[0] < interval[1] && entityInterval[1] > interval[0]; } } return inInterval; } /** * 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 {string} data - raw data received from the TextSourceBuffer * @param {number} intervalStart * @param {number} intervalEnd * @param {array} imageArray - images represented as binary strings */ function parse(data, intervalStart, intervalEnd, imageArray) { var tt = undefined, // Top element head = undefined, // head in tt body = undefined, // body in tt ttExtent = undefined, // extent attribute of tt element type = undefined, i = undefined; var errorMsg = ''; // Parse the TTML in a JSON object. ttml = converter.xml_str2json(data); if (!ttml) { throw new Error('TTML document could not be parsed'); } if (videoModel.getTTMLRenderingDiv()) { type = 'html'; } // Check the document and compare to the specification (TTML and EBU-TT-D). tt = ttml.tt; if (!tt) { throw new Error('TTML document lacks tt element'); } // Get the namespace if there is one defined in the JSON object. var ttNS = getNamespacePrefix(tt, 'http://www.w3.org/ns/ttml'); // Remove the namespace before each node if it exists: if (ttNS) { removeNamespacePrefix(tt, ttNS); } ttExtent = tt['tts:extent']; // Should check that tts is right namespace. head = tt.head; if (!head) { throw new Error('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 } var imageDataUrls = {}; if (imageArray) { for (i = 0; i < imageArray.length; i++) { var key = 'urn:mpeg:14496-30:subs:' + (i + 1).toString(); var dataUrl = 'data:image/png;base64,' + btoa(imageArray[i]); imageDataUrls[key] = dataUrl; } } if (head.metadata) { var embeddedImages = head.metadata.image_asArray; // Handle embedded images if (embeddedImages) { for (i = 0; i < embeddedImages.length; i++) { var key = '#' + embeddedImages[i]['xml:id']; var imageType = embeddedImages[i].imagetype.toLowerCase(); var dataUrl = 'data:image/' + imageType + ';base64,' + embeddedImages[i].__text; imageDataUrls[key] = dataUrl; } } } body = tt.body; if (!body) { throw new Error('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 (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 (tt.hasOwnProperty(nsttp + ':frameRate')) { tt.frameRate = parseInt(tt[nsttp + ':frameRate'], 10); } var captionArray = []; // Extract the div var divs = tt.body_asArray[0].__children; // Timing is either on div, paragraph or span level. for (var k = 0; k < divs.length; k++) { var div = divs[k].div; var divInterval = null; // This is mainly for image subtitles. if (null !== (divInterval = getInterval(div))) { // Timing on div level is not allowed by EBU-TT-D. // We only use it for SMPTE-TT image subtitle profile. // Layout should be defined by a region. Given early test material, we also support that it is on // div level var layout = undefined; if (div.region) { var region = findRegionFromID(ttmlLayout, div.region); layout = getRelativePositioning(region, ttExtent); } if (!layout) { layout = getRelativePositioning(div, ttExtent); } var imgKey = div['smpte:backgroundImage']; if (imgKey !== undefined && imageDataUrls[imgKey] !== undefined) { captionArray.push({ start: divInterval[0], end: divInterval[1], id: getCueID(), data: imageDataUrls[imgKey], type: 'image', layout: layout }); } continue; // Next div } var paragraphs = div.p_asArray; // Check if cues is not empty or undefined. if (divInterval === null && (!paragraphs || paragraphs.length === 0)) { errorMsg = 'TTML has div that contains no timing and no paragraphs.'; log(errorMsg); return captionArray; } for (var j2 = 0; j2 < paragraphs.length; j2++) { var paragraph = paragraphs[j2]; var spans = paragraph.span_asArray; var cueIntervals = []; // For timing, the overall goal is to find the intervals where there should be cues // The timing may either be on paragraph or span level. if (paragraph.hasOwnProperty('begin') && paragraph.hasOwnProperty('end')) { // Timing on paragraph level var clippedInterval = getClippedInterval(paragraph, intervalStart, intervalEnd); if (clippedInterval !== null) { cueIntervals.push(clippedInterval); } } else { // Timing must be on span level cueIntervals = createSpanIntervalList(spans, intervalStart, intervalEnd); } if (cueIntervals.length === 0) { errorMsg = 'TTML: Empty paragraph'; continue; // Nothing in this paragraph } var paragraphChildren = paragraph.__children; for (var i2 = 0; i2 < cueIntervals.length; i2++) { var interval = cueIntervals[i2]; var childrenInInterval = []; for (var k2 = 0; k2 < paragraphChildren.length; k2++) { var child = paragraphChildren[k2]; if (inIntervalOrNoTiming(child, interval)) { childrenInInterval.push(child); } } if (childrenInInterval.length === 0) { continue; // No children to render } if (type === 'html') { lineHeight = {}; linePadding = {}; fontSize = {}; /** * Find the region defined for the cue. */ // properties to be put in the "captionRegion" HTML element. var cueRegionProperties = constructCueRegion(paragraph, div, cellUnit); /** * Find the style defined for the cue. */ // properties to be put in the "paragraph" HTML element. var cueStyleProperties = constructCueStyle(paragraph, 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; // Create a 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(childrenInInterval, 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 = getCueID(); 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: interval[0], end: interval[1], type: 'html', cueHTMLElement: finalCue, regions: regions, regionID: regionID, cueID: finalCue.id, videoHeight: videoHeight, videoWidth: videoWidth, cellResolution: cellResolution, fontSize: fontSize || { defaultFontSize: '100' }, lineHeight: lineHeight, linePadding: linePadding }); } else { var text = ''; var textElements = childrenInInterval; 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: interval[0], end: interval[1], data: text, type: 'text' }); } } } } if (errorMsg !== '') { log(errorMsg); } if (captionArray.length > 0) { return captionArray; } else { // This seems too strong given that there are segments with no TTML subtitles throw new Error(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': 'auto;', 'left': 'auto;', '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;' }; converter = new _externalsXml2json2['default']({ escapeMode: false, attributePrefix: '', arrayAccessForm: 'property', emptyNodeForm: 'object', stripWhitespaces: false, enableToStringFunc: false, matchers: [] }); } 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 fullNsPrefix = nsPrefix + ':'; var nsPrefixPos = key.indexOf(fullNsPrefix); if (nsPrefixPos >= 0) { var newKey = key.slice(nsPrefixPos + fullNsPrefix.length); 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 + ');'; } // Convert an RGBA value written in TTML rgba(v,v,v,a => 0 to 255) to CSS rgba(v,v,v,a => 0 to 1). function convertAlphaValue(rgbaTTML) { var rgba = undefined, alpha = undefined, resu = undefined; rgba = rgbaTTML.replace(/^(rgb|rgba)\(/, '').replace(/\)$/, '').replace(/\s/g, '').split(','); alpha = parseInt(rgba[rgba.length - 1], 10) / 255; resu = 'rgba(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ',' + alpha + ');'; return resu; } // 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); } function getSizeTypeAndDefinition(cueStyleElement) { var returnTab = new Array(2); var startRef = cueStyleElement.indexOf(':') === -1 ? 0 : cueStyleElement.indexOf(':'); var endRef = undefined; if (cueStyleElement.indexOf('%') === -1) { if (cueStyleElement.indexOf('c') === -1) { if (cueStyleElement.indexOf('p') === -1) { returnTab[0] = returnTab[1] = null; } else { returnTab[0] = 'p'; endRef = cueStyleElement.indexOf('p'); } } else { returnTab[0] = 'c'; endRef = cueStyleElement.indexOf('c'); } } else { returnTab[0] = '%'; endRef = cueStyleElement.indexOf('%'); } returnTab[1] = cueStyleElement.slice(startRef, endRef); return returnTab; } /** * 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, includeRegionStyles) { var properties = []; var valueFtSizeInPx, valueLHSizeInPx; // 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 fontSizeTab = getSizeTypeAndDefinition(cueStyle['font-size']); var valueFtSize = parseFloat(fontSizeTab[1]); if ('id' in cueStyle) { fontSize[cueStyle.id] = fontSizeTab; } if (fontSizeTab[0] === '%') { valueFtSizeInPx = valueFtSize / 100 * cellUnit[1] + 'px;'; } else if (fontSizeTab[0] === 'c') { valueFtSizeInPx = valueFtSize * 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 LineHeightTab = getSizeTypeAndDefinition(cueStyle['line-height']); var valueLHSize = parseFloat(LineHeightTab[1]); if ('id' in cueStyle) { lineHeight[cueStyle.id] = LineHeightTab; } if (LineHeightTab[0] === '%') { valueLHSizeInPx = valueLHSize / 100 * cellUnit[1] + 'px;'; } else if (LineHeightTab[0] === 'c') { valueLHSizeInPx = valueLHSize * 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']); } else if (cueStyle['background-color'].indexOf('rgba') > -1) { rgbaValue = convertAlphaValue(cueStyle['background-color']); } else { rgbaValue = cueStyle['background-color'] + ';'; } properties.push('background-color: ' + rgbaValue); } // 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); } else if (cueStyle.color.indexOf('rgba') > -1) { rgbaValue = convertAlphaValue(cueStyle.color); } else { rgbaValue = cueStyle.color + ';'; } properties.push('color: ' + rgbaValue); } // 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'] + ';'); } if (includeRegionStyles) { properties = properties.concat(processRegion(cueStyle, cellUnit)); } // 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, includeRegionStyles) { 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, includeRegionStyles); styles = styles.concat(stylesFromId); } }); return styles; } // Calculate relative left, top, width, height from extent and origin in percent. // Return object with {left, top, width, height} as numbers in percent or null. function getRelativePositioning(element, ttExtent) { var pairRe = /([\d\.]+)(%|px)\s+([\d\.]+)(%|px)/; if ('tts:extent' in element && 'tts:origin' in element) { var extentParts = pairRe.exec(element['tts:extent']); var originParts = pairRe.exec(element['tts:origin']); if (extentParts === null || originParts === null) { log('Bad extent or origin: ' + element['tts:extent'] + ' ' + element['tts:origin']); return null; } var width = parseFloat(extentParts[1]); var height = parseFloat(extentParts[3]); var left = parseFloat(originParts[1]); var _top = parseFloat(originParts[3]); if (ttExtent) { // Should give overall scale in pixels var ttExtentParts = pairRe.exec(ttExtent); if (ttExtentParts === null || ttExtentParts[2] !== 'px' || ttExtentParts[4] !== 'px') { log('Bad tt.extent: ' + ttExtent); return null; } var exWidth = parseFloat(ttExtentParts[1]); var exHeight = parseFloat(ttExtentParts[3]); if (extentParts[2] === 'px') { width = width / exWidth * 100; } if (extentParts[4] === 'px') { height = height / exHeight * 100; } if (originParts[2] === 'px') { left = left / exWidth * 100; } if (originParts[4] === 'px') { _top = _top / exHeight * 100; } } return { 'left': left, 'top': _top, 'width': width, 'height': height }; } else { return null; } } /** * 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, true); 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 = '
'; var clonePropertyString = '' + 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'] = _coreFactoryMaker2['default'].getSingletonFactory(TTMLParser); module.exports = exports['default']; },{"../../../externals/xml2json":11,"../../core/Debug":13,"../../core/FactoryMaker":15}],164:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); /** * @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 resolveFunction = undefined; var schemeRegex = /^[a-z][a-z0-9+\-.]*:/i; var httpUrlRegex = /^https?:\/\//i; var originRegex = /^([a-z][a-z0-9+\-.]*:\/\/[^\/]+)\/?/i; /** * Resolves a url given an optional base url * Uses window.URL to do the resolution. * * @param {string} url * @param {string} [baseUrl] * @return {string} * @memberof module:URLUtils * @instance * @private */ var nativeURLResolver = function nativeURLResolver(url, baseUrl) { try { // this will throw if baseurl is undefined, invalid etc return new window.URL(url, baseUrl).toString(); } catch (e) { return url; } }; /** * Resolves a url given an optional base url * Does not resolve ./, ../ etc but will do enough to construct something * which will satisfy XHR etc when window.URL is not available ie * IE11/node etc. * * @param {string} url * @param {string} [baseUrl] * @return {string} * @memberof module:URLUtils * @instance * @private */ var dumbURLResolver = function dumbURLResolver(url, baseUrl) { var baseUrlParseFunc = parseBaseUrl; if (!baseUrl) { return url; } if (!isRelative(url)) { return url; } if (isPathAbsolute(url)) { baseUrlParseFunc = parseOrigin; } var base = baseUrlParseFunc(baseUrl); var joinChar = base.charAt(base.length - 1) !== '/' && url.charAt(0) !== '/' ? '/' : ''; return [base, url].join(joinChar); }; function setup() { try { var u = new window.URL('x', 'http://y'); //jshint ignore:line resolveFunction = nativeURLResolver; } catch (e) { // must be IE11/Node etc } finally { resolveFunction = resolveFunction || dumbURLResolver; } } /** * Returns a string that contains the Base URL of a URL, if determinable. * @param {string} url - full url * @return {string} * @memberof module:URLUtils * @instance */ function parseBaseUrl(url) { var slashIndex = url.indexOf('/'); var lastSlashIndex = url.lastIndexOf('/'); if (slashIndex !== -1) { // if there is only '//' if (lastSlashIndex === slashIndex + 1) { return url; } if (url.indexOf('?') !== -1) { url = url.substring(0, url.indexOf('?')); } return url.substring(0, lastSlashIndex + 1); } return ''; } /** * Returns a string that contains the scheme and origin of a URL, * if determinable. * @param {string} url - full url * @return {string} * @memberof module:URLUtils * @instance */ function parseOrigin(url) { var matches = url.match(originRegex); if (matches) { return matches[1]; } return ''; } /** * Determines whether the url is relative. * @return {bool} * @param {string} url * @memberof module:URLUtils * @instance */ function isRelative(url) { return !schemeRegex.test(url); } /** * Determines whether the url is path-absolute. * @return {bool} * @param {string} url * @memberof module:URLUtils * @instance */ function isPathAbsolute(url) { return isRelative(url) && url.charAt(0) === '/'; } /** * Determines whether the url is an HTTP-URL as defined in ISO/IEC * 23009-1:2014 3.1.15. ie URL with a fixed scheme of http or https * @return {bool} * @param {string} url * @memberof module:URLUtils * @instance */ function isHTTPURL(url) { return httpUrlRegex.test(url); } /** * Resolves a url given an optional base url * @return {string} * @param {string} url * @param {string} [baseUrl] * @memberof module:URLUtils * @instance */ function resolve(url, baseUrl) { return resolveFunction(url, baseUrl); } setup(); var instance = { parseBaseUrl: parseBaseUrl, parseOrigin: parseOrigin, isRelative: isRelative, isPathAbsolute: isPathAbsolute, isHTTPURL: isHTTPURL, resolve: resolve }; return instance; } URLUtils.__dashjs_factory_name = 'URLUtils'; exports['default'] = _coreFactoryMaker2['default'].getSingletonFactory(URLUtils); module.exports = exports['default']; },{"../../core/FactoryMaker":15}],165:[function(require,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 _coreFactoryMaker = require('../../core/FactoryMaker'); var _coreFactoryMaker2 = _interopRequireDefault(_coreFactoryMaker); var _coreDebug = require('../../core/Debug'); var _coreDebug2 = _interopRequireDefault(_coreDebug); function VTTParser() { var context = this.context; var log = (0, _coreDebug2['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'] = _coreFactoryMaker2['default'].getSingletonFactory(VTTParser); module.exports = exports['default']; },{"../../core/Debug":13,"../../core/FactoryMaker":15}],166:[function(require,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"]; },{}],167:[function(require,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"]; },{}],168:[function(require,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"]; },{}],169:[function(require,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']; },{}],170:[function(require,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 _FragmentRequest2 = require('./FragmentRequest'); var _FragmentRequest3 = _interopRequireDefault(_FragmentRequest2); 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; })(_FragmentRequest3['default']); exports['default'] = HeadRequest; module.exports = exports['default']; },{"./FragmentRequest":169}],171:[function(require,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"]; },{}],172:[function(require,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"]; },{}],173:[function(require,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"]; },{}],174:[function(require,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"]; },{}],175:[function(require,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"]; },{}],176:[function(require,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 _FragmentRequest2 = require('./FragmentRequest'); var _FragmentRequest3 = _interopRequireDefault(_FragmentRequest2); 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; })(_FragmentRequest3['default']); exports['default'] = TextRequest; module.exports = exports['default']; },{"./FragmentRequest":169}],177:[function(require,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"]; },{}],178:[function(require,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"]; },{}],179:[function(require,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"]; },{}],180:[function(require,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"]; },{}],181:[function(require,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"]; },{}],182:[function(require,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 _controllersBufferController = require('../../controllers/BufferController'); var _controllersBufferController2 = _interopRequireDefault(_controllersBufferController); /** * @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 = _controllersBufferController2['default'].BUFFER_EMPTY; }; exports['default'] = BufferState; module.exports = exports['default']; },{"../../controllers/BufferController":69}],183:[function(require,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"]; },{}],184:[function(require,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"]; },{}],185:[function(require,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. */ /** * @classdesc 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. */ '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 = /** * @class */ 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; } /** * @classdesc This Object holds reference to the progress of the HTTPRequest. */ ; var HTTPRequestTrace = /** * @class */ function HTTPRequestTrace() { _classCallCheck(this, HTTPRequestTrace); /** * 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 = []; }; 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.HTTPRequest = HTTPRequest; exports.HTTPRequestTrace = HTTPRequestTrace; },{}],186:[function(require,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. */ /** * @classdesc This Object holds reference to the manifest update information. */ "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 = /** * @class */ 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 = []; } /** * @classdesc This Object holds reference to the current period's stream information when the manifest was updated. */ ; var ManifestUpdateStreamInfo = /** * @class */ function ManifestUpdateStreamInfo() { _classCallCheck(this, ManifestUpdateStreamInfo); /** * Stream@id * @public */ this.id = null; /** * Period Index * @public */ this.index = null; /** * Stream@start * @public */ this.start = null; /** * Stream@duration * @public */ this.duration = null; } /** * @classdesc This Object holds reference to the current representation's info when the manifest was updated. */ ; var ManifestUpdateTrackInfo = /** * @class */ function ManifestUpdateTrackInfo() { _classCallCheck(this, ManifestUpdateTrackInfo); /** * 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; }; exports.ManifestUpdate = ManifestUpdate; exports.ManifestUpdateStreamInfo = ManifestUpdateStreamInfo; exports.ManifestUpdateTrackInfo = ManifestUpdateTrackInfo; },{}],187:[function(require,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. */ /** * @classdesc a PlayList from ISO23009-1 Annex D, this Object holds reference to the playback session information */ '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 = /** * @class */ 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 = []; } /* 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'; /** * @classdesc a PlayList.Trace from ISO23009-1 Annex D */ var PlayListTrace = /** * @class */ function PlayListTrace() { _classCallCheck(this, PlayListTrace); /** * 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; }; PlayListTrace.REPRESENTATION_SWITCH_STOP_REASON = 'representation_switch'; PlayListTrace.REBUFFERING_REASON = 'rebuffering'; PlayListTrace.USER_REQUEST_STOP_REASON = 'user_request'; PlayListTrace.END_OF_PERIOD_STOP_REASON = 'end_of_period'; PlayListTrace.END_OF_CONTENT_STOP_REASON = 'end_of_content'; PlayListTrace.METRICS_COLLECTION_STOP_REASON = 'metrics_collection_end'; PlayListTrace.FAILURE_STOP_REASON = 'failure'; exports.PlayList = PlayList; exports.PlayListTrace = PlayListTrace; },{}],188:[function(require,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"]; },{}],189:[function(require,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"]; },{}],190:[function(require,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"]; },{}],191:[function(require,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"]; },{}],192:[function(require,module,exports){ var isFunction = require('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":195}],193:[function(require,module,exports){ (function (global){ var topLevel = typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : {} var minDoc = require('min-document'); if (typeof document !== 'undefined') { module.exports = document; } else { var 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":6}],194:[function(require,module,exports){ (function (global){ if (typeof window !== "undefined") { module.exports = window; } else if (typeof global !== "undefined") { module.exports = global; } else if (typeof self !== "undefined"){ module.exports = self; } else { module.exports = {}; } }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],195:[function(require,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)) }; },{}],196:[function(require,module,exports){ 'use strict'; var _lineStream = require('./line-stream'); var _lineStream2 = _interopRequireDefault(_lineStream); var _parseStream = require('./parse-stream'); var _parseStream2 = _interopRequireDefault(_parseStream); var _parser = require('./parser'); var _parser2 = _interopRequireDefault(_parser); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } module.exports = { LineStream: _lineStream2['default'], ParseStream: _parseStream2['default'], Parser: _parser2['default'] }; /** * @file m3u8/index.js * * Utilities for parsing M3U8 files. If the entire manifest is available, * `Parser` will create an object representation with enough detail for managing * playback. `ParseStream` and `LineStream` are lower-level parsing primitives * that do not assume the entirety of the manifest is ready and expose a * ReadableStream-like interface. */ },{"./line-stream":197,"./parse-stream":198,"./parser":199}],197:[function(require,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 _stream = require('./stream'); var _stream2 = _interopRequireDefault(_stream); 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; } /** * @file m3u8/line-stream.js */ /** * A stream that buffers string input and generates a `data` event for each * line. * * @class LineStream * @extends Stream */ var LineStream = function (_Stream) { _inherits(LineStream, _Stream); function LineStream() { _classCallCheck(this, LineStream); var _this = _possibleConstructorReturn(this, (LineStream.__proto__ || Object.getPrototypeOf(LineStream)).call(this)); _this.buffer = ''; return _this; } /** * Add new data to be parsed. * * @param {String} data the text to process */ _createClass(LineStream, [{ key: 'push', value: function push(data) { var nextNewline = void 0; this.buffer += data; nextNewline = this.buffer.indexOf('\n'); for (; nextNewline > -1; nextNewline = this.buffer.indexOf('\n')) { this.trigger('data', this.buffer.substring(0, nextNewline)); this.buffer = this.buffer.substring(nextNewline + 1); } } }]); return LineStream; }(_stream2['default']); exports['default'] = LineStream; },{"./stream":200}],198:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); 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 _stream = require('./stream'); var _stream2 = _interopRequireDefault(_stream); 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; } /** * @file m3u8/parse-stream.js */ /** * "forgiving" attribute list psuedo-grammar: * attributes -> keyvalue (',' keyvalue)* * keyvalue -> key '=' value * key -> [^=]* * value -> '"' [^"]* '"' | [^,]* */ var attributeSeparator = function attributeSeparator() { var key = '[^=]*'; var value = '"[^"]*"|[^,]*'; var keyvalue = '(?:' + key + ')=(?:' + value + ')'; return new RegExp('(?:^|,)(' + keyvalue + ')'); }; /** * Parse attributes from a line given the seperator * * @param {String} attributes the attibute line to parse */ var parseAttributes = function parseAttributes(attributes) { // split the string using attributes as the separator var attrs = attributes.split(attributeSeparator()); var result = {}; var i = attrs.length; var attr = void 0; while (i--) { // filter out unmatched portions of the string if (attrs[i] === '') { continue; } // split the key and value attr = /([^=]*)=(.*)/.exec(attrs[i]).slice(1); // trim whitespace and remove optional quotes around the value attr[0] = attr[0].replace(/^\s+|\s+$/g, ''); attr[1] = attr[1].replace(/^\s+|\s+$/g, ''); attr[1] = attr[1].replace(/^['"](.*)['"]$/g, '$1'); result[attr[0]] = attr[1]; } return result; }; /** * A line-level M3U8 parser event stream. It expects to receive input one * line at a time and performs a context-free parse of its contents. A stream * interpretation of a manifest can be useful if the manifest is expected to * be too large to fit comfortably into memory or the entirety of the input * is not immediately available. Otherwise, it's probably much easier to work * with a regular `Parser` object. * * Produces `data` events with an object that captures the parser's * interpretation of the input. That object has a property `tag` that is one * of `uri`, `comment`, or `tag`. URIs only have a single additional * property, `line`, which captures the entirety of the input without * interpretation. Comments similarly have a single additional property * `text` which is the input without the leading `#`. * * Tags always have a property `tagType` which is the lower-cased version of * the M3U8 directive without the `#EXT` or `#EXT-X-` prefix. For instance, * `#EXT-X-MEDIA-SEQUENCE` becomes `media-sequence` when parsed. Unrecognized * tags are given the tag type `unknown` and a single additional property * `data` with the remainder of the input. * * @class ParseStream * @extends Stream */ var ParseStream = function (_Stream) { _inherits(ParseStream, _Stream); function ParseStream() { _classCallCheck(this, ParseStream); return _possibleConstructorReturn(this, (ParseStream.__proto__ || Object.getPrototypeOf(ParseStream)).call(this)); } /** * Parses an additional line of input. * * @param {String} line a single line of an M3U8 file to parse */ _createClass(ParseStream, [{ key: 'push', value: function push(line) { var match = void 0; var event = void 0; // strip whitespace line = line.replace(/^[\u0000\s]+|[\u0000\s]+$/g, ''); if (line.length === 0) { // ignore empty lines return; } // URIs if (line[0] !== '#') { this.trigger('data', { type: 'uri', uri: line }); return; } // Comments if (line.indexOf('#EXT') !== 0) { this.trigger('data', { type: 'comment', text: line.slice(1) }); return; } // strip off any carriage returns here so the regex matching // doesn't have to account for them. line = line.replace('\r', ''); // Tags match = /^#EXTM3U/.exec(line); if (match) { this.trigger('data', { type: 'tag', tagType: 'm3u' }); return; } match = /^#EXTINF:?([0-9\.]*)?,?(.*)?$/.exec(line); if (match) { event = { type: 'tag', tagType: 'inf' }; if (match[1]) { event.duration = parseFloat(match[1]); } if (match[2]) { event.title = match[2]; } this.trigger('data', event); return; } match = /^#EXT-X-TARGETDURATION:?([0-9.]*)?/.exec(line); if (match) { event = { type: 'tag', tagType: 'targetduration' }; if (match[1]) { event.duration = parseInt(match[1], 10); } this.trigger('data', event); return; } match = /^#ZEN-TOTAL-DURATION:?([0-9.]*)?/.exec(line); if (match) { event = { type: 'tag', tagType: 'totalduration' }; if (match[1]) { event.duration = parseInt(match[1], 10); } this.trigger('data', event); return; } match = /^#EXT-X-VERSION:?([0-9.]*)?/.exec(line); if (match) { event = { type: 'tag', tagType: 'version' }; if (match[1]) { event.version = parseInt(match[1], 10); } this.trigger('data', event); return; } match = /^#EXT-X-MEDIA-SEQUENCE:?(\-?[0-9.]*)?/.exec(line); if (match) { event = { type: 'tag', tagType: 'media-sequence' }; if (match[1]) { event.number = parseInt(match[1], 10); } this.trigger('data', event); return; } match = /^#EXT-X-DISCONTINUITY-SEQUENCE:?(\-?[0-9.]*)?/.exec(line); if (match) { event = { type: 'tag', tagType: 'discontinuity-sequence' }; if (match[1]) { event.number = parseInt(match[1], 10); } this.trigger('data', event); return; } match = /^#EXT-X-PLAYLIST-TYPE:?(.*)?$/.exec(line); if (match) { event = { type: 'tag', tagType: 'playlist-type' }; if (match[1]) { event.playlistType = match[1]; } this.trigger('data', event); return; } match = /^#EXT-X-BYTERANGE:?([0-9.]*)?@?([0-9.]*)?/.exec(line); if (match) { event = { type: 'tag', tagType: 'byterange' }; if (match[1]) { event.length = parseInt(match[1], 10); } if (match[2]) { event.offset = parseInt(match[2], 10); } this.trigger('data', event); return; } match = /^#EXT-X-ALLOW-CACHE:?(YES|NO)?/.exec(line); if (match) { event = { type: 'tag', tagType: 'allow-cache' }; if (match[1]) { event.allowed = !/NO/.test(match[1]); } this.trigger('data', event); return; } match = /^#EXT-X-MAP:?(.*)$/.exec(line); if (match) { event = { type: 'tag', tagType: 'map' }; if (match[1]) { var attributes = parseAttributes(match[1]); if (attributes.URI) { event.uri = attributes.URI; } if (attributes.BYTERANGE) { var _attributes$BYTERANGE = attributes.BYTERANGE.split('@'), _attributes$BYTERANGE2 = _slicedToArray(_attributes$BYTERANGE, 2), length = _attributes$BYTERANGE2[0], offset = _attributes$BYTERANGE2[1]; event.byterange = {}; if (length) { event.byterange.length = parseInt(length, 10); } if (offset) { event.byterange.offset = parseInt(offset, 10); } } } this.trigger('data', event); return; } match = /^#EXT-X-STREAM-INF:?(.*)$/.exec(line); if (match) { event = { type: 'tag', tagType: 'stream-inf' }; if (match[1]) { event.attributes = parseAttributes(match[1]); if (event.attributes.RESOLUTION) { var split = event.attributes.RESOLUTION.split('x'); var resolution = {}; if (split[0]) { resolution.width = parseInt(split[0], 10); } if (split[1]) { resolution.height = parseInt(split[1], 10); } event.attributes.RESOLUTION = resolution; } if (event.attributes.BANDWIDTH) { event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10); } if (event.attributes['PROGRAM-ID']) { event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10); } } this.trigger('data', event); return; } match = /^#EXT-X-MEDIA:?(.*)$/.exec(line); if (match) { event = { type: 'tag', tagType: 'media' }; if (match[1]) { event.attributes = parseAttributes(match[1]); } this.trigger('data', event); return; } match = /^#EXT-X-ENDLIST/.exec(line); if (match) { this.trigger('data', { type: 'tag', tagType: 'endlist' }); return; } match = /^#EXT-X-DISCONTINUITY/.exec(line); if (match) { this.trigger('data', { type: 'tag', tagType: 'discontinuity' }); return; } match = /^#EXT-X-PROGRAM-DATE-TIME:?(.*)$/.exec(line); if (match) { event = { type: 'tag', tagType: 'program-date-time' }; if (match[1]) { event.dateTimeString = match[1]; event.dateTimeObject = new Date(match[1]); } this.trigger('data', event); return; } match = /^#EXT-X-KEY:?(.*)$/.exec(line); if (match) { event = { type: 'tag', tagType: 'key' }; if (match[1]) { event.attributes = parseAttributes(match[1]); // parse the IV string into a Uint32Array if (event.attributes.IV) { if (event.attributes.IV.substring(0, 2).toLowerCase() === '0x') { event.attributes.IV = event.attributes.IV.substring(2); } event.attributes.IV = event.attributes.IV.match(/.{8}/g); event.attributes.IV[0] = parseInt(event.attributes.IV[0], 16); event.attributes.IV[1] = parseInt(event.attributes.IV[1], 16); event.attributes.IV[2] = parseInt(event.attributes.IV[2], 16); event.attributes.IV[3] = parseInt(event.attributes.IV[3], 16); event.attributes.IV = new Uint32Array(event.attributes.IV); } } this.trigger('data', event); return; } match = /^#EXT-X-CUE-OUT-CONT:?(.*)?$/.exec(line); if (match) { event = { type: 'tag', tagType: 'cue-out-cont' }; if (match[1]) { event.data = match[1]; } else { event.data = ''; } this.trigger('data', event); return; } match = /^#EXT-X-CUE-OUT:?(.*)?$/.exec(line); if (match) { event = { type: 'tag', tagType: 'cue-out' }; if (match[1]) { event.data = match[1]; } else { event.data = ''; } this.trigger('data', event); return; } match = /^#EXT-X-CUE-IN:?(.*)?$/.exec(line); if (match) { event = { type: 'tag', tagType: 'cue-in' }; if (match[1]) { event.data = match[1]; } else { event.data = ''; } this.trigger('data', event); return; } // unknown tag type this.trigger('data', { type: 'tag', data: line.slice(4) }); } }]); return ParseStream; }(_stream2['default']); exports['default'] = ParseStream; },{"./stream":200}],199:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 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 _stream = require('./stream'); var _stream2 = _interopRequireDefault(_stream); var _lineStream = require('./line-stream'); var _lineStream2 = _interopRequireDefault(_lineStream); var _parseStream = require('./parse-stream'); var _parseStream2 = _interopRequireDefault(_parseStream); 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; } /** * @file m3u8/parser.js */ /** * A parser for M3U8 files. The current interpretation of the input is * exposed as a property `manifest` on parser objects. It's just two lines to * create and parse a manifest once you have the contents available as a string: * * ```js * var parser = new m3u8.Parser(); * parser.push(xhr.responseText); * ``` * * New input can later be applied to update the manifest object by calling * `push` again. * * The parser attempts to create a usable manifest object even if the * underlying input is somewhat nonsensical. It emits `info` and `warning` * events during the parse if it encounters input that seems invalid or * requires some property of the manifest object to be defaulted. * * @class Parser * @extends Stream */ var Parser = function (_Stream) { _inherits(Parser, _Stream); function Parser() { _classCallCheck(this, Parser); var _this = _possibleConstructorReturn(this, (Parser.__proto__ || Object.getPrototypeOf(Parser)).call(this)); _this.lineStream = new _lineStream2['default'](); _this.parseStream = new _parseStream2['default'](); _this.lineStream.pipe(_this.parseStream); /* eslint-disable consistent-this */ var self = _this; /* eslint-enable consistent-this */ var uris = []; var currentUri = {}; // if specified, the active EXT-X-MAP definition var currentMap = void 0; // if specified, the active decryption key var _key = void 0; var noop = function noop() {}; var defaultMediaGroups = { 'AUDIO': {}, 'VIDEO': {}, 'CLOSED-CAPTIONS': {}, 'SUBTITLES': {} }; // group segments into numbered timelines delineated by discontinuities var currentTimeline = 0; // the manifest is empty until the parse stream begins delivering data _this.manifest = { allowCache: true, discontinuityStarts: [], segments: [] }; // update the manifest with the m3u8 entry from the parse stream _this.parseStream.on('data', function (entry) { var mediaGroup = void 0; var rendition = void 0; ({ tag: function tag() { // switch based on the tag type (({ 'allow-cache': function allowCache() { this.manifest.allowCache = entry.allowed; if (!('allowed' in entry)) { this.trigger('info', { message: 'defaulting allowCache to YES' }); this.manifest.allowCache = true; } }, byterange: function byterange() { var byterange = {}; if ('length' in entry) { currentUri.byterange = byterange; byterange.length = entry.length; if (!('offset' in entry)) { this.trigger('info', { message: 'defaulting offset to zero' }); entry.offset = 0; } } if ('offset' in entry) { currentUri.byterange = byterange; byterange.offset = entry.offset; } }, endlist: function endlist() { this.manifest.endList = true; }, inf: function inf() { if (!('mediaSequence' in this.manifest)) { this.manifest.mediaSequence = 0; this.trigger('info', { message: 'defaulting media sequence to zero' }); } if (!('discontinuitySequence' in this.manifest)) { this.manifest.discontinuitySequence = 0; this.trigger('info', { message: 'defaulting discontinuity sequence to zero' }); } if (entry.duration > 0) { currentUri.duration = entry.duration; } if (entry.duration === 0) { currentUri.duration = 0.01; this.trigger('info', { message: 'updating zero segment duration to a small value' }); } this.manifest.segments = uris; }, key: function key() { if (!entry.attributes) { this.trigger('warn', { message: 'ignoring key declaration without attribute list' }); return; } // clear the active encryption key if (entry.attributes.METHOD === 'NONE') { _key = null; return; } if (!entry.attributes.URI) { this.trigger('warn', { message: 'ignoring key declaration without URI' }); return; } if (!entry.attributes.METHOD) { this.trigger('warn', { message: 'defaulting key method to AES-128' }); } // setup an encryption key for upcoming segments _key = { method: entry.attributes.METHOD || 'AES-128', uri: entry.attributes.URI }; if (typeof entry.attributes.IV !== 'undefined') { _key.iv = entry.attributes.IV; } }, 'media-sequence': function mediaSequence() { if (!isFinite(entry.number)) { this.trigger('warn', { message: 'ignoring invalid media sequence: ' + entry.number }); return; } this.manifest.mediaSequence = entry.number; }, 'discontinuity-sequence': function discontinuitySequence() { if (!isFinite(entry.number)) { this.trigger('warn', { message: 'ignoring invalid discontinuity sequence: ' + entry.number }); return; } this.manifest.discontinuitySequence = entry.number; currentTimeline = entry.number; }, 'playlist-type': function playlistType() { if (!/VOD|EVENT/.test(entry.playlistType)) { this.trigger('warn', { message: 'ignoring unknown playlist type: ' + entry.playlist }); return; } this.manifest.playlistType = entry.playlistType; }, map: function map() { currentMap = {}; if (entry.uri) { currentMap.uri = entry.uri; } if (entry.byterange) { currentMap.byterange = entry.byterange; } }, 'stream-inf': function streamInf() { this.manifest.playlists = uris; this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups; if (!entry.attributes) { this.trigger('warn', { message: 'ignoring empty stream-inf attributes' }); return; } if (!currentUri.attributes) { currentUri.attributes = {}; } _extends(currentUri.attributes, entry.attributes); }, media: function media() { this.manifest.mediaGroups = this.manifest.mediaGroups || defaultMediaGroups; if (!(entry.attributes && entry.attributes.TYPE && entry.attributes['GROUP-ID'] && entry.attributes.NAME)) { this.trigger('warn', { message: 'ignoring incomplete or missing media group' }); return; } // find the media group, creating defaults as necessary var mediaGroupType = this.manifest.mediaGroups[entry.attributes.TYPE]; mediaGroupType[entry.attributes['GROUP-ID']] = mediaGroupType[entry.attributes['GROUP-ID']] || {}; mediaGroup = mediaGroupType[entry.attributes['GROUP-ID']]; // collect the rendition metadata rendition = { 'default': /yes/i.test(entry.attributes.DEFAULT) }; if (rendition['default']) { rendition.autoselect = true; } else { rendition.autoselect = /yes/i.test(entry.attributes.AUTOSELECT); } if (entry.attributes.LANGUAGE) { rendition.language = entry.attributes.LANGUAGE; } if (entry.attributes.URI) { rendition.uri = entry.attributes.URI; } if (entry.attributes['INSTREAM-ID']) { rendition.instreamId = entry.attributes['INSTREAM-ID']; } if (entry.attributes.CHARACTERISTICS) { rendition.characteristics = entry.attributes.CHARACTERISTICS; } if (entry.attributes.FORCED) { rendition.forced = /yes/i.test(entry.attributes.FORCED); } // insert the new rendition mediaGroup[entry.attributes.NAME] = rendition; }, discontinuity: function discontinuity() { currentTimeline += 1; currentUri.discontinuity = true; this.manifest.discontinuityStarts.push(uris.length); }, 'program-date-time': function programDateTime() { this.manifest.dateTimeString = entry.dateTimeString; this.manifest.dateTimeObject = entry.dateTimeObject; }, targetduration: function targetduration() { if (!isFinite(entry.duration) || entry.duration < 0) { this.trigger('warn', { message: 'ignoring invalid target duration: ' + entry.duration }); return; } this.manifest.targetDuration = entry.duration; }, totalduration: function totalduration() { if (!isFinite(entry.duration) || entry.duration < 0) { this.trigger('warn', { message: 'ignoring invalid total duration: ' + entry.duration }); return; } this.manifest.totalDuration = entry.duration; }, 'cue-out': function cueOut() { currentUri.cueOut = entry.data; }, 'cue-out-cont': function cueOutCont() { currentUri.cueOutCont = entry.data; }, 'cue-in': function cueIn() { currentUri.cueIn = entry.data; } })[entry.tagType] || noop).call(self); }, uri: function uri() { currentUri.uri = entry.uri; uris.push(currentUri); // if no explicit duration was declared, use the target duration if (this.manifest.targetDuration && !('duration' in currentUri)) { this.trigger('warn', { message: 'defaulting segment duration to the target duration' }); currentUri.duration = this.manifest.targetDuration; } // annotate with encryption information, if necessary if (_key) { currentUri.key = _key; } currentUri.timeline = currentTimeline; // annotate with initialization segment information, if necessary if (currentMap) { currentUri.map = currentMap; } // prepare for the next URI currentUri = {}; }, comment: function comment() { // comments are not important for playback } })[entry.type].call(self); }); return _this; } /** * Parse the input string and update the manifest object. * * @param {String} chunk a potentially incomplete portion of the manifest */ _createClass(Parser, [{ key: 'push', value: function push(chunk) { this.lineStream.push(chunk); } /** * Flush any remaining input. This can be handy if the last line of an M3U8 * manifest did not contain a trailing newline but the file has been * completely received. */ }, { key: 'end', value: function end() { // flush any buffered input this.lineStream.push('\n'); } }]); return Parser; }(_stream2['default']); exports['default'] = Parser; },{"./line-stream":197,"./parse-stream":198,"./stream":200}],200:[function(require,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"); } } /** * @file stream.js */ /** * A lightweight readable stream implemention that handles event dispatching. * * @class Stream */ var Stream = function () { function Stream() { _classCallCheck(this, Stream); this.listeners = {}; } /** * Add a listener for a specified event type. * * @param {String} type the event name * @param {Function} listener the callback to be invoked when an event of * the specified type occurs */ _createClass(Stream, [{ key: 'on', value: function on(type, listener) { if (!this.listeners[type]) { this.listeners[type] = []; } this.listeners[type].push(listener); } /** * Remove a listener for a specified event type. * * @param {String} type the event name * @param {Function} listener a function previously registered for this * type of event through `on` * @return {Boolean} if we could turn it off or not */ }, { key: 'off', value: function off(type, listener) { if (!this.listeners[type]) { return false; } var index = this.listeners[type].indexOf(listener); this.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 {String} type the event name */ }, { key: 'trigger', value: function trigger(type) { var callbacks = this.listeners[type]; var i = void 0; var length = void 0; var args = void 0; if (!callbacks) { return; } // 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 = Array.prototype.slice.call(arguments, 1); length = callbacks.length; for (i = 0; i < length; ++i) { callbacks[i].apply(this, args); } } } /** * Destroys the stream and cleans up. */ }, { key: 'dispose', value: function dispose() { this.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 {Stream} destination the stream that will receive all `data` events * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options */ }, { key: 'pipe', value: function pipe(destination) { this.on('data', function (data) { destination.push(data); }); } }]); return Stream; }(); exports['default'] = Stream; },{}],201:[function(require,module,exports){ /** * mux.js * * Copyright (c) 2016 Brightcove * All rights reserved. * * Utilities to detect basic properties and metadata about Aac data. */ 'use strict'; var ADTS_SAMPLING_FREQUENCIES = [ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 ]; var parseSyncSafeInteger = function(data) { return (data[0] << 21) | (data[1] << 14) | (data[2] << 7) | (data[3]); }; // return a percent-encoded representation of the specified byte range // @see http://en.wikipedia.org/wiki/Percent-encoding var 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 ISO-8859-1. var parseIso88591 = function(bytes, start, end) { return unescape(percentEncode(bytes, start, end)); // jshint ignore:line }; var 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; }; var 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; }; var parseType = function(header, byteIndex) { if ((header[byteIndex] === 'I'.charCodeAt(0)) && (header[byteIndex + 1] === 'D'.charCodeAt(0)) && (header[byteIndex + 2] === '3'.charCodeAt(0))) { return 'timed-metadata'; } else if ((header[byteIndex] & 0xff === 0xff) && ((header[byteIndex + 1] & 0xf0) === 0xf0)) { return 'audio'; } return null; }; var parseSampleRate = function(packet) { var i = 0; while (i + 5 < packet.length) { if (packet[i] !== 0xFF || (packet[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; } return ADTS_SAMPLING_FREQUENCIES[(packet[i + 2] & 0x3c) >>> 2]; } return null; }; var parseAacTimestamp = function(packet) { var frameStart, frameSize, frame, frameHeader; // find the start of the first frame and the end of the tag frameStart = 10; if (packet[5] & 0x40) { // advance the frame start past the extended header frameStart += 4; // header size field frameStart += parseSyncSafeInteger(packet.subarray(10, 14)); } // 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(packet.subarray(frameStart + 4, frameStart + 8)); if (frameSize < 1) { return null; } frameHeader = String.fromCharCode(packet[frameStart], packet[frameStart + 1], packet[frameStart + 2], packet[frameStart + 3]); if (frameHeader === 'PRIV') { frame = packet.subarray(frameStart + 10, frameStart + frameSize + 10); for (var i = 0; i < frame.byteLength; i++) { if (frame[i] === 0) { var owner = parseIso88591(frame, 0, i); if (owner === 'com.apple.streaming.transportStreamTimestamp') { var d = frame.subarray(i + 1); var size = ((d[3] & 0x01) << 30) | (d[4] << 22) | (d[5] << 14) | (d[6] << 6) | (d[7] >>> 2); size *= 4; size += d[7] & 0x03; return size; } break; } } } frameStart += 10; // advance past the frame header frameStart += frameSize; // advance past the frame body } while (frameStart < packet.byteLength); return null; }; module.exports = { parseId3TagSize: parseId3TagSize, parseAdtsSize: parseAdtsSize, parseType: parseType, parseSampleRate: parseSampleRate, parseAacTimestamp: parseAacTimestamp }; },{}],202:[function(require,module,exports){ /** * mux.js * * Copyright (c) 2016 Brightcove * All rights reserved. * * Utilities to detect basic properties and metadata about TS Segments. */ 'use strict'; var StreamTypes = require('./stream-types.js'); var parsePid = function(packet) { var pid = packet[1] & 0x1f; pid <<= 8; pid |= packet[2]; return pid; }; var parsePayloadUnitStartIndicator = function(packet) { return !!(packet[1] & 0x40); }; var parseAdaptionField = function(packet) { var offset = 0; // 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[4] + 1; } return offset; }; var parseType = function(packet, pmtPid) { var pid = parsePid(packet); if (pid === 0) { return 'pat'; } else if (pid === pmtPid) { return 'pmt'; } else if (pmtPid) { return 'pes'; } return null; }; var parsePat = function(packet) { var pusi = parsePayloadUnitStartIndicator(packet); var offset = 4 + parseAdaptionField(packet); if (pusi) { offset += packet[offset] + 1; } return (packet[offset + 10] & 0x1f) << 8 | packet[offset + 11]; }; var parsePmt = function(packet) { var programMapTable = {}; var pusi = parsePayloadUnitStartIndicator(packet); var payloadOffset = 4 + parseAdaptionField(packet); if (pusi) { payloadOffset += packet[payloadOffset] + 1; } // 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 (!(packet[payloadOffset + 5] & 0x01)) { return; } var sectionLength, tableEnd, programInfoLength; // the mapping table ends at the end of the current section sectionLength = (packet[payloadOffset + 1] & 0x0f) << 8 | packet[payloadOffset + 2]; tableEnd = 3 + sectionLength - 4; // to determine where the table is, we have to figure out how // long the program info descriptors are programInfoLength = (packet[payloadOffset + 10] & 0x0f) << 8 | packet[payloadOffset + 11]; // advance the offset to the first entry in the mapping table var offset = 12 + programInfoLength; while (offset < tableEnd) { var i = payloadOffset + offset; // add an entry that maps the elementary_pid to the stream_type programMapTable[(packet[i + 1] & 0x1F) << 8 | packet[i + 2]] = packet[i]; // move to the next table entry // skip past the elementary stream descriptors, if present offset += ((packet[i + 3] & 0x0F) << 8 | packet[i + 4]) + 5; } return programMapTable; }; var parsePesType = function(packet, programMapTable) { var pid = parsePid(packet); var type = programMapTable[pid]; switch (type) { case StreamTypes.H264_STREAM_TYPE: return 'video'; case StreamTypes.ADTS_STREAM_TYPE: return 'audio'; case StreamTypes.METADATA_STREAM_TYPE: return 'timed-metadata'; default: return null; } }; var parsePesTime = function(packet) { var pusi = parsePayloadUnitStartIndicator(packet); if (!pusi) { return null; } var offset = 4 + parseAdaptionField(packet); var pes = {}; var ptsDtsFlags; // 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 = packet[offset + 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 = (packet[offset + 9] & 0x0E) << 27 | (packet[offset + 10] & 0xFF) << 20 | (packet[offset + 11] & 0xFE) << 12 | (packet[offset + 12] & 0xFF) << 5 | (packet[offset + 13] & 0xFE) >>> 3; pes.pts *= 4; // Left shift by 2 pes.pts += (packet[offset + 13] & 0x06) >>> 1; // OR by the two LSBs pes.dts = pes.pts; if (ptsDtsFlags & 0x40) { pes.dts = (packet[offset + 14] & 0x0E) << 27 | (packet[offset + 15] & 0xFF) << 20 | (packet[offset + 16] & 0xFE) << 12 | (packet[offset + 17] & 0xFF) << 5 | (packet[offset + 18] & 0xFE) >>> 3; pes.dts *= 4; // Left shift by 2 pes.dts += (packet[offset + 18] & 0x06) >>> 1; // OR by the two LSBs } } return pes; }; var parseNalUnitType = function(type) { switch (type) { case 0x05: return 'slice_layer_without_partitioning_rbsp_idr'; case 0x06: return 'sei_rbsp'; case 0x07: return 'seq_parameter_set_rbsp'; case 0x08: return 'pic_parameter_set_rbsp'; case 0x09: return 'access_unit_delimiter_rbsp'; default: return null; } }; var videoPacketContainsKeyFrame = function(packet) { var offset = 4 + parseAdaptionField(packet); var frameBuffer = packet.subarray(offset); var frameI = 0; var frameSyncPoint = 0; var foundKeyFrame = false; var nalType; // advance the sync point to a NAL start, if necessary for (; frameSyncPoint < frameBuffer.byteLength - 3; frameSyncPoint++) { if (frameBuffer[frameSyncPoint + 2] === 1) { // the sync point is properly aligned frameI = frameSyncPoint + 5; break; } } while (frameI < frameBuffer.byteLength) { // look at the current byte to determine if we've hit the end of // a NAL unit boundary switch (frameBuffer[frameI]) { case 0: // skip past non-sync sequences if (frameBuffer[frameI - 1] !== 0) { frameI += 2; break; } else if (frameBuffer[frameI - 2] !== 0) { frameI++; break; } if (frameSyncPoint + 3 !== frameI - 2) { nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f); if (nalType === 'slice_layer_without_partitioning_rbsp_idr') { foundKeyFrame = true; } } // drop trailing zeroes do { frameI++; } while (frameBuffer[frameI] !== 1 && frameI < frameBuffer.length); frameSyncPoint = frameI - 2; frameI += 3; break; case 1: // skip past non-sync sequences if (frameBuffer[frameI - 1] !== 0 || frameBuffer[frameI - 2] !== 0) { frameI += 3; break; } nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f); if (nalType === 'slice_layer_without_partitioning_rbsp_idr') { foundKeyFrame = true; } frameSyncPoint = frameI - 2; frameI += 3; break; default: // the current byte isn't a one or zero, so it cannot be part // of a sync sequence frameI += 3; break; } } frameBuffer = frameBuffer.subarray(frameSyncPoint); frameI -= frameSyncPoint; frameSyncPoint = 0; // parse the final nal if (frameBuffer && frameBuffer.byteLength > 3) { nalType = parseNalUnitType(frameBuffer[frameSyncPoint + 3] & 0x1f); if (nalType === 'slice_layer_without_partitioning_rbsp_idr') { foundKeyFrame = true; } } return foundKeyFrame; }; module.exports = { parseType: parseType, parsePat: parsePat, parsePmt: parsePmt, parsePayloadUnitStartIndicator: parsePayloadUnitStartIndicator, parsePesType: parsePesType, parsePesTime: parsePesTime, videoPacketContainsKeyFrame: videoPacketContainsKeyFrame }; },{"./stream-types.js":203}],203:[function(require,module,exports){ 'use strict'; module.exports = { H264_STREAM_TYPE: 0x1B, ADTS_STREAM_TYPE: 0x0F, METADATA_STREAM_TYPE: 0x15 }; },{}],204:[function(require,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 = require('../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":207}],205:[function(require,module,exports){ /** * mux.js * * Copyright (c) 2015 Brightcove * All rights reserved. * * Utilities to detect basic properties and metadata about MP4s. */ 'use strict'; var findBox, parseType, timescale, startTime; // Find the data for a box specified by its path findBox = function(data, path) { var results = [], i, size, type, end, subresults; if (!path.length) { // short-circuit the search for empty paths return null; } for (i = 0; i < data.byteLength;) { size = data[i] << 24; size |= data[i + 1] << 16; size |= data[i + 2] << 8; size |= data[i + 3]; type = parseType(data.subarray(i + 4, i + 8)); end = size > 1 ? i + size : data.byteLength; 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.subarray(i + 8, end)); } else { // recursively search for the next box along the path subresults = findBox(data.subarray(i + 8, end), path.slice(1)); if (subresults.length) { results = results.concat(subresults); } } } i = end; } // we've finished searching all of data return results; }; /** * 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; }; /** * Parses an MP4 initialization segment and extracts the 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 * ``` * @param init {Uint8Array} the bytes of the init segment * @return {object} a hash of track ids to timescale values or null if * the init segment is malformed. */ timescale = function(init) { var result = {}, traks = findBox(init, ['moov', 'trak']); // mdhd timescale return traks.reduce(function(result, trak) { var tkhd, version, index, id, mdhd; tkhd = findBox(trak, ['tkhd'])[0]; if (!tkhd) { return null; } version = tkhd[0]; index = version === 0 ? 12 : 20; id = tkhd[index] << 24 | tkhd[index + 1] << 16 | tkhd[index + 2] << 8 | tkhd[index + 3]; mdhd = findBox(trak, ['mdia', 'mdhd'])[0]; if (!mdhd) { return null; } version = mdhd[0]; index = version === 0 ? 12 : 20; result[id] = mdhd[index] << 24 | mdhd[index + 1] << 16 | mdhd[index + 2] << 8 | mdhd[index + 3]; return result; }, 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 */ startTime = function(timescale, fragment) { var trafs, baseTimes, result; // we need info from two childrend of each track fragment box trafs = findBox(fragment, ['moof', 'traf']); // determine the start times for each track baseTimes = [].concat.apply([], trafs.map(function(traf) { return findBox(traf, ['tfhd']).map(function(tfhd) { var id, scale, baseTime; // get the track id from the tfhd id = tfhd[4] << 24 | tfhd[5] << 16 | tfhd[6] << 8 | tfhd[7]; // assume a 90kHz clock if no timescale was specified scale = timescale[id] || 90e3; // get the base media decode time from the tfdt baseTime = findBox(traf, ['tfdt']).map(function(tfdt) { var version, result; version = tfdt[0]; result = tfdt[4] << 24 | tfdt[5] << 16 | tfdt[6] << 8 | tfdt[7]; if (version === 1) { result *= Math.pow(2, 32); result += tfdt[8] << 24 | tfdt[9] << 16 | tfdt[10] << 8 | tfdt[11]; } 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; }; module.exports = { parseType: parseType, timescale: timescale, startTime: startTime }; },{}],206:[function(require,module,exports){ /** * mux.js * * Copyright (c) 2016 Brightcove * All rights reserved. * * Parse mpeg2 transport stream packets to extract basic timing information */ 'use strict'; var StreamTypes = require('../m2ts/stream-types.js'); var handleRollover = require('../m2ts/timestamp-rollover-stream.js').handleRollover; var probe = {}; probe.ts = require('../m2ts/probe.js'); probe.aac = require('../aac/probe.js'); var PES_TIMESCALE = 90000, MP2T_PACKET_LENGTH = 188, // bytes SYNC_BYTE = 0x47; var isLikelyAacData = function(data) { if ((data[0] === 'I'.charCodeAt(0)) && (data[1] === 'D'.charCodeAt(0)) && (data[2] === '3'.charCodeAt(0))) { return true; } return false; }; /** * walks through segment data looking for pat and pmt packets to parse out * program map table information */ var parsePsi_ = function(bytes, pmt) { var startIndex = 0, endIndex = MP2T_PACKET_LENGTH, packet, type; while (endIndex < bytes.byteLength) { // Look for a pair of start and end sync bytes in the data.. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { // We found a packet packet = bytes.subarray(startIndex, endIndex); type = probe.ts.parseType(packet, pmt.pid); switch (type) { case 'pat': if (!pmt.pid) { pmt.pid = probe.ts.parsePat(packet); } break; case 'pmt': if (!pmt.table) { pmt.table = probe.ts.parsePmt(packet); } break; default: break; } // Found the pat and pmt, we can stop walking the segment if (pmt.pid && pmt.table) { return; } 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++; } }; /** * walks through the segment data from the start and end to get timing information * for the first and last audio pes packets */ var parseAudioPes_ = function(bytes, pmt, result) { var startIndex = 0, endIndex = MP2T_PACKET_LENGTH, packet, type, pesType, pusi, parsed; var endLoop = false; // Start walking from start of segment to get first audio packet while (endIndex < bytes.byteLength) { // Look for a pair of start and end sync bytes in the data.. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { // We found a packet packet = bytes.subarray(startIndex, endIndex); type = probe.ts.parseType(packet, pmt.pid); switch (type) { case 'pes': pesType = probe.ts.parsePesType(packet, pmt.table); pusi = probe.ts.parsePayloadUnitStartIndicator(packet); if (pesType === 'audio' && pusi) { parsed = probe.ts.parsePesTime(packet); parsed.type = 'audio'; result.audio.push(parsed); endLoop = true; } break; default: break; } if (endLoop) { break; } 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++; } // Start walking from end of segment to get last audio packet endIndex = bytes.byteLength; startIndex = endIndex - MP2T_PACKET_LENGTH; endLoop = false; while (startIndex >= 0) { // Look for a pair of start and end sync bytes in the data.. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { // We found a packet packet = bytes.subarray(startIndex, endIndex); type = probe.ts.parseType(packet, pmt.pid); switch (type) { case 'pes': pesType = probe.ts.parsePesType(packet, pmt.table); pusi = probe.ts.parsePayloadUnitStartIndicator(packet); if (pesType === 'audio' && pusi) { parsed = probe.ts.parsePesTime(packet); parsed.type = 'audio'; result.audio.push(parsed); endLoop = true; } break; default: break; } if (endLoop) { break; } 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--; } }; /** * walks through the segment data from the start and end to get timing information * for the first and last video pes packets as well as timing information for the first * key frame. */ var parseVideoPes_ = function(bytes, pmt, result) { var startIndex = 0, endIndex = MP2T_PACKET_LENGTH, packet, type, pesType, pusi, parsed, frame, i, pes; var endLoop = false; var currentFrame = { data: [], size: 0 }; // Start walking from start of segment to get first video packet while (endIndex < bytes.byteLength) { // Look for a pair of start and end sync bytes in the data.. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { // We found a packet packet = bytes.subarray(startIndex, endIndex); type = probe.ts.parseType(packet, pmt.pid); switch (type) { case 'pes': pesType = probe.ts.parsePesType(packet, pmt.table); pusi = probe.ts.parsePayloadUnitStartIndicator(packet); if (pesType === 'video') { if (pusi && !endLoop) { parsed = probe.ts.parsePesTime(packet); parsed.type = 'video'; result.video.push(parsed); endLoop = true; } if (!result.firstKeyFrame) { if (pusi) { if (currentFrame.size !== 0) { frame = new Uint8Array(currentFrame.size); i = 0; while (currentFrame.data.length) { pes = currentFrame.data.shift(); frame.set(pes, i); i += pes.byteLength; } if (probe.ts.videoPacketContainsKeyFrame(frame)) { result.firstKeyFrame = probe.ts.parsePesTime(frame); result.firstKeyFrame.type = 'video'; } currentFrame.size = 0; } } currentFrame.data.push(packet); currentFrame.size += packet.byteLength; } } break; default: break; } if (endLoop && result.firstKeyFrame) { break; } 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++; } // Start walking from end of segment to get last video packet endIndex = bytes.byteLength; startIndex = endIndex - MP2T_PACKET_LENGTH; endLoop = false; while (startIndex >= 0) { // Look for a pair of start and end sync bytes in the data.. if (bytes[startIndex] === SYNC_BYTE && bytes[endIndex] === SYNC_BYTE) { // We found a packet packet = bytes.subarray(startIndex, endIndex); type = probe.ts.parseType(packet, pmt.pid); switch (type) { case 'pes': pesType = probe.ts.parsePesType(packet, pmt.table); pusi = probe.ts.parsePayloadUnitStartIndicator(packet); if (pesType === 'video' && pusi) { parsed = probe.ts.parsePesTime(packet); parsed.type = 'video'; result.video.push(parsed); endLoop = true; } break; default: break; } if (endLoop) { break; } 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--; } }; /** * Adjusts the timestamp information for the segment to account for * rollover and convert to seconds based on pes packet timescale (90khz clock) */ var adjustTimestamp_ = function(segmentInfo, baseTimestamp) { if (segmentInfo.audio && segmentInfo.audio.length) { var audioBaseTimestamp = baseTimestamp; if (typeof audioBaseTimestamp === 'undefined') { audioBaseTimestamp = segmentInfo.audio[0].dts; } segmentInfo.audio.forEach(function(info) { info.dts = handleRollover(info.dts, audioBaseTimestamp); info.pts = handleRollover(info.pts, audioBaseTimestamp); // time in seconds info.dtsTime = info.dts / PES_TIMESCALE; info.ptsTime = info.pts / PES_TIMESCALE; }); } if (segmentInfo.video && segmentInfo.video.length) { var videoBaseTimestamp = baseTimestamp; if (typeof videoBaseTimestamp === 'undefined') { videoBaseTimestamp = segmentInfo.video[0].dts; } segmentInfo.video.forEach(function(info) { info.dts = handleRollover(info.dts, videoBaseTimestamp); info.pts = handleRollover(info.pts, videoBaseTimestamp); // time in seconds info.dtsTime = info.dts / PES_TIMESCALE; info.ptsTime = info.pts / PES_TIMESCALE; }); if (segmentInfo.firstKeyFrame) { var frame = segmentInfo.firstKeyFrame; frame.dts = handleRollover(frame.dts, videoBaseTimestamp); frame.pts = handleRollover(frame.pts, videoBaseTimestamp); // time in seconds frame.dtsTime = frame.dts / PES_TIMESCALE; frame.ptsTime = frame.dts / PES_TIMESCALE; } } }; /** * inspects the aac data stream for start and end time information */ var inspectAac_ = function(bytes) { var endLoop = false, audioCount = 0, sampleRate = null, timestamp = null, frameSize = 0, byteIndex = 0, packet; while (bytes.length - byteIndex >= 3) { var type = probe.aac.parseType(bytes, byteIndex); switch (type) { case 'timed-metadata': // Exit early because we don't have enough to parse // the ID3 tag header if (bytes.length - byteIndex < 10) { endLoop = true; break; } frameSize = probe.aac.parseId3TagSize(bytes, byteIndex); // Exit early if we don't have enough in the buffer // to emit a full packet if (frameSize > bytes.length) { endLoop = true; break; } if (timestamp === null) { packet = bytes.subarray(byteIndex, byteIndex + frameSize); timestamp = probe.aac.parseAacTimestamp(packet); } byteIndex += frameSize; break; case 'audio': // Exit early because we don't have enough to parse // the ADTS frame header if (bytes.length - byteIndex < 7) { endLoop = true; break; } frameSize = probe.aac.parseAdtsSize(bytes, byteIndex); // Exit early if we don't have enough in the buffer // to emit a full packet if (frameSize > bytes.length) { endLoop = true; break; } if (sampleRate === null) { packet = bytes.subarray(byteIndex, byteIndex + frameSize); sampleRate = probe.aac.parseSampleRate(packet); } audioCount++; byteIndex += frameSize; break; default: byteIndex++; break; } if (endLoop) { return null; } } if (sampleRate === null || timestamp === null) { return null; } var audioTimescale = PES_TIMESCALE / sampleRate; var result = { audio: [ { type: 'audio', dts: timestamp, pts: timestamp }, { type: 'audio', dts: timestamp + (audioCount * 1024 * audioTimescale), pts: timestamp + (audioCount * 1024 * audioTimescale) } ] }; return result; }; /** * inspects the transport stream segment data for start and end time information * of the audio and video tracks (when present) as well as the first key frame's * start time. */ var inspectTs_ = function(bytes) { var pmt = { pid: null, table: null }; var result = {}; parsePsi_(bytes, pmt); for (var pid in pmt.table) { if (pmt.table.hasOwnProperty(pid)) { var type = pmt.table[pid]; switch (type) { case StreamTypes.H264_STREAM_TYPE: result.video = []; parseVideoPes_(bytes, pmt, result); if (result.video.length === 0) { delete result.video; } break; case StreamTypes.ADTS_STREAM_TYPE: result.audio = []; parseAudioPes_(bytes, pmt, result); if (result.audio.length === 0) { delete result.audio; } break; default: break; } } } return result; }; /** * Inspects segment byte data and returns an object with start and end timing information * * @param {Uint8Array} bytes The segment byte data * @param {Number} baseTimestamp Relative reference timestamp used when adjusting frame * timestamps for rollover. This value must be in 90khz clock. * @return {Object} Object containing start and end frame timing info of segment. */ var inspect = function(bytes, baseTimestamp) { var isAacData = isLikelyAacData(bytes); var result; if (isAacData) { result = inspectAac_(bytes); } else { result = inspectTs_(bytes); } if (!result || (!result.audio && !result.video)) { return null; } adjustTimestamp_(result, baseTimestamp); return result; }; module.exports = { inspect: inspect }; },{"../aac/probe.js":201,"../m2ts/probe.js":202,"../m2ts/stream-types.js":203,"../m2ts/timestamp-rollover-stream.js":204}],207:[function(require,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] = listeners[type].concat(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] = listeners[type].slice(); 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; } // 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; },{}],208:[function(require,module,exports){ var trim = require('trim') , forEach = require('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":192,"trim":217}],209:[function(require,module,exports){ /* * pkcs7.pad * https://github.com/brightcove/pkcs7 * * Copyright (c) 2014 Brightcove * Licensed under the apache2 license. */ 'use strict'; var PADDING; /** * Returns a new Uint8Array that is padded with PKCS#7 padding. * @param plaintext {Uint8Array} the input bytes before encryption * @return {Uint8Array} the padded bytes * @see http://tools.ietf.org/html/rfc5652 */ module.exports = function pad(plaintext) { var padding = PADDING[(plaintext.byteLength % 16) || 0], result = new Uint8Array(plaintext.byteLength + padding.length); result.set(plaintext); result.set(padding, plaintext.byteLength); return result; }; // pre-define the padding values PADDING = [ [16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16], [15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15], [14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14], [13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13], [12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12], [11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11], [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], [9, 9, 9, 9, 9, 9, 9, 9, 9], [8, 8, 8, 8, 8, 8, 8, 8], [7, 7, 7, 7, 7, 7, 7], [6, 6, 6, 6, 6, 6], [5, 5, 5, 5, 5], [4, 4, 4, 4], [3, 3, 3], [2, 2], [1] ]; },{}],210:[function(require,module,exports){ /* * pkcs7 * https://github.com/brightcove/pkcs7 * * Copyright (c) 2014 Brightcove * Licensed under the apache2 license. */ 'use strict'; exports.pad = require('./pad.js'); exports.unpad = require('./unpad.js'); },{"./pad.js":209,"./unpad.js":211}],211:[function(require,module,exports){ /* * pkcs7.unpad * https://github.com/brightcove/pkcs7 * * Copyright (c) 2014 Brightcove * Licensed under the apache2 license. */ 'use strict'; /** * Returns the subarray of a Uint8Array without PKCS#7 padding. * @param padded {Uint8Array} unencrypted bytes that have been padded * @return {Uint8Array} the unpadded bytes * @see http://tools.ietf.org/html/rfc5652 */ module.exports = function unpad(padded) { return padded.subarray(0, padded.byteLength - padded[padded.byteLength - 1]); }; },{}],212:[function(require,module,exports){ /** * Decimal adjustment of a number. * * @param {String} type The type of adjustment. * @param {Number} value The number. * @param {Integer} exp The exponent (the 10 logarithm of the adjustment base). * @returns {Number} The adjusted value. */ var decimalAdjust = exports.decimalAdjust = function(type, value, exp) { // If the exp is undefined or zero... if (typeof exp === 'undefined' || +exp === 0) { return Math[type](value); } value = +value; exp = +exp; // If the value is not a number or the exp is not an integer... if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) { return NaN; } // Shift value = value.toString().split('e'); value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp))); // Shift back value = value.toString().split('e'); return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)); } module.exports = { round10: function(value, exp) { return decimalAdjust('round', value, exp); }, floor10: function(value, exp) { return decimalAdjust('floor', value, exp); }, ceil10: function(value, exp) { return decimalAdjust('ceil', value, exp); }, }; module.exports.polyfill = function() { // Decimal round if (!Math.round10) { Math.round10 = module.exports.round10; } // Decimal floor if (!Math.floor10) { Math.floor10 = module.exports.floor10; } // Decimal ceil if (!Math.ceil10) { Math.ceil10 = module.exports.ceil10; } }; },{}],213:[function(require,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] } },{}],214:[function(require,module,exports){ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.THREE = global.THREE || {}))); }(this, (function (exports) { 'use strict'; // Polyfills if ( Number.EPSILON === undefined ) { Number.EPSILON = Math.pow( 2, - 52 ); } // if ( Math.sign === undefined ) { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign Math.sign = function ( x ) { return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; }; } if ( Function.prototype.name === undefined ) { // Missing in IE9-11. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name Object.defineProperty( Function.prototype, 'name', { get: function () { return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; } } ); } if ( Object.assign === undefined ) { // Missing in IE. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign ( function () { Object.assign = function ( target ) { 'use strict'; if ( target === undefined || target === null ) { throw new TypeError( 'Cannot convert undefined or null to object' ); } var output = Object( target ); for ( var index = 1; index < arguments.length; index ++ ) { var source = arguments[ index ]; if ( source !== undefined && source !== null ) { for ( var nextKey in source ) { if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { output[ nextKey ] = source[ nextKey ]; } } } } return output; }; } )(); } /** * https://github.com/mrdoob/eventdispatcher.js/ */ function EventDispatcher() {} Object.assign( EventDispatcher.prototype, { addEventListener: function ( type, listener ) { if ( this._listeners === undefined ) this._listeners = {}; var listeners = this._listeners; if ( listeners[ type ] === undefined ) { listeners[ type ] = []; } if ( listeners[ type ].indexOf( listener ) === - 1 ) { listeners[ type ].push( listener ); } }, hasEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return false; var listeners = this._listeners; return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; }, removeEventListener: function ( type, listener ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ type ]; if ( listenerArray !== undefined ) { var index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } } }, dispatchEvent: function ( event ) { if ( this._listeners === undefined ) return; var listeners = this._listeners; var listenerArray = listeners[ event.type ]; if ( listenerArray !== undefined ) { event.target = this; var array = [], i = 0; var length = listenerArray.length; for ( i = 0; i < length; i ++ ) { array[ i ] = listenerArray[ i ]; } for ( i = 0; i < length; i ++ ) { array[ i ].call( this, event ); } } } } ); var REVISION = '83'; var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; var CullFaceNone = 0; var CullFaceBack = 1; var CullFaceFront = 2; var CullFaceFrontBack = 3; var FrontFaceDirectionCW = 0; var FrontFaceDirectionCCW = 1; var BasicShadowMap = 0; var PCFShadowMap = 1; var PCFSoftShadowMap = 2; var FrontSide = 0; var BackSide = 1; var DoubleSide = 2; var FlatShading = 1; var SmoothShading = 2; var NoColors = 0; var FaceColors = 1; var VertexColors = 2; var NoBlending = 0; var NormalBlending = 1; var AdditiveBlending = 2; var SubtractiveBlending = 3; var MultiplyBlending = 4; var CustomBlending = 5; var BlendingMode = { NoBlending: NoBlending, NormalBlending: NormalBlending, AdditiveBlending: AdditiveBlending, SubtractiveBlending: SubtractiveBlending, MultiplyBlending: MultiplyBlending, CustomBlending: CustomBlending }; var AddEquation = 100; var SubtractEquation = 101; var ReverseSubtractEquation = 102; var MinEquation = 103; var MaxEquation = 104; var ZeroFactor = 200; var OneFactor = 201; var SrcColorFactor = 202; var OneMinusSrcColorFactor = 203; var SrcAlphaFactor = 204; var OneMinusSrcAlphaFactor = 205; var DstAlphaFactor = 206; var OneMinusDstAlphaFactor = 207; var DstColorFactor = 208; var OneMinusDstColorFactor = 209; var SrcAlphaSaturateFactor = 210; var NeverDepth = 0; var AlwaysDepth = 1; var LessDepth = 2; var LessEqualDepth = 3; var EqualDepth = 4; var GreaterEqualDepth = 5; var GreaterDepth = 6; var NotEqualDepth = 7; var MultiplyOperation = 0; var MixOperation = 1; var AddOperation = 2; var NoToneMapping = 0; var LinearToneMapping = 1; var ReinhardToneMapping = 2; var Uncharted2ToneMapping = 3; var CineonToneMapping = 4; var UVMapping = 300; var CubeReflectionMapping = 301; var CubeRefractionMapping = 302; var EquirectangularReflectionMapping = 303; var EquirectangularRefractionMapping = 304; var SphericalReflectionMapping = 305; var CubeUVReflectionMapping = 306; var CubeUVRefractionMapping = 307; var TextureMapping = { UVMapping: UVMapping, CubeReflectionMapping: CubeReflectionMapping, CubeRefractionMapping: CubeRefractionMapping, EquirectangularReflectionMapping: EquirectangularReflectionMapping, EquirectangularRefractionMapping: EquirectangularRefractionMapping, SphericalReflectionMapping: SphericalReflectionMapping, CubeUVReflectionMapping: CubeUVReflectionMapping, CubeUVRefractionMapping: CubeUVRefractionMapping }; var RepeatWrapping = 1000; var ClampToEdgeWrapping = 1001; var MirroredRepeatWrapping = 1002; var TextureWrapping = { RepeatWrapping: RepeatWrapping, ClampToEdgeWrapping: ClampToEdgeWrapping, MirroredRepeatWrapping: MirroredRepeatWrapping }; var NearestFilter = 1003; var NearestMipMapNearestFilter = 1004; var NearestMipMapLinearFilter = 1005; var LinearFilter = 1006; var LinearMipMapNearestFilter = 1007; var LinearMipMapLinearFilter = 1008; var TextureFilter = { NearestFilter: NearestFilter, NearestMipMapNearestFilter: NearestMipMapNearestFilter, NearestMipMapLinearFilter: NearestMipMapLinearFilter, LinearFilter: LinearFilter, LinearMipMapNearestFilter: LinearMipMapNearestFilter, LinearMipMapLinearFilter: LinearMipMapLinearFilter }; var UnsignedByteType = 1009; var ByteType = 1010; var ShortType = 1011; var UnsignedShortType = 1012; var IntType = 1013; var UnsignedIntType = 1014; var FloatType = 1015; var HalfFloatType = 1016; var UnsignedShort4444Type = 1017; var UnsignedShort5551Type = 1018; var UnsignedShort565Type = 1019; var UnsignedInt248Type = 1020; var AlphaFormat = 1021; var RGBFormat = 1022; var RGBAFormat = 1023; var LuminanceFormat = 1024; var LuminanceAlphaFormat = 1025; var RGBEFormat = RGBAFormat; var DepthFormat = 1026; var DepthStencilFormat = 1027; var RGB_S3TC_DXT1_Format = 2001; var RGBA_S3TC_DXT1_Format = 2002; var RGBA_S3TC_DXT3_Format = 2003; var RGBA_S3TC_DXT5_Format = 2004; var RGB_PVRTC_4BPPV1_Format = 2100; var RGB_PVRTC_2BPPV1_Format = 2101; var RGBA_PVRTC_4BPPV1_Format = 2102; var RGBA_PVRTC_2BPPV1_Format = 2103; var RGB_ETC1_Format = 2151; var LoopOnce = 2200; var LoopRepeat = 2201; var LoopPingPong = 2202; var InterpolateDiscrete = 2300; var InterpolateLinear = 2301; var InterpolateSmooth = 2302; var ZeroCurvatureEnding = 2400; var ZeroSlopeEnding = 2401; var WrapAroundEnding = 2402; var TrianglesDrawMode = 0; var TriangleStripDrawMode = 1; var TriangleFanDrawMode = 2; var LinearEncoding = 3000; var sRGBEncoding = 3001; var GammaEncoding = 3007; var RGBEEncoding = 3002; var LogLuvEncoding = 3003; var RGBM7Encoding = 3004; var RGBM16Encoding = 3005; var RGBDEncoding = 3006; var BasicDepthPacking = 3200; var RGBADepthPacking = 3201; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ var _Math = { DEG2RAD: Math.PI / 180, RAD2DEG: 180 / Math.PI, generateUUID: function () { // http://www.broofa.com/Tools/Math.uuid.htm var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); var uuid = new Array( 36 ); var rnd = 0, r; return function generateUUID() { for ( var i = 0; i < 36; i ++ ) { if ( i === 8 || i === 13 || i === 18 || i === 23 ) { uuid[ i ] = '-'; } else if ( i === 14 ) { uuid[ i ] = '4'; } else { if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; r = rnd & 0xf; rnd = rnd >> 4; uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ]; } } return uuid.join( '' ); }; }(), clamp: function ( value, min, max ) { return Math.max( min, Math.min( max, value ) ); }, // compute euclidian modulo of m % n // https://en.wikipedia.org/wiki/Modulo_operation euclideanModulo: function ( n, m ) { return ( ( n % m ) + m ) % m; }, // Linear mapping from range to range mapLinear: function ( x, a1, a2, b1, b2 ) { return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); }, // https://en.wikipedia.org/wiki/Linear_interpolation lerp: function ( x, y, t ) { return ( 1 - t ) * x + t * y; }, // http://en.wikipedia.org/wiki/Smoothstep smoothstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * ( 3 - 2 * x ); }, smootherstep: function ( x, min, max ) { if ( x <= min ) return 0; if ( x >= max ) return 1; x = ( x - min ) / ( max - min ); return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); }, // Random integer from interval randInt: function ( low, high ) { return low + Math.floor( Math.random() * ( high - low + 1 ) ); }, // Random float from interval randFloat: function ( low, high ) { return low + Math.random() * ( high - low ); }, // Random float from <-range/2, range/2> interval randFloatSpread: function ( range ) { return range * ( 0.5 - Math.random() ); }, degToRad: function ( degrees ) { return degrees * _Math.DEG2RAD; }, radToDeg: function ( radians ) { return radians * _Math.RAD2DEG; }, isPowerOfTwo: function ( value ) { return ( value & ( value - 1 ) ) === 0 && value !== 0; }, nearestPowerOfTwo: function ( value ) { return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); }, nextPowerOfTwo: function ( value ) { value --; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; value ++; return value; } }; /** * @author mrdoob / http://mrdoob.com/ * @author philogb / http://blog.thejit.org/ * @author egraether / http://egraether.com/ * @author zz85 / http://www.lab4games.net/zz85/blog */ function Vector2( x, y ) { this.x = x || 0; this.y = y || 0; } Vector2.prototype = { constructor: Vector2, isVector2: true, get width() { return this.x; }, set width( value ) { this.x = value; }, get height() { return this.y; }, set height( value ) { this.y = value; }, // set: function ( x, y ) { this.x = x; this.y = y; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; default: throw new Error( 'index is out of range: ' + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; default: throw new Error( 'index is out of range: ' + index ); } }, clone: function () { return new this.constructor( this.x, this.y ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; return this; }, multiply: function ( v ) { this.x *= v.x; this.y *= v.y; return this; }, multiplyScalar: function ( scalar ) { if ( isFinite( scalar ) ) { this.x *= scalar; this.y *= scalar; } else { this.x = 0; this.y = 0; } return this; }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); return this; }, clamp: function ( min, max ) { // This function assumes min < max, if this assumption isn't true it will not operate correctly this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); return this; }, clampScalar: function () { var min, max; return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { min = new Vector2(); max = new Vector2(); } min.set( minVal, minVal ); max.set( maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y; }, lengthSq: function () { return this.x * this.x + this.y * this.y; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y ); }, lengthManhattan: function() { return Math.abs( this.x ) + Math.abs( this.y ); }, normalize: function () { return this.divideScalar( this.length() ); }, angle: function () { // computes the angle in radians with respect to the positive x-axis var angle = Math.atan2( this.y, this.x ); if ( angle < 0 ) angle += 2 * Math.PI; return angle; }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y; return dx * dx + dy * dy; }, distanceToManhattan: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); }, setLength: function ( length ) { return this.multiplyScalar( length / this.length() ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; return array; }, fromAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( 'THREE.Vector2: offset has been removed from .fromAttribute().' ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); return this; }, rotateAround: function ( center, angle ) { var c = Math.cos( angle ), s = Math.sin( angle ); var x = this.x - center.x; var y = this.y - center.y; this.x = x * c - y * s + center.x; this.y = x * s + y * c + center.y; return this; } }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ */ var textureId = 0; function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { Object.defineProperty( this, 'id', { value: textureId ++ } ); this.uuid = _Math.generateUUID(); this.name = ''; this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; this.mipmaps = []; this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter; this.anisotropy = anisotropy !== undefined ? anisotropy : 1; this.format = format !== undefined ? format : RGBAFormat; this.type = type !== undefined ? type : UnsignedByteType; this.offset = new Vector2( 0, 0 ); this.repeat = new Vector2( 1, 1 ); this.generateMipmaps = true; this.premultiplyAlpha = false; this.flipY = true; this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. // // Also changing the encoding after already used by a Material will not automatically make the Material // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. this.encoding = encoding !== undefined ? encoding : LinearEncoding; this.version = 0; this.onUpdate = null; } Texture.DEFAULT_IMAGE = undefined; Texture.DEFAULT_MAPPING = UVMapping; Texture.prototype = { constructor: Texture, isTexture: true, set needsUpdate( value ) { if ( value === true ) this.version ++; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.image = source.image; this.mipmaps = source.mipmaps.slice( 0 ); this.mapping = source.mapping; this.wrapS = source.wrapS; this.wrapT = source.wrapT; this.magFilter = source.magFilter; this.minFilter = source.minFilter; this.anisotropy = source.anisotropy; this.format = source.format; this.type = source.type; this.offset.copy( source.offset ); this.repeat.copy( source.repeat ); this.generateMipmaps = source.generateMipmaps; this.premultiplyAlpha = source.premultiplyAlpha; this.flipY = source.flipY; this.unpackAlignment = source.unpackAlignment; this.encoding = source.encoding; return this; }, toJSON: function ( meta ) { if ( meta.textures[ this.uuid ] !== undefined ) { return meta.textures[ this.uuid ]; } function getDataURL( image ) { var canvas; if ( image.toDataURL !== undefined ) { canvas = image; } else { canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); canvas.width = image.width; canvas.height = image.height; canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); } if ( canvas.width > 2048 || canvas.height > 2048 ) { return canvas.toDataURL( 'image/jpeg', 0.6 ); } else { return canvas.toDataURL( 'image/png' ); } } var output = { metadata: { version: 4.4, type: 'Texture', generator: 'Texture.toJSON' }, uuid: this.uuid, name: this.name, mapping: this.mapping, repeat: [ this.repeat.x, this.repeat.y ], offset: [ this.offset.x, this.offset.y ], wrap: [ this.wrapS, this.wrapT ], minFilter: this.minFilter, magFilter: this.magFilter, anisotropy: this.anisotropy, flipY: this.flipY }; if ( this.image !== undefined ) { // TODO: Move to THREE.Image var image = this.image; if ( image.uuid === undefined ) { image.uuid = _Math.generateUUID(); // UGH } if ( meta.images[ image.uuid ] === undefined ) { meta.images[ image.uuid ] = { uuid: image.uuid, url: getDataURL( image ) }; } output.image = image.uuid; } meta.textures[ this.uuid ] = output; return output; }, dispose: function () { this.dispatchEvent( { type: 'dispose' } ); }, transformUv: function ( uv ) { if ( this.mapping !== UVMapping ) return; uv.multiply( this.repeat ); uv.add( this.offset ); if ( uv.x < 0 || uv.x > 1 ) { switch ( this.wrapS ) { case RepeatWrapping: uv.x = uv.x - Math.floor( uv.x ); break; case ClampToEdgeWrapping: uv.x = uv.x < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { uv.x = Math.ceil( uv.x ) - uv.x; } else { uv.x = uv.x - Math.floor( uv.x ); } break; } } if ( uv.y < 0 || uv.y > 1 ) { switch ( this.wrapT ) { case RepeatWrapping: uv.y = uv.y - Math.floor( uv.y ); break; case ClampToEdgeWrapping: uv.y = uv.y < 0 ? 0 : 1; break; case MirroredRepeatWrapping: if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { uv.y = Math.ceil( uv.y ) - uv.y; } else { uv.y = uv.y - Math.floor( uv.y ); } break; } } if ( this.flipY ) { uv.y = 1 - uv.y; } } }; Object.assign( Texture.prototype, EventDispatcher.prototype ); /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector4( x, y, z, w ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; this.w = ( w !== undefined ) ? w : 1; } Vector4.prototype = { constructor: Vector4, isVector4: true, set: function ( x, y, z, w ) { this.x = x; this.y = y; this.z = z; this.w = w; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; this.w = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setW: function ( w ) { this.w = w; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; case 3: this.w = value; break; default: throw new Error( 'index is out of range: ' + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; case 3: return this.w; default: throw new Error( 'index is out of range: ' + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z, this.w ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; this.w = ( v.w !== undefined ) ? v.w : 1; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; this.w += v.w; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; this.w += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; this.w = a.w + b.w; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; this.w += v.w * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; this.w -= v.w; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; this.w -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; this.w = a.w - b.w; return this; }, multiplyScalar: function ( scalar ) { if ( isFinite( scalar ) ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; this.w *= scalar; } else { this.x = 0; this.y = 0; this.z = 0; this.w = 0; } return this; }, applyMatrix4: function ( m ) { var x = this.x, y = this.y, z = this.z, w = this.w; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, setAxisAngleFromQuaternion: function ( q ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); var s = Math.sqrt( 1 - q.w * q.w ); if ( s < 0.0001 ) { this.x = 1; this.y = 0; this.z = 0; } else { this.x = q.x / s; this.y = q.y / s; this.z = q.z / s; } return this; }, setAxisAngleFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var angle, x, y, z, // variables for result epsilon = 0.01, // margin to allow for rounding errors epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; if ( ( Math.abs( m12 - m21 ) < epsilon ) && ( Math.abs( m13 - m31 ) < epsilon ) && ( Math.abs( m23 - m32 ) < epsilon ) ) { // singularity found // first check for identity matrix which must have +1 for all terms // in leading diagonal and zero in other terms if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && ( Math.abs( m13 + m31 ) < epsilon2 ) && ( Math.abs( m23 + m32 ) < epsilon2 ) && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { // this singularity is identity matrix so angle = 0 this.set( 1, 0, 0, 0 ); return this; // zero angle, arbitrary axis } // otherwise this singularity is angle = 180 angle = Math.PI; var xx = ( m11 + 1 ) / 2; var yy = ( m22 + 1 ) / 2; var zz = ( m33 + 1 ) / 2; var xy = ( m12 + m21 ) / 4; var xz = ( m13 + m31 ) / 4; var yz = ( m23 + m32 ) / 4; if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term if ( xx < epsilon ) { x = 0; y = 0.707106781; z = 0.707106781; } else { x = Math.sqrt( xx ); y = xy / x; z = xz / x; } } else if ( yy > zz ) { // m22 is the largest diagonal term if ( yy < epsilon ) { x = 0.707106781; y = 0; z = 0.707106781; } else { y = Math.sqrt( yy ); x = xy / y; z = yz / y; } } else { // m33 is the largest diagonal term so base result on this if ( zz < epsilon ) { x = 0.707106781; y = 0.707106781; z = 0; } else { z = Math.sqrt( zz ); x = xz / z; y = yz / z; } } this.set( x, y, z, angle ); return this; // return 180 deg rotation } // as we have reached here there are no singularities so we can handle normally var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + ( m13 - m31 ) * ( m13 - m31 ) + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize if ( Math.abs( s ) < 0.001 ) s = 1; // prevent divide by zero, should not happen if matrix is orthogonal and should be // caught by singularity test above, but I've left it in just in case this.x = ( m32 - m23 ) / s; this.y = ( m13 - m31 ) / s; this.z = ( m21 - m12 ) / s; this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); return this; }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); this.w = Math.min( this.w, v.w ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); this.w = Math.max( this.w, v.w ); return this; }, clamp: function ( min, max ) { // This function assumes min < max, if this assumption isn't true it will not operate correctly this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); this.w = Math.max( min.w, Math.min( max.w, this.w ) ); return this; }, clampScalar: function () { var min, max; return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { min = new Vector4(); max = new Vector4(); } min.set( minVal, minVal, minVal, minVal ); max.set( maxVal, maxVal, maxVal, maxVal ); return this.clamp( min, max ); }; }(), floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); this.w = Math.floor( this.w ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); this.w = Math.ceil( this.w ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); this.w = Math.round( this.w ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; this.w = - this.w; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; }, lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); }, lengthManhattan: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); }, normalize: function () { return this.divideScalar( this.length() ); }, setLength: function ( length ) { return this.multiplyScalar( length / this.length() ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; this.w += ( v.w - this.w ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; this.w = array[ offset + 3 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; array[ offset + 3 ] = this.w; return array; }, fromAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( 'THREE.Vector4: offset has been removed from .fromAttribute().' ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); this.w = attribute.getW( index ); return this; } }; /** * @author szimek / https://github.com/szimek/ * @author alteredq / http://alteredqualia.com/ * @author Marius Kintel / https://github.com/kintel */ /* In options, we can specify: * Texture parameters for an auto-generated target texture * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers */ function WebGLRenderTarget( width, height, options ) { this.uuid = _Math.generateUUID(); this.width = width; this.height = height; this.scissor = new Vector4( 0, 0, width, height ); this.scissorTest = false; this.viewport = new Vector4( 0, 0, width, height ); options = options || {}; if ( options.minFilter === undefined ) options.minFilter = LinearFilter; this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; } Object.assign( WebGLRenderTarget.prototype, EventDispatcher.prototype, { isWebGLRenderTarget: true, setSize: function ( width, height ) { if ( this.width !== width || this.height !== height ) { this.width = width; this.height = height; this.dispose(); } this.viewport.set( 0, 0, width, height ); this.scissor.set( 0, 0, width, height ); }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.width = source.width; this.height = source.height; this.viewport.copy( source.viewport ); this.texture = source.texture.clone(); this.depthBuffer = source.depthBuffer; this.stencilBuffer = source.stencilBuffer; this.depthTexture = source.depthTexture; return this; }, dispose: function () { this.dispatchEvent( { type: 'dispose' } ); } } ); /** * @author alteredq / http://alteredqualia.com */ function WebGLRenderTargetCube( width, height, options ) { WebGLRenderTarget.call( this, width, height, options ); this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 this.activeMipMapLevel = 0; } WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Quaternion( x, y, z, w ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._w = ( w !== undefined ) ? w : 1; } Quaternion.prototype = { constructor: Quaternion, get x () { return this._x; }, set x ( value ) { this._x = value; this.onChangeCallback(); }, get y () { return this._y; }, set y ( value ) { this._y = value; this.onChangeCallback(); }, get z () { return this._z; }, set z ( value ) { this._z = value; this.onChangeCallback(); }, get w () { return this._w; }, set w ( value ) { this._w = value; this.onChangeCallback(); }, set: function ( x, y, z, w ) { this._x = x; this._y = y; this._z = z; this._w = w; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._w ); }, copy: function ( quaternion ) { this._x = quaternion.x; this._y = quaternion.y; this._z = quaternion.z; this._w = quaternion.w; this.onChangeCallback(); return this; }, setFromEuler: function ( euler, update ) { if ( (euler && euler.isEuler) === false ) { throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); } // http://www.mathworks.com/matlabcentral/fileexchange/ // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ // content/SpinCalc.m var c1 = Math.cos( euler._x / 2 ); var c2 = Math.cos( euler._y / 2 ); var c3 = Math.cos( euler._z / 2 ); var s1 = Math.sin( euler._x / 2 ); var s2 = Math.sin( euler._y / 2 ); var s3 = Math.sin( euler._z / 2 ); var order = euler.order; if ( order === 'XYZ' ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === 'YXZ' ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } else if ( order === 'ZXY' ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === 'ZYX' ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } else if ( order === 'YZX' ) { this._x = s1 * c2 * c3 + c1 * s2 * s3; this._y = c1 * s2 * c3 + s1 * c2 * s3; this._z = c1 * c2 * s3 - s1 * s2 * c3; this._w = c1 * c2 * c3 - s1 * s2 * s3; } else if ( order === 'XZY' ) { this._x = s1 * c2 * c3 - c1 * s2 * s3; this._y = c1 * s2 * c3 - s1 * c2 * s3; this._z = c1 * c2 * s3 + s1 * s2 * c3; this._w = c1 * c2 * c3 + s1 * s2 * s3; } if ( update !== false ) this.onChangeCallback(); return this; }, setFromAxisAngle: function ( axis, angle ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm // assumes axis is normalized var halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; this._y = axis.y * s; this._z = axis.z * s; this._w = Math.cos( halfAngle ); this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m ) { // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements, m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], trace = m11 + m22 + m33, s; if ( trace > 0 ) { s = 0.5 / Math.sqrt( trace + 1.0 ); this._w = 0.25 / s; this._x = ( m32 - m23 ) * s; this._y = ( m13 - m31 ) * s; this._z = ( m21 - m12 ) * s; } else if ( m11 > m22 && m11 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); this._w = ( m32 - m23 ) / s; this._x = 0.25 * s; this._y = ( m12 + m21 ) / s; this._z = ( m13 + m31 ) / s; } else if ( m22 > m33 ) { s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); this._w = ( m13 - m31 ) / s; this._x = ( m12 + m21 ) / s; this._y = 0.25 * s; this._z = ( m23 + m32 ) / s; } else { s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); this._w = ( m21 - m12 ) / s; this._x = ( m13 + m31 ) / s; this._y = ( m23 + m32 ) / s; this._z = 0.25 * s; } this.onChangeCallback(); return this; }, setFromUnitVectors: function () { // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final // assumes direction vectors vFrom and vTo are normalized var v1, r; var EPS = 0.000001; return function setFromUnitVectors( vFrom, vTo ) { if ( v1 === undefined ) v1 = new Vector3(); r = vFrom.dot( vTo ) + 1; if ( r < EPS ) { r = 0; if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { v1.set( - vFrom.y, vFrom.x, 0 ); } else { v1.set( 0, - vFrom.z, vFrom.y ); } } else { v1.crossVectors( vFrom, vTo ); } this._x = v1.x; this._y = v1.y; this._z = v1.z; this._w = r; return this.normalize(); }; }(), inverse: function () { return this.conjugate().normalize(); }, conjugate: function () { this._x *= - 1; this._y *= - 1; this._z *= - 1; this.onChangeCallback(); return this; }, dot: function ( v ) { return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; }, lengthSq: function () { return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; }, length: function () { return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); }, normalize: function () { var l = this.length(); if ( l === 0 ) { this._x = 0; this._y = 0; this._z = 0; this._w = 1; } else { l = 1 / l; this._x = this._x * l; this._y = this._y * l; this._z = this._z * l; this._w = this._w * l; } this.onChangeCallback(); return this; }, multiply: function ( q, p ) { if ( p !== undefined ) { console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); return this.multiplyQuaternions( q, p ); } return this.multiplyQuaternions( this, q ); }, premultiply: function ( q ) { return this.multiplyQuaternions( q, this ); }, multiplyQuaternions: function ( a, b ) { // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; this.onChangeCallback(); return this; }, slerp: function ( qb, t ) { if ( t === 0 ) return this; if ( t === 1 ) return this.copy( qb ); var x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; if ( cosHalfTheta < 0 ) { this._w = - qb._w; this._x = - qb._x; this._y = - qb._y; this._z = - qb._z; cosHalfTheta = - cosHalfTheta; } else { this.copy( qb ); } if ( cosHalfTheta >= 1.0 ) { this._w = w; this._x = x; this._y = y; this._z = z; return this; } var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); if ( Math.abs( sinHalfTheta ) < 0.001 ) { this._w = 0.5 * ( w + this._w ); this._x = 0.5 * ( x + this._x ); this._y = 0.5 * ( y + this._y ); this._z = 0.5 * ( z + this._z ); return this; } var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; this._w = ( w * ratioA + this._w * ratioB ); this._x = ( x * ratioA + this._x * ratioB ); this._y = ( y * ratioA + this._y * ratioB ); this._z = ( z * ratioA + this._z * ratioB ); this.onChangeCallback(); return this; }, equals: function ( quaternion ) { return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this._x = array[ offset ]; this._y = array[ offset + 1 ]; this._z = array[ offset + 2 ]; this._w = array[ offset + 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._w; return array; }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} }; Object.assign( Quaternion, { slerp: function( qa, qb, qm, t ) { return qm.copy( qa ).slerp( qb, t ); }, slerpFlat: function( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { // fuzz-free, array-based Quaternion SLERP operation var x0 = src0[ srcOffset0 + 0 ], y0 = src0[ srcOffset0 + 1 ], z0 = src0[ srcOffset0 + 2 ], w0 = src0[ srcOffset0 + 3 ], x1 = src1[ srcOffset1 + 0 ], y1 = src1[ srcOffset1 + 1 ], z1 = src1[ srcOffset1 + 2 ], w1 = src1[ srcOffset1 + 3 ]; if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { var s = 1 - t, cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, dir = ( cos >= 0 ? 1 : - 1 ), sqrSin = 1 - cos * cos; // Skip the Slerp for tiny steps to avoid numeric problems: if ( sqrSin > Number.EPSILON ) { var sin = Math.sqrt( sqrSin ), len = Math.atan2( sin, cos * dir ); s = Math.sin( s * len ) / sin; t = Math.sin( t * len ) / sin; } var tDir = t * dir; x0 = x0 * s + x1 * tDir; y0 = y0 * s + y1 * tDir; z0 = z0 * s + z1 * tDir; w0 = w0 * s + w1 * tDir; // Normalize in case we just did a lerp: if ( s === 1 - t ) { var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); x0 *= f; y0 *= f; z0 *= f; w0 *= f; } } dst[ dstOffset ] = x0; dst[ dstOffset + 1 ] = y0; dst[ dstOffset + 2 ] = z0; dst[ dstOffset + 3 ] = w0; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author *kile / http://kile.stravaganza.org/ * @author philogb / http://blog.thejit.org/ * @author mikael emtinger / http://gomo.se/ * @author egraether / http://egraether.com/ * @author WestLangley / http://github.com/WestLangley */ function Vector3( x, y, z ) { this.x = x || 0; this.y = y || 0; this.z = z || 0; } Vector3.prototype = { constructor: Vector3, isVector3: true, set: function ( x, y, z ) { this.x = x; this.y = y; this.z = z; return this; }, setScalar: function ( scalar ) { this.x = scalar; this.y = scalar; this.z = scalar; return this; }, setX: function ( x ) { this.x = x; return this; }, setY: function ( y ) { this.y = y; return this; }, setZ: function ( z ) { this.z = z; return this; }, setComponent: function ( index, value ) { switch ( index ) { case 0: this.x = value; break; case 1: this.y = value; break; case 2: this.z = value; break; default: throw new Error( 'index is out of range: ' + index ); } return this; }, getComponent: function ( index ) { switch ( index ) { case 0: return this.x; case 1: return this.y; case 2: return this.z; default: throw new Error( 'index is out of range: ' + index ); } }, clone: function () { return new this.constructor( this.x, this.y, this.z ); }, copy: function ( v ) { this.x = v.x; this.y = v.y; this.z = v.z; return this; }, add: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); return this.addVectors( v, w ); } this.x += v.x; this.y += v.y; this.z += v.z; return this; }, addScalar: function ( s ) { this.x += s; this.y += s; this.z += s; return this; }, addVectors: function ( a, b ) { this.x = a.x + b.x; this.y = a.y + b.y; this.z = a.z + b.z; return this; }, addScaledVector: function ( v, s ) { this.x += v.x * s; this.y += v.y * s; this.z += v.z * s; return this; }, sub: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); return this.subVectors( v, w ); } this.x -= v.x; this.y -= v.y; this.z -= v.z; return this; }, subScalar: function ( s ) { this.x -= s; this.y -= s; this.z -= s; return this; }, subVectors: function ( a, b ) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this; }, multiply: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); return this.multiplyVectors( v, w ); } this.x *= v.x; this.y *= v.y; this.z *= v.z; return this; }, multiplyScalar: function ( scalar ) { if ( isFinite( scalar ) ) { this.x *= scalar; this.y *= scalar; this.z *= scalar; } else { this.x = 0; this.y = 0; this.z = 0; } return this; }, multiplyVectors: function ( a, b ) { this.x = a.x * b.x; this.y = a.y * b.y; this.z = a.z * b.z; return this; }, applyEuler: function () { var quaternion; return function applyEuler( euler ) { if ( (euler && euler.isEuler) === false ) { console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); } if ( quaternion === undefined ) quaternion = new Quaternion(); return this.applyQuaternion( quaternion.setFromEuler( euler ) ); }; }(), applyAxisAngle: function () { var quaternion; return function applyAxisAngle( axis, angle ) { if ( quaternion === undefined ) quaternion = new Quaternion(); return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); }; }(), applyMatrix3: function ( m ) { var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; return this; }, applyMatrix4: function ( m ) { // input: THREE.Matrix4 affine matrix var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; return this; }, applyProjection: function ( m ) { // input: THREE.Matrix4 projection matrix var x = this.x, y = this.y, z = this.z; var e = m.elements; var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; return this; }, applyQuaternion: function ( q ) { var x = this.x, y = this.y, z = this.z; var qx = q.x, qy = q.y, qz = q.z, qw = q.w; // calculate quat * vector var ix = qw * x + qy * z - qz * y; var iy = qw * y + qz * x - qx * z; var iz = qw * z + qx * y - qy * x; var iw = - qx * x - qy * y - qz * z; // calculate result * inverse quat this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; return this; }, project: function () { var matrix; return function project( camera ) { if ( matrix === undefined ) matrix = new Matrix4(); matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); return this.applyProjection( matrix ); }; }(), unproject: function () { var matrix; return function unproject( camera ) { if ( matrix === undefined ) matrix = new Matrix4(); matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); return this.applyProjection( matrix ); }; }(), transformDirection: function ( m ) { // input: THREE.Matrix4 affine matrix // vector interpreted as a direction var x = this.x, y = this.y, z = this.z; var e = m.elements; this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; return this.normalize(); }, divide: function ( v ) { this.x /= v.x; this.y /= v.y; this.z /= v.z; return this; }, divideScalar: function ( scalar ) { return this.multiplyScalar( 1 / scalar ); }, min: function ( v ) { this.x = Math.min( this.x, v.x ); this.y = Math.min( this.y, v.y ); this.z = Math.min( this.z, v.z ); return this; }, max: function ( v ) { this.x = Math.max( this.x, v.x ); this.y = Math.max( this.y, v.y ); this.z = Math.max( this.z, v.z ); return this; }, clamp: function ( min, max ) { // This function assumes min < max, if this assumption isn't true it will not operate correctly this.x = Math.max( min.x, Math.min( max.x, this.x ) ); this.y = Math.max( min.y, Math.min( max.y, this.y ) ); this.z = Math.max( min.z, Math.min( max.z, this.z ) ); return this; }, clampScalar: function () { var min, max; return function clampScalar( minVal, maxVal ) { if ( min === undefined ) { min = new Vector3(); max = new Vector3(); } min.set( minVal, minVal, minVal ); max.set( maxVal, maxVal, maxVal ); return this.clamp( min, max ); }; }(), clampLength: function ( min, max ) { var length = this.length(); return this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); }, floor: function () { this.x = Math.floor( this.x ); this.y = Math.floor( this.y ); this.z = Math.floor( this.z ); return this; }, ceil: function () { this.x = Math.ceil( this.x ); this.y = Math.ceil( this.y ); this.z = Math.ceil( this.z ); return this; }, round: function () { this.x = Math.round( this.x ); this.y = Math.round( this.y ); this.z = Math.round( this.z ); return this; }, roundToZero: function () { this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); return this; }, negate: function () { this.x = - this.x; this.y = - this.y; this.z = - this.z; return this; }, dot: function ( v ) { return this.x * v.x + this.y * v.y + this.z * v.z; }, lengthSq: function () { return this.x * this.x + this.y * this.y + this.z * this.z; }, length: function () { return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); }, lengthManhattan: function () { return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); }, normalize: function () { return this.divideScalar( this.length() ); }, setLength: function ( length ) { return this.multiplyScalar( length / this.length() ); }, lerp: function ( v, alpha ) { this.x += ( v.x - this.x ) * alpha; this.y += ( v.y - this.y ) * alpha; this.z += ( v.z - this.z ) * alpha; return this; }, lerpVectors: function ( v1, v2, alpha ) { return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); }, cross: function ( v, w ) { if ( w !== undefined ) { console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); return this.crossVectors( v, w ); } var x = this.x, y = this.y, z = this.z; this.x = y * v.z - z * v.y; this.y = z * v.x - x * v.z; this.z = x * v.y - y * v.x; return this; }, crossVectors: function ( a, b ) { var ax = a.x, ay = a.y, az = a.z; var bx = b.x, by = b.y, bz = b.z; this.x = ay * bz - az * by; this.y = az * bx - ax * bz; this.z = ax * by - ay * bx; return this; }, projectOnVector: function ( vector ) { var scalar = vector.dot( this ) / vector.lengthSq(); return this.copy( vector ).multiplyScalar( scalar ); }, projectOnPlane: function () { var v1; return function projectOnPlane( planeNormal ) { if ( v1 === undefined ) v1 = new Vector3(); v1.copy( this ).projectOnVector( planeNormal ); return this.sub( v1 ); }; }(), reflect: function () { // reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length var v1; return function reflect( normal ) { if ( v1 === undefined ) v1 = new Vector3(); return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); }; }(), angleTo: function ( v ) { var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); // clamp, to handle numerical problems return Math.acos( _Math.clamp( theta, - 1, 1 ) ); }, distanceTo: function ( v ) { return Math.sqrt( this.distanceToSquared( v ) ); }, distanceToSquared: function ( v ) { var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; return dx * dx + dy * dy + dz * dz; }, distanceToManhattan: function ( v ) { return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); }, setFromSpherical: function( s ) { var sinPhiRadius = Math.sin( s.phi ) * s.radius; this.x = sinPhiRadius * Math.sin( s.theta ); this.y = Math.cos( s.phi ) * s.radius; this.z = sinPhiRadius * Math.cos( s.theta ); return this; }, setFromCylindrical: function( c ) { this.x = c.radius * Math.sin( c.theta ); this.y = c.y; this.z = c.radius * Math.cos( c.theta ); return this; }, setFromMatrixPosition: function ( m ) { return this.setFromMatrixColumn( m, 3 ); }, setFromMatrixScale: function ( m ) { var sx = this.setFromMatrixColumn( m, 0 ).length(); var sy = this.setFromMatrixColumn( m, 1 ).length(); var sz = this.setFromMatrixColumn( m, 2 ).length(); this.x = sx; this.y = sy; this.z = sz; return this; }, setFromMatrixColumn: function ( m, index ) { if ( typeof m === 'number' ) { console.warn( 'THREE.Vector3: setFromMatrixColumn now expects ( matrix, index ).' ); var temp = m; m = index; index = temp; } return this.fromArray( m.elements, index * 4 ); }, equals: function ( v ) { return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.x = array[ offset ]; this.y = array[ offset + 1 ]; this.z = array[ offset + 2 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.x; array[ offset + 1 ] = this.y; array[ offset + 2 ] = this.z; return array; }, fromAttribute: function ( attribute, index, offset ) { if ( offset !== undefined ) { console.warn( 'THREE.Vector3: offset has been removed from .fromAttribute().' ); } this.x = attribute.getX( index ); this.y = attribute.getY( index ); this.z = attribute.getZ( index ); return this; } }; /** * @author mrdoob / http://mrdoob.com/ * @author supereggbert / http://www.paulbrunt.co.uk/ * @author philogb / http://blog.thejit.org/ * @author jordi_ros / http://plattsoft.com * @author D1plo1d / http://github.com/D1plo1d * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author timknip / http://www.floorplanner.com/ * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Matrix4() { this.elements = new Float32Array( [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ); if ( arguments.length > 0 ) { console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); } } Matrix4.prototype = { constructor: Matrix4, isMatrix4: true, set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { var te = this.elements; te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; return this; }, identity: function () { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, clone: function () { return new Matrix4().fromArray( this.elements ); }, copy: function ( m ) { this.elements.set( m.elements ); return this; }, copyPosition: function ( m ) { var te = this.elements; var me = m.elements; te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; return this; }, extractBasis: function ( xAxis, yAxis, zAxis ) { xAxis.setFromMatrixColumn( this, 0 ); yAxis.setFromMatrixColumn( this, 1 ); zAxis.setFromMatrixColumn( this, 2 ); return this; }, makeBasis: function ( xAxis, yAxis, zAxis ) { this.set( xAxis.x, yAxis.x, zAxis.x, 0, xAxis.y, yAxis.y, zAxis.y, 0, xAxis.z, yAxis.z, zAxis.z, 0, 0, 0, 0, 1 ); return this; }, extractRotation: function () { var v1; return function extractRotation( m ) { if ( v1 === undefined ) v1 = new Vector3(); var te = this.elements; var me = m.elements; var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length(); var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length(); var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length(); te[ 0 ] = me[ 0 ] * scaleX; te[ 1 ] = me[ 1 ] * scaleX; te[ 2 ] = me[ 2 ] * scaleX; te[ 4 ] = me[ 4 ] * scaleY; te[ 5 ] = me[ 5 ] * scaleY; te[ 6 ] = me[ 6 ] * scaleY; te[ 8 ] = me[ 8 ] * scaleZ; te[ 9 ] = me[ 9 ] * scaleZ; te[ 10 ] = me[ 10 ] * scaleZ; return this; }; }(), makeRotationFromEuler: function ( euler ) { if ( (euler && euler.isEuler) === false ) { console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); } var te = this.elements; var x = euler.x, y = euler.y, z = euler.z; var a = Math.cos( x ), b = Math.sin( x ); var c = Math.cos( y ), d = Math.sin( y ); var e = Math.cos( z ), f = Math.sin( z ); if ( euler.order === 'XYZ' ) { var ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = - c * f; te[ 8 ] = d; te[ 1 ] = af + be * d; te[ 5 ] = ae - bf * d; te[ 9 ] = - b * c; te[ 2 ] = bf - ae * d; te[ 6 ] = be + af * d; te[ 10 ] = a * c; } else if ( euler.order === 'YXZ' ) { var ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce + df * b; te[ 4 ] = de * b - cf; te[ 8 ] = a * d; te[ 1 ] = a * f; te[ 5 ] = a * e; te[ 9 ] = - b; te[ 2 ] = cf * b - de; te[ 6 ] = df + ce * b; te[ 10 ] = a * c; } else if ( euler.order === 'ZXY' ) { var ce = c * e, cf = c * f, de = d * e, df = d * f; te[ 0 ] = ce - df * b; te[ 4 ] = - a * f; te[ 8 ] = de + cf * b; te[ 1 ] = cf + de * b; te[ 5 ] = a * e; te[ 9 ] = df - ce * b; te[ 2 ] = - a * d; te[ 6 ] = b; te[ 10 ] = a * c; } else if ( euler.order === 'ZYX' ) { var ae = a * e, af = a * f, be = b * e, bf = b * f; te[ 0 ] = c * e; te[ 4 ] = be * d - af; te[ 8 ] = ae * d + bf; te[ 1 ] = c * f; te[ 5 ] = bf * d + ae; te[ 9 ] = af * d - be; te[ 2 ] = - d; te[ 6 ] = b * c; te[ 10 ] = a * c; } else if ( euler.order === 'YZX' ) { var ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = bd - ac * f; te[ 8 ] = bc * f + ad; te[ 1 ] = f; te[ 5 ] = a * e; te[ 9 ] = - b * e; te[ 2 ] = - d * e; te[ 6 ] = ad * f + bc; te[ 10 ] = ac - bd * f; } else if ( euler.order === 'XZY' ) { var ac = a * c, ad = a * d, bc = b * c, bd = b * d; te[ 0 ] = c * e; te[ 4 ] = - f; te[ 8 ] = d * e; te[ 1 ] = ac * f + bd; te[ 5 ] = a * e; te[ 9 ] = ad * f - bc; te[ 2 ] = bc * f - ad; te[ 6 ] = b * e; te[ 10 ] = bd * f + ac; } // last column te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // bottom row te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; }, makeRotationFromQuaternion: function ( q ) { var te = this.elements; var x = q.x, y = q.y, z = q.z, w = q.w; var x2 = x + x, y2 = y + y, z2 = z + z; var xx = x * x2, xy = x * y2, xz = x * z2; var yy = y * y2, yz = y * z2, zz = z * z2; var wx = w * x2, wy = w * y2, wz = w * z2; te[ 0 ] = 1 - ( yy + zz ); te[ 4 ] = xy - wz; te[ 8 ] = xz + wy; te[ 1 ] = xy + wz; te[ 5 ] = 1 - ( xx + zz ); te[ 9 ] = yz - wx; te[ 2 ] = xz - wy; te[ 6 ] = yz + wx; te[ 10 ] = 1 - ( xx + yy ); // last column te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; // bottom row te[ 12 ] = 0; te[ 13 ] = 0; te[ 14 ] = 0; te[ 15 ] = 1; return this; }, lookAt: function () { var x, y, z; return function lookAt( eye, target, up ) { if ( x === undefined ) { x = new Vector3(); y = new Vector3(); z = new Vector3(); } var te = this.elements; z.subVectors( eye, target ).normalize(); if ( z.lengthSq() === 0 ) { z.z = 1; } x.crossVectors( up, z ).normalize(); if ( x.lengthSq() === 0 ) { z.z += 0.0001; x.crossVectors( up, z ).normalize(); } y.crossVectors( z, x ); te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; return this; }; }(), multiply: function ( m, n ) { if ( n !== undefined ) { console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); return this.multiplyMatrices( m, n ); } return this.multiplyMatrices( this, m ); }, premultiply: function ( m ) { return this.multiplyMatrices( m, this ); }, multiplyMatrices: function ( a, b ) { var ae = a.elements; var be = b.elements; var te = this.elements; var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; }, multiplyToArray: function ( a, b, r ) { var te = this.elements; this.multiplyMatrices( a, b ); r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ]; r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ]; r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ]; r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ]; return this; }, multiplyScalar: function ( s ) { var te = this.elements; te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; return this; }, applyToVector3Array: function () { var v1; return function applyToVector3Array( array, offset, length ) { if ( v1 === undefined ) v1 = new Vector3(); if ( offset === undefined ) offset = 0; if ( length === undefined ) length = array.length; for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { v1.fromArray( array, j ); v1.applyMatrix4( this ); v1.toArray( array, j ); } return array; }; }(), applyToBufferAttribute: function () { var v1; return function applyToBufferAttribute( attribute ) { if ( v1 === undefined ) v1 = new Vector3(); for ( var i = 0, l = attribute.count; i < l; i ++ ) { v1.x = attribute.getX( i ); v1.y = attribute.getY( i ); v1.z = attribute.getZ( i ); v1.applyMatrix4( this ); attribute.setXYZ( i, v1.x, v1.y, v1.z ); } return attribute; }; }(), determinant: function () { var te = this.elements; var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; //TODO: make this more efficient //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) return ( n41 * ( + n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34 ) + n42 * ( + n11 * n23 * n34 - n11 * n24 * n33 + n14 * n21 * n33 - n13 * n21 * n34 + n13 * n24 * n31 - n14 * n23 * n31 ) + n43 * ( + n11 * n24 * n32 - n11 * n22 * n34 - n14 * n21 * n32 + n12 * n21 * n34 + n14 * n22 * n31 - n12 * n24 * n31 ) + n44 * ( - n13 * n22 * n31 - n11 * n23 * n32 + n11 * n22 * n33 + n13 * n21 * n32 - n12 * n21 * n33 + n12 * n23 * n31 ) ); }, transpose: function () { var te = this.elements; var tmp; tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; return this; }, setPosition: function ( v ) { var te = this.elements; te[ 12 ] = v.x; te[ 13 ] = v.y; te[ 14 ] = v.z; return this; }, getInverse: function ( m, throwOnDegenerate ) { // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm var te = this.elements, me = m.elements, n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; if ( det === 0 ) { var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0"; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } var detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; te[ 4 ] = t12 * detInv; te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; te[ 8 ] = t13 * detInv; te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; te[ 12 ] = t14 * detInv; te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; return this; }, scale: function ( v ) { var te = this.elements; var x = v.x, y = v.y, z = v.z; te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; return this; }, getMaxScaleOnAxis: function () { var te = this.elements; var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); }, makeTranslation: function ( x, y, z ) { this.set( 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 ); return this; }, makeRotationX: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( 1, 0, 0, 0, 0, c, - s, 0, 0, s, c, 0, 0, 0, 0, 1 ); return this; }, makeRotationY: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, 0, s, 0, 0, 1, 0, 0, - s, 0, c, 0, 0, 0, 0, 1 ); return this; }, makeRotationZ: function ( theta ) { var c = Math.cos( theta ), s = Math.sin( theta ); this.set( c, - s, 0, 0, s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, makeRotationAxis: function ( axis, angle ) { // Based on http://www.gamedev.net/reference/articles/article1199.asp var c = Math.cos( angle ); var s = Math.sin( angle ); var t = 1 - c; var x = axis.x, y = axis.y, z = axis.z; var tx = t * x, ty = t * y; this.set( tx * x + c, tx * y - s * z, tx * z + s * y, 0, tx * y + s * z, ty * y + c, ty * z - s * x, 0, tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 0, 0, 0, 1 ); return this; }, makeScale: function ( x, y, z ) { this.set( x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 ); return this; }, makeShear: function ( x, y, z ) { this.set( 1, y, z, 0, x, 1, z, 0, x, y, 1, 0, 0, 0, 0, 1 ); return this; }, compose: function ( position, quaternion, scale ) { this.makeRotationFromQuaternion( quaternion ); this.scale( scale ); this.setPosition( position ); return this; }, decompose: function () { var vector, matrix; return function decompose( position, quaternion, scale ) { if ( vector === undefined ) { vector = new Vector3(); matrix = new Matrix4(); } var te = this.elements; var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); // if determine is negative, we need to invert one scale var det = this.determinant(); if ( det < 0 ) { sx = - sx; } position.x = te[ 12 ]; position.y = te[ 13 ]; position.z = te[ 14 ]; // scale the rotation part matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy() var invSX = 1 / sx; var invSY = 1 / sy; var invSZ = 1 / sz; matrix.elements[ 0 ] *= invSX; matrix.elements[ 1 ] *= invSX; matrix.elements[ 2 ] *= invSX; matrix.elements[ 4 ] *= invSY; matrix.elements[ 5 ] *= invSY; matrix.elements[ 6 ] *= invSY; matrix.elements[ 8 ] *= invSZ; matrix.elements[ 9 ] *= invSZ; matrix.elements[ 10 ] *= invSZ; quaternion.setFromRotationMatrix( matrix ); scale.x = sx; scale.y = sy; scale.z = sz; return this; }; }(), makeFrustum: function ( left, right, bottom, top, near, far ) { var te = this.elements; var x = 2 * near / ( right - left ); var y = 2 * near / ( top - bottom ); var a = ( right + left ) / ( right - left ); var b = ( top + bottom ) / ( top - bottom ); var c = - ( far + near ) / ( far - near ); var d = - 2 * far * near / ( far - near ); te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; return this; }, makePerspective: function ( fov, aspect, near, far ) { var ymax = near * Math.tan( _Math.DEG2RAD * fov * 0.5 ); var ymin = - ymax; var xmin = ymin * aspect; var xmax = ymax * aspect; return this.makeFrustum( xmin, xmax, ymin, ymax, near, far ); }, makeOrthographic: function ( left, right, top, bottom, near, far ) { var te = this.elements; var w = 1.0 / ( right - left ); var h = 1.0 / ( top - bottom ); var p = 1.0 / ( far - near ); var x = ( right + left ) * w; var y = ( top + bottom ) * h; var z = ( far + near ) * p; te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; return this; }, equals: function ( matrix ) { var te = this.elements; var me = matrix.elements; for ( var i = 0; i < 16; i ++ ) { if ( te[ i ] !== me[ i ] ) return false; } return true; }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; for( var i = 0; i < 16; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; var te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; array[ offset + 9 ] = te[ 9 ]; array[ offset + 10 ] = te[ 10 ]; array[ offset + 11 ] = te[ 11 ]; array[ offset + 12 ] = te[ 12 ]; array[ offset + 13 ] = te[ 13 ]; array[ offset + 14 ] = te[ 14 ]; array[ offset + 15 ] = te[ 15 ]; return array; } }; /** * @author mrdoob / http://mrdoob.com/ */ function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { images = images !== undefined ? images : []; mapping = mapping !== undefined ? mapping : CubeReflectionMapping; Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.flipY = false; } CubeTexture.prototype = Object.create( Texture.prototype ); CubeTexture.prototype.constructor = CubeTexture; CubeTexture.prototype.isCubeTexture = true; Object.defineProperty( CubeTexture.prototype, 'images', { get: function () { return this.image; }, set: function ( value ) { this.image = value; } } ); /** * @author tschw * * Uniforms of a program. * Those form a tree structure with a special top-level container for the root, * which you get by calling 'new WebGLUniforms( gl, program, renderer )'. * * * Properties of inner nodes including the top-level container: * * .seq - array of nested uniforms * .map - nested uniforms by name * * * Methods of all nodes except the top-level container: * * .setValue( gl, value, [renderer] ) * * uploads a uniform value(s) * the 'renderer' parameter is needed for sampler uniforms * * * Static methods of the top-level container (renderer factorizations): * * .upload( gl, seq, values, renderer ) * * sets uniforms in 'seq' to 'values[id].value' * * .seqWithValue( seq, values ) : filteredSeq * * filters 'seq' entries with corresponding entry in values * * * Methods of the top-level container (renderer factorizations): * * .setValue( gl, name, value ) * * sets uniform with name 'name' to 'value' * * .set( gl, obj, prop ) * * sets uniform from object and property with same name than uniform * * .setOptional( gl, obj, prop ) * * like .set for an optional property of the object * */ var emptyTexture = new Texture(); var emptyCubeTexture = new CubeTexture(); // --- Base for inner nodes (including the root) --- function UniformContainer() { this.seq = []; this.map = {}; } // --- Utilities --- // Array Caches (provide typed arrays for temporary by size) var arrayCacheF32 = []; var arrayCacheI32 = []; // Flattening for arrays of vectors and matrices function flatten( array, nBlocks, blockSize ) { var firstElem = array[ 0 ]; if ( firstElem <= 0 || firstElem > 0 ) return array; // unoptimized: ! isNaN( firstElem ) // see http://jacksondunstan.com/articles/983 var n = nBlocks * blockSize, r = arrayCacheF32[ n ]; if ( r === undefined ) { r = new Float32Array( n ); arrayCacheF32[ n ] = r; } if ( nBlocks !== 0 ) { firstElem.toArray( r, 0 ); for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { offset += blockSize; array[ i ].toArray( r, offset ); } } return r; } // Texture unit allocation function allocTexUnits( renderer, n ) { var r = arrayCacheI32[ n ]; if ( r === undefined ) { r = new Int32Array( n ); arrayCacheI32[ n ] = r; } for ( var i = 0; i !== n; ++ i ) r[ i ] = renderer.allocTextureUnit(); return r; } // --- Setters --- // Note: Defining these methods externally, because they come in a bunch // and this way their names minify. // Single scalar function setValue1f( gl, v ) { gl.uniform1f( this.addr, v ); } function setValue1i( gl, v ) { gl.uniform1i( this.addr, v ); } // Single float vector (from flat array or THREE.VectorN) function setValue2fv( gl, v ) { if ( v.x === undefined ) gl.uniform2fv( this.addr, v ); else gl.uniform2f( this.addr, v.x, v.y ); } function setValue3fv( gl, v ) { if ( v.x !== undefined ) gl.uniform3f( this.addr, v.x, v.y, v.z ); else if ( v.r !== undefined ) gl.uniform3f( this.addr, v.r, v.g, v.b ); else gl.uniform3fv( this.addr, v ); } function setValue4fv( gl, v ) { if ( v.x === undefined ) gl.uniform4fv( this.addr, v ); else gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); } // Single matrix (from flat array or MatrixN) function setValue2fm( gl, v ) { gl.uniformMatrix2fv( this.addr, false, v.elements || v ); } function setValue3fm( gl, v ) { gl.uniformMatrix3fv( this.addr, false, v.elements || v ); } function setValue4fm( gl, v ) { gl.uniformMatrix4fv( this.addr, false, v.elements || v ); } // Single texture (2D / Cube) function setValueT1( gl, v, renderer ) { var unit = renderer.allocTextureUnit(); gl.uniform1i( this.addr, unit ); renderer.setTexture2D( v || emptyTexture, unit ); } function setValueT6( gl, v, renderer ) { var unit = renderer.allocTextureUnit(); gl.uniform1i( this.addr, unit ); renderer.setTextureCube( v || emptyCubeTexture, unit ); } // Integer / Boolean vectors or arrays thereof (always flat arrays) function setValue2iv( gl, v ) { gl.uniform2iv( this.addr, v ); } function setValue3iv( gl, v ) { gl.uniform3iv( this.addr, v ); } function setValue4iv( gl, v ) { gl.uniform4iv( this.addr, v ); } // Helper to pick the right setter for the singular case function getSingularSetter( type ) { switch ( type ) { case 0x1406: return setValue1f; // FLOAT case 0x8b50: return setValue2fv; // _VEC2 case 0x8b51: return setValue3fv; // _VEC3 case 0x8b52: return setValue4fv; // _VEC4 case 0x8b5a: return setValue2fm; // _MAT2 case 0x8b5b: return setValue3fm; // _MAT3 case 0x8b5c: return setValue4fm; // _MAT4 case 0x8b5e: return setValueT1; // SAMPLER_2D case 0x8b60: return setValueT6; // SAMPLER_CUBE case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } } // Array of scalars function setValue1fv( gl, v ) { gl.uniform1fv( this.addr, v ); } function setValue1iv( gl, v ) { gl.uniform1iv( this.addr, v ); } // Array of vectors (flat or from THREE classes) function setValueV2a( gl, v ) { gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) ); } function setValueV3a( gl, v ) { gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) ); } function setValueV4a( gl, v ) { gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) ); } // Array of matrices (flat or from THREE clases) function setValueM2a( gl, v ) { gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) ); } function setValueM3a( gl, v ) { gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) ); } function setValueM4a( gl, v ) { gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) ); } // Array of textures (2D / Cube) function setValueT1a( gl, v, renderer ) { var n = v.length, units = allocTexUnits( renderer, n ); gl.uniform1iv( this.addr, units ); for ( var i = 0; i !== n; ++ i ) { renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); } } function setValueT6a( gl, v, renderer ) { var n = v.length, units = allocTexUnits( renderer, n ); gl.uniform1iv( this.addr, units ); for ( var i = 0; i !== n; ++ i ) { renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } } // Helper to pick the right setter for a pure (bottom-level) array function getPureArraySetter( type ) { switch ( type ) { case 0x1406: return setValue1fv; // FLOAT case 0x8b50: return setValueV2a; // _VEC2 case 0x8b51: return setValueV3a; // _VEC3 case 0x8b52: return setValueV4a; // _VEC4 case 0x8b5a: return setValueM2a; // _MAT2 case 0x8b5b: return setValueM3a; // _MAT3 case 0x8b5c: return setValueM4a; // _MAT4 case 0x8b5e: return setValueT1a; // SAMPLER_2D case 0x8b60: return setValueT6a; // SAMPLER_CUBE case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } } // --- Uniform Classes --- function SingleUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.setValue = getSingularSetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } function PureArrayUniform( id, activeInfo, addr ) { this.id = id; this.addr = addr; this.size = activeInfo.size; this.setValue = getPureArraySetter( activeInfo.type ); // this.path = activeInfo.name; // DEBUG } function StructuredUniform( id ) { this.id = id; UniformContainer.call( this ); // mix-in } StructuredUniform.prototype.setValue = function( gl, value ) { // Note: Don't need an extra 'renderer' parameter, since samplers // are not allowed in structured uniforms. var seq = this.seq; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; u.setValue( gl, value[ u.id ] ); } }; // --- Top-level --- // Parser - builds up the property tree from the path strings var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g; // extracts // - the identifier (member name or array index) // - followed by an optional right bracket (found when array index) // - followed by an optional left bracket or dot (type of subscript) // // Note: These portions can be read in a non-overlapping fashion and // allow straightforward parsing of the hierarchy that WebGL encodes // in the uniform names. function addUniform( container, uniformObject ) { container.seq.push( uniformObject ); container.map[ uniformObject.id ] = uniformObject; } function parseUniform( activeInfo, addr, container ) { var path = activeInfo.name, pathLength = path.length; // reset RegExp object, because of the early exit of a previous run RePathPart.lastIndex = 0; for (; ;) { var match = RePathPart.exec( path ), matchEnd = RePathPart.lastIndex, id = match[ 1 ], idIsIndex = match[ 2 ] === ']', subscript = match[ 3 ]; if ( idIsIndex ) id = id | 0; // convert to integer if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { // bare name or "pure" bottom-level array "[0]" suffix addUniform( container, subscript === undefined ? new SingleUniform( id, activeInfo, addr ) : new PureArrayUniform( id, activeInfo, addr ) ); break; } else { // step into inner node / create it in case it doesn't exist var map = container.map, next = map[ id ]; if ( next === undefined ) { next = new StructuredUniform( id ); addUniform( container, next ); } container = next; } } } // Root Container function WebGLUniforms( gl, program, renderer ) { UniformContainer.call( this ); this.renderer = renderer; var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); for ( var i = 0; i !== n; ++ i ) { var info = gl.getActiveUniform( program, i ), path = info.name, addr = gl.getUniformLocation( program, path ); parseUniform( info, addr, this ); } } WebGLUniforms.prototype.setValue = function( gl, name, value ) { var u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, value, this.renderer ); }; WebGLUniforms.prototype.set = function( gl, object, name ) { var u = this.map[ name ]; if ( u !== undefined ) u.setValue( gl, object[ name ], this.renderer ); }; WebGLUniforms.prototype.setOptional = function( gl, object, name ) { var v = object[ name ]; if ( v !== undefined ) this.setValue( gl, name, v ); }; // Static interface WebGLUniforms.upload = function( gl, seq, values, renderer ) { for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ], v = values[ u.id ]; if ( v.needsUpdate !== false ) { // note: always updating when .needsUpdate is undefined u.setValue( gl, v.value, renderer ); } } }; WebGLUniforms.seqWithValue = function( seq, values ) { var r = []; for ( var i = 0, n = seq.length; i !== n; ++ i ) { var u = seq[ i ]; if ( u.id in values ) r.push( u ); } return r; }; /** * Uniform Utilities */ var UniformsUtils = { merge: function ( uniforms ) { var merged = {}; for ( var u = 0; u < uniforms.length; u ++ ) { var tmp = this.clone( uniforms[ u ] ); for ( var p in tmp ) { merged[ p ] = tmp[ p ]; } } return merged; }, clone: function ( uniforms_src ) { var uniforms_dst = {}; for ( var u in uniforms_src ) { uniforms_dst[ u ] = {}; for ( var p in uniforms_src[ u ] ) { var parameter_src = uniforms_src[ u ][ p ]; if ( parameter_src && ( parameter_src.isColor || parameter_src.isMatrix3 || parameter_src.isMatrix4 || parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 || parameter_src.isTexture ) ) { uniforms_dst[ u ][ p ] = parameter_src.clone(); } else if ( Array.isArray( parameter_src ) ) { uniforms_dst[ u ][ p ] = parameter_src.slice(); } else { uniforms_dst[ u ][ p ] = parameter_src; } } } return uniforms_dst; } }; var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n"; var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif\n"; var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n"; var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif\n"; var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; var begin_vertex = "\nvec3 transformed = vec3( position );\n"; var beginnormal_vertex = "\nvec3 objectNormal = vec3( normal );\n"; var bsdfs = "float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\t\tif( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\t\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\t\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t\treturn distanceFalloff * maxDistanceCutoffFactor;\n#else\n\t\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n\t\t}\n\t\treturn 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 ltcTextureCoords( const in GeometricContext geometry, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = (LUT_SIZE - 1.0)/LUT_SIZE;\n\tconst float LUT_BIAS = 0.5/LUT_SIZE;\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 P = geometry.position;\n\tfloat theta = acos( dot( N, V ) );\n\tvec2 uv = vec2(\n\t\tsqrt( saturate( roughness ) ),\n\t\tsaturate( theta / ( 0.5 * PI ) ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nvoid clipQuadToHorizon( inout vec3 L[5], out int n ) {\n\tint config = 0;\n\tif ( L[0].z > 0.0 ) config += 1;\n\tif ( L[1].z > 0.0 ) config += 2;\n\tif ( L[2].z > 0.0 ) config += 4;\n\tif ( L[3].z > 0.0 ) config += 8;\n\tn = 0;\n\tif ( config == 0 ) {\n\t} else if ( config == 1 ) {\n\t\tn = 3;\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t\tL[2] = -L[3].z * L[0] + L[0].z * L[3];\n\t} else if ( config == 2 ) {\n\t\tn = 3;\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t} else if ( config == 3 ) {\n\t\tn = 4;\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t\tL[3] = -L[3].z * L[0] + L[0].z * L[3];\n\t} else if ( config == 4 ) {\n\t\tn = 3;\n\t\tL[0] = -L[3].z * L[2] + L[2].z * L[3];\n\t\tL[1] = -L[1].z * L[2] + L[2].z * L[1];\n\t} else if ( config == 5 ) {\n\t\tn = 0;\n\t} else if ( config == 6 ) {\n\t\tn = 4;\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t\tL[3] = -L[3].z * L[2] + L[2].z * L[3];\n\t} else if ( config == 7 ) {\n\t\tn = 5;\n\t\tL[4] = -L[3].z * L[0] + L[0].z * L[3];\n\t\tL[3] = -L[3].z * L[2] + L[2].z * L[3];\n\t} else if ( config == 8 ) {\n\t\tn = 3;\n\t\tL[0] = -L[0].z * L[3] + L[3].z * L[0];\n\t\tL[1] = -L[2].z * L[3] + L[3].z * L[2];\n\t\tL[2] = L[3];\n\t} else if ( config == 9 ) {\n\t\tn = 4;\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t\tL[2] = -L[2].z * L[3] + L[3].z * L[2];\n\t} else if ( config == 10 ) {\n\t\tn = 0;\n\t} else if ( config == 11 ) {\n\t\tn = 5;\n\t\tL[4] = L[3];\n\t\tL[3] = -L[2].z * L[3] + L[3].z * L[2];\n\t\tL[2] = -L[2].z * L[1] + L[1].z * L[2];\n\t} else if ( config == 12 ) {\n\t\tn = 4;\n\t\tL[1] = -L[1].z * L[2] + L[2].z * L[1];\n\t\tL[0] = -L[0].z * L[3] + L[3].z * L[0];\n\t} else if ( config == 13 ) {\n\t\tn = 5;\n\t\tL[4] = L[3];\n\t\tL[3] = L[2];\n\t\tL[2] = -L[1].z * L[2] + L[2].z * L[1];\n\t\tL[1] = -L[1].z * L[0] + L[0].z * L[1];\n\t} else if ( config == 14 ) {\n\t\tn = 5;\n\t\tL[4] = -L[0].z * L[3] + L[3].z * L[0];\n\t\tL[0] = -L[0].z * L[1] + L[1].z * L[0];\n\t} else if ( config == 15 ) {\n\t\tn = 4;\n\t}\n\tif ( n == 3 )\n\t\tL[3] = L[0];\n\tif ( n == 4 )\n\t\tL[4] = L[0];\n}\nfloat integrateLtcBrdfOverRectEdge( vec3 v1, vec3 v2 ) {\n\tfloat cosTheta = dot( v1, v2 );\n\tfloat theta = acos( cosTheta );\n\tfloat res = cross( v1, v2 ).z * ( ( theta > 0.001 ) ? theta / sin( theta ) : 1.0 );\n\treturn res;\n}\nvoid initRectPoints( const in vec3 pos, const in vec3 halfWidth, const in vec3 halfHeight, out vec3 rectPoints[4] ) {\n\trectPoints[0] = pos - halfWidth - halfHeight;\n\trectPoints[1] = pos + halfWidth - halfHeight;\n\trectPoints[2] = pos + halfWidth + halfHeight;\n\trectPoints[3] = pos - halfWidth + halfHeight;\n}\nvec3 integrateLtcBrdfOverRect( const in GeometricContext geometry, const in mat3 brdfMat, const in vec3 rectPoints[4] ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 P = geometry.position;\n\tvec3 T1, T2;\n\tT1 = normalize(V - N * dot( V, N ));\n\tT2 = - cross( N, T1 );\n\tmat3 brdfWrtSurface = brdfMat * transpose( mat3( T1, T2, N ) );\n\tvec3 clippedRect[5];\n\tclippedRect[0] = brdfWrtSurface * ( rectPoints[0] - P );\n\tclippedRect[1] = brdfWrtSurface * ( rectPoints[1] - P );\n\tclippedRect[2] = brdfWrtSurface * ( rectPoints[2] - P );\n\tclippedRect[3] = brdfWrtSurface * ( rectPoints[3] - P );\n\tint n;\n\tclipQuadToHorizon(clippedRect, n);\n\tif ( n == 0 )\n\t\treturn vec3( 0, 0, 0 );\n\tclippedRect[0] = normalize( clippedRect[0] );\n\tclippedRect[1] = normalize( clippedRect[1] );\n\tclippedRect[2] = normalize( clippedRect[2] );\n\tclippedRect[3] = normalize( clippedRect[3] );\n\tclippedRect[4] = normalize( clippedRect[4] );\n\tfloat sum = 0.0;\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[0], clippedRect[1] );\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[1], clippedRect[2] );\n\tsum += integrateLtcBrdfOverRectEdge( clippedRect[2], clippedRect[3] );\n\tif (n >= 4)\n\t\tsum += integrateLtcBrdfOverRectEdge( clippedRect[3], clippedRect[4] );\n\tif (n == 5)\n\t\tsum += integrateLtcBrdfOverRectEdge( clippedRect[4], clippedRect[0] );\n\tsum = max( 0.0, sum );\n\tvec3 Lo_i = vec3( sum, sum, sum );\n\treturn Lo_i;\n}\nvec3 Rect_Area_Light_Specular_Reflectance(\n\t\tconst in GeometricContext geometry,\n\t\tconst in vec3 lightPos, const in vec3 lightHalfWidth, const in vec3 lightHalfHeight,\n\t\tconst in float roughness,\n\t\tconst in sampler2D ltcMat, const in sampler2D ltcMag ) {\n\tvec3 rectPoints[4];\n\tinitRectPoints( lightPos, lightHalfWidth, lightHalfHeight, rectPoints );\n\tvec2 uv = ltcTextureCoords( geometry, roughness );\n\tvec4 brdfLtcApproxParams, t;\n\tbrdfLtcApproxParams = texture2D( ltcMat, uv );\n\tt = texture2D( ltcMat, uv );\n\tfloat brdfLtcScalar = texture2D( ltcMag, uv ).a;\n\tmat3 brdfLtcApproxMat = mat3(\n\t\tvec3( 1, 0, t.y ),\n\t\tvec3( 0, t.z, 0 ),\n\t\tvec3( t.w, 0, t.x )\n\t);\n\tvec3 specularReflectance = integrateLtcBrdfOverRect( geometry, brdfLtcApproxMat, rectPoints );\n\tspecularReflectance *= brdfLtcScalar;\n\treturn specularReflectance;\n}\nvec3 Rect_Area_Light_Diffuse_Reflectance(\n\t\tconst in GeometricContext geometry,\n\t\tconst in vec3 lightPos, const in vec3 lightHalfWidth, const in vec3 lightHalfHeight ) {\n\tvec3 rectPoints[4];\n\tinitRectPoints( lightPos, lightHalfWidth, lightHalfHeight, rectPoints );\n\tmat3 diffuseBrdfMat = mat3(1);\n\tvec3 diffuseReflectance = integrateLtcBrdfOverRect( geometry, diffuseBrdfMat, rectPoints );\n\treturn diffuseReflectance;\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n\treturn specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n"; var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = dFdx( surf_pos );\n\t\tvec3 vSigmaY = dFdy( surf_pos );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif\n"; var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; ++ i ) {\n\t\tvec4 plane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t\t\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; ++ i ) {\n\t\t\tvec4 plane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t\n\t#endif\n#endif\n"; var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n"; var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvarying vec3 vViewPosition;\n#endif\n"; var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n"; var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif\n"; var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif"; var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transpose( const in mat3 v ) {\n\tmat3 tmp;\n\ttmp[0] = vec3(v[0].x, v[1].x, v[2].x);\n\ttmp[1] = vec3(v[0].y, v[1].y, v[2].y);\n\ttmp[2] = vec3(v[0].z, v[1].z, v[2].z);\n\treturn tmp;\n}\n"; var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif\n"; var defaultnormal_vertex = "#ifdef FLIP_SIDED\n\tobjectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif\n"; var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n"; var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n"; var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif\n"; var encodings_fragment = " gl_FragColor = linearToOutputTexel( gl_FragColor );\n"; var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n return value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n float maxComponent = max( max( value.r, value.g ), value.b );\n float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n return vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n float maxRGB = max( value.x, max( value.g, value.b ) );\n float M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n M = ceil( M * 255.0 ) / 255.0;\n return vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n float maxRGB = max( value.x, max( value.g, value.b ) );\n float D = max( maxRange / maxRGB, 1.0 );\n D = min( floor( D ) / 255.0, 1.0 );\n return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n vec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n vec4 vResult;\n vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n vResult.w = fract(Le);\n vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n return vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n float Le = value.z * 255.0 + value.w;\n vec3 Xp_Y_XYZp;\n Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n return vec4( max(vRGB, 0.0), 1.0 );\n}\n"; var envmap_fragment = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\tsampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n\t\tsampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\tvec3 reflectView = flipNormal * normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif\n"; var envmap_pars_fragment = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n\tuniform float reflectivity;\n\tuniform float envMapIntensity;\n#endif\n#ifdef USE_ENVMAP\n\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n\t\tvarying vec3 vWorldPosition;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\tuniform float flipEnvMap;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif\n"; var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif\n"; var envmap_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif\n"; var fog_fragment = "#ifdef USE_FOG\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tfloat depth = gl_FragDepthEXT / gl_FragCoord.w;\n\t#else\n\t\tfloat depth = gl_FragCoord.z / gl_FragCoord.w;\n\t#endif\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, depth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n"; var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif\n"; var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n"; var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif\n"; var lights_pars = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltcMat;\tuniform sampler2D ltcMag;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif\n#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = saturate( reflectVec.y * 0.5 + 0.5 );\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif\n"; var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n"; var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\n#if NUM_RECT_AREA_LIGHTS > 0\n void RE_Direct_RectArea_BlinnPhong( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n vec3 matDiffColor = material.diffuseColor;\n vec3 matSpecColor = material.specularColor;\n vec3 lightColor = rectAreaLight.color;\n float roughness = BlinnExponentToGGXRoughness( material.specularShininess );\n vec3 spec = Rect_Area_Light_Specular_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight,\n roughness,\n ltcMat, ltcMag );\n vec3 diff = Rect_Area_Light_Diffuse_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight );\n reflectedLight.directSpecular += lightColor * matSpecColor * spec / PI2;\n reflectedLight.directDiffuse += lightColor * matDiffColor * diff / PI2;\n }\n#endif\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)\n"; var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.clearCoat = saturate( clearCoat );\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n"; var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n\t#ifndef STANDARD\n\t\tfloat clearCoat;\n\t\tfloat clearCoatRoughness;\n\t#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n vec3 matDiffColor = material.diffuseColor;\n vec3 matSpecColor = material.specularColor;\n vec3 lightColor = rectAreaLight.color;\n float roughness = material.specularRoughness;\n vec3 spec = Rect_Area_Light_Specular_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight,\n roughness,\n ltcMat, ltcMag );\n vec3 diff = Rect_Area_Light_Diffuse_Reflectance(\n geometry,\n rectAreaLight.position, rectAreaLight.halfWidth, rectAreaLight.halfHeight );\n reflectedLight.directSpecular += lightColor * matSpecColor * spec;\n reflectedLight.directDiffuse += lightColor * matDiffColor * diff;\n }\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifndef STANDARD\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\t#ifndef STANDARD\n\t\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifndef STANDARD\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\tfloat dotNL = dotNV;\n\t\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n\t#else\n\t\tfloat clearCoatDHR = 0.0;\n\t#endif\n\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n\t#ifndef STANDARD\n\t\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n\t#endif\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n"; var lights_template = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#ifdef USE_SHADOWMAP\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t \tirradiance += getLightProbeIndirectIrradiance( geometry, 8 );\n\t#endif\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tvec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\n\t#ifndef STANDARD\n\t\tvec3 clearCoatRadiance = getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), 8 );\n\t#else\n\t\tvec3 clearCoatRadiance = vec3( 0.0 );\n\t#endif\n\t\t\n\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n"; var logdepthbuf_fragment = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\tgl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n#endif"; var logdepthbuf_pars_fragment = "#ifdef USE_LOGDEPTHBUF\n\tuniform float logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n#endif\n"; var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#endif\n\tuniform float logDepthBufFC;\n#endif"; var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\tgl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\t#endif\n#endif\n"; var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif\n"; var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n"; var map_particle_fragment = "#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n"; var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform vec4 offsetRepeat;\n\tuniform sampler2D map;\n#endif\n"; var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.r;\n#endif\n"; var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n"; var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif\n"; var normal_flip = "#ifdef DOUBLE_SIDED\n\tfloat flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n#else\n\tfloat flipNormal = 1.0;\n#endif\n"; var normal_fragment = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal ) * flipNormal;\n#endif\n#ifdef USE_NORMALMAP\n\tnormal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n"; var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\n\t\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n\t\tvec3 N = normalize( surf_norm );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif\n"; var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n return normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n return 1.0 - 2.0 * rgb.xyz;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n return ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n return linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n return (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n return ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n"; var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif\n"; var project_vertex = "#ifdef USE_SKINNING\n\tvec4 mvPosition = modelViewMatrix * skinned;\n#else\n\tvec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;\n"; var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.r;\n#endif\n"; var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\treturn (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn 1.0;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\tfloat dp = ( length( lightToPosition ) - shadowBias ) / 1000.0;\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif\n"; var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n#endif\n"; var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n #if NUM_RECT_AREA_LIGHTS > 0\n #endif\n#endif\n"; var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHTS > 0\n\tDirectionalLight directionalLight;\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHTS > 0\n\tSpotLight spotLight;\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHTS > 0\n\tPointLight pointLight;\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_RECT_AREA_LIGHTS > 0\n\t#endif\n\t#endif\n\treturn shadow;\n}\n"; var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureWidth;\n\t\tuniform int boneTextureHeight;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureWidth ) );\n\t\t\tfloat y = floor( j / float( boneTextureWidth ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureWidth );\n\t\t\tfloat dy = 1.0 / float( boneTextureHeight );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif\n"; var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\tskinned = bindMatrixInverse * skinned;\n#endif\n"; var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n"; var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; var tonemapping_fragment = "#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n"; var tonemapping_pars_fragment = "#define saturate(a) clamp( a, 0.0, 1.0 )\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n return toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n return saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n color = max( vec3( 0.0 ), color - 0.004 );\n return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n"; var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif"; var uv_pars_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n\tuniform vec4 offsetRepeat;\n#endif\n"; var uv_vertex = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif"; var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif"; var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif"; var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\t#ifdef USE_SKINNING\n\t\tvec4 worldPosition = modelMatrix * skinned;\n\t#else\n\t\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\t#endif\n#endif\n"; var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n"; var cube_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n"; var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n"; var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var distanceRGBA_frag = "uniform vec3 lightPos;\nvarying vec4 vWorldPosition;\n#include \n#include \n#include \nvoid main () {\n\t#include \n\tgl_FragColor = packDepthToRGBA( length( vWorldPosition.xyz - lightPos.xyz ) / 1000.0 );\n}\n"; var distanceRGBA_vert = "varying vec4 vWorldPosition;\n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition;\n}\n"; var equirect_frag = "uniform sampler2D tEquirect;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n"; var equirect_vert = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}\n"; var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n}\n"; var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}\n"; var meshphysical_frag = "#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n\tuniform float clearCoat;\n\tuniform float clearCoatRoughness;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var meshphysical_vert = "#define PHYSICAL\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n}\n"; var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n\t#include \n\t#include \n}\n"; var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}\n"; var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#ifdef USE_SIZEATTENUATION\n\t\tgl_PointSize = size * ( scale / - mvPosition.z );\n\t#else\n\t\tgl_PointSize = size;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var shadow_frag = "uniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( 0.0, 0.0, 0.0, opacity * ( 1.0 - getShadowMask() ) );\n}\n"; var shadow_vert = "#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n}\n"; var ShaderChunk = { alphamap_fragment: alphamap_fragment, alphamap_pars_fragment: alphamap_pars_fragment, alphatest_fragment: alphatest_fragment, aomap_fragment: aomap_fragment, aomap_pars_fragment: aomap_pars_fragment, begin_vertex: begin_vertex, beginnormal_vertex: beginnormal_vertex, bsdfs: bsdfs, bumpmap_pars_fragment: bumpmap_pars_fragment, clipping_planes_fragment: clipping_planes_fragment, clipping_planes_pars_fragment: clipping_planes_pars_fragment, clipping_planes_pars_vertex: clipping_planes_pars_vertex, clipping_planes_vertex: clipping_planes_vertex, color_fragment: color_fragment, color_pars_fragment: color_pars_fragment, color_pars_vertex: color_pars_vertex, color_vertex: color_vertex, common: common, cube_uv_reflection_fragment: cube_uv_reflection_fragment, defaultnormal_vertex: defaultnormal_vertex, displacementmap_pars_vertex: displacementmap_pars_vertex, displacementmap_vertex: displacementmap_vertex, emissivemap_fragment: emissivemap_fragment, emissivemap_pars_fragment: emissivemap_pars_fragment, encodings_fragment: encodings_fragment, encodings_pars_fragment: encodings_pars_fragment, envmap_fragment: envmap_fragment, envmap_pars_fragment: envmap_pars_fragment, envmap_pars_vertex: envmap_pars_vertex, envmap_vertex: envmap_vertex, fog_fragment: fog_fragment, fog_pars_fragment: fog_pars_fragment, gradientmap_pars_fragment: gradientmap_pars_fragment, lightmap_fragment: lightmap_fragment, lightmap_pars_fragment: lightmap_pars_fragment, lights_lambert_vertex: lights_lambert_vertex, lights_pars: lights_pars, lights_phong_fragment: lights_phong_fragment, lights_phong_pars_fragment: lights_phong_pars_fragment, lights_physical_fragment: lights_physical_fragment, lights_physical_pars_fragment: lights_physical_pars_fragment, lights_template: lights_template, logdepthbuf_fragment: logdepthbuf_fragment, logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, logdepthbuf_vertex: logdepthbuf_vertex, map_fragment: map_fragment, map_pars_fragment: map_pars_fragment, map_particle_fragment: map_particle_fragment, map_particle_pars_fragment: map_particle_pars_fragment, metalnessmap_fragment: metalnessmap_fragment, metalnessmap_pars_fragment: metalnessmap_pars_fragment, morphnormal_vertex: morphnormal_vertex, morphtarget_pars_vertex: morphtarget_pars_vertex, morphtarget_vertex: morphtarget_vertex, normal_flip: normal_flip, normal_fragment: normal_fragment, normalmap_pars_fragment: normalmap_pars_fragment, packing: packing, premultiplied_alpha_fragment: premultiplied_alpha_fragment, project_vertex: project_vertex, roughnessmap_fragment: roughnessmap_fragment, roughnessmap_pars_fragment: roughnessmap_pars_fragment, shadowmap_pars_fragment: shadowmap_pars_fragment, shadowmap_pars_vertex: shadowmap_pars_vertex, shadowmap_vertex: shadowmap_vertex, shadowmask_pars_fragment: shadowmask_pars_fragment, skinbase_vertex: skinbase_vertex, skinning_pars_vertex: skinning_pars_vertex, skinning_vertex: skinning_vertex, skinnormal_vertex: skinnormal_vertex, specularmap_fragment: specularmap_fragment, specularmap_pars_fragment: specularmap_pars_fragment, tonemapping_fragment: tonemapping_fragment, tonemapping_pars_fragment: tonemapping_pars_fragment, uv_pars_fragment: uv_pars_fragment, uv_pars_vertex: uv_pars_vertex, uv_vertex: uv_vertex, uv2_pars_fragment: uv2_pars_fragment, uv2_pars_vertex: uv2_pars_vertex, uv2_vertex: uv2_vertex, worldpos_vertex: worldpos_vertex, cube_frag: cube_frag, cube_vert: cube_vert, depth_frag: depth_frag, depth_vert: depth_vert, distanceRGBA_frag: distanceRGBA_frag, distanceRGBA_vert: distanceRGBA_vert, equirect_frag: equirect_frag, equirect_vert: equirect_vert, linedashed_frag: linedashed_frag, linedashed_vert: linedashed_vert, meshbasic_frag: meshbasic_frag, meshbasic_vert: meshbasic_vert, meshlambert_frag: meshlambert_frag, meshlambert_vert: meshlambert_vert, meshphong_frag: meshphong_frag, meshphong_vert: meshphong_vert, meshphysical_frag: meshphysical_frag, meshphysical_vert: meshphysical_vert, normal_frag: normal_frag, normal_vert: normal_vert, points_frag: points_frag, points_vert: points_vert, shadow_frag: shadow_frag, shadow_vert: shadow_vert }; /** * @author mrdoob / http://mrdoob.com/ */ function Color( r, g, b ) { if ( g === undefined && b === undefined ) { // r is THREE.Color, hex or string return this.set( r ); } return this.setRGB( r, g, b ); } Color.prototype = { constructor: Color, isColor: true, r: 1, g: 1, b: 1, set: function ( value ) { if ( value && value.isColor ) { this.copy( value ); } else if ( typeof value === 'number' ) { this.setHex( value ); } else if ( typeof value === 'string' ) { this.setStyle( value ); } return this; }, setScalar: function ( scalar ) { this.r = scalar; this.g = scalar; this.b = scalar; return this; }, setHex: function ( hex ) { hex = Math.floor( hex ); this.r = ( hex >> 16 & 255 ) / 255; this.g = ( hex >> 8 & 255 ) / 255; this.b = ( hex & 255 ) / 255; return this; }, setRGB: function ( r, g, b ) { this.r = r; this.g = g; this.b = b; return this; }, setHSL: function () { function hue2rgb( p, q, t ) { if ( t < 0 ) t += 1; if ( t > 1 ) t -= 1; if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; if ( t < 1 / 2 ) return q; if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); return p; } return function setHSL( h, s, l ) { // h,s,l ranges are in 0.0 - 1.0 h = _Math.euclideanModulo( h, 1 ); s = _Math.clamp( s, 0, 1 ); l = _Math.clamp( l, 0, 1 ); if ( s === 0 ) { this.r = this.g = this.b = l; } else { var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); var q = ( 2 * l ) - p; this.r = hue2rgb( q, p, h + 1 / 3 ); this.g = hue2rgb( q, p, h ); this.b = hue2rgb( q, p, h - 1 / 3 ); } return this; }; }(), setStyle: function ( style ) { function handleAlpha( string ) { if ( string === undefined ) return; if ( parseFloat( string ) < 1 ) { console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); } } var m; if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { // rgb / hsl var color; var name = m[ 1 ]; var components = m[ 2 ]; switch ( name ) { case 'rgb': case 'rgba': if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { // rgb(255,0,0) rgba(255,0,0,0.5) this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; handleAlpha( color[ 5 ] ); return this; } if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; handleAlpha( color[ 5 ] ); return this; } break; case 'hsl': case 'hsla': if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { // hsl(120,50%,50%) hsla(120,50%,50%,0.5) var h = parseFloat( color[ 1 ] ) / 360; var s = parseInt( color[ 2 ], 10 ) / 100; var l = parseInt( color[ 3 ], 10 ) / 100; handleAlpha( color[ 5 ] ); return this.setHSL( h, s, l ); } break; } } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { // hex color var hex = m[ 1 ]; var size = hex.length; if ( size === 3 ) { // #ff0 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; return this; } else if ( size === 6 ) { // #ff0000 this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; return this; } } if ( style && style.length > 0 ) { // color keywords var hex = ColorKeywords[ style ]; if ( hex !== undefined ) { // red this.setHex( hex ); } else { // unknown color console.warn( 'THREE.Color: Unknown color ' + style ); } } return this; }, clone: function () { return new this.constructor( this.r, this.g, this.b ); }, copy: function ( color ) { this.r = color.r; this.g = color.g; this.b = color.b; return this; }, copyGammaToLinear: function ( color, gammaFactor ) { if ( gammaFactor === undefined ) gammaFactor = 2.0; this.r = Math.pow( color.r, gammaFactor ); this.g = Math.pow( color.g, gammaFactor ); this.b = Math.pow( color.b, gammaFactor ); return this; }, copyLinearToGamma: function ( color, gammaFactor ) { if ( gammaFactor === undefined ) gammaFactor = 2.0; var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; this.r = Math.pow( color.r, safeInverse ); this.g = Math.pow( color.g, safeInverse ); this.b = Math.pow( color.b, safeInverse ); return this; }, convertGammaToLinear: function () { var r = this.r, g = this.g, b = this.b; this.r = r * r; this.g = g * g; this.b = b * b; return this; }, convertLinearToGamma: function () { this.r = Math.sqrt( this.r ); this.g = Math.sqrt( this.g ); this.b = Math.sqrt( this.b ); return this; }, getHex: function () { return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; }, getHexString: function () { return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); }, getHSL: function ( optionalTarget ) { // h,s,l ranges are in 0.0 - 1.0 var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; var r = this.r, g = this.g, b = this.b; var max = Math.max( r, g, b ); var min = Math.min( r, g, b ); var hue, saturation; var lightness = ( min + max ) / 2.0; if ( min === max ) { hue = 0; saturation = 0; } else { var delta = max - min; saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); switch ( max ) { case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; case g: hue = ( b - r ) / delta + 2; break; case b: hue = ( r - g ) / delta + 4; break; } hue /= 6; } hsl.h = hue; hsl.s = saturation; hsl.l = lightness; return hsl; }, getStyle: function () { return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; }, offsetHSL: function ( h, s, l ) { var hsl = this.getHSL(); hsl.h += h; hsl.s += s; hsl.l += l; this.setHSL( hsl.h, hsl.s, hsl.l ); return this; }, add: function ( color ) { this.r += color.r; this.g += color.g; this.b += color.b; return this; }, addColors: function ( color1, color2 ) { this.r = color1.r + color2.r; this.g = color1.g + color2.g; this.b = color1.b + color2.b; return this; }, addScalar: function ( s ) { this.r += s; this.g += s; this.b += s; return this; }, sub: function( color ) { this.r = Math.max( 0, this.r - color.r ); this.g = Math.max( 0, this.g - color.g ); this.b = Math.max( 0, this.b - color.b ); return this; }, multiply: function ( color ) { this.r *= color.r; this.g *= color.g; this.b *= color.b; return this; }, multiplyScalar: function ( s ) { this.r *= s; this.g *= s; this.b *= s; return this; }, lerp: function ( color, alpha ) { this.r += ( color.r - this.r ) * alpha; this.g += ( color.g - this.g ) * alpha; this.b += ( color.b - this.b ) * alpha; return this; }, equals: function ( c ) { return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; this.r = array[ offset ]; this.g = array[ offset + 1 ]; this.b = array[ offset + 2 ]; return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this.r; array[ offset + 1 ] = this.g; array[ offset + 2 ] = this.b; return array; }, toJSON: function () { return this.getHex(); } }; var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; /** * @author alteredq / http://alteredqualia.com/ */ function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.image = { data: data, width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.generateMipmaps = false; this.flipY = false; this.unpackAlignment = 1; } DataTexture.prototype = Object.create( Texture.prototype ); DataTexture.prototype.constructor = DataTexture; DataTexture.prototype.isDataTexture = true; /** * Uniforms library for shared webgl shaders */ var UniformsLib = { common: { diffuse: { value: new Color( 0xeeeeee ) }, opacity: { value: 1.0 }, map: { value: null }, offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) }, specularMap: { value: null }, alphaMap: { value: null }, envMap: { value: null }, flipEnvMap: { value: - 1 }, reflectivity: { value: 1.0 }, refractionRatio: { value: 0.98 } }, aomap: { aoMap: { value: null }, aoMapIntensity: { value: 1 } }, lightmap: { lightMap: { value: null }, lightMapIntensity: { value: 1 } }, emissivemap: { emissiveMap: { value: null } }, bumpmap: { bumpMap: { value: null }, bumpScale: { value: 1 } }, normalmap: { normalMap: { value: null }, normalScale: { value: new Vector2( 1, 1 ) } }, displacementmap: { displacementMap: { value: null }, displacementScale: { value: 1 }, displacementBias: { value: 0 } }, roughnessmap: { roughnessMap: { value: null } }, metalnessmap: { metalnessMap: { value: null } }, gradientmap: { gradientMap: { value: null } }, fog: { fogDensity: { value: 0.00025 }, fogNear: { value: 1 }, fogFar: { value: 2000 }, fogColor: { value: new Color( 0xffffff ) } }, lights: { ambientLightColor: { value: [] }, directionalLights: { value: [], properties: { direction: {}, color: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, directionalShadowMap: { value: [] }, directionalShadowMatrix: { value: [] }, spotLights: { value: [], properties: { color: {}, position: {}, direction: {}, distance: {}, coneCos: {}, penumbraCos: {}, decay: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, spotShadowMap: { value: [] }, spotShadowMatrix: { value: [] }, pointLights: { value: [], properties: { color: {}, position: {}, decay: {}, distance: {}, shadow: {}, shadowBias: {}, shadowRadius: {}, shadowMapSize: {} } }, pointShadowMap: { value: [] }, pointShadowMatrix: { value: [] }, hemisphereLights: { value: [], properties: { direction: {}, skyColor: {}, groundColor: {} } }, // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src rectAreaLights: { value: [], properties: { color: {}, position: {}, width: {}, height: {}, } } }, points: { diffuse: { value: new Color( 0xeeeeee ) }, opacity: { value: 1.0 }, size: { value: 1.0 }, scale: { value: 1.0 }, map: { value: null }, offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) } } }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ */ var ShaderLib = { basic: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.fog ] ), vertexShader: ShaderChunk.meshbasic_vert, fragmentShader: ShaderChunk.meshbasic_frag }, lambert: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) } } ] ), vertexShader: ShaderChunk.meshlambert_vert, fragmentShader: ShaderChunk.meshlambert_frag }, phong: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.gradientmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) }, specular: { value: new Color( 0x111111 ) }, shininess: { value: 30 } } ] ), vertexShader: ShaderChunk.meshphong_vert, fragmentShader: ShaderChunk.meshphong_frag }, standard: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.aomap, UniformsLib.lightmap, UniformsLib.emissivemap, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, UniformsLib.roughnessmap, UniformsLib.metalnessmap, UniformsLib.fog, UniformsLib.lights, { emissive: { value: new Color( 0x000000 ) }, roughness: { value: 0.5 }, metalness: { value: 0 }, envMapIntensity: { value: 1 } // temporary } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }, points: { uniforms: UniformsUtils.merge( [ UniformsLib.points, UniformsLib.fog ] ), vertexShader: ShaderChunk.points_vert, fragmentShader: ShaderChunk.points_frag }, dashed: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.fog, { scale: { value: 1 }, dashSize: { value: 1 }, totalSize: { value: 2 } } ] ), vertexShader: ShaderChunk.linedashed_vert, fragmentShader: ShaderChunk.linedashed_frag }, depth: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.displacementmap ] ), vertexShader: ShaderChunk.depth_vert, fragmentShader: ShaderChunk.depth_frag }, normal: { uniforms: UniformsUtils.merge( [ UniformsLib.common, UniformsLib.bumpmap, UniformsLib.normalmap, UniformsLib.displacementmap, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk.normal_vert, fragmentShader: ShaderChunk.normal_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ cube: { uniforms: { tCube: { value: null }, tFlip: { value: - 1 }, opacity: { value: 1.0 } }, vertexShader: ShaderChunk.cube_vert, fragmentShader: ShaderChunk.cube_frag }, /* ------------------------------------------------------------------------- // Cube map shader ------------------------------------------------------------------------- */ equirect: { uniforms: { tEquirect: { value: null }, tFlip: { value: - 1 } }, vertexShader: ShaderChunk.equirect_vert, fragmentShader: ShaderChunk.equirect_frag }, distanceRGBA: { uniforms: { lightPos: { value: new Vector3() } }, vertexShader: ShaderChunk.distanceRGBA_vert, fragmentShader: ShaderChunk.distanceRGBA_frag } }; ShaderLib.physical = { uniforms: UniformsUtils.merge( [ ShaderLib.standard.uniforms, { clearCoat: { value: 0 }, clearCoatRoughness: { value: 0 } } ] ), vertexShader: ShaderChunk.meshphysical_vert, fragmentShader: ShaderChunk.meshphysical_frag }; /** * @author bhouston / http://clara.io */ function Box2( min, max ) { this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); } Box2.prototype = { constructor: Box2, set: function ( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; }, setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; }, setFromCenterAndSize: function () { var v1 = new Vector2(); return function setFromCenterAndSize( center, size ) { var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; }, makeEmpty: function () { this.min.x = this.min.y = + Infinity; this.max.x = this.max.y = - Infinity; return this; }, isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); }, getCenter: function ( optionalTarget ) { var result = optionalTarget || new Vector2(); return this.isEmpty() ? result.set( 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); }, getSize: function ( optionalTarget ) { var result = optionalTarget || new Vector2(); return this.isEmpty() ? result.set( 0, 0 ) : result.subVectors( this.max, this.min ); }, expandByPoint: function ( point ) { this.min.min( point ); this.max.max( point ); return this; }, expandByVector: function ( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; }, expandByScalar: function ( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; }, containsPoint: function ( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y ? false : true; }, containsBox: function ( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y; }, getParameter: function ( point, optionalTarget ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. var result = optionalTarget || new Vector2(); return result.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ) ); }, intersectsBox: function ( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y ? false : true; }, clampPoint: function ( point, optionalTarget ) { var result = optionalTarget || new Vector2(); return result.copy( point ).clamp( this.min, this.max ); }, distanceToPoint: function () { var v1 = new Vector2(); return function distanceToPoint( point ) { var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); }; }(), intersect: function ( box ) { this.min.max( box.min ); this.max.min( box.max ); return this; }, union: function ( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; }, translate: function ( offset ) { this.min.add( offset ); this.max.add( offset ); return this; }, equals: function ( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } }; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function LensFlarePlugin( renderer, flares ) { var gl = renderer.context; var state = renderer.state; var vertexBuffer, elementBuffer; var shader, program, attributes, uniforms; var tempTexture, occlusionTexture; function init() { var vertices = new Float32Array( [ - 1, - 1, 0, 0, 1, - 1, 1, 0, 1, 1, 1, 1, - 1, 1, 0, 1 ] ); var faces = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); // buffers vertexBuffer = gl.createBuffer(); elementBuffer = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); // textures tempTexture = gl.createTexture(); occlusionTexture = gl.createTexture(); state.bindTexture( gl.TEXTURE_2D, tempTexture ); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); shader = { vertexShader: [ "uniform lowp int renderType;", "uniform vec3 screenPosition;", "uniform vec2 scale;", "uniform float rotation;", "uniform sampler2D occlusionMap;", "attribute vec2 position;", "attribute vec2 uv;", "varying vec2 vUV;", "varying float vVisibility;", "void main() {", "vUV = uv;", "vec2 pos = position;", "if ( renderType == 2 ) {", "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", "vVisibility = visibility.r / 9.0;", "vVisibility *= 1.0 - visibility.g / 9.0;", "vVisibility *= visibility.b / 9.0;", "vVisibility *= 1.0 - visibility.a / 9.0;", "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", "}", "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", "}" ].join( "\n" ), fragmentShader: [ "uniform lowp int renderType;", "uniform sampler2D map;", "uniform float opacity;", "uniform vec3 color;", "varying vec2 vUV;", "varying float vVisibility;", "void main() {", // pink square "if ( renderType == 0 ) {", "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", // restore "} else if ( renderType == 1 ) {", "gl_FragColor = texture2D( map, vUV );", // flare "} else {", "vec4 texture = texture2D( map, vUV );", "texture.a *= opacity * vVisibility;", "gl_FragColor = texture;", "gl_FragColor.rgb *= color;", "}", "}" ].join( "\n" ) }; program = createProgram( shader ); attributes = { vertex: gl.getAttribLocation ( program, "position" ), uv: gl.getAttribLocation ( program, "uv" ) }; uniforms = { renderType: gl.getUniformLocation( program, "renderType" ), map: gl.getUniformLocation( program, "map" ), occlusionMap: gl.getUniformLocation( program, "occlusionMap" ), opacity: gl.getUniformLocation( program, "opacity" ), color: gl.getUniformLocation( program, "color" ), scale: gl.getUniformLocation( program, "scale" ), rotation: gl.getUniformLocation( program, "rotation" ), screenPosition: gl.getUniformLocation( program, "screenPosition" ) }; } /* * Render lens flares * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, * reads these back and calculates occlusion. */ this.render = function ( scene, camera, viewport ) { if ( flares.length === 0 ) return; var tempPosition = new Vector3(); var invAspect = viewport.w / viewport.z, halfViewportWidth = viewport.z * 0.5, halfViewportHeight = viewport.w * 0.5; var size = 16 / viewport.w, scale = new Vector2( size * invAspect, size ); var screenPosition = new Vector3( 1, 1, 0 ), screenPositionPixels = new Vector2( 1, 1 ); var validArea = new Box2(); validArea.min.set( viewport.x, viewport.y ); validArea.max.set( viewport.x + ( viewport.z - 16 ), viewport.y + ( viewport.w - 16 ) ); if ( program === undefined ) { init(); } gl.useProgram( program ); state.initAttributes(); state.enableAttribute( attributes.vertex ); state.enableAttribute( attributes.uv ); state.disableUnusedAttributes(); // loop through all lens flares to update their occlusion and positions // setup gl and common used attribs/uniforms gl.uniform1i( uniforms.occlusionMap, 0 ); gl.uniform1i( uniforms.map, 1 ); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 ); gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); state.disable( gl.CULL_FACE ); state.setDepthWrite( false ); for ( var i = 0, l = flares.length; i < l; i ++ ) { size = 16 / viewport.w; scale.set( size * invAspect, size ); // calc object screen position var flare = flares[ i ]; tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); tempPosition.applyMatrix4( camera.matrixWorldInverse ); tempPosition.applyProjection( camera.projectionMatrix ); // setup arrays for gl programs screenPosition.copy( tempPosition ); // horizontal and vertical coordinate of the lower left corner of the pixels to copy screenPositionPixels.x = viewport.x + ( screenPosition.x * halfViewportWidth ) + halfViewportWidth - 8; screenPositionPixels.y = viewport.y + ( screenPosition.y * halfViewportHeight ) + halfViewportHeight - 8; // screen cull if ( validArea.containsPoint( screenPositionPixels ) === true ) { // save current RGB to temp texture state.activeTexture( gl.TEXTURE0 ); state.bindTexture( gl.TEXTURE_2D, null ); state.activeTexture( gl.TEXTURE1 ); state.bindTexture( gl.TEXTURE_2D, tempTexture ); gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 ); // render pink quad gl.uniform1i( uniforms.renderType, 0 ); gl.uniform2f( uniforms.scale, scale.x, scale.y ); gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); state.disable( gl.BLEND ); state.enable( gl.DEPTH_TEST ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); // copy result to occlusionMap state.activeTexture( gl.TEXTURE0 ); state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 ); // restore graphics gl.uniform1i( uniforms.renderType, 1 ); state.disable( gl.DEPTH_TEST ); state.activeTexture( gl.TEXTURE1 ); state.bindTexture( gl.TEXTURE_2D, tempTexture ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); // update object positions flare.positionScreen.copy( screenPosition ); if ( flare.customUpdateCallback ) { flare.customUpdateCallback( flare ); } else { flare.updateLensFlares(); } // render flares gl.uniform1i( uniforms.renderType, 2 ); state.enable( gl.BLEND ); for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { var sprite = flare.lensFlares[ j ]; if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { screenPosition.x = sprite.x; screenPosition.y = sprite.y; screenPosition.z = sprite.z; size = sprite.size * sprite.scale / viewport.w; scale.x = size * invAspect; scale.y = size; gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); gl.uniform2f( uniforms.scale, scale.x, scale.y ); gl.uniform1f( uniforms.rotation, sprite.rotation ); gl.uniform1f( uniforms.opacity, sprite.opacity ); gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); renderer.setTexture2D( sprite.texture, 1 ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); } } } } // restore gl state.enable( gl.CULL_FACE ); state.enable( gl.DEPTH_TEST ); state.setDepthWrite( true ); renderer.resetGLState(); }; function createProgram( shader ) { var program = gl.createProgram(); var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); var vertexShader = gl.createShader( gl.VERTEX_SHADER ); var prefix = "precision " + renderer.getPrecision() + " float;\n"; gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); gl.shaderSource( vertexShader, prefix + shader.vertexShader ); gl.compileShader( fragmentShader ); gl.compileShader( vertexShader ); gl.attachShader( program, fragmentShader ); gl.attachShader( program, vertexShader ); gl.linkProgram( program ); return program; } } /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function SpritePlugin( renderer, sprites ) { var gl = renderer.context; var state = renderer.state; var vertexBuffer, elementBuffer; var program, attributes, uniforms; var texture; // decompose matrixWorld var spritePosition = new Vector3(); var spriteRotation = new Quaternion(); var spriteScale = new Vector3(); function init() { var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0, 0.5, - 0.5, 1, 0, 0.5, 0.5, 1, 1, - 0.5, 0.5, 0, 1 ] ); var faces = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); vertexBuffer = gl.createBuffer(); elementBuffer = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); program = createProgram(); attributes = { position: gl.getAttribLocation ( program, 'position' ), uv: gl.getAttribLocation ( program, 'uv' ) }; uniforms = { uvOffset: gl.getUniformLocation( program, 'uvOffset' ), uvScale: gl.getUniformLocation( program, 'uvScale' ), rotation: gl.getUniformLocation( program, 'rotation' ), scale: gl.getUniformLocation( program, 'scale' ), color: gl.getUniformLocation( program, 'color' ), map: gl.getUniformLocation( program, 'map' ), opacity: gl.getUniformLocation( program, 'opacity' ), modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), fogType: gl.getUniformLocation( program, 'fogType' ), fogDensity: gl.getUniformLocation( program, 'fogDensity' ), fogNear: gl.getUniformLocation( program, 'fogNear' ), fogFar: gl.getUniformLocation( program, 'fogFar' ), fogColor: gl.getUniformLocation( program, 'fogColor' ), alphaTest: gl.getUniformLocation( program, 'alphaTest' ) }; var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); canvas.width = 8; canvas.height = 8; var context = canvas.getContext( '2d' ); context.fillStyle = 'white'; context.fillRect( 0, 0, 8, 8 ); texture = new Texture( canvas ); texture.needsUpdate = true; } this.render = function ( scene, camera ) { if ( sprites.length === 0 ) return; // setup gl if ( program === undefined ) { init(); } gl.useProgram( program ); state.initAttributes(); state.enableAttribute( attributes.position ); state.enableAttribute( attributes.uv ); state.disableUnusedAttributes(); state.disable( gl.CULL_FACE ); state.enable( gl.BLEND ); gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); state.activeTexture( gl.TEXTURE0 ); gl.uniform1i( uniforms.map, 0 ); var oldFogType = 0; var sceneFogType = 0; var fog = scene.fog; if ( fog ) { gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); if ( fog.isFog ) { gl.uniform1f( uniforms.fogNear, fog.near ); gl.uniform1f( uniforms.fogFar, fog.far ); gl.uniform1i( uniforms.fogType, 1 ); oldFogType = 1; sceneFogType = 1; } else if ( fog.isFogExp2 ) { gl.uniform1f( uniforms.fogDensity, fog.density ); gl.uniform1i( uniforms.fogType, 2 ); oldFogType = 2; sceneFogType = 2; } } else { gl.uniform1i( uniforms.fogType, 0 ); oldFogType = 0; sceneFogType = 0; } // update positions and sort for ( var i = 0, l = sprites.length; i < l; i ++ ) { var sprite = sprites[ i ]; sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; } sprites.sort( painterSortStable ); // render all sprites var scale = []; for ( var i = 0, l = sprites.length; i < l; i ++ ) { var sprite = sprites[ i ]; var material = sprite.material; if ( material.visible === false ) continue; gl.uniform1f( uniforms.alphaTest, material.alphaTest ); gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); scale[ 0 ] = spriteScale.x; scale[ 1 ] = spriteScale.y; var fogType = 0; if ( scene.fog && material.fog ) { fogType = sceneFogType; } if ( oldFogType !== fogType ) { gl.uniform1i( uniforms.fogType, fogType ); oldFogType = fogType; } if ( material.map !== null ) { gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); } else { gl.uniform2f( uniforms.uvOffset, 0, 0 ); gl.uniform2f( uniforms.uvScale, 1, 1 ); } gl.uniform1f( uniforms.opacity, material.opacity ); gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); gl.uniform1f( uniforms.rotation, material.rotation ); gl.uniform2fv( uniforms.scale, scale ); state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); state.setDepthTest( material.depthTest ); state.setDepthWrite( material.depthWrite ); if ( material.map ) { renderer.setTexture2D( material.map, 0 ); } else { renderer.setTexture2D( texture, 0 ); } gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); } // restore gl state.enable( gl.CULL_FACE ); renderer.resetGLState(); }; function createProgram() { var program = gl.createProgram(); var vertexShader = gl.createShader( gl.VERTEX_SHADER ); var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); gl.shaderSource( vertexShader, [ 'precision ' + renderer.getPrecision() + ' float;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform float rotation;', 'uniform vec2 scale;', 'uniform vec2 uvOffset;', 'uniform vec2 uvScale;', 'attribute vec2 position;', 'attribute vec2 uv;', 'varying vec2 vUV;', 'void main() {', 'vUV = uvOffset + uv * uvScale;', 'vec2 alignedPosition = position * scale;', 'vec2 rotatedPosition;', 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', 'vec4 finalPosition;', 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', 'finalPosition.xy += rotatedPosition;', 'finalPosition = projectionMatrix * finalPosition;', 'gl_Position = finalPosition;', '}' ].join( '\n' ) ); gl.shaderSource( fragmentShader, [ 'precision ' + renderer.getPrecision() + ' float;', 'uniform vec3 color;', 'uniform sampler2D map;', 'uniform float opacity;', 'uniform int fogType;', 'uniform vec3 fogColor;', 'uniform float fogDensity;', 'uniform float fogNear;', 'uniform float fogFar;', 'uniform float alphaTest;', 'varying vec2 vUV;', 'void main() {', 'vec4 texture = texture2D( map, vUV );', 'if ( texture.a < alphaTest ) discard;', 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', 'if ( fogType > 0 ) {', 'float depth = gl_FragCoord.z / gl_FragCoord.w;', 'float fogFactor = 0.0;', 'if ( fogType == 1 ) {', 'fogFactor = smoothstep( fogNear, fogFar, depth );', '} else {', 'const float LOG2 = 1.442695;', 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', '}', 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', '}', '}' ].join( '\n' ) ); gl.compileShader( vertexShader ); gl.compileShader( fragmentShader ); gl.attachShader( program, vertexShader ); gl.attachShader( program, fragmentShader ); gl.linkProgram( program ); return program; } function painterSortStable( a, b ) { if ( a.renderOrder !== b.renderOrder ) { return a.renderOrder - b.renderOrder; } else if ( a.z !== b.z ) { return b.z - a.z; } else { return b.id - a.id; } } } /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ var materialId = 0; function Material() { Object.defineProperty( this, 'id', { value: materialId ++ } ); this.uuid = _Math.generateUUID(); this.name = ''; this.type = 'Material'; this.fog = true; this.lights = true; this.blending = NormalBlending; this.side = FrontSide; this.shading = SmoothShading; // THREE.FlatShading, THREE.SmoothShading this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors this.opacity = 1; this.transparent = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; this.blendEquation = AddEquation; this.blendSrcAlpha = null; this.blendDstAlpha = null; this.blendEquationAlpha = null; this.depthFunc = LessEqualDepth; this.depthTest = true; this.depthWrite = true; this.clippingPlanes = null; this.clipIntersection = false; this.clipShadows = false; this.colorWrite = true; this.precision = null; // override the renderer's default precision for this material this.polygonOffset = false; this.polygonOffsetFactor = 0; this.polygonOffsetUnits = 0; this.alphaTest = 0; this.premultipliedAlpha = false; this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer this.visible = true; this._needsUpdate = true; } Material.prototype = { constructor: Material, isMaterial: true, get needsUpdate() { return this._needsUpdate; }, set needsUpdate( value ) { if ( value === true ) this.update(); this._needsUpdate = value; }, setValues: function ( values ) { if ( values === undefined ) return; for ( var key in values ) { var newValue = values[ key ]; if ( newValue === undefined ) { console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); continue; } var currentValue = this[ key ]; if ( currentValue === undefined ) { console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); continue; } if ( currentValue && currentValue.isColor ) { currentValue.set( newValue ); } else if ( (currentValue && currentValue.isVector3) && (newValue && newValue.isVector3) ) { currentValue.copy( newValue ); } else if ( key === 'overdraw' ) { // ensure overdraw is backwards-compatible with legacy boolean type this[ key ] = Number( newValue ); } else { this[ key ] = newValue; } } }, toJSON: function ( meta ) { var isRoot = meta === undefined; if ( isRoot ) { meta = { textures: {}, images: {} }; } var data = { metadata: { version: 4.4, type: 'Material', generator: 'Material.toJSON' } }; // standard Material serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== '' ) data.name = this.name; if ( this.color && this.color.isColor ) data.color = this.color.getHex(); if ( this.roughness !== undefined ) data.roughness = this.roughness; if ( this.metalness !== undefined ) data.metalness = this.metalness; if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); if ( this.shininess !== undefined ) data.shininess = this.shininess; if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat; if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness; if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; if ( this.bumpMap && this.bumpMap.isTexture ) { data.bumpMap = this.bumpMap.toJSON( meta ).uuid; data.bumpScale = this.bumpScale; } if ( this.normalMap && this.normalMap.isTexture ) { data.normalMap = this.normalMap.toJSON( meta ).uuid; data.normalScale = this.normalScale.toArray(); } if ( this.displacementMap && this.displacementMap.isTexture ) { data.displacementMap = this.displacementMap.toJSON( meta ).uuid; data.displacementScale = this.displacementScale; data.displacementBias = this.displacementBias; } if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; if ( this.envMap && this.envMap.isTexture ) { data.envMap = this.envMap.toJSON( meta ).uuid; data.reflectivity = this.reflectivity; // Scale behind envMap } if ( this.gradientMap && this.gradientMap.isTexture ) { data.gradientMap = this.gradientMap.toJSON( meta ).uuid; } if ( this.size !== undefined ) data.size = this.size; if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; if ( this.blending !== NormalBlending ) data.blending = this.blending; if ( this.shading !== SmoothShading ) data.shading = this.shading; if ( this.side !== FrontSide ) data.side = this.side; if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors; if ( this.opacity < 1 ) data.opacity = this.opacity; if ( this.transparent === true ) data.transparent = this.transparent; data.depthFunc = this.depthFunc; data.depthTest = this.depthTest; data.depthWrite = this.depthWrite; if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; if ( this.wireframe === true ) data.wireframe = this.wireframe; if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; data.skinning = this.skinning; data.morphTargets = this.morphTargets; // TODO: Copied from Object3D.toJSON function extractFromCache( cache ) { var values = []; for ( var key in cache ) { var data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } if ( isRoot ) { var textures = extractFromCache( meta.textures ); var images = extractFromCache( meta.images ); if ( textures.length > 0 ) data.textures = textures; if ( images.length > 0 ) data.images = images; } return data; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.name = source.name; this.fog = source.fog; this.lights = source.lights; this.blending = source.blending; this.side = source.side; this.shading = source.shading; this.vertexColors = source.vertexColors; this.opacity = source.opacity; this.transparent = source.transparent; this.blendSrc = source.blendSrc; this.blendDst = source.blendDst; this.blendEquation = source.blendEquation; this.blendSrcAlpha = source.blendSrcAlpha; this.blendDstAlpha = source.blendDstAlpha; this.blendEquationAlpha = source.blendEquationAlpha; this.depthFunc = source.depthFunc; this.depthTest = source.depthTest; this.depthWrite = source.depthWrite; this.colorWrite = source.colorWrite; this.precision = source.precision; this.polygonOffset = source.polygonOffset; this.polygonOffsetFactor = source.polygonOffsetFactor; this.polygonOffsetUnits = source.polygonOffsetUnits; this.alphaTest = source.alphaTest; this.premultipliedAlpha = source.premultipliedAlpha; this.overdraw = source.overdraw; this.visible = source.visible; this.clipShadows = source.clipShadows; this.clipIntersection = source.clipIntersection; var srcPlanes = source.clippingPlanes, dstPlanes = null; if ( srcPlanes !== null ) { var n = srcPlanes.length; dstPlanes = new Array( n ); for ( var i = 0; i !== n; ++ i ) dstPlanes[ i ] = srcPlanes[ i ].clone(); } this.clippingPlanes = dstPlanes; return this; }, update: function () { this.dispatchEvent( { type: 'update' } ); }, dispose: function () { this.dispatchEvent( { type: 'dispose' } ); } }; Object.assign( Material.prototype, EventDispatcher.prototype ); /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * defines: { "label" : "value" }, * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, * * fragmentShader: , * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: , * * skinning: , * morphTargets: , * morphNormals: * } */ function ShaderMaterial( parameters ) { Material.call( this ); this.type = 'ShaderMaterial'; this.defines = {}; this.uniforms = {}; this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; this.linewidth = 1; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; // set to use scene fog this.lights = false; // set to use scene lights this.clipping = false; // set to use user-defined clipping planes this.skinning = false; // set to use skinning attribute streams this.morphTargets = false; // set to use morph targets this.morphNormals = false; // set to use morph normals this.extensions = { derivatives: false, // set to use derivatives fragDepth: false, // set to use fragment depth values drawBuffers: false, // set to use draw buffers shaderTextureLOD: false // set to use shader texture LOD }; // When rendered geometry doesn't include these attributes but the material does, // use these default values in WebGL. This avoids errors when buffer data is missing. this.defaultAttributeValues = { 'color': [ 1, 1, 1 ], 'uv': [ 0, 0 ], 'uv2': [ 0, 0 ] }; this.index0AttributeName = undefined; if ( parameters !== undefined ) { if ( parameters.attributes !== undefined ) { console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); } this.setValues( parameters ); } } ShaderMaterial.prototype = Object.create( Material.prototype ); ShaderMaterial.prototype.constructor = ShaderMaterial; ShaderMaterial.prototype.isShaderMaterial = true; ShaderMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.fragmentShader = source.fragmentShader; this.vertexShader = source.vertexShader; this.uniforms = UniformsUtils.clone( source.uniforms ); this.defines = source.defines; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.lights = source.lights; this.clipping = source.clipping; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; this.extensions = source.extensions; return this; }; ShaderMaterial.prototype.toJSON = function ( meta ) { var data = Material.prototype.toJSON.call( this, meta ); data.uniforms = this.uniforms; data.vertexShader = this.vertexShader; data.fragmentShader = this.fragmentShader; return data; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author bhouston / https://clara.io * @author WestLangley / http://github.com/WestLangley * * parameters = { * * opacity: , * * map: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * } */ function MeshDepthMaterial( parameters ) { Material.call( this ); this.type = 'MeshDepthMaterial'; this.depthPacking = BasicDepthPacking; this.skinning = false; this.morphTargets = false; this.map = null; this.alphaMap = null; this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.lights = false; this.setValues( parameters ); } MeshDepthMaterial.prototype = Object.create( Material.prototype ); MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; MeshDepthMaterial.prototype.isMeshDepthMaterial = true; MeshDepthMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.depthPacking = source.depthPacking; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.map = source.map; this.alphaMap = source.alphaMap; this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; return this; }; /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ function Box3( min, max ) { this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); } Box3.prototype = { constructor: Box3, isBox3: true, set: function ( min, max ) { this.min.copy( min ); this.max.copy( max ); return this; }, setFromArray: function ( array ) { var minX = + Infinity; var minY = + Infinity; var minZ = + Infinity; var maxX = - Infinity; var maxY = - Infinity; var maxZ = - Infinity; for ( var i = 0, l = array.length; i < l; i += 3 ) { var x = array[ i ]; var y = array[ i + 1 ]; var z = array[ i + 2 ]; if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); }, setFromBufferAttribute: function ( attribute ) { var minX = + Infinity; var minY = + Infinity; var minZ = + Infinity; var maxX = - Infinity; var maxY = - Infinity; var maxZ = - Infinity; for ( var i = 0, l = attribute.count; i < l; i ++ ) { var x = attribute.getX( i ); var y = attribute.getY( i ); var z = attribute.getZ( i ); if ( x < minX ) minX = x; if ( y < minY ) minY = y; if ( z < minZ ) minZ = z; if ( x > maxX ) maxX = x; if ( y > maxY ) maxY = y; if ( z > maxZ ) maxZ = z; } this.min.set( minX, minY, minZ ); this.max.set( maxX, maxY, maxZ ); }, setFromPoints: function ( points ) { this.makeEmpty(); for ( var i = 0, il = points.length; i < il; i ++ ) { this.expandByPoint( points[ i ] ); } return this; }, setFromCenterAndSize: function () { var v1 = new Vector3(); return function setFromCenterAndSize( center, size ) { var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); this.min.copy( center ).sub( halfSize ); this.max.copy( center ).add( halfSize ); return this; }; }(), setFromObject: function () { // Computes the world-axis-aligned bounding box of an object (including its children), // accounting for both the object's, and children's, world transforms var v1 = new Vector3(); return function setFromObject( object ) { var scope = this; object.updateMatrixWorld( true ); this.makeEmpty(); object.traverse( function ( node ) { var i, l; var geometry = node.geometry; if ( geometry !== undefined ) { if ( geometry.isGeometry ) { var vertices = geometry.vertices; for ( i = 0, l = vertices.length; i < l; i ++ ) { v1.copy( vertices[ i ] ); v1.applyMatrix4( node.matrixWorld ); scope.expandByPoint( v1 ); } } else if ( geometry.isBufferGeometry ) { var attribute = geometry.attributes.position; if ( attribute !== undefined ) { for ( i = 0, l = attribute.count; i < l; i ++ ) { v1.fromAttribute( attribute, i ).applyMatrix4( node.matrixWorld ); scope.expandByPoint( v1 ); } } } } } ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( box ) { this.min.copy( box.min ); this.max.copy( box.max ); return this; }, makeEmpty: function () { this.min.x = this.min.y = this.min.z = + Infinity; this.max.x = this.max.y = this.max.z = - Infinity; return this; }, isEmpty: function () { // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); }, getCenter: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return this.isEmpty() ? result.set( 0, 0, 0 ) : result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); }, getSize: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return this.isEmpty() ? result.set( 0, 0, 0 ) : result.subVectors( this.max, this.min ); }, expandByPoint: function ( point ) { this.min.min( point ); this.max.max( point ); return this; }, expandByVector: function ( vector ) { this.min.sub( vector ); this.max.add( vector ); return this; }, expandByScalar: function ( scalar ) { this.min.addScalar( - scalar ); this.max.addScalar( scalar ); return this; }, containsPoint: function ( point ) { return point.x < this.min.x || point.x > this.max.x || point.y < this.min.y || point.y > this.max.y || point.z < this.min.z || point.z > this.max.z ? false : true; }, containsBox: function ( box ) { return this.min.x <= box.min.x && box.max.x <= this.max.x && this.min.y <= box.min.y && box.max.y <= this.max.y && this.min.z <= box.min.z && box.max.z <= this.max.z; }, getParameter: function ( point, optionalTarget ) { // This can potentially have a divide by zero if the box // has a size dimension of 0. var result = optionalTarget || new Vector3(); return result.set( ( point.x - this.min.x ) / ( this.max.x - this.min.x ), ( point.y - this.min.y ) / ( this.max.y - this.min.y ), ( point.z - this.min.z ) / ( this.max.z - this.min.z ) ); }, intersectsBox: function ( box ) { // using 6 splitting planes to rule out intersections. return box.max.x < this.min.x || box.min.x > this.max.x || box.max.y < this.min.y || box.min.y > this.max.y || box.max.z < this.min.z || box.min.z > this.max.z ? false : true; }, intersectsSphere: ( function () { var closestPoint; return function intersectsSphere( sphere ) { if ( closestPoint === undefined ) closestPoint = new Vector3(); // Find the point on the AABB closest to the sphere center. this.clampPoint( sphere.center, closestPoint ); // If that point is inside the sphere, the AABB and sphere intersect. return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); }; } )(), intersectsPlane: function ( plane ) { // We compute the minimum and maximum dot product values. If those values // are on the same side (back or front) of the plane, then there is no intersection. var min, max; if ( plane.normal.x > 0 ) { min = plane.normal.x * this.min.x; max = plane.normal.x * this.max.x; } else { min = plane.normal.x * this.max.x; max = plane.normal.x * this.min.x; } if ( plane.normal.y > 0 ) { min += plane.normal.y * this.min.y; max += plane.normal.y * this.max.y; } else { min += plane.normal.y * this.max.y; max += plane.normal.y * this.min.y; } if ( plane.normal.z > 0 ) { min += plane.normal.z * this.min.z; max += plane.normal.z * this.max.z; } else { min += plane.normal.z * this.max.z; max += plane.normal.z * this.min.z; } return ( min <= plane.constant && max >= plane.constant ); }, clampPoint: function ( point, optionalTarget ) { var result = optionalTarget || new Vector3(); return result.copy( point ).clamp( this.min, this.max ); }, distanceToPoint: function () { var v1 = new Vector3(); return function distanceToPoint( point ) { var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); return clampedPoint.sub( point ).length(); }; }(), getBoundingSphere: function () { var v1 = new Vector3(); return function getBoundingSphere( optionalTarget ) { var result = optionalTarget || new Sphere(); this.getCenter( result.center ); result.radius = this.getSize( v1 ).length() * 0.5; return result; }; }(), intersect: function ( box ) { this.min.max( box.min ); this.max.min( box.max ); // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. if( this.isEmpty() ) this.makeEmpty(); return this; }, union: function ( box ) { this.min.min( box.min ); this.max.max( box.max ); return this; }, applyMatrix4: function () { var points = [ new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3(), new Vector3() ]; return function applyMatrix4( matrix ) { // transform of empty box is an empty box. if( this.isEmpty() ) return this; // NOTE: I am using a binary pattern to specify all 2^3 combinations below points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 this.setFromPoints( points ); return this; }; }(), translate: function ( offset ) { this.min.add( offset ); this.max.add( offset ); return this; }, equals: function ( box ) { return box.min.equals( this.min ) && box.max.equals( this.max ); } }; /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ function Sphere( center, radius ) { this.center = ( center !== undefined ) ? center : new Vector3(); this.radius = ( radius !== undefined ) ? radius : 0; } Sphere.prototype = { constructor: Sphere, set: function ( center, radius ) { this.center.copy( center ); this.radius = radius; return this; }, setFromPoints: function () { var box = new Box3(); return function setFromPoints( points, optionalCenter ) { var center = this.center; if ( optionalCenter !== undefined ) { center.copy( optionalCenter ); } else { box.setFromPoints( points ).getCenter( center ); } var maxRadiusSq = 0; for ( var i = 0, il = points.length; i < il; i ++ ) { maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); } this.radius = Math.sqrt( maxRadiusSq ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( sphere ) { this.center.copy( sphere.center ); this.radius = sphere.radius; return this; }, empty: function () { return ( this.radius <= 0 ); }, containsPoint: function ( point ) { return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); }, distanceToPoint: function ( point ) { return ( point.distanceTo( this.center ) - this.radius ); }, intersectsSphere: function ( sphere ) { var radiusSum = this.radius + sphere.radius; return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); }, intersectsBox: function ( box ) { return box.intersectsSphere( this ); }, intersectsPlane: function ( plane ) { // We use the following equation to compute the signed distance from // the center of the sphere to the plane. // // distance = q * n - d // // If this distance is greater than the radius of the sphere, // then there is no intersection. return Math.abs( this.center.dot( plane.normal ) - plane.constant ) <= this.radius; }, clampPoint: function ( point, optionalTarget ) { var deltaLengthSq = this.center.distanceToSquared( point ); var result = optionalTarget || new Vector3(); result.copy( point ); if ( deltaLengthSq > ( this.radius * this.radius ) ) { result.sub( this.center ).normalize(); result.multiplyScalar( this.radius ).add( this.center ); } return result; }, getBoundingBox: function ( optionalTarget ) { var box = optionalTarget || new Box3(); box.set( this.center, this.center ); box.expandByScalar( this.radius ); return box; }, applyMatrix4: function ( matrix ) { this.center.applyMatrix4( matrix ); this.radius = this.radius * matrix.getMaxScaleOnAxis(); return this; }, translate: function ( offset ) { this.center.add( offset ); return this; }, equals: function ( sphere ) { return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); } }; /** * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io * @author tschw */ function Matrix3() { this.elements = new Float32Array( [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ] ); if ( arguments.length > 0 ) { console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); } } Matrix3.prototype = { constructor: Matrix3, isMatrix3: true, set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { var te = this.elements; te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; return this; }, identity: function () { this.set( 1, 0, 0, 0, 1, 0, 0, 0, 1 ); return this; }, clone: function () { return new this.constructor().fromArray( this.elements ); }, copy: function ( m ) { var me = m.elements; this.set( me[ 0 ], me[ 3 ], me[ 6 ], me[ 1 ], me[ 4 ], me[ 7 ], me[ 2 ], me[ 5 ], me[ 8 ] ); return this; }, setFromMatrix4: function( m ) { var me = m.elements; this.set( me[ 0 ], me[ 4 ], me[ 8 ], me[ 1 ], me[ 5 ], me[ 9 ], me[ 2 ], me[ 6 ], me[ 10 ] ); return this; }, applyToVector3Array: function () { var v1; return function applyToVector3Array( array, offset, length ) { if ( v1 === undefined ) v1 = new Vector3(); if ( offset === undefined ) offset = 0; if ( length === undefined ) length = array.length; for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { v1.fromArray( array, j ); v1.applyMatrix3( this ); v1.toArray( array, j ); } return array; }; }(), applyToBufferAttribute: function () { var v1; return function applyToBufferAttribute( attribute ) { if ( v1 === undefined ) v1 = new Vector3(); for ( var i = 0, l = attribute.count; i < l; i ++ ) { v1.x = attribute.getX( i ); v1.y = attribute.getY( i ); v1.z = attribute.getZ( i ); v1.applyMatrix3( this ); attribute.setXYZ( i, v1.x, v1.y, v1.z ); } return attribute; }; }(), multiplyScalar: function ( s ) { var te = this.elements; te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; return this; }, determinant: function () { var te = this.elements; var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; }, getInverse: function ( matrix, throwOnDegenerate ) { if ( matrix && matrix.isMatrix4 ) { console.error( "THREE.Matrix3.getInverse no longer takes a Matrix4 argument." ); } var me = matrix.elements, te = this.elements, n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], t11 = n33 * n22 - n32 * n23, t12 = n32 * n13 - n33 * n12, t13 = n23 * n12 - n22 * n13, det = n11 * t11 + n21 * t12 + n31 * t13; if ( det === 0 ) { var msg = "THREE.Matrix3.getInverse(): can't invert matrix, determinant is 0"; if ( throwOnDegenerate === true ) { throw new Error( msg ); } else { console.warn( msg ); } return this.identity(); } var detInv = 1 / det; te[ 0 ] = t11 * detInv; te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; te[ 3 ] = t12 * detInv; te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; te[ 6 ] = t13 * detInv; te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; return this; }, transpose: function () { var tmp, m = this.elements; tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; return this; }, getNormalMatrix: function ( matrix4 ) { return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); }, transposeIntoArray: function ( r ) { var m = this.elements; r[ 0 ] = m[ 0 ]; r[ 1 ] = m[ 3 ]; r[ 2 ] = m[ 6 ]; r[ 3 ] = m[ 1 ]; r[ 4 ] = m[ 4 ]; r[ 5 ] = m[ 7 ]; r[ 6 ] = m[ 2 ]; r[ 7 ] = m[ 5 ]; r[ 8 ] = m[ 8 ]; return this; }, fromArray: function ( array, offset ) { if ( offset === undefined ) offset = 0; for( var i = 0; i < 9; i ++ ) { this.elements[ i ] = array[ i + offset ]; } return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; var te = this.elements; array[ offset ] = te[ 0 ]; array[ offset + 1 ] = te[ 1 ]; array[ offset + 2 ] = te[ 2 ]; array[ offset + 3 ] = te[ 3 ]; array[ offset + 4 ] = te[ 4 ]; array[ offset + 5 ] = te[ 5 ]; array[ offset + 6 ] = te[ 6 ]; array[ offset + 7 ] = te[ 7 ]; array[ offset + 8 ] = te[ 8 ]; return array; } }; /** * @author bhouston / http://clara.io */ function Plane( normal, constant ) { this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); this.constant = ( constant !== undefined ) ? constant : 0; } Plane.prototype = { constructor: Plane, set: function ( normal, constant ) { this.normal.copy( normal ); this.constant = constant; return this; }, setComponents: function ( x, y, z, w ) { this.normal.set( x, y, z ); this.constant = w; return this; }, setFromNormalAndCoplanarPoint: function ( normal, point ) { this.normal.copy( normal ); this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized return this; }, setFromCoplanarPoints: function () { var v1 = new Vector3(); var v2 = new Vector3(); return function setFromCoplanarPoints( a, b, c ) { var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? this.setFromNormalAndCoplanarPoint( normal, a ); return this; }; }(), clone: function () { return new this.constructor().copy( this ); }, copy: function ( plane ) { this.normal.copy( plane.normal ); this.constant = plane.constant; return this; }, normalize: function () { // Note: will lead to a divide by zero if the plane is invalid. var inverseNormalLength = 1.0 / this.normal.length(); this.normal.multiplyScalar( inverseNormalLength ); this.constant *= inverseNormalLength; return this; }, negate: function () { this.constant *= - 1; this.normal.negate(); return this; }, distanceToPoint: function ( point ) { return this.normal.dot( point ) + this.constant; }, distanceToSphere: function ( sphere ) { return this.distanceToPoint( sphere.center ) - sphere.radius; }, projectPoint: function ( point, optionalTarget ) { return this.orthoPoint( point, optionalTarget ).sub( point ).negate(); }, orthoPoint: function ( point, optionalTarget ) { var perpendicularMagnitude = this.distanceToPoint( point ); var result = optionalTarget || new Vector3(); return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude ); }, intersectLine: function () { var v1 = new Vector3(); return function intersectLine( line, optionalTarget ) { var result = optionalTarget || new Vector3(); var direction = line.delta( v1 ); var denominator = this.normal.dot( direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( this.distanceToPoint( line.start ) === 0 ) { return result.copy( line.start ); } // Unsure if this is the correct method to handle this case. return undefined; } var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; if ( t < 0 || t > 1 ) { return undefined; } return result.copy( direction ).multiplyScalar( t ).add( line.start ); }; }(), intersectsLine: function ( line ) { // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. var startSign = this.distanceToPoint( line.start ); var endSign = this.distanceToPoint( line.end ); return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); }, intersectsBox: function ( box ) { return box.intersectsPlane( this ); }, intersectsSphere: function ( sphere ) { return sphere.intersectsPlane( this ); }, coplanarPoint: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return result.copy( this.normal ).multiplyScalar( - this.constant ); }, applyMatrix4: function () { var v1 = new Vector3(); var m1 = new Matrix3(); return function applyMatrix4( matrix, optionalNormalMatrix ) { var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix ); // transform normal based on theory here: // http://www.songho.ca/opengl/gl_normaltransform.html var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); // recalculate constant (like in setFromNormalAndCoplanarPoint) this.constant = - referencePoint.dot( normal ); return this; }; }(), translate: function ( offset ) { this.constant = this.constant - offset.dot( this.normal ); return this; }, equals: function ( plane ) { return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author bhouston / http://clara.io */ function Frustum( p0, p1, p2, p3, p4, p5 ) { this.planes = [ ( p0 !== undefined ) ? p0 : new Plane(), ( p1 !== undefined ) ? p1 : new Plane(), ( p2 !== undefined ) ? p2 : new Plane(), ( p3 !== undefined ) ? p3 : new Plane(), ( p4 !== undefined ) ? p4 : new Plane(), ( p5 !== undefined ) ? p5 : new Plane() ]; } Frustum.prototype = { constructor: Frustum, set: function ( p0, p1, p2, p3, p4, p5 ) { var planes = this.planes; planes[ 0 ].copy( p0 ); planes[ 1 ].copy( p1 ); planes[ 2 ].copy( p2 ); planes[ 3 ].copy( p3 ); planes[ 4 ].copy( p4 ); planes[ 5 ].copy( p5 ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( frustum ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { planes[ i ].copy( frustum.planes[ i ] ); } return this; }, setFromMatrix: function ( m ) { var planes = this.planes; var me = m.elements; var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); return this; }, intersectsObject: function () { var sphere = new Sphere(); return function intersectsObject( object ) { var geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ) .applyMatrix4( object.matrixWorld ); return this.intersectsSphere( sphere ); }; }(), intersectsSprite: function () { var sphere = new Sphere(); return function intersectsSprite( sprite ) { sphere.center.set( 0, 0, 0 ); sphere.radius = 0.7071067811865476; sphere.applyMatrix4( sprite.matrixWorld ); return this.intersectsSphere( sphere ); }; }(), intersectsSphere: function ( sphere ) { var planes = this.planes; var center = sphere.center; var negRadius = - sphere.radius; for ( var i = 0; i < 6; i ++ ) { var distance = planes[ i ].distanceToPoint( center ); if ( distance < negRadius ) { return false; } } return true; }, intersectsBox: function () { var p1 = new Vector3(), p2 = new Vector3(); return function intersectsBox( box ) { var planes = this.planes; for ( var i = 0; i < 6 ; i ++ ) { var plane = planes[ i ]; p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; var d1 = plane.distanceToPoint( p1 ); var d2 = plane.distanceToPoint( p2 ); // if both outside plane, no intersection if ( d1 < 0 && d2 < 0 ) { return false; } } return true; }; }(), containsPoint: function ( point ) { var planes = this.planes; for ( var i = 0; i < 6; i ++ ) { if ( planes[ i ].distanceToPoint( point ) < 0 ) { return false; } } return true; } }; /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function WebGLShadowMap( _renderer, _lights, _objects, capabilities ) { var _gl = _renderer.context, _state = _renderer.state, _frustum = new Frustum(), _projScreenMatrix = new Matrix4(), _lightShadows = _lights.shadows, _shadowMapSize = new Vector2(), _maxShadowMapSize = new Vector2( capabilities.maxTextureSize, capabilities.maxTextureSize ), _lookTarget = new Vector3(), _lightPositionWorld = new Vector3(), _renderList = [], _MorphingFlag = 1, _SkinningFlag = 2, _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, _depthMaterials = new Array( _NumberOfMaterialVariants ), _distanceMaterials = new Array( _NumberOfMaterialVariants ), _materialCache = {}; var cubeDirections = [ new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) ]; var cubeUps = [ new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) ]; var cube2DViewPorts = [ new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4(), new Vector4() ]; // init var depthMaterialTemplate = new MeshDepthMaterial(); depthMaterialTemplate.depthPacking = RGBADepthPacking; depthMaterialTemplate.clipping = true; var distanceShader = ShaderLib[ "distanceRGBA" ]; var distanceUniforms = UniformsUtils.clone( distanceShader.uniforms ); for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { var useMorphing = ( i & _MorphingFlag ) !== 0; var useSkinning = ( i & _SkinningFlag ) !== 0; var depthMaterial = depthMaterialTemplate.clone(); depthMaterial.morphTargets = useMorphing; depthMaterial.skinning = useSkinning; _depthMaterials[ i ] = depthMaterial; var distanceMaterial = new ShaderMaterial( { defines: { 'USE_SHADOWMAP': '' }, uniforms: distanceUniforms, vertexShader: distanceShader.vertexShader, fragmentShader: distanceShader.fragmentShader, morphTargets: useMorphing, skinning: useSkinning, clipping: true } ); _distanceMaterials[ i ] = distanceMaterial; } // var scope = this; this.enabled = false; this.autoUpdate = true; this.needsUpdate = false; this.type = PCFShadowMap; this.renderReverseSided = true; this.renderSingleSided = true; this.render = function ( scene, camera ) { if ( scope.enabled === false ) return; if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; if ( _lightShadows.length === 0 ) return; // Set GL state for depth map. _state.buffers.color.setClear( 1, 1, 1, 1 ); _state.disable( _gl.BLEND ); _state.setDepthTest( true ); _state.setScissorTest( false ); // render depth map var faceCount, isPointLight; for ( var i = 0, il = _lightShadows.length; i < il; i ++ ) { var light = _lightShadows[ i ]; var shadow = light.shadow; if ( shadow === undefined ) { console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); continue; } var shadowCamera = shadow.camera; _shadowMapSize.copy( shadow.mapSize ); _shadowMapSize.min( _maxShadowMapSize ); if ( light && light.isPointLight ) { faceCount = 6; isPointLight = true; var vpWidth = _shadowMapSize.x; var vpHeight = _shadowMapSize.y; // These viewports map a cube-map onto a 2D texture with the // following orientation: // // xzXZ // y Y // // X - Positive x direction // x - Negative x direction // Y - Positive y direction // y - Negative y direction // Z - Positive z direction // z - Negative z direction // positive X cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); // negative X cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); // positive Z cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); // negative Z cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); // positive Y cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); // negative Y cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); _shadowMapSize.x *= 4.0; _shadowMapSize.y *= 2.0; } else { faceCount = 1; isPointLight = false; } if ( shadow.map === null ) { var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); shadowCamera.updateProjectionMatrix(); } if ( shadow.isSpotLightShadow ) { shadow.update( light ); } // TODO (abelnation / sam-g-steel): is this needed? if (shadow && shadow.isRectAreaLightShadow ) { shadow.update( light ); } var shadowMap = shadow.map; var shadowMatrix = shadow.matrix; _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); shadowCamera.position.copy( _lightPositionWorld ); _renderer.setRenderTarget( shadowMap ); _renderer.clear(); // render shadow map for each cube face (if omni-directional) or // run a single pass if not for ( var face = 0; face < faceCount; face ++ ) { if ( isPointLight ) { _lookTarget.copy( shadowCamera.position ); _lookTarget.add( cubeDirections[ face ] ); shadowCamera.up.copy( cubeUps[ face ] ); shadowCamera.lookAt( _lookTarget ); var vpDimensions = cube2DViewPorts[ face ]; _state.viewport( vpDimensions ); } else { _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); shadowCamera.lookAt( _lookTarget ); } shadowCamera.updateMatrixWorld(); shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); // compute shadow matrix shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0 ); shadowMatrix.multiply( shadowCamera.projectionMatrix ); shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); // update camera matrices and frustum _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); // set object matrices & frustum culling _renderList.length = 0; projectObject( scene, camera, shadowCamera ); // render shadow map // render regular objects for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { var object = _renderList[ j ]; var geometry = _objects.update( object ); var material = object.material; if ( material && material.isMultiMaterial ) { var groups = geometry.groups; var materials = material.materials; for ( var k = 0, kl = groups.length; k < kl; k ++ ) { var group = groups[ k ]; var groupMaterial = materials[ group.materialIndex ]; if ( groupMaterial.visible === true ) { var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); } } } else { var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld ); _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } } } } // Restore GL state. var clearColor = _renderer.getClearColor(), clearAlpha = _renderer.getClearAlpha(); _renderer.setClearColor( clearColor, clearAlpha ); scope.needsUpdate = false; }; function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) { var geometry = object.geometry; var result = null; var materialVariants = _depthMaterials; var customMaterial = object.customDepthMaterial; if ( isPointLight ) { materialVariants = _distanceMaterials; customMaterial = object.customDistanceMaterial; } if ( ! customMaterial ) { var useMorphing = false; if ( material.morphTargets ) { if ( geometry && geometry.isBufferGeometry ) { useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; } else if ( geometry && geometry.isGeometry ) { useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0; } } var useSkinning = object.isSkinnedMesh && material.skinning; var variantIndex = 0; if ( useMorphing ) variantIndex |= _MorphingFlag; if ( useSkinning ) variantIndex |= _SkinningFlag; result = materialVariants[ variantIndex ]; } else { result = customMaterial; } if ( _renderer.localClippingEnabled && material.clipShadows === true && material.clippingPlanes.length !== 0 ) { // in this case we need a unique material instance reflecting the // appropriate state var keyA = result.uuid, keyB = material.uuid; var materialsForVariant = _materialCache[ keyA ]; if ( materialsForVariant === undefined ) { materialsForVariant = {}; _materialCache[ keyA ] = materialsForVariant; } var cachedMaterial = materialsForVariant[ keyB ]; if ( cachedMaterial === undefined ) { cachedMaterial = result.clone(); materialsForVariant[ keyB ] = cachedMaterial; } result = cachedMaterial; } result.visible = material.visible; result.wireframe = material.wireframe; var side = material.side; if ( scope.renderSingleSided && side == DoubleSide ) { side = FrontSide; } if ( scope.renderReverseSided ) { if ( side === FrontSide ) side = BackSide; else if ( side === BackSide ) side = FrontSide; } result.side = side; result.clipShadows = material.clipShadows; result.clippingPlanes = material.clippingPlanes; result.wireframeLinewidth = material.wireframeLinewidth; result.linewidth = material.linewidth; if ( isPointLight && result.uniforms.lightPos !== undefined ) { result.uniforms.lightPos.value.copy( lightPositionWorld ); } return result; } function projectObject( object, camera, shadowCamera ) { if ( object.visible === false ) return; var visible = ( object.layers.mask & camera.layers.mask ) !== 0; if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { var material = object.material; if ( material.visible === true ) { object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); _renderList.push( object ); } } } var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera, shadowCamera ); } } } /** * @author bhouston / http://clara.io */ function Ray( origin, direction ) { this.origin = ( origin !== undefined ) ? origin : new Vector3(); this.direction = ( direction !== undefined ) ? direction : new Vector3(); } Ray.prototype = { constructor: Ray, set: function ( origin, direction ) { this.origin.copy( origin ); this.direction.copy( direction ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( ray ) { this.origin.copy( ray.origin ); this.direction.copy( ray.direction ); return this; }, at: function ( t, optionalTarget ) { var result = optionalTarget || new Vector3(); return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); }, lookAt: function ( v ) { this.direction.copy( v ).sub( this.origin ).normalize(); return this; }, recast: function () { var v1 = new Vector3(); return function recast( t ) { this.origin.copy( this.at( t, v1 ) ); return this; }; }(), closestPointToPoint: function ( point, optionalTarget ) { var result = optionalTarget || new Vector3(); result.subVectors( point, this.origin ); var directionDistance = result.dot( this.direction ); if ( directionDistance < 0 ) { return result.copy( this.origin ); } return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); }, distanceToPoint: function ( point ) { return Math.sqrt( this.distanceSqToPoint( point ) ); }, distanceSqToPoint: function () { var v1 = new Vector3(); return function distanceSqToPoint( point ) { var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); // point behind the ray if ( directionDistance < 0 ) { return this.origin.distanceToSquared( point ); } v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); return v1.distanceToSquared( point ); }; }(), distanceSqToSegment: function () { var segCenter = new Vector3(); var segDir = new Vector3(); var diff = new Vector3(); return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h // It returns the min distance between the ray and the segment // defined by v0 and v1 // It can also set two optional targets : // - The closest point on the ray // - The closest point on the segment segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); segDir.copy( v1 ).sub( v0 ).normalize(); diff.copy( this.origin ).sub( segCenter ); var segExtent = v0.distanceTo( v1 ) * 0.5; var a01 = - this.direction.dot( segDir ); var b0 = diff.dot( this.direction ); var b1 = - diff.dot( segDir ); var c = diff.lengthSq(); var det = Math.abs( 1 - a01 * a01 ); var s0, s1, sqrDist, extDet; if ( det > 0 ) { // The ray and segment are not parallel. s0 = a01 * b1 - b0; s1 = a01 * b0 - b1; extDet = segExtent * det; if ( s0 >= 0 ) { if ( s1 >= - extDet ) { if ( s1 <= extDet ) { // region 0 // Minimum at interior points of ray and segment. var invDet = 1 / det; s0 *= invDet; s1 *= invDet; sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; } else { // region 1 s1 = segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { // region 5 s1 = - segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } else { if ( s1 <= - extDet ) { // region 4 s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } else if ( s1 <= extDet ) { // region 3 s0 = 0; s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = s1 * ( s1 + 2 * b1 ) + c; } else { // region 2 s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } } } else { // Ray and segment are parallel. s1 = ( a01 > 0 ) ? - segExtent : segExtent; s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; } if ( optionalPointOnRay ) { optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); } if ( optionalPointOnSegment ) { optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); } return sqrDist; }; }(), intersectSphere: function () { var v1 = new Vector3(); return function intersectSphere( sphere, optionalTarget ) { v1.subVectors( sphere.center, this.origin ); var tca = v1.dot( this.direction ); var d2 = v1.dot( v1 ) - tca * tca; var radius2 = sphere.radius * sphere.radius; if ( d2 > radius2 ) return null; var thc = Math.sqrt( radius2 - d2 ); // t0 = first intersect point - entrance on front of sphere var t0 = tca - thc; // t1 = second intersect point - exit point on back of sphere var t1 = tca + thc; // test to see if both t0 and t1 are behind the ray - if so, return null if ( t0 < 0 && t1 < 0 ) return null; // test to see if t0 is behind the ray: // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, // in order to always return an intersect point that is in front of the ray. if ( t0 < 0 ) return this.at( t1, optionalTarget ); // else t0 is in front of the ray, so return the first collision point scaled by t0 return this.at( t0, optionalTarget ); }; }(), intersectsSphere: function ( sphere ) { return this.distanceToPoint( sphere.center ) <= sphere.radius; }, distanceToPlane: function ( plane ) { var denominator = plane.normal.dot( this.direction ); if ( denominator === 0 ) { // line is coplanar, return origin if ( plane.distanceToPoint( this.origin ) === 0 ) { return 0; } // Null is preferable to undefined since undefined means.... it is undefined return null; } var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; // Return if the ray never intersects the plane return t >= 0 ? t : null; }, intersectPlane: function ( plane, optionalTarget ) { var t = this.distanceToPlane( plane ); if ( t === null ) { return null; } return this.at( t, optionalTarget ); }, intersectsPlane: function ( plane ) { // check if the ray lies on the plane first var distToPoint = plane.distanceToPoint( this.origin ); if ( distToPoint === 0 ) { return true; } var denominator = plane.normal.dot( this.direction ); if ( denominator * distToPoint < 0 ) { return true; } // ray origin is behind the plane (and is pointing behind it) return false; }, intersectBox: function ( box, optionalTarget ) { var tmin, tmax, tymin, tymax, tzmin, tzmax; var invdirx = 1 / this.direction.x, invdiry = 1 / this.direction.y, invdirz = 1 / this.direction.z; var origin = this.origin; if ( invdirx >= 0 ) { tmin = ( box.min.x - origin.x ) * invdirx; tmax = ( box.max.x - origin.x ) * invdirx; } else { tmin = ( box.max.x - origin.x ) * invdirx; tmax = ( box.min.x - origin.x ) * invdirx; } if ( invdiry >= 0 ) { tymin = ( box.min.y - origin.y ) * invdiry; tymax = ( box.max.y - origin.y ) * invdiry; } else { tymin = ( box.max.y - origin.y ) * invdiry; tymax = ( box.min.y - origin.y ) * invdiry; } if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; // These lines also handle the case where tmin or tmax is NaN // (result of 0 * Infinity). x !== x returns true if x is NaN if ( tymin > tmin || tmin !== tmin ) tmin = tymin; if ( tymax < tmax || tmax !== tmax ) tmax = tymax; if ( invdirz >= 0 ) { tzmin = ( box.min.z - origin.z ) * invdirz; tzmax = ( box.max.z - origin.z ) * invdirz; } else { tzmin = ( box.max.z - origin.z ) * invdirz; tzmax = ( box.min.z - origin.z ) * invdirz; } if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; //return point closest to the ray (positive side) if ( tmax < 0 ) return null; return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); }, intersectsBox: ( function () { var v = new Vector3(); return function intersectsBox( box ) { return this.intersectBox( box, v ) !== null; }; } )(), intersectTriangle: function () { // Compute the offset origin, edges, and normal. var diff = new Vector3(); var edge1 = new Vector3(); var edge2 = new Vector3(); var normal = new Vector3(); return function intersectTriangle( a, b, c, backfaceCulling, optionalTarget ) { // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h edge1.subVectors( b, a ); edge2.subVectors( c, a ); normal.crossVectors( edge1, edge2 ); // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) var DdN = this.direction.dot( normal ); var sign; if ( DdN > 0 ) { if ( backfaceCulling ) return null; sign = 1; } else if ( DdN < 0 ) { sign = - 1; DdN = - DdN; } else { return null; } diff.subVectors( this.origin, a ); var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); // b1 < 0, no intersection if ( DdQxE2 < 0 ) { return null; } var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); // b2 < 0, no intersection if ( DdE1xQ < 0 ) { return null; } // b1+b2 > 1, no intersection if ( DdQxE2 + DdE1xQ > DdN ) { return null; } // Line intersects triangle, check if ray does. var QdN = - sign * diff.dot( normal ); // t < 0, no intersection if ( QdN < 0 ) { return null; } // Ray intersects triangle. return this.at( QdN / DdN, optionalTarget ); }; }(), applyMatrix4: function ( matrix4 ) { this.direction.add( this.origin ).applyMatrix4( matrix4 ); this.origin.applyMatrix4( matrix4 ); this.direction.sub( this.origin ); this.direction.normalize(); return this; }, equals: function ( ray ) { return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley * @author bhouston / http://clara.io */ function Euler( x, y, z, order ) { this._x = x || 0; this._y = y || 0; this._z = z || 0; this._order = order || Euler.DefaultOrder; } Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; Euler.DefaultOrder = 'XYZ'; Euler.prototype = { constructor: Euler, isEuler: true, get x () { return this._x; }, set x ( value ) { this._x = value; this.onChangeCallback(); }, get y () { return this._y; }, set y ( value ) { this._y = value; this.onChangeCallback(); }, get z () { return this._z; }, set z ( value ) { this._z = value; this.onChangeCallback(); }, get order () { return this._order; }, set order ( value ) { this._order = value; this.onChangeCallback(); }, set: function ( x, y, z, order ) { this._x = x; this._y = y; this._z = z; this._order = order || this._order; this.onChangeCallback(); return this; }, clone: function () { return new this.constructor( this._x, this._y, this._z, this._order ); }, copy: function ( euler ) { this._x = euler._x; this._y = euler._y; this._z = euler._z; this._order = euler._order; this.onChangeCallback(); return this; }, setFromRotationMatrix: function ( m, order, update ) { var clamp = _Math.clamp; // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) var te = m.elements; var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; order = order || this._order; if ( order === 'XYZ' ) { this._y = Math.asin( clamp( m13, - 1, 1 ) ); if ( Math.abs( m13 ) < 0.99999 ) { this._x = Math.atan2( - m23, m33 ); this._z = Math.atan2( - m12, m11 ); } else { this._x = Math.atan2( m32, m22 ); this._z = 0; } } else if ( order === 'YXZ' ) { this._x = Math.asin( - clamp( m23, - 1, 1 ) ); if ( Math.abs( m23 ) < 0.99999 ) { this._y = Math.atan2( m13, m33 ); this._z = Math.atan2( m21, m22 ); } else { this._y = Math.atan2( - m31, m11 ); this._z = 0; } } else if ( order === 'ZXY' ) { this._x = Math.asin( clamp( m32, - 1, 1 ) ); if ( Math.abs( m32 ) < 0.99999 ) { this._y = Math.atan2( - m31, m33 ); this._z = Math.atan2( - m12, m22 ); } else { this._y = 0; this._z = Math.atan2( m21, m11 ); } } else if ( order === 'ZYX' ) { this._y = Math.asin( - clamp( m31, - 1, 1 ) ); if ( Math.abs( m31 ) < 0.99999 ) { this._x = Math.atan2( m32, m33 ); this._z = Math.atan2( m21, m11 ); } else { this._x = 0; this._z = Math.atan2( - m12, m22 ); } } else if ( order === 'YZX' ) { this._z = Math.asin( clamp( m21, - 1, 1 ) ); if ( Math.abs( m21 ) < 0.99999 ) { this._x = Math.atan2( - m23, m22 ); this._y = Math.atan2( - m31, m11 ); } else { this._x = 0; this._y = Math.atan2( m13, m33 ); } } else if ( order === 'XZY' ) { this._z = Math.asin( - clamp( m12, - 1, 1 ) ); if ( Math.abs( m12 ) < 0.99999 ) { this._x = Math.atan2( m32, m22 ); this._y = Math.atan2( m13, m11 ); } else { this._x = Math.atan2( - m23, m33 ); this._y = 0; } } else { console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ); } this._order = order; if ( update !== false ) this.onChangeCallback(); return this; }, setFromQuaternion: function () { var matrix; return function setFromQuaternion( q, order, update ) { if ( matrix === undefined ) matrix = new Matrix4(); matrix.makeRotationFromQuaternion( q ); return this.setFromRotationMatrix( matrix, order, update ); }; }(), setFromVector3: function ( v, order ) { return this.set( v.x, v.y, v.z, order || this._order ); }, reorder: function () { // WARNING: this discards revolution information -bhouston var q = new Quaternion(); return function reorder( newOrder ) { q.setFromEuler( this ); return this.setFromQuaternion( q, newOrder ); }; }(), equals: function ( euler ) { return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); }, fromArray: function ( array ) { this._x = array[ 0 ]; this._y = array[ 1 ]; this._z = array[ 2 ]; if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; this.onChangeCallback(); return this; }, toArray: function ( array, offset ) { if ( array === undefined ) array = []; if ( offset === undefined ) offset = 0; array[ offset ] = this._x; array[ offset + 1 ] = this._y; array[ offset + 2 ] = this._z; array[ offset + 3 ] = this._order; return array; }, toVector3: function ( optionalResult ) { if ( optionalResult ) { return optionalResult.set( this._x, this._y, this._z ); } else { return new Vector3( this._x, this._y, this._z ); } }, onChange: function ( callback ) { this.onChangeCallback = callback; return this; }, onChangeCallback: function () {} }; /** * @author mrdoob / http://mrdoob.com/ */ function Layers() { this.mask = 1; } Layers.prototype = { constructor: Layers, set: function ( channel ) { this.mask = 1 << channel; }, enable: function ( channel ) { this.mask |= 1 << channel; }, toggle: function ( channel ) { this.mask ^= 1 << channel; }, disable: function ( channel ) { this.mask &= ~ ( 1 << channel ); }, test: function ( layers ) { return ( this.mask & layers.mask ) !== 0; } }; /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author elephantatwork / www.elephantatwork.ch */ var object3DId = 0; function Object3D() { Object.defineProperty( this, 'id', { value: object3DId ++ } ); this.uuid = _Math.generateUUID(); this.name = ''; this.type = 'Object3D'; this.parent = null; this.children = []; this.up = Object3D.DefaultUp.clone(); var position = new Vector3(); var rotation = new Euler(); var quaternion = new Quaternion(); var scale = new Vector3( 1, 1, 1 ); function onRotationChange() { quaternion.setFromEuler( rotation, false ); } function onQuaternionChange() { rotation.setFromQuaternion( quaternion, undefined, false ); } rotation.onChange( onRotationChange ); quaternion.onChange( onQuaternionChange ); Object.defineProperties( this, { position: { enumerable: true, value: position }, rotation: { enumerable: true, value: rotation }, quaternion: { enumerable: true, value: quaternion }, scale: { enumerable: true, value: scale }, modelViewMatrix: { value: new Matrix4() }, normalMatrix: { value: new Matrix3() } } ); this.matrix = new Matrix4(); this.matrixWorld = new Matrix4(); this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; this.matrixWorldNeedsUpdate = false; this.layers = new Layers(); this.visible = true; this.castShadow = false; this.receiveShadow = false; this.frustumCulled = true; this.renderOrder = 0; this.userData = {}; this.onBeforeRender = function () {}; this.onAfterRender = function () {}; } Object3D.DefaultUp = new Vector3( 0, 1, 0 ); Object3D.DefaultMatrixAutoUpdate = true; Object.assign( Object3D.prototype, EventDispatcher.prototype, { isObject3D: true, applyMatrix: function ( matrix ) { this.matrix.multiplyMatrices( matrix, this.matrix ); this.matrix.decompose( this.position, this.quaternion, this.scale ); }, setRotationFromAxisAngle: function ( axis, angle ) { // assumes axis is normalized this.quaternion.setFromAxisAngle( axis, angle ); }, setRotationFromEuler: function ( euler ) { this.quaternion.setFromEuler( euler, true ); }, setRotationFromMatrix: function ( m ) { // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) this.quaternion.setFromRotationMatrix( m ); }, setRotationFromQuaternion: function ( q ) { // assumes q is normalized this.quaternion.copy( q ); }, rotateOnAxis: function () { // rotate object on axis in object space // axis is assumed to be normalized var q1 = new Quaternion(); return function rotateOnAxis( axis, angle ) { q1.setFromAxisAngle( axis, angle ); this.quaternion.multiply( q1 ); return this; }; }(), rotateX: function () { var v1 = new Vector3( 1, 0, 0 ); return function rotateX( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), rotateY: function () { var v1 = new Vector3( 0, 1, 0 ); return function rotateY( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), rotateZ: function () { var v1 = new Vector3( 0, 0, 1 ); return function rotateZ( angle ) { return this.rotateOnAxis( v1, angle ); }; }(), translateOnAxis: function () { // translate object by distance along axis in object space // axis is assumed to be normalized var v1 = new Vector3(); return function translateOnAxis( axis, distance ) { v1.copy( axis ).applyQuaternion( this.quaternion ); this.position.add( v1.multiplyScalar( distance ) ); return this; }; }(), translateX: function () { var v1 = new Vector3( 1, 0, 0 ); return function translateX( distance ) { return this.translateOnAxis( v1, distance ); }; }(), translateY: function () { var v1 = new Vector3( 0, 1, 0 ); return function translateY( distance ) { return this.translateOnAxis( v1, distance ); }; }(), translateZ: function () { var v1 = new Vector3( 0, 0, 1 ); return function translateZ( distance ) { return this.translateOnAxis( v1, distance ); }; }(), localToWorld: function ( vector ) { return vector.applyMatrix4( this.matrixWorld ); }, worldToLocal: function () { var m1 = new Matrix4(); return function worldToLocal( vector ) { return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); }; }(), lookAt: function () { // This routine does not support objects with rotated and/or translated parent(s) var m1 = new Matrix4(); return function lookAt( vector ) { m1.lookAt( vector, this.position, this.up ); this.quaternion.setFromRotationMatrix( m1 ); }; }(), add: function ( object ) { if ( arguments.length > 1 ) { for ( var i = 0; i < arguments.length; i ++ ) { this.add( arguments[ i ] ); } return this; } if ( object === this ) { console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); return this; } if ( ( object && object.isObject3D ) ) { if ( object.parent !== null ) { object.parent.remove( object ); } object.parent = this; object.dispatchEvent( { type: 'added' } ); this.children.push( object ); } else { console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); } return this; }, remove: function ( object ) { if ( arguments.length > 1 ) { for ( var i = 0; i < arguments.length; i ++ ) { this.remove( arguments[ i ] ); } } var index = this.children.indexOf( object ); if ( index !== - 1 ) { object.parent = null; object.dispatchEvent( { type: 'removed' } ); this.children.splice( index, 1 ); } }, getObjectById: function ( id ) { return this.getObjectByProperty( 'id', id ); }, getObjectByName: function ( name ) { return this.getObjectByProperty( 'name', name ); }, getObjectByProperty: function ( name, value ) { if ( this[ name ] === value ) return this; for ( var i = 0, l = this.children.length; i < l; i ++ ) { var child = this.children[ i ]; var object = child.getObjectByProperty( name, value ); if ( object !== undefined ) { return object; } } return undefined; }, getWorldPosition: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); this.updateMatrixWorld( true ); return result.setFromMatrixPosition( this.matrixWorld ); }, getWorldQuaternion: function () { var position = new Vector3(); var scale = new Vector3(); return function getWorldQuaternion( optionalTarget ) { var result = optionalTarget || new Quaternion(); this.updateMatrixWorld( true ); this.matrixWorld.decompose( position, result, scale ); return result; }; }(), getWorldRotation: function () { var quaternion = new Quaternion(); return function getWorldRotation( optionalTarget ) { var result = optionalTarget || new Euler(); this.getWorldQuaternion( quaternion ); return result.setFromQuaternion( quaternion, this.rotation.order, false ); }; }(), getWorldScale: function () { var position = new Vector3(); var quaternion = new Quaternion(); return function getWorldScale( optionalTarget ) { var result = optionalTarget || new Vector3(); this.updateMatrixWorld( true ); this.matrixWorld.decompose( position, quaternion, result ); return result; }; }(), getWorldDirection: function () { var quaternion = new Quaternion(); return function getWorldDirection( optionalTarget ) { var result = optionalTarget || new Vector3(); this.getWorldQuaternion( quaternion ); return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); }; }(), raycast: function () {}, traverse: function ( callback ) { callback( this ); var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverse( callback ); } }, traverseVisible: function ( callback ) { if ( this.visible === false ) return; callback( this ); var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].traverseVisible( callback ); } }, traverseAncestors: function ( callback ) { var parent = this.parent; if ( parent !== null ) { callback( parent ); parent.traverseAncestors( callback ); } }, updateMatrix: function () { this.matrix.compose( this.position, this.quaternion, this.scale ); this.matrixWorldNeedsUpdate = true; }, updateMatrixWorld: function ( force ) { if ( this.matrixAutoUpdate === true ) this.updateMatrix(); if ( this.matrixWorldNeedsUpdate === true || force === true ) { if ( this.parent === null ) { this.matrixWorld.copy( this.matrix ); } else { this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } this.matrixWorldNeedsUpdate = false; force = true; } // update children var children = this.children; for ( var i = 0, l = children.length; i < l; i ++ ) { children[ i ].updateMatrixWorld( force ); } }, toJSON: function ( meta ) { // meta is '' when called from JSON.stringify var isRootObject = ( meta === undefined || meta === '' ); var output = {}; // meta is a hash used to collect geometries, materials. // not providing it implies that this is the root object // being serialized. if ( isRootObject ) { // initialize meta obj meta = { geometries: {}, materials: {}, textures: {}, images: {} }; output.metadata = { version: 4.4, type: 'Object', generator: 'Object3D.toJSON' }; } // standard Object3D serialization var object = {}; object.uuid = this.uuid; object.type = this.type; if ( this.name !== '' ) object.name = this.name; if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; if ( this.castShadow === true ) object.castShadow = true; if ( this.receiveShadow === true ) object.receiveShadow = true; if ( this.visible === false ) object.visible = false; object.matrix = this.matrix.toArray(); // if ( this.geometry !== undefined ) { if ( meta.geometries[ this.geometry.uuid ] === undefined ) { meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta ); } object.geometry = this.geometry.uuid; } if ( this.material !== undefined ) { if ( meta.materials[ this.material.uuid ] === undefined ) { meta.materials[ this.material.uuid ] = this.material.toJSON( meta ); } object.material = this.material.uuid; } // if ( this.children.length > 0 ) { object.children = []; for ( var i = 0; i < this.children.length; i ++ ) { object.children.push( this.children[ i ].toJSON( meta ).object ); } } if ( isRootObject ) { var geometries = extractFromCache( meta.geometries ); var materials = extractFromCache( meta.materials ); var textures = extractFromCache( meta.textures ); var images = extractFromCache( meta.images ); if ( geometries.length > 0 ) output.geometries = geometries; if ( materials.length > 0 ) output.materials = materials; if ( textures.length > 0 ) output.textures = textures; if ( images.length > 0 ) output.images = images; } output.object = object; return output; // extract data from the cache hash // remove metadata on each item // and return as array function extractFromCache( cache ) { var values = []; for ( var key in cache ) { var data = cache[ key ]; delete data.metadata; values.push( data ); } return values; } }, clone: function ( recursive ) { return new this.constructor().copy( this, recursive ); }, copy: function ( source, recursive ) { if ( recursive === undefined ) recursive = true; this.name = source.name; this.up.copy( source.up ); this.position.copy( source.position ); this.quaternion.copy( source.quaternion ); this.scale.copy( source.scale ); this.matrix.copy( source.matrix ); this.matrixWorld.copy( source.matrixWorld ); this.matrixAutoUpdate = source.matrixAutoUpdate; this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; this.layers.mask = source.layers.mask; this.visible = source.visible; this.castShadow = source.castShadow; this.receiveShadow = source.receiveShadow; this.frustumCulled = source.frustumCulled; this.renderOrder = source.renderOrder; this.userData = JSON.parse( JSON.stringify( source.userData ) ); if ( recursive === true ) { for ( var i = 0; i < source.children.length; i ++ ) { var child = source.children[ i ]; this.add( child.clone() ); } } return this; } } ); /** * @author bhouston / http://clara.io */ function Line3( start, end ) { this.start = ( start !== undefined ) ? start : new Vector3(); this.end = ( end !== undefined ) ? end : new Vector3(); } Line3.prototype = { constructor: Line3, set: function ( start, end ) { this.start.copy( start ); this.end.copy( end ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( line ) { this.start.copy( line.start ); this.end.copy( line.end ); return this; }, getCenter: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); }, delta: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return result.subVectors( this.end, this.start ); }, distanceSq: function () { return this.start.distanceToSquared( this.end ); }, distance: function () { return this.start.distanceTo( this.end ); }, at: function ( t, optionalTarget ) { var result = optionalTarget || new Vector3(); return this.delta( result ).multiplyScalar( t ).add( this.start ); }, closestPointToPointParameter: function () { var startP = new Vector3(); var startEnd = new Vector3(); return function closestPointToPointParameter( point, clampToLine ) { startP.subVectors( point, this.start ); startEnd.subVectors( this.end, this.start ); var startEnd2 = startEnd.dot( startEnd ); var startEnd_startP = startEnd.dot( startP ); var t = startEnd_startP / startEnd2; if ( clampToLine ) { t = _Math.clamp( t, 0, 1 ); } return t; }; }(), closestPointToPoint: function ( point, clampToLine, optionalTarget ) { var t = this.closestPointToPointParameter( point, clampToLine ); var result = optionalTarget || new Vector3(); return this.delta( result ).multiplyScalar( t ).add( this.start ); }, applyMatrix4: function ( matrix ) { this.start.applyMatrix4( matrix ); this.end.applyMatrix4( matrix ); return this; }, equals: function ( line ) { return line.start.equals( this.start ) && line.end.equals( this.end ); } }; /** * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ function Triangle( a, b, c ) { this.a = ( a !== undefined ) ? a : new Vector3(); this.b = ( b !== undefined ) ? b : new Vector3(); this.c = ( c !== undefined ) ? c : new Vector3(); } Triangle.normal = function () { var v0 = new Vector3(); return function normal( a, b, c, optionalTarget ) { var result = optionalTarget || new Vector3(); result.subVectors( c, b ); v0.subVectors( a, b ); result.cross( v0 ); var resultLengthSq = result.lengthSq(); if ( resultLengthSq > 0 ) { return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); } return result.set( 0, 0, 0 ); }; }(); // static/instance method to calculate barycentric coordinates // based on: http://www.blackpawn.com/texts/pointinpoly/default.html Triangle.barycoordFromPoint = function () { var v0 = new Vector3(); var v1 = new Vector3(); var v2 = new Vector3(); return function barycoordFromPoint( point, a, b, c, optionalTarget ) { v0.subVectors( c, a ); v1.subVectors( b, a ); v2.subVectors( point, a ); var dot00 = v0.dot( v0 ); var dot01 = v0.dot( v1 ); var dot02 = v0.dot( v2 ); var dot11 = v1.dot( v1 ); var dot12 = v1.dot( v2 ); var denom = ( dot00 * dot11 - dot01 * dot01 ); var result = optionalTarget || new Vector3(); // collinear or singular triangle if ( denom === 0 ) { // arbitrary location outside of triangle? // not sure if this is the best idea, maybe should be returning undefined return result.set( - 2, - 1, - 1 ); } var invDenom = 1 / denom; var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; // barycentric coordinates must always sum to 1 return result.set( 1 - u - v, v, u ); }; }(); Triangle.containsPoint = function () { var v1 = new Vector3(); return function containsPoint( point, a, b, c ) { var result = Triangle.barycoordFromPoint( point, a, b, c, v1 ); return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); }; }(); Triangle.prototype = { constructor: Triangle, set: function ( a, b, c ) { this.a.copy( a ); this.b.copy( b ); this.c.copy( c ); return this; }, setFromPointsAndIndices: function ( points, i0, i1, i2 ) { this.a.copy( points[ i0 ] ); this.b.copy( points[ i1 ] ); this.c.copy( points[ i2 ] ); return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( triangle ) { this.a.copy( triangle.a ); this.b.copy( triangle.b ); this.c.copy( triangle.c ); return this; }, area: function () { var v0 = new Vector3(); var v1 = new Vector3(); return function area() { v0.subVectors( this.c, this.b ); v1.subVectors( this.a, this.b ); return v0.cross( v1 ).length() * 0.5; }; }(), midpoint: function ( optionalTarget ) { var result = optionalTarget || new Vector3(); return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); }, normal: function ( optionalTarget ) { return Triangle.normal( this.a, this.b, this.c, optionalTarget ); }, plane: function ( optionalTarget ) { var result = optionalTarget || new Plane(); return result.setFromCoplanarPoints( this.a, this.b, this.c ); }, barycoordFromPoint: function ( point, optionalTarget ) { return Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); }, containsPoint: function ( point ) { return Triangle.containsPoint( point, this.a, this.b, this.c ); }, closestPointToPoint: function () { var plane, edgeList, projectedPoint, closestPoint; return function closestPointToPoint( point, optionalTarget ) { if ( plane === undefined ) { plane = new Plane(); edgeList = [ new Line3(), new Line3(), new Line3() ]; projectedPoint = new Vector3(); closestPoint = new Vector3(); } var result = optionalTarget || new Vector3(); var minDistance = Infinity; // project the point onto the plane of the triangle plane.setFromCoplanarPoints( this.a, this.b, this.c ); plane.projectPoint( point, projectedPoint ); // check if the projection lies within the triangle if( this.containsPoint( projectedPoint ) === true ) { // if so, this is the closest point result.copy( projectedPoint ); } else { // if not, the point falls outside the triangle. the result is the closest point to the triangle's edges or vertices edgeList[ 0 ].set( this.a, this.b ); edgeList[ 1 ].set( this.b, this.c ); edgeList[ 2 ].set( this.c, this.a ); for( var i = 0; i < edgeList.length; i ++ ) { edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); var distance = projectedPoint.distanceToSquared( closestPoint ); if( distance < minDistance ) { minDistance = distance; result.copy( closestPoint ); } } } return result; }; }(), equals: function ( triangle ) { return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); } }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Face3( a, b, c, normal, color, materialIndex ) { this.a = a; this.b = b; this.c = c; this.normal = (normal && normal.isVector3) ? normal : new Vector3(); this.vertexNormals = Array.isArray( normal ) ? normal : []; this.color = (color && color.isColor) ? color : new Color(); this.vertexColors = Array.isArray( color ) ? color : []; this.materialIndex = materialIndex !== undefined ? materialIndex : 0; } Face3.prototype = { constructor: Face3, clone: function () { return new this.constructor().copy( this ); }, copy: function ( source ) { this.a = source.a; this.b = source.b; this.c = source.c; this.normal.copy( source.normal ); this.color.copy( source.color ); this.materialIndex = source.materialIndex; for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); } for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { this.vertexColors[ i ] = source.vertexColors[ i ].clone(); } return this; } }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * shading: THREE.SmoothShading, * depthTest: , * depthWrite: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: * } */ function MeshBasicMaterial( parameters ) { Material.call( this ); this.type = 'MeshBasicMaterial'; this.color = new Color( 0xffffff ); // emissive this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.skinning = false; this.morphTargets = false; this.lights = false; this.setValues( parameters ); } MeshBasicMaterial.prototype = Object.create( Material.prototype ); MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; MeshBasicMaterial.prototype.isMeshBasicMaterial = true; MeshBasicMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; return this; }; /** * @author mrdoob / http://mrdoob.com/ */ function BufferAttribute( array, itemSize, normalized ) { if ( Array.isArray( array ) ) { throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); } this.uuid = _Math.generateUUID(); this.array = array; this.itemSize = itemSize; this.count = array !== undefined ? array.length / itemSize : 0; this.normalized = normalized === true; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.onUploadCallback = function () {}; this.version = 0; } BufferAttribute.prototype = { constructor: BufferAttribute, isBufferAttribute: true, set needsUpdate( value ) { if ( value === true ) this.version ++; }, setArray: function ( array ) { if ( Array.isArray( array ) ) { throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); } this.count = array !== undefined ? array.length / this.itemSize : 0; this.array = array; }, setDynamic: function ( value ) { this.dynamic = value; return this; }, copy: function ( source ) { this.array = new source.array.constructor( source.array ); this.itemSize = source.itemSize; this.count = source.count; this.normalized = source.normalized; this.dynamic = source.dynamic; return this; }, copyAt: function ( index1, attribute, index2 ) { index1 *= this.itemSize; index2 *= attribute.itemSize; for ( var i = 0, l = this.itemSize; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; }, copyArray: function ( array ) { this.array.set( array ); return this; }, copyColorsArray: function ( colors ) { var array = this.array, offset = 0; for ( var i = 0, l = colors.length; i < l; i ++ ) { var color = colors[ i ]; if ( color === undefined ) { console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); color = new Color(); } array[ offset ++ ] = color.r; array[ offset ++ ] = color.g; array[ offset ++ ] = color.b; } return this; }, copyIndicesArray: function ( indices ) { var array = this.array, offset = 0; for ( var i = 0, l = indices.length; i < l; i ++ ) { var index = indices[ i ]; array[ offset ++ ] = index.a; array[ offset ++ ] = index.b; array[ offset ++ ] = index.c; } return this; }, copyVector2sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); vector = new Vector2(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; } return this; }, copyVector3sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); vector = new Vector3(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; } return this; }, copyVector4sArray: function ( vectors ) { var array = this.array, offset = 0; for ( var i = 0, l = vectors.length; i < l; i ++ ) { var vector = vectors[ i ]; if ( vector === undefined ) { console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); vector = new Vector4(); } array[ offset ++ ] = vector.x; array[ offset ++ ] = vector.y; array[ offset ++ ] = vector.z; array[ offset ++ ] = vector.w; } return this; }, set: function ( value, offset ) { if ( offset === undefined ) offset = 0; this.array.set( value, offset ); return this; }, getX: function ( index ) { return this.array[ index * this.itemSize ]; }, setX: function ( index, x ) { this.array[ index * this.itemSize ] = x; return this; }, getY: function ( index ) { return this.array[ index * this.itemSize + 1 ]; }, setY: function ( index, y ) { this.array[ index * this.itemSize + 1 ] = y; return this; }, getZ: function ( index ) { return this.array[ index * this.itemSize + 2 ]; }, setZ: function ( index, z ) { this.array[ index * this.itemSize + 2 ] = z; return this; }, getW: function ( index ) { return this.array[ index * this.itemSize + 3 ]; }, setW: function ( index, w ) { this.array[ index * this.itemSize + 3 ] = w; return this; }, setXY: function ( index, x, y ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; return this; }, setXYZ: function ( index, x, y, z ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; return this; }, setXYZW: function ( index, x, y, z, w ) { index *= this.itemSize; this.array[ index + 0 ] = x; this.array[ index + 1 ] = y; this.array[ index + 2 ] = z; this.array[ index + 3 ] = w; return this; }, onUpload: function ( callback ) { this.onUploadCallback = callback; return this; }, clone: function () { return new this.constructor().copy( this ); } }; // function Int8BufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Int8Array( array ), itemSize ); } Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; function Uint8BufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Uint8Array( array ), itemSize ); } Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; function Uint8ClampedBufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize ); } Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; function Int16BufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Int16Array( array ), itemSize ); } Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; function Uint16BufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Uint16Array( array ), itemSize ); } Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; function Int32BufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Int32Array( array ), itemSize ); } Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; function Uint32BufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Uint32Array( array ), itemSize ); } Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; function Float32BufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Float32Array( array ), itemSize ); } Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; function Float64BufferAttribute( array, itemSize ) { BufferAttribute.call( this, new Float64Array( array ), itemSize ); } Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; /** * @author mrdoob / http://mrdoob.com/ */ function DirectGeometry() { this.indices = []; this.vertices = []; this.normals = []; this.colors = []; this.uvs = []; this.uvs2 = []; this.groups = []; this.morphTargets = {}; this.skinWeights = []; this.skinIndices = []; // this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // update flags this.verticesNeedUpdate = false; this.normalsNeedUpdate = false; this.colorsNeedUpdate = false; this.uvsNeedUpdate = false; this.groupsNeedUpdate = false; } Object.assign( DirectGeometry.prototype, { computeGroups: function ( geometry ) { var group; var groups = []; var materialIndex = undefined; var faces = geometry.faces; for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; // materials if ( face.materialIndex !== materialIndex ) { materialIndex = face.materialIndex; if ( group !== undefined ) { group.count = ( i * 3 ) - group.start; groups.push( group ); } group = { start: i * 3, materialIndex: materialIndex }; } } if ( group !== undefined ) { group.count = ( i * 3 ) - group.start; groups.push( group ); } this.groups = groups; }, fromGeometry: function ( geometry ) { var faces = geometry.faces; var vertices = geometry.vertices; var faceVertexUvs = geometry.faceVertexUvs; var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; // morphs var morphTargets = geometry.morphTargets; var morphTargetsLength = morphTargets.length; var morphTargetsPosition; if ( morphTargetsLength > 0 ) { morphTargetsPosition = []; for ( var i = 0; i < morphTargetsLength; i ++ ) { morphTargetsPosition[ i ] = []; } this.morphTargets.position = morphTargetsPosition; } var morphNormals = geometry.morphNormals; var morphNormalsLength = morphNormals.length; var morphTargetsNormal; if ( morphNormalsLength > 0 ) { morphTargetsNormal = []; for ( var i = 0; i < morphNormalsLength; i ++ ) { morphTargetsNormal[ i ] = []; } this.morphTargets.normal = morphTargetsNormal; } // skins var skinIndices = geometry.skinIndices; var skinWeights = geometry.skinWeights; var hasSkinIndices = skinIndices.length === vertices.length; var hasSkinWeights = skinWeights.length === vertices.length; // for ( var i = 0; i < faces.length; i ++ ) { var face = faces[ i ]; this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); } else { var normal = face.normal; this.normals.push( normal, normal, normal ); } var vertexColors = face.vertexColors; if ( vertexColors.length === 3 ) { this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); } else { var color = face.color; this.colors.push( color, color, color ); } if ( hasFaceVertexUv === true ) { var vertexUvs = faceVertexUvs[ 0 ][ i ]; if ( vertexUvs !== undefined ) { this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); } else { console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); } } if ( hasFaceVertexUv2 === true ) { var vertexUvs = faceVertexUvs[ 1 ][ i ]; if ( vertexUvs !== undefined ) { this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); } else { console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); } } // morphs for ( var j = 0; j < morphTargetsLength; j ++ ) { var morphTarget = morphTargets[ j ].vertices; morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); } for ( var j = 0; j < morphNormalsLength; j ++ ) { var morphNormal = morphNormals[ j ].vertexNormals[ i ]; morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); } // skins if ( hasSkinIndices ) { this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); } if ( hasSkinWeights ) { this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); } } this.computeGroups( geometry ); this.verticesNeedUpdate = geometry.verticesNeedUpdate; this.normalsNeedUpdate = geometry.normalsNeedUpdate; this.colorsNeedUpdate = geometry.colorsNeedUpdate; this.uvsNeedUpdate = geometry.uvsNeedUpdate; this.groupsNeedUpdate = geometry.groupsNeedUpdate; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author kile / http://kile.stravaganza.org/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author zz85 / http://www.lab4games.net/zz85/blog * @author bhouston / http://clara.io */ function Geometry() { Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); this.uuid = _Math.generateUUID(); this.name = ''; this.type = 'Geometry'; this.vertices = []; this.colors = []; this.faces = []; this.faceVertexUvs = [ [] ]; this.morphTargets = []; this.morphNormals = []; this.skinWeights = []; this.skinIndices = []; this.lineDistances = []; this.boundingBox = null; this.boundingSphere = null; // update flags this.elementsNeedUpdate = false; this.verticesNeedUpdate = false; this.uvsNeedUpdate = false; this.normalsNeedUpdate = false; this.colorsNeedUpdate = false; this.lineDistancesNeedUpdate = false; this.groupsNeedUpdate = false; } Object.assign( Geometry.prototype, EventDispatcher.prototype, { isGeometry: true, applyMatrix: function ( matrix ) { var normalMatrix = new Matrix3().getNormalMatrix( matrix ); for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { var vertex = this.vertices[ i ]; vertex.applyMatrix4( matrix ); } for ( var i = 0, il = this.faces.length; i < il; i ++ ) { var face = this.faces[ i ]; face.normal.applyMatrix3( normalMatrix ).normalize(); for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); } } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } this.verticesNeedUpdate = true; this.normalsNeedUpdate = true; return this; }, rotateX: function () { // rotate geometry around world x-axis var m1; return function rotateX( angle ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeRotationX( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateY: function () { // rotate geometry around world y-axis var m1; return function rotateY( angle ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeRotationY( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateZ: function () { // rotate geometry around world z-axis var m1; return function rotateZ( angle ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeRotationZ( angle ); this.applyMatrix( m1 ); return this; }; }(), translate: function () { // translate geometry var m1; return function translate( x, y, z ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeTranslation( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), scale: function () { // scale geometry var m1; return function scale( x, y, z ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeScale( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), lookAt: function () { var obj; return function lookAt( vector ) { if ( obj === undefined ) obj = new Object3D(); obj.lookAt( vector ); obj.updateMatrix(); this.applyMatrix( obj.matrix ); }; }(), fromBufferGeometry: function ( geometry ) { var scope = this; var indices = geometry.index !== null ? geometry.index.array : undefined; var attributes = geometry.attributes; var positions = attributes.position.array; var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; var colors = attributes.color !== undefined ? attributes.color.array : undefined; var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; var tempNormals = []; var tempUVs = []; var tempUVs2 = []; for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); if ( normals !== undefined ) { tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); } if ( colors !== undefined ) { scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); } if ( uvs !== undefined ) { tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); } if ( uvs2 !== undefined ) { tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); } } function addFace( a, b, c, materialIndex ) { var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); scope.faces.push( face ); if ( uvs !== undefined ) { scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); } if ( uvs2 !== undefined ) { scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); } } if ( indices !== undefined ) { var groups = geometry.groups; if ( groups.length > 0 ) { for ( var i = 0; i < groups.length; i ++ ) { var group = groups[ i ]; var start = group.start; var count = group.count; for ( var j = start, jl = start + count; j < jl; j += 3 ) { addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); } } } else { for ( var i = 0; i < indices.length; i += 3 ) { addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); } } } else { for ( var i = 0; i < positions.length / 3; i += 3 ) { addFace( i, i + 1, i + 2 ); } } this.computeFaceNormals(); if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } return this; }, center: function () { this.computeBoundingBox(); var offset = this.boundingBox.getCenter().negate(); this.translate( offset.x, offset.y, offset.z ); return offset; }, normalize: function () { this.computeBoundingSphere(); var center = this.boundingSphere.center; var radius = this.boundingSphere.radius; var s = radius === 0 ? 1 : 1.0 / radius; var matrix = new Matrix4(); matrix.set( s, 0, 0, - s * center.x, 0, s, 0, - s * center.y, 0, 0, s, - s * center.z, 0, 0, 0, 1 ); this.applyMatrix( matrix ); return this; }, computeFaceNormals: function () { var cb = new Vector3(), ab = new Vector3(); for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { var face = this.faces[ f ]; var vA = this.vertices[ face.a ]; var vB = this.vertices[ face.b ]; var vC = this.vertices[ face.c ]; cb.subVectors( vC, vB ); ab.subVectors( vA, vB ); cb.cross( ab ); cb.normalize(); face.normal.copy( cb ); } }, computeVertexNormals: function ( areaWeighted ) { if ( areaWeighted === undefined ) areaWeighted = true; var v, vl, f, fl, face, vertices; vertices = new Array( this.vertices.length ); for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { vertices[ v ] = new Vector3(); } if ( areaWeighted ) { // vertex normals weighted by triangle areas // http://www.iquilezles.org/www/articles/normals/normals.htm var vA, vB, vC; var cb = new Vector3(), ab = new Vector3(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; vA = this.vertices[ face.a ]; vB = this.vertices[ face.b ]; vC = this.vertices[ face.c ]; cb.subVectors( vC, vB ); ab.subVectors( vA, vB ); cb.cross( ab ); vertices[ face.a ].add( cb ); vertices[ face.b ].add( cb ); vertices[ face.c ].add( cb ); } } else { this.computeFaceNormals(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; vertices[ face.a ].add( face.normal ); vertices[ face.b ].add( face.normal ); vertices[ face.c ].add( face.normal ); } } for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { vertices[ v ].normalize(); } for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { vertexNormals[ 0 ].copy( vertices[ face.a ] ); vertexNormals[ 1 ].copy( vertices[ face.b ] ); vertexNormals[ 2 ].copy( vertices[ face.c ] ); } else { vertexNormals[ 0 ] = vertices[ face.a ].clone(); vertexNormals[ 1 ] = vertices[ face.b ].clone(); vertexNormals[ 2 ] = vertices[ face.c ].clone(); } } if ( this.faces.length > 0 ) { this.normalsNeedUpdate = true; } }, computeFlatVertexNormals: function () { var f, fl, face; this.computeFaceNormals(); for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { vertexNormals[ 0 ].copy( face.normal ); vertexNormals[ 1 ].copy( face.normal ); vertexNormals[ 2 ].copy( face.normal ); } else { vertexNormals[ 0 ] = face.normal.clone(); vertexNormals[ 1 ] = face.normal.clone(); vertexNormals[ 2 ] = face.normal.clone(); } } if ( this.faces.length > 0 ) { this.normalsNeedUpdate = true; } }, computeMorphNormals: function () { var i, il, f, fl, face; // save original normals // - create temp variables on first access // otherwise just copy (for faster repeated calls) for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; if ( ! face.__originalFaceNormal ) { face.__originalFaceNormal = face.normal.clone(); } else { face.__originalFaceNormal.copy( face.normal ); } if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { if ( ! face.__originalVertexNormals[ i ] ) { face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); } else { face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); } } } // use temp geometry to compute face and vertex normals for each morph var tmpGeo = new Geometry(); tmpGeo.faces = this.faces; for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { // create on first access if ( ! this.morphNormals[ i ] ) { this.morphNormals[ i ] = {}; this.morphNormals[ i ].faceNormals = []; this.morphNormals[ i ].vertexNormals = []; var dstNormalsFace = this.morphNormals[ i ].faceNormals; var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; var faceNormal, vertexNormals; for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { faceNormal = new Vector3(); vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; dstNormalsFace.push( faceNormal ); dstNormalsVertex.push( vertexNormals ); } } var morphNormals = this.morphNormals[ i ]; // set vertices to morph target tmpGeo.vertices = this.morphTargets[ i ].vertices; // compute morph normals tmpGeo.computeFaceNormals(); tmpGeo.computeVertexNormals(); // store morph normals var faceNormal, vertexNormals; for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; faceNormal = morphNormals.faceNormals[ f ]; vertexNormals = morphNormals.vertexNormals[ f ]; faceNormal.copy( face.normal ); vertexNormals.a.copy( face.vertexNormals[ 0 ] ); vertexNormals.b.copy( face.vertexNormals[ 1 ] ); vertexNormals.c.copy( face.vertexNormals[ 2 ] ); } } // restore original normals for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { face = this.faces[ f ]; face.normal = face.__originalFaceNormal; face.vertexNormals = face.__originalVertexNormals; } }, computeLineDistances: function () { var d = 0; var vertices = this.vertices; for ( var i = 0, il = vertices.length; i < il; i ++ ) { if ( i > 0 ) { d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); } this.lineDistances[ i ] = d; } }, computeBoundingBox: function () { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } this.boundingBox.setFromPoints( this.vertices ); }, computeBoundingSphere: function () { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } this.boundingSphere.setFromPoints( this.vertices ); }, merge: function ( geometry, matrix, materialIndexOffset ) { if ( (geometry && geometry.isGeometry) === false ) { console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); return; } var normalMatrix, vertexOffset = this.vertices.length, vertices1 = this.vertices, vertices2 = geometry.vertices, faces1 = this.faces, faces2 = geometry.faces, uvs1 = this.faceVertexUvs[ 0 ], uvs2 = geometry.faceVertexUvs[ 0 ], colors1 = this.colors, colors2 = geometry.colors; if ( materialIndexOffset === undefined ) materialIndexOffset = 0; if ( matrix !== undefined ) { normalMatrix = new Matrix3().getNormalMatrix( matrix ); } // vertices for ( var i = 0, il = vertices2.length; i < il; i ++ ) { var vertex = vertices2[ i ]; var vertexCopy = vertex.clone(); if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); vertices1.push( vertexCopy ); } // colors for ( var i = 0, il = colors2.length; i < il; i ++ ) { colors1.push( colors2[ i ].clone() ); } // faces for ( i = 0, il = faces2.length; i < il; i ++ ) { var face = faces2[ i ], faceCopy, normal, color, faceVertexNormals = face.vertexNormals, faceVertexColors = face.vertexColors; faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); faceCopy.normal.copy( face.normal ); if ( normalMatrix !== undefined ) { faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); } for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { normal = faceVertexNormals[ j ].clone(); if ( normalMatrix !== undefined ) { normal.applyMatrix3( normalMatrix ).normalize(); } faceCopy.vertexNormals.push( normal ); } faceCopy.color.copy( face.color ); for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { color = faceVertexColors[ j ]; faceCopy.vertexColors.push( color.clone() ); } faceCopy.materialIndex = face.materialIndex + materialIndexOffset; faces1.push( faceCopy ); } // uvs for ( i = 0, il = uvs2.length; i < il; i ++ ) { var uv = uvs2[ i ], uvCopy = []; if ( uv === undefined ) { continue; } for ( var j = 0, jl = uv.length; j < jl; j ++ ) { uvCopy.push( uv[ j ].clone() ); } uvs1.push( uvCopy ); } }, mergeMesh: function ( mesh ) { if ( (mesh && mesh.isMesh) === false ) { console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); return; } mesh.matrixAutoUpdate && mesh.updateMatrix(); this.merge( mesh.geometry, mesh.matrix ); }, /* * Checks for duplicate vertices with hashmap. * Duplicated vertices are removed * and faces' vertices are updated. */ mergeVertices: function () { var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) var unique = [], changes = []; var v, key; var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 var precision = Math.pow( 10, precisionPoints ); var i, il, face; var indices, j, jl; for ( i = 0, il = this.vertices.length; i < il; i ++ ) { v = this.vertices[ i ]; key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); if ( verticesMap[ key ] === undefined ) { verticesMap[ key ] = i; unique.push( this.vertices[ i ] ); changes[ i ] = unique.length - 1; } else { //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); changes[ i ] = changes[ verticesMap[ key ] ]; } } // if faces are completely degenerate after merging vertices, we // have to remove them from the geometry. var faceIndicesToRemove = []; for ( i = 0, il = this.faces.length; i < il; i ++ ) { face = this.faces[ i ]; face.a = changes[ face.a ]; face.b = changes[ face.b ]; face.c = changes[ face.c ]; indices = [ face.a, face.b, face.c ]; var dupIndex = - 1; // if any duplicate vertices are found in a Face3 // we have to remove the face as nothing can be saved for ( var n = 0; n < 3; n ++ ) { if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { dupIndex = n; faceIndicesToRemove.push( i ); break; } } } for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { var idx = faceIndicesToRemove[ i ]; this.faces.splice( idx, 1 ); for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { this.faceVertexUvs[ j ].splice( idx, 1 ); } } // Use unique set of vertices var diff = this.vertices.length - unique.length; this.vertices = unique; return diff; }, sortFacesByMaterialIndex: function () { var faces = this.faces; var length = faces.length; // tag faces for ( var i = 0; i < length; i ++ ) { faces[ i ]._id = i; } // sort faces function materialIndexSort( a, b ) { return a.materialIndex - b.materialIndex; } faces.sort( materialIndexSort ); // sort uvs var uvs1 = this.faceVertexUvs[ 0 ]; var uvs2 = this.faceVertexUvs[ 1 ]; var newUvs1, newUvs2; if ( uvs1 && uvs1.length === length ) newUvs1 = []; if ( uvs2 && uvs2.length === length ) newUvs2 = []; for ( var i = 0; i < length; i ++ ) { var id = faces[ i ]._id; if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); } if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; }, toJSON: function () { var data = { metadata: { version: 4.4, type: 'Geometry', generator: 'Geometry.toJSON' } }; // standard Geometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== '' ) data.name = this.name; if ( this.parameters !== undefined ) { var parameters = this.parameters; for ( var key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } var vertices = []; for ( var i = 0; i < this.vertices.length; i ++ ) { var vertex = this.vertices[ i ]; vertices.push( vertex.x, vertex.y, vertex.z ); } var faces = []; var normals = []; var normalsHash = {}; var colors = []; var colorsHash = {}; var uvs = []; var uvsHash = {}; for ( var i = 0; i < this.faces.length; i ++ ) { var face = this.faces[ i ]; var hasMaterial = true; var hasFaceUv = false; // deprecated var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; var hasFaceNormal = face.normal.length() > 0; var hasFaceVertexNormal = face.vertexNormals.length > 0; var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; var hasFaceVertexColor = face.vertexColors.length > 0; var faceType = 0; faceType = setBit( faceType, 0, 0 ); // isQuad faceType = setBit( faceType, 1, hasMaterial ); faceType = setBit( faceType, 2, hasFaceUv ); faceType = setBit( faceType, 3, hasFaceVertexUv ); faceType = setBit( faceType, 4, hasFaceNormal ); faceType = setBit( faceType, 5, hasFaceVertexNormal ); faceType = setBit( faceType, 6, hasFaceColor ); faceType = setBit( faceType, 7, hasFaceVertexColor ); faces.push( faceType ); faces.push( face.a, face.b, face.c ); faces.push( face.materialIndex ); if ( hasFaceVertexUv ) { var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; faces.push( getUvIndex( faceVertexUvs[ 0 ] ), getUvIndex( faceVertexUvs[ 1 ] ), getUvIndex( faceVertexUvs[ 2 ] ) ); } if ( hasFaceNormal ) { faces.push( getNormalIndex( face.normal ) ); } if ( hasFaceVertexNormal ) { var vertexNormals = face.vertexNormals; faces.push( getNormalIndex( vertexNormals[ 0 ] ), getNormalIndex( vertexNormals[ 1 ] ), getNormalIndex( vertexNormals[ 2 ] ) ); } if ( hasFaceColor ) { faces.push( getColorIndex( face.color ) ); } if ( hasFaceVertexColor ) { var vertexColors = face.vertexColors; faces.push( getColorIndex( vertexColors[ 0 ] ), getColorIndex( vertexColors[ 1 ] ), getColorIndex( vertexColors[ 2 ] ) ); } } function setBit( value, position, enabled ) { return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); } function getNormalIndex( normal ) { var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); if ( normalsHash[ hash ] !== undefined ) { return normalsHash[ hash ]; } normalsHash[ hash ] = normals.length / 3; normals.push( normal.x, normal.y, normal.z ); return normalsHash[ hash ]; } function getColorIndex( color ) { var hash = color.r.toString() + color.g.toString() + color.b.toString(); if ( colorsHash[ hash ] !== undefined ) { return colorsHash[ hash ]; } colorsHash[ hash ] = colors.length; colors.push( color.getHex() ); return colorsHash[ hash ]; } function getUvIndex( uv ) { var hash = uv.x.toString() + uv.y.toString(); if ( uvsHash[ hash ] !== undefined ) { return uvsHash[ hash ]; } uvsHash[ hash ] = uvs.length / 2; uvs.push( uv.x, uv.y ); return uvsHash[ hash ]; } data.data = {}; data.data.vertices = vertices; data.data.normals = normals; if ( colors.length > 0 ) data.data.colors = colors; if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility data.data.faces = faces; return data; }, clone: function () { /* // Handle primitives var parameters = this.parameters; if ( parameters !== undefined ) { var values = []; for ( var key in parameters ) { values.push( parameters[ key ] ); } var geometry = Object.create( this.constructor.prototype ); this.constructor.apply( geometry, values ); return geometry; } return new this.constructor().copy( this ); */ return new Geometry().copy( this ); }, copy: function ( source ) { this.vertices = []; this.faces = []; this.faceVertexUvs = [ [] ]; this.colors = []; var vertices = source.vertices; for ( var i = 0, il = vertices.length; i < il; i ++ ) { this.vertices.push( vertices[ i ].clone() ); } var colors = source.colors; for ( var i = 0, il = colors.length; i < il; i ++ ) { this.colors.push( colors[ i ].clone() ); } var faces = source.faces; for ( var i = 0, il = faces.length; i < il; i ++ ) { this.faces.push( faces[ i ].clone() ); } for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { var faceVertexUvs = source.faceVertexUvs[ i ]; if ( this.faceVertexUvs[ i ] === undefined ) { this.faceVertexUvs[ i ] = []; } for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { var uvs = faceVertexUvs[ j ], uvsCopy = []; for ( var k = 0, kl = uvs.length; k < kl; k ++ ) { var uv = uvs[ k ]; uvsCopy.push( uv.clone() ); } this.faceVertexUvs[ i ].push( uvsCopy ); } } return this; }, dispose: function () { this.dispatchEvent( { type: 'dispose' } ); } } ); var count = 0; function GeometryIdCount() { return count++; } /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function BufferGeometry() { Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); this.uuid = _Math.generateUUID(); this.name = ''; this.type = 'BufferGeometry'; this.index = null; this.attributes = {}; this.morphAttributes = {}; this.groups = []; this.boundingBox = null; this.boundingSphere = null; this.drawRange = { start: 0, count: Infinity }; } Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, { isBufferGeometry: true, getIndex: function () { return this.index; }, setIndex: function ( index ) { this.index = index; }, addAttribute: function ( name, attribute ) { if ( ( attribute && attribute.isBufferAttribute ) === false && ( attribute && attribute.isInterleavedBufferAttribute ) === false ) { console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); return; } if ( name === 'index' ) { console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); this.setIndex( attribute ); return; } this.attributes[ name ] = attribute; return this; }, getAttribute: function ( name ) { return this.attributes[ name ]; }, removeAttribute: function ( name ) { delete this.attributes[ name ]; return this; }, addGroup: function ( start, count, materialIndex ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex !== undefined ? materialIndex : 0 } ); }, clearGroups: function () { this.groups = []; }, setDrawRange: function ( start, count ) { this.drawRange.start = start; this.drawRange.count = count; }, applyMatrix: function ( matrix ) { var position = this.attributes.position; if ( position !== undefined ) { matrix.applyToVector3Array( position.array ); position.needsUpdate = true; } var normal = this.attributes.normal; if ( normal !== undefined ) { var normalMatrix = new Matrix3().getNormalMatrix( matrix ); normalMatrix.applyToVector3Array( normal.array ); normal.needsUpdate = true; } if ( this.boundingBox !== null ) { this.computeBoundingBox(); } if ( this.boundingSphere !== null ) { this.computeBoundingSphere(); } return this; }, rotateX: function () { // rotate geometry around world x-axis var m1; return function rotateX( angle ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeRotationX( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateY: function () { // rotate geometry around world y-axis var m1; return function rotateY( angle ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeRotationY( angle ); this.applyMatrix( m1 ); return this; }; }(), rotateZ: function () { // rotate geometry around world z-axis var m1; return function rotateZ( angle ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeRotationZ( angle ); this.applyMatrix( m1 ); return this; }; }(), translate: function () { // translate geometry var m1; return function translate( x, y, z ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeTranslation( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), scale: function () { // scale geometry var m1; return function scale( x, y, z ) { if ( m1 === undefined ) m1 = new Matrix4(); m1.makeScale( x, y, z ); this.applyMatrix( m1 ); return this; }; }(), lookAt: function () { var obj; return function lookAt( vector ) { if ( obj === undefined ) obj = new Object3D(); obj.lookAt( vector ); obj.updateMatrix(); this.applyMatrix( obj.matrix ); }; }(), center: function () { this.computeBoundingBox(); var offset = this.boundingBox.getCenter().negate(); this.translate( offset.x, offset.y, offset.z ); return offset; }, setFromObject: function ( object ) { // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); var geometry = object.geometry; if ( object.isPoints || object.isLine ) { var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); } if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } } else if ( object.isMesh ) { if ( geometry && geometry.isGeometry ) { this.fromGeometry( geometry ); } } return this; }, updateFromObject: function ( object ) { var geometry = object.geometry; if ( object.isMesh ) { var direct = geometry.__directGeometry; if ( geometry.elementsNeedUpdate === true ) { direct = undefined; geometry.elementsNeedUpdate = false; } if ( direct === undefined ) { return this.fromGeometry( geometry ); } direct.verticesNeedUpdate = geometry.verticesNeedUpdate; direct.normalsNeedUpdate = geometry.normalsNeedUpdate; direct.colorsNeedUpdate = geometry.colorsNeedUpdate; direct.uvsNeedUpdate = geometry.uvsNeedUpdate; direct.groupsNeedUpdate = geometry.groupsNeedUpdate; geometry.verticesNeedUpdate = false; geometry.normalsNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.uvsNeedUpdate = false; geometry.groupsNeedUpdate = false; geometry = direct; } var attribute; if ( geometry.verticesNeedUpdate === true ) { attribute = this.attributes.position; if ( attribute !== undefined ) { attribute.copyVector3sArray( geometry.vertices ); attribute.needsUpdate = true; } geometry.verticesNeedUpdate = false; } if ( geometry.normalsNeedUpdate === true ) { attribute = this.attributes.normal; if ( attribute !== undefined ) { attribute.copyVector3sArray( geometry.normals ); attribute.needsUpdate = true; } geometry.normalsNeedUpdate = false; } if ( geometry.colorsNeedUpdate === true ) { attribute = this.attributes.color; if ( attribute !== undefined ) { attribute.copyColorsArray( geometry.colors ); attribute.needsUpdate = true; } geometry.colorsNeedUpdate = false; } if ( geometry.uvsNeedUpdate ) { attribute = this.attributes.uv; if ( attribute !== undefined ) { attribute.copyVector2sArray( geometry.uvs ); attribute.needsUpdate = true; } geometry.uvsNeedUpdate = false; } if ( geometry.lineDistancesNeedUpdate ) { attribute = this.attributes.lineDistance; if ( attribute !== undefined ) { attribute.copyArray( geometry.lineDistances ); attribute.needsUpdate = true; } geometry.lineDistancesNeedUpdate = false; } if ( geometry.groupsNeedUpdate ) { geometry.computeGroups( object.geometry ); this.groups = geometry.groups; geometry.groupsNeedUpdate = false; } return this; }, fromGeometry: function ( geometry ) { geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); return this.fromDirectGeometry( geometry.__directGeometry ); }, fromDirectGeometry: function ( geometry ) { var positions = new Float32Array( geometry.vertices.length * 3 ); this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); if ( geometry.normals.length > 0 ) { var normals = new Float32Array( geometry.normals.length * 3 ); this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); } if ( geometry.colors.length > 0 ) { var colors = new Float32Array( geometry.colors.length * 3 ); this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); } if ( geometry.uvs.length > 0 ) { var uvs = new Float32Array( geometry.uvs.length * 2 ); this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); } if ( geometry.uvs2.length > 0 ) { var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); } if ( geometry.indices.length > 0 ) { var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array; var indices = new TypeArray( geometry.indices.length * 3 ); this.setIndex( new BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); } // groups this.groups = geometry.groups; // morphs for ( var name in geometry.morphTargets ) { var array = []; var morphTargets = geometry.morphTargets[ name ]; for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { var morphTarget = morphTargets[ i ]; var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 ); array.push( attribute.copyVector3sArray( morphTarget ) ); } this.morphAttributes[ name ] = array; } // skinning if ( geometry.skinIndices.length > 0 ) { var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); } if ( geometry.skinWeights.length > 0 ) { var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); } // if ( geometry.boundingSphere !== null ) { this.boundingSphere = geometry.boundingSphere.clone(); } if ( geometry.boundingBox !== null ) { this.boundingBox = geometry.boundingBox.clone(); } return this; }, computeBoundingBox: function () { if ( this.boundingBox === null ) { this.boundingBox = new Box3(); } var position = this.attributes.position; if ( position !== undefined ) { this.boundingBox.setFromBufferAttribute( position ); } else { this.boundingBox.makeEmpty(); } if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); } }, computeBoundingSphere: function () { var box = new Box3(); var vector = new Vector3(); return function computeBoundingSphere() { if ( this.boundingSphere === null ) { this.boundingSphere = new Sphere(); } var position = this.attributes.position; if ( position ) { var center = this.boundingSphere.center; box.setFromBufferAttribute( position ); box.getCenter( center ); // hoping to find a boundingSphere with a radius smaller than the // boundingSphere of the boundingBox: sqrt(3) smaller in the best case var maxRadiusSq = 0; for ( var i = 0, il = position.count; i < il; i ++ ) { vector.x = position.getX( i ); vector.y = position.getY( i ); vector.z = position.getZ( i ); maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); } this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); if ( isNaN( this.boundingSphere.radius ) ) { console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); } } }; }(), computeFaceNormals: function () { // backwards compatibility }, computeVertexNormals: function () { var index = this.index; var attributes = this.attributes; var groups = this.groups; if ( attributes.position ) { var positions = attributes.position.array; if ( attributes.normal === undefined ) { this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) ); } else { // reset existing normals to zero var array = attributes.normal.array; for ( var i = 0, il = array.length; i < il; i ++ ) { array[ i ] = 0; } } var normals = attributes.normal.array; var vA, vB, vC; var pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); var cb = new Vector3(), ab = new Vector3(); // indexed elements if ( index ) { var indices = index.array; if ( groups.length === 0 ) { this.addGroup( 0, indices.length ); } for ( var j = 0, jl = groups.length; j < jl; ++ j ) { var group = groups[ j ]; var start = group.start; var count = group.count; for ( var i = start, il = start + count; i < il; i += 3 ) { vA = indices[ i + 0 ] * 3; vB = indices[ i + 1 ] * 3; vC = indices[ i + 2 ] * 3; pA.fromArray( positions, vA ); pB.fromArray( positions, vB ); pC.fromArray( positions, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normals[ vA ] += cb.x; normals[ vA + 1 ] += cb.y; normals[ vA + 2 ] += cb.z; normals[ vB ] += cb.x; normals[ vB + 1 ] += cb.y; normals[ vB + 2 ] += cb.z; normals[ vC ] += cb.x; normals[ vC + 1 ] += cb.y; normals[ vC + 2 ] += cb.z; } } } else { // non-indexed elements (unconnected triangle soup) for ( var i = 0, il = positions.length; i < il; i += 9 ) { pA.fromArray( positions, i ); pB.fromArray( positions, i + 3 ); pC.fromArray( positions, i + 6 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); normals[ i ] = cb.x; normals[ i + 1 ] = cb.y; normals[ i + 2 ] = cb.z; normals[ i + 3 ] = cb.x; normals[ i + 4 ] = cb.y; normals[ i + 5 ] = cb.z; normals[ i + 6 ] = cb.x; normals[ i + 7 ] = cb.y; normals[ i + 8 ] = cb.z; } } this.normalizeNormals(); attributes.normal.needsUpdate = true; } }, merge: function ( geometry, offset ) { if ( ( geometry && geometry.isBufferGeometry ) === false ) { console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); return; } if ( offset === undefined ) offset = 0; var attributes = this.attributes; for ( var key in attributes ) { if ( geometry.attributes[ key ] === undefined ) continue; var attribute1 = attributes[ key ]; var attributeArray1 = attribute1.array; var attribute2 = geometry.attributes[ key ]; var attributeArray2 = attribute2.array; var attributeSize = attribute2.itemSize; for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { attributeArray1[ j ] = attributeArray2[ i ]; } } return this; }, normalizeNormals: function () { var normals = this.attributes.normal.array; var x, y, z, n; for ( var i = 0, il = normals.length; i < il; i += 3 ) { x = normals[ i ]; y = normals[ i + 1 ]; z = normals[ i + 2 ]; n = 1.0 / Math.sqrt( x * x + y * y + z * z ); normals[ i ] *= n; normals[ i + 1 ] *= n; normals[ i + 2 ] *= n; } }, toNonIndexed: function () { if ( this.index === null ) { console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); return this; } var geometry2 = new BufferGeometry(); var indices = this.index.array; var attributes = this.attributes; for ( var name in attributes ) { var attribute = attributes[ name ]; var array = attribute.array; var itemSize = attribute.itemSize; var array2 = new array.constructor( indices.length * itemSize ); var index = 0, index2 = 0; for ( var i = 0, l = indices.length; i < l; i ++ ) { index = indices[ i ] * itemSize; for ( var j = 0; j < itemSize; j ++ ) { array2[ index2 ++ ] = array[ index ++ ]; } } geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); } return geometry2; }, toJSON: function () { var data = { metadata: { version: 4.4, type: 'BufferGeometry', generator: 'BufferGeometry.toJSON' } }; // standard BufferGeometry serialization data.uuid = this.uuid; data.type = this.type; if ( this.name !== '' ) data.name = this.name; if ( this.parameters !== undefined ) { var parameters = this.parameters; for ( var key in parameters ) { if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } return data; } data.data = { attributes: {} }; var index = this.index; if ( index !== null ) { var array = Array.prototype.slice.call( index.array ); data.data.index = { type: index.array.constructor.name, array: array }; } var attributes = this.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; var array = Array.prototype.slice.call( attribute.array ); data.data.attributes[ key ] = { itemSize: attribute.itemSize, type: attribute.array.constructor.name, array: array, normalized: attribute.normalized }; } var groups = this.groups; if ( groups.length > 0 ) { data.data.groups = JSON.parse( JSON.stringify( groups ) ); } var boundingSphere = this.boundingSphere; if ( boundingSphere !== null ) { data.data.boundingSphere = { center: boundingSphere.center.toArray(), radius: boundingSphere.radius }; } return data; }, clone: function () { /* // Handle primitives var parameters = this.parameters; if ( parameters !== undefined ) { var values = []; for ( var key in parameters ) { values.push( parameters[ key ] ); } var geometry = Object.create( this.constructor.prototype ); this.constructor.apply( geometry, values ); return geometry; } return new this.constructor().copy( this ); */ return new BufferGeometry().copy( this ); }, copy: function ( source ) { var index = source.index; if ( index !== null ) { this.setIndex( index.clone() ); } var attributes = source.attributes; for ( var name in attributes ) { var attribute = attributes[ name ]; this.addAttribute( name, attribute.clone() ); } var groups = source.groups; for ( var i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } return this; }, dispose: function () { this.dispatchEvent( { type: 'dispose' } ); } } ); BufferGeometry.MaxIndex = 65535; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author jonobr1 / http://jonobr1.com/ */ function Mesh( geometry, material ) { Object3D.call( this ); this.type = 'Mesh'; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); this.drawMode = TrianglesDrawMode; this.updateMorphTargets(); } Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Mesh, isMesh: true, setDrawMode: function ( value ) { this.drawMode = value; }, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.drawMode = source.drawMode; return this; }, updateMorphTargets: function () { var morphTargets = this.geometry.morphTargets; if ( morphTargets !== undefined && morphTargets.length > 0 ) { this.morphTargetInfluences = []; this.morphTargetDictionary = {}; for ( var m = 0, ml = morphTargets.length; m < ml; m ++ ) { this.morphTargetInfluences.push( 0 ); this.morphTargetDictionary[ morphTargets[ m ].name ] = m; } } }, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); var vA = new Vector3(); var vB = new Vector3(); var vC = new Vector3(); var tempA = new Vector3(); var tempB = new Vector3(); var tempC = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); var barycoord = new Vector3(); var intersectionPoint = new Vector3(); var intersectionPointWorld = new Vector3(); function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); uv1.multiplyScalar( barycoord.x ); uv2.multiplyScalar( barycoord.y ); uv3.multiplyScalar( barycoord.z ); uv1.add( uv2 ).add( uv3 ); return uv1.clone(); } function checkIntersection( object, raycaster, ray, pA, pB, pC, point ) { var intersect; var material = object.material; if ( material.side === BackSide ) { intersect = ray.intersectTriangle( pC, pB, pA, true, point ); } else { intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); } if ( intersect === null ) return null; intersectionPointWorld.copy( point ); intersectionPointWorld.applyMatrix4( object.matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); if ( distance < raycaster.near || distance > raycaster.far ) return null; return { distance: distance, point: intersectionPointWorld.clone(), object: object }; } function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) { vA.fromArray( positions, a * 3 ); vB.fromArray( positions, b * 3 ); vC.fromArray( positions, c * 3 ); var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint ); if ( intersection ) { if ( uvs ) { uvA.fromArray( uvs, a * 2 ); uvB.fromArray( uvs, b * 2 ); uvC.fromArray( uvs, c * 2 ); intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); } intersection.face = new Face3( a, b, c, Triangle.normal( vA, vB, vC ) ); intersection.faceIndex = a; } return intersection; } return function raycast( raycaster, intersects ) { var geometry = this.geometry; var material = this.material; var matrixWorld = this.matrixWorld; if ( material === undefined ) return; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); // Check boundingBox before continuing if ( geometry.boundingBox !== null ) { if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; } var uvs, intersection; if ( geometry.isBufferGeometry ) { var a, b, c; var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( attributes.uv !== undefined ) { uvs = attributes.uv.array; } if ( index !== null ) { var indices = index.array; for ( var i = 0, l = indices.length; i < l; i += 3 ) { a = indices[ i ]; b = indices[ i + 1 ]; c = indices[ i + 2 ]; intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); if ( intersection ) { intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics intersects.push( intersection ); } } } else { for ( var i = 0, l = positions.length; i < l; i += 9 ) { a = i / 3; b = a + 1; c = a + 2; intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); if ( intersection ) { intersection.index = a; // triangle number in positions buffer semantics intersects.push( intersection ); } } } } else if ( geometry.isGeometry ) { var fvA, fvB, fvC; var isFaceMaterial = (material && material.isMultiMaterial); var materials = isFaceMaterial === true ? material.materials : null; var vertices = geometry.vertices; var faces = geometry.faces; var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; for ( var f = 0, fl = faces.length; f < fl; f ++ ) { var face = faces[ f ]; var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; if ( faceMaterial === undefined ) continue; fvA = vertices[ face.a ]; fvB = vertices[ face.b ]; fvC = vertices[ face.c ]; if ( faceMaterial.morphTargets === true ) { var morphTargets = geometry.morphTargets; var morphInfluences = this.morphTargetInfluences; vA.set( 0, 0, 0 ); vB.set( 0, 0, 0 ); vC.set( 0, 0, 0 ); for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { var influence = morphInfluences[ t ]; if ( influence === 0 ) continue; var targets = morphTargets[ t ].vertices; vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); } vA.add( fvA ); vB.add( fvB ); vC.add( fvC ); fvA = vA; fvB = vB; fvC = vC; } intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); if ( intersection ) { if ( uvs ) { var uvs_f = uvs[ f ]; uvA.copy( uvs_f[ 0 ] ); uvB.copy( uvs_f[ 1 ] ); uvC.copy( uvs_f[ 2 ] ); intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); } intersection.face = face; intersection.faceIndex = f; intersects.push( intersection ); } } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author Mugen87 / https://github.com/Mugen87 */ function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { BufferGeometry.call( this ); this.type = 'BoxBufferGeometry'; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; var scope = this; // segments widthSegments = Math.floor( widthSegments ) || 1; heightSegments = Math.floor( heightSegments ) || 1; depthSegments = Math.floor( depthSegments ) || 1; // these are used to calculate buffer length var vertexCount = calculateVertexCount( widthSegments, heightSegments, depthSegments ); var indexCount = calculateIndexCount( widthSegments, heightSegments, depthSegments ); // buffers var indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ); var vertices = new Float32Array( vertexCount * 3 ); var normals = new Float32Array( vertexCount * 3 ); var uvs = new Float32Array( vertexCount * 2 ); // offset variables var vertexBufferOffset = 0; var uvBufferOffset = 0; var indexBufferOffset = 0; var numberOfVertices = 0; // group variables var groupStart = 0; // build each side of the box geometry buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz // build geometry this.setIndex( new BufferAttribute( indices, 1 ) ); this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); // helper functions function calculateVertexCount( w, h, d ) { var vertices = 0; // calculate the amount of vertices for each side (plane) vertices += (w + 1) * (h + 1) * 2; // xy vertices += (w + 1) * (d + 1) * 2; // xz vertices += (d + 1) * (h + 1) * 2; // zy return vertices; } function calculateIndexCount( w, h, d ) { var index = 0; // calculate the amount of squares for each side index += w * h * 2; // xy index += w * d * 2; // xz index += d * h * 2; // zy return index * 6; // two triangles per square => six vertices per square } function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { var segmentWidth = width / gridX; var segmentHeight = height / gridY; var widthHalf = width / 2; var heightHalf = height / 2; var depthHalf = depth / 2; var gridX1 = gridX + 1; var gridY1 = gridY + 1; var vertexCounter = 0; var groupCount = 0; var vector = new Vector3(); // generate vertices, normals and uvs for ( var iy = 0; iy < gridY1; iy ++ ) { var y = iy * segmentHeight - heightHalf; for ( var ix = 0; ix < gridX1; ix ++ ) { var x = ix * segmentWidth - widthHalf; // set values to correct vector component vector[ u ] = x * udir; vector[ v ] = y * vdir; vector[ w ] = depthHalf; // now apply vector to vertex buffer vertices[ vertexBufferOffset ] = vector.x; vertices[ vertexBufferOffset + 1 ] = vector.y; vertices[ vertexBufferOffset + 2 ] = vector.z; // set values to correct vector component vector[ u ] = 0; vector[ v ] = 0; vector[ w ] = depth > 0 ? 1 : - 1; // now apply vector to normal buffer normals[ vertexBufferOffset ] = vector.x; normals[ vertexBufferOffset + 1 ] = vector.y; normals[ vertexBufferOffset + 2 ] = vector.z; // uvs uvs[ uvBufferOffset ] = ix / gridX; uvs[ uvBufferOffset + 1 ] = 1 - ( iy / gridY ); // update offsets and counters vertexBufferOffset += 3; uvBufferOffset += 2; vertexCounter += 1; } } // 1. you need three indices to draw a single face // 2. a single segment consists of two faces // 3. so we need to generate six (2*3) indices per segment for ( iy = 0; iy < gridY; iy ++ ) { for ( ix = 0; ix < gridX; ix ++ ) { // indices var a = numberOfVertices + ix + gridX1 * iy; var b = numberOfVertices + ix + gridX1 * ( iy + 1 ); var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; // face one indices[ indexBufferOffset ] = a; indices[ indexBufferOffset + 1 ] = b; indices[ indexBufferOffset + 2 ] = d; // face two indices[ indexBufferOffset + 3 ] = b; indices[ indexBufferOffset + 4 ] = c; indices[ indexBufferOffset + 5 ] = d; // update offsets and counters indexBufferOffset += 6; groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, materialIndex ); // calculate new start value for groups groupStart += groupCount; // update total number of vertices numberOfVertices += vertexCounter; } } BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as */ function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { BufferGeometry.call( this ); this.type = 'PlaneBufferGeometry'; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; var width_half = width / 2; var height_half = height / 2; var gridX = Math.floor( widthSegments ) || 1; var gridY = Math.floor( heightSegments ) || 1; var gridX1 = gridX + 1; var gridY1 = gridY + 1; var segment_width = width / gridX; var segment_height = height / gridY; var vertices = new Float32Array( gridX1 * gridY1 * 3 ); var normals = new Float32Array( gridX1 * gridY1 * 3 ); var uvs = new Float32Array( gridX1 * gridY1 * 2 ); var offset = 0; var offset2 = 0; for ( var iy = 0; iy < gridY1; iy ++ ) { var y = iy * segment_height - height_half; for ( var ix = 0; ix < gridX1; ix ++ ) { var x = ix * segment_width - width_half; vertices[ offset ] = x; vertices[ offset + 1 ] = - y; normals[ offset + 2 ] = 1; uvs[ offset2 ] = ix / gridX; uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); offset += 3; offset2 += 2; } } offset = 0; var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 ); for ( var iy = 0; iy < gridY; iy ++ ) { for ( var ix = 0; ix < gridX; ix ++ ) { var a = ix + gridX1 * iy; var b = ix + gridX1 * ( iy + 1 ); var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); var d = ( ix + 1 ) + gridX1 * iy; indices[ offset ] = a; indices[ offset + 1 ] = b; indices[ offset + 2 ] = d; indices[ offset + 3 ] = b; indices[ offset + 4 ] = c; indices[ offset + 5 ] = d; offset += 6; } } this.setIndex( new BufferAttribute( indices, 1 ) ); this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); } PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ * @author WestLangley / http://github.com/WestLangley */ function Camera() { Object3D.call( this ); this.type = 'Camera'; this.matrixWorldInverse = new Matrix4(); this.projectionMatrix = new Matrix4(); } Camera.prototype = Object.create( Object3D.prototype ); Camera.prototype.constructor = Camera; Camera.prototype.isCamera = true; Camera.prototype.getWorldDirection = function () { var quaternion = new Quaternion(); return function getWorldDirection( optionalTarget ) { var result = optionalTarget || new Vector3(); this.getWorldQuaternion( quaternion ); return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); }; }(); Camera.prototype.lookAt = function () { // This routine does not support cameras with rotated and/or translated parent(s) var m1 = new Matrix4(); return function lookAt( vector ) { m1.lookAt( this.position, vector, this.up ); this.quaternion.setFromRotationMatrix( m1 ); }; }(); Camera.prototype.clone = function () { return new this.constructor().copy( this ); }; Camera.prototype.copy = function ( source ) { Object3D.prototype.copy.call( this, source ); this.matrixWorldInverse.copy( source.matrixWorldInverse ); this.projectionMatrix.copy( source.projectionMatrix ); return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author greggman / http://games.greggman.com/ * @author zz85 / http://www.lab4games.net/zz85/blog * @author tschw */ function PerspectiveCamera( fov, aspect, near, far ) { Camera.call( this ); this.type = 'PerspectiveCamera'; this.fov = fov !== undefined ? fov : 50; this.zoom = 1; this.near = near !== undefined ? near : 0.1; this.far = far !== undefined ? far : 2000; this.focus = 10; this.aspect = aspect !== undefined ? aspect : 1; this.view = null; this.filmGauge = 35; // width of the film (default in millimeters) this.filmOffset = 0; // horizontal film offset (same unit as gauge) this.updateProjectionMatrix(); } PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: PerspectiveCamera, isPerspectiveCamera: true, copy: function ( source ) { Camera.prototype.copy.call( this, source ); this.fov = source.fov; this.zoom = source.zoom; this.near = source.near; this.far = source.far; this.focus = source.focus; this.aspect = source.aspect; this.view = source.view === null ? null : Object.assign( {}, source.view ); this.filmGauge = source.filmGauge; this.filmOffset = source.filmOffset; return this; }, /** * Sets the FOV by focal length in respect to the current .filmGauge. * * The default film gauge is 35, so that the focal length can be specified for * a 35mm (full frame) camera. * * Values for focal length and film gauge must have the same unit. */ setFocalLength: function ( focalLength ) { // see http://www.bobatkins.com/photography/technical/field_of_view.html var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); this.updateProjectionMatrix(); }, /** * Calculates the focal length from the current .fov and .filmGauge. */ getFocalLength: function () { var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov ); return 0.5 * this.getFilmHeight() / vExtentSlope; }, getEffectiveFOV: function () { return _Math.RAD2DEG * 2 * Math.atan( Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); }, getFilmWidth: function () { // film not completely covered in portrait format (aspect < 1) return this.filmGauge * Math.min( this.aspect, 1 ); }, getFilmHeight: function () { // film not completely covered in landscape format (aspect > 1) return this.filmGauge / Math.max( this.aspect, 1 ); }, /** * Sets an offset in a larger frustum. This is useful for multi-window or * multi-monitor/multi-machine setups. * * For example, if you have 3x2 monitors and each monitor is 1920x1080 and * the monitors are in grid like this * * +---+---+---+ * | A | B | C | * +---+---+---+ * | D | E | F | * +---+---+---+ * * then for each monitor you would call it like this * * var w = 1920; * var h = 1080; * var fullWidth = w * 3; * var fullHeight = h * 2; * * --A-- * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); * --B-- * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); * --C-- * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); * --D-- * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); * --E-- * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); * --F-- * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); * * Note there is no reason monitors have to be the same size or in a grid. */ setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { this.aspect = fullWidth / fullHeight; this.view = { fullWidth: fullWidth, fullHeight: fullHeight, offsetX: x, offsetY: y, width: width, height: height }; this.updateProjectionMatrix(); }, clearViewOffset: function() { this.view = null; this.updateProjectionMatrix(); }, updateProjectionMatrix: function () { var near = this.near, top = near * Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, height = 2 * top, width = this.aspect * height, left = - 0.5 * width, view = this.view; if ( view !== null ) { var fullWidth = view.fullWidth, fullHeight = view.fullHeight; left += view.offsetX * width / fullWidth; top -= view.offsetY * height / fullHeight; width *= view.width / fullWidth; height *= view.height / fullHeight; } var skew = this.filmOffset; if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); this.projectionMatrix.makeFrustum( left, left + width, top - height, top, near, this.far ); }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.fov = this.fov; data.object.zoom = this.zoom; data.object.near = this.near; data.object.far = this.far; data.object.focus = this.focus; data.object.aspect = this.aspect; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); data.object.filmGauge = this.filmGauge; data.object.filmOffset = this.filmOffset; return data; } } ); /** * @author alteredq / http://alteredqualia.com/ * @author arose / http://github.com/arose */ function OrthographicCamera( left, right, top, bottom, near, far ) { Camera.call( this ); this.type = 'OrthographicCamera'; this.zoom = 1; this.view = null; this.left = left; this.right = right; this.top = top; this.bottom = bottom; this.near = ( near !== undefined ) ? near : 0.1; this.far = ( far !== undefined ) ? far : 2000; this.updateProjectionMatrix(); } OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { constructor: OrthographicCamera, isOrthographicCamera: true, copy: function ( source ) { Camera.prototype.copy.call( this, source ); this.left = source.left; this.right = source.right; this.top = source.top; this.bottom = source.bottom; this.near = source.near; this.far = source.far; this.zoom = source.zoom; this.view = source.view === null ? null : Object.assign( {}, source.view ); return this; }, setViewOffset: function( fullWidth, fullHeight, x, y, width, height ) { this.view = { fullWidth: fullWidth, fullHeight: fullHeight, offsetX: x, offsetY: y, width: width, height: height }; this.updateProjectionMatrix(); }, clearViewOffset: function() { this.view = null; this.updateProjectionMatrix(); }, updateProjectionMatrix: function () { var dx = ( this.right - this.left ) / ( 2 * this.zoom ); var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); var cx = ( this.right + this.left ) / 2; var cy = ( this.top + this.bottom ) / 2; var left = cx - dx; var right = cx + dx; var top = cy + dy; var bottom = cy - dy; if ( this.view !== null ) { var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); var scaleW = ( this.right - this.left ) / this.view.width; var scaleH = ( this.top - this.bottom ) / this.view.height; left += scaleW * ( this.view.offsetX / zoomW ); right = left + scaleW * ( this.view.width / zoomW ); top -= scaleH * ( this.view.offsetY / zoomH ); bottom = top - scaleH * ( this.view.height / zoomH ); } this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.zoom = this.zoom; data.object.left = this.left; data.object.right = this.right; data.object.top = this.top; data.object.bottom = this.bottom; data.object.near = this.near; data.object.far = this.far; if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); return data; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function WebGLIndexedBufferRenderer( gl, extensions, infoRender ) { var mode; function setMode( value ) { mode = value; } var type, size; function setIndex( index ) { if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { type = gl.UNSIGNED_INT; size = 4; } else if ( index.array instanceof Uint16Array ) { type = gl.UNSIGNED_SHORT; size = 2; } else { type = gl.UNSIGNED_BYTE; size = 1; } } function render( start, count ) { gl.drawElements( mode, count, type, start * size ); infoRender.calls ++; infoRender.vertices += count; if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; } function renderInstances( geometry, start, count ) { var extension = extensions.get( 'ANGLE_instanced_arrays' ); if ( extension === null ) { console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); return; } extension.drawElementsInstancedANGLE( mode, count, type, start * size, geometry.maxInstancedCount ); infoRender.calls ++; infoRender.vertices += count * geometry.maxInstancedCount; if ( mode === gl.TRIANGLES ) infoRender.faces += geometry.maxInstancedCount * count / 3; } return { setMode: setMode, setIndex: setIndex, render: render, renderInstances: renderInstances }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLBufferRenderer( gl, extensions, infoRender ) { var mode; function setMode( value ) { mode = value; } function render( start, count ) { gl.drawArrays( mode, start, count ); infoRender.calls ++; infoRender.vertices += count; if ( mode === gl.TRIANGLES ) infoRender.faces += count / 3; } function renderInstances( geometry ) { var extension = extensions.get( 'ANGLE_instanced_arrays' ); if ( extension === null ) { console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); return; } var position = geometry.attributes.position; var count = 0; if ( position.isInterleavedBufferAttribute ) { count = position.data.count; extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); } else { count = position.count; extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); } infoRender.calls ++; infoRender.vertices += count * geometry.maxInstancedCount; if ( mode === gl.TRIANGLES ) infoRender.faces += geometry.maxInstancedCount * count / 3; } return { setMode: setMode, render: render, renderInstances: renderInstances }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLLights() { var lights = {}; return { get: function ( light ) { if ( lights[ light.id ] !== undefined ) { return lights[ light.id ]; } var uniforms; switch ( light.type ) { case 'DirectionalLight': uniforms = { direction: new Vector3(), color: new Color(), shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case 'SpotLight': uniforms = { position: new Vector3(), direction: new Vector3(), color: new Color(), distance: 0, coneCos: 0, penumbraCos: 0, decay: 0, shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case 'PointLight': uniforms = { position: new Vector3(), color: new Color(), distance: 0, decay: 0, shadow: false, shadowBias: 0, shadowRadius: 1, shadowMapSize: new Vector2() }; break; case 'HemisphereLight': uniforms = { direction: new Vector3(), skyColor: new Color(), groundColor: new Color() }; break; case 'RectAreaLight': uniforms = { color: new Color(), position: new Vector3(), halfWidth: new Vector3(), halfHeight: new Vector3() // TODO (abelnation): set RectAreaLight shadow uniforms }; break; } lights[ light.id ] = uniforms; return uniforms; } }; } /** * @author mrdoob / http://mrdoob.com/ */ function addLineNumbers( string ) { var lines = string.split( '\n' ); for ( var i = 0; i < lines.length; i ++ ) { lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; } return lines.join( '\n' ); } function WebGLShader( gl, type, string ) { var shader = gl.createShader( type ); gl.shaderSource( shader, string ); gl.compileShader( shader ); if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); } if ( gl.getShaderInfoLog( shader ) !== '' ) { console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); } // --enable-privileged-webgl-extension // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); return shader; } /** * @author mrdoob / http://mrdoob.com/ */ var programIdCount = 0; function getEncodingComponents( encoding ) { switch ( encoding ) { case LinearEncoding: return [ 'Linear','( value )' ]; case sRGBEncoding: return [ 'sRGB','( value )' ]; case RGBEEncoding: return [ 'RGBE','( value )' ]; case RGBM7Encoding: return [ 'RGBM','( value, 7.0 )' ]; case RGBM16Encoding: return [ 'RGBM','( value, 16.0 )' ]; case RGBDEncoding: return [ 'RGBD','( value, 256.0 )' ]; case GammaEncoding: return [ 'Gamma','( value, float( GAMMA_FACTOR ) )' ]; default: throw new Error( 'unsupported encoding: ' + encoding ); } } function getTexelDecodingFunction( functionName, encoding ) { var components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return " + components[ 0 ] + "ToLinear" + components[ 1 ] + "; }"; } function getTexelEncodingFunction( functionName, encoding ) { var components = getEncodingComponents( encoding ); return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; } function getToneMappingFunction( functionName, toneMapping ) { var toneMappingName; switch ( toneMapping ) { case LinearToneMapping: toneMappingName = "Linear"; break; case ReinhardToneMapping: toneMappingName = "Reinhard"; break; case Uncharted2ToneMapping: toneMappingName = "Uncharted2"; break; case CineonToneMapping: toneMappingName = "OptimizedCineon"; break; default: throw new Error( 'unsupported toneMapping: ' + toneMapping ); } return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; } function generateExtensions( extensions, parameters, rendererExtensions ) { extensions = extensions || {}; var chunks = [ ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '', ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '' ]; return chunks.filter( filterEmptyLine ).join( '\n' ); } function generateDefines( defines ) { var chunks = []; for ( var name in defines ) { var value = defines[ name ]; if ( value === false ) continue; chunks.push( '#define ' + name + ' ' + value ); } return chunks.join( '\n' ); } function fetchAttributeLocations( gl, program, identifiers ) { var attributes = {}; var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); for ( var i = 0; i < n; i ++ ) { var info = gl.getActiveAttrib( program, i ); var name = info.name; // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); attributes[ name ] = gl.getAttribLocation( program, name ); } return attributes; } function filterEmptyLine( string ) { return string !== ''; } function replaceLightNums( string, parameters ) { return string .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); } function parseIncludes( string ) { var pattern = /#include +<([\w\d.]+)>/g; function replace( match, include ) { var replace = ShaderChunk[ include ]; if ( replace === undefined ) { throw new Error( 'Can not resolve #include <' + include + '>' ); } return parseIncludes( replace ); } return string.replace( pattern, replace ); } function unrollLoops( string ) { var pattern = /for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; function replace( match, start, end, snippet ) { var unroll = ''; for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { unroll += snippet.replace( /\[ i \]/g, '[ ' + i + ' ]' ); } return unroll; } return string.replace( pattern, replace ); } function WebGLProgram( renderer, code, material, parameters ) { var gl = renderer.context; var extensions = material.extensions; var defines = material.defines; var vertexShader = material.__webglShader.vertexShader; var fragmentShader = material.__webglShader.fragmentShader; var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; if ( parameters.shadowMapType === PCFShadowMap ) { shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; } var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; if ( parameters.envMap ) { switch ( material.envMap.mapping ) { case CubeReflectionMapping: case CubeRefractionMapping: envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; break; case CubeUVReflectionMapping: case CubeUVRefractionMapping: envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; break; case EquirectangularReflectionMapping: case EquirectangularRefractionMapping: envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; break; case SphericalReflectionMapping: envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; break; } switch ( material.envMap.mapping ) { case CubeRefractionMapping: case EquirectangularRefractionMapping: envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; break; } switch ( material.combine ) { case MultiplyOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; break; case MixOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; break; case AddOperation: envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; break; } } var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; // console.log( 'building new program ' ); // var customExtensions = generateExtensions( extensions, parameters, renderer.extensions ); var customDefines = generateDefines( defines ); // var program = gl.createProgram(); var prefixVertex, prefixFragment; if ( material.isRawShaderMaterial ) { prefixVertex = [ customDefines, '\n' ].filter( filterEmptyLine ).join( '\n' ); prefixFragment = [ customExtensions, customDefines, '\n' ].filter( filterEmptyLine ).join( '\n' ); } else { prefixVertex = [ 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', '#define SHADER_NAME ' + material.__webglShader.name, customDefines, parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', '#define GAMMA_FACTOR ' + gammaFactorDefine, '#define MAX_BONES ' + parameters.maxBones, parameters.map ? '#define USE_MAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.vertexColors ? '#define USE_COLOR' : '', parameters.flatShading ? '#define FLAT_SHADED' : '', parameters.skinning ? '#define USE_SKINNING' : '', parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', '#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes, parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', 'uniform mat4 modelMatrix;', 'uniform mat4 modelViewMatrix;', 'uniform mat4 projectionMatrix;', 'uniform mat4 viewMatrix;', 'uniform mat3 normalMatrix;', 'uniform vec3 cameraPosition;', 'attribute vec3 position;', 'attribute vec3 normal;', 'attribute vec2 uv;', '#ifdef USE_COLOR', ' attribute vec3 color;', '#endif', '#ifdef USE_MORPHTARGETS', ' attribute vec3 morphTarget0;', ' attribute vec3 morphTarget1;', ' attribute vec3 morphTarget2;', ' attribute vec3 morphTarget3;', ' #ifdef USE_MORPHNORMALS', ' attribute vec3 morphNormal0;', ' attribute vec3 morphNormal1;', ' attribute vec3 morphNormal2;', ' attribute vec3 morphNormal3;', ' #else', ' attribute vec3 morphTarget4;', ' attribute vec3 morphTarget5;', ' attribute vec3 morphTarget6;', ' attribute vec3 morphTarget7;', ' #endif', '#endif', '#ifdef USE_SKINNING', ' attribute vec4 skinIndex;', ' attribute vec4 skinWeight;', '#endif', '\n' ].filter( filterEmptyLine ).join( '\n' ); prefixFragment = [ customExtensions, 'precision ' + parameters.precision + ' float;', 'precision ' + parameters.precision + ' int;', '#define SHADER_NAME ' + material.__webglShader.name, customDefines, parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', '#define GAMMA_FACTOR ' + gammaFactorDefine, ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', parameters.map ? '#define USE_MAP' : '', parameters.envMap ? '#define USE_ENVMAP' : '', parameters.envMap ? '#define ' + envMapTypeDefine : '', parameters.envMap ? '#define ' + envMapModeDefine : '', parameters.envMap ? '#define ' + envMapBlendingDefine : '', parameters.lightMap ? '#define USE_LIGHTMAP' : '', parameters.aoMap ? '#define USE_AOMAP' : '', parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', parameters.bumpMap ? '#define USE_BUMPMAP' : '', parameters.normalMap ? '#define USE_NORMALMAP' : '', parameters.specularMap ? '#define USE_SPECULARMAP' : '', parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', parameters.alphaMap ? '#define USE_ALPHAMAP' : '', parameters.vertexColors ? '#define USE_COLOR' : '', parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', parameters.flatShading ? '#define FLAT_SHADED' : '', parameters.doubleSided ? '#define DOUBLE_SIDED' : '', parameters.flipSided ? '#define FLIP_SIDED' : '', '#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes, '#define UNION_CLIPPING_PLANES ' + (parameters.numClippingPlanes - parameters.numClipIntersection), parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : '', parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : '', parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', parameters.envMap && renderer.extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '', 'uniform mat4 viewMatrix;', 'uniform vec3 cameraPosition;', ( parameters.toneMapping !== NoToneMapping ) ? "#define TONE_MAPPING" : '', ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : '', ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', parameters.outputEncoding ? getTexelEncodingFunction( "linearToOutputTexel", parameters.outputEncoding ) : '', parameters.depthPacking ? "#define DEPTH_PACKING " + material.depthPacking : '', '\n' ].filter( filterEmptyLine ).join( '\n' ); } vertexShader = parseIncludes( vertexShader, parameters ); vertexShader = replaceLightNums( vertexShader, parameters ); fragmentShader = parseIncludes( fragmentShader, parameters ); fragmentShader = replaceLightNums( fragmentShader, parameters ); if ( ! material.isShaderMaterial ) { vertexShader = unrollLoops( vertexShader ); fragmentShader = unrollLoops( fragmentShader ); } var vertexGlsl = prefixVertex + vertexShader; var fragmentGlsl = prefixFragment + fragmentShader; // console.log( '*VERTEX*', vertexGlsl ); // console.log( '*FRAGMENT*', fragmentGlsl ); var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); gl.attachShader( program, glVertexShader ); gl.attachShader( program, glFragmentShader ); // Force a particular attribute to index 0. if ( material.index0AttributeName !== undefined ) { gl.bindAttribLocation( program, 0, material.index0AttributeName ); } else if ( parameters.morphTargets === true ) { // programs with morphTargets displace position out of attribute 0 gl.bindAttribLocation( program, 0, 'position' ); } gl.linkProgram( program ); var programLog = gl.getProgramInfoLog( program ); var vertexLog = gl.getShaderInfoLog( glVertexShader ); var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); var runnable = true; var haveDiagnostics = true; // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { runnable = false; console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); } else if ( programLog !== '' ) { console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); } else if ( vertexLog === '' || fragmentLog === '' ) { haveDiagnostics = false; } if ( haveDiagnostics ) { this.diagnostics = { runnable: runnable, material: material, programLog: programLog, vertexShader: { log: vertexLog, prefix: prefixVertex }, fragmentShader: { log: fragmentLog, prefix: prefixFragment } }; } // clean up gl.deleteShader( glVertexShader ); gl.deleteShader( glFragmentShader ); // set up caching for uniform locations var cachedUniforms; this.getUniforms = function() { if ( cachedUniforms === undefined ) { cachedUniforms = new WebGLUniforms( gl, program, renderer ); } return cachedUniforms; }; // set up caching for attribute locations var cachedAttributes; this.getAttributes = function() { if ( cachedAttributes === undefined ) { cachedAttributes = fetchAttributeLocations( gl, program ); } return cachedAttributes; }; // free resource this.destroy = function() { gl.deleteProgram( program ); this.program = undefined; }; // DEPRECATED Object.defineProperties( this, { uniforms: { get: function() { console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); return this.getUniforms(); } }, attributes: { get: function() { console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); return this.getAttributes(); } } } ); // this.id = programIdCount ++; this.code = code; this.usedTimes = 1; this.program = program; this.vertexShader = glVertexShader; this.fragmentShader = glFragmentShader; return this; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLPrograms( renderer, capabilities ) { var programs = []; var shaderIDs = { MeshDepthMaterial: 'depth', MeshNormalMaterial: 'normal', MeshBasicMaterial: 'basic', MeshLambertMaterial: 'lambert', MeshPhongMaterial: 'phong', MeshToonMaterial: 'phong', MeshStandardMaterial: 'physical', MeshPhysicalMaterial: 'physical', LineBasicMaterial: 'basic', LineDashedMaterial: 'dashed', PointsMaterial: 'points' }; var parameterNames = [ "precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding", "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "gradientMap", "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", "maxBones", "useVertexTexture", "morphTargets", "morphNormals", "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking" ]; function allocateBones( object ) { if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE's 254 max uniform vectors // (up to 54 should be safe) var nVertexUniforms = capabilities.maxVertexUniforms; var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); var maxBones = nVertexMatrices; if ( object !== undefined && (object && object.isSkinnedMesh) ) { maxBones = Math.min( object.skeleton.bones.length, maxBones ); if ( maxBones < object.skeleton.bones.length ) { console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); } } return maxBones; } } function getTextureEncodingFromMap( map, gammaOverrideLinear ) { var encoding; if ( ! map ) { encoding = LinearEncoding; } else if ( map.isTexture ) { encoding = map.encoding; } else if ( map.isWebGLRenderTarget ) { console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); encoding = map.texture.encoding; } // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. if ( encoding === LinearEncoding && gammaOverrideLinear ) { encoding = GammaEncoding; } return encoding; } this.getParameters = function ( material, lights, fog, nClipPlanes, nClipIntersection, object ) { var shaderID = shaderIDs[ material.type ]; // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) var maxBones = allocateBones( object ); var precision = renderer.getPrecision(); if ( material.precision !== null ) { precision = capabilities.getMaxPrecision( material.precision ); if ( precision !== material.precision ) { console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); } } var currentRenderTarget = renderer.getCurrentRenderTarget(); var parameters = { shaderID: shaderID, precision: precision, supportsVertexTextures: capabilities.vertexTextures, outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), map: !! material.map, mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ), lightMap: !! material.lightMap, aoMap: !! material.aoMap, emissiveMap: !! material.emissiveMap, emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, displacementMap: !! material.displacementMap, roughnessMap: !! material.roughnessMap, metalnessMap: !! material.metalnessMap, specularMap: !! material.specularMap, alphaMap: !! material.alphaMap, gradientMap: !! material.gradientMap, combine: material.combine, vertexColors: material.vertexColors, fog: !! fog, useFog: material.fog, fogExp: (fog && fog.isFogExp2), flatShading: material.shading === FlatShading, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, skinning: material.skinning, maxBones: maxBones, useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture, morphTargets: material.morphTargets, morphNormals: material.morphNormals, maxMorphTargets: renderer.maxMorphTargets, maxMorphNormals: renderer.maxMorphNormals, numDirLights: lights.directional.length, numPointLights: lights.point.length, numSpotLights: lights.spot.length, numRectAreaLights: lights.rectArea.length, numHemiLights: lights.hemi.length, numClippingPlanes: nClipPlanes, numClipIntersection: nClipIntersection, shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0, shadowMapType: renderer.shadowMap.type, toneMapping: renderer.toneMapping, physicallyCorrectLights: renderer.physicallyCorrectLights, premultipliedAlpha: material.premultipliedAlpha, alphaTest: material.alphaTest, doubleSided: material.side === DoubleSide, flipSided: material.side === BackSide, depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false }; return parameters; }; this.getProgramCode = function ( material, parameters ) { var array = []; if ( parameters.shaderID ) { array.push( parameters.shaderID ); } else { array.push( material.fragmentShader ); array.push( material.vertexShader ); } if ( material.defines !== undefined ) { for ( var name in material.defines ) { array.push( name ); array.push( material.defines[ name ] ); } } for ( var i = 0; i < parameterNames.length; i ++ ) { array.push( parameters[ parameterNames[ i ] ] ); } return array.join(); }; this.acquireProgram = function ( material, parameters, code ) { var program; // Check if code has been already compiled for ( var p = 0, pl = programs.length; p < pl; p ++ ) { var programInfo = programs[ p ]; if ( programInfo.code === code ) { program = programInfo; ++ program.usedTimes; break; } } if ( program === undefined ) { program = new WebGLProgram( renderer, code, material, parameters ); programs.push( program ); } return program; }; this.releaseProgram = function( program ) { if ( -- program.usedTimes === 0 ) { // Remove from unordered set var i = programs.indexOf( program ); programs[ i ] = programs[ programs.length - 1 ]; programs.pop(); // Free WebGL resources program.destroy(); } }; // Exposed for resource monitoring & error feedback via renderer.info: this.programs = programs; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLGeometries( gl, properties, info ) { var geometries = {}; function onGeometryDispose( event ) { var geometry = event.target; var buffergeometry = geometries[ geometry.id ]; if ( buffergeometry.index !== null ) { deleteAttribute( buffergeometry.index ); } deleteAttributes( buffergeometry.attributes ); geometry.removeEventListener( 'dispose', onGeometryDispose ); delete geometries[ geometry.id ]; // TODO var property = properties.get( geometry ); if ( property.wireframe ) { deleteAttribute( property.wireframe ); } properties.delete( geometry ); var bufferproperty = properties.get( buffergeometry ); if ( bufferproperty.wireframe ) { deleteAttribute( bufferproperty.wireframe ); } properties.delete( buffergeometry ); // info.memory.geometries --; } function getAttributeBuffer( attribute ) { if ( attribute.isInterleavedBufferAttribute ) { return properties.get( attribute.data ).__webglBuffer; } return properties.get( attribute ).__webglBuffer; } function deleteAttribute( attribute ) { var buffer = getAttributeBuffer( attribute ); if ( buffer !== undefined ) { gl.deleteBuffer( buffer ); removeAttributeBuffer( attribute ); } } function deleteAttributes( attributes ) { for ( var name in attributes ) { deleteAttribute( attributes[ name ] ); } } function removeAttributeBuffer( attribute ) { if ( attribute.isInterleavedBufferAttribute ) { properties.delete( attribute.data ); } else { properties.delete( attribute ); } } return { get: function ( object ) { var geometry = object.geometry; if ( geometries[ geometry.id ] !== undefined ) { return geometries[ geometry.id ]; } geometry.addEventListener( 'dispose', onGeometryDispose ); var buffergeometry; if ( geometry.isBufferGeometry ) { buffergeometry = geometry; } else if ( geometry.isGeometry ) { if ( geometry._bufferGeometry === undefined ) { geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); } buffergeometry = geometry._bufferGeometry; } geometries[ geometry.id ] = buffergeometry; info.memory.geometries ++; return buffergeometry; } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLObjects( gl, properties, info ) { var geometries = new WebGLGeometries( gl, properties, info ); // function update( object ) { // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter. var geometry = geometries.get( object ); if ( object.geometry.isGeometry ) { geometry.updateFromObject( object ); } var index = geometry.index; var attributes = geometry.attributes; if ( index !== null ) { updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); } for ( var name in attributes ) { updateAttribute( attributes[ name ], gl.ARRAY_BUFFER ); } // morph targets var morphAttributes = geometry.morphAttributes; for ( var name in morphAttributes ) { var array = morphAttributes[ name ]; for ( var i = 0, l = array.length; i < l; i ++ ) { updateAttribute( array[ i ], gl.ARRAY_BUFFER ); } } return geometry; } function updateAttribute( attribute, bufferType ) { var data = ( attribute.isInterleavedBufferAttribute ) ? attribute.data : attribute; var attributeProperties = properties.get( data ); if ( attributeProperties.__webglBuffer === undefined ) { createBuffer( attributeProperties, data, bufferType ); } else if ( attributeProperties.version !== data.version ) { updateBuffer( attributeProperties, data, bufferType ); } } function createBuffer( attributeProperties, data, bufferType ) { attributeProperties.__webglBuffer = gl.createBuffer(); gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; gl.bufferData( bufferType, data.array, usage ); var type = gl.FLOAT; var array = data.array; if ( array instanceof Float32Array ) { type = gl.FLOAT; } else if ( array instanceof Float64Array ) { console.warn( "Unsupported data buffer format: Float64Array" ); } else if ( array instanceof Uint16Array ) { type = gl.UNSIGNED_SHORT; } else if ( array instanceof Int16Array ) { type = gl.SHORT; } else if ( array instanceof Uint32Array ) { type = gl.UNSIGNED_INT; } else if ( array instanceof Int32Array ) { type = gl.INT; } else if ( array instanceof Int8Array ) { type = gl.BYTE; } else if ( array instanceof Uint8Array ) { type = gl.UNSIGNED_BYTE; } attributeProperties.bytesPerElement = array.BYTES_PER_ELEMENT; attributeProperties.type = type; attributeProperties.version = data.version; data.onUploadCallback(); } function updateBuffer( attributeProperties, data, bufferType ) { gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); if ( data.dynamic === false ) { gl.bufferData( bufferType, data.array, gl.STATIC_DRAW ); } else if ( data.updateRange.count === - 1 ) { // Not using update ranges gl.bufferSubData( bufferType, 0, data.array ); } else if ( data.updateRange.count === 0 ) { console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); } else { gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); data.updateRange.count = 0; // reset range } attributeProperties.version = data.version; } function getAttributeBuffer( attribute ) { if ( attribute.isInterleavedBufferAttribute ) { return properties.get( attribute.data ).__webglBuffer; } return properties.get( attribute ).__webglBuffer; } function getAttributeProperties( attribute ) { if ( attribute.isInterleavedBufferAttribute ) { return properties.get( attribute.data ); } return properties.get( attribute ); } function getWireframeAttribute( geometry ) { var property = properties.get( geometry ); if ( property.wireframe !== undefined ) { return property.wireframe; } var indices = []; var index = geometry.index; var attributes = geometry.attributes; var position = attributes.position; // console.time( 'wireframe' ); if ( index !== null ) { var edges = {}; var array = index.array; for ( var i = 0, l = array.length; i < l; i += 3 ) { var a = array[ i + 0 ]; var b = array[ i + 1 ]; var c = array[ i + 2 ]; indices.push( a, b, b, c, c, a ); } } else { var array = attributes.position.array; for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { var a = i + 0; var b = i + 1; var c = i + 2; indices.push( a, b, b, c, c, a ); } } // console.timeEnd( 'wireframe' ); var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array; var attribute = new BufferAttribute( new TypeArray( indices ), 1 ); updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER ); property.wireframe = attribute; return attribute; } return { getAttributeBuffer: getAttributeBuffer, getAttributeProperties: getAttributeProperties, getWireframeAttribute: getWireframeAttribute, update: update }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLTextures( _gl, extensions, state, properties, capabilities, paramThreeToGL, info ) { var _infoMemory = info.memory; var _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext ); // function clampToMaxSize( image, maxSize ) { if ( image.width > maxSize || image.height > maxSize ) { // Warning: Scaling through the canvas will only work with images that use // premultiplied alpha. var scale = maxSize / Math.max( image.width, image.height ); var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); canvas.width = Math.floor( image.width * scale ); canvas.height = Math.floor( image.height * scale ); var context = canvas.getContext( '2d' ); context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); return canvas; } return image; } function isPowerOfTwo( image ) { return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); } function makePowerOfTwo( image ) { if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) { var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); canvas.width = _Math.nearestPowerOfTwo( image.width ); canvas.height = _Math.nearestPowerOfTwo( image.height ); var context = canvas.getContext( '2d' ); context.drawImage( image, 0, 0, canvas.width, canvas.height ); console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); return canvas; } return image; } function textureNeedsPowerOfTwo( texture ) { return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); } // Fallback filters for non-power-of-2 textures function filterFallback( f ) { if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) { return _gl.NEAREST; } return _gl.LINEAR; } // function onTextureDispose( event ) { var texture = event.target; texture.removeEventListener( 'dispose', onTextureDispose ); deallocateTexture( texture ); _infoMemory.textures --; } function onRenderTargetDispose( event ) { var renderTarget = event.target; renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); _infoMemory.textures --; } // function deallocateTexture( texture ) { var textureProperties = properties.get( texture ); if ( texture.image && textureProperties.__image__webglTextureCube ) { // cube texture _gl.deleteTexture( textureProperties.__image__webglTextureCube ); } else { // 2D texture if ( textureProperties.__webglInit === undefined ) return; _gl.deleteTexture( textureProperties.__webglTexture ); } // remove all webgl properties properties.delete( texture ); } function deallocateRenderTarget( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var textureProperties = properties.get( renderTarget.texture ); if ( ! renderTarget ) return; if ( textureProperties.__webglTexture !== undefined ) { _gl.deleteTexture( textureProperties.__webglTexture ); } if ( renderTarget.depthTexture ) { renderTarget.depthTexture.dispose(); } if ( renderTarget.isWebGLRenderTargetCube ) { for ( var i = 0; i < 6; i ++ ) { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); } } else { _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); } properties.delete( renderTarget.texture ); properties.delete( renderTarget ); } // function setTexture2D( texture, slot ) { var textureProperties = properties.get( texture ); if ( texture.version > 0 && textureProperties.__version !== texture.version ) { var image = texture.image; if ( image === undefined ) { console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); } else if ( image.complete === false ) { console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture ); } else { uploadTexture( textureProperties, texture, slot ); return; } } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); } function setTextureCube( texture, slot ) { var textureProperties = properties.get( texture ); if ( texture.image.length === 6 ) { if ( texture.version > 0 && textureProperties.__version !== texture.version ) { if ( ! textureProperties.__image__webglTextureCube ) { texture.addEventListener( 'dispose', onTextureDispose ); textureProperties.__image__webglTextureCube = _gl.createTexture(); _infoMemory.textures ++; } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); var isCompressed = ( texture && texture.isCompressedTexture ); var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); var cubeImage = []; for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } } var image = cubeImage[ 0 ], isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = paramThreeToGL( texture.format ), glType = paramThreeToGL( texture.type ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed ) { if ( isDataTexture ) { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); } } else { var mipmap, mipmaps = cubeImage[ i ].mipmaps; for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { mipmap = mipmaps[ j ]; if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); } } else { state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } if ( texture.generateMipmaps && isPowerOfTwoImage ) { _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); } textureProperties.__version = texture.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } else { state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); } } } function setTextureCubeDynamic( texture, slot ) { state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); } function setTextureParameters( textureType, texture, isPowerOfTwoImage ) { var extension; if ( isPowerOfTwoImage ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); } else { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture ); } } extension = extensions.get( 'EXT_texture_filter_anisotropic' ); if ( extension ) { if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; if ( texture.type === HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); properties.get( texture ).__currentAnisotropy = texture.anisotropy; } } } function uploadTexture( textureProperties, texture, slot ) { if ( textureProperties.__webglInit === undefined ) { textureProperties.__webglInit = true; texture.addEventListener( 'dispose', onTextureDispose ); textureProperties.__webglTexture = _gl.createTexture(); _infoMemory.textures ++; } state.activeTexture( _gl.TEXTURE0 + slot ); state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { image = makePowerOfTwo( image ); } var isPowerOfTwoImage = isPowerOfTwo( image ), glFormat = paramThreeToGL( texture.format ), glType = paramThreeToGL( texture.type ); setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); var mipmap, mipmaps = texture.mipmaps; if ( texture.isDepthTexture ) { // populate depth texture with dummy data var internalFormat = _gl.DEPTH_COMPONENT; if ( texture.type === FloatType ) { if ( !_isWebGL2 ) throw new Error('Float Depth Texture only supported in WebGL2.0'); internalFormat = _gl.DEPTH_COMPONENT32F; } else if ( _isWebGL2 ) { // WebGL 2.0 requires signed internalformat for glTexImage2D internalFormat = _gl.DEPTH_COMPONENT16; } if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) { // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); texture.type = UnsignedShortType; glType = paramThreeToGL( texture.type ); } } // Depth stencil textures need the DEPTH_STENCIL internal format // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.format === DepthStencilFormat ) { internalFormat = _gl.DEPTH_STENCIL; // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) if ( texture.type !== UnsignedInt248Type ) { console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); texture.type = UnsignedInt248Type; glType = paramThreeToGL( texture.type ); } } state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null ); } else if ( texture.isDataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } texture.generateMipmaps = false; } else { state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); } } else if ( texture.isCompressedTexture ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); } } else { state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isPowerOfTwoImage ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); } texture.generateMipmaps = false; } else { state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); } } if ( texture.generateMipmaps && isPowerOfTwoImage ) _gl.generateMipmap( _gl.TEXTURE_2D ); textureProperties.__version = texture.version; if ( texture.onUpdate ) texture.onUpdate( texture ); } // Render targets // Setup storage for target texture and bind it to correct framebuffer function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { var glFormat = paramThreeToGL( renderTarget.texture.format ); var glType = paramThreeToGL( renderTarget.texture.type ); state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer function setupRenderBufferStorage( renderbuffer, renderTarget ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { // FIXME: We don't support !depth !stencil _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); } // Setup resources for a Depth Texture for a FBO (needs an extension) function setupDepthTexture( framebuffer, renderTarget ) { var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); if ( isCube ) throw new Error('Depth Texture with cube render targets is not supported!'); _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); if ( !( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { throw new Error('renderTarget.depthTexture must be an instance of THREE.DepthTexture'); } // upload an empty depth texture with framebuffer size if ( !properties.get( renderTarget.depthTexture ).__webglTexture || renderTarget.depthTexture.image.width !== renderTarget.width || renderTarget.depthTexture.image.height !== renderTarget.height ) { renderTarget.depthTexture.image.width = renderTarget.width; renderTarget.depthTexture.image.height = renderTarget.height; renderTarget.depthTexture.needsUpdate = true; } setTexture2D( renderTarget.depthTexture, 0 ); var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; if ( renderTarget.depthTexture.format === DepthFormat ) { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); } else { throw new Error('Unknown depthTexture format') } } // Setup GL resources for a non-texture depth buffer function setupDepthRenderbuffer( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); if ( renderTarget.depthTexture ) { if ( isCube ) throw new Error('target.depthTexture not supported in Cube render targets'); setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); } else { if ( isCube ) { renderTargetProperties.__webglDepthbuffer = []; for ( var i = 0; i < 6; i ++ ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); } } else { _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); } } _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } // Set up GL resources for the render target function setupRenderTarget( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); var textureProperties = properties.get( renderTarget.texture ); renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); textureProperties.__webglTexture = _gl.createTexture(); _infoMemory.textures ++; var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); // Setup framebuffer if ( isCube ) { renderTargetProperties.__webglFramebuffer = []; for ( var i = 0; i < 6; i ++ ) { renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); } } else { renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } // Setup color buffer if ( isCube ) { state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); for ( var i = 0; i < 6; i ++ ) { setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); } if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); } else { state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); state.bindTexture( _gl.TEXTURE_2D, null ); } // Setup depth and stencil buffers if ( renderTarget.depthBuffer ) { setupDepthRenderbuffer( renderTarget ); } } function updateRenderTargetMipmap( renderTarget ) { var texture = renderTarget.texture; if ( texture.generateMipmaps && isPowerOfTwo( renderTarget ) && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { var target = (renderTarget && renderTarget.isWebGLRenderTargetCube) ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; var webglTexture = properties.get( texture ).__webglTexture; state.bindTexture( target, webglTexture ); _gl.generateMipmap( target ); state.bindTexture( target, null ); } } this.setTexture2D = setTexture2D; this.setTextureCube = setTextureCube; this.setTextureCubeDynamic = setTextureCubeDynamic; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; } /** * @author fordacious / fordacious.github.io */ function WebGLProperties() { var properties = {}; return { get: function ( object ) { var uuid = object.uuid; var map = properties[ uuid ]; if ( map === undefined ) { map = {}; properties[ uuid ] = map; } return map; }, delete: function ( object ) { delete properties[ object.uuid ]; }, clear: function () { properties = {}; } }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLState( gl, extensions, paramThreeToGL ) { function ColorBuffer() { var locked = false; var color = new Vector4(); var currentColorMask = null; var currentColorClear = new Vector4(); return { setMask: function ( colorMask ) { if ( currentColorMask !== colorMask && ! locked ) { gl.colorMask( colorMask, colorMask, colorMask, colorMask ); currentColorMask = colorMask; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( r, g, b, a, premultipliedAlpha ) { if ( premultipliedAlpha === true ) { r *= a; g *= a; b *= a; } color.set( r, g, b, a ); if ( currentColorClear.equals( color ) === false ) { gl.clearColor( r, g, b, a ); currentColorClear.copy( color ); } }, reset: function () { locked = false; currentColorMask = null; currentColorClear.set( 0, 0, 0, 1 ); } }; } function DepthBuffer() { var locked = false; var currentDepthMask = null; var currentDepthFunc = null; var currentDepthClear = null; return { setTest: function ( depthTest ) { if ( depthTest ) { enable( gl.DEPTH_TEST ); } else { disable( gl.DEPTH_TEST ); } }, setMask: function ( depthMask ) { if ( currentDepthMask !== depthMask && ! locked ) { gl.depthMask( depthMask ); currentDepthMask = depthMask; } }, setFunc: function ( depthFunc ) { if ( currentDepthFunc !== depthFunc ) { if ( depthFunc ) { switch ( depthFunc ) { case NeverDepth: gl.depthFunc( gl.NEVER ); break; case AlwaysDepth: gl.depthFunc( gl.ALWAYS ); break; case LessDepth: gl.depthFunc( gl.LESS ); break; case LessEqualDepth: gl.depthFunc( gl.LEQUAL ); break; case EqualDepth: gl.depthFunc( gl.EQUAL ); break; case GreaterEqualDepth: gl.depthFunc( gl.GEQUAL ); break; case GreaterDepth: gl.depthFunc( gl.GREATER ); break; case NotEqualDepth: gl.depthFunc( gl.NOTEQUAL ); break; default: gl.depthFunc( gl.LEQUAL ); } } else { gl.depthFunc( gl.LEQUAL ); } currentDepthFunc = depthFunc; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( depth ) { if ( currentDepthClear !== depth ) { gl.clearDepth( depth ); currentDepthClear = depth; } }, reset: function () { locked = false; currentDepthMask = null; currentDepthFunc = null; currentDepthClear = null; } }; } function StencilBuffer() { var locked = false; var currentStencilMask = null; var currentStencilFunc = null; var currentStencilRef = null; var currentStencilFuncMask = null; var currentStencilFail = null; var currentStencilZFail = null; var currentStencilZPass = null; var currentStencilClear = null; return { setTest: function ( stencilTest ) { if ( stencilTest ) { enable( gl.STENCIL_TEST ); } else { disable( gl.STENCIL_TEST ); } }, setMask: function ( stencilMask ) { if ( currentStencilMask !== stencilMask && ! locked ) { gl.stencilMask( stencilMask ); currentStencilMask = stencilMask; } }, setFunc: function ( stencilFunc, stencilRef, stencilMask ) { if ( currentStencilFunc !== stencilFunc || currentStencilRef !== stencilRef || currentStencilFuncMask !== stencilMask ) { gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); currentStencilFunc = stencilFunc; currentStencilRef = stencilRef; currentStencilFuncMask = stencilMask; } }, setOp: function ( stencilFail, stencilZFail, stencilZPass ) { if ( currentStencilFail !== stencilFail || currentStencilZFail !== stencilZFail || currentStencilZPass !== stencilZPass ) { gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); currentStencilFail = stencilFail; currentStencilZFail = stencilZFail; currentStencilZPass = stencilZPass; } }, setLocked: function ( lock ) { locked = lock; }, setClear: function ( stencil ) { if ( currentStencilClear !== stencil ) { gl.clearStencil( stencil ); currentStencilClear = stencil; } }, reset: function () { locked = false; currentStencilMask = null; currentStencilFunc = null; currentStencilRef = null; currentStencilFuncMask = null; currentStencilFail = null; currentStencilZFail = null; currentStencilZPass = null; currentStencilClear = null; } }; } // var colorBuffer = new ColorBuffer(); var depthBuffer = new DepthBuffer(); var stencilBuffer = new StencilBuffer(); var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); var newAttributes = new Uint8Array( maxVertexAttributes ); var enabledAttributes = new Uint8Array( maxVertexAttributes ); var attributeDivisors = new Uint8Array( maxVertexAttributes ); var capabilities = {}; var compressedTextureFormats = null; var currentBlending = null; var currentBlendEquation = null; var currentBlendSrc = null; var currentBlendDst = null; var currentBlendEquationAlpha = null; var currentBlendSrcAlpha = null; var currentBlendDstAlpha = null; var currentPremultipledAlpha = false; var currentFlipSided = null; var currentCullFace = null; var currentLineWidth = null; var currentPolygonOffsetFactor = null; var currentPolygonOffsetUnits = null; var currentScissorTest = null; var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); var version = parseFloat( /^WebGL\ ([0-9])/.exec( gl.getParameter( gl.VERSION ) )[ 1 ] ); var lineWidthAvailable = parseFloat( version ) >= 1.0; var currentTextureSlot = null; var currentBoundTextures = {}; var currentScissor = new Vector4(); var currentViewport = new Vector4(); function createTexture( type, target, count ) { var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. var texture = gl.createTexture(); gl.bindTexture( type, texture ); gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); for ( var i = 0; i < count; i ++ ) { gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } return texture; } var emptyTextures = {}; emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); // function init() { colorBuffer.setClear( 0, 0, 0, 1 ); depthBuffer.setClear( 1 ); stencilBuffer.setClear( 0 ); enable( gl.DEPTH_TEST ); setDepthFunc( LessEqualDepth ); setFlipSided( false ); setCullFace( CullFaceBack ); enable( gl.CULL_FACE ); enable( gl.BLEND ); setBlending( NormalBlending ); } function initAttributes() { for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== 0 ) { var extension = extensions.get( 'ANGLE_instanced_arrays' ); extension.vertexAttribDivisorANGLE( attribute, 0 ); attributeDivisors[ attribute ] = 0; } } function enableAttributeAndDivisor( attribute, meshPerAttribute, extension ) { newAttributes[ attribute ] = 1; if ( enabledAttributes[ attribute ] === 0 ) { gl.enableVertexAttribArray( attribute ); enabledAttributes[ attribute ] = 1; } if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); attributeDivisors[ attribute ] = meshPerAttribute; } } function disableUnusedAttributes() { for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } } function enable( id ) { if ( capabilities[ id ] !== true ) { gl.enable( id ); capabilities[ id ] = true; } } function disable( id ) { if ( capabilities[ id ] !== false ) { gl.disable( id ); capabilities[ id ] = false; } } function getCompressedTextureFormats() { if ( compressedTextureFormats === null ) { compressedTextureFormats = []; if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || extensions.get( 'WEBGL_compressed_texture_s3tc' ) || extensions.get( 'WEBGL_compressed_texture_etc1' ) ) { var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); for ( var i = 0; i < formats.length; i ++ ) { compressedTextureFormats.push( formats[ i ] ); } } } return compressedTextureFormats; } function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { if ( blending !== NoBlending ) { enable( gl.BLEND ); } else { disable( gl.BLEND ); } if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { if ( blending === AdditiveBlending ) { if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); } } else if ( blending === SubtractiveBlending ) { if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); } } else if ( blending === MultiplyBlending ) { if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); } else { gl.blendEquation( gl.FUNC_ADD ); gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); } } else { if ( premultipliedAlpha ) { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); } else { gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); } } currentBlending = blending; currentPremultipledAlpha = premultipliedAlpha; } if ( blending === CustomBlending ) { blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) ); currentBlendEquation = blendEquation; currentBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) ); currentBlendSrc = blendSrc; currentBlendDst = blendDst; currentBlendSrcAlpha = blendSrcAlpha; currentBlendDstAlpha = blendDstAlpha; } } else { currentBlendEquation = null; currentBlendSrc = null; currentBlendDst = null; currentBlendEquationAlpha = null; currentBlendSrcAlpha = null; currentBlendDstAlpha = null; } } // TODO Deprecate function setColorWrite( colorWrite ) { colorBuffer.setMask( colorWrite ); } function setDepthTest( depthTest ) { depthBuffer.setTest( depthTest ); } function setDepthWrite( depthWrite ) { depthBuffer.setMask( depthWrite ); } function setDepthFunc( depthFunc ) { depthBuffer.setFunc( depthFunc ); } function setStencilTest( stencilTest ) { stencilBuffer.setTest( stencilTest ); } function setStencilWrite( stencilWrite ) { stencilBuffer.setMask( stencilWrite ); } function setStencilFunc( stencilFunc, stencilRef, stencilMask ) { stencilBuffer.setFunc( stencilFunc, stencilRef, stencilMask ); } function setStencilOp( stencilFail, stencilZFail, stencilZPass ) { stencilBuffer.setOp( stencilFail, stencilZFail, stencilZPass ); } // function setFlipSided( flipSided ) { if ( currentFlipSided !== flipSided ) { if ( flipSided ) { gl.frontFace( gl.CW ); } else { gl.frontFace( gl.CCW ); } currentFlipSided = flipSided; } } function setCullFace( cullFace ) { if ( cullFace !== CullFaceNone ) { enable( gl.CULL_FACE ); if ( cullFace !== currentCullFace ) { if ( cullFace === CullFaceBack ) { gl.cullFace( gl.BACK ); } else if ( cullFace === CullFaceFront ) { gl.cullFace( gl.FRONT ); } else { gl.cullFace( gl.FRONT_AND_BACK ); } } } else { disable( gl.CULL_FACE ); } currentCullFace = cullFace; } function setLineWidth( width ) { if ( width !== currentLineWidth ) { if ( lineWidthAvailable ) gl.lineWidth( width ); currentLineWidth = width; } } function setPolygonOffset( polygonOffset, factor, units ) { if ( polygonOffset ) { enable( gl.POLYGON_OFFSET_FILL ); if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { gl.polygonOffset( factor, units ); currentPolygonOffsetFactor = factor; currentPolygonOffsetUnits = units; } } else { disable( gl.POLYGON_OFFSET_FILL ); } } function getScissorTest() { return currentScissorTest; } function setScissorTest( scissorTest ) { currentScissorTest = scissorTest; if ( scissorTest ) { enable( gl.SCISSOR_TEST ); } else { disable( gl.SCISSOR_TEST ); } } // texture function activeTexture( webglSlot ) { if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; if ( currentTextureSlot !== webglSlot ) { gl.activeTexture( webglSlot ); currentTextureSlot = webglSlot; } } function bindTexture( webglType, webglTexture ) { if ( currentTextureSlot === null ) { activeTexture(); } var boundTexture = currentBoundTextures[ currentTextureSlot ]; if ( boundTexture === undefined ) { boundTexture = { type: undefined, texture: undefined }; currentBoundTextures[ currentTextureSlot ] = boundTexture; } if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); boundTexture.type = webglType; boundTexture.texture = webglTexture; } } function compressedTexImage2D() { try { gl.compressedTexImage2D.apply( gl, arguments ); } catch ( error ) { console.error( error ); } } function texImage2D() { try { gl.texImage2D.apply( gl, arguments ); } catch ( error ) { console.error( error ); } } // function scissor( scissor ) { if ( currentScissor.equals( scissor ) === false ) { gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); currentScissor.copy( scissor ); } } function viewport( viewport ) { if ( currentViewport.equals( viewport ) === false ) { gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); currentViewport.copy( viewport ); } } // function reset() { for ( var i = 0; i < enabledAttributes.length; i ++ ) { if ( enabledAttributes[ i ] === 1 ) { gl.disableVertexAttribArray( i ); enabledAttributes[ i ] = 0; } } capabilities = {}; compressedTextureFormats = null; currentTextureSlot = null; currentBoundTextures = {}; currentBlending = null; currentFlipSided = null; currentCullFace = null; colorBuffer.reset(); depthBuffer.reset(); stencilBuffer.reset(); } return { buffers: { color: colorBuffer, depth: depthBuffer, stencil: stencilBuffer }, init: init, initAttributes: initAttributes, enableAttribute: enableAttribute, enableAttributeAndDivisor: enableAttributeAndDivisor, disableUnusedAttributes: disableUnusedAttributes, enable: enable, disable: disable, getCompressedTextureFormats: getCompressedTextureFormats, setBlending: setBlending, setColorWrite: setColorWrite, setDepthTest: setDepthTest, setDepthWrite: setDepthWrite, setDepthFunc: setDepthFunc, setStencilTest: setStencilTest, setStencilWrite: setStencilWrite, setStencilFunc: setStencilFunc, setStencilOp: setStencilOp, setFlipSided: setFlipSided, setCullFace: setCullFace, setLineWidth: setLineWidth, setPolygonOffset: setPolygonOffset, getScissorTest: getScissorTest, setScissorTest: setScissorTest, activeTexture: activeTexture, bindTexture: bindTexture, compressedTexImage2D: compressedTexImage2D, texImage2D: texImage2D, scissor: scissor, viewport: viewport, reset: reset }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLCapabilities( gl, extensions, parameters ) { var maxAnisotropy; function getMaxAnisotropy() { if ( maxAnisotropy !== undefined ) return maxAnisotropy; var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); if ( extension !== null ) { maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); } else { maxAnisotropy = 0; } return maxAnisotropy; } function getMaxPrecision( precision ) { if ( precision === 'highp' ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { return 'highp'; } precision = 'mediump'; } if ( precision === 'mediump' ) { if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { return 'mediump'; } } return 'lowp'; } var precision = parameters.precision !== undefined ? parameters.precision : 'highp'; var maxPrecision = getMaxPrecision( precision ); if ( maxPrecision !== precision ) { console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); precision = maxPrecision; } var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true && !! extensions.get( 'EXT_frag_depth' ); var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); var vertexTextures = maxVertexTextures > 0; var floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); var floatVertexTextures = vertexTextures && floatFragmentTextures; return { getMaxAnisotropy: getMaxAnisotropy, getMaxPrecision: getMaxPrecision, precision: precision, logarithmicDepthBuffer: logarithmicDepthBuffer, maxTextures: maxTextures, maxVertexTextures: maxVertexTextures, maxTextureSize: maxTextureSize, maxCubemapSize: maxCubemapSize, maxAttributes: maxAttributes, maxVertexUniforms: maxVertexUniforms, maxVaryings: maxVaryings, maxFragmentUniforms: maxFragmentUniforms, vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, floatVertexTextures: floatVertexTextures }; } /** * @author mrdoob / http://mrdoob.com/ */ function WebGLExtensions( gl ) { var extensions = {}; return { get: function ( name ) { if ( extensions[ name ] !== undefined ) { return extensions[ name ]; } var extension; switch ( name ) { case 'WEBGL_depth_texture': extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); break; case 'EXT_texture_filter_anisotropic': extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); break; case 'WEBGL_compressed_texture_s3tc': extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); break; case 'WEBGL_compressed_texture_pvrtc': extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); break; case 'WEBGL_compressed_texture_etc1': extension = gl.getExtension( 'WEBGL_compressed_texture_etc1' ); break; default: extension = gl.getExtension( name ); } if ( extension === null ) { console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); } extensions[ name ] = extension; return extension; } }; } /** * @author tschw */ function WebGLClipping() { var scope = this, globalState = null, numGlobalPlanes = 0, localClippingEnabled = false, renderingShadows = false, plane = new Plane(), viewNormalMatrix = new Matrix3(), uniform = { value: null, needsUpdate: false }; this.uniform = uniform; this.numPlanes = 0; this.numIntersection = 0; this.init = function( planes, enableLocalClipping, camera ) { var enabled = planes.length !== 0 || enableLocalClipping || // enable state of previous frame - the clipping code has to // run another frame in order to reset the state: numGlobalPlanes !== 0 || localClippingEnabled; localClippingEnabled = enableLocalClipping; globalState = projectPlanes( planes, camera, 0 ); numGlobalPlanes = planes.length; return enabled; }; this.beginShadows = function() { renderingShadows = true; projectPlanes( null ); }; this.endShadows = function() { renderingShadows = false; resetGlobalState(); }; this.setState = function( planes, clipIntersection, clipShadows, camera, cache, fromCache ) { if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { // there's no local clipping if ( renderingShadows ) { // there's no global clipping projectPlanes( null ); } else { resetGlobalState(); } } else { var nGlobal = renderingShadows ? 0 : numGlobalPlanes, lGlobal = nGlobal * 4, dstArray = cache.clippingState || null; uniform.value = dstArray; // ensure unique state dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); for ( var i = 0; i !== lGlobal; ++ i ) { dstArray[ i ] = globalState[ i ]; } cache.clippingState = dstArray; this.numIntersection = clipIntersection ? this.numPlanes : 0; this.numPlanes += nGlobal; } }; function resetGlobalState() { if ( uniform.value !== globalState ) { uniform.value = globalState; uniform.needsUpdate = numGlobalPlanes > 0; } scope.numPlanes = numGlobalPlanes; scope.numIntersection = 0; } function projectPlanes( planes, camera, dstOffset, skipTransform ) { var nPlanes = planes !== null ? planes.length : 0, dstArray = null; if ( nPlanes !== 0 ) { dstArray = uniform.value; if ( skipTransform !== true || dstArray === null ) { var flatSize = dstOffset + nPlanes * 4, viewMatrix = camera.matrixWorldInverse; viewNormalMatrix.getNormalMatrix( viewMatrix ); if ( dstArray === null || dstArray.length < flatSize ) { dstArray = new Float32Array( flatSize ); } for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { plane.copy( planes[ i ] ). applyMatrix4( viewMatrix, viewNormalMatrix ); plane.normal.toArray( dstArray, i4 ); dstArray[ i4 + 3 ] = plane.constant; } } uniform.value = dstArray; uniform.needsUpdate = true; } scope.numPlanes = nPlanes; return dstArray; } } /** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ * @author tschw */ function WebGLRenderer( parameters ) { console.log( 'THREE.WebGLRenderer', REVISION ); parameters = parameters || {}; var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), _context = parameters.context !== undefined ? parameters.context : null, _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false; var lights = []; var opaqueObjects = []; var opaqueObjectsLastIndex = - 1; var transparentObjects = []; var transparentObjectsLastIndex = - 1; var morphInfluences = new Float32Array( 8 ); var sprites = []; var lensFlares = []; // public properties this.domElement = _canvas; this.context = null; // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; // user-defined clipping this.clippingPlanes = []; this.localClippingEnabled = false; // physically based shading this.gammaFactor = 2.0; // for backwards compatibility this.gammaInput = false; this.gammaOutput = false; // physical lights this.physicallyCorrectLights = false; // tone mapping this.toneMapping = LinearToneMapping; this.toneMappingExposure = 1.0; this.toneMappingWhitePoint = 1.0; // morphs this.maxMorphTargets = 8; this.maxMorphNormals = 4; // internal properties var _this = this, // internal state cache _currentProgram = null, _currentRenderTarget = null, _currentFramebuffer = null, _currentMaterialId = - 1, _currentGeometryProgram = '', _currentCamera = null, _currentScissor = new Vector4(), _currentScissorTest = null, _currentViewport = new Vector4(), // _usedTextureUnits = 0, // _clearColor = new Color( 0x000000 ), _clearAlpha = 0, _width = _canvas.width, _height = _canvas.height, _pixelRatio = 1, _scissor = new Vector4( 0, 0, _width, _height ), _scissorTest = false, _viewport = new Vector4( 0, 0, _width, _height ), // frustum _frustum = new Frustum(), // clipping _clipping = new WebGLClipping(), _clippingEnabled = false, _localClippingEnabled = false, _sphere = new Sphere(), // camera matrices cache _projScreenMatrix = new Matrix4(), _vector3 = new Vector3(), _matrix4 = new Matrix4(), _matrix42 = new Matrix4(), // light arrays cache _lights = { hash: '', ambient: [ 0, 0, 0 ], directional: [], directionalShadowMap: [], directionalShadowMatrix: [], spot: [], spotShadowMap: [], spotShadowMatrix: [], rectArea: [], point: [], pointShadowMap: [], pointShadowMatrix: [], hemi: [], shadows: [] }, // info _infoRender = { calls: 0, vertices: 0, faces: 0, points: 0 }; this.info = { render: _infoRender, memory: { geometries: 0, textures: 0 }, programs: null }; // initialize var _gl; try { var attributes = { alpha: _alpha, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer }; _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); if ( _gl === null ) { if ( _canvas.getContext( 'webgl' ) !== null ) { throw 'Error creating WebGL context with your selected attributes.'; } else { throw 'Error creating WebGL context.'; } } // Some experimental-webgl implementations do not have getShaderPrecisionFormat if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; }; } _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); } catch ( error ) { console.error( 'THREE.WebGLRenderer: ' + error ); } var extensions = new WebGLExtensions( _gl ); extensions.get( 'WEBGL_depth_texture' ); extensions.get( 'OES_texture_float' ); extensions.get( 'OES_texture_float_linear' ); extensions.get( 'OES_texture_half_float' ); extensions.get( 'OES_texture_half_float_linear' ); extensions.get( 'OES_standard_derivatives' ); extensions.get( 'ANGLE_instanced_arrays' ); if ( extensions.get( 'OES_element_index_uint' ) ) { BufferGeometry.MaxIndex = 4294967296; } var capabilities = new WebGLCapabilities( _gl, extensions, parameters ); var state = new WebGLState( _gl, extensions, paramThreeToGL ); var properties = new WebGLProperties(); var textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, paramThreeToGL, this.info ); var objects = new WebGLObjects( _gl, properties, this.info ); var programCache = new WebGLPrograms( this, capabilities ); var lightCache = new WebGLLights(); this.info.programs = programCache.programs; var bufferRenderer = new WebGLBufferRenderer( _gl, extensions, _infoRender ); var indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); // var backgroundCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); var backgroundCamera2 = new PerspectiveCamera(); var backgroundPlaneMesh = new Mesh( new PlaneBufferGeometry( 2, 2 ), new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } ) ); var backgroundBoxShader = ShaderLib[ 'cube' ]; var backgroundBoxMesh = new Mesh( new BoxBufferGeometry( 5, 5, 5 ), new ShaderMaterial( { uniforms: backgroundBoxShader.uniforms, vertexShader: backgroundBoxShader.vertexShader, fragmentShader: backgroundBoxShader.fragmentShader, side: BackSide, depthTest: false, depthWrite: false, fog: false } ) ); // function getTargetPixelRatio() { return _currentRenderTarget === null ? _pixelRatio : 1; } function setDefaultGLState() { state.init(); state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); } function resetGLState() { _currentProgram = null; _currentCamera = null; _currentGeometryProgram = ''; _currentMaterialId = - 1; state.reset(); } setDefaultGLState(); this.context = _gl; this.capabilities = capabilities; this.extensions = extensions; this.properties = properties; this.state = state; // shadow map var shadowMap = new WebGLShadowMap( this, _lights, objects, capabilities ); this.shadowMap = shadowMap; // Plugins var spritePlugin = new SpritePlugin( this, sprites ); var lensFlarePlugin = new LensFlarePlugin( this, lensFlares ); // API this.getContext = function () { return _gl; }; this.getContextAttributes = function () { return _gl.getContextAttributes(); }; this.forceContextLoss = function () { extensions.get( 'WEBGL_lose_context' ).loseContext(); }; this.getMaxAnisotropy = function () { return capabilities.getMaxAnisotropy(); }; this.getPrecision = function () { return capabilities.precision; }; this.getPixelRatio = function () { return _pixelRatio; }; this.setPixelRatio = function ( value ) { if ( value === undefined ) return; _pixelRatio = value; this.setSize( _viewport.z, _viewport.w, false ); }; this.getSize = function () { return { width: _width, height: _height }; }; this.setSize = function ( width, height, updateStyle ) { _width = width; _height = height; _canvas.width = width * _pixelRatio; _canvas.height = height * _pixelRatio; if ( updateStyle !== false ) { _canvas.style.width = width + 'px'; _canvas.style.height = height + 'px'; } this.setViewport( 0, 0, width, height ); }; this.setViewport = function ( x, y, width, height ) { state.viewport( _viewport.set( x, y, width, height ) ); }; this.setScissor = function ( x, y, width, height ) { state.scissor( _scissor.set( x, y, width, height ) ); }; this.setScissorTest = function ( boolean ) { state.setScissorTest( _scissorTest = boolean ); }; // Clearing this.getClearColor = function () { return _clearColor; }; this.setClearColor = function ( color, alpha ) { _clearColor.set( color ); _clearAlpha = alpha !== undefined ? alpha : 1; state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); }; this.getClearAlpha = function () { return _clearAlpha; }; this.setClearAlpha = function ( alpha ) { _clearAlpha = alpha; state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); }; this.clear = function ( color, depth, stencil ) { var bits = 0; if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear( bits ); }; this.clearColor = function () { this.clear( true, false, false ); }; this.clearDepth = function () { this.clear( false, true, false ); }; this.clearStencil = function () { this.clear( false, false, true ); }; this.clearTarget = function ( renderTarget, color, depth, stencil ) { this.setRenderTarget( renderTarget ); this.clear( color, depth, stencil ); }; // Reset this.resetGLState = resetGLState; this.dispose = function() { transparentObjects = []; transparentObjectsLastIndex = -1; opaqueObjects = []; opaqueObjectsLastIndex = -1; _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); }; // Events function onContextLost( event ) { event.preventDefault(); resetGLState(); setDefaultGLState(); properties.clear(); } function onMaterialDispose( event ) { var material = event.target; material.removeEventListener( 'dispose', onMaterialDispose ); deallocateMaterial( material ); } // Buffer deallocation function deallocateMaterial( material ) { releaseMaterialProgramReference( material ); properties.delete( material ); } function releaseMaterialProgramReference( material ) { var programInfo = properties.get( material ).program; material.program = undefined; if ( programInfo !== undefined ) { programCache.releaseProgram( programInfo ); } } // Buffer rendering this.renderBufferImmediate = function ( object, program, material ) { state.initAttributes(); var buffers = properties.get( object ); if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); var attributes = program.getAttributes(); if ( object.hasPositions ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( attributes.position ); _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); if ( ! material.isMeshPhongMaterial && ! material.isMeshStandardMaterial && ! material.isMeshNormalMaterial && material.shading === FlatShading ) { for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { var array = object.normalArray; var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; array[ i + 0 ] = nx; array[ i + 1 ] = ny; array[ i + 2 ] = nz; array[ i + 3 ] = nx; array[ i + 4 ] = ny; array[ i + 5 ] = nz; array[ i + 6 ] = nx; array[ i + 7 ] = ny; array[ i + 8 ] = nz; } } _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( attributes.normal ); _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasUvs && material.map ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( attributes.uv ); _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); } if ( object.hasColors && material.vertexColors !== NoColors ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); state.enableAttribute( attributes.color ); _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } state.disableUnusedAttributes(); _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); object.count = 0; }; this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { setMaterial( material ); var program = setProgram( camera, fog, material, object ); var updateBuffers = false; var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; if ( geometryProgram !== _currentGeometryProgram ) { _currentGeometryProgram = geometryProgram; updateBuffers = true; } // morph targets var morphTargetInfluences = object.morphTargetInfluences; if ( morphTargetInfluences !== undefined ) { var activeInfluences = []; for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { var influence = morphTargetInfluences[ i ]; activeInfluences.push( [ influence, i ] ); } activeInfluences.sort( absNumericalSort ); if ( activeInfluences.length > 8 ) { activeInfluences.length = 8; } var morphAttributes = geometry.morphAttributes; for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { var influence = activeInfluences[ i ]; morphInfluences[ i ] = influence[ 0 ]; if ( influence[ 0 ] !== 0 ) { var index = influence[ 1 ]; if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] ); if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] ); } else { if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); } } for ( var i = activeInfluences.length, il = morphInfluences.length; i < il; i ++ ) { morphInfluences[ i ] = 0.0; } program.getUniforms().setValue( _gl, 'morphTargetInfluences', morphInfluences ); updateBuffers = true; } // var index = geometry.index; var position = geometry.attributes.position; var rangeFactor = 1; if ( material.wireframe === true ) { index = objects.getWireframeAttribute( geometry ); rangeFactor = 2; } var renderer; if ( index !== null ) { renderer = indexedBufferRenderer; renderer.setIndex( index ); } else { renderer = bufferRenderer; } if ( updateBuffers ) { setupVertexAttributes( material, program, geometry ); if ( index !== null ) { _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); } } // var dataCount = 0; if ( index !== null ) { dataCount = index.count; } else if ( position !== undefined ) { dataCount = position.count; } var rangeStart = geometry.drawRange.start * rangeFactor; var rangeCount = geometry.drawRange.count * rangeFactor; var groupStart = group !== null ? group.start * rangeFactor : 0; var groupCount = group !== null ? group.count * rangeFactor : Infinity; var drawStart = Math.max( rangeStart, groupStart ); var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); if ( drawCount === 0 ) return; // if ( object.isMesh ) { if ( material.wireframe === true ) { state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); renderer.setMode( _gl.LINES ); } else { switch ( object.drawMode ) { case TrianglesDrawMode: renderer.setMode( _gl.TRIANGLES ); break; case TriangleStripDrawMode: renderer.setMode( _gl.TRIANGLE_STRIP ); break; case TriangleFanDrawMode: renderer.setMode( _gl.TRIANGLE_FAN ); break; } } } else if ( object.isLine ) { var lineWidth = material.linewidth; if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material state.setLineWidth( lineWidth * getTargetPixelRatio() ); if ( object.isLineSegments ) { renderer.setMode( _gl.LINES ); } else { renderer.setMode( _gl.LINE_STRIP ); } } else if ( object.isPoints ) { renderer.setMode( _gl.POINTS ); } if ( geometry && geometry.isInstancedBufferGeometry ) { if ( geometry.maxInstancedCount > 0 ) { renderer.renderInstances( geometry, drawStart, drawCount ); } } else { renderer.render( drawStart, drawCount ); } }; function setupVertexAttributes( material, program, geometry, startIndex ) { var extension; if ( geometry && geometry.isInstancedBufferGeometry ) { extension = extensions.get( 'ANGLE_instanced_arrays' ); if ( extension === null ) { console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); return; } } if ( startIndex === undefined ) startIndex = 0; state.initAttributes(); var geometryAttributes = geometry.attributes; var programAttributes = program.getAttributes(); var materialDefaultAttributeValues = material.defaultAttributeValues; for ( var name in programAttributes ) { var programAttribute = programAttributes[ name ]; if ( programAttribute >= 0 ) { var geometryAttribute = geometryAttributes[ name ]; if ( geometryAttribute !== undefined ) { var normalized = geometryAttribute.normalized; var size = geometryAttribute.itemSize; var attributeProperties = objects.getAttributeProperties( geometryAttribute ); var buffer = attributeProperties.__webglBuffer; var type = attributeProperties.type; var bytesPerElement = attributeProperties.bytesPerElement; if ( geometryAttribute.isInterleavedBufferAttribute ) { var data = geometryAttribute.data; var stride = data.stride; var offset = geometryAttribute.offset; if ( data && data.isInstancedInterleavedBuffer ) { state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension ); if ( geometry.maxInstancedCount === undefined ) { geometry.maxInstancedCount = data.meshPerAttribute * data.count; } } else { state.enableAttribute( programAttribute ); } _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, ( startIndex * stride + offset ) * bytesPerElement ); } else { if ( geometryAttribute.isInstancedBufferAttribute ) { state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension ); if ( geometry.maxInstancedCount === undefined ) { geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; } } else { state.enableAttribute( programAttribute ); } _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * bytesPerElement ); } } else if ( materialDefaultAttributeValues !== undefined ) { var value = materialDefaultAttributeValues[ name ]; if ( value !== undefined ) { switch ( value.length ) { case 2: _gl.vertexAttrib2fv( programAttribute, value ); break; case 3: _gl.vertexAttrib3fv( programAttribute, value ); break; case 4: _gl.vertexAttrib4fv( programAttribute, value ); break; default: _gl.vertexAttrib1fv( programAttribute, value ); } } } } } state.disableUnusedAttributes(); } // Sorting function absNumericalSort( a, b ) { return Math.abs( b[ 0 ] ) - Math.abs( a[ 0 ] ); } function painterSortStable( a, b ) { if ( a.object.renderOrder !== b.object.renderOrder ) { return a.object.renderOrder - b.object.renderOrder; } else if ( a.material.program && b.material.program && a.material.program !== b.material.program ) { return a.material.program.id - b.material.program.id; } else if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable( a, b ) { if ( a.object.renderOrder !== b.object.renderOrder ) { return a.object.renderOrder - b.object.renderOrder; } if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } // Rendering this.render = function ( scene, camera, renderTarget, forceClear ) { if ( camera !== undefined && camera.isCamera !== true ) { console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); return; } // reset caching for this frame _currentGeometryProgram = ''; _currentMaterialId = - 1; _currentCamera = null; // update scene graph if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === null ) camera.updateMatrixWorld(); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); lights.length = 0; opaqueObjectsLastIndex = - 1; transparentObjectsLastIndex = - 1; sprites.length = 0; lensFlares.length = 0; _localClippingEnabled = this.localClippingEnabled; _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); projectObject( scene, camera ); opaqueObjects.length = opaqueObjectsLastIndex + 1; transparentObjects.length = transparentObjectsLastIndex + 1; if ( _this.sortObjects === true ) { opaqueObjects.sort( painterSortStable ); transparentObjects.sort( reversePainterSortStable ); } // if ( _clippingEnabled ) _clipping.beginShadows(); setupShadows( lights ); shadowMap.render( scene, camera ); setupLights( lights, camera ); if ( _clippingEnabled ) _clipping.endShadows(); // _infoRender.calls = 0; _infoRender.vertices = 0; _infoRender.faces = 0; _infoRender.points = 0; if ( renderTarget === undefined ) { renderTarget = null; } this.setRenderTarget( renderTarget ); // var background = scene.background; if ( background === null ) { state.buffers.color.setClear( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha, _premultipliedAlpha ); } else if ( background && background.isColor ) { state.buffers.color.setClear( background.r, background.g, background.b, 1, _premultipliedAlpha ); forceClear = true; } if ( this.autoClear || forceClear ) { this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); } if ( background && background.isCubeTexture ) { backgroundCamera2.projectionMatrix.copy( camera.projectionMatrix ); backgroundCamera2.matrixWorld.extractRotation( camera.matrixWorld ); backgroundCamera2.matrixWorldInverse.getInverse( backgroundCamera2.matrixWorld ); backgroundBoxMesh.material.uniforms[ "tCube" ].value = background; backgroundBoxMesh.modelViewMatrix.multiplyMatrices( backgroundCamera2.matrixWorldInverse, backgroundBoxMesh.matrixWorld ); objects.update( backgroundBoxMesh ); _this.renderBufferDirect( backgroundCamera2, null, backgroundBoxMesh.geometry, backgroundBoxMesh.material, backgroundBoxMesh, null ); } else if ( background && background.isTexture ) { backgroundPlaneMesh.material.map = background; objects.update( backgroundPlaneMesh ); _this.renderBufferDirect( backgroundCamera, null, backgroundPlaneMesh.geometry, backgroundPlaneMesh.material, backgroundPlaneMesh, null ); } // if ( scene.overrideMaterial ) { var overrideMaterial = scene.overrideMaterial; renderObjects( opaqueObjects, scene, camera, overrideMaterial ); renderObjects( transparentObjects, scene, camera, overrideMaterial ); } else { // opaque pass (front-to-back order) state.setBlending( NoBlending ); renderObjects( opaqueObjects, scene, camera ); // transparent pass (back-to-front order) renderObjects( transparentObjects, scene, camera ); } // custom render plugins (post pass) spritePlugin.render( scene, camera ); lensFlarePlugin.render( scene, camera, _currentViewport ); // Generate mipmap if we're using any kind of mipmap filtering if ( renderTarget ) { textures.updateRenderTargetMipmap( renderTarget ); } // Ensure depth buffer writing is enabled so it can be cleared on next render state.setDepthTest( true ); state.setDepthWrite( true ); state.setColorWrite( true ); // _gl.finish(); }; function pushRenderItem( object, geometry, material, z, group ) { var array, index; // allocate the next position in the appropriate array if ( material.transparent ) { array = transparentObjects; index = ++ transparentObjectsLastIndex; } else { array = opaqueObjects; index = ++ opaqueObjectsLastIndex; } // recycle existing render item or grow the array var renderItem = array[ index ]; if ( renderItem !== undefined ) { renderItem.id = object.id; renderItem.object = object; renderItem.geometry = geometry; renderItem.material = material; renderItem.z = _vector3.z; renderItem.group = group; } else { renderItem = { id: object.id, object: object, geometry: geometry, material: material, z: _vector3.z, group: group }; // assert( index === array.length ); array.push( renderItem ); } } // TODO Duplicated code (Frustum) function isObjectViewable( object ) { var geometry = object.geometry; if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); _sphere.copy( geometry.boundingSphere ). applyMatrix4( object.matrixWorld ); return isSphereViewable( _sphere ); } function isSpriteViewable( sprite ) { _sphere.center.set( 0, 0, 0 ); _sphere.radius = 0.7071067811865476; _sphere.applyMatrix4( sprite.matrixWorld ); return isSphereViewable( _sphere ); } function isSphereViewable( sphere ) { if ( ! _frustum.intersectsSphere( sphere ) ) return false; var numPlanes = _clipping.numPlanes; if ( numPlanes === 0 ) return true; var planes = _this.clippingPlanes, center = sphere.center, negRad = - sphere.radius, i = 0; do { // out when deeper than radius in the negative halfspace if ( planes[ i ].distanceToPoint( center ) < negRad ) return false; } while ( ++ i !== numPlanes ); return true; } function projectObject( object, camera ) { if ( object.visible === false ) return; var visible = ( object.layers.mask & camera.layers.mask ) !== 0; if ( visible ) { if ( object.isLight ) { lights.push( object ); } else if ( object.isSprite ) { if ( object.frustumCulled === false || isSpriteViewable( object ) === true ) { sprites.push( object ); } } else if ( object.isLensFlare ) { lensFlares.push( object ); } else if ( object.isImmediateRenderObject ) { if ( _this.sortObjects === true ) { _vector3.setFromMatrixPosition( object.matrixWorld ); _vector3.applyProjection( _projScreenMatrix ); } pushRenderItem( object, null, object.material, _vector3.z, null ); } else if ( object.isMesh || object.isLine || object.isPoints ) { if ( object.isSkinnedMesh ) { object.skeleton.update(); } if ( object.frustumCulled === false || isObjectViewable( object ) === true ) { var material = object.material; if ( material.visible === true ) { if ( _this.sortObjects === true ) { _vector3.setFromMatrixPosition( object.matrixWorld ); _vector3.applyProjection( _projScreenMatrix ); } var geometry = objects.update( object ); if ( material.isMultiMaterial ) { var groups = geometry.groups; var materials = material.materials; for ( var i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; var groupMaterial = materials[ group.materialIndex ]; if ( groupMaterial.visible === true ) { pushRenderItem( object, geometry, groupMaterial, _vector3.z, group ); } } } else { pushRenderItem( object, geometry, material, _vector3.z, null ); } } } } } var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { projectObject( children[ i ], camera ); } } function renderObjects( renderList, scene, camera, overrideMaterial ) { for ( var i = 0, l = renderList.length; i < l; i ++ ) { var renderItem = renderList[ i ]; var object = renderItem.object; var geometry = renderItem.geometry; var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; var group = renderItem.group; object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); object.onBeforeRender( _this, scene, camera, geometry, material, group ); if ( object.isImmediateRenderObject ) { setMaterial( material ); var program = setProgram( camera, scene.fog, material, object ); _currentGeometryProgram = ''; object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); } else { _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); } object.onAfterRender( _this, scene, camera, geometry, material, group ); } } function initMaterial( material, fog, object ) { var materialProperties = properties.get( material ); var parameters = programCache.getParameters( material, _lights, fog, _clipping.numPlanes, _clipping.numIntersection, object ); var code = programCache.getProgramCode( material, parameters ); var program = materialProperties.program; var programChange = true; if ( program === undefined ) { // new material material.addEventListener( 'dispose', onMaterialDispose ); } else if ( program.code !== code ) { // changed glsl or parameters releaseMaterialProgramReference( material ); } else if ( parameters.shaderID !== undefined ) { // same glsl and uniform list return; } else { // only rebuild uniform list programChange = false; } if ( programChange ) { if ( parameters.shaderID ) { var shader = ShaderLib[ parameters.shaderID ]; materialProperties.__webglShader = { name: material.type, uniforms: UniformsUtils.clone( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader }; } else { materialProperties.__webglShader = { name: material.type, uniforms: material.uniforms, vertexShader: material.vertexShader, fragmentShader: material.fragmentShader }; } material.__webglShader = materialProperties.__webglShader; program = programCache.acquireProgram( material, parameters, code ); materialProperties.program = program; material.program = program; } var attributes = program.getAttributes(); if ( material.morphTargets ) { material.numSupportedMorphTargets = 0; for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { if ( attributes[ 'morphTarget' + i ] >= 0 ) { material.numSupportedMorphTargets ++; } } } if ( material.morphNormals ) { material.numSupportedMorphNormals = 0; for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { if ( attributes[ 'morphNormal' + i ] >= 0 ) { material.numSupportedMorphNormals ++; } } } var uniforms = materialProperties.__webglShader.uniforms; if ( ! material.isShaderMaterial && ! material.isRawShaderMaterial || material.clipping === true ) { materialProperties.numClippingPlanes = _clipping.numPlanes; materialProperties.numIntersection = _clipping.numIntersection; uniforms.clippingPlanes = _clipping.uniform; } materialProperties.fog = fog; // store the light setup it was created for materialProperties.lightsHash = _lights.hash; if ( material.lights ) { // wire up the material to this renderer's lighting state uniforms.ambientLightColor.value = _lights.ambient; uniforms.directionalLights.value = _lights.directional; uniforms.spotLights.value = _lights.spot; uniforms.rectAreaLights.value = _lights.rectArea; uniforms.pointLights.value = _lights.point; uniforms.hemisphereLights.value = _lights.hemi; uniforms.directionalShadowMap.value = _lights.directionalShadowMap; uniforms.directionalShadowMatrix.value = _lights.directionalShadowMatrix; uniforms.spotShadowMap.value = _lights.spotShadowMap; uniforms.spotShadowMatrix.value = _lights.spotShadowMatrix; uniforms.pointShadowMap.value = _lights.pointShadowMap; uniforms.pointShadowMatrix.value = _lights.pointShadowMatrix; // TODO (abelnation): add area lights shadow info to uniforms } var progUniforms = materialProperties.program.getUniforms(), uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); materialProperties.uniformsList = uniformsList; } function setMaterial( material ) { material.side === DoubleSide ? state.disable( _gl.CULL_FACE ) : state.enable( _gl.CULL_FACE ); state.setFlipSided( material.side === BackSide ); material.transparent === true ? state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ) : state.setBlending( NoBlending ); state.setDepthFunc( material.depthFunc ); state.setDepthTest( material.depthTest ); state.setDepthWrite( material.depthWrite ); state.setColorWrite( material.colorWrite ); state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); } function setProgram( camera, fog, material, object ) { _usedTextureUnits = 0; var materialProperties = properties.get( material ); if ( _clippingEnabled ) { if ( _localClippingEnabled || camera !== _currentCamera ) { var useCache = camera === _currentCamera && material.id === _currentMaterialId; // we might want to call this function with some ClippingGroup // object instead of the material, once it becomes feasible // (#8465, #8379) _clipping.setState( material.clippingPlanes, material.clipIntersection, material.clipShadows, camera, materialProperties, useCache ); } } if ( material.needsUpdate === false ) { if ( materialProperties.program === undefined ) { material.needsUpdate = true; } else if ( material.fog && materialProperties.fog !== fog ) { material.needsUpdate = true; } else if ( material.lights && materialProperties.lightsHash !== _lights.hash ) { material.needsUpdate = true; } else if ( materialProperties.numClippingPlanes !== undefined && ( materialProperties.numClippingPlanes !== _clipping.numPlanes || materialProperties.numIntersection !== _clipping.numIntersection ) ) { material.needsUpdate = true; } } if ( material.needsUpdate ) { initMaterial( material, fog, object ); material.needsUpdate = false; } var refreshProgram = false; var refreshMaterial = false; var refreshLights = false; var program = materialProperties.program, p_uniforms = program.getUniforms(), m_uniforms = materialProperties.__webglShader.uniforms; if ( program.id !== _currentProgram ) { _gl.useProgram( program.program ); _currentProgram = program.id; refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || camera !== _currentCamera ) { p_uniforms.set( _gl, camera, 'projectionMatrix' ); if ( capabilities.logarithmicDepthBuffer ) { p_uniforms.setValue( _gl, 'logDepthBufFC', 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } if ( camera !== _currentCamera ) { _currentCamera = camera; // lighting uniforms depend on the camera so enforce an update // now, in case this material supports lights - or later, when // the next material that does gets activated: refreshMaterial = true; // set to true on material change refreshLights = true; // remains set until update done } // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material.isShaderMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.envMap ) { var uCamPos = p_uniforms.map.cameraPosition; if ( uCamPos !== undefined ) { uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } } if ( material.isMeshPhongMaterial || material.isMeshLambertMaterial || material.isMeshBasicMaterial || material.isMeshStandardMaterial || material.isShaderMaterial || material.skinning ) { p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); } p_uniforms.set( _gl, _this, 'toneMappingExposure' ); p_uniforms.set( _gl, _this, 'toneMappingWhitePoint' ); } // skinning uniforms must be set even if material didn't change // auto-setting of texture unit for bone texture must go before other textures // not sure why, but otherwise weird things happen if ( material.skinning ) { p_uniforms.setOptional( _gl, object, 'bindMatrix' ); p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); var skeleton = object.skeleton; if ( skeleton ) { if ( capabilities.floatVertexTextures && skeleton.useVertexTexture ) { p_uniforms.set( _gl, skeleton, 'boneTexture' ); p_uniforms.set( _gl, skeleton, 'boneTextureWidth' ); p_uniforms.set( _gl, skeleton, 'boneTextureHeight' ); } else { p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); } } } if ( refreshMaterial ) { if ( material.lights ) { // the current material requires lighting info // note: all lighting uniforms are always set correctly // they simply reference the renderer's state for their // values // // use the current material's .needsUpdate flags to set // the GL state when required markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); } // refresh uniforms common to several materials if ( fog && material.fog ) { refreshUniformsFog( m_uniforms, fog ); } if ( material.isMeshBasicMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial || material.isMeshNormalMaterial || material.isMeshDepthMaterial ) { refreshUniformsCommon( m_uniforms, material ); } // refresh single material specific uniforms if ( material.isLineBasicMaterial ) { refreshUniformsLine( m_uniforms, material ); } else if ( material.isLineDashedMaterial ) { refreshUniformsLine( m_uniforms, material ); refreshUniformsDash( m_uniforms, material ); } else if ( material.isPointsMaterial ) { refreshUniformsPoints( m_uniforms, material ); } else if ( material.isMeshLambertMaterial ) { refreshUniformsLambert( m_uniforms, material ); } else if ( material.isMeshToonMaterial ) { refreshUniformsToon( m_uniforms, material ); } else if ( material.isMeshPhongMaterial ) { refreshUniformsPhong( m_uniforms, material ); } else if ( material.isMeshPhysicalMaterial ) { refreshUniformsPhysical( m_uniforms, material ); } else if ( material.isMeshStandardMaterial ) { refreshUniformsStandard( m_uniforms, material ); } else if ( material.isMeshDepthMaterial ) { if ( material.displacementMap ) { m_uniforms.displacementMap.value = material.displacementMap; m_uniforms.displacementScale.value = material.displacementScale; m_uniforms.displacementBias.value = material.displacementBias; } } else if ( material.isMeshNormalMaterial ) { refreshUniformsNormal( m_uniforms, material ); } // RectAreaLight Texture // TODO (mrdoob): Find a nicer implementation if ( m_uniforms.ltcMat !== undefined ) m_uniforms.ltcMat.value = THREE.UniformsLib.LTC_MAT_TEXTURE; if ( m_uniforms.ltcMag !== undefined ) m_uniforms.ltcMag.value = THREE.UniformsLib.LTC_MAG_TEXTURE; WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this ); } // common matrices p_uniforms.set( _gl, object, 'modelViewMatrix' ); p_uniforms.set( _gl, object, 'normalMatrix' ); p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); return program; } // Uniforms (refresh uniforms objects) function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; uniforms.diffuse.value = material.color; if ( material.emissive ) { uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } uniforms.map.value = material.map; uniforms.specularMap.value = material.specularMap; uniforms.alphaMap.value = material.alphaMap; if ( material.lightMap ) { uniforms.lightMap.value = material.lightMap; uniforms.lightMapIntensity.value = material.lightMapIntensity; } if ( material.aoMap ) { uniforms.aoMap.value = material.aoMap; uniforms.aoMapIntensity.value = material.aoMapIntensity; } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. normal map // 4. bump map // 5. alpha map // 6. emissive map var uvScaleMap; if ( material.map ) { uvScaleMap = material.map; } else if ( material.specularMap ) { uvScaleMap = material.specularMap; } else if ( material.displacementMap ) { uvScaleMap = material.displacementMap; } else if ( material.normalMap ) { uvScaleMap = material.normalMap; } else if ( material.bumpMap ) { uvScaleMap = material.bumpMap; } else if ( material.roughnessMap ) { uvScaleMap = material.roughnessMap; } else if ( material.metalnessMap ) { uvScaleMap = material.metalnessMap; } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; } else if ( material.emissiveMap ) { uvScaleMap = material.emissiveMap; } if ( uvScaleMap !== undefined ) { // backwards compatibility if ( uvScaleMap.isWebGLRenderTarget ) { uvScaleMap = uvScaleMap.texture; } var offset = uvScaleMap.offset; var repeat = uvScaleMap.repeat; uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); } uniforms.envMap.value = material.envMap; // don't flip CubeTexture envMaps, flip everything else: // WebGLRenderTargetCube will be flipped for backwards compatibility // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1; uniforms.reflectivity.value = material.reflectivity; uniforms.refractionRatio.value = material.refractionRatio; } function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; } function refreshUniformsDash( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsPoints( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; uniforms.size.value = material.size * _pixelRatio; uniforms.scale.value = _height * 0.5; uniforms.map.value = material.map; if ( material.map !== null ) { var offset = material.map.offset; var repeat = material.map.repeat; uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); } } function refreshUniformsFog( uniforms, fog ) { uniforms.fogColor.value = fog.color; if ( fog.isFog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog.isFogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshUniformsLambert( uniforms, material ) { if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } } function refreshUniformsPhong( uniforms, material ) { uniforms.specular.value = material.specular; uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } function refreshUniformsToon( uniforms, material ) { refreshUniformsPhong( uniforms, material ); if ( material.gradientMap ) { uniforms.gradientMap.value = material.gradientMap; } } function refreshUniformsStandard( uniforms, material ) { uniforms.roughness.value = material.roughness; uniforms.metalness.value = material.metalness; if ( material.roughnessMap ) { uniforms.roughnessMap.value = material.roughnessMap; } if ( material.metalnessMap ) { uniforms.metalnessMap.value = material.metalnessMap; } if ( material.emissiveMap ) { uniforms.emissiveMap.value = material.emissiveMap; } if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } if ( material.envMap ) { //uniforms.envMap.value = material.envMap; // part of uniforms common uniforms.envMapIntensity.value = material.envMapIntensity; } } function refreshUniformsPhysical( uniforms, material ) { uniforms.clearCoat.value = material.clearCoat; uniforms.clearCoatRoughness.value = material.clearCoatRoughness; refreshUniformsStandard( uniforms, material ); } function refreshUniformsNormal( uniforms, material ) { if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); } if ( material.displacementMap ) { uniforms.displacementMap.value = material.displacementMap; uniforms.displacementScale.value = material.displacementScale; uniforms.displacementBias.value = material.displacementBias; } } // If uniforms are marked as clean, they don't need to be loaded to the GPU. function markUniformsLightsNeedsUpdate( uniforms, value ) { uniforms.ambientLightColor.needsUpdate = value; uniforms.directionalLights.needsUpdate = value; uniforms.pointLights.needsUpdate = value; uniforms.spotLights.needsUpdate = value; uniforms.rectAreaLights.needsUpdate = value; uniforms.hemisphereLights.needsUpdate = value; } // Lighting function setupShadows( lights ) { var lightShadowsLength = 0; for ( var i = 0, l = lights.length; i < l; i ++ ) { var light = lights[ i ]; if ( light.castShadow ) { _lights.shadows[ lightShadowsLength ++ ] = light; } } _lights.shadows.length = lightShadowsLength; } function setupLights( lights, camera ) { var l, ll, light, r = 0, g = 0, b = 0, color, intensity, distance, shadowMap, viewMatrix = camera.matrixWorldInverse, directionalLength = 0, pointLength = 0, spotLength = 0, rectAreaLength = 0, hemiLength = 0; for ( l = 0, ll = lights.length; l < ll; l ++ ) { light = lights[ l ]; color = light.color; intensity = light.intensity; distance = light.distance; shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; if ( light.isAmbientLight ) { r += color.r * intensity; g += color.g * intensity; b += color.b * intensity; } else if ( light.isDirectionalLight ) { var uniforms = lightCache.get( light ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); _vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( _vector3 ); uniforms.direction.transformDirection( viewMatrix ); uniforms.shadow = light.castShadow; if ( light.castShadow ) { uniforms.shadowBias = light.shadow.bias; uniforms.shadowRadius = light.shadow.radius; uniforms.shadowMapSize = light.shadow.mapSize; } _lights.directionalShadowMap[ directionalLength ] = shadowMap; _lights.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; _lights.directional[ directionalLength ++ ] = uniforms; } else if ( light.isSpotLight ) { var uniforms = lightCache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.distance = distance; uniforms.direction.setFromMatrixPosition( light.matrixWorld ); _vector3.setFromMatrixPosition( light.target.matrixWorld ); uniforms.direction.sub( _vector3 ); uniforms.direction.transformDirection( viewMatrix ); uniforms.coneCos = Math.cos( light.angle ); uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; uniforms.shadow = light.castShadow; if ( light.castShadow ) { uniforms.shadowBias = light.shadow.bias; uniforms.shadowRadius = light.shadow.radius; uniforms.shadowMapSize = light.shadow.mapSize; } _lights.spotShadowMap[ spotLength ] = shadowMap; _lights.spotShadowMatrix[ spotLength ] = light.shadow.matrix; _lights.spot[ spotLength ++ ] = uniforms; } else if ( light.isRectAreaLight ) { var uniforms = lightCache.get( light ); // (a) intensity controls irradiance of entire light uniforms.color .copy( color ) .multiplyScalar( intensity / ( light.width * light.height ) ); // (b) intensity controls the radiance per light area // uniforms.color.copy( color ).multiplyScalar( intensity ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); // extract local rotation of light to derive width/height half vectors _matrix42.identity(); _matrix4.copy( light.matrixWorld ); _matrix4.premultiply( viewMatrix ); _matrix42.extractRotation( _matrix4 ); uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); uniforms.halfWidth.applyMatrix4( _matrix42 ); uniforms.halfHeight.applyMatrix4( _matrix42 ); // TODO (abelnation): RectAreaLight distance? // uniforms.distance = distance; _lights.rectArea[ rectAreaLength ++ ] = uniforms; } else if ( light.isPointLight ) { var uniforms = lightCache.get( light ); uniforms.position.setFromMatrixPosition( light.matrixWorld ); uniforms.position.applyMatrix4( viewMatrix ); uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); uniforms.distance = light.distance; uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; uniforms.shadow = light.castShadow; if ( light.castShadow ) { uniforms.shadowBias = light.shadow.bias; uniforms.shadowRadius = light.shadow.radius; uniforms.shadowMapSize = light.shadow.mapSize; } _lights.pointShadowMap[ pointLength ] = shadowMap; if ( _lights.pointShadowMatrix[ pointLength ] === undefined ) { _lights.pointShadowMatrix[ pointLength ] = new Matrix4(); } // for point lights we set the shadow matrix to be a translation-only matrix // equal to inverse of the light's position _vector3.setFromMatrixPosition( light.matrixWorld ).negate(); _lights.pointShadowMatrix[ pointLength ].identity().setPosition( _vector3 ); _lights.point[ pointLength ++ ] = uniforms; } else if ( light.isHemisphereLight ) { var uniforms = lightCache.get( light ); uniforms.direction.setFromMatrixPosition( light.matrixWorld ); uniforms.direction.transformDirection( viewMatrix ); uniforms.direction.normalize(); uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); _lights.hemi[ hemiLength ++ ] = uniforms; } } _lights.ambient[ 0 ] = r; _lights.ambient[ 1 ] = g; _lights.ambient[ 2 ] = b; _lights.directional.length = directionalLength; _lights.spot.length = spotLength; _lights.rectArea.length = rectAreaLength; _lights.point.length = pointLength; _lights.hemi.length = hemiLength; // TODO (sam-g-steel) why aren't we using join _lights.hash = directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + _lights.shadows.length; } // GL state setting this.setFaceCulling = function ( cullFace, frontFaceDirection ) { state.setCullFace( cullFace ); state.setFlipSided( frontFaceDirection === FrontFaceDirectionCW ); }; // Textures function allocTextureUnit() { var textureUnit = _usedTextureUnits; if ( textureUnit >= capabilities.maxTextures ) { console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); } _usedTextureUnits += 1; return textureUnit; } this.allocTextureUnit = allocTextureUnit; // this.setTexture2D = setTexture2D; this.setTexture2D = ( function() { var warned = false; // backwards compatibility: peel texture.texture return function setTexture2D( texture, slot ) { if ( texture && texture.isWebGLRenderTarget ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead." ); warned = true; } texture = texture.texture; } textures.setTexture2D( texture, slot ); }; }() ); this.setTexture = ( function() { var warned = false; return function setTexture( texture, slot ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." ); warned = true; } textures.setTexture2D( texture, slot ); }; }() ); this.setTextureCube = ( function() { var warned = false; return function setTextureCube( texture, slot ) { // backwards compatibility: peel texture.texture if ( texture && texture.isWebGLRenderTargetCube ) { if ( ! warned ) { console.warn( "THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); warned = true; } texture = texture.texture; } // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture // TODO: unify these code paths if ( ( texture && texture.isCubeTexture ) || ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ // this function alone should take care of cube textures textures.setTextureCube( texture, slot ); } else { // assumed: texture property of THREE.WebGLRenderTargetCube textures.setTextureCubeDynamic( texture, slot ); } }; }() ); this.getCurrentRenderTarget = function() { return _currentRenderTarget; }; this.setRenderTarget = function ( renderTarget ) { _currentRenderTarget = renderTarget; if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { textures.setupRenderTarget( renderTarget ); } var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); var framebuffer; if ( renderTarget ) { var renderTargetProperties = properties.get( renderTarget ); if ( isCube ) { framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ]; } else { framebuffer = renderTargetProperties.__webglFramebuffer; } _currentScissor.copy( renderTarget.scissor ); _currentScissorTest = renderTarget.scissorTest; _currentViewport.copy( renderTarget.viewport ); } else { framebuffer = null; _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); _currentScissorTest = _scissorTest; _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); } if ( _currentFramebuffer !== framebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _currentFramebuffer = framebuffer; } state.scissor( _currentScissor ); state.setScissorTest( _currentScissorTest ); state.viewport( _currentViewport ); if ( isCube ) { var textureProperties = properties.get( renderTarget.texture ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel ); } }; this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { if ( ( renderTarget && renderTarget.isWebGLRenderTarget ) === false ) { console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); return; } var framebuffer = properties.get( renderTarget ).__webglFramebuffer; if ( framebuffer ) { var restore = false; if ( framebuffer !== _currentFramebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); restore = true; } try { var texture = renderTarget.texture; var textureFormat = texture.format; var textureType = texture.type; if ( textureFormat !== RGBAFormat && paramThreeToGL( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); return; } if ( textureType !== UnsignedByteType && paramThreeToGL( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513) ! ( textureType === FloatType && ( extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox ! ( textureType === HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) { console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); return; } if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { _gl.readPixels( x, y, width, height, paramThreeToGL( textureFormat ), paramThreeToGL( textureType ), buffer ); } } else { console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); } } finally { if ( restore ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); } } } }; // Map three.js constants to WebGL constants function paramThreeToGL( p ) { var extension; if ( p === RepeatWrapping ) return _gl.REPEAT; if ( p === ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; if ( p === MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; if ( p === NearestFilter ) return _gl.NEAREST; if ( p === NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; if ( p === NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; if ( p === LinearFilter ) return _gl.LINEAR; if ( p === LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; if ( p === LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; if ( p === UnsignedByteType ) return _gl.UNSIGNED_BYTE; if ( p === UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; if ( p === UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; if ( p === UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; if ( p === ByteType ) return _gl.BYTE; if ( p === ShortType ) return _gl.SHORT; if ( p === UnsignedShortType ) return _gl.UNSIGNED_SHORT; if ( p === IntType ) return _gl.INT; if ( p === UnsignedIntType ) return _gl.UNSIGNED_INT; if ( p === FloatType ) return _gl.FLOAT; if ( p === HalfFloatType ) { extension = extensions.get( 'OES_texture_half_float' ); if ( extension !== null ) return extension.HALF_FLOAT_OES; } if ( p === AlphaFormat ) return _gl.ALPHA; if ( p === RGBFormat ) return _gl.RGB; if ( p === RGBAFormat ) return _gl.RGBA; if ( p === LuminanceFormat ) return _gl.LUMINANCE; if ( p === LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; if ( p === DepthFormat ) return _gl.DEPTH_COMPONENT; if ( p === DepthStencilFormat ) return _gl.DEPTH_STENCIL; if ( p === AddEquation ) return _gl.FUNC_ADD; if ( p === SubtractEquation ) return _gl.FUNC_SUBTRACT; if ( p === ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; if ( p === ZeroFactor ) return _gl.ZERO; if ( p === OneFactor ) return _gl.ONE; if ( p === SrcColorFactor ) return _gl.SRC_COLOR; if ( p === OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; if ( p === SrcAlphaFactor ) return _gl.SRC_ALPHA; if ( p === OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; if ( p === DstAlphaFactor ) return _gl.DST_ALPHA; if ( p === OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; if ( p === DstColorFactor ) return _gl.DST_COLOR; if ( p === OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; if ( p === SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); if ( extension !== null ) { if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } } if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); if ( extension !== null ) { if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } } if ( p === RGB_ETC1_Format ) { extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL; } if ( p === MinEquation || p === MaxEquation ) { extension = extensions.get( 'EXT_blend_minmax' ); if ( extension !== null ) { if ( p === MinEquation ) return extension.MIN_EXT; if ( p === MaxEquation ) return extension.MAX_EXT; } } if ( p === UnsignedInt248Type ) { extension = extensions.get( 'WEBGL_depth_texture' ); if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL; } return 0; } } /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function FogExp2 ( color, density ) { this.name = ''; this.color = new Color( color ); this.density = ( density !== undefined ) ? density : 0.00025; } FogExp2.prototype.isFogExp2 = true; FogExp2.prototype.clone = function () { return new FogExp2( this.color.getHex(), this.density ); }; FogExp2.prototype.toJSON = function ( meta ) { return { type: 'FogExp2', color: this.color.getHex(), density: this.density }; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Fog ( color, near, far ) { this.name = ''; this.color = new Color( color ); this.near = ( near !== undefined ) ? near : 1; this.far = ( far !== undefined ) ? far : 1000; } Fog.prototype.isFog = true; Fog.prototype.clone = function () { return new Fog( this.color.getHex(), this.near, this.far ); }; Fog.prototype.toJSON = function ( meta ) { return { type: 'Fog', color: this.color.getHex(), near: this.near, far: this.far }; }; /** * @author mrdoob / http://mrdoob.com/ */ function Scene () { Object3D.call( this ); this.type = 'Scene'; this.background = null; this.fog = null; this.overrideMaterial = null; this.autoUpdate = true; // checked by the renderer } Scene.prototype = Object.create( Object3D.prototype ); Scene.prototype.constructor = Scene; Scene.prototype.copy = function ( source, recursive ) { Object3D.prototype.copy.call( this, source, recursive ); if ( source.background !== null ) this.background = source.background.clone(); if ( source.fog !== null ) this.fog = source.fog.clone(); if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); this.autoUpdate = source.autoUpdate; this.matrixAutoUpdate = source.matrixAutoUpdate; return this; }; Scene.prototype.toJSON = function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); return data; }; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function LensFlare( texture, size, distance, blending, color ) { Object3D.call( this ); this.lensFlares = []; this.positionScreen = new Vector3(); this.customUpdateCallback = undefined; if ( texture !== undefined ) { this.add( texture, size, distance, blending, color ); } } LensFlare.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: LensFlare, isLensFlare: true, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.positionScreen.copy( source.positionScreen ); this.customUpdateCallback = source.customUpdateCallback; for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { this.lensFlares.push( source.lensFlares[ i ] ); } return this; }, add: function ( texture, size, distance, blending, color, opacity ) { if ( size === undefined ) size = - 1; if ( distance === undefined ) distance = 0; if ( opacity === undefined ) opacity = 1; if ( color === undefined ) color = new Color( 0xffffff ); if ( blending === undefined ) blending = NormalBlending; distance = Math.min( distance, Math.max( 0, distance ) ); this.lensFlares.push( { texture: texture, // THREE.Texture size: size, // size in pixels (-1 = use texture.width) distance: distance, // distance (0-1) from light source (0=at light source) x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back scale: 1, // scale rotation: 0, // rotation opacity: opacity, // opacity color: color, // color blending: blending // blending } ); }, /* * Update lens flares update positions on all flares based on the screen position * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. */ updateLensFlares: function () { var f, fl = this.lensFlares.length; var flare; var vecX = - this.positionScreen.x * 2; var vecY = - this.positionScreen.y * 2; for ( f = 0; f < fl; f ++ ) { flare = this.lensFlares[ f ]; flare.x = this.positionScreen.x + vecX * flare.distance; flare.y = this.positionScreen.y + vecY * flare.distance; flare.wantedRotation = flare.x * Math.PI * 0.25; flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; } } } ); /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * uvOffset: new THREE.Vector2(), * uvScale: new THREE.Vector2() * } */ function SpriteMaterial( parameters ) { Material.call( this ); this.type = 'SpriteMaterial'; this.color = new Color( 0xffffff ); this.map = null; this.rotation = 0; this.fog = false; this.lights = false; this.setValues( parameters ); } SpriteMaterial.prototype = Object.create( Material.prototype ); SpriteMaterial.prototype.constructor = SpriteMaterial; SpriteMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.rotation = source.rotation; return this; }; /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ function Sprite( material ) { Object3D.call( this ); this.type = 'Sprite'; this.material = ( material !== undefined ) ? material : new SpriteMaterial(); } Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Sprite, isSprite: true, raycast: ( function () { var matrixPosition = new Vector3(); return function raycast( raycaster, intersects ) { matrixPosition.setFromMatrixPosition( this.matrixWorld ); var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); var guessSizeSq = this.scale.x * this.scale.y / 4; if ( distanceSq > guessSizeSq ) { return; } intersects.push( { distance: Math.sqrt( distanceSq ), point: this.position, face: null, object: this } ); }; }() ), clone: function () { return new this.constructor( this.material ).copy( this ); } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function LOD() { Object3D.call( this ); this.type = 'LOD'; Object.defineProperties( this, { levels: { enumerable: true, value: [] } } ); } LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: LOD, copy: function ( source ) { Object3D.prototype.copy.call( this, source, false ); var levels = source.levels; for ( var i = 0, l = levels.length; i < l; i ++ ) { var level = levels[ i ]; this.addLevel( level.object.clone(), level.distance ); } return this; }, addLevel: function ( object, distance ) { if ( distance === undefined ) distance = 0; distance = Math.abs( distance ); var levels = this.levels; for ( var l = 0; l < levels.length; l ++ ) { if ( distance < levels[ l ].distance ) { break; } } levels.splice( l, 0, { distance: distance, object: object } ); this.add( object ); }, getObjectForDistance: function ( distance ) { var levels = this.levels; for ( var i = 1, l = levels.length; i < l; i ++ ) { if ( distance < levels[ i ].distance ) { break; } } return levels[ i - 1 ].object; }, raycast: ( function () { var matrixPosition = new Vector3(); return function raycast( raycaster, intersects ) { matrixPosition.setFromMatrixPosition( this.matrixWorld ); var distance = raycaster.ray.origin.distanceTo( matrixPosition ); this.getObjectForDistance( distance ).raycast( raycaster, intersects ); }; }() ), update: function () { var v1 = new Vector3(); var v2 = new Vector3(); return function update( camera ) { var levels = this.levels; if ( levels.length > 1 ) { v1.setFromMatrixPosition( camera.matrixWorld ); v2.setFromMatrixPosition( this.matrixWorld ); var distance = v1.distanceTo( v2 ); levels[ 0 ].object.visible = true; for ( var i = 1, l = levels.length; i < l; i ++ ) { if ( distance >= levels[ i ].distance ) { levels[ i - 1 ].object.visible = false; levels[ i ].object.visible = true; } else { break; } } for ( ; i < l; i ++ ) { levels[ i ].object.visible = false; } } }; }(), toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.levels = []; var levels = this.levels; for ( var i = 0, l = levels.length; i < l; i ++ ) { var level = levels[ i ]; data.object.levels.push( { object: level.object.uuid, distance: level.distance } ); } return data; } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author michael guerrero / http://realitymeltdown.com * @author ikerr / http://verold.com */ function Skeleton( bones, boneInverses, useVertexTexture ) { this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; this.identityMatrix = new Matrix4(); // copy the bone array bones = bones || []; this.bones = bones.slice( 0 ); // create a bone texture or an array of floats if ( this.useVertexTexture ) { // layout (1 matrix = 4 pixels) // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix size = _Math.nextPowerOfTwo( Math.ceil( size ) ); size = Math.max( size, 4 ); this.boneTextureWidth = size; this.boneTextureHeight = size; this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel this.boneTexture = new DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, RGBAFormat, FloatType ); } else { this.boneMatrices = new Float32Array( 16 * this.bones.length ); } // use the supplied bone inverses or calculate the inverses if ( boneInverses === undefined ) { this.calculateInverses(); } else { if ( this.bones.length === boneInverses.length ) { this.boneInverses = boneInverses.slice( 0 ); } else { console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); this.boneInverses = []; for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { this.boneInverses.push( new Matrix4() ); } } } } Object.assign( Skeleton.prototype, { calculateInverses: function () { this.boneInverses = []; for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { var inverse = new Matrix4(); if ( this.bones[ b ] ) { inverse.getInverse( this.bones[ b ].matrixWorld ); } this.boneInverses.push( inverse ); } }, pose: function () { var bone; // recover the bind-time world matrices for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { bone = this.bones[ b ]; if ( bone ) { bone.matrixWorld.getInverse( this.boneInverses[ b ] ); } } // compute the local matrices, positions, rotations and scales for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { bone = this.bones[ b ]; if ( bone ) { if ( bone.parent && bone.parent.isBone ) { bone.matrix.getInverse( bone.parent.matrixWorld ); bone.matrix.multiply( bone.matrixWorld ); } else { bone.matrix.copy( bone.matrixWorld ); } bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); } } }, update: ( function () { var offsetMatrix = new Matrix4(); return function update() { // flatten bone matrices to array for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { // compute the offset between the current and the original transform var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); offsetMatrix.toArray( this.boneMatrices, b * 16 ); } if ( this.useVertexTexture ) { this.boneTexture.needsUpdate = true; } }; } )(), clone: function () { return new Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); } } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author ikerr / http://verold.com */ function Bone() { Object3D.call( this ); this.type = 'Bone'; } Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Bone, isBone: true } ); /** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author ikerr / http://verold.com */ function SkinnedMesh( geometry, material, useVertexTexture ) { Mesh.call( this, geometry, material ); this.type = 'SkinnedMesh'; this.bindMode = "attached"; this.bindMatrix = new Matrix4(); this.bindMatrixInverse = new Matrix4(); // init bones // TODO: remove bone creation as there is no reason (other than // convenience) for THREE.SkinnedMesh to do this. var bones = []; if ( this.geometry && this.geometry.bones !== undefined ) { var bone, gbone; for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { gbone = this.geometry.bones[ b ]; bone = new Bone(); bones.push( bone ); bone.name = gbone.name; bone.position.fromArray( gbone.pos ); bone.quaternion.fromArray( gbone.rotq ); if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); } for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { gbone = this.geometry.bones[ b ]; if ( gbone.parent !== - 1 && gbone.parent !== null && bones[ gbone.parent ] !== undefined ) { bones[ gbone.parent ].add( bones[ b ] ); } else { this.add( bones[ b ] ); } } } this.normalizeSkinWeights(); this.updateMatrixWorld( true ); this.bind( new Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); } SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { constructor: SkinnedMesh, isSkinnedMesh: true, bind: function( skeleton, bindMatrix ) { this.skeleton = skeleton; if ( bindMatrix === undefined ) { this.updateMatrixWorld( true ); this.skeleton.calculateInverses(); bindMatrix = this.matrixWorld; } this.bindMatrix.copy( bindMatrix ); this.bindMatrixInverse.getInverse( bindMatrix ); }, pose: function () { this.skeleton.pose(); }, normalizeSkinWeights: function () { if ( this.geometry && this.geometry.isGeometry ) { for ( var i = 0; i < this.geometry.skinWeights.length; i ++ ) { var sw = this.geometry.skinWeights[ i ]; var scale = 1.0 / sw.lengthManhattan(); if ( scale !== Infinity ) { sw.multiplyScalar( scale ); } else { sw.set( 1, 0, 0, 0 ); // do something reasonable } } } else if ( this.geometry && this.geometry.isBufferGeometry ) { var vec = new Vector4(); var skinWeight = this.geometry.attributes.skinWeight; for ( var i = 0; i < skinWeight.count; i ++ ) { vec.x = skinWeight.getX( i ); vec.y = skinWeight.getY( i ); vec.z = skinWeight.getZ( i ); vec.w = skinWeight.getW( i ); var scale = 1.0 / vec.lengthManhattan(); if ( scale !== Infinity ) { vec.multiplyScalar( scale ); } else { vec.set( 1, 0, 0, 0 ); // do something reasonable } skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); } } }, updateMatrixWorld: function( force ) { Mesh.prototype.updateMatrixWorld.call( this, true ); if ( this.bindMode === "attached" ) { this.bindMatrixInverse.getInverse( this.matrixWorld ); } else if ( this.bindMode === "detached" ) { this.bindMatrixInverse.getInverse( this.bindMatrix ); } else { console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); } }, clone: function() { return new this.constructor( this.geometry, this.material, this.skeleton.useVertexTexture ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * linewidth: , * linecap: "round", * linejoin: "round" * } */ function LineBasicMaterial( parameters ) { Material.call( this ); this.type = 'LineBasicMaterial'; this.color = new Color( 0xffffff ); this.linewidth = 1; this.linecap = 'round'; this.linejoin = 'round'; this.lights = false; this.setValues( parameters ); } LineBasicMaterial.prototype = Object.create( Material.prototype ); LineBasicMaterial.prototype.constructor = LineBasicMaterial; LineBasicMaterial.prototype.isLineBasicMaterial = true; LineBasicMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.linewidth = source.linewidth; this.linecap = source.linecap; this.linejoin = source.linejoin; return this; }; /** * @author mrdoob / http://mrdoob.com/ */ function Line( geometry, material, mode ) { if ( mode === 1 ) { console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); return new LineSegments( geometry, material ); } Object3D.call( this ); this.type = 'Line'; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); } Line.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Line, isLine: true, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); return function raycast( raycaster, intersects ) { var precision = raycaster.linePrecision; var precisionSq = precision * precision; var geometry = this.geometry; var matrixWorld = this.matrixWorld; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var vStart = new Vector3(); var vEnd = new Vector3(); var interSegment = new Vector3(); var interRay = new Vector3(); var step = (this && this.isLineSegments) ? 2 : 1; if ( geometry.isBufferGeometry ) { var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( index !== null ) { var indices = index.array; for ( var i = 0, l = indices.length - 1; i < l; i += step ) { var a = indices[ i ]; var b = indices[ i + 1 ]; vStart.fromArray( positions, a * 3 ); vEnd.fromArray( positions, b * 3 ); var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } else { for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { vStart.fromArray( positions, 3 * i ); vEnd.fromArray( positions, 3 * i + 3 ); var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } } else if ( geometry.isGeometry ) { var vertices = geometry.vertices; var nbVertices = vertices.length; for ( var i = 0; i < nbVertices - 1; i += step ) { var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); if ( distSq > precisionSq ) continue; interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation var distance = raycaster.ray.origin.distanceTo( interRay ); if ( distance < raycaster.near || distance > raycaster.far ) continue; intersects.push( { distance: distance, // What do we want? intersection point on the ray or on the segment?? // point: raycaster.ray.at( distance ), point: interSegment.clone().applyMatrix4( this.matrixWorld ), index: i, face: null, faceIndex: null, object: this } ); } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function LineSegments( geometry, material ) { Line.call( this, geometry, material ); this.type = 'LineSegments'; } LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { constructor: LineSegments, isLineSegments: true } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * map: new THREE.Texture( ), * * size: , * sizeAttenuation: * } */ function PointsMaterial( parameters ) { Material.call( this ); this.type = 'PointsMaterial'; this.color = new Color( 0xffffff ); this.map = null; this.size = 1; this.sizeAttenuation = true; this.lights = false; this.setValues( parameters ); } PointsMaterial.prototype = Object.create( Material.prototype ); PointsMaterial.prototype.constructor = PointsMaterial; PointsMaterial.prototype.isPointsMaterial = true; PointsMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.size = source.size; this.sizeAttenuation = source.sizeAttenuation; return this; }; /** * @author alteredq / http://alteredqualia.com/ */ function Points( geometry, material ) { Object3D.call( this ); this.type = 'Points'; this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } ); } Points.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Points, isPoints: true, raycast: ( function () { var inverseMatrix = new Matrix4(); var ray = new Ray(); var sphere = new Sphere(); return function raycast( raycaster, intersects ) { var object = this; var geometry = this.geometry; var matrixWorld = this.matrixWorld; var threshold = raycaster.params.Points.threshold; // Checking boundingSphere distance to ray if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); sphere.copy( geometry.boundingSphere ); sphere.applyMatrix4( matrixWorld ); if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; // inverseMatrix.getInverse( matrixWorld ); ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); var localThresholdSq = localThreshold * localThreshold; var position = new Vector3(); function testPoint( point, index ) { var rayPointDistanceSq = ray.distanceSqToPoint( point ); if ( rayPointDistanceSq < localThresholdSq ) { var intersectPoint = ray.closestPointToPoint( point ); intersectPoint.applyMatrix4( matrixWorld ); var distance = raycaster.ray.origin.distanceTo( intersectPoint ); if ( distance < raycaster.near || distance > raycaster.far ) return; intersects.push( { distance: distance, distanceToRay: Math.sqrt( rayPointDistanceSq ), point: intersectPoint.clone(), index: index, face: null, object: object } ); } } if ( geometry.isBufferGeometry ) { var index = geometry.index; var attributes = geometry.attributes; var positions = attributes.position.array; if ( index !== null ) { var indices = index.array; for ( var i = 0, il = indices.length; i < il; i ++ ) { var a = indices[ i ]; position.fromArray( positions, a * 3 ); testPoint( position, a ); } } else { for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { position.fromArray( positions, i * 3 ); testPoint( position, i ); } } } else { var vertices = geometry.vertices; for ( var i = 0, l = vertices.length; i < l; i ++ ) { testPoint( vertices[ i ], i ); } } }; }() ), clone: function () { return new this.constructor( this.geometry, this.material ).copy( this ); } } ); /** * @author mrdoob / http://mrdoob.com/ */ function Group() { Object3D.call( this ); this.type = 'Group'; } Group.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Group } ); /** * @author mrdoob / http://mrdoob.com/ */ function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.generateMipmaps = false; var scope = this; function update() { requestAnimationFrame( update ); if ( video.readyState >= video.HAVE_CURRENT_DATA ) { scope.needsUpdate = true; } } update(); } VideoTexture.prototype = Object.create( Texture.prototype ); VideoTexture.prototype.constructor = VideoTexture; /** * @author alteredq / http://alteredqualia.com/ */ function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); this.image = { width: width, height: height }; this.mipmaps = mipmaps; // no flipping for cube textures // (also flipping doesn't work for compressed textures ) this.flipY = false; // can't generate mipmaps for compressed textures // mips must be embedded in DDS files this.generateMipmaps = false; } CompressedTexture.prototype = Object.create( Texture.prototype ); CompressedTexture.prototype.constructor = CompressedTexture; CompressedTexture.prototype.isCompressedTexture = true; /** * @author mrdoob / http://mrdoob.com/ */ function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.needsUpdate = true; } CanvasTexture.prototype = Object.create( Texture.prototype ); CanvasTexture.prototype.constructor = CanvasTexture; /** * @author Matt DesLauriers / @mattdesl * @author atix / arthursilber.de */ function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { format = format !== undefined ? format : DepthFormat; if ( format !== DepthFormat && format !== DepthStencilFormat ) { throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ) } if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); this.image = { width: width, height: height }; this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; this.flipY = false; this.generateMipmaps = false; } DepthTexture.prototype = Object.create( Texture.prototype ); DepthTexture.prototype.constructor = DepthTexture; DepthTexture.prototype.isDepthTexture = true; /** * @author mrdoob / http://mrdoob.com/ */ function WireframeGeometry( geometry ) { BufferGeometry.call( this ); var edge = [ 0, 0 ], hash = {}; function sortFunction( a, b ) { return a - b; } var keys = [ 'a', 'b', 'c' ]; if ( geometry && geometry.isGeometry ) { var vertices = geometry.vertices; var faces = geometry.faces; var numEdges = 0; // allocate maximal size var edges = new Uint32Array( 6 * faces.length ); for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0; j < 3; j ++ ) { edge[ 0 ] = face[ keys[ j ] ]; edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; edge.sort( sortFunction ); var key = edge.toString(); if ( hash[ key ] === undefined ) { edges[ 2 * numEdges ] = edge[ 0 ]; edges[ 2 * numEdges + 1 ] = edge[ 1 ]; hash[ key ] = true; numEdges ++; } } } var coords = new Float32Array( numEdges * 2 * 3 ); for ( var i = 0, l = numEdges; i < l; i ++ ) { for ( var j = 0; j < 2; j ++ ) { var vertex = vertices[ edges [ 2 * i + j ] ]; var index = 6 * i + 3 * j; coords[ index + 0 ] = vertex.x; coords[ index + 1 ] = vertex.y; coords[ index + 2 ] = vertex.z; } } this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); } else if ( geometry && geometry.isBufferGeometry ) { if ( geometry.index !== null ) { // Indexed BufferGeometry var indices = geometry.index.array; var vertices = geometry.attributes.position; var groups = geometry.groups; var numEdges = 0; if ( groups.length === 0 ) { geometry.addGroup( 0, indices.length ); } // allocate maximal size var edges = new Uint32Array( 2 * indices.length ); for ( var o = 0, ol = groups.length; o < ol; ++ o ) { var group = groups[ o ]; var start = group.start; var count = group.count; for ( var i = start, il = start + count; i < il; i += 3 ) { for ( var j = 0; j < 3; j ++ ) { edge[ 0 ] = indices[ i + j ]; edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ]; edge.sort( sortFunction ); var key = edge.toString(); if ( hash[ key ] === undefined ) { edges[ 2 * numEdges ] = edge[ 0 ]; edges[ 2 * numEdges + 1 ] = edge[ 1 ]; hash[ key ] = true; numEdges ++; } } } } var coords = new Float32Array( numEdges * 2 * 3 ); for ( var i = 0, l = numEdges; i < l; i ++ ) { for ( var j = 0; j < 2; j ++ ) { var index = 6 * i + 3 * j; var index2 = edges[ 2 * i + j ]; coords[ index + 0 ] = vertices.getX( index2 ); coords[ index + 1 ] = vertices.getY( index2 ); coords[ index + 2 ] = vertices.getZ( index2 ); } } this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); } else { // non-indexed BufferGeometry var vertices = geometry.attributes.position.array; var numEdges = vertices.length / 3; var numTris = numEdges / 3; var coords = new Float32Array( numEdges * 2 * 3 ); for ( var i = 0, l = numTris; i < l; i ++ ) { for ( var j = 0; j < 3; j ++ ) { var index = 18 * i + 6 * j; var index1 = 9 * i + 3 * j; coords[ index + 0 ] = vertices[ index1 ]; coords[ index + 1 ] = vertices[ index1 + 1 ]; coords[ index + 2 ] = vertices[ index1 + 2 ]; var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); coords[ index + 3 ] = vertices[ index2 ]; coords[ index + 4 ] = vertices[ index2 + 1 ]; coords[ index + 5 ] = vertices[ index2 + 2 ]; } } this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); } } } WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); WireframeGeometry.prototype.constructor = WireframeGeometry; /** * @author Mugen87 / https://github.com/Mugen87 * * Parametric Surfaces Geometry * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 */ function ParametricBufferGeometry( func, slices, stacks ) { BufferGeometry.call( this ); this.type = 'ParametricBufferGeometry'; this.parameters = { func: func, slices: slices, stacks: stacks }; // generate vertices and uvs var vertices = []; var uvs = []; var i, j, p; var u, v; var sliceCount = slices + 1; for ( i = 0; i <= stacks; i ++ ) { v = i / stacks; for ( j = 0; j <= slices; j ++ ) { u = j / slices; p = func( u, v ); vertices.push( p.x, p.y, p.z ); uvs.push( u, v ); } } // generate indices var indices = []; var a, b, c, d; for ( i = 0; i < stacks; i ++ ) { for ( j = 0; j < slices; j ++ ) { a = i * sliceCount + j; b = i * sliceCount + j + 1; c = ( i + 1 ) * sliceCount + j + 1; d = ( i + 1 ) * sliceCount + j; // faces one and two indices.push( a, b, d ); indices.push( b, c, d ); } } // build geometry this.setIndex( new ( indices.length > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ) ); this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); // generate normals this.computeVertexNormals(); } ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; /** * @author zz85 / https://github.com/zz85 * * Parametric Surfaces Geometry * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 */ function ParametricGeometry( func, slices, stacks ) { Geometry.call( this ); this.type = 'ParametricGeometry'; this.parameters = { func: func, slices: slices, stacks: stacks }; this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); this.mergeVertices(); } ParametricGeometry.prototype = Object.create( Geometry.prototype ); ParametricGeometry.prototype.constructor = ParametricGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ function PolyhedronBufferGeometry( vertices, indices, radius, detail ) { BufferGeometry.call( this ); this.type = 'PolyhedronBufferGeometry'; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; radius = radius || 1; detail = detail || 0; // default buffer data var vertexBuffer = []; var uvBuffer = []; // the subdivision creates the vertex buffer data subdivide( detail ); // all vertices should lie on a conceptual sphere with a given radius appplyRadius( radius ); // finally, create the uv data generateUVs(); // build non-indexed geometry this.addAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); this.addAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); this.addAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); this.normalizeNormals(); this.boundingSphere = new Sphere( new Vector3(), radius ); // helper functions function subdivide( detail ) { var a = new Vector3(); var b = new Vector3(); var c = new Vector3(); // iterate over all faces and apply a subdivison with the given detail value for ( var i = 0; i < indices.length; i += 3 ) { // get the vertices of the face getVertexByIndex( indices[ i + 0 ], a ); getVertexByIndex( indices[ i + 1 ], b ); getVertexByIndex( indices[ i + 2 ], c ); // perform subdivision subdivideFace( a, b, c, detail ); } } function subdivideFace( a, b, c, detail ) { var cols = Math.pow( 2, detail ); // we use this multidimensional array as a data structure for creating the subdivision var v = []; var i, j; // construct all of the vertices for this subdivision for ( i = 0 ; i <= cols; i ++ ) { v[ i ] = []; var aj = a.clone().lerp( c, i / cols ); var bj = b.clone().lerp( c, i / cols ); var rows = cols - i; for ( j = 0; j <= rows; j ++ ) { if ( j === 0 && i === cols ) { v[ i ][ j ] = aj; } else { v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); } } } // construct all of the faces for ( i = 0; i < cols ; i ++ ) { for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { var k = Math.floor( j / 2 ); if ( j % 2 === 0 ) { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); pushVertex( v[ i ][ k ] ); } else { pushVertex( v[ i ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k + 1 ] ); pushVertex( v[ i + 1 ][ k ] ); } } } } function appplyRadius( radius ) { var vertex = new Vector3(); // iterate over the entire buffer and apply the radius to each vertex for ( var i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; vertex.normalize().multiplyScalar( radius ); vertexBuffer[ i + 0 ] = vertex.x; vertexBuffer[ i + 1 ] = vertex.y; vertexBuffer[ i + 2 ] = vertex.z; } } function generateUVs() { var vertex = new Vector3(); for ( var i = 0; i < vertexBuffer.length; i += 3 ) { vertex.x = vertexBuffer[ i + 0 ]; vertex.y = vertexBuffer[ i + 1 ]; vertex.z = vertexBuffer[ i + 2 ]; var u = azimuth( vertex ) / 2 / Math.PI + 0.5; var v = inclination( vertex ) / Math.PI + 0.5; uvBuffer.push( u, 1 - v ); } correctUVs(); correctSeam(); } function correctSeam() { // handle case when face straddles the seam, see #3269 for ( var i = 0; i < uvBuffer.length; i += 6 ) { // uv data of a single face var x0 = uvBuffer[ i + 0 ]; var x1 = uvBuffer[ i + 2 ]; var x2 = uvBuffer[ i + 4 ]; var max = Math.max( x0, x1, x2 ); var min = Math.min( x0, x1, x2 ); // 0.9 is somewhat arbitrary if ( max > 0.9 && min < 0.1 ) { if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; } } } function pushVertex( vertex ) { vertexBuffer.push( vertex.x, vertex.y, vertex.z ); } function getVertexByIndex( index, vertex ) { var stride = index * 3; vertex.x = vertices[ stride + 0 ]; vertex.y = vertices[ stride + 1 ]; vertex.z = vertices[ stride + 2 ]; } function correctUVs() { var a = new Vector3(); var b = new Vector3(); var c = new Vector3(); var centroid = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); var uvC = new Vector2(); for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); var azi = azimuth( centroid ); correctUV( uvA, j + 0, a, azi ); correctUV( uvB, j + 2, b, azi ); correctUV( uvC, j + 4, c, azi ); } } function correctUV( uv, stride, vector, azimuth ) { if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { uvBuffer[ stride ] = uv.x - 1; } if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; } } // Angle around the Y axis, counter-clockwise when looking from above. function azimuth( vector ) { return Math.atan2( vector.z, - vector.x ); } // Angle above the XZ plane. function inclination( vector ) { return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); } } PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ function TetrahedronBufferGeometry( radius, detail ) { var vertices = [ 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 ]; var indices = [ 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = 'TetrahedronBufferGeometry'; this.parameters = { radius: radius, detail: detail }; } TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley */ function TetrahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = 'TetrahedronGeometry'; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } TetrahedronGeometry.prototype = Object.create( Geometry.prototype ); TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ function OctahedronBufferGeometry( radius,detail ) { var vertices = [ 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 ]; var indices = [ 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = 'OctahedronBufferGeometry'; this.parameters = { radius: radius, detail: detail }; } OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley */ function OctahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = 'OctahedronGeometry'; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } OctahedronGeometry.prototype = Object.create( Geometry.prototype ); OctahedronGeometry.prototype.constructor = OctahedronGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ function IcosahedronBufferGeometry( radius, detail ) { var t = ( 1 + Math.sqrt( 5 ) ) / 2; var vertices = [ - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 ]; var indices = [ 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = 'IcosahedronBufferGeometry'; this.parameters = { radius: radius, detail: detail }; } IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry; /** * @author timothypratley / https://github.com/timothypratley */ function IcosahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = 'IcosahedronGeometry'; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } IcosahedronGeometry.prototype = Object.create( Geometry.prototype ); IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ function DodecahedronBufferGeometry( radius, detail ) { var t = ( 1 + Math.sqrt( 5 ) ) / 2; var r = 1 / t; var vertices = [ // (±1, ±1, ±1) - 1, - 1, - 1, - 1, - 1, 1, - 1, 1, - 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, // (0, ±1/φ, ±φ) 0, - r, - t, 0, - r, t, 0, r, - t, 0, r, t, // (±1/φ, ±φ, 0) - r, - t, 0, - r, t, 0, r, - t, 0, r, t, 0, // (±φ, 0, ±1/φ) - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, r ]; var indices = [ 3, 11, 7, 3, 7, 15, 3, 15, 13, 7, 19, 17, 7, 17, 6, 7, 6, 15, 17, 4, 8, 17, 8, 10, 17, 10, 6, 8, 0, 16, 8, 16, 2, 8, 2, 10, 0, 12, 1, 0, 1, 18, 0, 18, 16, 6, 10, 2, 6, 2, 13, 6, 13, 15, 2, 16, 18, 2, 18, 3, 2, 3, 13, 18, 1, 9, 18, 9, 11, 18, 11, 3, 4, 14, 12, 4, 12, 0, 4, 0, 8, 11, 9, 5, 11, 5, 19, 11, 19, 7, 19, 5, 14, 19, 14, 4, 19, 4, 17, 1, 12, 14, 1, 14, 5, 1, 5, 9 ]; PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); this.type = 'DodecahedronBufferGeometry'; this.parameters = { radius: radius, detail: detail }; } DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry; /** * @author Abe Pazos / https://hamoid.com */ function DodecahedronGeometry( radius, detail ) { Geometry.call( this ); this.type = 'DodecahedronGeometry'; this.parameters = { radius: radius, detail: detail }; this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); this.mergeVertices(); } DodecahedronGeometry.prototype = Object.create( Geometry.prototype ); DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; /** * @author clockworkgeek / https://github.com/clockworkgeek * @author timothypratley / https://github.com/timothypratley * @author WestLangley / http://github.com/WestLangley */ function PolyhedronGeometry( vertices, indices, radius, detail ) { Geometry.call( this ); this.type = 'PolyhedronGeometry'; this.parameters = { vertices: vertices, indices: indices, radius: radius, detail: detail }; this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); this.mergeVertices(); } PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; /** * @author Mugen87 / https://github.com/Mugen87 * * Creates a tube which extrudes along a 3d spline. * */ function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) { BufferGeometry.call( this ); this.type = 'TubeBufferGeometry'; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; tubularSegments = tubularSegments || 64; radius = radius || 1; radialSegments = radialSegments || 8; closed = closed || false; var frames = path.computeFrenetFrames( tubularSegments, closed ); // expose internals this.tangents = frames.tangents; this.normals = frames.normals; this.binormals = frames.binormals; // helper variables var vertex = new Vector3(); var normal = new Vector3(); var uv = new Vector2(); var i, j; // buffer var vertices = []; var normals = []; var uvs = []; var indices = []; // create buffer data generateBufferData(); // build geometry this.setIndex( new ( indices.length > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ) ); this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); // functions function generateBufferData() { for ( i = 0; i < tubularSegments; i ++ ) { generateSegment( i ); } // if the geometry is not closed, generate the last row of vertices and normals // at the regular position on the given path // // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) generateSegment( ( closed === false ) ? tubularSegments : 0 ); // uvs are generated in a separate function. // this makes it easy compute correct values for closed geometries generateUVs(); // finally create faces generateIndices(); } function generateSegment( i ) { // we use getPointAt to sample evenly distributed points from the given path var P = path.getPointAt( i / tubularSegments ); // retrieve corresponding normal and binormal var N = frames.normals[ i ]; var B = frames.binormals[ i ]; // generate normals and vertices for the current segment for ( j = 0; j <= radialSegments; j ++ ) { var v = j / radialSegments * Math.PI * 2; var sin = Math.sin( v ); var cos = - Math.cos( v ); // normal normal.x = ( cos * N.x + sin * B.x ); normal.y = ( cos * N.y + sin * B.y ); normal.z = ( cos * N.z + sin * B.z ); normal.normalize(); normals.push( normal.x, normal.y, normal.z ); // vertex vertex.x = P.x + radius * normal.x; vertex.y = P.y + radius * normal.y; vertex.z = P.z + radius * normal.z; vertices.push( vertex.x, vertex.y, vertex.z ); } } function generateIndices() { for ( j = 1; j <= tubularSegments; j ++ ) { for ( i = 1; i <= radialSegments; i ++ ) { var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); var b = ( radialSegments + 1 ) * j + ( i - 1 ); var c = ( radialSegments + 1 ) * j + i; var d = ( radialSegments + 1 ) * ( j - 1 ) + i; // faces indices.push( a, b, d ); indices.push( b, c, d ); } } } function generateUVs() { for ( i = 0; i <= tubularSegments; i ++ ) { for ( j = 0; j <= radialSegments; j ++ ) { uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.push( uv.x, uv.y ); } } } } TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; /** * @author oosmoxiecode / https://github.com/oosmoxiecode * @author WestLangley / https://github.com/WestLangley * @author zz85 / https://github.com/zz85 * @author miningold / https://github.com/miningold * @author jonobr1 / https://github.com/jonobr1 * * Creates a tube which extrudes along a 3d spline. */ function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) { Geometry.call( this ); this.type = 'TubeGeometry'; this.parameters = { path: path, tubularSegments: tubularSegments, radius: radius, radialSegments: radialSegments, closed: closed }; if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' ); var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); // expose internals this.tangents = bufferGeometry.tangents; this.normals = bufferGeometry.normals; this.binormals = bufferGeometry.binormals; // create geometry this.fromBufferGeometry( bufferGeometry ); this.mergeVertices(); } TubeGeometry.prototype = Object.create( Geometry.prototype ); TubeGeometry.prototype.constructor = TubeGeometry; /** * @author Mugen87 / https://github.com/Mugen87 * * see: http://www.blackpawn.com/texts/pqtorus/ */ function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { BufferGeometry.call( this ); this.type = 'TorusKnotBufferGeometry'; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; radius = radius || 100; tube = tube || 40; tubularSegments = Math.floor( tubularSegments ) || 64; radialSegments = Math.floor( radialSegments ) || 8; p = p || 2; q = q || 3; // used to calculate buffer length var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); var indexCount = radialSegments * tubularSegments * 2 * 3; // buffers var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); // helper variables var i, j, index = 0, indexOffset = 0; var vertex = new Vector3(); var normal = new Vector3(); var uv = new Vector2(); var P1 = new Vector3(); var P2 = new Vector3(); var B = new Vector3(); var T = new Vector3(); var N = new Vector3(); // generate vertices, normals and uvs for ( i = 0; i <= tubularSegments; ++ i ) { // the radian "u" is used to calculate the position on the torus curve of the current tubular segement var u = i / tubularSegments * p * Math.PI * 2; // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions calculatePositionOnCurve( u, p, q, radius, P1 ); calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); // calculate orthonormal basis T.subVectors( P2, P1 ); N.addVectors( P2, P1 ); B.crossVectors( T, N ); N.crossVectors( B, T ); // normalize B, N. T can be ignored, we don't use it B.normalize(); N.normalize(); for ( j = 0; j <= radialSegments; ++ j ) { // now calculate the vertices. they are nothing more than an extrusion of the torus curve. // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. var v = j / radialSegments * Math.PI * 2; var cx = - tube * Math.cos( v ); var cy = tube * Math.sin( v ); // now calculate the final vertex position. // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve vertex.x = P1.x + ( cx * N.x + cy * B.x ); vertex.y = P1.y + ( cx * N.y + cy * B.y ); vertex.z = P1.z + ( cx * N.z + cy * B.z ); // vertex vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) normal.subVectors( vertex, P1 ).normalize(); normals.setXYZ( index, normal.x, normal.y, normal.z ); // uv uv.x = i / tubularSegments; uv.y = j / radialSegments; uvs.setXY( index, uv.x, uv.y ); // increase index index ++; } } // generate indices for ( j = 1; j <= tubularSegments; j ++ ) { for ( i = 1; i <= radialSegments; i ++ ) { // indices var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); var b = ( radialSegments + 1 ) * j + ( i - 1 ); var c = ( radialSegments + 1 ) * j + i; var d = ( radialSegments + 1 ) * ( j - 1 ) + i; // face one indices.setX( indexOffset, a ); indexOffset++; indices.setX( indexOffset, b ); indexOffset++; indices.setX( indexOffset, d ); indexOffset++; // face two indices.setX( indexOffset, b ); indexOffset++; indices.setX( indexOffset, c ); indexOffset++; indices.setX( indexOffset, d ); indexOffset++; } } // build geometry this.setIndex( indices ); this.addAttribute( 'position', vertices ); this.addAttribute( 'normal', normals ); this.addAttribute( 'uv', uvs ); // this function calculates the current position on the torus curve function calculatePositionOnCurve( u, p, q, radius, position ) { var cu = Math.cos( u ); var su = Math.sin( u ); var quOverP = q / p * u; var cs = Math.cos( quOverP ); position.x = radius * ( 2 + cs ) * 0.5 * cu; position.y = radius * ( 2 + cs ) * su * 0.5; position.z = radius * Math.sin( quOverP ) * 0.5; } } TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; /** * @author oosmoxiecode */ function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { Geometry.call( this ); this.type = 'TorusKnotGeometry'; this.parameters = { radius: radius, tube: tube, tubularSegments: tubularSegments, radialSegments: radialSegments, p: p, q: q }; if( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); this.mergeVertices(); } TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { BufferGeometry.call( this ); this.type = 'TorusBufferGeometry'; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; radius = radius || 100; tube = tube || 40; radialSegments = Math.floor( radialSegments ) || 8; tubularSegments = Math.floor( tubularSegments ) || 6; arc = arc || Math.PI * 2; // used to calculate buffer length var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); var indexCount = radialSegments * tubularSegments * 2 * 3; // buffers var indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ); var vertices = new Float32Array( vertexCount * 3 ); var normals = new Float32Array( vertexCount * 3 ); var uvs = new Float32Array( vertexCount * 2 ); // offset variables var vertexBufferOffset = 0; var uvBufferOffset = 0; var indexBufferOffset = 0; // helper variables var center = new Vector3(); var vertex = new Vector3(); var normal = new Vector3(); var j, i; // generate vertices, normals and uvs for ( j = 0; j <= radialSegments; j ++ ) { for ( i = 0; i <= tubularSegments; i ++ ) { var u = i / tubularSegments * arc; var v = j / radialSegments * Math.PI * 2; // vertex vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); vertex.z = tube * Math.sin( v ); vertices[ vertexBufferOffset ] = vertex.x; vertices[ vertexBufferOffset + 1 ] = vertex.y; vertices[ vertexBufferOffset + 2 ] = vertex.z; // this vector is used to calculate the normal center.x = radius * Math.cos( u ); center.y = radius * Math.sin( u ); // normal normal.subVectors( vertex, center ).normalize(); normals[ vertexBufferOffset ] = normal.x; normals[ vertexBufferOffset + 1 ] = normal.y; normals[ vertexBufferOffset + 2 ] = normal.z; // uv uvs[ uvBufferOffset ] = i / tubularSegments; uvs[ uvBufferOffset + 1 ] = j / radialSegments; // update offsets vertexBufferOffset += 3; uvBufferOffset += 2; } } // generate indices for ( j = 1; j <= radialSegments; j ++ ) { for ( i = 1; i <= tubularSegments; i ++ ) { // indices var a = ( tubularSegments + 1 ) * j + i - 1; var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; var d = ( tubularSegments + 1 ) * j + i; // face one indices[ indexBufferOffset ] = a; indices[ indexBufferOffset + 1 ] = b; indices[ indexBufferOffset + 2 ] = d; // face two indices[ indexBufferOffset + 3 ] = b; indices[ indexBufferOffset + 4 ] = c; indices[ indexBufferOffset + 5 ] = d; // update offset indexBufferOffset += 6; } } // build geometry this.setIndex( new BufferAttribute( indices, 1 ) ); this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); } TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; /** * @author oosmoxiecode * @author mrdoob / http://mrdoob.com/ * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 */ function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { Geometry.call( this ); this.type = 'TorusGeometry'; this.parameters = { radius: radius, tube: tube, radialSegments: radialSegments, tubularSegments: tubularSegments, arc: arc }; this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); } TorusGeometry.prototype = Object.create( Geometry.prototype ); TorusGeometry.prototype.constructor = TorusGeometry; /** * @author zz85 / http://www.lab4games.net/zz85/blog */ var ShapeUtils = { // calculate area of the contour polygon area: function ( contour ) { var n = contour.length; var a = 0.0; for ( var p = n - 1, q = 0; q < n; p = q ++ ) { a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; } return a * 0.5; }, triangulate: ( function () { /** * This code is a quick port of code written in C++ which was submitted to * flipcode.com by John W. Ratcliff // July 22, 2000 * See original code and more information here: * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml * * ported to actionscript by Zevan Rosser * www.actionsnippet.com * * ported to javascript by Joshua Koo * http://www.lab4games.net/zz85/blog * */ function snip( contour, u, v, w, n, verts ) { var p; var ax, ay, bx, by; var cx, cy, px, py; ax = contour[ verts[ u ] ].x; ay = contour[ verts[ u ] ].y; bx = contour[ verts[ v ] ].x; by = contour[ verts[ v ] ].y; cx = contour[ verts[ w ] ].x; cy = contour[ verts[ w ] ].y; if ( ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax ) <= 0 ) return false; var aX, aY, bX, bY, cX, cY; var apx, apy, bpx, bpy, cpx, cpy; var cCROSSap, bCROSScp, aCROSSbp; aX = cx - bx; aY = cy - by; bX = ax - cx; bY = ay - cy; cX = bx - ax; cY = by - ay; for ( p = 0; p < n; p ++ ) { px = contour[ verts[ p ] ].x; py = contour[ verts[ p ] ].y; if ( ( ( px === ax ) && ( py === ay ) ) || ( ( px === bx ) && ( py === by ) ) || ( ( px === cx ) && ( py === cy ) ) ) continue; apx = px - ax; apy = py - ay; bpx = px - bx; bpy = py - by; cpx = px - cx; cpy = py - cy; // see if p is inside triangle abc aCROSSbp = aX * bpy - aY * bpx; cCROSSap = cX * apy - cY * apx; bCROSScp = bX * cpy - bY * cpx; if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false; } return true; } // takes in an contour array and returns return function triangulate( contour, indices ) { var n = contour.length; if ( n < 3 ) return null; var result = [], verts = [], vertIndices = []; /* we want a counter-clockwise polygon in verts */ var u, v, w; if ( ShapeUtils.area( contour ) > 0.0 ) { for ( v = 0; v < n; v ++ ) verts[ v ] = v; } else { for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; } var nv = n; /* remove nv - 2 vertices, creating 1 triangle every time */ var count = 2 * nv; /* error detection */ for ( v = nv - 1; nv > 2; ) { /* if we loop, it is probably a non-simple polygon */ if ( ( count -- ) <= 0 ) { //** Triangulate: ERROR - probable bad polygon! //throw ( "Warning, unable to triangulate polygon!" ); //return null; // Sometimes warning is fine, especially polygons are triangulated in reverse. console.warn( 'THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()' ); if ( indices ) return vertIndices; return result; } /* three consecutive vertices in current polygon, */ u = v; if ( nv <= u ) u = 0; /* previous */ v = u + 1; if ( nv <= v ) v = 0; /* new v */ w = v + 1; if ( nv <= w ) w = 0; /* next */ if ( snip( contour, u, v, w, nv, verts ) ) { var a, b, c, s, t; /* true names of the vertices */ a = verts[ u ]; b = verts[ v ]; c = verts[ w ]; /* output Triangle */ result.push( [ contour[ a ], contour[ b ], contour[ c ] ] ); vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); /* remove v from the remaining polygon */ for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { verts[ s ] = verts[ t ]; } nv --; /* reset error detection counter */ count = 2 * nv; } } if ( indices ) return vertIndices; return result; } } )(), triangulateShape: function ( contour, holes ) { function removeDupEndPts(points) { var l = points.length; if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { points.pop(); } } removeDupEndPts( contour ); holes.forEach( removeDupEndPts ); function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { // inOtherPt needs to be collinear to the inSegment if ( inSegPt1.x !== inSegPt2.x ) { if ( inSegPt1.x < inSegPt2.x ) { return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); } else { return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); } } else { if ( inSegPt1.y < inSegPt2.y ) { return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); } else { return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); } } } function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; var limit = seg1dy * seg2dx - seg1dx * seg2dy; var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; if ( Math.abs( limit ) > Number.EPSILON ) { // not parallel var perpSeg2; if ( limit > 0 ) { if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; } else { if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; } // i.e. to reduce rounding errors // intersection at endpoint of segment#1? if ( perpSeg2 === 0 ) { if ( ( inExcludeAdjacentSegs ) && ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; return [ inSeg1Pt1 ]; } if ( perpSeg2 === limit ) { if ( ( inExcludeAdjacentSegs ) && ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; return [ inSeg1Pt2 ]; } // intersection at endpoint of segment#2? if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; // return real intersection point var factorSeg1 = perpSeg2 / limit; return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; } else { // parallel or collinear if ( ( perpSeg1 !== 0 ) || ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return []; // they are collinear or degenerate var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point? var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point? // both segments are points if ( seg1Pt && seg2Pt ) { if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) || ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points return [ inSeg1Pt1 ]; // they are the same point } // segment#1 is a single point if ( seg1Pt ) { if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 return [ inSeg1Pt1 ]; } // segment#2 is a single point if ( seg2Pt ) { if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 return [ inSeg2Pt1 ]; } // they are collinear segments, which might overlap var seg1min, seg1max, seg1minVal, seg1maxVal; var seg2min, seg2max, seg2minVal, seg2maxVal; if ( seg1dx !== 0 ) { // the segments are NOT on a vertical line if ( inSeg1Pt1.x < inSeg1Pt2.x ) { seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; } else { seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; } if ( inSeg2Pt1.x < inSeg2Pt2.x ) { seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; } else { seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; } } else { // the segments are on a vertical line if ( inSeg1Pt1.y < inSeg1Pt2.y ) { seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; } else { seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; } if ( inSeg2Pt1.y < inSeg2Pt2.y ) { seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; } else { seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; } } if ( seg1minVal <= seg2minVal ) { if ( seg1maxVal < seg2minVal ) return []; if ( seg1maxVal === seg2minVal ) { if ( inExcludeAdjacentSegs ) return []; return [ seg2min ]; } if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; return [ seg2min, seg2max ]; } else { if ( seg1minVal > seg2maxVal ) return []; if ( seg1minVal === seg2maxVal ) { if ( inExcludeAdjacentSegs ) return []; return [ seg1min ]; } if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; return [ seg1min, seg2max ]; } } } function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { // The order of legs is important // translation of all points, so that Vertex is at (0,0) var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; if ( Math.abs( from2toAngle ) > Number.EPSILON ) { // angle != 180 deg. var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); if ( from2toAngle > 0 ) { // main angle < 180 deg. return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); } else { // main angle > 180 deg. return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); } } else { // angle == 180 deg. // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); return ( from2otherAngle > 0 ); } } function removeHoles( contour, holes ) { var shape = contour.concat(); // work on this shape var hole; function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { // Check if hole point lies within angle around shape point var lastShapeIdx = shape.length - 1; var prevShapeIdx = inShapeIdx - 1; if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; var nextShapeIdx = inShapeIdx + 1; if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] ); if ( ! insideAngle ) { // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); return false; } // Check if shape point lies within angle around hole point var lastHoleIdx = hole.length - 1; var prevHoleIdx = inHoleIdx - 1; if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; var nextHoleIdx = inHoleIdx + 1; if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); if ( ! insideAngle ) { // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); return false; } return true; } function intersectsShapeEdge( inShapePt, inHolePt ) { // checks for intersections with shape edges var sIdx, nextIdx, intersection; for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { nextIdx = sIdx + 1; nextIdx %= shape.length; intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true ); if ( intersection.length > 0 ) return true; } return false; } var indepHoles = []; function intersectsHoleEdge( inShapePt, inHolePt ) { // checks for intersections with hole edges var ihIdx, chkHole, hIdx, nextIdx, intersection; for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { chkHole = holes[ indepHoles[ ihIdx ]]; for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { nextIdx = hIdx + 1; nextIdx %= chkHole.length; intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); if ( intersection.length > 0 ) return true; } } return false; } var holeIndex, shapeIndex, shapePt, holePt, holeIdx, cutKey, failedCuts = [], tmpShape1, tmpShape2, tmpHole1, tmpHole2; for ( var h = 0, hl = holes.length; h < hl; h ++ ) { indepHoles.push( h ); } var minShapeIndex = 0; var counter = indepHoles.length * 2; while ( indepHoles.length > 0 ) { counter --; if ( counter < 0 ) { console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); break; } // search for shape-vertex and hole-vertex, // which can be connected without intersections for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { shapePt = shape[ shapeIndex ]; holeIndex = - 1; // search for hole which can be reached without intersections for ( var h = 0; h < indepHoles.length; h ++ ) { holeIdx = indepHoles[ h ]; // prevent multiple checks cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; if ( failedCuts[ cutKey ] !== undefined ) continue; hole = holes[ holeIdx ]; for ( var h2 = 0; h2 < hole.length; h2 ++ ) { holePt = hole[ h2 ]; if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; if ( intersectsShapeEdge( shapePt, holePt ) ) continue; if ( intersectsHoleEdge( shapePt, holePt ) ) continue; holeIndex = h2; indepHoles.splice( h, 1 ); tmpShape1 = shape.slice( 0, shapeIndex + 1 ); tmpShape2 = shape.slice( shapeIndex ); tmpHole1 = hole.slice( holeIndex ); tmpHole2 = hole.slice( 0, holeIndex + 1 ); shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); minShapeIndex = shapeIndex; // Debug only, to show the selected cuts // glob_CutLines.push( [ shapePt, holePt ] ); break; } if ( holeIndex >= 0 ) break; // hole-vertex found failedCuts[ cutKey ] = true; // remember failure } if ( holeIndex >= 0 ) break; // hole-vertex found } } return shape; /* shape with no holes */ } var i, il, f, face, key, index, allPointsMap = {}; // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. var allpoints = contour.concat(); for ( var h = 0, hl = holes.length; h < hl; h ++ ) { Array.prototype.push.apply( allpoints, holes[ h ] ); } //console.log( "allpoints",allpoints, allpoints.length ); // prepare all points map for ( i = 0, il = allpoints.length; i < il; i ++ ) { key = allpoints[ i ].x + ":" + allpoints[ i ].y; if ( allPointsMap[ key ] !== undefined ) { console.warn( "THREE.ShapeUtils: Duplicate point", key, i ); } allPointsMap[ key ] = i; } // remove holes by cutting paths to holes and adding them to the shape var shapeWithoutHoles = removeHoles( contour, holes ); var triangles = ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape //console.log( "triangles",triangles, triangles.length ); // check all face vertices against all points map for ( i = 0, il = triangles.length; i < il; i ++ ) { face = triangles[ i ]; for ( f = 0; f < 3; f ++ ) { key = face[ f ].x + ":" + face[ f ].y; index = allPointsMap[ key ]; if ( index !== undefined ) { face[ f ] = index; } } } return triangles.concat(); }, isClockWise: function ( pts ) { return ShapeUtils.area( pts ) < 0; }, // Bezier Curves formulas obtained from // http://en.wikipedia.org/wiki/B%C3%A9zier_curve // Quad Bezier Functions b2: ( function () { function b2p0( t, p ) { var k = 1 - t; return k * k * p; } function b2p1( t, p ) { return 2 * ( 1 - t ) * t * p; } function b2p2( t, p ) { return t * t * p; } return function b2( t, p0, p1, p2 ) { return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 ); }; } )(), // Cubic Bezier Functions b3: ( function () { function b3p0( t, p ) { var k = 1 - t; return k * k * k * p; } function b3p1( t, p ) { var k = 1 - t; return 3 * k * k * t * p; } function b3p2( t, p ) { var k = 1 - t; return 3 * k * t * t * p; } function b3p3( t, p ) { return t * t * t * p; } return function b3( t, p0, p1, p2, p3 ) { return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 ); }; } )() }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * * Creates extruded geometry from a path shape. * * parameters = { * * curveSegments: , // number of points on the curves * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too * amount: , // Depth to extrude the shape * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into the original shape bevel goes * bevelSize: , // how far from shape outline is bevel * bevelSegments: , // number of bevel layers * * extrudePath: // curve to extrude shape along * frames: // containing arrays of tangents, normals, binormals * * uvGenerator: // object that provides UV generator functions * * } **/ function ExtrudeGeometry( shapes, options ) { if ( typeof( shapes ) === "undefined" ) { shapes = []; return; } Geometry.call( this ); this.type = 'ExtrudeGeometry'; shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; this.addShapeList( shapes, options ); this.computeFaceNormals(); // can't really use automatic vertex normals // as then front and back sides get smoothed too // should do separate smoothing just for sides //this.computeVertexNormals(); //console.log( "took", ( Date.now() - startTime ) ); } ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { var sl = shapes.length; for ( var s = 0; s < sl; s ++ ) { var shape = shapes[ s ]; this.addShape( shape, options ); } }; ExtrudeGeometry.prototype.addShape = function ( shape, options ) { var amount = options.amount !== undefined ? options.amount : 100; var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; var steps = options.steps !== undefined ? options.steps : 1; var extrudePath = options.extrudePath; var extrudePts, extrudeByPath = false; // Use default WorldUVGenerator if no UV generators are specified. var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator; var splineTube, binormal, normal, position2; if ( extrudePath ) { extrudePts = extrudePath.getSpacedPoints( steps ); extrudeByPath = true; bevelEnabled = false; // bevels not supported for path extrusion // SETUP TNB variables // TODO1 - have a .isClosed in spline? splineTube = options.frames !== undefined ? options.frames : extrudePath.computeFrenetFrames( steps, false ); // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); binormal = new Vector3(); normal = new Vector3(); position2 = new Vector3(); } // Safeguards if bevels are not enabled if ( ! bevelEnabled ) { bevelSegments = 0; bevelThickness = 0; bevelSize = 0; } // Variables initialization var ahole, h, hl; // looping of holes var scope = this; var shapesOffset = this.vertices.length; var shapePoints = shape.extractPoints( curveSegments ); var vertices = shapePoints.shape; var holes = shapePoints.holes; var reverse = ! ShapeUtils.isClockWise( vertices ); if ( reverse ) { vertices = vertices.reverse(); // Maybe we should also check if holes are in the opposite direction, just to be safe ... for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; if ( ShapeUtils.isClockWise( ahole ) ) { holes[ h ] = ahole.reverse(); } } reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! } var faces = ShapeUtils.triangulateShape( vertices, holes ); /* Vertices */ var contour = vertices; // vertices has all points but contour has only points of circumference for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; vertices = vertices.concat( ahole ); } function scalePt2( pt, vec, size ) { if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); return vec.clone().multiplyScalar( size ).add( pt ); } var b, bs, t, z, vert, vlen = vertices.length, face, flen = faces.length; // Find directions for point movement function getBevelVec( inPt, inPrev, inNext ) { // computes for inPt the corresponding point inPt' on a new contour // shifted by 1 unit (length of normalized vector) to the left // if we walk along contour clockwise, this new contour is outside the old one // // inPt' is the intersection of the two lines parallel to the two // adjacent edges of inPt at a distance of 1 unit on the left side. var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt // good reading for geometry algorithms (here: line-line intersection) // http://geomalgorithms.com/a05-_intersect-1.html var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); // check for collinear edges var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); if ( Math.abs( collinear0 ) > Number.EPSILON ) { // not collinear // length of vectors for normalizing var v_prev_len = Math.sqrt( v_prev_lensq ); var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); // shift adjacent points by unit vectors to the left var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); // scaling factor for v_prev to intersection point var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / ( v_prev_x * v_next_y - v_prev_y * v_next_x ); // vector from inPt to intersection point v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); // Don't normalize!, otherwise sharp corners become ugly // but prevent crazy spikes var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); if ( v_trans_lensq <= 2 ) { return new Vector2( v_trans_x, v_trans_y ); } else { shrink_by = Math.sqrt( v_trans_lensq / 2 ); } } else { // handle special case of collinear edges var direction_eq = false; // assumes: opposite if ( v_prev_x > Number.EPSILON ) { if ( v_next_x > Number.EPSILON ) { direction_eq = true; } } else { if ( v_prev_x < - Number.EPSILON ) { if ( v_next_x < - Number.EPSILON ) { direction_eq = true; } } else { if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { direction_eq = true; } } } if ( direction_eq ) { // console.log("Warning: lines are a straight sequence"); v_trans_x = - v_prev_y; v_trans_y = v_prev_x; shrink_by = Math.sqrt( v_prev_lensq ); } else { // console.log("Warning: lines are a straight spike"); v_trans_x = v_prev_x; v_trans_y = v_prev_y; shrink_by = Math.sqrt( v_prev_lensq / 2 ); } } return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); } var contourMovements = []; for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) // console.log('i,j,k', i, j , k) contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); } var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = []; for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { if ( j === il ) j = 0; if ( k === il ) k = 0; // (j)---(i)---(k) oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); } holesMovements.push( oneHoleMovements ); verticesMovements = verticesMovements.concat( oneHoleMovements ); } // Loop bevelSegments, 1 for the front, 1 for the back for ( b = 0; b < bevelSegments; b ++ ) { //for ( b = bevelSegments; b > 0; b -- ) { t = b / bevelSegments; z = bevelThickness * Math.cos( t * Math.PI / 2 ); bs = bevelSize * Math.sin( t * Math.PI / 2 ); // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, - z ); } // expand holes for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0, il = ahole.length; i < il; i ++ ) { vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); v( vert.x, vert.y, - z ); } } } bs = bevelSize; // Back facing vertices for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, 0 ); } else { // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } // Add stepped vertices... // Including front facing vertices var s; for ( s = 1; s <= steps; s ++ ) { for ( i = 0; i < vlen; i ++ ) { vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; if ( ! extrudeByPath ) { v( vert.x, vert.y, amount / steps * s ); } else { // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); v( position2.x, position2.y, position2.z ); } } } // Add bevel segments planes //for ( b = 1; b <= bevelSegments; b ++ ) { for ( b = bevelSegments - 1; b >= 0; b -- ) { t = b / bevelSegments; z = bevelThickness * Math.cos ( t * Math.PI / 2 ); bs = bevelSize * Math.sin( t * Math.PI / 2 ); // contract shape for ( i = 0, il = contour.length; i < il; i ++ ) { vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); v( vert.x, vert.y, amount + z ); } // expand holes for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; oneHoleMovements = holesMovements[ h ]; for ( i = 0, il = ahole.length; i < il; i ++ ) { vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); if ( ! extrudeByPath ) { v( vert.x, vert.y, amount + z ); } else { v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); } } } } /* Faces */ // Top and bottom faces buildLidFaces(); // Sides faces buildSideFaces(); ///// Internal functions function buildLidFaces() { if ( bevelEnabled ) { var layer = 0; // steps + 1 var offset = vlen * layer; // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); } layer = steps + bevelSegments * 2; offset = vlen * layer; // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); } } else { // Bottom faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 2 ], face[ 1 ], face[ 0 ] ); } // Top faces for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); } } } // Create faces for the z-sides of the shape function buildSideFaces() { var layeroffset = 0; sidewalls( contour, layeroffset ); layeroffset += contour.length; for ( h = 0, hl = holes.length; h < hl; h ++ ) { ahole = holes[ h ]; sidewalls( ahole, layeroffset ); //, true layeroffset += ahole.length; } } function sidewalls( contour, layeroffset ) { var j, k; i = contour.length; while ( -- i >= 0 ) { j = i; k = i - 1; if ( k < 0 ) k = contour.length - 1; //console.log('b', i,j, i-1, k,vertices.length); var s = 0, sl = steps + bevelSegments * 2; for ( s = 0; s < sl; s ++ ) { var slen1 = vlen * s; var slen2 = vlen * ( s + 1 ); var a = layeroffset + j + slen1, b = layeroffset + k + slen1, c = layeroffset + k + slen2, d = layeroffset + j + slen2; f4( a, b, c, d, contour, s, sl, j, k ); } } } function v( x, y, z ) { scope.vertices.push( new Vector3( x, y, z ) ); } function f3( a, b, c ) { a += shapesOffset; b += shapesOffset; c += shapesOffset; scope.faces.push( new Face3( a, b, c, null, null, 0 ) ); var uvs = uvgen.generateTopUV( scope, a, b, c ); scope.faceVertexUvs[ 0 ].push( uvs ); } function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { a += shapesOffset; b += shapesOffset; c += shapesOffset; d += shapesOffset; scope.faces.push( new Face3( a, b, d, null, null, 1 ) ); scope.faces.push( new Face3( b, c, d, null, null, 1 ) ); var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); } }; ExtrudeGeometry.WorldUVGenerator = { generateTopUV: function ( geometry, indexA, indexB, indexC ) { var vertices = geometry.vertices; var a = vertices[ indexA ]; var b = vertices[ indexB ]; var c = vertices[ indexC ]; return [ new Vector2( a.x, a.y ), new Vector2( b.x, b.y ), new Vector2( c.x, c.y ) ]; }, generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { var vertices = geometry.vertices; var a = vertices[ indexA ]; var b = vertices[ indexB ]; var c = vertices[ indexC ]; var d = vertices[ indexD ]; if ( Math.abs( a.y - b.y ) < 0.01 ) { return [ new Vector2( a.x, 1 - a.z ), new Vector2( b.x, 1 - b.z ), new Vector2( c.x, 1 - c.z ), new Vector2( d.x, 1 - d.z ) ]; } else { return [ new Vector2( a.y, 1 - a.z ), new Vector2( b.y, 1 - b.z ), new Vector2( c.y, 1 - c.z ), new Vector2( d.y, 1 - d.z ) ]; } } }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author alteredq / http://alteredqualia.com/ * * Text = 3D Text * * parameters = { * font: , // font * * size: , // size of the text * height: , // thickness to extrude text * curveSegments: , // number of points on the curves * * bevelEnabled: , // turn on bevel * bevelThickness: , // how deep into text bevel goes * bevelSize: // how far from text outline is bevel * } */ function TextGeometry( text, parameters ) { parameters = parameters || {}; var font = parameters.font; if ( (font && font.isFont) === false ) { console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); return new Geometry(); } var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); // translate parameters to ExtrudeGeometry API parameters.amount = parameters.height !== undefined ? parameters.height : 50; // defaults if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; ExtrudeGeometry.call( this, shapes, parameters ); this.type = 'TextGeometry'; } TextGeometry.prototype = Object.create( ExtrudeGeometry.prototype ); TextGeometry.prototype.constructor = TextGeometry; /** * @author benaadams / https://twitter.com/ben_a_adams * based on THREE.SphereGeometry */ function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = 'SphereBufferGeometry'; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; radius = radius || 50; widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); phiStart = phiStart !== undefined ? phiStart : 0; phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; var thetaEnd = thetaStart + thetaLength; var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) ); var positions = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); var index = 0, vertices = [], normal = new Vector3(); for ( var y = 0; y <= heightSegments; y ++ ) { var verticesRow = []; var v = y / heightSegments; for ( var x = 0; x <= widthSegments; x ++ ) { var u = x / widthSegments; var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); var py = radius * Math.cos( thetaStart + v * thetaLength ); var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); normal.set( px, py, pz ).normalize(); positions.setXYZ( index, px, py, pz ); normals.setXYZ( index, normal.x, normal.y, normal.z ); uvs.setXY( index, u, 1 - v ); verticesRow.push( index ); index ++; } vertices.push( verticesRow ); } var indices = []; for ( var y = 0; y < heightSegments; y ++ ) { for ( var x = 0; x < widthSegments; x ++ ) { var v1 = vertices[ y ][ x + 1 ]; var v2 = vertices[ y ][ x ]; var v3 = vertices[ y + 1 ][ x ]; var v4 = vertices[ y + 1 ][ x + 1 ]; if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 ); if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 ); } } this.setIndex( new ( positions.count > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ) ); this.addAttribute( 'position', positions ); this.addAttribute( 'normal', normals ); this.addAttribute( 'uv', uvs ); this.boundingSphere = new Sphere( new Vector3(), radius ); } SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ */ function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { Geometry.call( this ); this.type = 'SphereGeometry'; this.parameters = { radius: radius, widthSegments: widthSegments, heightSegments: heightSegments, phiStart: phiStart, phiLength: phiLength, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); } SphereGeometry.prototype = Object.create( Geometry.prototype ); SphereGeometry.prototype.constructor = SphereGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = 'RingBufferGeometry'; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; innerRadius = innerRadius || 20; outerRadius = outerRadius || 50; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; // these are used to calculate buffer length var vertexCount = ( thetaSegments + 1 ) * ( phiSegments + 1 ); var indexCount = thetaSegments * phiSegments * 2 * 3; // buffers var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); // some helper variables var index = 0, indexOffset = 0, segment; var radius = innerRadius; var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); var vertex = new Vector3(); var uv = new Vector2(); var j, i; // generate vertices, normals and uvs // values are generate from the inside of the ring to the outside for ( j = 0; j <= phiSegments; j ++ ) { for ( i = 0; i <= thetaSegments; i ++ ) { segment = thetaStart + i / thetaSegments * thetaLength; // vertex vertex.x = radius * Math.cos( segment ); vertex.y = radius * Math.sin( segment ); vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); // normal normals.setXYZ( index, 0, 0, 1 ); // uv uv.x = ( vertex.x / outerRadius + 1 ) / 2; uv.y = ( vertex.y / outerRadius + 1 ) / 2; uvs.setXY( index, uv.x, uv.y ); // increase index index++; } // increase the radius for next row of vertices radius += radiusStep; } // generate indices for ( j = 0; j < phiSegments; j ++ ) { var thetaSegmentLevel = j * ( thetaSegments + 1 ); for ( i = 0; i < thetaSegments; i ++ ) { segment = i + thetaSegmentLevel; // indices var a = segment; var b = segment + thetaSegments + 1; var c = segment + thetaSegments + 2; var d = segment + 1; // face one indices.setX( indexOffset, a ); indexOffset++; indices.setX( indexOffset, b ); indexOffset++; indices.setX( indexOffset, c ); indexOffset++; // face two indices.setX( indexOffset, a ); indexOffset++; indices.setX( indexOffset, c ); indexOffset++; indices.setX( indexOffset, d ); indexOffset++; } } // build geometry this.setIndex( indices ); this.addAttribute( 'position', vertices ); this.addAttribute( 'normal', normals ); this.addAttribute( 'uv', uvs ); } RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); RingBufferGeometry.prototype.constructor = RingBufferGeometry; /** * @author Kaleb Murphy */ function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { Geometry.call( this ); this.type = 'RingGeometry'; this.parameters = { innerRadius: innerRadius, outerRadius: outerRadius, thetaSegments: thetaSegments, phiSegments: phiSegments, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); } RingGeometry.prototype = Object.create( Geometry.prototype ); RingGeometry.prototype.constructor = RingGeometry; /** * @author mrdoob / http://mrdoob.com/ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as */ function PlaneGeometry( width, height, widthSegments, heightSegments ) { Geometry.call( this ); this.type = 'PlaneGeometry'; this.parameters = { width: width, height: height, widthSegments: widthSegments, heightSegments: heightSegments }; this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); } PlaneGeometry.prototype = Object.create( Geometry.prototype ); PlaneGeometry.prototype.constructor = PlaneGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ // points - to create a closed torus, one must use a set of points // like so: [ a, b, c, d, a ], see first is the same as last. // segments - the number of circumference segments to create // phiStart - the starting radian // phiLength - the radian (0 to 2PI) range of the lathed section // 2PI is a closed lathe, less than 2PI is a portion. function LatheBufferGeometry( points, segments, phiStart, phiLength ) { BufferGeometry.call( this ); this.type = 'LatheBufferGeometry'; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; segments = Math.floor( segments ) || 12; phiStart = phiStart || 0; phiLength = phiLength || Math.PI * 2; // clamp phiLength so it's in range of [ 0, 2PI ] phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 ); // these are used to calculate buffer length var vertexCount = ( segments + 1 ) * points.length; var indexCount = segments * points.length * 2 * 3; // buffers var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); // helper variables var index = 0, indexOffset = 0, base; var inverseSegments = 1.0 / segments; var vertex = new Vector3(); var uv = new Vector2(); var i, j; // generate vertices and uvs for ( i = 0; i <= segments; i ++ ) { var phi = phiStart + i * inverseSegments * phiLength; var sin = Math.sin( phi ); var cos = Math.cos( phi ); for ( j = 0; j <= ( points.length - 1 ); j ++ ) { // vertex vertex.x = points[ j ].x * sin; vertex.y = points[ j ].y; vertex.z = points[ j ].x * cos; vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); // uv uv.x = i / segments; uv.y = j / ( points.length - 1 ); uvs.setXY( index, uv.x, uv.y ); // increase index index ++; } } // generate indices for ( i = 0; i < segments; i ++ ) { for ( j = 0; j < ( points.length - 1 ); j ++ ) { base = j + i * points.length; // indices var a = base; var b = base + points.length; var c = base + points.length + 1; var d = base + 1; // face one indices.setX( indexOffset, a ); indexOffset++; indices.setX( indexOffset, b ); indexOffset++; indices.setX( indexOffset, d ); indexOffset++; // face two indices.setX( indexOffset, b ); indexOffset++; indices.setX( indexOffset, c ); indexOffset++; indices.setX( indexOffset, d ); indexOffset++; } } // build geometry this.setIndex( indices ); this.addAttribute( 'position', vertices ); this.addAttribute( 'uv', uvs ); // generate normals this.computeVertexNormals(); // if the geometry is closed, we need to average the normals along the seam. // because the corresponding vertices are identical (but still have different UVs). if( phiLength === Math.PI * 2 ) { var normals = this.attributes.normal.array; var n1 = new Vector3(); var n2 = new Vector3(); var n = new Vector3(); // this is the buffer offset for the last line of vertices base = segments * points.length * 3; for( i = 0, j = 0; i < points.length; i ++, j += 3 ) { // select the normal of the vertex in the first line n1.x = normals[ j + 0 ]; n1.y = normals[ j + 1 ]; n1.z = normals[ j + 2 ]; // select the normal of the vertex in the last line n2.x = normals[ base + j + 0 ]; n2.y = normals[ base + j + 1 ]; n2.z = normals[ base + j + 2 ]; // average normals n.addVectors( n1, n2 ).normalize(); // assign the new values to both normals normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; } // next row } } LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; /** * @author astrodud / http://astrodud.isgreat.org/ * @author zz85 / https://github.com/zz85 * @author bhouston / http://clara.io */ // points - to create a closed torus, one must use a set of points // like so: [ a, b, c, d, a ], see first is the same as last. // segments - the number of circumference segments to create // phiStart - the starting radian // phiLength - the radian (0 to 2PI) range of the lathed section // 2PI is a closed lathe, less than 2PI is a portion. function LatheGeometry( points, segments, phiStart, phiLength ) { Geometry.call( this ); this.type = 'LatheGeometry'; this.parameters = { points: points, segments: segments, phiStart: phiStart, phiLength: phiLength }; this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); this.mergeVertices(); } LatheGeometry.prototype = Object.create( Geometry.prototype ); LatheGeometry.prototype.constructor = LatheGeometry; /** * @author Mugen87 / https://github.com/Mugen87 * * Creates a one-sided polygonal geometry from one or more shapes. * **/ function ShapeBufferGeometry( shapes, curveSegments ) { BufferGeometry.call( this ); this.type = 'ShapeBufferGeometry'; this.parameters = { shapes: shapes, curveSegments: curveSegments }; curveSegments = curveSegments || 12; var vertices = []; var normals = []; var uvs = []; var indices = []; var groupStart = 0; var groupCount = 0; // allow single and array values for "shapes" parameter if ( Array.isArray( shapes ) === false ) { addShape( shapes ); } else { for ( var i = 0; i < shapes.length; i++ ) { addShape( shapes[ i ] ); this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support groupStart += groupCount; groupCount = 0; } } // build geometry this.setIndex( new ( indices.length > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ) ); this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); // helper functions function addShape( shape ) { var i, l, shapeHole; var indexOffset = vertices.length / 3; var points = shape.extractPoints( curveSegments ); var shapeVertices = points.shape; var shapeHoles = points.holes; // check direction of vertices if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { shapeVertices = shapeVertices.reverse(); // also check if holes are in the opposite direction for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { shapeHole = shapeHoles[ i ]; if ( ShapeUtils.isClockWise( shapeHole ) === true ) { shapeHoles[ i ] = shapeHole.reverse(); } } } var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); // join vertices of inner and outer paths to a single array for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { shapeHole = shapeHoles[ i ]; shapeVertices = shapeVertices.concat( shapeHole ); } // vertices, normals, uvs for ( i = 0, l = shapeVertices.length; i < l; i ++ ) { var vertex = shapeVertices[ i ]; vertices.push( vertex.x, vertex.y, 0 ); normals.push( 0, 0, 1 ); uvs.push( vertex.x, vertex.y ); // world uvs } // incides for ( i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; var a = face[ 0 ] + indexOffset; var b = face[ 1 ] + indexOffset; var c = face[ 2 ] + indexOffset; indices.push( a, b, c ); groupCount += 3; } } } ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry; /** * @author jonobr1 / http://jonobr1.com * * Creates a one-sided polygonal geometry from a path shape. * **/ function ShapeGeometry( shapes, curveSegments ) { Geometry.call( this ); this.type = 'ShapeGeometry'; if ( typeof curveSegments === 'object' ) { console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' ); curveSegments = curveSegments.curveSegments; } this.parameters = { shapes: shapes, curveSegments: curveSegments }; this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); this.mergeVertices(); } ShapeGeometry.prototype = Object.create( Geometry.prototype ); ShapeGeometry.prototype.constructor = ShapeGeometry; /** * @author WestLangley / http://github.com/WestLangley */ function EdgesGeometry( geometry, thresholdAngle ) { BufferGeometry.call( this ); thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle ); var edge = [ 0, 0 ], hash = {}; function sortFunction( a, b ) { return a - b; } var keys = [ 'a', 'b', 'c' ]; var geometry2; if ( geometry.isBufferGeometry ) { geometry2 = new Geometry(); geometry2.fromBufferGeometry( geometry ); } else { geometry2 = geometry.clone(); } geometry2.mergeVertices(); geometry2.computeFaceNormals(); var vertices = geometry2.vertices; var faces = geometry2.faces; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0; j < 3; j ++ ) { edge[ 0 ] = face[ keys[ j ] ]; edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; edge.sort( sortFunction ); var key = edge.toString(); if ( hash[ key ] === undefined ) { hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; } else { hash[ key ].face2 = i; } } } var coords = []; for ( var key in hash ) { var h = hash[ key ]; // An edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { var vertex = vertices[ h.vert1 ]; coords.push( vertex.x ); coords.push( vertex.y ); coords.push( vertex.z ); vertex = vertices[ h.vert2 ]; coords.push( vertex.x ); coords.push( vertex.y ); coords.push( vertex.z ); } } this.addAttribute( 'position', new Float32BufferAttribute( coords, 3 ) ); } EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); EdgesGeometry.prototype.constructor = EdgesGeometry; /** * @author Mugen87 / https://github.com/Mugen87 */ function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = 'CylinderBufferGeometry'; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; var scope = this; radiusTop = radiusTop !== undefined ? radiusTop : 20; radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; height = height !== undefined ? height : 100; radialSegments = Math.floor( radialSegments ) || 8; heightSegments = Math.floor( heightSegments ) || 1; openEnded = openEnded !== undefined ? openEnded : false; thetaStart = thetaStart !== undefined ? thetaStart : 0.0; thetaLength = thetaLength !== undefined ? thetaLength : 2.0 * Math.PI; // used to calculate buffer length var nbCap = 0; if ( openEnded === false ) { if ( radiusTop > 0 ) nbCap ++; if ( radiusBottom > 0 ) nbCap ++; } var vertexCount = calculateVertexCount(); var indexCount = calculateIndexCount(); // buffers var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ), 1 ); var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); // helper variables var index = 0, indexOffset = 0, indexArray = [], halfHeight = height / 2; // group variables var groupStart = 0; // generate geometry generateTorso(); if ( openEnded === false ) { if ( radiusTop > 0 ) generateCap( true ); if ( radiusBottom > 0 ) generateCap( false ); } // build geometry this.setIndex( indices ); this.addAttribute( 'position', vertices ); this.addAttribute( 'normal', normals ); this.addAttribute( 'uv', uvs ); // helper functions function calculateVertexCount() { var count = ( radialSegments + 1 ) * ( heightSegments + 1 ); if ( openEnded === false ) { count += ( ( radialSegments + 1 ) * nbCap ) + ( radialSegments * nbCap ); } return count; } function calculateIndexCount() { var count = radialSegments * heightSegments * 2 * 3; if ( openEnded === false ) { count += radialSegments * nbCap * 3; } return count; } function generateTorso() { var x, y; var normal = new Vector3(); var vertex = new Vector3(); var groupCount = 0; // this will be used to calculate the normal var slope = ( radiusBottom - radiusTop ) / height; // generate vertices, normals and uvs for ( y = 0; y <= heightSegments; y ++ ) { var indexRow = []; var v = y / heightSegments; // calculate the radius of the current row var radius = v * ( radiusBottom - radiusTop ) + radiusTop; for ( x = 0; x <= radialSegments; x ++ ) { var u = x / radialSegments; var theta = u * thetaLength + thetaStart; var sinTheta = Math.sin( theta ); var cosTheta = Math.cos( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = - v * height + halfHeight; vertex.z = radius * cosTheta; vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); // normal normal.set( sinTheta, slope, cosTheta ).normalize(); normals.setXYZ( index, normal.x, normal.y, normal.z ); // uv uvs.setXY( index, u, 1 - v ); // save index of vertex in respective row indexRow.push( index ); // increase index index ++; } // now save vertices of the row in our index array indexArray.push( indexRow ); } // generate indices for ( x = 0; x < radialSegments; x ++ ) { for ( y = 0; y < heightSegments; y ++ ) { // we use the index array to access the correct indices var i1 = indexArray[ y ][ x ]; var i2 = indexArray[ y + 1 ][ x ]; var i3 = indexArray[ y + 1 ][ x + 1 ]; var i4 = indexArray[ y ][ x + 1 ]; // face one indices.setX( indexOffset, i1 ); indexOffset ++; indices.setX( indexOffset, i2 ); indexOffset ++; indices.setX( indexOffset, i4 ); indexOffset ++; // face two indices.setX( indexOffset, i2 ); indexOffset ++; indices.setX( indexOffset, i3 ); indexOffset ++; indices.setX( indexOffset, i4 ); indexOffset ++; // update counters groupCount += 6; } } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, 0 ); // calculate new start value for groups groupStart += groupCount; } function generateCap( top ) { var x, centerIndexStart, centerIndexEnd; var uv = new Vector2(); var vertex = new Vector3(); var groupCount = 0; var radius = ( top === true ) ? radiusTop : radiusBottom; var sign = ( top === true ) ? 1 : - 1; // save the index of the first center vertex centerIndexStart = index; // first we generate the center vertex data of the cap. // because the geometry needs one set of uvs per face, // we must generate a center vertex per face/segment for ( x = 1; x <= radialSegments; x ++ ) { // vertex vertices.setXYZ( index, 0, halfHeight * sign, 0 ); // normal normals.setXYZ( index, 0, sign, 0 ); // uv uv.x = 0.5; uv.y = 0.5; uvs.setXY( index, uv.x, uv.y ); // increase index index ++; } // save the index of the last center vertex centerIndexEnd = index; // now we generate the surrounding vertices, normals and uvs for ( x = 0; x <= radialSegments; x ++ ) { var u = x / radialSegments; var theta = u * thetaLength + thetaStart; var cosTheta = Math.cos( theta ); var sinTheta = Math.sin( theta ); // vertex vertex.x = radius * sinTheta; vertex.y = halfHeight * sign; vertex.z = radius * cosTheta; vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); // normal normals.setXYZ( index, 0, sign, 0 ); // uv uv.x = ( cosTheta * 0.5 ) + 0.5; uv.y = ( sinTheta * 0.5 * sign ) + 0.5; uvs.setXY( index, uv.x, uv.y ); // increase index index ++; } // generate indices for ( x = 0; x < radialSegments; x ++ ) { var c = centerIndexStart + x; var i = centerIndexEnd + x; if ( top === true ) { // face top indices.setX( indexOffset, i ); indexOffset ++; indices.setX( indexOffset, i + 1 ); indexOffset ++; indices.setX( indexOffset, c ); indexOffset ++; } else { // face bottom indices.setX( indexOffset, i + 1 ); indexOffset ++; indices.setX( indexOffset, i ); indexOffset ++; indices.setX( indexOffset, c ); indexOffset ++; } // update counters groupCount += 3; } // add a group to the geometry. this will ensure multi material support scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); // calculate new start value for groups groupStart += groupCount; } } CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; /** * @author mrdoob / http://mrdoob.com/ */ function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { Geometry.call( this ); this.type = 'CylinderGeometry'; this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); this.mergeVertices(); } CylinderGeometry.prototype = Object.create( Geometry.prototype ); CylinderGeometry.prototype.constructor = CylinderGeometry; /** * @author abelnation / http://github.com/abelnation */ function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = 'ConeGeometry'; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); ConeGeometry.prototype.constructor = ConeGeometry; /** * @author: abelnation / http://github.com/abelnation */ function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); this.type = 'ConeBufferGeometry'; this.parameters = { radius: radius, height: height, radialSegments: radialSegments, heightSegments: heightSegments, openEnded: openEnded, thetaStart: thetaStart, thetaLength: thetaLength }; } ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype ); ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; /** * @author benaadams / https://twitter.com/ben_a_adams */ function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { BufferGeometry.call( this ); this.type = 'CircleBufferGeometry'; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; radius = radius || 50; segments = segments !== undefined ? Math.max( 3, segments ) : 8; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; var vertices = segments + 2; var positions = new Float32Array( vertices * 3 ); var normals = new Float32Array( vertices * 3 ); var uvs = new Float32Array( vertices * 2 ); // center data is already zero, but need to set a few extras normals[ 2 ] = 1.0; uvs[ 0 ] = 0.5; uvs[ 1 ] = 0.5; for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) { var segment = thetaStart + s / segments * thetaLength; positions[ i ] = radius * Math.cos( segment ); positions[ i + 1 ] = radius * Math.sin( segment ); normals[ i + 2 ] = 1; // normal z uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2; uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2; } var indices = []; for ( var i = 1; i <= segments; i ++ ) { indices.push( i, i + 1, 0 ); } this.setIndex( new BufferAttribute( new Uint16Array( indices ), 1 ) ); this.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); this.boundingSphere = new Sphere( new Vector3(), radius ); } CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; /** * @author hughes */ function CircleGeometry( radius, segments, thetaStart, thetaLength ) { Geometry.call( this ); this.type = 'CircleGeometry'; this.parameters = { radius: radius, segments: segments, thetaStart: thetaStart, thetaLength: thetaLength }; this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); } CircleGeometry.prototype = Object.create( Geometry.prototype ); CircleGeometry.prototype.constructor = CircleGeometry; /** * @author mrdoob / http://mrdoob.com/ * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as */ function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { Geometry.call( this ); this.type = 'BoxGeometry'; this.parameters = { width: width, height: height, depth: depth, widthSegments: widthSegments, heightSegments: heightSegments, depthSegments: depthSegments }; this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); this.mergeVertices(); } BoxGeometry.prototype = Object.create( Geometry.prototype ); BoxGeometry.prototype.constructor = BoxGeometry; var Geometries = Object.freeze({ WireframeGeometry: WireframeGeometry, ParametricGeometry: ParametricGeometry, ParametricBufferGeometry: ParametricBufferGeometry, TetrahedronGeometry: TetrahedronGeometry, TetrahedronBufferGeometry: TetrahedronBufferGeometry, OctahedronGeometry: OctahedronGeometry, OctahedronBufferGeometry: OctahedronBufferGeometry, IcosahedronGeometry: IcosahedronGeometry, IcosahedronBufferGeometry: IcosahedronBufferGeometry, DodecahedronGeometry: DodecahedronGeometry, DodecahedronBufferGeometry: DodecahedronBufferGeometry, PolyhedronGeometry: PolyhedronGeometry, PolyhedronBufferGeometry: PolyhedronBufferGeometry, TubeGeometry: TubeGeometry, TubeBufferGeometry: TubeBufferGeometry, TorusKnotGeometry: TorusKnotGeometry, TorusKnotBufferGeometry: TorusKnotBufferGeometry, TorusGeometry: TorusGeometry, TorusBufferGeometry: TorusBufferGeometry, TextGeometry: TextGeometry, SphereBufferGeometry: SphereBufferGeometry, SphereGeometry: SphereGeometry, RingGeometry: RingGeometry, RingBufferGeometry: RingBufferGeometry, PlaneBufferGeometry: PlaneBufferGeometry, PlaneGeometry: PlaneGeometry, LatheGeometry: LatheGeometry, LatheBufferGeometry: LatheBufferGeometry, ShapeGeometry: ShapeGeometry, ShapeBufferGeometry: ShapeBufferGeometry, ExtrudeGeometry: ExtrudeGeometry, EdgesGeometry: EdgesGeometry, ConeGeometry: ConeGeometry, ConeBufferGeometry: ConeBufferGeometry, CylinderGeometry: CylinderGeometry, CylinderBufferGeometry: CylinderBufferGeometry, CircleBufferGeometry: CircleBufferGeometry, CircleGeometry: CircleGeometry, BoxBufferGeometry: BoxBufferGeometry, BoxGeometry: BoxGeometry }); /** * @author mrdoob / http://mrdoob.com/ */ function ShadowMaterial() { ShaderMaterial.call( this, { uniforms: UniformsUtils.merge( [ UniformsLib.lights, { opacity: { value: 1.0 } } ] ), vertexShader: ShaderChunk[ 'shadow_vert' ], fragmentShader: ShaderChunk[ 'shadow_frag' ] } ); this.lights = true; this.transparent = true; Object.defineProperties( this, { opacity: { enumerable: true, get: function () { return this.uniforms.opacity.value; }, set: function ( value ) { this.uniforms.opacity.value = value; } } } ); } ShadowMaterial.prototype = Object.create( ShaderMaterial.prototype ); ShadowMaterial.prototype.constructor = ShadowMaterial; ShadowMaterial.prototype.isShadowMaterial = true; /** * @author mrdoob / http://mrdoob.com/ */ function RawShaderMaterial( parameters ) { ShaderMaterial.call( this, parameters ); this.type = 'RawShaderMaterial'; } RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); RawShaderMaterial.prototype.constructor = RawShaderMaterial; RawShaderMaterial.prototype.isRawShaderMaterial = true; /** * @author mrdoob / http://mrdoob.com/ */ function MultiMaterial( materials ) { this.uuid = _Math.generateUUID(); this.type = 'MultiMaterial'; this.materials = Array.isArray( materials ) ? materials : []; this.visible = true; } MultiMaterial.prototype = { constructor: MultiMaterial, isMultiMaterial: true, toJSON: function ( meta ) { var output = { metadata: { version: 4.2, type: 'material', generator: 'MaterialExporter' }, uuid: this.uuid, type: this.type, materials: [] }; var materials = this.materials; for ( var i = 0, l = materials.length; i < l; i ++ ) { var material = materials[ i ].toJSON( meta ); delete material.metadata; output.materials.push( material ); } output.visible = this.visible; return output; }, clone: function () { var material = new this.constructor(); for ( var i = 0; i < this.materials.length; i ++ ) { material.materials.push( this.materials[ i ].clone() ); } material.visible = this.visible; return material; } }; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * color: , * roughness: , * metalness: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * roughnessMap: new THREE.Texture( ), * * metalnessMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), * envMapIntensity: * * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshStandardMaterial( parameters ) { Material.call( this ); this.defines = { 'STANDARD': '' }; this.type = 'MeshStandardMaterial'; this.color = new Color( 0xffffff ); // diffuse this.roughness = 0.5; this.metalness = 0.5; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.roughnessMap = null; this.metalnessMap = null; this.alphaMap = null; this.envMap = null; this.envMapIntensity = 1.0; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshStandardMaterial.prototype = Object.create( Material.prototype ); MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; MeshStandardMaterial.prototype.isMeshStandardMaterial = true; MeshStandardMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.defines = { 'STANDARD': '' }; this.color.copy( source.color ); this.roughness = source.roughness; this.metalness = source.metalness; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.roughnessMap = source.roughnessMap; this.metalnessMap = source.metalnessMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.envMapIntensity = source.envMapIntensity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author WestLangley / http://github.com/WestLangley * * parameters = { * reflectivity: * } */ function MeshPhysicalMaterial( parameters ) { MeshStandardMaterial.call( this ); this.defines = { 'PHYSICAL': '' }; this.type = 'MeshPhysicalMaterial'; this.reflectivity = 0.5; // maps to F0 = 0.04 this.clearCoat = 0.0; this.clearCoatRoughness = 0.0; this.setValues( parameters ); } MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; MeshPhysicalMaterial.prototype.copy = function ( source ) { MeshStandardMaterial.prototype.copy.call( this, source ); this.defines = { 'PHYSICAL': '' }; this.reflectivity = source.reflectivity; this.clearCoat = source.clearCoat; this.clearCoatRoughness = source.clearCoatRoughness; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * specular: , * shininess: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshPhongMaterial( parameters ) { Material.call( this ); this.type = 'MeshPhongMaterial'; this.color = new Color( 0xffffff ); // diffuse this.specular = new Color( 0x111111 ); this.shininess = 30; this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshPhongMaterial.prototype = Object.create( Material.prototype ); MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; MeshPhongMaterial.prototype.isMeshPhongMaterial = true; MeshPhongMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.specular.copy( source.specular ); this.shininess = source.shininess; this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author takahirox / http://github.com/takahirox * * parameters = { * gradientMap: new THREE.Texture( ) * } */ function MeshToonMaterial( parameters ) { MeshPhongMaterial.call( this ); this.defines = { 'TOON': '' }; this.type = 'MeshToonMaterial'; this.gradientMap = null; this.setValues( parameters ); } MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype ); MeshToonMaterial.prototype.constructor = MeshToonMaterial; MeshToonMaterial.prototype.isMeshToonMaterial = true; MeshToonMaterial.prototype.copy = function ( source ) { MeshPhongMaterial.prototype.copy.call( this, source ); this.gradientMap = source.gradientMap; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley * * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalScale: , * * displacementMap: new THREE.Texture( ), * displacementScale: , * displacementBias: , * * wireframe: , * wireframeLinewidth: * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshNormalMaterial( parameters ) { Material.call( this, parameters ); this.type = 'MeshNormalMaterial'; this.bumpMap = null; this.bumpScale = 1; this.normalMap = null; this.normalScale = new Vector2( 1, 1 ); this.displacementMap = null; this.displacementScale = 1; this.displacementBias = 0; this.wireframe = false; this.wireframeLinewidth = 1; this.fog = false; this.lights = false; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshNormalMaterial.prototype = Object.create( Material.prototype ); MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; MeshNormalMaterial.prototype.isMeshNormalMaterial = true; MeshNormalMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.bumpMap = source.bumpMap; this.bumpScale = source.bumpScale; this.normalMap = source.normalMap; this.normalScale.copy( source.normalScale ); this.displacementMap = source.displacementMap; this.displacementScale = source.displacementScale; this.displacementBias = source.displacementBias; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * map: new THREE.Texture( ), * * lightMap: new THREE.Texture( ), * lightMapIntensity: * * aoMap: new THREE.Texture( ), * aoMapIntensity: * * emissive: , * emissiveIntensity: * emissiveMap: new THREE.Texture( ), * * specularMap: new THREE.Texture( ), * * alphaMap: new THREE.Texture( ), * * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), * combine: THREE.Multiply, * reflectivity: , * refractionRatio: , * * wireframe: , * wireframeLinewidth: , * * skinning: , * morphTargets: , * morphNormals: * } */ function MeshLambertMaterial( parameters ) { Material.call( this ); this.type = 'MeshLambertMaterial'; this.color = new Color( 0xffffff ); // diffuse this.map = null; this.lightMap = null; this.lightMapIntensity = 1.0; this.aoMap = null; this.aoMapIntensity = 1.0; this.emissive = new Color( 0x000000 ); this.emissiveIntensity = 1.0; this.emissiveMap = null; this.specularMap = null; this.alphaMap = null; this.envMap = null; this.combine = MultiplyOperation; this.reflectivity = 1; this.refractionRatio = 0.98; this.wireframe = false; this.wireframeLinewidth = 1; this.wireframeLinecap = 'round'; this.wireframeLinejoin = 'round'; this.skinning = false; this.morphTargets = false; this.morphNormals = false; this.setValues( parameters ); } MeshLambertMaterial.prototype = Object.create( Material.prototype ); MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; MeshLambertMaterial.prototype.isMeshLambertMaterial = true; MeshLambertMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.map = source.map; this.lightMap = source.lightMap; this.lightMapIntensity = source.lightMapIntensity; this.aoMap = source.aoMap; this.aoMapIntensity = source.aoMapIntensity; this.emissive.copy( source.emissive ); this.emissiveMap = source.emissiveMap; this.emissiveIntensity = source.emissiveIntensity; this.specularMap = source.specularMap; this.alphaMap = source.alphaMap; this.envMap = source.envMap; this.combine = source.combine; this.reflectivity = source.reflectivity; this.refractionRatio = source.refractionRatio; this.wireframe = source.wireframe; this.wireframeLinewidth = source.wireframeLinewidth; this.wireframeLinecap = source.wireframeLinecap; this.wireframeLinejoin = source.wireframeLinejoin; this.skinning = source.skinning; this.morphTargets = source.morphTargets; this.morphNormals = source.morphNormals; return this; }; /** * @author alteredq / http://alteredqualia.com/ * * parameters = { * color: , * opacity: , * * linewidth: , * * scale: , * dashSize: , * gapSize: * } */ function LineDashedMaterial( parameters ) { Material.call( this ); this.type = 'LineDashedMaterial'; this.color = new Color( 0xffffff ); this.linewidth = 1; this.scale = 1; this.dashSize = 3; this.gapSize = 1; this.lights = false; this.setValues( parameters ); } LineDashedMaterial.prototype = Object.create( Material.prototype ); LineDashedMaterial.prototype.constructor = LineDashedMaterial; LineDashedMaterial.prototype.isLineDashedMaterial = true; LineDashedMaterial.prototype.copy = function ( source ) { Material.prototype.copy.call( this, source ); this.color.copy( source.color ); this.linewidth = source.linewidth; this.scale = source.scale; this.dashSize = source.dashSize; this.gapSize = source.gapSize; return this; }; var Materials = Object.freeze({ ShadowMaterial: ShadowMaterial, SpriteMaterial: SpriteMaterial, RawShaderMaterial: RawShaderMaterial, ShaderMaterial: ShaderMaterial, PointsMaterial: PointsMaterial, MultiMaterial: MultiMaterial, MeshPhysicalMaterial: MeshPhysicalMaterial, MeshStandardMaterial: MeshStandardMaterial, MeshPhongMaterial: MeshPhongMaterial, MeshToonMaterial: MeshToonMaterial, MeshNormalMaterial: MeshNormalMaterial, MeshLambertMaterial: MeshLambertMaterial, MeshDepthMaterial: MeshDepthMaterial, MeshBasicMaterial: MeshBasicMaterial, LineDashedMaterial: LineDashedMaterial, LineBasicMaterial: LineBasicMaterial, Material: Material }); /** * @author mrdoob / http://mrdoob.com/ */ var Cache = { enabled: false, files: {}, add: function ( key, file ) { if ( this.enabled === false ) return; // console.log( 'THREE.Cache', 'Adding key:', key ); this.files[ key ] = file; }, get: function ( key ) { if ( this.enabled === false ) return; // console.log( 'THREE.Cache', 'Checking key:', key ); return this.files[ key ]; }, remove: function ( key ) { delete this.files[ key ]; }, clear: function () { this.files = {}; } }; /** * @author mrdoob / http://mrdoob.com/ */ function LoadingManager( onLoad, onProgress, onError ) { var scope = this; var isLoading = false, itemsLoaded = 0, itemsTotal = 0; this.onStart = undefined; this.onLoad = onLoad; this.onProgress = onProgress; this.onError = onError; this.itemStart = function ( url ) { itemsTotal ++; if ( isLoading === false ) { if ( scope.onStart !== undefined ) { scope.onStart( url, itemsLoaded, itemsTotal ); } } isLoading = true; }; this.itemEnd = function ( url ) { itemsLoaded ++; if ( scope.onProgress !== undefined ) { scope.onProgress( url, itemsLoaded, itemsTotal ); } if ( itemsLoaded === itemsTotal ) { isLoading = false; if ( scope.onLoad !== undefined ) { scope.onLoad(); } } }; this.itemError = function ( url ) { if ( scope.onError !== undefined ) { scope.onError( url ); } }; } var DefaultLoadingManager = new LoadingManager(); /** * @author mrdoob / http://mrdoob.com/ */ function FileLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( FileLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { if ( url === undefined ) url = ''; if ( this.path !== undefined ) url = this.path + url; var scope = this; var cached = Cache.get( url ); if ( cached !== undefined ) { scope.manager.itemStart( url ); setTimeout( function () { if ( onLoad ) onLoad( cached ); scope.manager.itemEnd( url ); }, 0 ); return cached; } // Check for data: URI var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; var dataUriRegexResult = url.match( dataUriRegex ); // Safari can not handle Data URIs through XMLHttpRequest so process manually if ( dataUriRegexResult ) { var mimeType = dataUriRegexResult[ 1 ]; var isBase64 = !! dataUriRegexResult[ 2 ]; var data = dataUriRegexResult[ 3 ]; data = window.decodeURIComponent( data ); if ( isBase64 ) data = window.atob( data ); try { var response; var responseType = ( this.responseType || '' ).toLowerCase(); switch ( responseType ) { case 'arraybuffer': case 'blob': response = new ArrayBuffer( data.length ); var view = new Uint8Array( response ); for ( var i = 0; i < data.length; i ++ ) { view[ i ] = data.charCodeAt( i ); } if ( responseType === 'blob' ) { response = new Blob( [ response ], { type: mimeType } ); } break; case 'document': var parser = new DOMParser(); response = parser.parseFromString( data, mimeType ); break; case 'json': response = JSON.parse( data ); break; default: // 'text' or other response = data; break; } // Wait for next browser tick window.setTimeout( function () { if ( onLoad ) onLoad( response ); scope.manager.itemEnd( url ); }, 0 ); } catch ( error ) { // Wait for next browser tick window.setTimeout( function () { if ( onError ) onError( error ); scope.manager.itemError( url ); }, 0 ); } } else { var request = new XMLHttpRequest(); request.open( 'GET', url, true ); request.addEventListener( 'load', function ( event ) { var response = event.target.response; Cache.add( url, response ); if ( this.status === 200 ) { if ( onLoad ) onLoad( response ); scope.manager.itemEnd( url ); } else if ( this.status === 0 ) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. 'file://' or 'data://'. Handle as success. console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); if ( onLoad ) onLoad( response ); scope.manager.itemEnd( url ); } else { if ( onError ) onError( event ); scope.manager.itemError( url ); } }, false ); if ( onProgress !== undefined ) { request.addEventListener( 'progress', function ( event ) { onProgress( event ); }, false ); } request.addEventListener( 'error', function ( event ) { if ( onError ) onError( event ); scope.manager.itemError( url ); }, false ); if ( this.responseType !== undefined ) request.responseType = this.responseType; if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); request.send( null ); } scope.manager.itemStart( url ); return request; }, setPath: function ( value ) { this.path = value; return this; }, setResponseType: function ( value ) { this.responseType = value; return this; }, setWithCredentials: function ( value ) { this.withCredentials = value; return this; }, setMimeType: function ( value ) { this.mimeType = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * * Abstract Base class to block based textures loader (dds, pvr, ...) */ function CompressedTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; // override in sub classes this._parser = null; } Object.assign( CompressedTextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var images = []; var texture = new CompressedTexture(); texture.image = images; var loader = new FileLoader( this.manager ); loader.setPath( this.path ); loader.setResponseType( 'arraybuffer' ); function loadTexture( i ) { loader.load( url[ i ], function ( buffer ) { var texDatas = scope._parser( buffer, true ); images[ i ] = { width: texDatas.width, height: texDatas.height, format: texDatas.format, mipmaps: texDatas.mipmaps }; loaded += 1; if ( loaded === 6 ) { if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, onProgress, onError ); } if ( Array.isArray( url ) ) { var loaded = 0; for ( var i = 0, il = url.length; i < il; ++ i ) { loadTexture( i ); } } else { // compressed cubemap texture stored in a single DDS file loader.load( url, function ( buffer ) { var texDatas = scope._parser( buffer, true ); if ( texDatas.isCubemap ) { var faces = texDatas.mipmaps.length / texDatas.mipmapCount; for ( var f = 0; f < faces; f ++ ) { images[ f ] = { mipmaps : [] }; for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); images[ f ].format = texDatas.format; images[ f ].width = texDatas.width; images[ f ].height = texDatas.height; } } } else { texture.image.width = texDatas.width; texture.image.height = texDatas.height; texture.mipmaps = texDatas.mipmaps; } if ( texDatas.mipmapCount === 1 ) { texture.minFilter = LinearFilter; } texture.format = texDatas.format; texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); }, onProgress, onError ); } return texture; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author Nikos M. / https://github.com/foo123/ * * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) */ var DataTextureLoader = BinaryTextureLoader; function BinaryTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; // override in sub classes this._parser = null; } Object.assign( BinaryTextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var texture = new DataTexture(); var loader = new FileLoader( this.manager ); loader.setResponseType( 'arraybuffer' ); loader.load( url, function ( buffer ) { var texData = scope._parser( buffer ); if ( ! texData ) return; if ( undefined !== texData.image ) { texture.image = texData.image; } else if ( undefined !== texData.data ) { texture.image.width = texData.width; texture.image.height = texData.height; texture.image.data = texData.data; } texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; if ( undefined !== texData.format ) { texture.format = texData.format; } if ( undefined !== texData.type ) { texture.type = texData.type; } if ( undefined !== texData.mipmaps ) { texture.mipmaps = texData.mipmaps; } if ( 1 === texData.mipmapCount ) { texture.minFilter = LinearFilter; } texture.needsUpdate = true; if ( onLoad ) onLoad( texture, texData ); }, onProgress, onError ); return texture; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function ImageLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( ImageLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); image.onload = function () { image.onload = null; URL.revokeObjectURL( image.src ); if ( onLoad ) onLoad( image ); scope.manager.itemEnd( url ); }; image.onerror = onError; if ( url.indexOf( 'data:' ) === 0 ) { image.src = url; } else if ( this.crossOrigin !== undefined ) { // crossOrigin doesn't work with URL.createObjectURL()? image.crossOrigin = this.crossOrigin; image.src = url; } else { var loader = new FileLoader(); loader.setPath( this.path ); loader.setResponseType( 'blob' ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( blob ) { image.src = URL.createObjectURL( blob ); }, onProgress, onError ); } scope.manager.itemStart( url ); return image; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setWithCredentials: function ( value ) { this.withCredentials = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function CubeTextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( CubeTextureLoader.prototype, { load: function ( urls, onLoad, onProgress, onError ) { var texture = new CubeTexture(); var loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setPath( this.path ); var loaded = 0; function loadTexture( i ) { loader.load( urls[ i ], function ( image ) { texture.images[ i ] = image; loaded ++; if ( loaded === 6 ) { texture.needsUpdate = true; if ( onLoad ) onLoad( texture ); } }, undefined, onError ); } for ( var i = 0; i < urls.length; ++ i ) { loadTexture( i ); } return texture; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function TextureLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( TextureLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var texture = new Texture(); var loader = new ImageLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); loader.setWithCredentials( this.withCredentials ); loader.setPath( this.path ); loader.load( url, function ( image ) { // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. var isJPEG = url.search( /\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; texture.format = isJPEG ? RGBFormat : RGBAFormat; texture.image = image; texture.needsUpdate = true; if ( onLoad !== undefined ) { onLoad( texture ); } }, onProgress, onError ); return texture; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; return this; }, setWithCredentials: function ( value ) { this.withCredentials = value; return this; }, setPath: function ( value ) { this.path = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Light( color, intensity ) { Object3D.call( this ); this.type = 'Light'; this.color = new Color( color ); this.intensity = intensity !== undefined ? intensity : 1; this.receiveShadow = undefined; } Light.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Light, isLight: true, copy: function ( source ) { Object3D.prototype.copy.call( this, source ); this.color.copy( source.color ); this.intensity = source.intensity; return this; }, toJSON: function ( meta ) { var data = Object3D.prototype.toJSON.call( this, meta ); data.object.color = this.color.getHex(); data.object.intensity = this.intensity; if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); if ( this.distance !== undefined ) data.object.distance = this.distance; if ( this.angle !== undefined ) data.object.angle = this.angle; if ( this.decay !== undefined ) data.object.decay = this.decay; if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); return data; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function HemisphereLight( skyColor, groundColor, intensity ) { Light.call( this, skyColor, intensity ); this.type = 'HemisphereLight'; this.castShadow = undefined; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.groundColor = new Color( groundColor ); } HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: HemisphereLight, isHemisphereLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.groundColor.copy( source.groundColor ); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function LightShadow( camera ) { this.camera = camera; this.bias = 0; this.radius = 1; this.mapSize = new Vector2( 512, 512 ); this.map = null; this.matrix = new Matrix4(); } Object.assign( LightShadow.prototype, { copy: function ( source ) { this.camera = source.camera.clone(); this.bias = source.bias; this.radius = source.radius; this.mapSize.copy( source.mapSize ); return this; }, clone: function () { return new this.constructor().copy( this ); }, toJSON: function () { var object = {}; if ( this.bias !== 0 ) object.bias = this.bias; if ( this.radius !== 1 ) object.radius = this.radius; if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); object.camera = this.camera.toJSON( false ).object; delete object.camera.matrix; return object; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function SpotLightShadow() { LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); } SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { constructor: SpotLightShadow, isSpotLightShadow: true, update: function ( light ) { var fov = _Math.RAD2DEG * 2 * light.angle; var aspect = this.mapSize.width / this.mapSize.height; var far = light.distance || 500; var camera = this.camera; if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { camera.fov = fov; camera.aspect = aspect; camera.far = far; camera.updateProjectionMatrix(); } } } ); /** * @author alteredq / http://alteredqualia.com/ */ function SpotLight( color, intensity, distance, angle, penumbra, decay ) { Light.call( this, color, intensity ); this.type = 'SpotLight'; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.target = new Object3D(); Object.defineProperty( this, 'power', { get: function () { // intensity = power per solid angle. // ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf return this.intensity * Math.PI; }, set: function ( power ) { // intensity = power per solid angle. // ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf this.intensity = power / Math.PI; } } ); this.distance = ( distance !== undefined ) ? distance : 0; this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. this.shadow = new SpotLightShadow(); } SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: SpotLight, isSpotLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.distance = source.distance; this.angle = source.angle; this.penumbra = source.penumbra; this.decay = source.decay; this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function PointLight( color, intensity, distance, decay ) { Light.call( this, color, intensity ); this.type = 'PointLight'; Object.defineProperty( this, 'power', { get: function () { // intensity = power per solid angle. // ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf return this.intensity * 4 * Math.PI; }, set: function ( power ) { // intensity = power per solid angle. // ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf this.intensity = power / ( 4 * Math.PI ); } } ); this.distance = ( distance !== undefined ) ? distance : 0; this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); } PointLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: PointLight, isPointLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.distance = source.distance; this.decay = source.decay; this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function DirectionalLightShadow( light ) { LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); } DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { constructor: DirectionalLightShadow } ); /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function DirectionalLight( color, intensity ) { Light.call( this, color, intensity ); this.type = 'DirectionalLight'; this.position.copy( Object3D.DefaultUp ); this.updateMatrix(); this.target = new Object3D(); this.shadow = new DirectionalLightShadow(); } DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: DirectionalLight, isDirectionalLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.target = source.target.clone(); this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function AmbientLight( color, intensity ) { Light.call( this, color, intensity ); this.type = 'AmbientLight'; this.castShadow = undefined; } AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: AmbientLight, isAmbientLight: true } ); /** * @author tschw * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ */ var AnimationUtils = { // same as Array.prototype.slice, but also works on typed arrays arraySlice: function( array, from, to ) { if ( AnimationUtils.isTypedArray( array ) ) { return new array.constructor( array.subarray( from, to ) ); } return array.slice( from, to ); }, // converts an array to a specific type convertArray: function( array, type, forceClone ) { if ( ! array || // let 'undefined' and 'null' pass ! forceClone && array.constructor === type ) return array; if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { return new type( array ); // create typed array } return Array.prototype.slice.call( array ); // create Array }, isTypedArray: function( object ) { return ArrayBuffer.isView( object ) && ! ( object instanceof DataView ); }, // returns an array by which times and values can be sorted getKeyframeOrder: function( times ) { function compareTime( i, j ) { return times[ i ] - times[ j ]; } var n = times.length; var result = new Array( n ); for ( var i = 0; i !== n; ++ i ) result[ i ] = i; result.sort( compareTime ); return result; }, // uses the array previously returned by 'getKeyframeOrder' to sort data sortedArray: function( values, stride, order ) { var nValues = values.length; var result = new values.constructor( nValues ); for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { var srcOffset = order[ i ] * stride; for ( var j = 0; j !== stride; ++ j ) { result[ dstOffset ++ ] = values[ srcOffset + j ]; } } return result; }, // function for parsing AOS keyframe formats flattenJSON: function( jsonKeys, times, values, valuePropertyName ) { var i = 1, key = jsonKeys[ 0 ]; while ( key !== undefined && key[ valuePropertyName ] === undefined ) { key = jsonKeys[ i ++ ]; } if ( key === undefined ) return; // no data var value = key[ valuePropertyName ]; if ( value === undefined ) return; // no data if ( Array.isArray( value ) ) { do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push.apply( values, value ); // push all elements } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else if ( value.toArray !== undefined ) { // ...assume THREE.Math-ish do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); value.toArray( values, values.length ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } else { // otherwise push as-is do { value = key[ valuePropertyName ]; if ( value !== undefined ) { times.push( key.time ); values.push( value ); } key = jsonKeys[ i ++ ]; } while ( key !== undefined ); } } }; /** * Abstract base class of interpolants over parametric samples. * * The parameter domain is one dimensional, typically the time or a path * along a curve defined by the data. * * The sample values can have any dimensionality and derived classes may * apply special interpretations to the data. * * This class provides the interval seek in a Template Method, deferring * the actual interpolation to derived classes. * * Time complexity is O(1) for linear access crossing at most two points * and O(log N) for random access, where N is the number of positions. * * References: * * http://www.oodesign.com/template-method-pattern.html * * @author tschw */ function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { this.parameterPositions = parameterPositions; this._cachedIndex = 0; this.resultBuffer = resultBuffer !== undefined ? resultBuffer : new sampleValues.constructor( sampleSize ); this.sampleValues = sampleValues; this.valueSize = sampleSize; } Interpolant.prototype = { constructor: Interpolant, evaluate: function( t ) { var pp = this.parameterPositions, i1 = this._cachedIndex, t1 = pp[ i1 ], t0 = pp[ i1 - 1 ]; validate_interval: { seek: { var right; linear_scan: { //- See http://jsperf.com/comparison-to-undefined/3 //- slower code: //- //- if ( t >= t1 || t1 === undefined ) { forward_scan: if ( ! ( t < t1 ) ) { for ( var giveUpAt = i1 + 2; ;) { if ( t1 === undefined ) { if ( t < t0 ) break forward_scan; // after end i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_( i1 - 1, t, t0 ); } if ( i1 === giveUpAt ) break; // this loop t0 = t1; t1 = pp[ ++ i1 ]; if ( t < t1 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the right side of the index right = pp.length; break linear_scan; } //- slower code: //- if ( t < t0 || t0 === undefined ) { if ( ! ( t >= t0 ) ) { // looping? var t1global = pp[ 1 ]; if ( t < t1global ) { i1 = 2; // + 1, using the scan for the details t0 = t1global; } // linear reverse scan for ( var giveUpAt = i1 - 2; ;) { if ( t0 === undefined ) { // before start this._cachedIndex = 0; return this.beforeStart_( 0, t, t1 ); } if ( i1 === giveUpAt ) break; // this loop t1 = t0; t0 = pp[ -- i1 - 1 ]; if ( t >= t0 ) { // we have arrived at the sought interval break seek; } } // prepare binary search on the left side of the index right = i1; i1 = 0; break linear_scan; } // the interval is valid break validate_interval; } // linear scan // binary search while ( i1 < right ) { var mid = ( i1 + right ) >>> 1; if ( t < pp[ mid ] ) { right = mid; } else { i1 = mid + 1; } } t1 = pp[ i1 ]; t0 = pp[ i1 - 1 ]; // check boundary cases, again if ( t0 === undefined ) { this._cachedIndex = 0; return this.beforeStart_( 0, t, t1 ); } if ( t1 === undefined ) { i1 = pp.length; this._cachedIndex = i1; return this.afterEnd_( i1 - 1, t0, t ); } } // seek this._cachedIndex = i1; this.intervalChanged_( i1, t0, t1 ); } // validate_interval return this.interpolate_( i1, t0, t, t1 ); }, settings: null, // optional, subclass-specific settings structure // Note: The indirection allows central control of many interpolants. // --- Protected interface DefaultSettings_: {}, getSettings_: function() { return this.settings || this.DefaultSettings_; }, copySampleValue_: function( index ) { // copies a sample value to the result buffer var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = index * stride; for ( var i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset + i ]; } return result; }, // Template methods for derived classes: interpolate_: function( i1, t0, t, t1 ) { throw new Error( "call to abstract method" ); // implementations shall return this.resultBuffer }, intervalChanged_: function( i1, t0, t1 ) { // empty } }; Object.assign( Interpolant.prototype, { beforeStart_: //( 0, t, t0 ), returns this.resultBuffer Interpolant.prototype.copySampleValue_, afterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer Interpolant.prototype.copySampleValue_ } ); /** * Fast and simple cubic spline interpolant. * * It was derived from a Hermitian construction setting the first derivative * at each sample position to the linear slope between neighboring positions * over their parameter interval. * * @author tschw */ function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); this._weightPrev = -0; this._offsetPrev = -0; this._weightNext = -0; this._offsetNext = -0; } CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: CubicInterpolant, DefaultSettings_: { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }, intervalChanged_: function( i1, t0, t1 ) { var pp = this.parameterPositions, iPrev = i1 - 2, iNext = i1 + 1, tPrev = pp[ iPrev ], tNext = pp[ iNext ]; if ( tPrev === undefined ) { switch ( this.getSettings_().endingStart ) { case ZeroSlopeEnding: // f'(t0) = 0 iPrev = i1; tPrev = 2 * t0 - t1; break; case WrapAroundEnding: // use the other end of the curve iPrev = pp.length - 2; tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; break; default: // ZeroCurvatureEnding // f''(t0) = 0 a.k.a. Natural Spline iPrev = i1; tPrev = t1; } } if ( tNext === undefined ) { switch ( this.getSettings_().endingEnd ) { case ZeroSlopeEnding: // f'(tN) = 0 iNext = i1; tNext = 2 * t1 - t0; break; case WrapAroundEnding: // use the other end of the curve iNext = 1; tNext = t1 + pp[ 1 ] - pp[ 0 ]; break; default: // ZeroCurvatureEnding // f''(tN) = 0, a.k.a. Natural Spline iNext = i1 - 1; tNext = t0; } } var halfDt = ( t1 - t0 ) * 0.5, stride = this.valueSize; this._weightPrev = halfDt / ( t0 - tPrev ); this._weightNext = halfDt / ( tNext - t1 ); this._offsetPrev = iPrev * stride; this._offsetNext = iNext * stride; }, interpolate_: function( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, o1 = i1 * stride, o0 = o1 - stride, oP = this._offsetPrev, oN = this._offsetNext, wP = this._weightPrev, wN = this._weightNext, p = ( t - t0 ) / ( t1 - t0 ), pp = p * p, ppp = pp * p; // evaluate polynomials var sP = - wP * ppp + 2 * wP * pp - wP * p; var s0 = ( 1 + wP ) * ppp + (-1.5 - 2 * wP ) * pp + ( -0.5 + wP ) * p + 1; var s1 = (-1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; var sN = wN * ppp - wN * pp; // combine data linearly for ( var i = 0; i !== stride; ++ i ) { result[ i ] = sP * values[ oP + i ] + s0 * values[ o0 + i ] + s1 * values[ o1 + i ] + sN * values[ oN + i ]; } return result; } } ); /** * @author tschw */ function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: LinearInterpolant, interpolate_: function( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset1 = i1 * stride, offset0 = offset1 - stride, weight1 = ( t - t0 ) / ( t1 - t0 ), weight0 = 1 - weight1; for ( var i = 0; i !== stride; ++ i ) { result[ i ] = values[ offset0 + i ] * weight0 + values[ offset1 + i ] * weight1; } return result; } } ); /** * * Interpolant that evaluates to the sample value at the position preceeding * the parameter. * * @author tschw */ function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: DiscreteInterpolant, interpolate_: function( i1, t0, t, t1 ) { return this.copySampleValue_( i1 - 1 ); } } ); var KeyframeTrackPrototype; KeyframeTrackPrototype = { TimeBufferType: Float32Array, ValueBufferType: Float32Array, DefaultInterpolation: InterpolateLinear, InterpolantFactoryMethodDiscrete: function ( result ) { return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodLinear: function ( result ) { return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodSmooth: function ( result ) { return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); }, setInterpolation: function ( interpolation ) { var factoryMethod; switch ( interpolation ) { case InterpolateDiscrete: factoryMethod = this.InterpolantFactoryMethodDiscrete; break; case InterpolateLinear: factoryMethod = this.InterpolantFactoryMethodLinear; break; case InterpolateSmooth: factoryMethod = this.InterpolantFactoryMethodSmooth; break; } if ( factoryMethod === undefined ) { var message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name; if ( this.createInterpolant === undefined ) { // fall back to default, unless the default itself is messed up if ( interpolation !== this.DefaultInterpolation ) { this.setInterpolation( this.DefaultInterpolation ); } else { throw new Error( message ); // fatal, in this case } } console.warn( message ); return; } this.createInterpolant = factoryMethod; }, getInterpolation: function () { switch ( this.createInterpolant ) { case this.InterpolantFactoryMethodDiscrete: return InterpolateDiscrete; case this.InterpolantFactoryMethodLinear: return InterpolateLinear; case this.InterpolantFactoryMethodSmooth: return InterpolateSmooth; } }, getValueSize: function () { return this.values.length / this.times.length; }, // move all keyframes either forwards or backwards in time shift: function ( timeOffset ) { if ( timeOffset !== 0.0 ) { var times = this.times; for ( var i = 0, n = times.length; i !== n; ++ i ) { times[ i ] += timeOffset; } } return this; }, // scale all keyframe times by a factor (useful for frame <-> seconds conversions) scale: function ( timeScale ) { if ( timeScale !== 1.0 ) { var times = this.times; for ( var i = 0, n = times.length; i !== n; ++ i ) { times[ i ] *= timeScale; } } return this; }, // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values trim: function ( startTime, endTime ) { var times = this.times, nKeys = times.length, from = 0, to = nKeys - 1; while ( from !== nKeys && times[ from ] < startTime ) ++ from; while ( to !== - 1 && times[ to ] > endTime ) -- to; ++ to; // inclusive -> exclusive bound if ( from !== 0 || to !== nKeys ) { // empty tracks are forbidden, so keep at least one keyframe if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; var stride = this.getValueSize(); this.times = AnimationUtils.arraySlice( times, from, to ); this.values = AnimationUtils. arraySlice( this.values, from * stride, to * stride ); } return this; }, // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable validate: function () { var valid = true; var valueSize = this.getValueSize(); if ( valueSize - Math.floor( valueSize ) !== 0 ) { console.error( "invalid value size in track", this ); valid = false; } var times = this.times, values = this.values, nKeys = times.length; if ( nKeys === 0 ) { console.error( "track is empty", this ); valid = false; } var prevTime = null; for ( var i = 0; i !== nKeys; i ++ ) { var currTime = times[ i ]; if ( typeof currTime === 'number' && isNaN( currTime ) ) { console.error( "time is not a valid number", this, i, currTime ); valid = false; break; } if ( prevTime !== null && prevTime > currTime ) { console.error( "out of order keys", this, i, currTime, prevTime ); valid = false; break; } prevTime = currTime; } if ( values !== undefined ) { if ( AnimationUtils.isTypedArray( values ) ) { for ( var i = 0, n = values.length; i !== n; ++ i ) { var value = values[ i ]; if ( isNaN( value ) ) { console.error( "value is not a valid number", this, i, value ); valid = false; break; } } } } return valid; }, // removes equivalent sequential keys as common in morph target sequences // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) optimize: function () { var times = this.times, values = this.values, stride = this.getValueSize(), smoothInterpolation = this.getInterpolation() === InterpolateSmooth, writeIndex = 1, lastIndex = times.length - 1; for ( var i = 1; i < lastIndex; ++ i ) { var keep = false; var time = times[ i ]; var timeNext = times[ i + 1 ]; // remove adjacent keyframes scheduled at the same time if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { if ( ! smoothInterpolation ) { // remove unnecessary keyframes same as their neighbors var offset = i * stride, offsetP = offset - stride, offsetN = offset + stride; for ( var j = 0; j !== stride; ++ j ) { var value = values[ offset + j ]; if ( value !== values[ offsetP + j ] || value !== values[ offsetN + j ] ) { keep = true; break; } } } else keep = true; } // in-place compaction if ( keep ) { if ( i !== writeIndex ) { times[ writeIndex ] = times[ i ]; var readOffset = i * stride, writeOffset = writeIndex * stride; for ( var j = 0; j !== stride; ++ j ) values[ writeOffset + j ] = values[ readOffset + j ]; } ++ writeIndex; } } // flush last keyframe (compaction looks ahead) if ( lastIndex > 0 ) { times[ writeIndex ] = times[ lastIndex ]; for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) values[ writeOffset + j ] = values[ readOffset + j ]; ++ writeIndex; } if ( writeIndex !== times.length ) { this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); } return this; } }; function KeyframeTrackConstructor( name, times, values, interpolation ) { if( name === undefined ) throw new Error( "track name is undefined" ); if( times === undefined || times.length === 0 ) { throw new Error( "no keyframes in track named " + name ); } this.name = name; this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); this.setInterpolation( interpolation || this.DefaultInterpolation ); this.validate(); this.optimize(); } /** * * A Track of vectored keyframe values. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function VectorKeyframeTrack( name, times, values, interpolation ) { KeyframeTrackConstructor.call( this, name, times, values, interpolation ); } VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), { constructor: VectorKeyframeTrack, ValueTypeName: 'vector' // ValueBufferType is inherited // DefaultInterpolation is inherited } ); /** * Spherical linear unit quaternion interpolant. * * @author tschw */ function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); } QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { constructor: QuaternionLinearInterpolant, interpolate_: function( i1, t0, t, t1 ) { var result = this.resultBuffer, values = this.sampleValues, stride = this.valueSize, offset = i1 * stride, alpha = ( t - t0 ) / ( t1 - t0 ); for ( var end = offset + stride; offset !== end; offset += 4 ) { Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); } return result; } } ); /** * * A Track of quaternion keyframe values. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function QuaternionKeyframeTrack( name, times, values, interpolation ) { KeyframeTrackConstructor.call( this, name, times, values, interpolation ); } QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), { constructor: QuaternionKeyframeTrack, ValueTypeName: 'quaternion', // ValueBufferType is inherited DefaultInterpolation: InterpolateLinear, InterpolantFactoryMethodLinear: function( result ) { return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); }, InterpolantFactoryMethodSmooth: undefined // not yet implemented } ); /** * * A Track of numeric keyframe values. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function NumberKeyframeTrack( name, times, values, interpolation ) { KeyframeTrackConstructor.call( this, name, times, values, interpolation ); } NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), { constructor: NumberKeyframeTrack, ValueTypeName: 'number' // ValueBufferType is inherited // DefaultInterpolation is inherited } ); /** * * A Track that interpolates Strings * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function StringKeyframeTrack( name, times, values, interpolation ) { KeyframeTrackConstructor.call( this, name, times, values, interpolation ); } StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), { constructor: StringKeyframeTrack, ValueTypeName: 'string', ValueBufferType: Array, DefaultInterpolation: InterpolateDiscrete, InterpolantFactoryMethodLinear: undefined, InterpolantFactoryMethodSmooth: undefined } ); /** * * A Track of Boolean keyframe values. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function BooleanKeyframeTrack( name, times, values ) { KeyframeTrackConstructor.call( this, name, times, values ); } BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), { constructor: BooleanKeyframeTrack, ValueTypeName: 'bool', ValueBufferType: Array, DefaultInterpolation: InterpolateDiscrete, InterpolantFactoryMethodLinear: undefined, InterpolantFactoryMethodSmooth: undefined // Note: Actually this track could have a optimized / compressed // representation of a single value and a custom interpolant that // computes "firstValue ^ isOdd( index )". } ); /** * * A Track of keyframe values that represent color. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function ColorKeyframeTrack( name, times, values, interpolation ) { KeyframeTrackConstructor.call( this, name, times, values, interpolation ); } ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrackPrototype ), { constructor: ColorKeyframeTrack, ValueTypeName: 'color' // ValueBufferType is inherited // DefaultInterpolation is inherited // Note: Very basic implementation and nothing special yet. // However, this is the place for color space parameterization. } ); /** * * A timed sequence of keyframes for a specific property. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function KeyframeTrack( name, times, values, interpolation ) { KeyframeTrackConstructor.apply( this, arguments ); } KeyframeTrack.prototype = KeyframeTrackPrototype; KeyframeTrackPrototype.constructor = KeyframeTrack; // Static methods: Object.assign( KeyframeTrack, { // Serialization (in static context, because of constructor invocation // and automatic invocation of .toJSON): parse: function( json ) { if( json.type === undefined ) { throw new Error( "track type undefined, can not parse" ); } var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); if ( json.times === undefined ) { var times = [], values = []; AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); json.times = times; json.values = values; } // derived classes can define a static parse method if ( trackType.parse !== undefined ) { return trackType.parse( json ); } else { // by default, we asssume a constructor compatible with the base return new trackType( json.name, json.times, json.values, json.interpolation ); } }, toJSON: function( track ) { var trackType = track.constructor; var json; // derived classes can define a static toJSON method if ( trackType.toJSON !== undefined ) { json = trackType.toJSON( track ); } else { // by default, we assume the data can be serialized as-is json = { 'name': track.name, 'times': AnimationUtils.convertArray( track.times, Array ), 'values': AnimationUtils.convertArray( track.values, Array ) }; var interpolation = track.getInterpolation(); if ( interpolation !== track.DefaultInterpolation ) { json.interpolation = interpolation; } } json.type = track.ValueTypeName; // mandatory return json; }, _getTrackTypeForValueTypeName: function( typeName ) { switch( typeName.toLowerCase() ) { case "scalar": case "double": case "float": case "number": case "integer": return NumberKeyframeTrack; case "vector": case "vector2": case "vector3": case "vector4": return VectorKeyframeTrack; case "color": return ColorKeyframeTrack; case "quaternion": return QuaternionKeyframeTrack; case "bool": case "boolean": return BooleanKeyframeTrack; case "string": return StringKeyframeTrack; } throw new Error( "Unsupported typeName: " + typeName ); } } ); /** * * Reusable set of Tracks that represent an animation. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ */ function AnimationClip( name, duration, tracks ) { this.name = name; this.tracks = tracks; this.duration = ( duration !== undefined ) ? duration : -1; this.uuid = _Math.generateUUID(); // this means it should figure out its duration by scanning the tracks if ( this.duration < 0 ) { this.resetDuration(); } this.optimize(); } AnimationClip.prototype = { constructor: AnimationClip, resetDuration: function() { var tracks = this.tracks, duration = 0; for ( var i = 0, n = tracks.length; i !== n; ++ i ) { var track = this.tracks[ i ]; duration = Math.max( duration, track.times[ track.times.length - 1 ] ); } this.duration = duration; }, trim: function() { for ( var i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].trim( 0, this.duration ); } return this; }, optimize: function() { for ( var i = 0; i < this.tracks.length; i ++ ) { this.tracks[ i ].optimize(); } return this; } }; // Static methods: Object.assign( AnimationClip, { parse: function( json ) { var tracks = [], jsonTracks = json.tracks, frameTime = 1.0 / ( json.fps || 1.0 ); for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); } return new AnimationClip( json.name, json.duration, tracks ); }, toJSON: function( clip ) { var tracks = [], clipTracks = clip.tracks; var json = { 'name': clip.name, 'duration': clip.duration, 'tracks': tracks }; for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); } return json; }, CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) { var numMorphTargets = morphTargetSequence.length; var tracks = []; for ( var i = 0; i < numMorphTargets; i ++ ) { var times = []; var values = []; times.push( ( i + numMorphTargets - 1 ) % numMorphTargets, i, ( i + 1 ) % numMorphTargets ); values.push( 0, 1, 0 ); var order = AnimationUtils.getKeyframeOrder( times ); times = AnimationUtils.sortedArray( times, 1, order ); values = AnimationUtils.sortedArray( values, 1, order ); // if there is a key at the first frame, duplicate it as the // last frame as well for perfect loop. if ( ! noLoop && times[ 0 ] === 0 ) { times.push( numMorphTargets ); values.push( values[ 0 ] ); } tracks.push( new NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', times, values ).scale( 1.0 / fps ) ); } return new AnimationClip( name, -1, tracks ); }, findByName: function( objectOrClipArray, name ) { var clipArray = objectOrClipArray; if ( ! Array.isArray( objectOrClipArray ) ) { var o = objectOrClipArray; clipArray = o.geometry && o.geometry.animations || o.animations; } for ( var i = 0; i < clipArray.length; i ++ ) { if ( clipArray[ i ].name === name ) { return clipArray[ i ]; } } return null; }, CreateClipsFromMorphTargetSequences: function( morphTargets, fps, noLoop ) { var animationToMorphTargets = {}; // tested with https://regex101.com/ on trick sequences // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 var pattern = /^([\w-]*?)([\d]+)$/; // sort morph target names into animation groups based // patterns like Walk_001, Walk_002, Run_001, Run_002 for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { var morphTarget = morphTargets[ i ]; var parts = morphTarget.name.match( pattern ); if ( parts && parts.length > 1 ) { var name = parts[ 1 ]; var animationMorphTargets = animationToMorphTargets[ name ]; if ( ! animationMorphTargets ) { animationToMorphTargets[ name ] = animationMorphTargets = []; } animationMorphTargets.push( morphTarget ); } } var clips = []; for ( var name in animationToMorphTargets ) { clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); } return clips; }, // parse the animation.hierarchy format parseAnimation: function( animation, bones ) { if ( ! animation ) { console.error( " no animation in JSONLoader data" ); return null; } var addNonemptyTrack = function( trackType, trackName, animationKeys, propertyName, destTracks ) { // only return track if there are actually keys. if ( animationKeys.length !== 0 ) { var times = []; var values = []; AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); // empty keys are filtered out, so check again if ( times.length !== 0 ) { destTracks.push( new trackType( trackName, times, values ) ); } } }; var tracks = []; var clipName = animation.name || 'default'; // automatic length determination in AnimationClip. var duration = animation.length || -1; var fps = animation.fps || 30; var hierarchyTracks = animation.hierarchy || []; for ( var h = 0; h < hierarchyTracks.length; h ++ ) { var animationKeys = hierarchyTracks[ h ].keys; // skip empty tracks if ( ! animationKeys || animationKeys.length === 0 ) continue; // process morph targets in a way exactly compatible // with AnimationHandler.init( animation ) if ( animationKeys[0].morphTargets ) { // figure out all morph targets used in this track var morphTargetNames = {}; for ( var k = 0; k < animationKeys.length; k ++ ) { if ( animationKeys[k].morphTargets ) { for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; } } } // create a track for each morph target with all zero // morphTargetInfluences except for the keys in which // the morphTarget is named. for ( var morphTargetName in morphTargetNames ) { var times = []; var values = []; for ( var m = 0; m !== animationKeys[k].morphTargets.length; ++ m ) { var animationKey = animationKeys[k]; times.push( animationKey.time ); values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); } tracks.push( new NumberKeyframeTrack('.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); } duration = morphTargetNames.length * ( fps || 1.0 ); } else { // ...assume skeletal animation var boneName = '.bones[' + bones[ h ].name + ']'; addNonemptyTrack( VectorKeyframeTrack, boneName + '.position', animationKeys, 'pos', tracks ); addNonemptyTrack( QuaternionKeyframeTrack, boneName + '.quaternion', animationKeys, 'rot', tracks ); addNonemptyTrack( VectorKeyframeTrack, boneName + '.scale', animationKeys, 'scl', tracks ); } } if ( tracks.length === 0 ) { return null; } var clip = new AnimationClip( clipName, duration, tracks ); return clip; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function MaterialLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.textures = {}; } Object.assign( MaterialLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); }, onProgress, onError ); }, setTextures: function ( value ) { this.textures = value; }, parse: function ( json ) { var textures = this.textures; function getTexture( name ) { if ( textures[ name ] === undefined ) { console.warn( 'THREE.MaterialLoader: Undefined texture', name ); } return textures[ name ]; } var material = new Materials[ json.type ](); if ( json.uuid !== undefined ) material.uuid = json.uuid; if ( json.name !== undefined ) material.name = json.name; if ( json.color !== undefined ) material.color.setHex( json.color ); if ( json.roughness !== undefined ) material.roughness = json.roughness; if ( json.metalness !== undefined ) material.metalness = json.metalness; if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); if ( json.specular !== undefined ) material.specular.setHex( json.specular ); if ( json.shininess !== undefined ) material.shininess = json.shininess; if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat; if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness; if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; if ( json.fog !== undefined ) material.fog = json.fog; if ( json.shading !== undefined ) material.shading = json.shading; if ( json.blending !== undefined ) material.blending = json.blending; if ( json.side !== undefined ) material.side = json.side; if ( json.opacity !== undefined ) material.opacity = json.opacity; if ( json.transparent !== undefined ) material.transparent = json.transparent; if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; if ( json.skinning !== undefined ) material.skinning = json.skinning; if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; // for PointsMaterial if ( json.size !== undefined ) material.size = json.size; if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; // maps if ( json.map !== undefined ) material.map = getTexture( json.map ); if ( json.alphaMap !== undefined ) { material.alphaMap = getTexture( json.alphaMap ); material.transparent = true; } if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); if ( json.normalScale !== undefined ) { var normalScale = json.normalScale; if ( Array.isArray( normalScale ) === false ) { // Blender exporter used to export a scalar. See #7459 normalScale = [ normalScale, normalScale ]; } material.normalScale = new Vector2().fromArray( normalScale ); } if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); // MultiMaterial if ( json.materials !== undefined ) { for ( var i = 0, l = json.materials.length; i < l; i ++ ) { material.materials.push( this.parse( json.materials[ i ] ) ); } } return material; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function BufferGeometryLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( BufferGeometryLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { onLoad( scope.parse( JSON.parse( text ) ) ); }, onProgress, onError ); }, parse: function ( json ) { var geometry = new BufferGeometry(); var index = json.data.index; var TYPED_ARRAYS = { 'Int8Array': Int8Array, 'Uint8Array': Uint8Array, 'Uint8ClampedArray': Uint8ClampedArray, 'Int16Array': Int16Array, 'Uint16Array': Uint16Array, 'Int32Array': Int32Array, 'Uint32Array': Uint32Array, 'Float32Array': Float32Array, 'Float64Array': Float64Array }; if ( index !== undefined ) { var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); } var attributes = json.data.attributes; for ( var key in attributes ) { var attribute = attributes[ key ]; var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); } var groups = json.data.groups || json.data.drawcalls || json.data.offsets; if ( groups !== undefined ) { for ( var i = 0, n = groups.length; i !== n; ++ i ) { var group = groups[ i ]; geometry.addGroup( group.start, group.count, group.materialIndex ); } } var boundingSphere = json.data.boundingSphere; if ( boundingSphere !== undefined ) { var center = new Vector3(); if ( boundingSphere.center !== undefined ) { center.fromArray( boundingSphere.center ); } geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); } return geometry; } } ); /** * @author alteredq / http://alteredqualia.com/ */ function Loader() { this.onLoadStart = function () {}; this.onLoadProgress = function () {}; this.onLoadComplete = function () {}; } Loader.prototype = { constructor: Loader, crossOrigin: undefined, extractUrlBase: function ( url ) { var parts = url.split( '/' ); if ( parts.length === 1 ) return './'; parts.pop(); return parts.join( '/' ) + '/'; }, initMaterials: function ( materials, texturePath, crossOrigin ) { var array = []; for ( var i = 0; i < materials.length; ++ i ) { array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); } return array; }, createMaterial: ( function () { var color, textureLoader, materialLoader; return function createMaterial( m, texturePath, crossOrigin ) { if ( color === undefined ) color = new Color(); if ( textureLoader === undefined ) textureLoader = new TextureLoader(); if ( materialLoader === undefined ) materialLoader = new MaterialLoader(); // convert from old material format var textures = {}; function loadTexture( path, repeat, offset, wrap, anisotropy ) { var fullPath = texturePath + path; var loader = Loader.Handlers.get( fullPath ); var texture; if ( loader !== null ) { texture = loader.load( fullPath ); } else { textureLoader.setCrossOrigin( crossOrigin ); texture = textureLoader.load( fullPath ); } if ( repeat !== undefined ) { texture.repeat.fromArray( repeat ); if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; } if ( offset !== undefined ) { texture.offset.fromArray( offset ); } if ( wrap !== undefined ) { if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping; if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping; if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping; if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping; } if ( anisotropy !== undefined ) { texture.anisotropy = anisotropy; } var uuid = _Math.generateUUID(); textures[ uuid ] = texture; return uuid; } // var json = { uuid: _Math.generateUUID(), type: 'MeshLambertMaterial' }; for ( var name in m ) { var value = m[ name ]; switch ( name ) { case 'DbgColor': case 'DbgIndex': case 'opticalDensity': case 'illumination': break; case 'DbgName': json.name = value; break; case 'blending': json.blending = BlendingMode[ value ]; break; case 'colorAmbient': case 'mapAmbient': console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' ); break; case 'colorDiffuse': json.color = color.fromArray( value ).getHex(); break; case 'colorSpecular': json.specular = color.fromArray( value ).getHex(); break; case 'colorEmissive': json.emissive = color.fromArray( value ).getHex(); break; case 'specularCoef': json.shininess = value; break; case 'shading': if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial'; break; case 'mapDiffuse': json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); break; case 'mapDiffuseRepeat': case 'mapDiffuseOffset': case 'mapDiffuseWrap': case 'mapDiffuseAnisotropy': break; case 'mapEmissive': json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); break; case 'mapEmissiveRepeat': case 'mapEmissiveOffset': case 'mapEmissiveWrap': case 'mapEmissiveAnisotropy': break; case 'mapLight': json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); break; case 'mapLightRepeat': case 'mapLightOffset': case 'mapLightWrap': case 'mapLightAnisotropy': break; case 'mapAO': json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); break; case 'mapAORepeat': case 'mapAOOffset': case 'mapAOWrap': case 'mapAOAnisotropy': break; case 'mapBump': json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); break; case 'mapBumpScale': json.bumpScale = value; break; case 'mapBumpRepeat': case 'mapBumpOffset': case 'mapBumpWrap': case 'mapBumpAnisotropy': break; case 'mapNormal': json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); break; case 'mapNormalFactor': json.normalScale = [ value, value ]; break; case 'mapNormalRepeat': case 'mapNormalOffset': case 'mapNormalWrap': case 'mapNormalAnisotropy': break; case 'mapSpecular': json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); break; case 'mapSpecularRepeat': case 'mapSpecularOffset': case 'mapSpecularWrap': case 'mapSpecularAnisotropy': break; case 'mapMetalness': json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); break; case 'mapMetalnessRepeat': case 'mapMetalnessOffset': case 'mapMetalnessWrap': case 'mapMetalnessAnisotropy': break; case 'mapRoughness': json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); break; case 'mapRoughnessRepeat': case 'mapRoughnessOffset': case 'mapRoughnessWrap': case 'mapRoughnessAnisotropy': break; case 'mapAlpha': json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); break; case 'mapAlphaRepeat': case 'mapAlphaOffset': case 'mapAlphaWrap': case 'mapAlphaAnisotropy': break; case 'flipSided': json.side = BackSide; break; case 'doubleSided': json.side = DoubleSide; break; case 'transparency': console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' ); json.opacity = value; break; case 'depthTest': case 'depthWrite': case 'colorWrite': case 'opacity': case 'reflectivity': case 'transparent': case 'visible': case 'wireframe': json[ name ] = value; break; case 'vertexColors': if ( value === true ) json.vertexColors = VertexColors; if ( value === 'face' ) json.vertexColors = FaceColors; break; default: console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); break; } } if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; if ( json.opacity < 1 ) json.transparent = true; materialLoader.setTextures( textures ); return materialLoader.parse( json ); }; } )() }; Loader.Handlers = { handlers: [], add: function ( regex, loader ) { this.handlers.push( regex, loader ); }, get: function ( file ) { var handlers = this.handlers; for ( var i = 0, l = handlers.length; i < l; i += 2 ) { var regex = handlers[ i ]; var loader = handlers[ i + 1 ]; if ( regex.test( file ) ) { return loader; } } return null; } }; /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function JSONLoader( manager ) { if ( typeof manager === 'boolean' ) { console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); manager = undefined; } this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.withCredentials = false; } Object.assign( JSONLoader.prototype, { load: function( url, onLoad, onProgress, onError ) { var scope = this; var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : Loader.prototype.extractUrlBase( url ); var loader = new FileLoader( this.manager ); loader.setWithCredentials( this.withCredentials ); loader.load( url, function ( text ) { var json = JSON.parse( text ); var metadata = json.metadata; if ( metadata !== undefined ) { var type = metadata.type; if ( type !== undefined ) { if ( type.toLowerCase() === 'object' ) { console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); return; } if ( type.toLowerCase() === 'scene' ) { console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); return; } } } var object = scope.parse( json, texturePath ); onLoad( object.geometry, object.materials ); }, onProgress, onError ); }, setTexturePath: function ( value ) { this.texturePath = value; }, parse: function ( json, texturePath ) { var geometry = new Geometry(), scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; parseModel( scale ); parseSkin(); parseMorphing( scale ); parseAnimations(); geometry.computeFaceNormals(); geometry.computeBoundingSphere(); function parseModel( scale ) { function isBitSet( value, position ) { return value & ( 1 << position ); } var i, j, fi, offset, zLength, colorIndex, normalIndex, uvIndex, materialIndex, type, isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor, vertex, face, faceA, faceB, hex, normal, uvLayer, uv, u, v, faces = json.faces, vertices = json.vertices, normals = json.normals, colors = json.colors, nUvLayers = 0; if ( json.uvs !== undefined ) { // disregard empty arrays for ( i = 0; i < json.uvs.length; i ++ ) { if ( json.uvs[ i ].length ) nUvLayers ++; } for ( i = 0; i < nUvLayers; i ++ ) { geometry.faceVertexUvs[ i ] = []; } } offset = 0; zLength = vertices.length; while ( offset < zLength ) { vertex = new Vector3(); vertex.x = vertices[ offset ++ ] * scale; vertex.y = vertices[ offset ++ ] * scale; vertex.z = vertices[ offset ++ ] * scale; geometry.vertices.push( vertex ); } offset = 0; zLength = faces.length; while ( offset < zLength ) { type = faces[ offset ++ ]; isQuad = isBitSet( type, 0 ); hasMaterial = isBitSet( type, 1 ); hasFaceVertexUv = isBitSet( type, 3 ); hasFaceNormal = isBitSet( type, 4 ); hasFaceVertexNormal = isBitSet( type, 5 ); hasFaceColor = isBitSet( type, 6 ); hasFaceVertexColor = isBitSet( type, 7 ); // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); if ( isQuad ) { faceA = new Face3(); faceA.a = faces[ offset ]; faceA.b = faces[ offset + 1 ]; faceA.c = faces[ offset + 3 ]; faceB = new Face3(); faceB.a = faces[ offset + 1 ]; faceB.b = faces[ offset + 2 ]; faceB.c = faces[ offset + 3 ]; offset += 4; if ( hasMaterial ) { materialIndex = faces[ offset ++ ]; faceA.materialIndex = materialIndex; faceB.materialIndex = materialIndex; } // to get face <=> uv index correspondence fi = geometry.faces.length; if ( hasFaceVertexUv ) { for ( i = 0; i < nUvLayers; i ++ ) { uvLayer = json.uvs[ i ]; geometry.faceVertexUvs[ i ][ fi ] = []; geometry.faceVertexUvs[ i ][ fi + 1 ] = []; for ( j = 0; j < 4; j ++ ) { uvIndex = faces[ offset ++ ]; u = uvLayer[ uvIndex * 2 ]; v = uvLayer[ uvIndex * 2 + 1 ]; uv = new Vector2( u, v ); if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); } } } if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; faceA.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); faceB.normal.copy( faceA.normal ); } if ( hasFaceVertexNormal ) { for ( i = 0; i < 4; i ++ ) { normalIndex = faces[ offset ++ ] * 3; normal = new Vector3( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); if ( i !== 2 ) faceA.vertexNormals.push( normal ); if ( i !== 0 ) faceB.vertexNormals.push( normal ); } } if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; faceA.color.setHex( hex ); faceB.color.setHex( hex ); } if ( hasFaceVertexColor ) { for ( i = 0; i < 4; i ++ ) { colorIndex = faces[ offset ++ ]; hex = colors[ colorIndex ]; if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); } } geometry.faces.push( faceA ); geometry.faces.push( faceB ); } else { face = new Face3(); face.a = faces[ offset ++ ]; face.b = faces[ offset ++ ]; face.c = faces[ offset ++ ]; if ( hasMaterial ) { materialIndex = faces[ offset ++ ]; face.materialIndex = materialIndex; } // to get face <=> uv index correspondence fi = geometry.faces.length; if ( hasFaceVertexUv ) { for ( i = 0; i < nUvLayers; i ++ ) { uvLayer = json.uvs[ i ]; geometry.faceVertexUvs[ i ][ fi ] = []; for ( j = 0; j < 3; j ++ ) { uvIndex = faces[ offset ++ ]; u = uvLayer[ uvIndex * 2 ]; v = uvLayer[ uvIndex * 2 + 1 ]; uv = new Vector2( u, v ); geometry.faceVertexUvs[ i ][ fi ].push( uv ); } } } if ( hasFaceNormal ) { normalIndex = faces[ offset ++ ] * 3; face.normal.set( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); } if ( hasFaceVertexNormal ) { for ( i = 0; i < 3; i ++ ) { normalIndex = faces[ offset ++ ] * 3; normal = new Vector3( normals[ normalIndex ++ ], normals[ normalIndex ++ ], normals[ normalIndex ] ); face.vertexNormals.push( normal ); } } if ( hasFaceColor ) { colorIndex = faces[ offset ++ ]; face.color.setHex( colors[ colorIndex ] ); } if ( hasFaceVertexColor ) { for ( i = 0; i < 3; i ++ ) { colorIndex = faces[ offset ++ ]; face.vertexColors.push( new Color( colors[ colorIndex ] ) ); } } geometry.faces.push( face ); } } } function parseSkin() { var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; if ( json.skinWeights ) { for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { var x = json.skinWeights[ i ]; var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; geometry.skinWeights.push( new Vector4( x, y, z, w ) ); } } if ( json.skinIndices ) { for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { var a = json.skinIndices[ i ]; var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; geometry.skinIndices.push( new Vector4( a, b, c, d ) ); } } geometry.bones = json.bones; if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); } } function parseMorphing( scale ) { if ( json.morphTargets !== undefined ) { for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { geometry.morphTargets[ i ] = {}; geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; geometry.morphTargets[ i ].vertices = []; var dstVertices = geometry.morphTargets[ i ].vertices; var srcVertices = json.morphTargets[ i ].vertices; for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { var vertex = new Vector3(); vertex.x = srcVertices[ v ] * scale; vertex.y = srcVertices[ v + 1 ] * scale; vertex.z = srcVertices[ v + 2 ] * scale; dstVertices.push( vertex ); } } } if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); var faces = geometry.faces; var morphColors = json.morphColors[ 0 ].colors; for ( var i = 0, l = faces.length; i < l; i ++ ) { faces[ i ].color.fromArray( morphColors, i * 3 ); } } } function parseAnimations() { var outputAnimations = []; // parse old style Bone/Hierarchy animations var animations = []; if ( json.animation !== undefined ) { animations.push( json.animation ); } if ( json.animations !== undefined ) { if ( json.animations.length ) { animations = animations.concat( json.animations ); } else { animations.push( json.animations ); } } for ( var i = 0; i < animations.length; i ++ ) { var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); if ( clip ) outputAnimations.push( clip ); } // parse implicit morph animations if ( geometry.morphTargets ) { // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); outputAnimations = outputAnimations.concat( morphAnimationClips ); } if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; } if ( json.materials === undefined || json.materials.length === 0 ) { return { geometry: geometry }; } else { var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); return { geometry: geometry, materials: materials }; } } } ); /** * @author mrdoob / http://mrdoob.com/ */ function ObjectLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; this.texturePath = ''; } Object.assign( ObjectLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { if ( this.texturePath === '' ) { this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); } var scope = this; var loader = new FileLoader( scope.manager ); loader.load( url, function ( text ) { var json = null; try { json = JSON.parse( text ); } catch ( error ) { console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); return; } var metadata = json.metadata; if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { console.error( 'THREE.ObjectLoader: Can\'t load ' + url + '. Use THREE.JSONLoader instead.' ); return; } scope.parse( json, onLoad ); }, onProgress, onError ); }, setTexturePath: function ( value ) { this.texturePath = value; }, setCrossOrigin: function ( value ) { this.crossOrigin = value; }, parse: function ( json, onLoad ) { var geometries = this.parseGeometries( json.geometries ); var images = this.parseImages( json.images, function () { if ( onLoad !== undefined ) onLoad( object ); } ); var textures = this.parseTextures( json.textures, images ); var materials = this.parseMaterials( json.materials, textures ); var object = this.parseObject( json.object, geometries, materials ); if ( json.animations ) { object.animations = this.parseAnimations( json.animations ); } if ( json.images === undefined || json.images.length === 0 ) { if ( onLoad !== undefined ) onLoad( object ); } return object; }, parseGeometries: function ( json ) { var geometries = {}; if ( json !== undefined ) { var geometryLoader = new JSONLoader(); var bufferGeometryLoader = new BufferGeometryLoader(); for ( var i = 0, l = json.length; i < l; i ++ ) { var geometry; var data = json[ i ]; switch ( data.type ) { case 'PlaneGeometry': case 'PlaneBufferGeometry': geometry = new Geometries[ data.type ]( data.width, data.height, data.widthSegments, data.heightSegments ); break; case 'BoxGeometry': case 'BoxBufferGeometry': case 'CubeGeometry': // backwards compatible geometry = new Geometries[ data.type ]( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); break; case 'CircleGeometry': case 'CircleBufferGeometry': geometry = new Geometries[ data.type ]( data.radius, data.segments, data.thetaStart, data.thetaLength ); break; case 'CylinderGeometry': case 'CylinderBufferGeometry': geometry = new Geometries[ data.type ]( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); break; case 'ConeGeometry': case 'ConeBufferGeometry': geometry = new Geometries[ data.type ]( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); break; case 'SphereGeometry': case 'SphereBufferGeometry': geometry = new Geometries[ data.type ]( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); break; case 'DodecahedronGeometry': case 'IcosahedronGeometry': case 'OctahedronGeometry': case 'TetrahedronGeometry': geometry = new Geometries[ data.type ]( data.radius, data.detail ); break; case 'RingGeometry': case 'RingBufferGeometry': geometry = new Geometries[ data.type ]( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength ); break; case 'TorusGeometry': case 'TorusBufferGeometry': geometry = new Geometries[ data.type ]( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc ); break; case 'TorusKnotGeometry': case 'TorusKnotBufferGeometry': geometry = new Geometries[ data.type ]( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q ); break; case 'LatheGeometry': case 'LatheBufferGeometry': geometry = new Geometries[ data.type ]( data.points, data.segments, data.phiStart, data.phiLength ); break; case 'BufferGeometry': geometry = bufferGeometryLoader.parse( data ); break; case 'Geometry': geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; break; default: console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); continue; } geometry.uuid = data.uuid; if ( data.name !== undefined ) geometry.name = data.name; geometries[ data.uuid ] = geometry; } } return geometries; }, parseMaterials: function ( json, textures ) { var materials = {}; if ( json !== undefined ) { var loader = new MaterialLoader(); loader.setTextures( textures ); for ( var i = 0, l = json.length; i < l; i ++ ) { var material = loader.parse( json[ i ] ); materials[ material.uuid ] = material; } } return materials; }, parseAnimations: function ( json ) { var animations = []; for ( var i = 0; i < json.length; i ++ ) { var clip = AnimationClip.parse( json[ i ] ); animations.push( clip ); } return animations; }, parseImages: function ( json, onLoad ) { var scope = this; var images = {}; function loadImage( url ) { scope.manager.itemStart( url ); return loader.load( url, function () { scope.manager.itemEnd( url ); }, undefined, function () { scope.manager.itemError( url ); } ); } if ( json !== undefined && json.length > 0 ) { var manager = new LoadingManager( onLoad ); var loader = new ImageLoader( manager ); loader.setCrossOrigin( this.crossOrigin ); for ( var i = 0, l = json.length; i < l; i ++ ) { var image = json[ i ]; var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; images[ image.uuid ] = loadImage( path ); } } return images; }, parseTextures: function ( json, images ) { function parseConstant( value, type ) { if ( typeof( value ) === 'number' ) return value; console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); return type[ value ]; } var textures = {}; if ( json !== undefined ) { for ( var i = 0, l = json.length; i < l; i ++ ) { var data = json[ i ]; if ( data.image === undefined ) { console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); } if ( images[ data.image ] === undefined ) { console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); } var texture = new Texture( images[ data.image ] ); texture.needsUpdate = true; texture.uuid = data.uuid; if ( data.name !== undefined ) texture.name = data.name; if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TextureMapping ); if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); if ( data.wrap !== undefined ) { texture.wrapS = parseConstant( data.wrap[ 0 ], TextureWrapping ); texture.wrapT = parseConstant( data.wrap[ 1 ], TextureWrapping ); } if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TextureFilter ); if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TextureFilter ); if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; if ( data.flipY !== undefined ) texture.flipY = data.flipY; textures[ data.uuid ] = texture; } } return textures; }, parseObject: function () { var matrix = new Matrix4(); return function parseObject( data, geometries, materials ) { var object; function getGeometry( name ) { if ( geometries[ name ] === undefined ) { console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); } return geometries[ name ]; } function getMaterial( name ) { if ( name === undefined ) return undefined; if ( materials[ name ] === undefined ) { console.warn( 'THREE.ObjectLoader: Undefined material', name ); } return materials[ name ]; } switch ( data.type ) { case 'Scene': object = new Scene(); if ( data.background !== undefined ) { if ( Number.isInteger( data.background ) ) { object.background = new Color( data.background ); } } if ( data.fog !== undefined ) { if ( data.fog.type === 'Fog' ) { object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); } else if ( data.fog.type === 'FogExp2' ) { object.fog = new FogExp2( data.fog.color, data.fog.density ); } } break; case 'PerspectiveCamera': object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); if ( data.focus !== undefined ) object.focus = data.focus; if ( data.zoom !== undefined ) object.zoom = data.zoom; if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); break; case 'OrthographicCamera': object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); break; case 'AmbientLight': object = new AmbientLight( data.color, data.intensity ); break; case 'DirectionalLight': object = new DirectionalLight( data.color, data.intensity ); break; case 'PointLight': object = new PointLight( data.color, data.intensity, data.distance, data.decay ); break; case 'SpotLight': object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); break; case 'HemisphereLight': object = new HemisphereLight( data.color, data.groundColor, data.intensity ); break; case 'Mesh': var geometry = getGeometry( data.geometry ); var material = getMaterial( data.material ); if ( geometry.bones && geometry.bones.length > 0 ) { object = new SkinnedMesh( geometry, material ); } else { object = new Mesh( geometry, material ); } break; case 'LOD': object = new LOD(); break; case 'Line': object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); break; case 'LineSegments': object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case 'PointCloud': case 'Points': object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); break; case 'Sprite': object = new Sprite( getMaterial( data.material ) ); break; case 'Group': object = new Group(); break; case 'SkinnedMesh': console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh type. Instantiates Object3D instead.' ); default: object = new Object3D(); } object.uuid = data.uuid; if ( data.name !== undefined ) object.name = data.name; if ( data.matrix !== undefined ) { matrix.fromArray( data.matrix ); matrix.decompose( object.position, object.quaternion, object.scale ); } else { if ( data.position !== undefined ) object.position.fromArray( data.position ); if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); } if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; if ( data.shadow ) { if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); } if ( data.visible !== undefined ) object.visible = data.visible; if ( data.userData !== undefined ) object.userData = data.userData; if ( data.children !== undefined ) { for ( var child in data.children ) { object.add( this.parseObject( data.children[ child ], geometries, materials ) ); } } if ( data.type === 'LOD' ) { var levels = data.levels; for ( var l = 0; l < levels.length; l ++ ) { var level = levels[ l ]; var child = object.getObjectByProperty( 'uuid', level.object ); if ( child !== undefined ) { object.addLevel( child, level.distance ); } } } return object; }; }() } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Extensible curve object * * Some common of Curve methods * .getPoint(t), getTangent(t) * .getPointAt(u), getTangentAt(u) * .getPoints(), .getSpacedPoints() * .getLength() * .updateArcLengths() * * This following classes subclasses THREE.Curve: * * -- 2d classes -- * THREE.LineCurve * THREE.QuadraticBezierCurve * THREE.CubicBezierCurve * THREE.SplineCurve * THREE.ArcCurve * THREE.EllipseCurve * * -- 3d classes -- * THREE.LineCurve3 * THREE.QuadraticBezierCurve3 * THREE.CubicBezierCurve3 * THREE.SplineCurve3 * * A series of curves can be represented as a THREE.CurvePath * **/ /************************************************************** * Abstract Curve base class **************************************************************/ function Curve() {} Curve.prototype = { constructor: Curve, // Virtual base class method to overwrite and implement in subclasses // - t [0 .. 1] getPoint: function ( t ) { console.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); return null; }, // Get point at relative position in curve according to arc length // - u [0 .. 1] getPointAt: function ( u ) { var t = this.getUtoTmapping( u ); return this.getPoint( t ); }, // Get sequence of points using getPoint( t ) getPoints: function ( divisions ) { if ( ! divisions ) divisions = 5; var points = []; for ( var d = 0; d <= divisions; d ++ ) { points.push( this.getPoint( d / divisions ) ); } return points; }, // Get sequence of points using getPointAt( u ) getSpacedPoints: function ( divisions ) { if ( ! divisions ) divisions = 5; var points = []; for ( var d = 0; d <= divisions; d ++ ) { points.push( this.getPointAt( d / divisions ) ); } return points; }, // Get total curve arc length getLength: function () { var lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; }, // Get list of cumulative segment lengths getLengths: function ( divisions ) { if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200; if ( this.cacheArcLengths && ( this.cacheArcLengths.length === divisions + 1 ) && ! this.needsUpdate ) { //console.log( "cached", this.cacheArcLengths ); return this.cacheArcLengths; } this.needsUpdate = false; var cache = []; var current, last = this.getPoint( 0 ); var p, sum = 0; cache.push( 0 ); for ( p = 1; p <= divisions; p ++ ) { current = this.getPoint ( p / divisions ); sum += current.distanceTo( last ); cache.push( sum ); last = current; } this.cacheArcLengths = cache; return cache; // { sums: cache, sum:sum }; Sum is in the last element. }, updateArcLengths: function() { this.needsUpdate = true; this.getLengths(); }, // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping: function ( u, distance ) { var arcLengths = this.getLengths(); var i = 0, il = arcLengths.length; var targetArcLength; // The targeted u distance value to get if ( distance ) { targetArcLength = distance; } else { targetArcLength = u * arcLengths[ il - 1 ]; } //var time = Date.now(); // binary search for the index with largest value smaller than target u distance var low = 0, high = il - 1, comparison; while ( low <= high ) { i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats comparison = arcLengths[ i ] - targetArcLength; if ( comparison < 0 ) { low = i + 1; } else if ( comparison > 0 ) { high = i - 1; } else { high = i; break; // DONE } } i = high; //console.log('b' , i, low, high, Date.now()- time); if ( arcLengths[ i ] === targetArcLength ) { var t = i / ( il - 1 ); return t; } // we could get finer grain at lengths, or use simple interpolation between two points var lengthBefore = arcLengths[ i ]; var lengthAfter = arcLengths[ i + 1 ]; var segmentLength = lengthAfter - lengthBefore; // determine where we are between the 'before' and 'after' points var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; // add that fractional amount to t var t = ( i + segmentFraction ) / ( il - 1 ); return t; }, // Returns a unit vector tangent at t // In case any sub curve does not implement its tangent derivation, // 2 points a small delta apart will be used to find its gradient // which seems to give a reasonable approximation getTangent: function( t ) { var delta = 0.0001; var t1 = t - delta; var t2 = t + delta; // Capping in case of danger if ( t1 < 0 ) t1 = 0; if ( t2 > 1 ) t2 = 1; var pt1 = this.getPoint( t1 ); var pt2 = this.getPoint( t2 ); var vec = pt2.clone().sub( pt1 ); return vec.normalize(); }, getTangentAt: function ( u ) { var t = this.getUtoTmapping( u ); return this.getTangent( t ); }, computeFrenetFrames: function ( segments, closed ) { // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf var normal = new Vector3(); var tangents = []; var normals = []; var binormals = []; var vec = new Vector3(); var mat = new Matrix4(); var i, u, theta; // compute the tangent vectors for each segment on the curve for ( i = 0; i <= segments; i ++ ) { u = i / segments; tangents[ i ] = this.getTangentAt( u ); tangents[ i ].normalize(); } // select an initial normal vector perpendicular to the first tangent vector, // and in the direction of the minimum tangent xyz component normals[ 0 ] = new Vector3(); binormals[ 0 ] = new Vector3(); var min = Number.MAX_VALUE; var tx = Math.abs( tangents[ 0 ].x ); var ty = Math.abs( tangents[ 0 ].y ); var tz = Math.abs( tangents[ 0 ].z ); if ( tx <= min ) { min = tx; normal.set( 1, 0, 0 ); } if ( ty <= min ) { min = ty; normal.set( 0, 1, 0 ); } if ( tz <= min ) { normal.set( 0, 0, 1 ); } vec.crossVectors( tangents[ 0 ], normal ).normalize(); normals[ 0 ].crossVectors( tangents[ 0 ], vec ); binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); // compute the slowly-varying normal and binormal vectors for each segment on the curve for ( i = 1; i <= segments; i ++ ) { normals[ i ] = normals[ i - 1 ].clone(); binormals[ i ] = binormals[ i - 1 ].clone(); vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); if ( vec.length() > Number.EPSILON ) { vec.normalize(); theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); } binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if ( closed === true ) { theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); theta /= segments; if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { theta = - theta; } for ( i = 1; i <= segments; i ++ ) { // twist a little... normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } } return { tangents: tangents, normals: normals, binormals: binormals }; } }; // TODO: Transformation for Curves? /************************************************************** * 3D Curves **************************************************************/ // A Factory method for creating new curve subclasses Curve.create = function ( constructor, getPointFunc ) { constructor.prototype = Object.create( Curve.prototype ); constructor.prototype.constructor = constructor; constructor.prototype.getPoint = getPointFunc; return constructor; }; /************************************************************** * Line **************************************************************/ function LineCurve( v1, v2 ) { this.v1 = v1; this.v2 = v2; } LineCurve.prototype = Object.create( Curve.prototype ); LineCurve.prototype.constructor = LineCurve; LineCurve.prototype.isLineCurve = true; LineCurve.prototype.getPoint = function ( t ) { if ( t === 1 ) { return this.v2.clone(); } var point = this.v2.clone().sub( this.v1 ); point.multiplyScalar( t ).add( this.v1 ); return point; }; // Line curve is linear, so we can overwrite default getPointAt LineCurve.prototype.getPointAt = function ( u ) { return this.getPoint( u ); }; LineCurve.prototype.getTangent = function( t ) { var tangent = this.v2.clone().sub( this.v1 ); return tangent.normalize(); }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * **/ /************************************************************** * Curved Path - a curve path is simply a array of connected * curves, but retains the api of a curve **************************************************************/ function CurvePath() { this.curves = []; this.autoClose = false; // Automatically closes the path } CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { constructor: CurvePath, add: function ( curve ) { this.curves.push( curve ); }, closePath: function () { // Add a line curve if start and end of lines are not connected var startPoint = this.curves[ 0 ].getPoint( 0 ); var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); if ( ! startPoint.equals( endPoint ) ) { this.curves.push( new LineCurve( endPoint, startPoint ) ); } }, // To get accurate point with reference to // entire path distance at time t, // following has to be done: // 1. Length of each sub path have to be known // 2. Locate and identify type of curve // 3. Get t for the curve // 4. Return curve.getPointAt(t') getPoint: function ( t ) { var d = t * this.getLength(); var curveLengths = this.getCurveLengths(); var i = 0; // To think about boundaries points. while ( i < curveLengths.length ) { if ( curveLengths[ i ] >= d ) { var diff = curveLengths[ i ] - d; var curve = this.curves[ i ]; var segmentLength = curve.getLength(); var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; return curve.getPointAt( u ); } i ++; } return null; // loop where sum != 0, sum > d , sum+1 1 && !points[ points.length - 1 ].equals( points[ 0 ] ) ) { points.push( points[ 0 ] ); } return points; }, /************************************************************** * Create Geometries Helpers **************************************************************/ /// Generate geometry from path points (for Line or Points objects) createPointsGeometry: function ( divisions ) { var pts = this.getPoints( divisions ); return this.createGeometry( pts ); }, // Generate geometry from equidistant sampling along the path createSpacedPointsGeometry: function ( divisions ) { var pts = this.getSpacedPoints( divisions ); return this.createGeometry( pts ); }, createGeometry: function ( points ) { var geometry = new Geometry(); for ( var i = 0, l = points.length; i < l; i ++ ) { var point = points[ i ]; geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); } return geometry; } } ); /************************************************************** * Ellipse curve **************************************************************/ function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { this.aX = aX; this.aY = aY; this.xRadius = xRadius; this.yRadius = yRadius; this.aStartAngle = aStartAngle; this.aEndAngle = aEndAngle; this.aClockwise = aClockwise; this.aRotation = aRotation || 0; } EllipseCurve.prototype = Object.create( Curve.prototype ); EllipseCurve.prototype.constructor = EllipseCurve; EllipseCurve.prototype.isEllipseCurve = true; EllipseCurve.prototype.getPoint = function( t ) { var twoPi = Math.PI * 2; var deltaAngle = this.aEndAngle - this.aStartAngle; var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; // ensures that deltaAngle is 0 .. 2 PI while ( deltaAngle < 0 ) deltaAngle += twoPi; while ( deltaAngle > twoPi ) deltaAngle -= twoPi; if ( deltaAngle < Number.EPSILON ) { if ( samePoints ) { deltaAngle = 0; } else { deltaAngle = twoPi; } } if ( this.aClockwise === true && ! samePoints ) { if ( deltaAngle === twoPi ) { deltaAngle = - twoPi; } else { deltaAngle = deltaAngle - twoPi; } } var angle = this.aStartAngle + t * deltaAngle; var x = this.aX + this.xRadius * Math.cos( angle ); var y = this.aY + this.yRadius * Math.sin( angle ); if ( this.aRotation !== 0 ) { var cos = Math.cos( this.aRotation ); var sin = Math.sin( this.aRotation ); var tx = x - this.aX; var ty = y - this.aY; // Rotate the point about the center of the ellipse. x = tx * cos - ty * sin + this.aX; y = tx * sin + ty * cos + this.aY; } return new Vector2( x, y ); }; /** * @author zz85 / http://www.lab4games.net/zz85/blog */ var CurveUtils = { tangentQuadraticBezier: function ( t, p0, p1, p2 ) { return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); }, // Puay Bing, thanks for helping with this derivative! tangentCubicBezier: function ( t, p0, p1, p2, p3 ) { return - 3 * p0 * ( 1 - t ) * ( 1 - t ) + 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) + 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 + 3 * t * t * p3; }, tangentSpline: function ( t, p0, p1, p2, p3 ) { // To check if my formulas are correct var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 var h11 = 3 * t * t - 2 * t; // t3 − t2 return h00 + h10 + h01 + h11; }, // Catmull-Rom interpolate: function( p0, p1, p2, p3, t ) { var v0 = ( p2 - p0 ) * 0.5; var v1 = ( p3 - p1 ) * 0.5; var t2 = t * t; var t3 = t * t2; return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } }; /************************************************************** * Spline curve **************************************************************/ function SplineCurve( points /* array of Vector2 */ ) { this.points = ( points === undefined ) ? [] : points; } SplineCurve.prototype = Object.create( Curve.prototype ); SplineCurve.prototype.constructor = SplineCurve; SplineCurve.prototype.isSplineCurve = true; SplineCurve.prototype.getPoint = function ( t ) { var points = this.points; var point = ( points.length - 1 ) * t; var intPoint = Math.floor( point ); var weight = point - intPoint; var point0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; var point1 = points[ intPoint ]; var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; var interpolate = CurveUtils.interpolate; return new Vector2( interpolate( point0.x, point1.x, point2.x, point3.x, weight ), interpolate( point0.y, point1.y, point2.y, point3.y, weight ) ); }; /************************************************************** * Cubic Bezier curve **************************************************************/ function CubicBezierCurve( v0, v1, v2, v3 ) { this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; } CubicBezierCurve.prototype = Object.create( Curve.prototype ); CubicBezierCurve.prototype.constructor = CubicBezierCurve; CubicBezierCurve.prototype.getPoint = function ( t ) { var b3 = ShapeUtils.b3; return new Vector2( b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) ); }; CubicBezierCurve.prototype.getTangent = function( t ) { var tangentCubicBezier = CurveUtils.tangentCubicBezier; return new Vector2( tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) ).normalize(); }; /************************************************************** * Quadratic Bezier curve **************************************************************/ function QuadraticBezierCurve( v0, v1, v2 ) { this.v0 = v0; this.v1 = v1; this.v2 = v2; } QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; QuadraticBezierCurve.prototype.getPoint = function ( t ) { var b2 = ShapeUtils.b2; return new Vector2( b2( t, this.v0.x, this.v1.x, this.v2.x ), b2( t, this.v0.y, this.v1.y, this.v2.y ) ); }; QuadraticBezierCurve.prototype.getTangent = function( t ) { var tangentQuadraticBezier = CurveUtils.tangentQuadraticBezier; return new Vector2( tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ), tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ) ).normalize(); }; var PathPrototype = Object.assign( Object.create( CurvePath.prototype ), { fromPoints: function ( vectors ) { this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); for ( var i = 1, l = vectors.length; i < l; i ++ ) { this.lineTo( vectors[ i ].x, vectors[ i ].y ); } }, moveTo: function ( x, y ) { this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? }, lineTo: function ( x, y ) { var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); this.curves.push( curve ); this.currentPoint.set( x, y ); }, quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { var curve = new QuadraticBezierCurve( this.currentPoint.clone(), new Vector2( aCPx, aCPy ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); }, bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { var curve = new CubicBezierCurve( this.currentPoint.clone(), new Vector2( aCP1x, aCP1y ), new Vector2( aCP2x, aCP2y ), new Vector2( aX, aY ) ); this.curves.push( curve ); this.currentPoint.set( aX, aY ); }, splineThru: function ( pts /*Array of Vector*/ ) { var npts = [ this.currentPoint.clone() ].concat( pts ); var curve = new SplineCurve( npts ); this.curves.push( curve ); this.currentPoint.copy( pts[ pts.length - 1 ] ); }, arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { var x0 = this.currentPoint.x; var y0 = this.currentPoint.y; this.absarc( aX + x0, aY + y0, aRadius, aStartAngle, aEndAngle, aClockwise ); }, absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); }, ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { var x0 = this.currentPoint.x; var y0 = this.currentPoint.y; this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); }, absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); if ( this.curves.length > 0 ) { // if a previous curve is present, attempt to join var firstPoint = curve.getPoint( 0 ); if ( ! firstPoint.equals( this.currentPoint ) ) { this.lineTo( firstPoint.x, firstPoint.y ); } } this.curves.push( curve ); var lastPoint = curve.getPoint( 1 ); this.currentPoint.copy( lastPoint ); } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Defines a 2d shape plane using paths. **/ // STEP 1 Create a path. // STEP 2 Turn path into shape. // STEP 3 ExtrudeGeometry takes in Shape/Shapes // STEP 3a - Extract points from each shape, turn to vertices // STEP 3b - Triangulate each shape, add faces. function Shape() { Path.apply( this, arguments ); this.holes = []; } Shape.prototype = Object.assign( Object.create( PathPrototype ), { constructor: Shape, getPointsHoles: function ( divisions ) { var holesPts = []; for ( var i = 0, l = this.holes.length; i < l; i ++ ) { holesPts[ i ] = this.holes[ i ].getPoints( divisions ); } return holesPts; }, // Get points of shape and holes (keypoints based on segments parameter) extractAllPoints: function ( divisions ) { return { shape: this.getPoints( divisions ), holes: this.getPointsHoles( divisions ) }; }, extractPoints: function ( divisions ) { return this.extractAllPoints( divisions ); } } ); /** * @author zz85 / http://www.lab4games.net/zz85/blog * Creates free form 2d path using series of points, lines or curves. * **/ function Path( points ) { CurvePath.call( this ); this.currentPoint = new Vector2(); if ( points ) { this.fromPoints( points ); } } Path.prototype = PathPrototype; PathPrototype.constructor = Path; // minimal class for proxing functions to Path. Replaces old "extractSubpaths()" function ShapePath() { this.subPaths = []; this.currentPath = null; } ShapePath.prototype = { moveTo: function ( x, y ) { this.currentPath = new Path(); this.subPaths.push(this.currentPath); this.currentPath.moveTo( x, y ); }, lineTo: function ( x, y ) { this.currentPath.lineTo( x, y ); }, quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); }, bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); }, splineThru: function ( pts ) { this.currentPath.splineThru( pts ); }, toShapes: function ( isCCW, noHoles ) { function toShapesNoHoles( inSubpaths ) { var shapes = []; for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { var tmpPath = inSubpaths[ i ]; var tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); } return shapes; } function isPointInsidePolygon( inPt, inPolygon ) { var polyLen = inPolygon.length; // inPt on polygon contour => immediate success or // toggling of inside/outside at every single! intersection point of an edge // with the horizontal line through inPt, left of inPt // not counting lowerY endpoints of edges and whole edges on that line var inside = false; for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { var edgeLowPt = inPolygon[ p ]; var edgeHighPt = inPolygon[ q ]; var edgeDx = edgeHighPt.x - edgeLowPt.x; var edgeDy = edgeHighPt.y - edgeLowPt.y; if ( Math.abs( edgeDy ) > Number.EPSILON ) { // not parallel if ( edgeDy < 0 ) { edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; } if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; if ( inPt.y === edgeLowPt.y ) { if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? // continue; // no intersection or edgeLowPt => doesn't count !!! } else { var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); if ( perpEdge === 0 ) return true; // inPt is on contour ? if ( perpEdge < 0 ) continue; inside = ! inside; // true intersection left of inPt } } else { // parallel or collinear if ( inPt.y !== edgeLowPt.y ) continue; // parallel // edge lies on the same horizontal line as inPt if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! // continue; } } return inside; } var isClockWise = ShapeUtils.isClockWise; var subPaths = this.subPaths; if ( subPaths.length === 0 ) return []; if ( noHoles === true ) return toShapesNoHoles( subPaths ); var solid, tmpPath, tmpShape, shapes = []; if ( subPaths.length === 1 ) { tmpPath = subPaths[ 0 ]; tmpShape = new Shape(); tmpShape.curves = tmpPath.curves; shapes.push( tmpShape ); return shapes; } var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); holesFirst = isCCW ? ! holesFirst : holesFirst; // console.log("Holes first", holesFirst); var betterShapeHoles = []; var newShapes = []; var newShapeHoles = []; var mainIdx = 0; var tmpPoints; newShapes[ mainIdx ] = undefined; newShapeHoles[ mainIdx ] = []; for ( var i = 0, l = subPaths.length; i < l; i ++ ) { tmpPath = subPaths[ i ]; tmpPoints = tmpPath.getPoints(); solid = isClockWise( tmpPoints ); solid = isCCW ? ! solid : solid; if ( solid ) { if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; newShapes[ mainIdx ].s.curves = tmpPath.curves; if ( holesFirst ) mainIdx ++; newShapeHoles[ mainIdx ] = []; //console.log('cw', i); } else { newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); //console.log('ccw', i); } } // only Holes? -> probably all Shapes with wrong orientation if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); if ( newShapes.length > 1 ) { var ambiguous = false; var toChange = []; for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { betterShapeHoles[ sIdx ] = []; } for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { var sho = newShapeHoles[ sIdx ]; for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { var ho = sho[ hIdx ]; var hole_unassigned = true; for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); if ( hole_unassigned ) { hole_unassigned = false; betterShapeHoles[ s2Idx ].push( ho ); } else { ambiguous = true; } } } if ( hole_unassigned ) { betterShapeHoles[ sIdx ].push( ho ); } } } // console.log("ambiguous: ", ambiguous); if ( toChange.length > 0 ) { // console.log("to change: ", toChange); if ( ! ambiguous ) newShapeHoles = betterShapeHoles; } } var tmpHoles; for ( var i = 0, il = newShapes.length; i < il; i ++ ) { tmpShape = newShapes[ i ].s; shapes.push( tmpShape ); tmpHoles = newShapeHoles[ i ]; for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { tmpShape.holes.push( tmpHoles[ j ].h ); } } //console.log("shape", shapes); return shapes; } }; /** * @author zz85 / http://www.lab4games.net/zz85/blog * @author mrdoob / http://mrdoob.com/ */ function Font( data ) { this.data = data; } Object.assign( Font.prototype, { isFont: true, generateShapes: function ( text, size, divisions ) { function createPaths( text ) { var chars = String( text ).split( '' ); var scale = size / data.resolution; var offset = 0; var paths = []; for ( var i = 0; i < chars.length; i ++ ) { var ret = createPath( chars[ i ], scale, offset ); offset += ret.offset; paths.push( ret.path ); } return paths; } function createPath( c, scale, offset ) { var glyph = data.glyphs[ c ] || data.glyphs[ '?' ]; if ( ! glyph ) return; var path = new ShapePath(); var pts = [], b2 = ShapeUtils.b2, b3 = ShapeUtils.b3; var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste; if ( glyph.o ) { var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); for ( var i = 0, l = outline.length; i < l; ) { var action = outline[ i ++ ]; switch ( action ) { case 'm': // moveTo x = outline[ i ++ ] * scale + offset; y = outline[ i ++ ] * scale; path.moveTo( x, y ); break; case 'l': // lineTo x = outline[ i ++ ] * scale + offset; y = outline[ i ++ ] * scale; path.lineTo( x, y ); break; case 'q': // quadraticCurveTo cpx = outline[ i ++ ] * scale + offset; cpy = outline[ i ++ ] * scale; cpx1 = outline[ i ++ ] * scale + offset; cpy1 = outline[ i ++ ] * scale; path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); laste = pts[ pts.length - 1 ]; if ( laste ) { cpx0 = laste.x; cpy0 = laste.y; for ( var i2 = 1; i2 <= divisions; i2 ++ ) { var t = i2 / divisions; b2( t, cpx0, cpx1, cpx ); b2( t, cpy0, cpy1, cpy ); } } break; case 'b': // bezierCurveTo cpx = outline[ i ++ ] * scale + offset; cpy = outline[ i ++ ] * scale; cpx1 = outline[ i ++ ] * scale + offset; cpy1 = outline[ i ++ ] * scale; cpx2 = outline[ i ++ ] * scale + offset; cpy2 = outline[ i ++ ] * scale; path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); laste = pts[ pts.length - 1 ]; if ( laste ) { cpx0 = laste.x; cpy0 = laste.y; for ( var i2 = 1; i2 <= divisions; i2 ++ ) { var t = i2 / divisions; b3( t, cpx0, cpx1, cpx2, cpx ); b3( t, cpy0, cpy1, cpy2, cpy ); } } break; } } } return { offset: glyph.ha * scale, path: path }; } // if ( size === undefined ) size = 100; if ( divisions === undefined ) divisions = 4; var data = this.data; var paths = createPaths( text ); var shapes = []; for ( var p = 0, pl = paths.length; p < pl; p ++ ) { Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); } return shapes; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function FontLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( FontLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var scope = this; var loader = new FileLoader( this.manager ); loader.load( url, function ( text ) { var json; try { json = JSON.parse( text ); } catch ( e ) { console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); json = JSON.parse( text.substring( 65, text.length - 2 ) ); } var font = scope.parse( json ); if ( onLoad ) onLoad( font ); }, onProgress, onError ); }, parse: function ( json ) { return new Font( json ); } } ); var context; var AudioContext = { getContext: function () { if ( context === undefined ) { context = new ( window.AudioContext || window.webkitAudioContext )(); } return context; }, setContext: function ( value ) { context = value; } }; /** * @author Reece Aaron Lecrivain / http://reecenotes.com/ */ function AudioLoader( manager ) { this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; } Object.assign( AudioLoader.prototype, { load: function ( url, onLoad, onProgress, onError ) { var loader = new FileLoader( this.manager ); loader.setResponseType( 'arraybuffer' ); loader.load( url, function ( buffer ) { var context = AudioContext.getContext(); context.decodeAudioData( buffer, function ( audioBuffer ) { onLoad( audioBuffer ); } ); }, onProgress, onError ); } } ); /** * @author abelnation / http://github.com/abelnation */ function RectAreaLight ( color, intensity, width, height ) { Light.call( this, color, intensity ); this.type = 'RectAreaLight'; this.position.set( 0, 1, 0 ); this.updateMatrix(); this.width = ( width !== undefined ) ? width : 10; this.height = ( height !== undefined ) ? height : 10; // TODO (abelnation): distance/decay // TODO (abelnation): update method for RectAreaLight to update transform to lookat target // TODO (abelnation): shadows // this.shadow = new THREE.RectAreaLightShadow( new THREE.PerspectiveCamera( 90, 1, 0.5, 500 ) ); } // TODO (abelnation): RectAreaLight update when light shape is changed RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { constructor: RectAreaLight, isRectAreaLight: true, copy: function ( source ) { Light.prototype.copy.call( this, source ); this.width = source.width; this.height = source.height; // this.shadow = source.shadow.clone(); return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function StereoCamera() { this.type = 'StereoCamera'; this.aspect = 1; this.eyeSep = 0.064; this.cameraL = new PerspectiveCamera(); this.cameraL.layers.enable( 1 ); this.cameraL.matrixAutoUpdate = false; this.cameraR = new PerspectiveCamera(); this.cameraR.layers.enable( 2 ); this.cameraR.matrixAutoUpdate = false; } Object.assign( StereoCamera.prototype, { update: ( function () { var instance, focus, fov, aspect, near, far, zoom; var eyeRight = new Matrix4(); var eyeLeft = new Matrix4(); return function update( camera ) { var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov || aspect !== camera.aspect * this.aspect || near !== camera.near || far !== camera.far || zoom !== camera.zoom; if ( needsUpdate ) { instance = this; focus = camera.focus; fov = camera.fov; aspect = camera.aspect * this.aspect; near = camera.near; far = camera.far; zoom = camera.zoom; // Off-axis stereoscopic effect based on // http://paulbourke.net/stereographics/stereorender/ var projectionMatrix = camera.projectionMatrix.clone(); var eyeSep = this.eyeSep / 2; var eyeSepOnProjection = eyeSep * near / focus; var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom; var xmin, xmax; // translate xOffset eyeLeft.elements[ 12 ] = - eyeSep; eyeRight.elements[ 12 ] = eyeSep; // for left eye xmin = - ymax * aspect + eyeSepOnProjection; xmax = ymax * aspect + eyeSepOnProjection; projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraL.projectionMatrix.copy( projectionMatrix ); // for right eye xmin = - ymax * aspect - eyeSepOnProjection; xmax = ymax * aspect - eyeSepOnProjection; projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); this.cameraR.projectionMatrix.copy( projectionMatrix ); } this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); }; } )() } ); /** * Camera for rendering cube maps * - renders scene into axis-aligned cube * * @author alteredq / http://alteredqualia.com/ */ function CubeCamera( near, far, cubeResolution ) { Object3D.call( this ); this.type = 'CubeCamera'; var fov = 90, aspect = 1; var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); cameraPX.up.set( 0, - 1, 0 ); cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); this.add( cameraPX ); var cameraNX = new PerspectiveCamera( fov, aspect, near, far ); cameraNX.up.set( 0, - 1, 0 ); cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); this.add( cameraNX ); var cameraPY = new PerspectiveCamera( fov, aspect, near, far ); cameraPY.up.set( 0, 0, 1 ); cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); this.add( cameraPY ); var cameraNY = new PerspectiveCamera( fov, aspect, near, far ); cameraNY.up.set( 0, 0, - 1 ); cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); this.add( cameraNY ); var cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); cameraPZ.up.set( 0, - 1, 0 ); cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); this.add( cameraPZ ); var cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); cameraNZ.up.set( 0, - 1, 0 ); cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); this.add( cameraNZ ); var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); this.updateCubeMap = function ( renderer, scene ) { if ( this.parent === null ) this.updateMatrixWorld(); var renderTarget = this.renderTarget; var generateMipmaps = renderTarget.texture.generateMipmaps; renderTarget.texture.generateMipmaps = false; renderTarget.activeCubeFace = 0; renderer.render( scene, cameraPX, renderTarget ); renderTarget.activeCubeFace = 1; renderer.render( scene, cameraNX, renderTarget ); renderTarget.activeCubeFace = 2; renderer.render( scene, cameraPY, renderTarget ); renderTarget.activeCubeFace = 3; renderer.render( scene, cameraNY, renderTarget ); renderTarget.activeCubeFace = 4; renderer.render( scene, cameraPZ, renderTarget ); renderTarget.texture.generateMipmaps = generateMipmaps; renderTarget.activeCubeFace = 5; renderer.render( scene, cameraNZ, renderTarget ); renderer.setRenderTarget( null ); }; } CubeCamera.prototype = Object.create( Object3D.prototype ); CubeCamera.prototype.constructor = CubeCamera; /** * @author mrdoob / http://mrdoob.com/ */ function AudioListener() { Object3D.call( this ); this.type = 'AudioListener'; this.context = AudioContext.getContext(); this.gain = this.context.createGain(); this.gain.connect( this.context.destination ); this.filter = null; } AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: AudioListener, getInput: function () { return this.gain; }, removeFilter: function ( ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); this.gain.connect( this.context.destination ); this.filter = null; } }, getFilter: function () { return this.filter; }, setFilter: function ( value ) { if ( this.filter !== null ) { this.gain.disconnect( this.filter ); this.filter.disconnect( this.context.destination ); } else { this.gain.disconnect( this.context.destination ); } this.filter = value; this.gain.connect( this.filter ); this.filter.connect( this.context.destination ); }, getMasterVolume: function () { return this.gain.gain.value; }, setMasterVolume: function ( value ) { this.gain.gain.value = value; }, updateMatrixWorld: ( function () { var position = new Vector3(); var quaternion = new Quaternion(); var scale = new Vector3(); var orientation = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); var listener = this.context.listener; var up = this.up; this.matrixWorld.decompose( position, quaternion, scale ); orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); if ( listener.positionX ) { listener.positionX.setValueAtTime( position.x, this.context.currentTime ); listener.positionY.setValueAtTime( position.y, this.context.currentTime ); listener.positionZ.setValueAtTime( position.z, this.context.currentTime ); listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime ); listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime ); listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime ); listener.upX.setValueAtTime( up.x, this.context.currentTime ); listener.upY.setValueAtTime( up.y, this.context.currentTime ); listener.upZ.setValueAtTime( up.z, this.context.currentTime ); } else { listener.setPosition( position.x, position.y, position.z ); listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); } }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ * @author Reece Aaron Lecrivain / http://reecenotes.com/ */ function Audio( listener ) { Object3D.call( this ); this.type = 'Audio'; this.context = listener.context; this.gain = this.context.createGain(); this.gain.connect( listener.getInput() ); this.autoplay = false; this.buffer = null; this.loop = false; this.startTime = 0; this.playbackRate = 1; this.isPlaying = false; this.hasPlaybackControl = true; this.sourceType = 'empty'; this.filters = []; } Audio.prototype = Object.assign( Object.create( Object3D.prototype ), { constructor: Audio, getOutput: function () { return this.gain; }, setNodeSource: function ( audioNode ) { this.hasPlaybackControl = false; this.sourceType = 'audioNode'; this.source = audioNode; this.connect(); return this; }, setBuffer: function ( audioBuffer ) { this.buffer = audioBuffer; this.sourceType = 'buffer'; if ( this.autoplay ) this.play(); return this; }, play: function () { if ( this.isPlaying === true ) { console.warn( 'THREE.Audio: Audio is already playing.' ); return; } if ( this.hasPlaybackControl === false ) { console.warn( 'THREE.Audio: this Audio has no playback control.' ); return; } var source = this.context.createBufferSource(); source.buffer = this.buffer; source.loop = this.loop; source.onended = this.onEnded.bind( this ); source.playbackRate.setValueAtTime( this.playbackRate, this.startTime ); source.start( 0, this.startTime ); this.isPlaying = true; this.source = source; return this.connect(); }, pause: function () { if ( this.hasPlaybackControl === false ) { console.warn( 'THREE.Audio: this Audio has no playback control.' ); return; } this.source.stop(); this.startTime = this.context.currentTime; this.isPlaying = false; return this; }, stop: function () { if ( this.hasPlaybackControl === false ) { console.warn( 'THREE.Audio: this Audio has no playback control.' ); return; } this.source.stop(); this.startTime = 0; this.isPlaying = false; return this; }, connect: function () { if ( this.filters.length > 0 ) { this.source.connect( this.filters[ 0 ] ); for ( var i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].connect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); } else { this.source.connect( this.getOutput() ); } return this; }, disconnect: function () { if ( this.filters.length > 0 ) { this.source.disconnect( this.filters[ 0 ] ); for ( var i = 1, l = this.filters.length; i < l; i ++ ) { this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); } else { this.source.disconnect( this.getOutput() ); } return this; }, getFilters: function () { return this.filters; }, setFilters: function ( value ) { if ( ! value ) value = []; if ( this.isPlaying === true ) { this.disconnect(); this.filters = value; this.connect(); } else { this.filters = value; } return this; }, getFilter: function () { return this.getFilters()[ 0 ]; }, setFilter: function ( filter ) { return this.setFilters( filter ? [ filter ] : [] ); }, setPlaybackRate: function ( value ) { if ( this.hasPlaybackControl === false ) { console.warn( 'THREE.Audio: this Audio has no playback control.' ); return; } this.playbackRate = value; if ( this.isPlaying === true ) { this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime ); } return this; }, getPlaybackRate: function () { return this.playbackRate; }, onEnded: function () { this.isPlaying = false; }, getLoop: function () { if ( this.hasPlaybackControl === false ) { console.warn( 'THREE.Audio: this Audio has no playback control.' ); return false; } return this.loop; }, setLoop: function ( value ) { if ( this.hasPlaybackControl === false ) { console.warn( 'THREE.Audio: this Audio has no playback control.' ); return; } this.loop = value; if ( this.isPlaying === true ) { this.source.loop = this.loop; } return this; }, getVolume: function () { return this.gain.gain.value; }, setVolume: function ( value ) { this.gain.gain.value = value; return this; } } ); /** * @author mrdoob / http://mrdoob.com/ */ function PositionalAudio( listener ) { Audio.call( this, listener ); this.panner = this.context.createPanner(); this.panner.connect( this.gain ); } PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), { constructor: PositionalAudio, getOutput: function () { return this.panner; }, getRefDistance: function () { return this.panner.refDistance; }, setRefDistance: function ( value ) { this.panner.refDistance = value; }, getRolloffFactor: function () { return this.panner.rolloffFactor; }, setRolloffFactor: function ( value ) { this.panner.rolloffFactor = value; }, getDistanceModel: function () { return this.panner.distanceModel; }, setDistanceModel: function ( value ) { this.panner.distanceModel = value; }, getMaxDistance: function () { return this.panner.maxDistance; }, setMaxDistance: function ( value ) { this.panner.maxDistance = value; }, updateMatrixWorld: ( function () { var position = new Vector3(); return function updateMatrixWorld( force ) { Object3D.prototype.updateMatrixWorld.call( this, force ); position.setFromMatrixPosition( this.matrixWorld ); this.panner.setPosition( position.x, position.y, position.z ); }; } )() } ); /** * @author mrdoob / http://mrdoob.com/ */ function AudioAnalyser( audio, fftSize ) { this.analyser = audio.context.createAnalyser(); this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; this.data = new Uint8Array( this.analyser.frequencyBinCount ); audio.getOutput().connect( this.analyser ); } Object.assign( AudioAnalyser.prototype, { getFrequencyData: function () { this.analyser.getByteFrequencyData( this.data ); return this.data; }, getAverageFrequency: function () { var value = 0, data = this.getFrequencyData(); for ( var i = 0; i < data.length; i ++ ) { value += data[ i ]; } return value / data.length; } } ); /** * * Buffered scene graph property that allows weighted accumulation. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function PropertyMixer( binding, typeName, valueSize ) { this.binding = binding; this.valueSize = valueSize; var bufferType = Float64Array, mixFunction; switch ( typeName ) { case 'quaternion': mixFunction = this._slerp; break; case 'string': case 'bool': bufferType = Array; mixFunction = this._select; break; default: mixFunction = this._lerp; } this.buffer = new bufferType( valueSize * 4 ); // layout: [ incoming | accu0 | accu1 | orig ] // // interpolators can use .buffer as their .result // the data then goes to 'incoming' // // 'accu0' and 'accu1' are used frame-interleaved for // the cumulative result and are compared to detect // changes // // 'orig' stores the original state of the property this._mixBufferRegion = mixFunction; this.cumulativeWeight = 0; this.useCount = 0; this.referenceCount = 0; } PropertyMixer.prototype = { constructor: PropertyMixer, // accumulate data in the 'incoming' region into 'accu' accumulate: function( accuIndex, weight ) { // note: happily accumulating nothing when weight = 0, the caller knows // the weight and shouldn't have made the call in the first place var buffer = this.buffer, stride = this.valueSize, offset = accuIndex * stride + stride, currentWeight = this.cumulativeWeight; if ( currentWeight === 0 ) { // accuN := incoming * weight for ( var i = 0; i !== stride; ++ i ) { buffer[ offset + i ] = buffer[ i ]; } currentWeight = weight; } else { // accuN := accuN + incoming * weight currentWeight += weight; var mix = weight / currentWeight; this._mixBufferRegion( buffer, offset, 0, mix, stride ); } this.cumulativeWeight = currentWeight; }, // apply the state of 'accu' to the binding when accus differ apply: function( accuIndex ) { var stride = this.valueSize, buffer = this.buffer, offset = accuIndex * stride + stride, weight = this.cumulativeWeight, binding = this.binding; this.cumulativeWeight = 0; if ( weight < 1 ) { // accuN := accuN + original * ( 1 - cumulativeWeight ) var originalValueOffset = stride * 3; this._mixBufferRegion( buffer, offset, originalValueOffset, 1 - weight, stride ); } for ( var i = stride, e = stride + stride; i !== e; ++ i ) { if ( buffer[ i ] !== buffer[ i + stride ] ) { // value has changed -> update scene graph binding.setValue( buffer, offset ); break; } } }, // remember the state of the bound property and copy it to both accus saveOriginalState: function() { var binding = this.binding; var buffer = this.buffer, stride = this.valueSize, originalValueOffset = stride * 3; binding.getValue( buffer, originalValueOffset ); // accu[0..1] := orig -- initially detect changes against the original for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; } this.cumulativeWeight = 0; }, // apply the state previously taken via 'saveOriginalState' to the binding restoreOriginalState: function() { var originalValueOffset = this.valueSize * 3; this.binding.setValue( this.buffer, originalValueOffset ); }, // mix functions _select: function( buffer, dstOffset, srcOffset, t, stride ) { if ( t >= 0.5 ) { for ( var i = 0; i !== stride; ++ i ) { buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; } } }, _slerp: function( buffer, dstOffset, srcOffset, t, stride ) { Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); }, _lerp: function( buffer, dstOffset, srcOffset, t, stride ) { var s = 1 - t; for ( var i = 0; i !== stride; ++ i ) { var j = dstOffset + i; buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; } } }; /** * * A reference to a real property in the scene graph. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function PropertyBinding( rootNode, path, parsedPath ) { this.path = path; this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; this.rootNode = rootNode; } PropertyBinding.prototype = { constructor: PropertyBinding, getValue: function getValue_unbound( targetArray, offset ) { this.bind(); this.getValue( targetArray, offset ); // Note: This class uses a State pattern on a per-method basis: // 'bind' sets 'this.getValue' / 'setValue' and shadows the // prototype version of these methods with one that represents // the bound state. When the property is not found, the methods // become no-ops. }, setValue: function getValue_unbound( sourceArray, offset ) { this.bind(); this.setValue( sourceArray, offset ); }, // create getter / setter pair for a property in the scene graph bind: function() { var targetObject = this.node, parsedPath = this.parsedPath, objectName = parsedPath.objectName, propertyName = parsedPath.propertyName, propertyIndex = parsedPath.propertyIndex; if ( ! targetObject ) { targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; this.node = targetObject; } // set fail state so we can just 'return' on error this.getValue = this._getValue_unavailable; this.setValue = this._setValue_unavailable; // ensure there is a value node if ( ! targetObject ) { console.error( " trying to update node for track: " + this.path + " but it wasn't found." ); return; } if ( objectName ) { var objectIndex = parsedPath.objectIndex; // special cases were we need to reach deeper into the hierarchy to get the face materials.... switch ( objectName ) { case 'materials': if ( ! targetObject.material ) { console.error( ' can not bind to material as node does not have a material', this ); return; } if ( ! targetObject.material.materials ) { console.error( ' can not bind to material.materials as node.material does not have a materials array', this ); return; } targetObject = targetObject.material.materials; break; case 'bones': if ( ! targetObject.skeleton ) { console.error( ' can not bind to bones as node does not have a skeleton', this ); return; } // potential future optimization: skip this if propertyIndex is already an integer // and convert the integer string to a true integer. targetObject = targetObject.skeleton.bones; // support resolving morphTarget names into indices. for ( var i = 0; i < targetObject.length; i ++ ) { if ( targetObject[ i ].name === objectIndex ) { objectIndex = i; break; } } break; default: if ( targetObject[ objectName ] === undefined ) { console.error( ' can not bind to objectName of node, undefined', this ); return; } targetObject = targetObject[ objectName ]; } if ( objectIndex !== undefined ) { if ( targetObject[ objectIndex ] === undefined ) { console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject ); return; } targetObject = targetObject[ objectIndex ]; } } // resolve property var nodeProperty = targetObject[ propertyName ]; if ( nodeProperty === undefined ) { var nodeName = parsedPath.nodeName; console.error( " trying to update property for track: " + nodeName + '.' + propertyName + " but it wasn't found.", targetObject ); return; } // determine versioning scheme var versioning = this.Versioning.None; if ( targetObject.needsUpdate !== undefined ) { // material versioning = this.Versioning.NeedsUpdate; this.targetObject = targetObject; } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform versioning = this.Versioning.MatrixWorldNeedsUpdate; this.targetObject = targetObject; } // determine how the property gets bound var bindingType = this.BindingType.Direct; if ( propertyIndex !== undefined ) { // access a sub element of the property array (only primitives are supported right now) if ( propertyName === "morphTargetInfluences" ) { // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. // support resolving morphTarget names into indices. if ( ! targetObject.geometry ) { console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this ); return; } if ( ! targetObject.geometry.morphTargets ) { console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this ); return; } for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) { propertyIndex = i; break; } } } bindingType = this.BindingType.ArrayElement; this.resolvedProperty = nodeProperty; this.propertyIndex = propertyIndex; } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { // must use copy for Object3D.Euler/Quaternion bindingType = this.BindingType.HasFromToArray; this.resolvedProperty = nodeProperty; } else if ( nodeProperty.length !== undefined ) { bindingType = this.BindingType.EntireArray; this.resolvedProperty = nodeProperty; } else { this.propertyName = propertyName; } // select getter / setter this.getValue = this.GetterByBindingType[ bindingType ]; this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; }, unbind: function() { this.node = null; // back to the prototype version of getValue / setValue // note: avoiding to mutate the shape of 'this' via 'delete' this.getValue = this._getValue_unbound; this.setValue = this._setValue_unbound; } }; Object.assign( PropertyBinding.prototype, { // prototype, continued // these are used to "bind" a nonexistent property _getValue_unavailable: function() {}, _setValue_unavailable: function() {}, // initial state of these methods that calls 'bind' _getValue_unbound: PropertyBinding.prototype.getValue, _setValue_unbound: PropertyBinding.prototype.setValue, BindingType: { Direct: 0, EntireArray: 1, ArrayElement: 2, HasFromToArray: 3 }, Versioning: { None: 0, NeedsUpdate: 1, MatrixWorldNeedsUpdate: 2 }, GetterByBindingType: [ function getValue_direct( buffer, offset ) { buffer[ offset ] = this.node[ this.propertyName ]; }, function getValue_array( buffer, offset ) { var source = this.resolvedProperty; for ( var i = 0, n = source.length; i !== n; ++ i ) { buffer[ offset ++ ] = source[ i ]; } }, function getValue_arrayElement( buffer, offset ) { buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; }, function getValue_toArray( buffer, offset ) { this.resolvedProperty.toArray( buffer, offset ); } ], SetterByBindingTypeAndVersioning: [ [ // Direct function setValue_direct( buffer, offset ) { this.node[ this.propertyName ] = buffer[ offset ]; }, function setValue_direct_setNeedsUpdate( buffer, offset ) { this.node[ this.propertyName ] = buffer[ offset ]; this.targetObject.needsUpdate = true; }, function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { this.node[ this.propertyName ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // EntireArray function setValue_array( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } }, function setValue_array_setNeedsUpdate( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.needsUpdate = true; }, function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { var dest = this.resolvedProperty; for ( var i = 0, n = dest.length; i !== n; ++ i ) { dest[ i ] = buffer[ offset ++ ]; } this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // ArrayElement function setValue_arrayElement( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; }, function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.needsUpdate = true; }, function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; this.targetObject.matrixWorldNeedsUpdate = true; } ], [ // HasToFromArray function setValue_fromArray( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); }, function setValue_fromArray_setNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.needsUpdate = true; }, function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { this.resolvedProperty.fromArray( buffer, offset ); this.targetObject.matrixWorldNeedsUpdate = true; } ] ] } ); PropertyBinding.Composite = function( targetGroup, path, optionalParsedPath ) { var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); this._targetGroup = targetGroup; this._bindings = targetGroup.subscribe_( path, parsedPath ); }; PropertyBinding.Composite.prototype = { constructor: PropertyBinding.Composite, getValue: function( array, offset ) { this.bind(); // bind all binding var firstValidIndex = this._targetGroup.nCachedObjects_, binding = this._bindings[ firstValidIndex ]; // and only call .getValue on the first if ( binding !== undefined ) binding.getValue( array, offset ); }, setValue: function( array, offset ) { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].setValue( array, offset ); } }, bind: function() { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].bind(); } }, unbind: function() { var bindings = this._bindings; for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { bindings[ i ].unbind(); } } }; PropertyBinding.create = function( root, path, parsedPath ) { if ( ! ( root && root.isAnimationObjectGroup ) ) { return new PropertyBinding( root, path, parsedPath ); } else { return new PropertyBinding.Composite( root, path, parsedPath ); } }; PropertyBinding.parseTrackName = function( trackName ) { // matches strings in the form of: // nodeName.property // nodeName.property[accessor] // nodeName.material.property[accessor] // uuid.property[accessor] // uuid.objectName[objectIndex].propertyName[propertyIndex] // parentName/nodeName.property // parentName/parentName/nodeName.property[index] // .bone[Armature.DEF_cog].position // scene:helium_balloon_model:helium_balloon_model.position // created and tested via https://regex101.com/#javascript var re = /^((?:[\w-]+[\/:])*)([\w-]+)?(?:\.([\w-]+)(?:\[(.+)\])?)?\.([\w-]+)(?:\[(.+)\])?$/; var matches = re.exec( trackName ); if ( ! matches ) { throw new Error( "cannot parse trackName at all: " + trackName ); } var results = { // directoryName: matches[ 1 ], // (tschw) currently unused nodeName: matches[ 2 ], // allowed to be null, specified root node. objectName: matches[ 3 ], objectIndex: matches[ 4 ], propertyName: matches[ 5 ], propertyIndex: matches[ 6 ] // allowed to be null, specifies that the whole property is set. }; if ( results.propertyName === null || results.propertyName.length === 0 ) { throw new Error( "can not parse propertyName from trackName: " + trackName ); } return results; }; PropertyBinding.findNode = function( root, nodeName ) { if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { return root; } // search into skeleton bones. if ( root.skeleton ) { var searchSkeleton = function( skeleton ) { for( var i = 0; i < skeleton.bones.length; i ++ ) { var bone = skeleton.bones[ i ]; if ( bone.name === nodeName ) { return bone; } } return null; }; var bone = searchSkeleton( root.skeleton ); if ( bone ) { return bone; } } // search into node subtree. if ( root.children ) { var searchNodeSubtree = function( children ) { for( var i = 0; i < children.length; i ++ ) { var childNode = children[ i ]; if ( childNode.name === nodeName || childNode.uuid === nodeName ) { return childNode; } var result = searchNodeSubtree( childNode.children ); if ( result ) return result; } return null; }; var subTreeNode = searchNodeSubtree( root.children ); if ( subTreeNode ) { return subTreeNode; } } return null; }; /** * * A group of objects that receives a shared animation state. * * Usage: * * - Add objects you would otherwise pass as 'root' to the * constructor or the .clipAction method of AnimationMixer. * * - Instead pass this object as 'root'. * * - You can also add and remove objects later when the mixer * is running. * * Note: * * Objects of this class appear as one object to the mixer, * so cache control of the individual objects must be done * on the group. * * Limitation: * * - The animated properties must be compatible among the * all objects in the group. * * - A single property can either be controlled through a * target group or directly, but not both. * * @author tschw */ function AnimationObjectGroup( var_args ) { this.uuid = _Math.generateUUID(); // cached objects followed by the active ones this._objects = Array.prototype.slice.call( arguments ); this.nCachedObjects_ = 0; // threshold // note: read by PropertyBinding.Composite var indices = {}; this._indicesByUUID = indices; // for bookkeeping for ( var i = 0, n = arguments.length; i !== n; ++ i ) { indices[ arguments[ i ].uuid ] = i; } this._paths = []; // inside: string this._parsedPaths = []; // inside: { we don't care, here } this._bindings = []; // inside: Array< PropertyBinding > this._bindingsIndicesByPath = {}; // inside: indices in these arrays var scope = this; this.stats = { objects: { get total() { return scope._objects.length; }, get inUse() { return this.total - scope.nCachedObjects_; } }, get bindingsPerObject() { return scope._bindings.length; } }; } AnimationObjectGroup.prototype = { constructor: AnimationObjectGroup, isAnimationObjectGroup: true, add: function( var_args ) { var objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ], knownObject = undefined; if ( index === undefined ) { // unknown object -> add it to the ACTIVE region index = nObjects ++; indicesByUUID[ uuid ] = index; objects.push( object ); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); } } else if ( index < nCachedObjects ) { knownObject = objects[ index ]; // move existing object to the ACTIVE region var firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ]; indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; indicesByUUID[ uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = object; // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = lastCached; if ( binding === undefined ) { // since we do not bother to create new bindings // for objects that are cached, the binding may // or may not exist binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); } bindingsForPath[ firstActiveIndex ] = binding; } } else if ( objects[ index ] !== knownObject) { console.error( "Different objects with the same UUID " + "detected. Clean the caches or recreate your " + "infrastructure when reloading scenes..." ); } // else the object is already where we want it to be } // for arguments this.nCachedObjects_ = nCachedObjects; }, remove: function( var_args ) { var objects = this._objects, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined && index >= nCachedObjects ) { // move existing object into the CACHED region var lastCachedIndex = nCachedObjects ++, firstActiveObject = objects[ lastCachedIndex ]; indicesByUUID[ firstActiveObject.uuid ] = index; objects[ index ] = firstActiveObject; indicesByUUID[ uuid ] = lastCachedIndex; objects[ lastCachedIndex ] = object; // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], firstActive = bindingsForPath[ lastCachedIndex ], binding = bindingsForPath[ index ]; bindingsForPath[ index ] = firstActive; bindingsForPath[ lastCachedIndex ] = binding; } } } // for arguments this.nCachedObjects_ = nCachedObjects; }, // remove & forget uncache: function( var_args ) { var objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, indicesByUUID = this._indicesByUUID, bindings = this._bindings, nBindings = bindings.length; for ( var i = 0, n = arguments.length; i !== n; ++ i ) { var object = arguments[ i ], uuid = object.uuid, index = indicesByUUID[ uuid ]; if ( index !== undefined ) { delete indicesByUUID[ uuid ]; if ( index < nCachedObjects ) { // object is cached, shrink the CACHED region var firstActiveIndex = -- nCachedObjects, lastCachedObject = objects[ firstActiveIndex ], lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; // last cached object takes this object's place indicesByUUID[ lastCachedObject.uuid ] = index; objects[ index ] = lastCachedObject; // last object goes to the activated slot and pop indicesByUUID[ lastObject.uuid ] = firstActiveIndex; objects[ firstActiveIndex ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ], lastCached = bindingsForPath[ firstActiveIndex ], last = bindingsForPath[ lastIndex ]; bindingsForPath[ index ] = lastCached; bindingsForPath[ firstActiveIndex ] = last; bindingsForPath.pop(); } } else { // object is active, just swap with the last and pop var lastIndex = -- nObjects, lastObject = objects[ lastIndex ]; indicesByUUID[ lastObject.uuid ] = index; objects[ index ] = lastObject; objects.pop(); // accounting is done, now do the same for all bindings for ( var j = 0, m = nBindings; j !== m; ++ j ) { var bindingsForPath = bindings[ j ]; bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; bindingsForPath.pop(); } } // cached or active } // if object is known } // for arguments this.nCachedObjects_ = nCachedObjects; }, // Internal interface used by befriended PropertyBinding.Composite: subscribe_: function( path, parsedPath ) { // returns an array of bindings for the given path that is changed // according to the contained objects in the group var indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ], bindings = this._bindings; if ( index !== undefined ) return bindings[ index ]; var paths = this._paths, parsedPaths = this._parsedPaths, objects = this._objects, nObjects = objects.length, nCachedObjects = this.nCachedObjects_, bindingsForPath = new Array( nObjects ); index = bindings.length; indicesByPath[ path ] = index; paths.push( path ); parsedPaths.push( parsedPath ); bindings.push( bindingsForPath ); for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) { var object = objects[ i ]; bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); } return bindingsForPath; }, unsubscribe_: function( path ) { // tells the group to forget about a property path and no longer // update the array previously obtained with 'subscribe_' var indicesByPath = this._bindingsIndicesByPath, index = indicesByPath[ path ]; if ( index !== undefined ) { var paths = this._paths, parsedPaths = this._parsedPaths, bindings = this._bindings, lastBindingsIndex = bindings.length - 1, lastBindings = bindings[ lastBindingsIndex ], lastBindingsPath = path[ lastBindingsIndex ]; indicesByPath[ lastBindingsPath ] = index; bindings[ index ] = lastBindings; bindings.pop(); parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; parsedPaths.pop(); paths[ index ] = paths[ lastBindingsIndex ]; paths.pop(); } } }; /** * * Action provided by AnimationMixer for scheduling clip playback on specific * objects. * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw * */ function AnimationAction( mixer, clip, localRoot ) { this._mixer = mixer; this._clip = clip; this._localRoot = localRoot || null; var tracks = clip.tracks, nTracks = tracks.length, interpolants = new Array( nTracks ); var interpolantSettings = { endingStart: ZeroCurvatureEnding, endingEnd: ZeroCurvatureEnding }; for ( var i = 0; i !== nTracks; ++ i ) { var interpolant = tracks[ i ].createInterpolant( null ); interpolants[ i ] = interpolant; interpolant.settings = interpolantSettings; } this._interpolantSettings = interpolantSettings; this._interpolants = interpolants; // bound by the mixer // inside: PropertyMixer (managed by the mixer) this._propertyBindings = new Array( nTracks ); this._cacheIndex = null; // for the memory manager this._byClipCacheIndex = null; // for the memory manager this._timeScaleInterpolant = null; this._weightInterpolant = null; this.loop = LoopRepeat; this._loopCount = -1; // global mixer time when the action is to be started // it's set back to 'null' upon start of the action this._startTime = null; // scaled local time of the action // gets clamped or wrapped to 0..clip.duration according to loop this.time = 0; this.timeScale = 1; this._effectiveTimeScale = 1; this.weight = 1; this._effectiveWeight = 1; this.repetitions = Infinity; // no. of repetitions when looping this.paused = false; // false -> zero effective time scale this.enabled = true; // true -> zero effective weight this.clampWhenFinished = false; // keep feeding the last frame? this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate this.zeroSlopeAtEnd = true; // clips for start, loop and end } AnimationAction.prototype = { constructor: AnimationAction, // State & Scheduling play: function() { this._mixer._activateAction( this ); return this; }, stop: function() { this._mixer._deactivateAction( this ); return this.reset(); }, reset: function() { this.paused = false; this.enabled = true; this.time = 0; // restart clip this._loopCount = -1; // forget previous loops this._startTime = null; // forget scheduling return this.stopFading().stopWarping(); }, isRunning: function() { return this.enabled && ! this.paused && this.timeScale !== 0 && this._startTime === null && this._mixer._isActiveAction( this ); }, // return true when play has been called isScheduled: function() { return this._mixer._isActiveAction( this ); }, startAt: function( time ) { this._startTime = time; return this; }, setLoop: function( mode, repetitions ) { this.loop = mode; this.repetitions = repetitions; return this; }, // Weight // set the weight stopping any scheduled fading // although .enabled = false yields an effective weight of zero, this // method does *not* change .enabled, because it would be confusing setEffectiveWeight: function( weight ) { this.weight = weight; // note: same logic as when updated at runtime this._effectiveWeight = this.enabled ? weight : 0; return this.stopFading(); }, // return the weight considering fading and .enabled getEffectiveWeight: function() { return this._effectiveWeight; }, fadeIn: function( duration ) { return this._scheduleFading( duration, 0, 1 ); }, fadeOut: function( duration ) { return this._scheduleFading( duration, 1, 0 ); }, crossFadeFrom: function( fadeOutAction, duration, warp ) { fadeOutAction.fadeOut( duration ); this.fadeIn( duration ); if( warp ) { var fadeInDuration = this._clip.duration, fadeOutDuration = fadeOutAction._clip.duration, startEndRatio = fadeOutDuration / fadeInDuration, endStartRatio = fadeInDuration / fadeOutDuration; fadeOutAction.warp( 1.0, startEndRatio, duration ); this.warp( endStartRatio, 1.0, duration ); } return this; }, crossFadeTo: function( fadeInAction, duration, warp ) { return fadeInAction.crossFadeFrom( this, duration, warp ); }, stopFading: function() { var weightInterpolant = this._weightInterpolant; if ( weightInterpolant !== null ) { this._weightInterpolant = null; this._mixer._takeBackControlInterpolant( weightInterpolant ); } return this; }, // Time Scale Control // set the weight stopping any scheduled warping // although .paused = true yields an effective time scale of zero, this // method does *not* change .paused, because it would be confusing setEffectiveTimeScale: function( timeScale ) { this.timeScale = timeScale; this._effectiveTimeScale = this.paused ? 0 :timeScale; return this.stopWarping(); }, // return the time scale considering warping and .paused getEffectiveTimeScale: function() { return this._effectiveTimeScale; }, setDuration: function( duration ) { this.timeScale = this._clip.duration / duration; return this.stopWarping(); }, syncWith: function( action ) { this.time = action.time; this.timeScale = action.timeScale; return this.stopWarping(); }, halt: function( duration ) { return this.warp( this._effectiveTimeScale, 0, duration ); }, warp: function( startTimeScale, endTimeScale, duration ) { var mixer = this._mixer, now = mixer.time, interpolant = this._timeScaleInterpolant, timeScale = this.timeScale; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._timeScaleInterpolant = interpolant; } var times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; times[ 1 ] = now + duration; values[ 0 ] = startTimeScale / timeScale; values[ 1 ] = endTimeScale / timeScale; return this; }, stopWarping: function() { var timeScaleInterpolant = this._timeScaleInterpolant; if ( timeScaleInterpolant !== null ) { this._timeScaleInterpolant = null; this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); } return this; }, // Object Accessors getMixer: function() { return this._mixer; }, getClip: function() { return this._clip; }, getRoot: function() { return this._localRoot || this._mixer._root; }, // Interna _update: function( time, deltaTime, timeDirection, accuIndex ) { // called by the mixer var startTime = this._startTime; if ( startTime !== null ) { // check for scheduled start of action var timeRunning = ( time - startTime ) * timeDirection; if ( timeRunning < 0 || timeDirection === 0 ) { return; // yet to come / don't decide when delta = 0 } // start this._startTime = null; // unschedule deltaTime = timeDirection * timeRunning; } // apply time scale and advance time deltaTime *= this._updateTimeScale( time ); var clipTime = this._updateTime( deltaTime ); // note: _updateTime may disable the action resulting in // an effective weight of 0 var weight = this._updateWeight( time ); if ( weight > 0 ) { var interpolants = this._interpolants; var propertyMixers = this._propertyBindings; for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { interpolants[ j ].evaluate( clipTime ); propertyMixers[ j ].accumulate( accuIndex, weight ); } } }, _updateWeight: function( time ) { var weight = 0; if ( this.enabled ) { weight = this.weight; var interpolant = this._weightInterpolant; if ( interpolant !== null ) { var interpolantValue = interpolant.evaluate( time )[ 0 ]; weight *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopFading(); if ( interpolantValue === 0 ) { // faded out, disable this.enabled = false; } } } } this._effectiveWeight = weight; return weight; }, _updateTimeScale: function( time ) { var timeScale = 0; if ( ! this.paused ) { timeScale = this.timeScale; var interpolant = this._timeScaleInterpolant; if ( interpolant !== null ) { var interpolantValue = interpolant.evaluate( time )[ 0 ]; timeScale *= interpolantValue; if ( time > interpolant.parameterPositions[ 1 ] ) { this.stopWarping(); if ( timeScale === 0 ) { // motion has halted, pause this.paused = true; } else { // warp done - apply final time scale this.timeScale = timeScale; } } } } this._effectiveTimeScale = timeScale; return timeScale; }, _updateTime: function( deltaTime ) { var time = this.time + deltaTime; if ( deltaTime === 0 ) return time; var duration = this._clip.duration, loop = this.loop, loopCount = this._loopCount; if ( loop === LoopOnce ) { if ( loopCount === -1 ) { // just started this._loopCount = 0; this._setEndings( true, true, false ); } handle_stop: { if ( time >= duration ) { time = duration; } else if ( time < 0 ) { time = 0; } else break handle_stop; if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; this._mixer.dispatchEvent( { type: 'finished', action: this, direction: deltaTime < 0 ? -1 : 1 } ); } } else { // repetitive Repeat or PingPong var pingPong = ( loop === LoopPingPong ); if ( loopCount === -1 ) { // just started if ( deltaTime >= 0 ) { loopCount = 0; this._setEndings( true, this.repetitions === 0, pingPong ); } else { // when looping in reverse direction, the initial // transition through zero counts as a repetition, // so leave loopCount at -1 this._setEndings( this.repetitions === 0, true, pingPong ); } } if ( time >= duration || time < 0 ) { // wrap around var loopDelta = Math.floor( time / duration ); // signed time -= duration * loopDelta; loopCount += Math.abs( loopDelta ); var pending = this.repetitions - loopCount; if ( pending < 0 ) { // have to stop (switch state, clamp time, fire event) if ( this.clampWhenFinished ) this.paused = true; else this.enabled = false; time = deltaTime > 0 ? duration : 0; this._mixer.dispatchEvent( { type: 'finished', action: this, direction: deltaTime > 0 ? 1 : -1 } ); } else { // keep running if ( pending === 0 ) { // entering the last round var atStart = deltaTime < 0; this._setEndings( atStart, ! atStart, pingPong ); } else { this._setEndings( false, false, pingPong ); } this._loopCount = loopCount; this._mixer.dispatchEvent( { type: 'loop', action: this, loopDelta: loopDelta } ); } } if ( pingPong && ( loopCount & 1 ) === 1 ) { // invert time for the "pong round" this.time = time; return duration - time; } } this.time = time; return time; }, _setEndings: function( atStart, atEnd, pingPong ) { var settings = this._interpolantSettings; if ( pingPong ) { settings.endingStart = ZeroSlopeEnding; settings.endingEnd = ZeroSlopeEnding; } else { // assuming for LoopOnce atStart == atEnd == true if ( atStart ) { settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingStart = WrapAroundEnding; } if ( atEnd ) { settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; } else { settings.endingEnd = WrapAroundEnding; } } }, _scheduleFading: function( duration, weightNow, weightThen ) { var mixer = this._mixer, now = mixer.time, interpolant = this._weightInterpolant; if ( interpolant === null ) { interpolant = mixer._lendControlInterpolant(); this._weightInterpolant = interpolant; } var times = interpolant.parameterPositions, values = interpolant.sampleValues; times[ 0 ] = now; values[ 0 ] = weightNow; times[ 1 ] = now + duration; values[ 1 ] = weightThen; return this; } }; /** * * Player for AnimationClips. * * * @author Ben Houston / http://clara.io/ * @author David Sarno / http://lighthaus.us/ * @author tschw */ function AnimationMixer( root ) { this._root = root; this._initMemoryManager(); this._accuIndex = 0; this.time = 0; this.timeScale = 1.0; } Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, { // return an action for a clip optionally using a custom root target // object (this method allocates a lot of dynamic memory in case a // previously unknown clip/root combination is specified) clipAction: function( clip, optionalRoot ) { var root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject !== null ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ], prototypeAction = null; if ( actionsForClip !== undefined ) { var existingAction = actionsForClip.actionByRoot[ rootUuid ]; if ( existingAction !== undefined ) { return existingAction; } // we know the clip, so we don't have to parse all // the bindings again but can just copy prototypeAction = actionsForClip.knownActions[ 0 ]; // also, take the clip from the prototype action if ( clipObject === null ) clipObject = prototypeAction._clip; } // clip must be known when specified via string if ( clipObject === null ) return null; // allocate all resources required to run it var newAction = new AnimationAction( this, clipObject, optionalRoot ); this._bindAction( newAction, prototypeAction ); // and make the action known to the memory manager this._addInactiveAction( newAction, clipUuid, rootUuid ); return newAction; }, // get an existing action existingAction: function( clip, optionalRoot ) { var root = optionalRoot || this._root, rootUuid = root.uuid, clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip, clipUuid = clipObject ? clipObject.uuid : clip, actionsForClip = this._actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { return actionsForClip.actionByRoot[ rootUuid ] || null; } return null; }, // deactivates all previously scheduled actions stopAllAction: function() { var actions = this._actions, nActions = this._nActiveActions, bindings = this._bindings, nBindings = this._nActiveBindings; this._nActiveActions = 0; this._nActiveBindings = 0; for ( var i = 0; i !== nActions; ++ i ) { actions[ i ].reset(); } for ( var i = 0; i !== nBindings; ++ i ) { bindings[ i ].useCount = 0; } return this; }, // advance the time and update apply the animation update: function( deltaTime ) { deltaTime *= this.timeScale; var actions = this._actions, nActions = this._nActiveActions, time = this.time += deltaTime, timeDirection = Math.sign( deltaTime ), accuIndex = this._accuIndex ^= 1; // run active actions for ( var i = 0; i !== nActions; ++ i ) { var action = actions[ i ]; if ( action.enabled ) { action._update( time, deltaTime, timeDirection, accuIndex ); } } // update scene graph var bindings = this._bindings, nBindings = this._nActiveBindings; for ( var i = 0; i !== nBindings; ++ i ) { bindings[ i ].apply( accuIndex ); } return this; }, // return this mixer's root target object getRoot: function() { return this._root; }, // free all resources specific to a particular clip uncacheClip: function( clip ) { var actions = this._actions, clipUuid = clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip !== undefined ) { // note: just calling _removeInactiveAction would mess up the // iteration state and also require updating the state we can // just throw away var actionsToRemove = actionsForClip.knownActions; for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { var action = actionsToRemove[ i ]; this._deactivateAction( action ); var cacheIndex = action._cacheIndex, lastInactiveAction = actions[ actions.length - 1 ]; action._cacheIndex = null; action._byClipCacheIndex = null; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); this._removeInactiveBindingsForAction( action ); } delete actionsByClip[ clipUuid ]; } }, // free all resources specific to a particular root target object uncacheRoot: function( root ) { var rootUuid = root.uuid, actionsByClip = this._actionsByClip; for ( var clipUuid in actionsByClip ) { var actionByRoot = actionsByClip[ clipUuid ].actionByRoot, action = actionByRoot[ rootUuid ]; if ( action !== undefined ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } var bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ]; if ( bindingByName !== undefined ) { for ( var trackName in bindingByName ) { var binding = bindingByName[ trackName ]; binding.restoreOriginalState(); this._removeInactiveBinding( binding ); } } }, // remove a targeted clip from the cache uncacheAction: function( clip, optionalRoot ) { var action = this.existingAction( clip, optionalRoot ); if ( action !== null ) { this._deactivateAction( action ); this._removeInactiveAction( action ); } } } ); // Implementation details: Object.assign( AnimationMixer.prototype, { _bindAction: function( action, prototypeAction ) { var root = action._localRoot || this._root, tracks = action._clip.tracks, nTracks = tracks.length, bindings = action._propertyBindings, interpolants = action._interpolants, rootUuid = root.uuid, bindingsByRoot = this._bindingsByRootAndName, bindingsByName = bindingsByRoot[ rootUuid ]; if ( bindingsByName === undefined ) { bindingsByName = {}; bindingsByRoot[ rootUuid ] = bindingsByName; } for ( var i = 0; i !== nTracks; ++ i ) { var track = tracks[ i ], trackName = track.name, binding = bindingsByName[ trackName ]; if ( binding !== undefined ) { bindings[ i ] = binding; } else { binding = bindings[ i ]; if ( binding !== undefined ) { // existing binding, make sure the cache knows if ( binding._cacheIndex === null ) { ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); } continue; } var path = prototypeAction && prototypeAction. _propertyBindings[ i ].binding.parsedPath; binding = new PropertyMixer( PropertyBinding.create( root, trackName, path ), track.ValueTypeName, track.getValueSize() ); ++ binding.referenceCount; this._addInactiveBinding( binding, rootUuid, trackName ); bindings[ i ] = binding; } interpolants[ i ].resultBuffer = binding.buffer; } }, _activateAction: function( action ) { if ( ! this._isActiveAction( action ) ) { if ( action._cacheIndex === null ) { // this action has been forgotten by the cache, but the user // appears to be still using it -> rebind var rootUuid = ( action._localRoot || this._root ).uuid, clipUuid = action._clip.uuid, actionsForClip = this._actionsByClip[ clipUuid ]; this._bindAction( action, actionsForClip && actionsForClip.knownActions[ 0 ] ); this._addInactiveAction( action, clipUuid, rootUuid ); } var bindings = action._propertyBindings; // increment reference counts / sort out state for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( binding.useCount ++ === 0 ) { this._lendBinding( binding ); binding.saveOriginalState(); } } this._lendAction( action ); } }, _deactivateAction: function( action ) { if ( this._isActiveAction( action ) ) { var bindings = action._propertyBindings; // decrement reference counts / sort out state for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( -- binding.useCount === 0 ) { binding.restoreOriginalState(); this._takeBackBinding( binding ); } } this._takeBackAction( action ); } }, // Memory manager _initMemoryManager: function() { this._actions = []; // 'nActiveActions' followed by inactive ones this._nActiveActions = 0; this._actionsByClip = {}; // inside: // { // knownActions: Array< AnimationAction > - used as prototypes // actionByRoot: AnimationAction - lookup // } this._bindings = []; // 'nActiveBindings' followed by inactive ones this._nActiveBindings = 0; this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > this._controlInterpolants = []; // same game as above this._nActiveControlInterpolants = 0; var scope = this; this.stats = { actions: { get total() { return scope._actions.length; }, get inUse() { return scope._nActiveActions; } }, bindings: { get total() { return scope._bindings.length; }, get inUse() { return scope._nActiveBindings; } }, controlInterpolants: { get total() { return scope._controlInterpolants.length; }, get inUse() { return scope._nActiveControlInterpolants; } } }; }, // Memory management for AnimationAction objects _isActiveAction: function( action ) { var index = action._cacheIndex; return index !== null && index < this._nActiveActions; }, _addInactiveAction: function( action, clipUuid, rootUuid ) { var actions = this._actions, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ]; if ( actionsForClip === undefined ) { actionsForClip = { knownActions: [ action ], actionByRoot: {} }; action._byClipCacheIndex = 0; actionsByClip[ clipUuid ] = actionsForClip; } else { var knownActions = actionsForClip.knownActions; action._byClipCacheIndex = knownActions.length; knownActions.push( action ); } action._cacheIndex = actions.length; actions.push( action ); actionsForClip.actionByRoot[ rootUuid ] = action; }, _removeInactiveAction: function( action ) { var actions = this._actions, lastInactiveAction = actions[ actions.length - 1 ], cacheIndex = action._cacheIndex; lastInactiveAction._cacheIndex = cacheIndex; actions[ cacheIndex ] = lastInactiveAction; actions.pop(); action._cacheIndex = null; var clipUuid = action._clip.uuid, actionsByClip = this._actionsByClip, actionsForClip = actionsByClip[ clipUuid ], knownActionsForClip = actionsForClip.knownActions, lastKnownAction = knownActionsForClip[ knownActionsForClip.length - 1 ], byClipCacheIndex = action._byClipCacheIndex; lastKnownAction._byClipCacheIndex = byClipCacheIndex; knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; knownActionsForClip.pop(); action._byClipCacheIndex = null; var actionByRoot = actionsForClip.actionByRoot, rootUuid = ( actions._localRoot || this._root ).uuid; delete actionByRoot[ rootUuid ]; if ( knownActionsForClip.length === 0 ) { delete actionsByClip[ clipUuid ]; } this._removeInactiveBindingsForAction( action ); }, _removeInactiveBindingsForAction: function( action ) { var bindings = action._propertyBindings; for ( var i = 0, n = bindings.length; i !== n; ++ i ) { var binding = bindings[ i ]; if ( -- binding.referenceCount === 0 ) { this._removeInactiveBinding( binding ); } } }, _lendAction: function( action ) { // [ active actions | inactive actions ] // [ active actions >| inactive actions ] // s a // <-swap-> // a s var actions = this._actions, prevIndex = action._cacheIndex, lastActiveIndex = this._nActiveActions ++, firstInactiveAction = actions[ lastActiveIndex ]; action._cacheIndex = lastActiveIndex; actions[ lastActiveIndex ] = action; firstInactiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = firstInactiveAction; }, _takeBackAction: function( action ) { // [ active actions | inactive actions ] // [ active actions |< inactive actions ] // a s // <-swap-> // s a var actions = this._actions, prevIndex = action._cacheIndex, firstInactiveIndex = -- this._nActiveActions, lastActiveAction = actions[ firstInactiveIndex ]; action._cacheIndex = firstInactiveIndex; actions[ firstInactiveIndex ] = action; lastActiveAction._cacheIndex = prevIndex; actions[ prevIndex ] = lastActiveAction; }, // Memory management for PropertyMixer objects _addInactiveBinding: function( binding, rootUuid, trackName ) { var bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], bindings = this._bindings; if ( bindingByName === undefined ) { bindingByName = {}; bindingsByRoot[ rootUuid ] = bindingByName; } bindingByName[ trackName ] = binding; binding._cacheIndex = bindings.length; bindings.push( binding ); }, _removeInactiveBinding: function( binding ) { var bindings = this._bindings, propBinding = binding.binding, rootUuid = propBinding.rootNode.uuid, trackName = propBinding.path, bindingsByRoot = this._bindingsByRootAndName, bindingByName = bindingsByRoot[ rootUuid ], lastInactiveBinding = bindings[ bindings.length - 1 ], cacheIndex = binding._cacheIndex; lastInactiveBinding._cacheIndex = cacheIndex; bindings[ cacheIndex ] = lastInactiveBinding; bindings.pop(); delete bindingByName[ trackName ]; remove_empty_map: { for ( var _ in bindingByName ) break remove_empty_map; delete bindingsByRoot[ rootUuid ]; } }, _lendBinding: function( binding ) { var bindings = this._bindings, prevIndex = binding._cacheIndex, lastActiveIndex = this._nActiveBindings ++, firstInactiveBinding = bindings[ lastActiveIndex ]; binding._cacheIndex = lastActiveIndex; bindings[ lastActiveIndex ] = binding; firstInactiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = firstInactiveBinding; }, _takeBackBinding: function( binding ) { var bindings = this._bindings, prevIndex = binding._cacheIndex, firstInactiveIndex = -- this._nActiveBindings, lastActiveBinding = bindings[ firstInactiveIndex ]; binding._cacheIndex = firstInactiveIndex; bindings[ firstInactiveIndex ] = binding; lastActiveBinding._cacheIndex = prevIndex; bindings[ prevIndex ] = lastActiveBinding; }, // Memory management of Interpolants for weight and time scale _lendControlInterpolant: function() { var interpolants = this._controlInterpolants, lastActiveIndex = this._nActiveControlInterpolants ++, interpolant = interpolants[ lastActiveIndex ]; if ( interpolant === undefined ) { interpolant = new LinearInterpolant( new Float32Array( 2 ), new Float32Array( 2 ), 1, this._controlInterpolantsResultBuffer ); interpolant.__cacheIndex = lastActiveIndex; interpolants[ lastActiveIndex ] = interpolant; } return interpolant; }, _takeBackControlInterpolant: function( interpolant ) { var interpolants = this._controlInterpolants, prevIndex = interpolant.__cacheIndex, firstInactiveIndex = -- this._nActiveControlInterpolants, lastActiveInterpolant = interpolants[ firstInactiveIndex ]; interpolant.__cacheIndex = firstInactiveIndex; interpolants[ firstInactiveIndex ] = interpolant; lastActiveInterpolant.__cacheIndex = prevIndex; interpolants[ prevIndex ] = lastActiveInterpolant; }, _controlInterpolantsResultBuffer: new Float32Array( 1 ) } ); /** * @author mrdoob / http://mrdoob.com/ */ function Uniform( value ) { if ( typeof value === 'string' ) { console.warn( 'THREE.Uniform: Type parameter is no longer needed.' ); value = arguments[ 1 ]; } this.value = value; } Uniform.prototype.clone = function () { return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); }; /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedBufferGeometry() { BufferGeometry.call( this ); this.type = 'InstancedBufferGeometry'; this.maxInstancedCount = undefined; } InstancedBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); InstancedBufferGeometry.prototype.constructor = InstancedBufferGeometry; InstancedBufferGeometry.prototype.isInstancedBufferGeometry = true; InstancedBufferGeometry.prototype.addGroup = function ( start, count, materialIndex ) { this.groups.push( { start: start, count: count, materialIndex: materialIndex } ); }; InstancedBufferGeometry.prototype.copy = function ( source ) { var index = source.index; if ( index !== null ) { this.setIndex( index.clone() ); } var attributes = source.attributes; for ( var name in attributes ) { var attribute = attributes[ name ]; this.addAttribute( name, attribute.clone() ); } var groups = source.groups; for ( var i = 0, l = groups.length; i < l; i ++ ) { var group = groups[ i ]; this.addGroup( group.start, group.count, group.materialIndex ); } return this; }; /** * @author benaadams / https://twitter.com/ben_a_adams */ function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { this.uuid = _Math.generateUUID(); this.data = interleavedBuffer; this.itemSize = itemSize; this.offset = offset; this.normalized = normalized === true; } InterleavedBufferAttribute.prototype = { constructor: InterleavedBufferAttribute, isInterleavedBufferAttribute: true, get count() { return this.data.count; }, get array() { return this.data.array; }, setX: function ( index, x ) { this.data.array[ index * this.data.stride + this.offset ] = x; return this; }, setY: function ( index, y ) { this.data.array[ index * this.data.stride + this.offset + 1 ] = y; return this; }, setZ: function ( index, z ) { this.data.array[ index * this.data.stride + this.offset + 2 ] = z; return this; }, setW: function ( index, w ) { this.data.array[ index * this.data.stride + this.offset + 3 ] = w; return this; }, getX: function ( index ) { return this.data.array[ index * this.data.stride + this.offset ]; }, getY: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 1 ]; }, getZ: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 2 ]; }, getW: function ( index ) { return this.data.array[ index * this.data.stride + this.offset + 3 ]; }, setXY: function ( index, x, y ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; return this; }, setXYZ: function ( index, x, y, z ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; return this; }, setXYZW: function ( index, x, y, z, w ) { index = index * this.data.stride + this.offset; this.data.array[ index + 0 ] = x; this.data.array[ index + 1 ] = y; this.data.array[ index + 2 ] = z; this.data.array[ index + 3 ] = w; return this; } }; /** * @author benaadams / https://twitter.com/ben_a_adams */ function InterleavedBuffer( array, stride ) { this.uuid = _Math.generateUUID(); this.array = array; this.stride = stride; this.count = array !== undefined ? array.length / stride : 0; this.dynamic = false; this.updateRange = { offset: 0, count: - 1 }; this.onUploadCallback = function () {}; this.version = 0; } InterleavedBuffer.prototype = { constructor: InterleavedBuffer, isInterleavedBuffer: true, set needsUpdate( value ) { if ( value === true ) this.version ++; }, setArray: function ( array ) { if ( Array.isArray( array ) ) { throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); } this.count = array !== undefined ? array.length / this.stride : 0; this.array = array; }, setDynamic: function ( value ) { this.dynamic = value; return this; }, copy: function ( source ) { this.array = new source.array.constructor( source.array ); this.count = source.count; this.stride = source.stride; this.dynamic = source.dynamic; return this; }, copyAt: function ( index1, attribute, index2 ) { index1 *= this.stride; index2 *= attribute.stride; for ( var i = 0, l = this.stride; i < l; i ++ ) { this.array[ index1 + i ] = attribute.array[ index2 + i ]; } return this; }, set: function ( value, offset ) { if ( offset === undefined ) offset = 0; this.array.set( value, offset ); return this; }, clone: function () { return new this.constructor().copy( this ); }, onUpload: function ( callback ) { this.onUploadCallback = callback; return this; } }; /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { InterleavedBuffer.call( this, array, stride ); this.meshPerAttribute = meshPerAttribute || 1; } InstancedInterleavedBuffer.prototype = Object.create( InterleavedBuffer.prototype ); InstancedInterleavedBuffer.prototype.constructor = InstancedInterleavedBuffer; InstancedInterleavedBuffer.prototype.isInstancedInterleavedBuffer = true; InstancedInterleavedBuffer.prototype.copy = function ( source ) { InterleavedBuffer.prototype.copy.call( this, source ); this.meshPerAttribute = source.meshPerAttribute; return this; }; /** * @author benaadams / https://twitter.com/ben_a_adams */ function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) { BufferAttribute.call( this, array, itemSize ); this.meshPerAttribute = meshPerAttribute || 1; } InstancedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); InstancedBufferAttribute.prototype.constructor = InstancedBufferAttribute; InstancedBufferAttribute.prototype.isInstancedBufferAttribute = true; InstancedBufferAttribute.prototype.copy = function ( source ) { BufferAttribute.prototype.copy.call( this, source ); this.meshPerAttribute = source.meshPerAttribute; return this; }; /** * @author mrdoob / http://mrdoob.com/ * @author bhouston / http://clara.io/ * @author stephomi / http://stephaneginier.com/ */ function Raycaster( origin, direction, near, far ) { this.ray = new Ray( origin, direction ); // direction is assumed to be normalized (for accurate distance calculations) this.near = near || 0; this.far = far || Infinity; this.params = { Mesh: {}, Line: {}, LOD: {}, Points: { threshold: 1 }, Sprite: {} }; Object.defineProperties( this.params, { PointCloud: { get: function () { console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); return this.Points; } } } ); } function ascSort( a, b ) { return a.distance - b.distance; } function intersectObject( object, raycaster, intersects, recursive ) { if ( object.visible === false ) return; object.raycast( raycaster, intersects ); if ( recursive === true ) { var children = object.children; for ( var i = 0, l = children.length; i < l; i ++ ) { intersectObject( children[ i ], raycaster, intersects, true ); } } } // Raycaster.prototype = { constructor: Raycaster, linePrecision: 1, set: function ( origin, direction ) { // direction is assumed to be normalized (for accurate distance calculations) this.ray.set( origin, direction ); }, setFromCamera: function ( coords, camera ) { if ( (camera && camera.isPerspectiveCamera) ) { this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); } else if ( (camera && camera.isOrthographicCamera) ) { this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); } else { console.error( 'THREE.Raycaster: Unsupported camera type.' ); } }, intersectObject: function ( object, recursive ) { var intersects = []; intersectObject( object, this, intersects, recursive ); intersects.sort( ascSort ); return intersects; }, intersectObjects: function ( objects, recursive ) { var intersects = []; if ( Array.isArray( objects ) === false ) { console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); return intersects; } for ( var i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); } intersects.sort( ascSort ); return intersects; } }; /** * @author alteredq / http://alteredqualia.com/ */ function Clock( autoStart ) { this.autoStart = ( autoStart !== undefined ) ? autoStart : true; this.startTime = 0; this.oldTime = 0; this.elapsedTime = 0; this.running = false; } Clock.prototype = { constructor: Clock, start: function () { this.startTime = ( performance || Date ).now(); this.oldTime = this.startTime; this.elapsedTime = 0; this.running = true; }, stop: function () { this.getElapsedTime(); this.running = false; }, getElapsedTime: function () { this.getDelta(); return this.elapsedTime; }, getDelta: function () { var diff = 0; if ( this.autoStart && ! this.running ) { this.start(); } if ( this.running ) { var newTime = ( performance || Date ).now(); diff = ( newTime - this.oldTime ) / 1000; this.oldTime = newTime; this.elapsedTime += diff; } return diff; } }; /** * Spline from Tween.js, slightly optimized (and trashed) * http://sole.github.com/tween.js/examples/05_spline.html * * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ function Spline( points ) { this.points = points; var c = [], v3 = { x: 0, y: 0, z: 0 }, point, intPoint, weight, w2, w3, pa, pb, pc, pd; this.initFromArray = function ( a ) { this.points = []; for ( var i = 0; i < a.length; i ++ ) { this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; } }; this.getPoint = function ( k ) { point = ( this.points.length - 1 ) * k; intPoint = Math.floor( point ); weight = point - intPoint; c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; c[ 1 ] = intPoint; c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1; c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2; pa = this.points[ c[ 0 ] ]; pb = this.points[ c[ 1 ] ]; pc = this.points[ c[ 2 ] ]; pd = this.points[ c[ 3 ] ]; w2 = weight * weight; w3 = weight * w2; v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); return v3; }; this.getControlPointsArray = function () { var i, p, l = this.points.length, coords = []; for ( i = 0; i < l; i ++ ) { p = this.points[ i ]; coords[ i ] = [ p.x, p.y, p.z ]; } return coords; }; // approximate length by summing linear segments this.getLength = function ( nSubDivisions ) { var i, index, nSamples, position, point = 0, intPoint = 0, oldIntPoint = 0, oldPosition = new Vector3(), tmpVec = new Vector3(), chunkLengths = [], totalLength = 0; // first point has 0 length chunkLengths[ 0 ] = 0; if ( ! nSubDivisions ) nSubDivisions = 100; nSamples = this.points.length * nSubDivisions; oldPosition.copy( this.points[ 0 ] ); for ( i = 1; i < nSamples; i ++ ) { index = i / nSamples; position = this.getPoint( index ); tmpVec.copy( position ); totalLength += tmpVec.distanceTo( oldPosition ); oldPosition.copy( position ); point = ( this.points.length - 1 ) * index; intPoint = Math.floor( point ); if ( intPoint !== oldIntPoint ) { chunkLengths[ intPoint ] = totalLength; oldIntPoint = intPoint; } } // last point ends with total length chunkLengths[ chunkLengths.length ] = totalLength; return { chunks: chunkLengths, total: totalLength }; }; this.reparametrizeByArcLength = function ( samplingCoef ) { var i, j, index, indexCurrent, indexNext, realDistance, sampling, position, newpoints = [], tmpVec = new Vector3(), sl = this.getLength(); newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); for ( i = 1; i < this.points.length; i ++ ) { //tmpVec.copy( this.points[ i - 1 ] ); //linearDistance = tmpVec.distanceTo( this.points[ i ] ); realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; sampling = Math.ceil( samplingCoef * realDistance / sl.total ); indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); indexNext = i / ( this.points.length - 1 ); for ( j = 1; j < sampling - 1; j ++ ) { index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); position = this.getPoint( index ); newpoints.push( tmpVec.copy( position ).clone() ); } newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); } this.points = newpoints; }; // Catmull-Rom function interpolate( p0, p1, p2, p3, t, t2, t3 ) { var v0 = ( p2 - p0 ) * 0.5, v1 = ( p3 - p1 ) * 0.5; return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; } } /** * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley * * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system * * The poles (phi) are at the positive and negative y axis. * The equator starts at positive z. */ function Spherical( radius, phi, theta ) { this.radius = ( radius !== undefined ) ? radius : 1.0; this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere return this; } Spherical.prototype = { constructor: Spherical, set: function ( radius, phi, theta ) { this.radius = radius; this.phi = phi; this.theta = theta; return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( other ) { this.radius = other.radius; this.phi = other.phi; this.theta = other.theta; return this; }, // restrict phi to be betwee EPS and PI-EPS makeSafe: function() { var EPS = 0.000001; this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); return this; }, setFromVector3: function( vec3 ) { this.radius = vec3.length(); if ( this.radius === 0 ) { this.theta = 0; this.phi = 0; } else { this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle } return this; } }; /** * @author Mugen87 / https://github.com/Mugen87 * * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system * */ function Cylindrical( radius, theta, y ) { this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane return this; } Cylindrical.prototype = { constructor: Cylindrical, set: function ( radius, theta, y ) { this.radius = radius; this.theta = theta; this.y = y; return this; }, clone: function () { return new this.constructor().copy( this ); }, copy: function ( other ) { this.radius = other.radius; this.theta = other.theta; this.y = other.y; return this; }, setFromVector3: function( vec3 ) { this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z ); this.theta = Math.atan2( vec3.x, vec3.z ); this.y = vec3.y; return this; } }; /** * @author alteredq / http://alteredqualia.com/ */ function MorphBlendMesh( geometry, material ) { Mesh.call( this, geometry, material ); this.animationsMap = {}; this.animationsList = []; // prepare default animation // (all frames played together in 1 second) var numFrames = this.geometry.morphTargets.length; var name = "__default"; var startFrame = 0; var endFrame = numFrames - 1; var fps = numFrames / 1; this.createAnimation( name, startFrame, endFrame, fps ); this.setAnimationWeight( name, 1 ); } MorphBlendMesh.prototype = Object.create( Mesh.prototype ); MorphBlendMesh.prototype.constructor = MorphBlendMesh; MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { var animation = { start: start, end: end, length: end - start + 1, fps: fps, duration: ( end - start ) / fps, lastFrame: 0, currentFrame: 0, active: false, time: 0, direction: 1, weight: 1, directionBackwards: false, mirroredLoop: false }; this.animationsMap[ name ] = animation; this.animationsList.push( animation ); }; MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { var pattern = /([a-z]+)_?(\d+)/i; var firstAnimation, frameRanges = {}; var geometry = this.geometry; for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { var morph = geometry.morphTargets[ i ]; var chunks = morph.name.match( pattern ); if ( chunks && chunks.length > 1 ) { var name = chunks[ 1 ]; if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; var range = frameRanges[ name ]; if ( i < range.start ) range.start = i; if ( i > range.end ) range.end = i; if ( ! firstAnimation ) firstAnimation = name; } } for ( var name in frameRanges ) { var range = frameRanges[ name ]; this.createAnimation( name, range.start, range.end, fps ); } this.firstAnimation = firstAnimation; }; MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { var animation = this.animationsMap[ name ]; if ( animation ) { animation.direction = 1; animation.directionBackwards = false; } }; MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { var animation = this.animationsMap[ name ]; if ( animation ) { animation.direction = - 1; animation.directionBackwards = true; } }; MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { var animation = this.animationsMap[ name ]; if ( animation ) { animation.fps = fps; animation.duration = ( animation.end - animation.start ) / animation.fps; } }; MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { var animation = this.animationsMap[ name ]; if ( animation ) { animation.duration = duration; animation.fps = ( animation.end - animation.start ) / animation.duration; } }; MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { var animation = this.animationsMap[ name ]; if ( animation ) { animation.weight = weight; } }; MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { var animation = this.animationsMap[ name ]; if ( animation ) { animation.time = time; } }; MorphBlendMesh.prototype.getAnimationTime = function ( name ) { var time = 0; var animation = this.animationsMap[ name ]; if ( animation ) { time = animation.time; } return time; }; MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { var duration = - 1; var animation = this.animationsMap[ name ]; if ( animation ) { duration = animation.duration; } return duration; }; MorphBlendMesh.prototype.playAnimation = function ( name ) { var animation = this.animationsMap[ name ]; if ( animation ) { animation.time = 0; animation.active = true; } else { console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" ); } }; MorphBlendMesh.prototype.stopAnimation = function ( name ) { var animation = this.animationsMap[ name ]; if ( animation ) { animation.active = false; } }; MorphBlendMesh.prototype.update = function ( delta ) { for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { var animation = this.animationsList[ i ]; if ( ! animation.active ) continue; var frameTime = animation.duration / animation.length; animation.time += animation.direction * delta; if ( animation.mirroredLoop ) { if ( animation.time > animation.duration || animation.time < 0 ) { animation.direction *= - 1; if ( animation.time > animation.duration ) { animation.time = animation.duration; animation.directionBackwards = true; } if ( animation.time < 0 ) { animation.time = 0; animation.directionBackwards = false; } } } else { animation.time = animation.time % animation.duration; if ( animation.time < 0 ) animation.time += animation.duration; } var keyframe = animation.start + _Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); var weight = animation.weight; if ( keyframe !== animation.currentFrame ) { this.morphTargetInfluences[ animation.lastFrame ] = 0; this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; this.morphTargetInfluences[ keyframe ] = 0; animation.lastFrame = animation.currentFrame; animation.currentFrame = keyframe; } var mix = ( animation.time % frameTime ) / frameTime; if ( animation.directionBackwards ) mix = 1 - mix; if ( animation.currentFrame !== animation.lastFrame ) { this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; } else { this.morphTargetInfluences[ animation.currentFrame ] = weight; } } }; /** * @author alteredq / http://alteredqualia.com/ */ function ImmediateRenderObject( material ) { Object3D.call( this ); this.material = material; this.render = function ( renderCallback ) {}; } ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; ImmediateRenderObject.prototype.isImmediateRenderObject = true; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function VertexNormalsHelper( object, size, hex, linewidth ) { this.object = object; this.size = ( size !== undefined ) ? size : 1; var color = ( hex !== undefined ) ? hex : 0xff0000; var width = ( linewidth !== undefined ) ? linewidth : 1; // var nNormals = 0; var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { nNormals = objGeometry.faces.length * 3; } else if ( objGeometry && objGeometry.isBufferGeometry ) { nNormals = objGeometry.attributes.normal.count; } // var geometry = new BufferGeometry(); var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); geometry.addAttribute( 'position', positions ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); // this.matrixAutoUpdate = false; this.update(); } VertexNormalsHelper.prototype = Object.create( LineSegments.prototype ); VertexNormalsHelper.prototype.constructor = VertexNormalsHelper; VertexNormalsHelper.prototype.update = ( function () { var v1 = new Vector3(); var v2 = new Vector3(); var normalMatrix = new Matrix3(); return function update() { var keys = [ 'a', 'b', 'c' ]; this.object.updateMatrixWorld( true ); normalMatrix.getNormalMatrix( this.object.matrixWorld ); var matrixWorld = this.object.matrixWorld; var position = this.geometry.attributes.position; // var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { var vertices = objGeometry.vertices; var faces = objGeometry.faces; var idx = 0; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { var vertex = vertices[ face[ keys[ j ] ] ]; var normal = face.vertexNormals[ j ]; v1.copy( vertex ).applyMatrix4( matrixWorld ); v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } } } else if ( objGeometry && objGeometry.isBufferGeometry ) { var objPos = objGeometry.attributes.position; var objNorm = objGeometry.attributes.normal; var idx = 0; // for simplicity, ignore index and drawcalls, and render every normal for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } } position.needsUpdate = true; return this; }; }() ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function SpotLightHelper( light ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; var geometry = new BufferGeometry(); var positions = [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, - 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, - 1, 1 ]; for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { var p1 = ( i / l ) * Math.PI * 2; var p2 = ( j / l ) * Math.PI * 2; positions.push( Math.cos( p1 ), Math.sin( p1 ), 1, Math.cos( p2 ), Math.sin( p2 ), 1 ); } geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); var material = new LineBasicMaterial( { fog: false } ); this.cone = new LineSegments( geometry, material ); this.add( this.cone ); this.update(); } SpotLightHelper.prototype = Object.create( Object3D.prototype ); SpotLightHelper.prototype.constructor = SpotLightHelper; SpotLightHelper.prototype.dispose = function () { this.cone.geometry.dispose(); this.cone.material.dispose(); }; SpotLightHelper.prototype.update = function () { var vector = new Vector3(); var vector2 = new Vector3(); return function update() { var coneLength = this.light.distance ? this.light.distance : 1000; var coneWidth = coneLength * Math.tan( this.light.angle ); this.cone.scale.set( coneWidth, coneWidth, coneLength ); vector.setFromMatrixPosition( this.light.matrixWorld ); vector2.setFromMatrixPosition( this.light.target.matrixWorld ); this.cone.lookAt( vector2.sub( vector ) ); this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); }; }(); /** * @author Sean Griffin / http://twitter.com/sgrif * @author Michael Guerrero / http://realitymeltdown.com * @author mrdoob / http://mrdoob.com/ * @author ikerr / http://verold.com * @author Mugen87 / https://github.com/Mugen87 */ function SkeletonHelper( object ) { this.bones = this.getBoneList( object ); var geometry = new BufferGeometry(); var vertices = []; var colors = []; var color1 = new Color( 0, 0, 1 ); var color2 = new Color( 0, 1, 0 ); for ( var i = 0; i < this.bones.length; i ++ ) { var bone = this.bones[ i ]; if ( bone.parent && bone.parent.isBone ) { vertices.push( 0, 0, 0 ); vertices.push( 0, 0, 0 ); colors.push( color1.r, color1.g, color1.b ); colors.push( color2.r, color2.g, color2.b ); } } geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } ); LineSegments.call( this, geometry, material ); this.root = object; this.matrix = object.matrixWorld; this.matrixAutoUpdate = false; this.update(); } SkeletonHelper.prototype = Object.create( LineSegments.prototype ); SkeletonHelper.prototype.constructor = SkeletonHelper; SkeletonHelper.prototype.getBoneList = function( object ) { var boneList = []; if ( object && object.isBone ) { boneList.push( object ); } for ( var i = 0; i < object.children.length; i ++ ) { boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); } return boneList; }; SkeletonHelper.prototype.update = function () { var vector = new Vector3(); var boneMatrix = new Matrix4(); var matrixWorldInv = new Matrix4(); return function update() { var geometry = this.geometry; var position = geometry.getAttribute( 'position' ); matrixWorldInv.getInverse( this.root.matrixWorld ); for ( var i = 0, j = 0; i < this.bones.length; i ++ ) { var bone = this.bones[ i ]; if ( bone.parent && bone.parent.isBone ) { boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); vector.setFromMatrixPosition( boneMatrix ); position.setXYZ( j, vector.x, vector.y, vector.z ); boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); vector.setFromMatrixPosition( boneMatrix ); position.setXYZ( j + 1, vector.x, vector.y, vector.z ); j += 2; } } geometry.getAttribute( 'position' ).needsUpdate = true; }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ function PointLightHelper( light, sphereSize ) { this.light = light; this.light.updateMatrixWorld(); var geometry = new SphereBufferGeometry( sphereSize, 4, 2 ); var material = new MeshBasicMaterial( { wireframe: true, fog: false } ); material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); Mesh.call( this, geometry, material ); this.matrix = this.light.matrixWorld; this.matrixAutoUpdate = false; /* var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); var d = light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.scale.set( d, d, d ); } this.add( this.lightDistance ); */ } PointLightHelper.prototype = Object.create( Mesh.prototype ); PointLightHelper.prototype.constructor = PointLightHelper; PointLightHelper.prototype.dispose = function () { this.geometry.dispose(); this.material.dispose(); }; PointLightHelper.prototype.update = function () { this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); /* var d = this.light.distance; if ( d === 0.0 ) { this.lightDistance.visible = false; } else { this.lightDistance.visible = true; this.lightDistance.scale.set( d, d, d ); } */ }; /** * @author abelnation / http://github.com/abelnation * @author Mugen87 / http://github.com/Mugen87 */ function RectAreaLightHelper( light ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); var materialFront = new MeshBasicMaterial( { color: light.color, fog: false } ); var materialBack = new MeshBasicMaterial( { color: light.color, fog: false, wireframe: true } ); var geometry = new BufferGeometry(); geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( 6 * 3 ), 3 ) ); // shows the "front" of the light, e.g. where light comes from this.add( new Mesh( geometry, materialFront ) ); // shows the "back" of the light, which does not emit light this.add( new Mesh( geometry, materialBack ) ); this.update(); } RectAreaLightHelper.prototype = Object.create( Object3D.prototype ); RectAreaLightHelper.prototype.constructor = RectAreaLightHelper; RectAreaLightHelper.prototype.dispose = function () { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); this.children[ 1 ].geometry.dispose(); this.children[ 1 ].material.dispose(); }; RectAreaLightHelper.prototype.update = function () { var vector1 = new Vector3(); var vector2 = new Vector3(); return function update() { var mesh1 = this.children[ 0 ]; var mesh2 = this.children[ 1 ]; if ( this.light.target ) { vector1.setFromMatrixPosition( this.light.matrixWorld ); vector2.setFromMatrixPosition( this.light.target.matrixWorld ); var lookVec = vector2.clone().sub( vector1 ); mesh1.lookAt( lookVec ); mesh2.lookAt( lookVec ); } // update materials mesh1.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); mesh2.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); // calculate new dimensions of the helper var hx = this.light.width * 0.5; var hy = this.light.height * 0.5; // because the buffer attribute is shared over both geometries, we only have to update once var position = mesh1.geometry.getAttribute( 'position' ); var array = position.array; // first face array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0; array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0; array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0; // second face array[ 9 ] = - hx; array[ 10 ] = hy; array[ 11 ] = 0; array[ 12 ] = - hx; array[ 13 ] = - hy; array[ 14 ] = 0; array[ 15 ] = hx; array[ 16 ] = - hy; array[ 17 ] = 0; position.needsUpdate = true; }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / https://github.com/Mugen87 */ function HemisphereLightHelper( light, size ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; var geometry = new OctahedronBufferGeometry( size ); geometry.rotateY( Math.PI * 0.5 ); var material = new MeshBasicMaterial( { vertexColors: VertexColors, wireframe: true } ); var position = geometry.getAttribute( 'position' ); var colors = new Float32Array( position.count * 3 ); geometry.addAttribute( 'color', new BufferAttribute( colors, 3 ) ); this.add( new Mesh( geometry, material ) ); this.update(); } HemisphereLightHelper.prototype = Object.create( Object3D.prototype ); HemisphereLightHelper.prototype.constructor = HemisphereLightHelper; HemisphereLightHelper.prototype.dispose = function () { this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); }; HemisphereLightHelper.prototype.update = function () { var vector = new Vector3(); var color1 = new Color(); var color2 = new Color(); return function update() { var mesh = this.children[ 0 ]; var colors = mesh.geometry.getAttribute( 'color' ); color1.copy( this.light.color ).multiplyScalar( this.light.intensity ); color2.copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); for ( var i = 0, l = colors.count; i < l; i ++ ) { var color = ( i < ( l / 2 ) ) ? color1 : color2; colors.setXYZ( i, color.r, color.g, color.b ); } mesh.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); colors.needsUpdate = true; }; }(); /** * @author mrdoob / http://mrdoob.com/ */ function GridHelper( size, divisions, color1, color2 ) { size = size || 10; divisions = divisions || 10; color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); var center = divisions / 2; var step = ( size * 2 ) / divisions; var vertices = [], colors = []; for ( var i = 0, j = 0, k = - size; i <= divisions; i ++, k += step ) { vertices.push( - size, 0, k, size, 0, k ); vertices.push( k, 0, - size, k, 0, size ); var color = i === center ? color1 : color2; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; color.toArray( colors, j ); j += 3; } var geometry = new BufferGeometry(); geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } GridHelper.prototype = Object.create( LineSegments.prototype ); GridHelper.prototype.constructor = GridHelper; /** * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / http://github.com/Mugen87 * @author Hectate / http://www.github.com/Hectate */ function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) { radius = radius || 10; radials = radials || 16; circles = circles || 8; divisions = divisions || 64; color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); var vertices = []; var colors = []; var x, z; var v, i, j, r, color; // create the radials for ( i = 0; i <= radials; i ++ ) { v = ( i / radials ) * ( Math.PI * 2 ); x = Math.sin( v ) * radius; z = Math.cos( v ) * radius; vertices.push( 0, 0, 0 ); vertices.push( x, 0, z ); color = ( i & 1 ) ? color1 : color2; colors.push( color.r, color.g, color.b ); colors.push( color.r, color.g, color.b ); } // create the circles for ( i = 0; i <= circles; i ++ ) { color = ( i & 1 ) ? color1 : color2; r = radius - ( radius / circles * i ); for ( j = 0; j < divisions; j ++ ) { // first vertex v = ( j / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); // second vertex v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); x = Math.sin( v ) * r; z = Math.cos( v ) * r; vertices.push( x, 0, z ); colors.push( color.r, color.g, color.b ); } } var geometry = new BufferGeometry(); geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } PolarGridHelper.prototype = Object.create( LineSegments.prototype ); PolarGridHelper.prototype.constructor = PolarGridHelper; /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function FaceNormalsHelper( object, size, hex, linewidth ) { // FaceNormalsHelper only supports THREE.Geometry this.object = object; this.size = ( size !== undefined ) ? size : 1; var color = ( hex !== undefined ) ? hex : 0xffff00; var width = ( linewidth !== undefined ) ? linewidth : 1; // var nNormals = 0; var objGeometry = this.object.geometry; if ( objGeometry && objGeometry.isGeometry ) { nNormals = objGeometry.faces.length; } else { console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' ); } // var geometry = new BufferGeometry(); var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); geometry.addAttribute( 'position', positions ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); // this.matrixAutoUpdate = false; this.update(); } FaceNormalsHelper.prototype = Object.create( LineSegments.prototype ); FaceNormalsHelper.prototype.constructor = FaceNormalsHelper; FaceNormalsHelper.prototype.update = ( function () { var v1 = new Vector3(); var v2 = new Vector3(); var normalMatrix = new Matrix3(); return function update() { this.object.updateMatrixWorld( true ); normalMatrix.getNormalMatrix( this.object.matrixWorld ); var matrixWorld = this.object.matrixWorld; var position = this.geometry.attributes.position; // var objGeometry = this.object.geometry; var vertices = objGeometry.vertices; var faces = objGeometry.faces; var idx = 0; for ( var i = 0, l = faces.length; i < l; i ++ ) { var face = faces[ i ]; var normal = face.normal; v1.copy( vertices[ face.a ] ) .add( vertices[ face.b ] ) .add( vertices[ face.c ] ) .divideScalar( 3 ) .applyMatrix4( matrixWorld ); v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); position.setXYZ( idx, v1.x, v1.y, v1.z ); idx = idx + 1; position.setXYZ( idx, v2.x, v2.y, v2.z ); idx = idx + 1; } position.needsUpdate = true; return this; }; }() ); /** * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley */ function DirectionalLightHelper( light, size ) { Object3D.call( this ); this.light = light; this.light.updateMatrixWorld(); this.matrix = light.matrixWorld; this.matrixAutoUpdate = false; if ( size === undefined ) size = 1; var geometry = new BufferGeometry(); geometry.addAttribute( 'position', new Float32BufferAttribute( [ - size, size, 0, size, size, 0, size, - size, 0, - size, - size, 0, - size, size, 0 ], 3 ) ); var material = new LineBasicMaterial( { fog: false } ); this.add( new Line( geometry, material ) ); geometry = new BufferGeometry(); geometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); this.add( new Line( geometry, material )); this.update(); } DirectionalLightHelper.prototype = Object.create( Object3D.prototype ); DirectionalLightHelper.prototype.constructor = DirectionalLightHelper; DirectionalLightHelper.prototype.dispose = function () { var lightPlane = this.children[ 0 ]; var targetLine = this.children[ 1 ]; lightPlane.geometry.dispose(); lightPlane.material.dispose(); targetLine.geometry.dispose(); targetLine.material.dispose(); }; DirectionalLightHelper.prototype.update = function () { var v1 = new Vector3(); var v2 = new Vector3(); var v3 = new Vector3(); return function update() { v1.setFromMatrixPosition( this.light.matrixWorld ); v2.setFromMatrixPosition( this.light.target.matrixWorld ); v3.subVectors( v2, v1 ); var lightPlane = this.children[ 0 ]; var targetLine = this.children[ 1 ]; lightPlane.lookAt( v3 ); lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); targetLine.lookAt( v3 ); targetLine.scale.z = v3.length(); }; }(); /** * @author alteredq / http://alteredqualia.com/ * @author Mugen87 / https://github.com/Mugen87 * * - shows frustum, line of sight and up of the camera * - suitable for fast updates * - based on frustum visualization in lightgl.js shadowmap example * http://evanw.github.com/lightgl.js/tests/shadowmap.html */ function CameraHelper( camera ) { var geometry = new BufferGeometry(); var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } ); var vertices = []; var colors = []; var pointMap = {}; // colors var colorFrustum = new Color( 0xffaa00 ); var colorCone = new Color( 0xff0000 ); var colorUp = new Color( 0x00aaff ); var colorTarget = new Color( 0xffffff ); var colorCross = new Color( 0x333333 ); // near addLine( "n1", "n2", colorFrustum ); addLine( "n2", "n4", colorFrustum ); addLine( "n4", "n3", colorFrustum ); addLine( "n3", "n1", colorFrustum ); // far addLine( "f1", "f2", colorFrustum ); addLine( "f2", "f4", colorFrustum ); addLine( "f4", "f3", colorFrustum ); addLine( "f3", "f1", colorFrustum ); // sides addLine( "n1", "f1", colorFrustum ); addLine( "n2", "f2", colorFrustum ); addLine( "n3", "f3", colorFrustum ); addLine( "n4", "f4", colorFrustum ); // cone addLine( "p", "n1", colorCone ); addLine( "p", "n2", colorCone ); addLine( "p", "n3", colorCone ); addLine( "p", "n4", colorCone ); // up addLine( "u1", "u2", colorUp ); addLine( "u2", "u3", colorUp ); addLine( "u3", "u1", colorUp ); // target addLine( "c", "t", colorTarget ); addLine( "p", "c", colorCross ); // cross addLine( "cn1", "cn2", colorCross ); addLine( "cn3", "cn4", colorCross ); addLine( "cf1", "cf2", colorCross ); addLine( "cf3", "cf4", colorCross ); function addLine( a, b, color ) { addPoint( a, color ); addPoint( b, color ); } function addPoint( id, color ) { vertices.push( 0, 0, 0 ); colors.push( color.r, color.g, color.b ); if ( pointMap[ id ] === undefined ) { pointMap[ id ] = []; } pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); } geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); LineSegments.call( this, geometry, material ); this.camera = camera; if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); this.matrix = camera.matrixWorld; this.matrixAutoUpdate = false; this.pointMap = pointMap; this.update(); } CameraHelper.prototype = Object.create( LineSegments.prototype ); CameraHelper.prototype.constructor = CameraHelper; CameraHelper.prototype.update = function () { var geometry, pointMap; var vector = new Vector3(); var camera = new Camera(); function setPoint( point, x, y, z ) { vector.set( x, y, z ).unproject( camera ); var points = pointMap[ point ]; if ( points !== undefined ) { var position = geometry.getAttribute( 'position' ); for ( var i = 0, l = points.length; i < l; i ++ ) { position.setXYZ( points[ i ], vector.x, vector.y, vector.z ); } } } return function update() { geometry = this.geometry; pointMap = this.pointMap; var w = 1, h = 1; // we need just camera projection matrix // world matrix must be identity camera.projectionMatrix.copy( this.camera.projectionMatrix ); // center / target setPoint( "c", 0, 0, - 1 ); setPoint( "t", 0, 0, 1 ); // near setPoint( "n1", - w, - h, - 1 ); setPoint( "n2", w, - h, - 1 ); setPoint( "n3", - w, h, - 1 ); setPoint( "n4", w, h, - 1 ); // far setPoint( "f1", - w, - h, 1 ); setPoint( "f2", w, - h, 1 ); setPoint( "f3", - w, h, 1 ); setPoint( "f4", w, h, 1 ); // up setPoint( "u1", w * 0.7, h * 1.1, - 1 ); setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); setPoint( "u3", 0, h * 2, - 1 ); // cross setPoint( "cf1", - w, 0, 1 ); setPoint( "cf2", w, 0, 1 ); setPoint( "cf3", 0, - h, 1 ); setPoint( "cf4", 0, h, 1 ); setPoint( "cn1", - w, 0, - 1 ); setPoint( "cn2", w, 0, - 1 ); setPoint( "cn3", 0, - h, - 1 ); setPoint( "cn4", 0, h, - 1 ); geometry.getAttribute( 'position' ).needsUpdate = true; }; }(); /** * @author mrdoob / http://mrdoob.com/ */ function BoxHelper( object, color ) { if ( color === undefined ) color = 0xffff00; var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); var positions = new Float32Array( 8 * 3 ); var geometry = new BufferGeometry(); geometry.setIndex( new BufferAttribute( indices, 1 ) ); geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); if ( object !== undefined ) { this.update( object ); } } BoxHelper.prototype = Object.create( LineSegments.prototype ); BoxHelper.prototype.constructor = BoxHelper; BoxHelper.prototype.update = ( function () { var box = new Box3(); return function update( object ) { if ( object && object.isBox3 ) { box.copy( object ); } else { box.setFromObject( object ); } if ( box.isEmpty() ) return; var min = box.min; var max = box.max; /* 5____4 1/___0/| | 6__|_7 2/___3/ 0: max.x, max.y, max.z 1: min.x, max.y, max.z 2: min.x, min.y, max.z 3: max.x, min.y, max.z 4: max.x, max.y, min.z 5: min.x, max.y, min.z 6: min.x, min.y, min.z 7: max.x, min.y, min.z */ var position = this.geometry.attributes.position; var array = position.array; array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; position.needsUpdate = true; this.geometry.computeBoundingSphere(); }; } )(); /** * @author WestLangley / http://github.com/WestLangley * @author zz85 / http://github.com/zz85 * @author bhouston / http://clara.io * * Creates an arrow for visualizing directions * * Parameters: * dir - Vector3 * origin - Vector3 * length - Number * color - color in hex value * headLength - Number * headWidth - Number */ var lineGeometry = new BufferGeometry(); lineGeometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); var coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); coneGeometry.translate( 0, - 0.5, 0 ); function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { // dir is assumed to be normalized Object3D.call( this ); if ( color === undefined ) color = 0xffff00; if ( length === undefined ) length = 1; if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; this.position.copy( origin ); this.line = new Line( lineGeometry, new LineBasicMaterial( { color: color } ) ); this.line.matrixAutoUpdate = false; this.add( this.line ); this.cone = new Mesh( coneGeometry, new MeshBasicMaterial( { color: color } ) ); this.cone.matrixAutoUpdate = false; this.add( this.cone ); this.setDirection( dir ); this.setLength( length, headLength, headWidth ); } ArrowHelper.prototype = Object.create( Object3D.prototype ); ArrowHelper.prototype.constructor = ArrowHelper; ArrowHelper.prototype.setDirection = ( function () { var axis = new Vector3(); var radians; return function setDirection( dir ) { // dir is assumed to be normalized if ( dir.y > 0.99999 ) { this.quaternion.set( 0, 0, 0, 1 ); } else if ( dir.y < - 0.99999 ) { this.quaternion.set( 1, 0, 0, 0 ); } else { axis.set( dir.z, 0, - dir.x ).normalize(); radians = Math.acos( dir.y ); this.quaternion.setFromAxisAngle( axis, radians ); } }; }() ); ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { if ( headLength === undefined ) headLength = 0.2 * length; if ( headWidth === undefined ) headWidth = 0.2 * headLength; this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 ); this.line.updateMatrix(); this.cone.scale.set( headWidth, headLength, headWidth ); this.cone.position.y = length; this.cone.updateMatrix(); }; ArrowHelper.prototype.setColor = function ( color ) { this.line.material.color.copy( color ); this.cone.material.color.copy( color ); }; /** * @author sroucheray / http://sroucheray.org/ * @author mrdoob / http://mrdoob.com/ */ function AxisHelper( size ) { size = size || 1; var vertices = [ 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size, 0, 0, 0, 0, 0, 0, size ]; var colors = [ 1, 0, 0, 1, 0.6, 0, 0, 1, 0, 0.6, 1, 0, 0, 0, 1, 0, 0.6, 1 ]; var geometry = new BufferGeometry(); geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); var material = new LineBasicMaterial( { vertexColors: VertexColors } ); LineSegments.call( this, geometry, material ); } AxisHelper.prototype = Object.create( LineSegments.prototype ); AxisHelper.prototype.constructor = AxisHelper; /** * @author zz85 https://github.com/zz85 * * Centripetal CatmullRom Curve - which is useful for avoiding * cusps and self-intersections in non-uniform catmull rom curves. * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf * * curve.type accepts centripetal(default), chordal and catmullrom * curve.tension is used for catmullrom which defaults to 0.5 */ var CatmullRomCurve3 = ( function() { var tmp = new Vector3(), px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); /* Based on an optimized c++ solution in - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - http://ideone.com/NoEbVM This CubicPoly class could be used for reusing some variables and calculations, but for three.js curve use, it could be possible inlined and flatten into a single function call which can be placed in CurveUtils. */ function CubicPoly() {} /* * Compute coefficients for a cubic polynomial * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 * such that * p(0) = x0, p(1) = x1 * and * p'(0) = t0, p'(1) = t1. */ CubicPoly.prototype.init = function( x0, x1, t0, t1 ) { this.c0 = x0; this.c1 = t0; this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; this.c3 = 2 * x0 - 2 * x1 + t0 + t1; }; CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) { // compute tangents when parameterized in [t1,t2] var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; // initCubicPoly this.init( x1, x2, t1, t2 ); }; // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) { this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); }; CubicPoly.prototype.calc = function( t ) { var t2 = t * t; var t3 = t2 * t; return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3; }; // Subclass Three.js curve return Curve.create( function ( p /* array of Vector3 */ ) { this.points = p || []; this.closed = false; }, function ( t ) { var points = this.points, point, intPoint, weight, l; l = points.length; if ( l < 2 ) console.log( 'duh, you need at least 2 points' ); point = ( l - ( this.closed ? 0 : 1 ) ) * t; intPoint = Math.floor( point ); weight = point - intPoint; if ( this.closed ) { intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; } else if ( weight === 0 && intPoint === l - 1 ) { intPoint = l - 2; weight = 1; } var p0, p1, p2, p3; // 4 points if ( this.closed || intPoint > 0 ) { p0 = points[ ( intPoint - 1 ) % l ]; } else { // extrapolate first point tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); p0 = tmp; } p1 = points[ intPoint % l ]; p2 = points[ ( intPoint + 1 ) % l ]; if ( this.closed || intPoint + 2 < l ) { p3 = points[ ( intPoint + 2 ) % l ]; } else { // extrapolate last point tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); p3 = tmp; } if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) { // init Centripetal / Chordal Catmull-Rom var pow = this.type === 'chordal' ? 0.5 : 0.25; var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); // safety check for repeated points if ( dt1 < 1e-4 ) dt1 = 1.0; if ( dt0 < 1e-4 ) dt0 = dt1; if ( dt2 < 1e-4 ) dt2 = dt1; px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); } else if ( this.type === 'catmullrom' ) { var tension = this.tension !== undefined ? this.tension : 0.5; px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension ); py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension ); pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension ); } var v = new Vector3( px.calc( weight ), py.calc( weight ), pz.calc( weight ) ); return v; } ); } )(); /************************************************************** * Spline 3D curve **************************************************************/ var SplineCurve3 = Curve.create( function ( points /* array of Vector3 */ ) { console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' ); this.points = ( points === undefined ) ? [] : points; }, function ( t ) { var points = this.points; var point = ( points.length - 1 ) * t; var intPoint = Math.floor( point ); var weight = point - intPoint; var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]; var point1 = points[ intPoint ]; var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; var interpolate = CurveUtils.interpolate; return new Vector3( interpolate( point0.x, point1.x, point2.x, point3.x, weight ), interpolate( point0.y, point1.y, point2.y, point3.y, weight ), interpolate( point0.z, point1.z, point2.z, point3.z, weight ) ); } ); /************************************************************** * Cubic Bezier 3D curve **************************************************************/ var CubicBezierCurve3 = Curve.create( function ( v0, v1, v2, v3 ) { this.v0 = v0; this.v1 = v1; this.v2 = v2; this.v3 = v3; }, function ( t ) { var b3 = ShapeUtils.b3; return new Vector3( b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ), b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ) ); } ); /************************************************************** * Quadratic Bezier 3D curve **************************************************************/ var QuadraticBezierCurve3 = Curve.create( function ( v0, v1, v2 ) { this.v0 = v0; this.v1 = v1; this.v2 = v2; }, function ( t ) { var b2 = ShapeUtils.b2; return new Vector3( b2( t, this.v0.x, this.v1.x, this.v2.x ), b2( t, this.v0.y, this.v1.y, this.v2.y ), b2( t, this.v0.z, this.v1.z, this.v2.z ) ); } ); /************************************************************** * Line3D **************************************************************/ var LineCurve3 = Curve.create( function ( v1, v2 ) { this.v1 = v1; this.v2 = v2; }, function ( t ) { if ( t === 1 ) { return this.v2.clone(); } var vector = new Vector3(); vector.subVectors( this.v2, this.v1 ); // diff vector.multiplyScalar( t ); vector.add( this.v1 ); return vector; } ); /************************************************************** * Arc curve **************************************************************/ function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); } ArcCurve.prototype = Object.create( EllipseCurve.prototype ); ArcCurve.prototype.constructor = ArcCurve; /** * @author alteredq / http://alteredqualia.com/ */ var SceneUtils = { createMultiMaterialObject: function ( geometry, materials ) { var group = new Group(); for ( var i = 0, l = materials.length; i < l; i ++ ) { group.add( new Mesh( geometry, materials[ i ] ) ); } return group; }, detach: function ( child, parent, scene ) { child.applyMatrix( parent.matrixWorld ); parent.remove( child ); scene.add( child ); }, attach: function ( child, scene, parent ) { var matrixWorldInverse = new Matrix4(); matrixWorldInverse.getInverse( parent.matrixWorld ); child.applyMatrix( matrixWorldInverse ); scene.remove( child ); parent.add( child ); } }; /** * @author mrdoob / http://mrdoob.com/ */ function Face4( a, b, c, d, normal, color, materialIndex ) { console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ); return new Face3( a, b, c, normal, color, materialIndex ); } var LineStrip = 0; var LinePieces = 1; function MeshFaceMaterial( materials ) { console.warn( 'THREE.MeshFaceMaterial has been renamed to THREE.MultiMaterial.' ); return new MultiMaterial( materials ); } function PointCloud( geometry, material ) { console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); return new Points( geometry, material ); } function Particle( material ) { console.warn( 'THREE.Particle has been renamed to THREE.Sprite.' ); return new Sprite( material ); } function ParticleSystem( geometry, material ) { console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); return new Points( geometry, material ); } function PointCloudMaterial( parameters ) { console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); return new PointsMaterial( parameters ); } function ParticleBasicMaterial( parameters ) { console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); return new PointsMaterial( parameters ); } function ParticleSystemMaterial( parameters ) { console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); return new PointsMaterial( parameters ); } function Vertex( x, y, z ) { console.warn( 'THREE.Vertex has been removed. Use THREE.Vector3 instead.' ); return new Vector3( x, y, z ); } // function DynamicBufferAttribute( array, itemSize ) { console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); return new BufferAttribute( array, itemSize ).setDynamic( true ); } function Int8Attribute( array, itemSize ) { console.warn( 'THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead.' ); return new Int8BufferAttribute( array, itemSize ); } function Uint8Attribute( array, itemSize ) { console.warn( 'THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead.' ); return new Uint8BufferAttribute( array, itemSize ); } function Uint8ClampedAttribute( array, itemSize ) { console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead.' ); return new Uint8ClampedBufferAttribute( array, itemSize ); } function Int16Attribute( array, itemSize ) { console.warn( 'THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead.' ); return new Int16BufferAttribute( array, itemSize ); } function Uint16Attribute( array, itemSize ) { console.warn( 'THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead.' ); return new Uint16BufferAttribute( array, itemSize ); } function Int32Attribute( array, itemSize ) { console.warn( 'THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead.' ); return new Int32BufferAttribute( array, itemSize ); } function Uint32Attribute( array, itemSize ) { console.warn( 'THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead.' ); return new Uint32BufferAttribute( array, itemSize ); } function Float32Attribute( array, itemSize ) { console.warn( 'THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead.' ); return new Float32BufferAttribute( array, itemSize ); } function Float64Attribute( array, itemSize ) { console.warn( 'THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead.' ); return new Float64BufferAttribute( array, itemSize ); } // function ClosedSplineCurve3( points ) { console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' ); CatmullRomCurve3.call( this, points ); this.type = 'catmullrom'; this.closed = true; } ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); // function BoundingBoxHelper( object, color ) { console.warn( 'THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead.' ); return new BoxHelper( object, color ); } function EdgesHelper( object, hex ) { console.warn( 'THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead.' ); return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); } GridHelper.prototype.setColors = function () { console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); }; function WireframeHelper( object, hex ) { console.warn( 'THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead.' ); return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); } // function XHRLoader( manager ) { console.warn( 'THREE.XHRLoader has been renamed to THREE.FileLoader.' ); return new FileLoader( manager ); } // Object.assign( Box2.prototype, { center: function ( optionalTarget ) { console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' ); return this.getCenter( optionalTarget ); }, empty: function () { console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); return this.isEmpty(); }, isIntersectionBox: function ( box ) { console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); return this.intersectsBox( box ); }, size: function ( optionalTarget ) { console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' ); return this.getSize( optionalTarget ); } } ); Object.assign( Box3.prototype, { center: function ( optionalTarget ) { console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); return this.getCenter( optionalTarget ); }, empty: function () { console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); return this.isEmpty(); }, isIntersectionBox: function ( box ) { console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); return this.intersectsBox( box ); }, isIntersectionSphere: function ( sphere ) { console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); return this.intersectsSphere( sphere ); }, size: function ( optionalTarget ) { console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); return this.getSize( optionalTarget ); } } ); Line3.prototype.center = function ( optionalTarget ) { console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' ); return this.getCenter( optionalTarget ); }; _Math.random16 = function () { console.warn( 'THREE.Math.random16() has been deprecated. Use Math.random() instead.' ); return Math.random(); }; Object.assign( Matrix3.prototype, { flattenToArrayOffset: function ( array, offset ) { console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); return this.toArray( array, offset ); }, multiplyVector3: function ( vector ) { console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); return vector.applyMatrix3( this ); }, multiplyVector3Array: function ( a ) { console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); return this.applyToVector3Array( a ); }, applyToBuffer: function( buffer, offset, length ) { console.warn( 'THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' ); return this.applyToBufferAttribute( buffer ); } } ); Object.assign( Matrix4.prototype, { extractPosition: function ( m ) { console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); return this.copyPosition( m ); }, flattenToArrayOffset: function ( array, offset ) { console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); return this.toArray( array, offset ); }, getPosition: function () { var v1; return function getPosition() { if ( v1 === undefined ) v1 = new Vector3(); console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); return v1.setFromMatrixColumn( this, 3 ); }; }(), setRotationFromQuaternion: function ( q ) { console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); return this.makeRotationFromQuaternion( q ); }, multiplyVector3: function ( vector ) { console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); return vector.applyProjection( this ); }, multiplyVector4: function ( vector ) { console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); return vector.applyMatrix4( this ); }, multiplyVector3Array: function ( a ) { console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); return this.applyToVector3Array( a ); }, rotateAxis: function ( v ) { console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); v.transformDirection( this ); }, crossVector: function ( vector ) { console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); return vector.applyMatrix4( this ); }, translate: function () { console.error( 'THREE.Matrix4: .translate() has been removed.' ); }, rotateX: function () { console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); }, rotateY: function () { console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); }, rotateZ: function () { console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); }, rotateByAxis: function () { console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); }, applyToBuffer: function( buffer, offset, length ) { console.warn( 'THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' ); return this.applyToBufferAttribute( buffer ); } } ); Plane.prototype.isIntersectionLine = function ( line ) { console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); return this.intersectsLine( line ); }; Quaternion.prototype.multiplyVector3 = function ( vector ) { console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); return vector.applyQuaternion( this ); }; Object.assign( Ray.prototype, { isIntersectionBox: function ( box ) { console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); return this.intersectsBox( box ); }, isIntersectionPlane: function ( plane ) { console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); return this.intersectsPlane( plane ); }, isIntersectionSphere: function ( sphere ) { console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); return this.intersectsSphere( sphere ); } } ); Object.assign( Shape.prototype, { extrude: function ( options ) { console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); return new ExtrudeGeometry( this, options ); }, makeGeometry: function ( options ) { console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); return new ShapeGeometry( this, options ); } } ); Object.assign( Vector3.prototype, { setEulerFromRotationMatrix: function () { console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); }, setEulerFromQuaternion: function () { console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); }, getPositionFromMatrix: function ( m ) { console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); return this.setFromMatrixPosition( m ); }, getScaleFromMatrix: function ( m ) { console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); return this.setFromMatrixScale( m ); }, getColumnFromMatrix: function ( index, matrix ) { console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); return this.setFromMatrixColumn( matrix, index ); } } ); // Geometry.prototype.computeTangents = function () { console.warn( 'THREE.Geometry: .computeTangents() has been removed.' ); }; Object.assign( Object3D.prototype, { getChildByName: function ( name ) { console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); return this.getObjectByName( name ); }, renderDepth: function () { console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); }, translate: function ( distance, axis ) { console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); return this.translateOnAxis( axis, distance ); } } ); Object.defineProperties( Object3D.prototype, { eulerOrder: { get: function () { console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); return this.rotation.order; }, set: function ( value ) { console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); this.rotation.order = value; } }, useQuaternion: { get: function () { console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); }, set: function () { console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); } } } ); Object.defineProperties( LOD.prototype, { objects: { get: function () { console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); return this.levels; } } } ); // PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup." ); if ( filmGauge !== undefined ) this.filmGauge = filmGauge; this.setFocalLength( focalLength ); }; // Object.defineProperties( Light.prototype, { onlyShadow: { set: function () { console.warn( 'THREE.Light: .onlyShadow has been removed.' ); } }, shadowCameraFov: { set: function ( value ) { console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); this.shadow.camera.fov = value; } }, shadowCameraLeft: { set: function ( value ) { console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); this.shadow.camera.left = value; } }, shadowCameraRight: { set: function ( value ) { console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); this.shadow.camera.right = value; } }, shadowCameraTop: { set: function ( value ) { console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); this.shadow.camera.top = value; } }, shadowCameraBottom: { set: function ( value ) { console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); this.shadow.camera.bottom = value; } }, shadowCameraNear: { set: function ( value ) { console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); this.shadow.camera.near = value; } }, shadowCameraFar: { set: function ( value ) { console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); this.shadow.camera.far = value; } }, shadowCameraVisible: { set: function () { console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); } }, shadowBias: { set: function ( value ) { console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); this.shadow.bias = value; } }, shadowDarkness: { set: function () { console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); } }, shadowMapWidth: { set: function ( value ) { console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); this.shadow.mapSize.width = value; } }, shadowMapHeight: { set: function ( value ) { console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); this.shadow.mapSize.height = value; } } } ); // Object.defineProperties( BufferAttribute.prototype, { length: { get: function () { console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); return this.array.length; } } } ); Object.assign( BufferGeometry.prototype, { addIndex: function ( index ) { console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); this.setIndex( index ); }, addDrawCall: function ( start, count, indexOffset ) { if ( indexOffset !== undefined ) { console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); } console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); this.addGroup( start, count ); }, clearDrawCalls: function () { console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); this.clearGroups(); }, computeTangents: function () { console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); }, computeOffsets: function () { console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); } } ); Object.defineProperties( BufferGeometry.prototype, { drawcalls: { get: function () { console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); return this.groups; } }, offsets: { get: function () { console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); return this.groups; } } } ); // Object.defineProperties( Uniform.prototype, { dynamic: { set: function () { console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' ); } }, onUpdate: { value: function () { console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' ); return this; } } } ); // Object.defineProperties( Material.prototype, { wrapAround: { get: function () { console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); }, set: function () { console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); } }, wrapRGB: { get: function () { console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' ); return new Color(); } } } ); Object.defineProperties( MeshPhongMaterial.prototype, { metal: { get: function () { console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); return false; }, set: function () { console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); } } } ); Object.defineProperties( ShaderMaterial.prototype, { derivatives: { get: function () { console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); return this.extensions.derivatives; }, set: function ( value ) { console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); this.extensions.derivatives = value; } } } ); // EventDispatcher.prototype = Object.assign( Object.create( { // Note: Extra base ensures these properties are not 'assign'ed. constructor: EventDispatcher, apply: function ( target ) { console.warn( "THREE.EventDispatcher: .apply is deprecated, " + "just inherit or Object.assign the prototype to mix-in." ); Object.assign( target, this ); } } ), EventDispatcher.prototype ); // Object.assign( WebGLRenderer.prototype, { supportsFloatTextures: function () { console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); return this.extensions.get( 'OES_texture_float' ); }, supportsHalfFloatTextures: function () { console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); return this.extensions.get( 'OES_texture_half_float' ); }, supportsStandardDerivatives: function () { console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); return this.extensions.get( 'OES_standard_derivatives' ); }, supportsCompressedTextureS3TC: function () { console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); }, supportsCompressedTexturePVRTC: function () { console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); }, supportsBlendMinMax: function () { console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); return this.extensions.get( 'EXT_blend_minmax' ); }, supportsVertexTextures: function () { console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); return this.capabilities.vertexTextures; }, supportsInstancedArrays: function () { console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); return this.extensions.get( 'ANGLE_instanced_arrays' ); }, enableScissorTest: function ( boolean ) { console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); this.setScissorTest( boolean ); }, initMaterial: function () { console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); }, addPrePlugin: function () { console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); }, addPostPlugin: function () { console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); }, updateShadowMap: function () { console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); } } ); Object.defineProperties( WebGLRenderer.prototype, { shadowMapEnabled: { get: function () { return this.shadowMap.enabled; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); this.shadowMap.enabled = value; } }, shadowMapType: { get: function () { return this.shadowMap.type; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); this.shadowMap.type = value; } }, shadowMapCullFace: { get: function () { return this.shadowMap.cullFace; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' ); this.shadowMap.cullFace = value; } } } ); Object.defineProperties( WebGLShadowMap.prototype, { cullFace: { get: function () { return this.renderReverseSided ? CullFaceFront : CullFaceBack; }, set: function ( cullFace ) { var value = ( cullFace !== CullFaceBack ); console.warn( "WebGLRenderer: .shadowMap.cullFace is deprecated. Set .shadowMap.renderReverseSided to " + value + "." ); this.renderReverseSided = value; } } } ); // Object.defineProperties( WebGLRenderTarget.prototype, { wrapS: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); return this.texture.wrapS; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); this.texture.wrapS = value; } }, wrapT: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); return this.texture.wrapT; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); this.texture.wrapT = value; } }, magFilter: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); return this.texture.magFilter; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); this.texture.magFilter = value; } }, minFilter: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); return this.texture.minFilter; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); this.texture.minFilter = value; } }, anisotropy: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); return this.texture.anisotropy; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); this.texture.anisotropy = value; } }, offset: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); return this.texture.offset; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); this.texture.offset = value; } }, repeat: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); return this.texture.repeat; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); this.texture.repeat = value; } }, format: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); return this.texture.format; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); this.texture.format = value; } }, type: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); return this.texture.type; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); this.texture.type = value; } }, generateMipmaps: { get: function () { console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); return this.texture.generateMipmaps; }, set: function ( value ) { console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); this.texture.generateMipmaps = value; } } } ); // Audio.prototype.load = function ( file ) { console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); var scope = this; var audioLoader = new AudioLoader(); audioLoader.load( file, function ( buffer ) { scope.setBuffer( buffer ); } ); return this; }; AudioAnalyser.prototype.getData = function () { console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' ); return this.getFrequencyData(); }; // var GeometryUtils = { merge: function ( geometry1, geometry2, materialIndexOffset ) { console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); var matrix; if ( geometry2.isMesh ) { geometry2.matrixAutoUpdate && geometry2.updateMatrix(); matrix = geometry2.matrix; geometry2 = geometry2.geometry; } geometry1.merge( geometry2, matrix, materialIndexOffset ); }, center: function ( geometry ) { console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); return geometry.center(); } }; var ImageUtils = { crossOrigin: undefined, loadTexture: function ( url, mapping, onLoad, onError ) { console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); var loader = new TextureLoader(); loader.setCrossOrigin( this.crossOrigin ); var texture = loader.load( url, onLoad, undefined, onError ); if ( mapping ) texture.mapping = mapping; return texture; }, loadTextureCube: function ( urls, mapping, onLoad, onError ) { console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); var loader = new CubeTextureLoader(); loader.setCrossOrigin( this.crossOrigin ); var texture = loader.load( urls, onLoad, undefined, onError ); if ( mapping ) texture.mapping = mapping; return texture; }, loadCompressedTexture: function () { console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); }, loadCompressedTextureCube: function () { console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); } }; // function Projector() { console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' ); this.projectVector = function ( vector, camera ) { console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); vector.project( camera ); }; this.unprojectVector = function ( vector, camera ) { console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); vector.unproject( camera ); }; this.pickingRay = function () { console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); }; } // function CanvasRenderer() { console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' ); this.domElement = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); this.clear = function () {}; this.render = function () {}; this.setClearColor = function () {}; this.setSize = function () {}; } exports.WebGLRenderTargetCube = WebGLRenderTargetCube; exports.WebGLRenderTarget = WebGLRenderTarget; exports.WebGLRenderer = WebGLRenderer; exports.ShaderLib = ShaderLib; exports.UniformsLib = UniformsLib; exports.UniformsUtils = UniformsUtils; exports.ShaderChunk = ShaderChunk; exports.FogExp2 = FogExp2; exports.Fog = Fog; exports.Scene = Scene; exports.LensFlare = LensFlare; exports.Sprite = Sprite; exports.LOD = LOD; exports.SkinnedMesh = SkinnedMesh; exports.Skeleton = Skeleton; exports.Bone = Bone; exports.Mesh = Mesh; exports.LineSegments = LineSegments; exports.Line = Line; exports.Points = Points; exports.Group = Group; exports.VideoTexture = VideoTexture; exports.DataTexture = DataTexture; exports.CompressedTexture = CompressedTexture; exports.CubeTexture = CubeTexture; exports.CanvasTexture = CanvasTexture; exports.DepthTexture = DepthTexture; exports.Texture = Texture; exports.CompressedTextureLoader = CompressedTextureLoader; exports.BinaryTextureLoader = BinaryTextureLoader; exports.DataTextureLoader = DataTextureLoader; exports.CubeTextureLoader = CubeTextureLoader; exports.TextureLoader = TextureLoader; exports.ObjectLoader = ObjectLoader; exports.MaterialLoader = MaterialLoader; exports.BufferGeometryLoader = BufferGeometryLoader; exports.DefaultLoadingManager = DefaultLoadingManager; exports.LoadingManager = LoadingManager; exports.JSONLoader = JSONLoader; exports.ImageLoader = ImageLoader; exports.FontLoader = FontLoader; exports.FileLoader = FileLoader; exports.Loader = Loader; exports.Cache = Cache; exports.AudioLoader = AudioLoader; exports.SpotLightShadow = SpotLightShadow; exports.SpotLight = SpotLight; exports.PointLight = PointLight; exports.RectAreaLight = RectAreaLight; exports.HemisphereLight = HemisphereLight; exports.DirectionalLightShadow = DirectionalLightShadow; exports.DirectionalLight = DirectionalLight; exports.AmbientLight = AmbientLight; exports.LightShadow = LightShadow; exports.Light = Light; exports.StereoCamera = StereoCamera; exports.PerspectiveCamera = PerspectiveCamera; exports.OrthographicCamera = OrthographicCamera; exports.CubeCamera = CubeCamera; exports.Camera = Camera; exports.AudioListener = AudioListener; exports.PositionalAudio = PositionalAudio; exports.AudioContext = AudioContext; exports.AudioAnalyser = AudioAnalyser; exports.Audio = Audio; exports.VectorKeyframeTrack = VectorKeyframeTrack; exports.StringKeyframeTrack = StringKeyframeTrack; exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; exports.NumberKeyframeTrack = NumberKeyframeTrack; exports.ColorKeyframeTrack = ColorKeyframeTrack; exports.BooleanKeyframeTrack = BooleanKeyframeTrack; exports.PropertyMixer = PropertyMixer; exports.PropertyBinding = PropertyBinding; exports.KeyframeTrack = KeyframeTrack; exports.AnimationUtils = AnimationUtils; exports.AnimationObjectGroup = AnimationObjectGroup; exports.AnimationMixer = AnimationMixer; exports.AnimationClip = AnimationClip; exports.Uniform = Uniform; exports.InstancedBufferGeometry = InstancedBufferGeometry; exports.BufferGeometry = BufferGeometry; exports.GeometryIdCount = GeometryIdCount; exports.Geometry = Geometry; exports.InterleavedBufferAttribute = InterleavedBufferAttribute; exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; exports.InterleavedBuffer = InterleavedBuffer; exports.InstancedBufferAttribute = InstancedBufferAttribute; exports.Face3 = Face3; exports.Object3D = Object3D; exports.Raycaster = Raycaster; exports.Layers = Layers; exports.EventDispatcher = EventDispatcher; exports.Clock = Clock; exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; exports.LinearInterpolant = LinearInterpolant; exports.DiscreteInterpolant = DiscreteInterpolant; exports.CubicInterpolant = CubicInterpolant; exports.Interpolant = Interpolant; exports.Triangle = Triangle; exports.Spline = Spline; exports.Math = _Math; exports.Spherical = Spherical; exports.Cylindrical = Cylindrical; exports.Plane = Plane; exports.Frustum = Frustum; exports.Sphere = Sphere; exports.Ray = Ray; exports.Matrix4 = Matrix4; exports.Matrix3 = Matrix3; exports.Box3 = Box3; exports.Box2 = Box2; exports.Line3 = Line3; exports.Euler = Euler; exports.Vector4 = Vector4; exports.Vector3 = Vector3; exports.Vector2 = Vector2; exports.Quaternion = Quaternion; exports.Color = Color; exports.MorphBlendMesh = MorphBlendMesh; exports.ImmediateRenderObject = ImmediateRenderObject; exports.VertexNormalsHelper = VertexNormalsHelper; exports.SpotLightHelper = SpotLightHelper; exports.SkeletonHelper = SkeletonHelper; exports.PointLightHelper = PointLightHelper; exports.RectAreaLightHelper = RectAreaLightHelper; exports.HemisphereLightHelper = HemisphereLightHelper; exports.GridHelper = GridHelper; exports.PolarGridHelper = PolarGridHelper; exports.FaceNormalsHelper = FaceNormalsHelper; exports.DirectionalLightHelper = DirectionalLightHelper; exports.CameraHelper = CameraHelper; exports.BoxHelper = BoxHelper; exports.ArrowHelper = ArrowHelper; exports.AxisHelper = AxisHelper; exports.CatmullRomCurve3 = CatmullRomCurve3; exports.SplineCurve3 = SplineCurve3; exports.CubicBezierCurve3 = CubicBezierCurve3; exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; exports.LineCurve3 = LineCurve3; exports.ArcCurve = ArcCurve; exports.EllipseCurve = EllipseCurve; exports.SplineCurve = SplineCurve; exports.CubicBezierCurve = CubicBezierCurve; exports.QuadraticBezierCurve = QuadraticBezierCurve; exports.LineCurve = LineCurve; exports.Shape = Shape; exports.ShapePath = ShapePath; exports.Path = Path; exports.Font = Font; exports.CurvePath = CurvePath; exports.Curve = Curve; exports.ShapeUtils = ShapeUtils; exports.SceneUtils = SceneUtils; exports.CurveUtils = CurveUtils; exports.WireframeGeometry = WireframeGeometry; exports.ParametricGeometry = ParametricGeometry; exports.ParametricBufferGeometry = ParametricBufferGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry; exports.OctahedronGeometry = OctahedronGeometry; exports.OctahedronBufferGeometry = OctahedronBufferGeometry; exports.IcosahedronGeometry = IcosahedronGeometry; exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry; exports.DodecahedronGeometry = DodecahedronGeometry; exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry; exports.PolyhedronGeometry = PolyhedronGeometry; exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry; exports.TubeGeometry = TubeGeometry; exports.TubeBufferGeometry = TubeBufferGeometry; exports.TorusKnotGeometry = TorusKnotGeometry; exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry; exports.TorusGeometry = TorusGeometry; exports.TorusBufferGeometry = TorusBufferGeometry; exports.TextGeometry = TextGeometry; exports.SphereBufferGeometry = SphereBufferGeometry; exports.SphereGeometry = SphereGeometry; exports.RingGeometry = RingGeometry; exports.RingBufferGeometry = RingBufferGeometry; exports.PlaneBufferGeometry = PlaneBufferGeometry; exports.PlaneGeometry = PlaneGeometry; exports.LatheGeometry = LatheGeometry; exports.LatheBufferGeometry = LatheBufferGeometry; exports.ShapeGeometry = ShapeGeometry; exports.ShapeBufferGeometry = ShapeBufferGeometry; exports.ExtrudeGeometry = ExtrudeGeometry; exports.EdgesGeometry = EdgesGeometry; exports.ConeGeometry = ConeGeometry; exports.ConeBufferGeometry = ConeBufferGeometry; exports.CylinderGeometry = CylinderGeometry; exports.CylinderBufferGeometry = CylinderBufferGeometry; exports.CircleBufferGeometry = CircleBufferGeometry; exports.CircleGeometry = CircleGeometry; exports.BoxBufferGeometry = BoxBufferGeometry; exports.BoxGeometry = BoxGeometry; exports.ShadowMaterial = ShadowMaterial; exports.SpriteMaterial = SpriteMaterial; exports.RawShaderMaterial = RawShaderMaterial; exports.ShaderMaterial = ShaderMaterial; exports.PointsMaterial = PointsMaterial; exports.MultiMaterial = MultiMaterial; exports.MeshPhysicalMaterial = MeshPhysicalMaterial; exports.MeshStandardMaterial = MeshStandardMaterial; exports.MeshPhongMaterial = MeshPhongMaterial; exports.MeshToonMaterial = MeshToonMaterial; exports.MeshNormalMaterial = MeshNormalMaterial; exports.MeshLambertMaterial = MeshLambertMaterial; exports.MeshDepthMaterial = MeshDepthMaterial; exports.MeshBasicMaterial = MeshBasicMaterial; exports.LineDashedMaterial = LineDashedMaterial; exports.LineBasicMaterial = LineBasicMaterial; exports.Material = Material; exports.Float64BufferAttribute = Float64BufferAttribute; exports.Float32BufferAttribute = Float32BufferAttribute; exports.Uint32BufferAttribute = Uint32BufferAttribute; exports.Int32BufferAttribute = Int32BufferAttribute; exports.Uint16BufferAttribute = Uint16BufferAttribute; exports.Int16BufferAttribute = Int16BufferAttribute; exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; exports.Uint8BufferAttribute = Uint8BufferAttribute; exports.Int8BufferAttribute = Int8BufferAttribute; exports.BufferAttribute = BufferAttribute; exports.REVISION = REVISION; exports.MOUSE = MOUSE; exports.CullFaceNone = CullFaceNone; exports.CullFaceBack = CullFaceBack; exports.CullFaceFront = CullFaceFront; exports.CullFaceFrontBack = CullFaceFrontBack; exports.FrontFaceDirectionCW = FrontFaceDirectionCW; exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW; exports.BasicShadowMap = BasicShadowMap; exports.PCFShadowMap = PCFShadowMap; exports.PCFSoftShadowMap = PCFSoftShadowMap; exports.FrontSide = FrontSide; exports.BackSide = BackSide; exports.DoubleSide = DoubleSide; exports.FlatShading = FlatShading; exports.SmoothShading = SmoothShading; exports.NoColors = NoColors; exports.FaceColors = FaceColors; exports.VertexColors = VertexColors; exports.NoBlending = NoBlending; exports.NormalBlending = NormalBlending; exports.AdditiveBlending = AdditiveBlending; exports.SubtractiveBlending = SubtractiveBlending; exports.MultiplyBlending = MultiplyBlending; exports.CustomBlending = CustomBlending; exports.BlendingMode = BlendingMode; exports.AddEquation = AddEquation; exports.SubtractEquation = SubtractEquation; exports.ReverseSubtractEquation = ReverseSubtractEquation; exports.MinEquation = MinEquation; exports.MaxEquation = MaxEquation; exports.ZeroFactor = ZeroFactor; exports.OneFactor = OneFactor; exports.SrcColorFactor = SrcColorFactor; exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; exports.SrcAlphaFactor = SrcAlphaFactor; exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; exports.DstAlphaFactor = DstAlphaFactor; exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; exports.DstColorFactor = DstColorFactor; exports.OneMinusDstColorFactor = OneMinusDstColorFactor; exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; exports.NeverDepth = NeverDepth; exports.AlwaysDepth = AlwaysDepth; exports.LessDepth = LessDepth; exports.LessEqualDepth = LessEqualDepth; exports.EqualDepth = EqualDepth; exports.GreaterEqualDepth = GreaterEqualDepth; exports.GreaterDepth = GreaterDepth; exports.NotEqualDepth = NotEqualDepth; exports.MultiplyOperation = MultiplyOperation; exports.MixOperation = MixOperation; exports.AddOperation = AddOperation; exports.NoToneMapping = NoToneMapping; exports.LinearToneMapping = LinearToneMapping; exports.ReinhardToneMapping = ReinhardToneMapping; exports.Uncharted2ToneMapping = Uncharted2ToneMapping; exports.CineonToneMapping = CineonToneMapping; exports.UVMapping = UVMapping; exports.CubeReflectionMapping = CubeReflectionMapping; exports.CubeRefractionMapping = CubeRefractionMapping; exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; exports.SphericalReflectionMapping = SphericalReflectionMapping; exports.CubeUVReflectionMapping = CubeUVReflectionMapping; exports.CubeUVRefractionMapping = CubeUVRefractionMapping; exports.TextureMapping = TextureMapping; exports.RepeatWrapping = RepeatWrapping; exports.ClampToEdgeWrapping = ClampToEdgeWrapping; exports.MirroredRepeatWrapping = MirroredRepeatWrapping; exports.TextureWrapping = TextureWrapping; exports.NearestFilter = NearestFilter; exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; exports.LinearFilter = LinearFilter; exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; exports.TextureFilter = TextureFilter; exports.UnsignedByteType = UnsignedByteType; exports.ByteType = ByteType; exports.ShortType = ShortType; exports.UnsignedShortType = UnsignedShortType; exports.IntType = IntType; exports.UnsignedIntType = UnsignedIntType; exports.FloatType = FloatType; exports.HalfFloatType = HalfFloatType; exports.UnsignedShort4444Type = UnsignedShort4444Type; exports.UnsignedShort5551Type = UnsignedShort5551Type; exports.UnsignedShort565Type = UnsignedShort565Type; exports.UnsignedInt248Type = UnsignedInt248Type; exports.AlphaFormat = AlphaFormat; exports.RGBFormat = RGBFormat; exports.RGBAFormat = RGBAFormat; exports.LuminanceFormat = LuminanceFormat; exports.LuminanceAlphaFormat = LuminanceAlphaFormat; exports.RGBEFormat = RGBEFormat; exports.DepthFormat = DepthFormat; exports.DepthStencilFormat = DepthStencilFormat; exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; exports.RGB_ETC1_Format = RGB_ETC1_Format; exports.LoopOnce = LoopOnce; exports.LoopRepeat = LoopRepeat; exports.LoopPingPong = LoopPingPong; exports.InterpolateDiscrete = InterpolateDiscrete; exports.InterpolateLinear = InterpolateLinear; exports.InterpolateSmooth = InterpolateSmooth; exports.ZeroCurvatureEnding = ZeroCurvatureEnding; exports.ZeroSlopeEnding = ZeroSlopeEnding; exports.WrapAroundEnding = WrapAroundEnding; exports.TrianglesDrawMode = TrianglesDrawMode; exports.TriangleStripDrawMode = TriangleStripDrawMode; exports.TriangleFanDrawMode = TriangleFanDrawMode; exports.LinearEncoding = LinearEncoding; exports.sRGBEncoding = sRGBEncoding; exports.GammaEncoding = GammaEncoding; exports.RGBEEncoding = RGBEEncoding; exports.LogLuvEncoding = LogLuvEncoding; exports.RGBM7Encoding = RGBM7Encoding; exports.RGBM16Encoding = RGBM16Encoding; exports.RGBDEncoding = RGBDEncoding; exports.BasicDepthPacking = BasicDepthPacking; exports.RGBADepthPacking = RGBADepthPacking; exports.CubeGeometry = BoxGeometry; exports.Face4 = Face4; exports.LineStrip = LineStrip; exports.LinePieces = LinePieces; exports.MeshFaceMaterial = MeshFaceMaterial; exports.PointCloud = PointCloud; exports.Particle = Particle; exports.ParticleSystem = ParticleSystem; exports.PointCloudMaterial = PointCloudMaterial; exports.ParticleBasicMaterial = ParticleBasicMaterial; exports.ParticleSystemMaterial = ParticleSystemMaterial; exports.Vertex = Vertex; exports.DynamicBufferAttribute = DynamicBufferAttribute; exports.Int8Attribute = Int8Attribute; exports.Uint8Attribute = Uint8Attribute; exports.Uint8ClampedAttribute = Uint8ClampedAttribute; exports.Int16Attribute = Int16Attribute; exports.Uint16Attribute = Uint16Attribute; exports.Int32Attribute = Int32Attribute; exports.Uint32Attribute = Uint32Attribute; exports.Float32Attribute = Float32Attribute; exports.Float64Attribute = Float64Attribute; exports.ClosedSplineCurve3 = ClosedSplineCurve3; exports.BoundingBoxHelper = BoundingBoxHelper; exports.EdgesHelper = EdgesHelper; exports.WireframeHelper = WireframeHelper; exports.XHRLoader = XHRLoader; exports.GeometryUtils = GeometryUtils; exports.ImageUtils = ImageUtils; exports.Projector = Projector; exports.CanvasRenderer = CanvasRenderer; Object.defineProperty(exports, '__esModule', { value: true }); }))); },{}],215:[function(require,module,exports){ /** * @author dmarcos / https://github.com/dmarcos * @author mrdoob / http://mrdoob.com */ THREE.VRControls = function ( object, onError ) { var scope = this; var vrDisplay, vrDisplays; var standingMatrix = new THREE.Matrix4(); var frameData = null; if ( 'VRFrameData' in window ) { frameData = new VRFrameData(); } function gotVRDisplays( displays ) { vrDisplays = displays; if ( displays.length > 0 ) { vrDisplay = displays[ 0 ]; } else { if ( onError ) onError( 'VR input not available.' ); } } if ( navigator.getVRDisplays ) { navigator.getVRDisplays().then( gotVRDisplays ).catch ( function () { console.warn( 'THREE.VRControls: Unable to get VR Displays' ); } ); } // the Rift SDK returns the position in meters // this scale factor allows the user to define how meters // are converted to scene units. this.scale = 1; // If true will use "standing space" coordinate system where y=0 is the // floor and x=0, z=0 is the center of the room. this.standing = false; // Distance from the users eyes to the floor in meters. Used when // standing=true but the VRDisplay doesn't provide stageParameters. this.userHeight = 1.6; this.getVRDisplay = function () { return vrDisplay; }; this.setVRDisplay = function ( value ) { vrDisplay = value; }; this.getVRDisplays = function () { console.warn( 'THREE.VRControls: getVRDisplays() is being deprecated.' ); return vrDisplays; }; this.getStandingMatrix = function () { return standingMatrix; }; this.update = function () { if ( vrDisplay ) { var pose; if ( vrDisplay.getFrameData ) { vrDisplay.getFrameData( frameData ); pose = frameData.pose; } else if ( vrDisplay.getPose ) { pose = vrDisplay.getPose(); } if ( pose.orientation !== null ) { object.quaternion.fromArray( pose.orientation ); } if ( pose.position !== null ) { object.position.fromArray( pose.position ); } else { object.position.set( 0, 0, 0 ); } if ( this.standing ) { if ( vrDisplay.stageParameters ) { object.updateMatrix(); standingMatrix.fromArray( vrDisplay.stageParameters.sittingToStandingTransform ); object.applyMatrix( standingMatrix ); } else { object.position.setY( object.position.y + this.userHeight ); } } object.position.multiplyScalar( scope.scale ); } }; this.resetPose = function () { if ( vrDisplay ) { vrDisplay.resetPose(); } }; this.resetSensor = function () { console.warn( 'THREE.VRControls: .resetSensor() is now .resetPose().' ); this.resetPose(); }; this.zeroSensor = function () { console.warn( 'THREE.VRControls: .zeroSensor() is now .resetPose().' ); this.resetPose(); }; this.dispose = function () { vrDisplay = null; }; }; },{}],216:[function(require,module,exports){ /** * @author dmarcos / https://github.com/dmarcos * @author mrdoob / http://mrdoob.com * * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html * * Firefox: http://mozvr.com/downloads/ * Chromium: https://webvr.info/get-chrome * */ THREE.VREffect = function( renderer, onError ) { var vrDisplay, vrDisplays; var eyeTranslationL = new THREE.Vector3(); var eyeTranslationR = new THREE.Vector3(); var renderRectL, renderRectR; var frameData = null; if ( 'VRFrameData' in window ) { frameData = new window.VRFrameData(); } function gotVRDisplays( displays ) { vrDisplays = displays; if ( displays.length > 0 ) { vrDisplay = displays[ 0 ]; } else { if ( onError ) onError( 'HMD not available' ); } } if ( navigator.getVRDisplays ) { navigator.getVRDisplays().then( gotVRDisplays ).catch( function() { console.warn( 'THREE.VREffect: Unable to get VR Displays' ); } ); } // this.isPresenting = false; this.scale = 1; var scope = this; var rendererSize = renderer.getSize(); var rendererUpdateStyle = false; var rendererPixelRatio = renderer.getPixelRatio(); this.getVRDisplay = function() { return vrDisplay; }; this.setVRDisplay = function( value ) { vrDisplay = value; }; this.getVRDisplays = function() { console.warn( 'THREE.VREffect: getVRDisplays() is being deprecated.' ); return vrDisplays; }; this.setSize = function( width, height, updateStyle ) { rendererSize = { width: width, height: height }; rendererUpdateStyle = updateStyle; if ( scope.isPresenting ) { var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); renderer.setPixelRatio( 1 ); renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false ); } else { renderer.setPixelRatio( rendererPixelRatio ); renderer.setSize( width, height, updateStyle ); } }; // VR presentation var canvas = renderer.domElement; var defaultLeftBounds = [ 0.0, 0.0, 0.5, 1.0 ]; var defaultRightBounds = [ 0.5, 0.0, 0.5, 1.0 ]; function onVRDisplayPresentChange() { var wasPresenting = scope.isPresenting; scope.isPresenting = vrDisplay !== undefined && vrDisplay.isPresenting; if ( scope.isPresenting ) { var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); var eyeWidth = eyeParamsL.renderWidth; var eyeHeight = eyeParamsL.renderHeight; if ( ! wasPresenting ) { rendererPixelRatio = renderer.getPixelRatio(); rendererSize = renderer.getSize(); renderer.setPixelRatio( 1 ); renderer.setSize( eyeWidth * 2, eyeHeight, false ); } } else if ( wasPresenting ) { renderer.setPixelRatio( rendererPixelRatio ); renderer.setSize( rendererSize.width, rendererSize.height, rendererUpdateStyle ); } } window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); this.setFullScreen = function( boolean ) { return new Promise( function( resolve, reject ) { if ( vrDisplay === undefined ) { reject( new Error( 'No VR hardware found.' ) ); return; } if ( scope.isPresenting === boolean ) { resolve(); return; } if ( boolean ) { resolve( vrDisplay.requestPresent( [ { source: canvas } ] ) ); } else { resolve( vrDisplay.exitPresent() ); } } ); }; this.requestPresent = function() { return this.setFullScreen( true ); }; this.exitPresent = function() { return this.setFullScreen( false ); }; this.requestAnimationFrame = function( f ) { if ( vrDisplay !== undefined ) { return vrDisplay.requestAnimationFrame( f ); } else { return window.requestAnimationFrame( f ); } }; this.cancelAnimationFrame = function( h ) { if ( vrDisplay !== undefined ) { vrDisplay.cancelAnimationFrame( h ); } else { window.cancelAnimationFrame( h ); } }; this.submitFrame = function() { if ( vrDisplay !== undefined && scope.isPresenting ) { vrDisplay.submitFrame(); } }; this.autoSubmitFrame = true; // render var cameraL = new THREE.PerspectiveCamera(); cameraL.layers.enable( 1 ); var cameraR = new THREE.PerspectiveCamera(); cameraR.layers.enable( 2 ); this.render = function( scene, camera, renderTarget, forceClear ) { if ( vrDisplay && scope.isPresenting ) { var autoUpdate = scene.autoUpdate; if ( autoUpdate ) { scene.updateMatrixWorld(); scene.autoUpdate = false; } var eyeParamsL = vrDisplay.getEyeParameters( 'left' ); var eyeParamsR = vrDisplay.getEyeParameters( 'right' ); eyeTranslationL.fromArray( eyeParamsL.offset ); eyeTranslationR.fromArray( eyeParamsR.offset ); if ( Array.isArray( scene ) ) { console.warn( 'THREE.VREffect.render() no longer supports arrays. Use object.layers instead.' ); scene = scene[ 0 ]; } // When rendering we don't care what the recommended size is, only what the actual size // of the backbuffer is. var size = renderer.getSize(); var layers = vrDisplay.getLayers(); var leftBounds; var rightBounds; if ( layers.length ) { var layer = layers[ 0 ]; leftBounds = layer.leftBounds !== null && layer.leftBounds.length === 4 ? layer.leftBounds : defaultLeftBounds; rightBounds = layer.rightBounds !== null && layer.rightBounds.length === 4 ? layer.rightBounds : defaultRightBounds; } else { leftBounds = defaultLeftBounds; rightBounds = defaultRightBounds; } renderRectL = { x: Math.round( size.width * leftBounds[ 0 ] ), y: Math.round( size.height * leftBounds[ 1 ] ), width: Math.round( size.width * leftBounds[ 2 ] ), height: Math.round( size.height * leftBounds[ 3 ] ) }; renderRectR = { x: Math.round( size.width * rightBounds[ 0 ] ), y: Math.round( size.height * rightBounds[ 1 ] ), width: Math.round( size.width * rightBounds[ 2 ] ), height: Math.round( size.height * rightBounds[ 3 ] ) }; if ( renderTarget ) { renderer.setRenderTarget( renderTarget ); renderTarget.scissorTest = true; } else { renderer.setRenderTarget( null ); renderer.setScissorTest( true ); } if ( renderer.autoClear || forceClear ) renderer.clear(); if ( camera.parent === null ) camera.updateMatrixWorld(); camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale ); camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale ); var scale = this.scale; cameraL.translateOnAxis( eyeTranslationL, scale ); cameraR.translateOnAxis( eyeTranslationR, scale ); if ( vrDisplay.getFrameData ) { vrDisplay.depthNear = camera.near; vrDisplay.depthFar = camera.far; vrDisplay.getFrameData( frameData ); cameraL.projectionMatrix.elements = frameData.leftProjectionMatrix; cameraR.projectionMatrix.elements = frameData.rightProjectionMatrix; } else { cameraL.projectionMatrix = fovToProjection( eyeParamsL.fieldOfView, true, camera.near, camera.far ); cameraR.projectionMatrix = fovToProjection( eyeParamsR.fieldOfView, true, camera.near, camera.far ); } // render left eye if ( renderTarget ) { renderTarget.viewport.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); renderTarget.scissor.set( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); } else { renderer.setViewport( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); renderer.setScissor( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height ); } renderer.render( scene, cameraL, renderTarget, forceClear ); // render right eye if ( renderTarget ) { renderTarget.viewport.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); renderTarget.scissor.set( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); } else { renderer.setViewport( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); renderer.setScissor( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height ); } renderer.render( scene, cameraR, renderTarget, forceClear ); if ( renderTarget ) { renderTarget.viewport.set( 0, 0, size.width, size.height ); renderTarget.scissor.set( 0, 0, size.width, size.height ); renderTarget.scissorTest = false; renderer.setRenderTarget( null ); } else { renderer.setViewport( 0, 0, size.width, size.height ); renderer.setScissorTest( false ); } if ( autoUpdate ) { scene.autoUpdate = true; } if ( scope.autoSubmitFrame ) { scope.submitFrame(); } return; } // Regular render mode if not HMD renderer.render( scene, camera, renderTarget, forceClear ); }; this.dispose = function() { window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); }; // function fovToNDCScaleOffset( fov ) { var pxscale = 2.0 / ( fov.leftTan + fov.rightTan ); var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5; var pyscale = 2.0 / ( fov.upTan + fov.downTan ); var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5; return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] }; } function fovPortToProjection( fov, rightHanded, zNear, zFar ) { rightHanded = rightHanded === undefined ? true : rightHanded; zNear = zNear === undefined ? 0.01 : zNear; zFar = zFar === undefined ? 10000.0 : zFar; var handednessScale = rightHanded ? - 1.0 : 1.0; // start with an identity matrix var mobj = new THREE.Matrix4(); var m = mobj.elements; // and with scale/offset info for normalized device coords var scaleAndOffset = fovToNDCScaleOffset( fov ); // X result, map clip edges to [-w,+w] m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ]; m[ 0 * 4 + 1 ] = 0.0; m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale; m[ 0 * 4 + 3 ] = 0.0; // Y result, map clip edges to [-w,+w] // Y offset is negated because this proj matrix transforms from world coords with Y=up, // but the NDC scaling has Y=down (thanks D3D?) m[ 1 * 4 + 0 ] = 0.0; m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ]; m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale; m[ 1 * 4 + 3 ] = 0.0; // Z result (up to the app) m[ 2 * 4 + 0 ] = 0.0; m[ 2 * 4 + 1 ] = 0.0; m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale; m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar ); // W result (= Z in) m[ 3 * 4 + 0 ] = 0.0; m[ 3 * 4 + 1 ] = 0.0; m[ 3 * 4 + 2 ] = handednessScale; m[ 3 * 4 + 3 ] = 0.0; mobj.transpose(); return mobj; } function fovToProjection( fov, rightHanded, zNear, zFar ) { var DEG2RAD = Math.PI / 180.0; var fovPort = { upTan: Math.tan( fov.upDegrees * DEG2RAD ), downTan: Math.tan( fov.downDegrees * DEG2RAD ), leftTan: Math.tan( fov.leftDegrees * DEG2RAD ), rightTan: Math.tan( fov.rightDegrees * DEG2RAD ) }; return fovPortToProjection( fovPort, rightHanded, zNear, zFar ); } }; },{}],217:[function(require,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*$/, ''); }; },{}],218:[function(require,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 } },{}],219:[function(require,module,exports){ /* jshint ignore:start */ (function(root) { /* jshint ignore:end */ var HASH_SPLIT = /^([^#]*)(.*)$/; var QUERY_SPLIT = /^([^\?]*)(.*)$/; var DOMAIN_SPLIT = /^(((?:[a-z]+:)?\/\/)?[^:\/]+(?::[0-9]+)?)?(\/?.*)$/i; var URLToolkit = { // build an absolute URL from a relative one using the provided baseURL // if relativeURL is an absolute URL it will be returned as is. buildAbsoluteURL: function(baseURL, relativeURL) { // remove any remaining space and CRLF relativeURL = relativeURL.trim(); if (/^[a-z]+:/i.test(relativeURL)) { // complete url, not relative return relativeURL; } var relativeURLQuery = null; var relativeURLHash = null; var relativeURLHashSplit = HASH_SPLIT.exec(relativeURL); if (relativeURLHashSplit) { relativeURLHash = relativeURLHashSplit[2]; relativeURL = relativeURLHashSplit[1]; } var relativeURLQuerySplit = QUERY_SPLIT.exec(relativeURL); if (relativeURLQuerySplit) { relativeURLQuery = relativeURLQuerySplit[2]; relativeURL = relativeURLQuerySplit[1]; } var baseURLHashSplit = HASH_SPLIT.exec(baseURL); if (baseURLHashSplit) { baseURL = baseURLHashSplit[1]; } var baseURLQuerySplit = QUERY_SPLIT.exec(baseURL); if (baseURLQuerySplit) { baseURL = baseURLQuerySplit[1]; } var baseURLDomainSplit = DOMAIN_SPLIT.exec(baseURL); if (!baseURLDomainSplit) { throw new Error('Error trying to parse base URL.'); } // e.g. 'http://', 'https://', '//', '' var baseURLProtocol = baseURLDomainSplit[2] || '//'; // if there is no protocol default to '//' // e.g. 'http://example.com', '//example.com', 'example.com', '' var baseURLProtocolDomain = baseURLDomainSplit[1] || ''; // e.g. '/a/b/c/playlist.m3u8', 'a/b/c/playlist.m3u8' var baseURLPath = baseURLDomainSplit[3]; if (baseURLPath.indexOf('/') !== 0 && baseURLProtocolDomain !== '') { // this handles a base url of http://example.com (missing last slash) baseURLPath = '/'+baseURLPath; } var builtURL = null; if (/^\/\//.test(relativeURL)) { // relative url starts wth '//' so copy protocol builtURL = baseURLProtocol+URLToolkit.buildAbsolutePath('', relativeURL.substring(2)); } else if (/^\//.test(relativeURL)) { // relative url starts with '/' so start from root of domain builtURL = baseURLProtocolDomain+'/'+URLToolkit.buildAbsolutePath('', relativeURL.substring(1)); } else { builtURL = URLToolkit.buildAbsolutePath(baseURLProtocolDomain+baseURLPath, relativeURL); } // put the query and hash parts back if (relativeURLQuery) { builtURL += relativeURLQuery; } if (relativeURLHash) { builtURL += relativeURLHash; } return builtURL; }, // build an absolute path using the provided basePath // adapted from https://developer.mozilla.org/en-US/docs/Web/API/document/cookie#Using_relative_URLs_in_the_path_parameter // this does not handle the case where relativePath is "/" or "//". These cases should be handled outside this. buildAbsolutePath: function(basePath, relativePath) { var sRelPath = relativePath; var nUpLn, sDir = '', sPath = basePath.replace(/[^\/]*$/, sRelPath.replace(/(\/|^)(?:\.?\/+)+/g, '$1')); for (var nEnd, nStart = 0; nEnd = sPath.indexOf('/../', nStart), nEnd > -1; nStart = nEnd + nUpLn) { nUpLn = /^\/(?:\.\.\/)*/.exec(sPath.slice(nEnd))[0].length; sDir = (sDir + sPath.substring(nStart, nEnd)).replace(new RegExp('(?:\\\/+[^\\\/]*){0,' + ((nUpLn - 1) / 3) + '}$'), '/'); } return sDir + sPath.substr(nStart); } }; /* 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 */ },{}],220:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _button = require('./button.js'); var _button2 = _interopRequireDefault(_button); var _component = require('./component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file big-play-button.js */ /** * The initial play button that shows before the video has played. The hiding of the * `BigPlayButton` get done via CSS and `Player` states. * * @extends Button */ var BigPlayButton = function (_Button) { _inherits(BigPlayButton, _Button); function BigPlayButton() { _classCallCheck(this, BigPlayButton); return _possibleConstructorReturn(this, _Button.apply(this, arguments)); } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. Always returns 'vjs-big-play-button'. */ BigPlayButton.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-big-play-button'; }; /** * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent} * for more detailed information on what a click can be. * * @param {EventTarget~Event} event * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ BigPlayButton.prototype.handleClick = function handleClick(event) { this.player_.play(); }; return BigPlayButton; }(_button2['default']); /** * The text that should display over the `BigPlayButton`s controls. Added to for localization. * * @type {string} * @private */ BigPlayButton.prototype.controlText_ = 'Play Video'; _component2['default'].registerComponent('BigPlayButton', BigPlayButton); exports['default'] = BigPlayButton; },{"./button.js":221,"./component.js":224}],221:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _clickableComponent = require('./clickable-component.js'); var _clickableComponent2 = _interopRequireDefault(_clickableComponent); var _component = require('./component'); var _component2 = _interopRequireDefault(_component); var _log = require('./utils/log.js'); var _log2 = _interopRequireDefault(_log); var _obj = require('./utils/obj'); 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; } /** * @file button.js */ /** * Base class for all buttons. * * @extends ClickableComponent */ var Button = function (_ClickableComponent) { _inherits(Button, _ClickableComponent); function Button() { _classCallCheck(this, Button); return _possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments)); } /** * Create the `Button`s DOM element. * * @param {string} [tag=button] * Element's node type. e.g. 'button' * * @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} * The element that gets created. */ Button.prototype.createEl = function createEl() { var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'button'; var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; props = (0, _obj.assign)({ className: this.buildCSSClass() }, props); if (tag !== 'button') { _log2['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 = (0, _obj.assign)({ tabIndex: 0 }, props); // Add ARIA attributes for clickable element which is not a native HTML button attributes = (0, _obj.assign)({ role: 'button' }, attributes); } // Add attributes for button element attributes = (0, _obj.assign)({ // Necessary since the default button type is "submit" 'type': 'button', // let the screen reader user know that the text of the button may change 'aria-live': 'polite' }, attributes); var el = _component2['default'].prototype.createEl.call(this, tag, props, attributes); this.createControlTextEl(el); return el; }; /** * Add a child `Component` inside of this `Button`. * * @param {string|Component} child * The name or instance of a child to add. * * @param {Object} [options={}] * The key/value store of options that will get passed to children of * the child. * * @return {Component} * The `Component` that gets added as a child. When using a string the * `Component` will get created by this process. * * @deprecated since version 5 */ Button.prototype.addChild = function addChild(child) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var className = this.constructor.name; _log2['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); }; /** * Enable the `Button` element so that it can be activated or clicked. Use this with * {@link Button#disable}. */ Button.prototype.enable = function enable() { _ClickableComponent.prototype.enable.call(this); this.el_.removeAttribute('disabled'); }; /** * Enable the `Button` element so that it cannot be activated or clicked. Use this with * {@link Button#enable}. */ Button.prototype.disable = function disable() { _ClickableComponent.prototype.disable.call(this); this.el_.setAttribute('disabled', 'disabled'); }; /** * This gets called when a `Button` has focus and `keydown` is triggered via a key * press. * * @param {EventTarget~Event} event * The event that caused this function to get called. * * @listens keydown */ 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) { return; } // Pass keypress handling up for unsupported keys _ClickableComponent.prototype.handleKeyPress.call(this, event); }; return Button; }(_clickableComponent2['default']); _component2['default'].registerComponent('Button', Button); exports['default'] = Button; },{"./clickable-component.js":222,"./component":224,"./utils/log.js":305,"./utils/obj":307}],222:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('./component'); var _component2 = _interopRequireDefault(_component); var _dom = require('./utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _events = require('./utils/events.js'); var Events = _interopRequireWildcard(_events); var _fn = require('./utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _log = require('./utils/log.js'); var _log2 = _interopRequireDefault(_log); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); var _obj = require('./utils/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 _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; } /** * @file button.js */ /** * Clickable Component which is clickable or keyboard actionable, * but is not a native HTML button. * * @extends Component */ var ClickableComponent = function (_Component) { _inherits(ClickableComponent, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function ClickableComponent(player, options) { _classCallCheck(this, ClickableComponent); var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _this.emitTapEvents(); _this.enable(); return _this; } /** * Create the `Component`s DOM element. * * @param {string} [tag=div] * The element's node type. * * @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} * The element that gets created. */ ClickableComponent.prototype.createEl = function createEl() { var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div'; var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; props = (0, _obj.assign)({ className: this.buildCSSClass(), tabIndex: 0 }, props); if (tag === 'button') { _log2['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 = (0, _obj.assign)({ 'role': 'button', // let the screen reader user know that the text of the element may change 'aria-live': 'polite' }, attributes); this.tabIndex_ = props.tabIndex; var el = _Component.prototype.createEl.call(this, tag, props, attributes); this.createControlTextEl(el); return el; }; /** * Create a control text element on this `Component` * * @param {Element} [el] * Parent element for the control text. * * @return {Element} * The control text element that gets created. */ 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_, el); return this.controlTextEl_; }; /** * Get or set the localize text to use for the controls on the `Component`. * * @param {string} [text] * Control text for element. * * @param {Element} [el=this.el()] * Element to set the title on. * * @return {string|ClickableComponent} * - The control text when getting * - Returns itself when setting; method can be chained. */ ClickableComponent.prototype.controlText = function controlText(text) { var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el(); if (!text) { return this.controlText_ || 'Need Text'; } var localizedText = this.localize(text); this.controlText_ = text; this.controlTextEl_.innerHTML = localizedText; el.setAttribute('title', localizedText); return this; }; /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ ClickableComponent.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this); }; /** * Enable this `Component`s element. * * @return {ClickableComponent} * Returns itself; method can be chained. */ ClickableComponent.prototype.enable = function enable() { this.removeClass('vjs-disabled'); this.el_.setAttribute('aria-disabled', 'false'); if (typeof this.tabIndex_ !== 'undefined') { this.el_.setAttribute('tabIndex', this.tabIndex_); } this.on('tap', this.handleClick); this.on('click', this.handleClick); this.on('focus', this.handleFocus); this.on('blur', this.handleBlur); return this; }; /** * Disable this `Component`s element. * * @return {ClickableComponent} * Returns itself; method can be chained. */ ClickableComponent.prototype.disable = function disable() { this.addClass('vjs-disabled'); this.el_.setAttribute('aria-disabled', 'true'); if (typeof this.tabIndex_ !== 'undefined') { this.el_.removeAttribute('tabIndex'); } this.off('tap', this.handleClick); this.off('click', this.handleClick); this.off('focus', this.handleFocus); this.off('blur', this.handleBlur); return this; }; /** * This gets called when a `ClickableComponent` gets: * - Clicked (via the `click` event, listening starts in the constructor) * - Tapped (via the `tap` event, listening starts in the constructor) * - The following things happen in order: * 1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the * `ClickableComponent`. * 2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using * {@link ClickableComponent#handleKeyPress}. * 3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses * the space or enter key. * 4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown` * event as a parameter. * * @param {EventTarget~Event} event * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click * @abstract */ ClickableComponent.prototype.handleClick = function handleClick(event) {}; /** * This gets called when a `ClickableComponent` gains focus via a `focus` event. * Turns on listening for `keydown` events. When they happen it * calls `this.handleKeyPress`. * * @param {EventTarget~Event} event * The `focus` event that caused this function to be called. * * @listens focus */ ClickableComponent.prototype.handleFocus = function handleFocus(event) { Events.on(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress)); }; /** * Called when this ClickableComponent has focus and a key gets pressed down. By * default it will call `this.handleClick` when the key is space or enter. * * @param {EventTarget~Event} event * The `keydown` event that caused this function to be called. * * @listens keydown */ 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) { // Pass keypress handling up for unsupported keys _Component.prototype.handleKeyPress.call(this, event); } }; /** * Called when a `ClickableComponent` loses focus. Turns off the listener for * `keydown` events. Which Stops `this.handleKeyPress` from getting called. * * @param {EventTarget~Event} event * The `blur` event that caused this function to be called. * * @listens blur */ ClickableComponent.prototype.handleBlur = function handleBlur(event) { Events.off(_document2['default'], 'keydown', Fn.bind(this, this.handleKeyPress)); }; return ClickableComponent; }(_component2['default']); _component2['default'].registerComponent('ClickableComponent', ClickableComponent); exports['default'] = ClickableComponent; },{"./component":224,"./utils/dom.js":300,"./utils/events.js":301,"./utils/fn.js":302,"./utils/log.js":305,"./utils/obj":307,"global/document":193}],223:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _button = require('./button'); var _button2 = _interopRequireDefault(_button); var _component = require('./component'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file close-button.js */ /** * The `CloseButton` is a `{@link Button}` that fires a `close` event when * it gets clicked. * * @extends Button */ var CloseButton = function (_Button) { _inherits(CloseButton, _Button); /** * Creates an instance of the this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function CloseButton(player, options) { _classCallCheck(this, CloseButton); var _this = _possibleConstructorReturn(this, _Button.call(this, player, options)); _this.controlText(options && options.controlText || _this.localize('Close')); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ CloseButton.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this); }; /** * This gets called when a `CloseButton` gets clicked. See * {@link ClickableComponent#handleClick} for more information on when this will be * triggered * * @param {EventTarget~Event} event * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click * @fires CloseButton#close */ CloseButton.prototype.handleClick = function handleClick(event) { /** * Triggered when the a `CloseButton` is clicked. * * @event CloseButton#close * @type {EventTarget~Event} * * @property {boolean} [bubbles=false] * set to false so that the close event does not * bubble up to parents if there is no listener */ this.trigger({ type: 'close', bubbles: false }); }; return CloseButton; }(_button2['default']); _component2['default'].registerComponent('CloseButton', CloseButton); exports['default'] = CloseButton; },{"./button":221,"./component":224}],224:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); var _dom = require('./utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _fn = require('./utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _guid = require('./utils/guid.js'); var Guid = _interopRequireWildcard(_guid); var _events = require('./utils/events.js'); var Events = _interopRequireWildcard(_events); var _log = require('./utils/log.js'); var _log2 = _interopRequireDefault(_log); var _toTitleCase = require('./utils/to-title-case.js'); var _toTitleCase2 = _interopRequireDefault(_toTitleCase); var _mergeOptions = require('./utils/merge-options.js'); var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 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"); } } /** * Player Component - Base class for all UI objects * * @file component.js */ /** * Base class for all UI Components. * Components are UI objects which represent both a javascript object and an element * in the DOM. They can be children of other components, and can have * children themselves. * * Components can also use methods from {@link EventTarget} */ var Component = function () { /** * A callback that is called when a component is ready. Does not have any * paramters and any callback value will be ignored. * * @callback Component~ReadyCallback * @this Component */ /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. # * @param {Object[]} [options.children] * An array of children objects to intialize this component with. Children objects have * a name property that will be used if more than one component of the same type needs to be * added. * * @param {Component~ReadyCallback} [ready] * Function that gets called when the `Component` is ready. */ 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_ = (0, _mergeOptions2['default'])({}, this.options_); // Updated options with supplied options options = this.options_ = (0, _mergeOptions2['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. * * @fires Component#dispose */ Component.prototype.dispose = function dispose() { /** * Triggered when a `Component` is disposed. * * @event Component#dispose * @type {EventTarget~Event} * * @property {boolean} [bubbles=false] * set to false so that the close event does not * bubble up */ 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 {@link Player} that the `Component` has attached to. * * @return {Player} * The player that this `Component` has attached to. */ Component.prototype.player = function player() { return this.player_; }; /** * Deep merge of options objects with new options. * > Note: When both `obj` and `options` contain properties whose values are objects. * The two properties get merged using {@link module:mergeOptions} * * @param {Object} obj * The object that contains new options. * * @return {Object} * A new object of `this.options_` and `obj` merged together. * * @deprecated since version 5 */ Component.prototype.options = function options(obj) { _log2['default'].warn('this.options() has been deprecated and will be moved to the constructor in 6.0'); if (!obj) { return this.options_; } this.options_ = (0, _mergeOptions2['default'])(this.options_, obj); return this.options_; }; /** * Get the `Component`s DOM element * * @return {Element} * The DOM element for this `Component`. */ Component.prototype.el = function el() { return this.el_; }; /** * Create the `Component`s DOM element. * * @param {string} [tagName] * Element's DOM 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} * The element that gets created. */ Component.prototype.createEl = function createEl(tagName, properties, attributes) { return Dom.createEl(tagName, properties, attributes); }; /** * Localize a string given the string in english. * * @param {string} string * The string to localize. * * @return {string} * The localized string or if no localization exists the english string. */ 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. This is where children get inserted. * This will usually be the the same as the element returned in {@link Component#el}. * * @return {Element} * The content element for this `Component`. */ Component.prototype.contentEl = function contentEl() { return this.contentEl_ || this.el_; }; /** * Get this `Component`s ID * * @return {string} * The id of this `Component` */ Component.prototype.id = function id() { return this.id_; }; /** * Get the `Component`s name. The name gets used to reference the `Component` * and is set during registration. * * @return {string} * The name of this `Component`. */ Component.prototype.name = function name() { return this.name_; }; /** * Get an array of all child components * * @return {Array} * The children */ Component.prototype.children = function children() { return this.children_; }; /** * Returns the child `Component` with the given `id`. * * @param {string} id * The id of the child `Component` to get. * * @return {Component|undefined} * The child `Component` with the given `id` or undefined. */ Component.prototype.getChildById = function getChildById(id) { return this.childIndex_[id]; }; /** * Returns the child `Component` with the given `name`. * * @param {string} name * The name of the child `Component` to get. * * @return {Component|undefined} * The child `Component` with the given `name` or undefined. */ Component.prototype.getChild = function getChild(name) { if (!name) { return; } name = (0, _toTitleCase2['default'])(name); return this.childNameIndex_[name]; }; /** * Add a child `Component` inside the current `Component`. * * * @param {string|Component} child * The name or instance of a child to add. * * @param {Object} [options={}] * The key/value store of options that will get passed to children of * the child. * * @param {number} [index=this.children_.length] * The index to attempt to add a child into. * * @return {Component} * The `Component` that gets added as a child. When using a string the * `Component` will get created by this process. */ Component.prototype.addChild = function addChild(child) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length; var component = void 0; var componentName = void 0; // If child is a string, create component with options if (typeof child === 'string') { componentName = (0, _toTitleCase2['default'])(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) { _log2['default'].warn('Initializing a child component with `true` is deprecated.' + 'Children should be defined in an array when possible, ' + 'but if necessary use an object instead of `true`.'); options = {}; } var componentClassName = options.componentClass || 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. Also removes * the child `Component`s element from this `Component`s element. * * @param {Component} component * The child `Component` to remove. */ 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 `Component`s based upon options. */ 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 = void 0; 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; } return child === wchild.name; }); })).map(function (child) { var name = void 0; var opts = void 0; 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 || (0, _toTitleCase2['default'])(child.name)); return c && !Tech.isTech(c); }).forEach(handleAdd); })(); } }; /** * Builds the default DOM class name. Should be overriden by sub-components. * * @return {string} * The DOM class name for this object. * * @abstract */ 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. * * The benefit of using this over the following: * - `VjsEvents.on(otherElement, 'eventName', myFunc)` * - `otherComponent.on('eventName', myFunc)` * * 1. Is that the listeners will get cleaned up when either component gets disposed. * 1. It will also bind `myComponent` as the context of `myFunc`. * > NOTE: If you remove the element from the DOM that has used `on` you need to * clean up references using: `myComponent.trigger(el, 'dispose')` * This will also allow the browser to garbage collect it. In special * cases such as with `window` and `document`, which are both permanent, * this is not necessary. * * @param {string|Component|string[]} [first] * The event name, and array of event names, or another `Component`. * * @param {EventTarget~EventListener|string|string[]} [second] * The listener function, an event name, or an Array of events names. * * @param {EventTarget~EventListener} [third] * The event handler if `first` is a `Component` and `second` is an event name * or an Array of event names. * * @return {Component} * Returns itself; method can be chained. * * @listens Component#dispose */ 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. If the second argument is * exluded all listeners for the type passed in as the first argument will be removed. * * @param {string|Component|string[]} [first] * The event name, and array of event names, or another `Component`. * * @param {EventTarget~EventListener|string|string[]} [second] * The listener function, an event name, or an Array of events names. * * @param {EventTarget~EventListener} [third] * The event handler if `first` is a `Component` and `second` is an event name * or an Array of event names. * * @return {Component} * Returns itself; method can be chained. */ 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 that gets triggered only once and then gets removed. * * @param {string|Component|string[]} [first] * The event name, and array of event names, or another `Component`. * * @param {EventTarget~EventListener|string|string[]} [second] * The listener function, an event name, or an Array of events names. * * @param {EventTarget~EventListener} [third] * The event handler if `first` is a `Component` and `second` is an event name * or an Array of event names. * * @return {Component} * Returns itself; method can be chained. */ 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. * * @param {EventTarget~Event|Object|string} event * The event name, and Event, or an event-like object with a type attribute * set to the event name. * * @param {Object} [hash] * Data hash to pass along with the event * * @return {Component} * Returns itself; method can be chained. */ Component.prototype.trigger = function trigger(event, hash) { Events.trigger(this.el_, event, hash); return this; }; /** * Bind a listener to the component's ready state. If the ready event has already * happened it will trigger the function immediately. * * @param {Component~ReadyCallback} fn * A function to call when ready is triggered. * * @param {boolean} [sync=false] * Execute the listener synchronously if `Component` is ready. * * @return {Component} * Returns itself; method can be chained. */ Component.prototype.ready = function ready(fn) { var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 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 all the ready listeners for this `Component`. * * @fires Component#ready */ 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 /** * Triggered when a `Component` is ready. * * @event Component#ready * @type {EventTarget~Event} */ this.trigger('ready'); }, 1); }; /** * Find a single DOM element matching a `selector`. This can be within the `Component`s * `contentEl()` or another custom context. * * @param {string} selector * A valid CSS selector, which will be passed to `querySelector`. * * @param {Element|string} [context=this.contentEl()] * A DOM element within which to query. Can also be a selector string in * which case the first matching element will get used as context. If * missing `this.contentEl()` gets used. If `this.contentEl()` returns * nothing it falls back to `document`. * * @return {Element|null} * the dom element that was found, or null * * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors) */ Component.prototype.$ = function $(selector, context) { return Dom.$(selector, context || this.contentEl()); }; /** * Finds all DOM element matching a `selector`. This can be within the `Component`s * `contentEl()` or another custom context. * * @param {string} selector * A valid CSS selector, which will be passed to `querySelectorAll`. * * @param {Element|string} [context=this.contentEl()] * A DOM element within which to query. Can also be a selector string in * which case the first matching element will get used as context. If * missing `this.contentEl()` gets used. If `this.contentEl()` returns * nothing it falls back to `document`. * * @return {NodeList} * a list of dom elements that were found * * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors) */ 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 * CSS class name to check. * * @return {boolean} * - True if the `Component` has the class. * - False if the `Component` does not have the class` */ 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 * CSS class name to add * * @return {Component} * Returns itself; method can be chained. */ 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 * CSS class name to remove * * @return {Component} * Returns itself; method can be chained. */ 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. * - `classToToggle` gets added when {@link Component#hasClass} would return false. * - `classToToggle` gets removed when {@link Component#hasClass} would return true. * * @param {string} classToToggle * The class to add or remove based on (@link Component#hasClass} * * @param {boolean|Dom~predicate} [predicate] * An {@link Dom~predicate} function or a boolean * * @return {Component} * Returns itself; method can be chained. */ Component.prototype.toggleClass = function toggleClass(classToToggle, predicate) { Dom.toggleElClass(this.el_, classToToggle, predicate); return this; }; /** * Show the `Component`s element if it is hidden by removing the * 'vjs-hidden' class name from it. * * @return {Component} * Returns itself; method can be chained. */ Component.prototype.show = function show() { this.removeClass('vjs-hidden'); return this; }; /** * Hide the `Component`s element if it is currently showing by adding the * 'vjs-hidden` class name to it. * * @return {Component} * Returns itself; method can be chained. */ Component.prototype.hide = function hide() { this.addClass('vjs-hidden'); return this; }; /** * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing' * class name to it. Used during fadeIn/fadeOut. * * @return {Component} * Returns itself; method can be chained. * * @private */ Component.prototype.lockShowing = function lockShowing() { this.addClass('vjs-lock-showing'); return this; }; /** * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing' * class name from it. Used during fadeIn/fadeOut. * * @return {Component} * Returns itself; method can be chained. * * @private */ Component.prototype.unlockShowing = function unlockShowing() { this.removeClass('vjs-lock-showing'); return this; }; /** * Get the value of an attribute on the `Component`s element. * * @param {string} attribute * Name of the attribute to get the value from. * * @return {string|null} * - The value of the attribute that was asked for. * - Can be an empty string on some browsers if the attribute does not exist * or has no value * - Most browsers will return null if the attibute does not exist or has * no value. * * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute} */ Component.prototype.getAttribute = function getAttribute(attribute) { return Dom.getAttribute(this.el_, attribute); }; /** * Set the value of an attribute on the `Component`'s element * * @param {string} attribute * Name of the attribute to set. * * @param {string} value * Value to set the attribute to. * * @return {Component} * Returns itself; method can be chained. * * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute} */ Component.prototype.setAttribute = function setAttribute(attribute, value) { Dom.setAttribute(this.el_, attribute, value); return this; }; /** * Remove an attribute from the `Component`s element. * * @param {string} attribute * Name of the attribute to remove. * * @return {Component} * Returns itself; method can be chained. * * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute} */ Component.prototype.removeAttribute = function removeAttribute(attribute) { Dom.removeAttribute(this.el_, attribute); return this; }; /** * Get or set the width of the component based upon the CSS styles. * See {@link Component#dimension} for more detailed information. * * @param {number|string} [num] * The width that you want to set postfixed with '%', 'px' or nothing. * * @param {boolean} [skipListeners] * Skip the resize event trigger * * @return {Component|number|string} * - The width when getting, zero if there is no width. Can be a string * postpixed with '%' or 'px'. * - Returns itself when setting; method can be chained. */ Component.prototype.width = function width(num, skipListeners) { return this.dimension('width', num, skipListeners); }; /** * Get or set the height of the component based upon the CSS styles. * See {@link Component#dimension} for more detailed information. * * @param {number|string} [num] * The height that you want to set postfixed with '%', 'px' or nothing. * * @param {boolean} [skipListeners] * Skip the resize event trigger * * @return {Component|number|string} * - The width when getting, zero if there is no width. Can be a string * postpixed with '%' or 'px'. * - Returns itself when setting; method can be chained. */ Component.prototype.height = function height(num, skipListeners) { return this.dimension('height', num, skipListeners); }; /** * Set both the width and height of the `Component` element at the same time. * * @param {number|string} width * Width to set the `Component`s element to. * * @param {number|string} height * Height to set the `Component`s element to. * * @return {Component} * Returns itself; method can be chained. */ 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 of the `Component` element. This is the shared code * for the {@link Component#width} and {@link Component#height}. * * Things to know: * - If the width or height in an number this will return the number postfixed with 'px'. * - If the width/height is a percent this will return the percent postfixed with '%' * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function * defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`. * See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/} * for more information * - If you want the computed style of the component, use {@link Component#currentWidth} * and {@link {Component#currentHeight} * * @fires Component#resize * * @param {string} widthOrHeight 8 'width' or 'height' * * @param {number|string} [num] 8 New dimension * * @param {boolean} [skipListeners] * Skip resize event trigger * * @return {Component} * - the dimension when getting or 0 if unset * - Returns itself when setting; method can be chained. */ 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) { /** * Triggered when a component is resized. * * @event Component#resize * @type {EventTarget~Event} */ 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' + (0, _toTitleCase2['default'])(widthOrHeight)], 10); }; /** * Get the width or the height of the `Component` elements computed style. Uses * `window.getComputedStyle`. * * @param {string} widthOrHeight * A string containing 'width' or 'height'. Whichever one you want to get. * * @return {number} * The dimension that gets asked for or 0 if nothing was set * for that dimension. */ 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 _window2['default'].getComputedStyle === 'function') { var computedStyle = _window2['default'].getComputedStyle(this.el_); computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight]; } // remove 'px' from variable and parse as integer computedWidthOrHeight = parseFloat(computedWidthOrHeight); // if the computed value is still 0, it's possible that the browser is lying // and we want to check the offset values. // This code also runs on IE8 and wherever getComputedStyle doesn't exist. if (computedWidthOrHeight === 0) { var rule = 'offset' + (0, _toTitleCase2['default'])(widthOrHeight); computedWidthOrHeight = this.el_[rule]; } return computedWidthOrHeight; }; /** * An object that contains width and height values of the `Component`s * computed style. Uses `window.getComputedStyle`. * * @typedef {Object} Component~DimensionObject * * @property {number} width * The width of the `Component`s computed style. * * @property {number} height * The height of the `Component`s computed style. */ /** * Get an object that contains width and height values of the `Component`s * computed style. * * @return {Component~DimensionObject} * The dimensions of the components element */ Component.prototype.currentDimensions = function currentDimensions() { return { width: this.currentDimension('width'), height: this.currentDimension('height') }; }; /** * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`. * * @return {number} width * The width of the `Component`s computed style. */ Component.prototype.currentWidth = function currentWidth() { return this.currentDimension('width'); }; /** * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`. * * @return {number} height * The height of the `Component`s computed style. */ Component.prototype.currentHeight = function currentHeight() { return this.currentDimension('height'); }; /** * Emit a 'tap' events when touch event support gets detected. This gets used to * support toggling the controls through a tap on the video. They get enabled * because every sub-component would have extra overhead otherwise. * * @private * @fires Component#tap * @listens Component#touchstart * @listens Component#touchmove * @listens Component#touchleave * @listens Component#touchcancel * @listens Component#touchend */ 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 = void 0; 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) { // 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(); /** * Triggered when a `Component` is tapped. * * @event Component#tap * @type {EventTarget~Event} */ 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) } } }); }; /** * This function reports user activity whenever touch events happen. This can get * turned off by any sub-components that wants touch events to act another way. * * Report user touch activity when touch events occur. User activity gets used to * determine when controls should show/hide. It is 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 as `touchstart` and `touchend` toggle player * controls. So touch events can't help us at the player level either. * * User activity gets 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. * Furthermore a `touchmove` event and anything other than a tap, should not turn * controls back on. * * @listens Component#touchstart * @listens Component#touchmove * @listens Component#touchend * @listens Component#touchcancel */ 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 = void 0; 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); }; /** * A callback that has no parameters and is bound into `Component`s context. * * @callback Component~GenericCallback * @this Component */ /** * Creates a function that runs after an `x` millisecond timeout. This function is a * wrapper around `window.setTimeout`. There are a few reasons to use this one * instead though: * 1. It gets cleared via {@link Component#clearTimeout} when * {@link Component#dispose} gets called. * 2. The function callback will gets turned into a {@link Component~GenericCallback} * * > Note: You can use `window.clearTimeout` on the id returned by this function. This * will cause its dispose listener not to get cleaned up! Please use * {@link Component#clearTimeout} or {@link Component#dispose}. * * @param {Component~GenericCallback} fn * The function that will be run after `timeout`. * * @param {number} timeout * Timeout in milliseconds to delay before executing the specified function. * * @return {number} * Returns a timeout ID that gets used to identify the timeout. It can also * get used in {@link Component#clearTimeout} to clear the timeout that * was set. * * @listens Component#dispose * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout} */ Component.prototype.setTimeout = function setTimeout(fn, timeout) { fn = Fn.bind(this, fn); var timeoutId = _window2['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 that gets created via `window.setTimeout` or * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout} * use this function instead of `window.clearTimout`. If you don't your dispose * listener will not get cleaned up until {@link Component#dispose}! * * @param {number} timeoutId * The id of the timeout to clear. The return value of * {@link Component#setTimeout} or `window.setTimeout`. * * @return {number} * Returns the timeout id that was cleared. * * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout} */ Component.prototype.clearTimeout = function clearTimeout(timeoutId) { _window2['default'].clearTimeout(timeoutId); var disposeFn = function disposeFn() {}; disposeFn.guid = 'vjs-timeout-' + timeoutId; this.off('dispose', disposeFn); return timeoutId; }; /** * Creates a function that gets run every `x` milliseconds. This function is a wrapper * around `window.setInterval`. There are a few reasons to use this one instead though. * 1. It gets cleared via {@link Component#clearInterval} when * {@link Component#dispose} gets called. * 2. The function callback will be a {@link Component~GenericCallback} * * @param {Component~GenericCallback} fn * The function to run every `x` seconds. * * @param {number} interval * Execute the specified function every `x` milliseconds. * * @return {number} * Returns an id that can be used to identify the interval. It can also be be used in * {@link Component#clearInterval} to clear the interval. * * @listens Component#dispose * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval} */ Component.prototype.setInterval = function setInterval(fn, interval) { fn = Fn.bind(this, fn); var intervalId = _window2['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 that gets created via `window.setInterval` or * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval} * use this function instead of `window.clearInterval`. If you don't your dispose * listener will not get cleaned up until {@link Component#dispose}! * * @param {number} intervalId * The id of the interval to clear. The return value of * {@link Component#setInterval} or `window.setInterval`. * * @return {number} * Returns the interval id that was cleared. * * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval} */ Component.prototype.clearInterval = function clearInterval(intervalId) { _window2['default'].clearInterval(intervalId); var disposeFn = function disposeFn() {}; disposeFn.guid = 'vjs-interval-' + intervalId; this.off('dispose', disposeFn); return intervalId; }; /** * Register a `Component` with `videojs` given the name and the component. * * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s * should be registered using {@link Tech.registerTech} or * {@link videojs:videojs.registerTech}. * * > NOTE: This function can also be seen on videojs as * {@link videojs:videojs.registerComponent}. * * @param {string} name * The name of the `Component` to register. * * @param {Component} comp * The `Component` class to register. * * @return {Component} * The `Component` that was registered. */ Component.registerComponent = function registerComponent(name, comp) { if (!name) { return; } name = (0, _toTitleCase2['default'])(name); if (!Component.components_) { Component.components_ = {}; } if (name === 'Player' && Component.components_[name]) { (function () { var Player = Component.components_[name]; // If we have players that were disposed, then their name will still be // in Players.players. So, we must loop through and verify that the value // for each item is not null. This allows registration of the Player component // after all players have been disposed or before any were created. if (Player.players && Object.keys(Player.players).length > 0 && Object.keys(Player.players).map(function (playerName) { return Player.players[playerName]; }).every(Boolean)) { throw new Error('Can not register Player component after player has been created'); } })(); } Component.components_[name] = comp; return comp; }; /** * Get a `Component` based on the name it was registered with. * * @param {string} name * The Name of the component to get. * * @return {Component} * The `Component` that got registered under the given name. * * @deprecated In `videojs` 6 this will not return `Component`s that were not * registered using {@link Component.registerComponent}. Currently we * check the global `videojs` object for a `Component` name and * return that if it exists. */ Component.getComponent = function getComponent(name) { if (!name) { return; } name = (0, _toTitleCase2['default'])(name); if (Component.components_ && Component.components_[name]) { return Component.components_[name]; } if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) { _log2['default'].warn('The ' + name + ' component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)'); return _window2['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. * * @return {Object} * the extended object. * * @deprecated since version 5 */ Component.extend = function extend(props) { props = props || {}; _log2['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; },{"./utils/dom.js":300,"./utils/events.js":301,"./utils/fn.js":302,"./utils/guid.js":304,"./utils/log.js":305,"./utils/merge-options.js":306,"./utils/to-title-case.js":310,"global/window":194}],225:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _trackButton = require('../track-button.js'); var _trackButton2 = _interopRequireDefault(_trackButton); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _audioTrackMenuItem = require('./audio-track-menu-item.js'); var _audioTrackMenuItem2 = _interopRequireDefault(_audioTrackMenuItem); 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; } /** * @file audio-track-button.js */ /** * The base class for buttons that toggle specific {@link AudioTrack} types. * * @extends TrackButton */ var AudioTrackButton = function (_TrackButton) { _inherits(AudioTrackButton, _TrackButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options={}] * The key/value store of player options. */ function AudioTrackButton(player) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, AudioTrackButton); options.tracks = player.audioTracks && player.audioTracks(); var _this = _possibleConstructorReturn(this, _TrackButton.call(this, player, options)); _this.el_.setAttribute('aria-label', 'Audio Menu'); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this); }; /** * Create a menu item for each audio track * * @param {AudioTrackMenuItem[]} [items=[]] * An array of existing menu items to use. * * @return {AudioTrackMenuItem[]} * An array of menu items */ 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 _audioTrackMenuItem2['default'](this.player_, { track: track, // MenuItem is selectable selectable: true })); } return items; }; return AudioTrackButton; }(_trackButton2['default']); /** * The text that should display over the `AudioTrackButton`s controls. Added for localization. * * @type {string} * @private */ AudioTrackButton.prototype.controlText_ = 'Audio Track'; _component2['default'].registerComponent('AudioTrackButton', AudioTrackButton); exports['default'] = AudioTrackButton; },{"../../component.js":224,"../track-button.js":255,"./audio-track-menu-item.js":226}],226:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _menuItem = require('../../menu/menu-item.js'); var _menuItem2 = _interopRequireDefault(_menuItem); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); 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 _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; } /** * @file audio-track-menu-item.js */ /** * An {@link AudioTrack} {@link MenuItem} * * @extends MenuItem */ var AudioTrackMenuItem = function (_MenuItem) { _inherits(AudioTrackMenuItem, _MenuItem); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function AudioTrackMenuItem(player, options) { _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; var _this = _possibleConstructorReturn(this, _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); }); })(); } return _this; } /** * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent} * for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ 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]; track.enabled = track === this.track; } }; /** * Handle any {@link AudioTrack} change. * * @param {EventTarget~Event} [event] * The {@link AudioTrackList#change} event that caused this to run. * * @listens AudioTrackList#change */ AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) { this.selected(this.track.enabled); }; return AudioTrackMenuItem; }(_menuItem2['default']); _component2['default'].registerComponent('AudioTrackMenuItem', AudioTrackMenuItem); exports['default'] = AudioTrackMenuItem; },{"../../component.js":224,"../../menu/menu-item.js":267,"../../utils/fn.js":302}],227:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); require('./play-toggle.js'); require('./time-controls/current-time-display.js'); require('./time-controls/duration-display.js'); require('./time-controls/time-divider.js'); require('./time-controls/remaining-time-display.js'); require('./live-display.js'); require('./progress-control/progress-control.js'); require('./fullscreen-toggle.js'); require('./volume-control/volume-control.js'); require('./volume-menu-button.js'); require('./mute-toggle.js'); require('./text-track-controls/chapters-button.js'); require('./text-track-controls/descriptions-button.js'); require('./text-track-controls/subtitles-button.js'); require('./text-track-controls/captions-button.js'); require('./audio-track-controls/audio-track-button.js'); require('./playback-rate-menu/playback-rate-menu-button.js'); require('./spacer-controls/custom-control-spacer.js'); 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; } /** * @file control-bar.js */ // Required children /** * Container of main controls. * * @extends Component */ var ControlBar = function (_Component) { _inherits(ControlBar, _Component); function ControlBar() { _classCallCheck(this, ControlBar); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ ControlBar.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-control-bar', dir: 'ltr' }, { // The control bar is a group, so it can contain menuitems role: 'group' }); }; return ControlBar; }(_component2['default']); /** * Default options for `ControlBar` * * @type {Object} * @private */ ControlBar.prototype.options_ = { children: ['playToggle', 'volumeMenuButton', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subtitlesButton', 'captionsButton', 'audioTrackButton', 'fullscreenToggle'] }; _component2['default'].registerComponent('ControlBar', ControlBar); exports['default'] = ControlBar; },{"../component.js":224,"./audio-track-controls/audio-track-button.js":225,"./fullscreen-toggle.js":228,"./live-display.js":229,"./mute-toggle.js":230,"./play-toggle.js":231,"./playback-rate-menu/playback-rate-menu-button.js":232,"./progress-control/progress-control.js":237,"./spacer-controls/custom-control-spacer.js":240,"./text-track-controls/captions-button.js":243,"./text-track-controls/chapters-button.js":244,"./text-track-controls/descriptions-button.js":246,"./text-track-controls/subtitles-button.js":248,"./time-controls/current-time-display.js":251,"./time-controls/duration-display.js":252,"./time-controls/remaining-time-display.js":253,"./time-controls/time-divider.js":254,"./volume-control/volume-control.js":257,"./volume-menu-button.js":259}],228:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _button = require('../button.js'); var _button2 = _interopRequireDefault(_button); var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file fullscreen-toggle.js */ /** * Toggle fullscreen video * * @extends Button */ var FullscreenToggle = function (_Button) { _inherits(FullscreenToggle, _Button); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function FullscreenToggle(player, options) { _classCallCheck(this, FullscreenToggle); var _this = _possibleConstructorReturn(this, _Button.call(this, player, options)); _this.on(player, 'fullscreenchange', _this.handleFullscreenChange); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this); }; /** * Handles fullscreenchange on the player and change control text accordingly. * * @param {EventTarget~Event} [event] * The {@link Player#fullscreenchange} event that caused this function to be * called. * * @listens Player#fullscreenchange */ FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) { if (this.player_.isFullscreen()) { this.controlText('Non-Fullscreen'); } else { this.controlText('Fullscreen'); } }; /** * This gets called when an `FullscreenToggle` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ FullscreenToggle.prototype.handleClick = function handleClick(event) { if (!this.player_.isFullscreen()) { this.player_.requestFullscreen(); } else { this.player_.exitFullscreen(); } }; return FullscreenToggle; }(_button2['default']); /** * The text that should display over the `FullscreenToggle`s controls. Added for localization. * * @type {string} * @private */ FullscreenToggle.prototype.controlText_ = 'Fullscreen'; _component2['default'].registerComponent('FullscreenToggle', FullscreenToggle); exports['default'] = FullscreenToggle; },{"../button.js":221,"../component.js":224}],229:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../component'); var _component2 = _interopRequireDefault(_component); var _dom = require('../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); 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 _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; } /** * @file live-display.js */ // TODO - Future make it click to snap to live /** * Displays the live indicator when duration is Infinity. * * @extends Component */ var LiveDisplay = function (_Component) { _inherits(LiveDisplay, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function LiveDisplay(player, options) { _classCallCheck(this, LiveDisplay); var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _this.updateShowing(); _this.on(_this.player(), 'durationchange', _this.updateShowing); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ 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: '' + this.localize('Stream Type') + '' + this.localize('LIVE') }, { 'aria-live': 'off' }); el.appendChild(this.contentEl_); return el; }; /** * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide * it accordingly * * @param {EventTarget~Event} [event] * The {@link Player#durationchange} event that caused this function to run. * * @listens Player#durationchange */ LiveDisplay.prototype.updateShowing = function updateShowing(event) { if (this.player().duration() === Infinity) { this.show(); } else { this.hide(); } }; return LiveDisplay; }(_component2['default']); _component2['default'].registerComponent('LiveDisplay', LiveDisplay); exports['default'] = LiveDisplay; },{"../component":224,"../utils/dom.js":300}],230:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _button = require('../button'); var _button2 = _interopRequireDefault(_button); var _component = require('../component'); var _component2 = _interopRequireDefault(_component); var _dom = require('../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); 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 _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; } /** * @file mute-toggle.js */ /** * A button component for muting the audio. * * @extends Button */ var MuteToggle = function (_Button) { _inherits(MuteToggle, _Button); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function MuteToggle(player, options) { _classCallCheck(this, MuteToggle); var _this = _possibleConstructorReturn(this, _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_.featuresVolumeControl === false) { _this.addClass('vjs-hidden'); } _this.on(player, 'loadstart', function () { // We need to update the button to account for a default muted state. this.update(); if (player.tech_.featuresVolumeControl === false) { this.addClass('vjs-hidden'); } else { this.removeClass('vjs-hidden'); } }); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ MuteToggle.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this); }; /** * This gets called when an `MuteToggle` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ MuteToggle.prototype.handleClick = function handleClick(event) { this.player_.muted(this.player_.muted() ? false : true); }; /** * Update the state of volume. * * @param {EventTarget~Event} [event] * The {@link Player#loadstart} event if this function was called through an * event. * * @listens Player#loadstart */ MuteToggle.prototype.update = function update(event) { var vol = this.player_.volume(); var 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']); /** * The text that should display over the `MuteToggle`s controls. Added for localization. * * @type {string} * @private */ MuteToggle.prototype.controlText_ = 'Mute'; _component2['default'].registerComponent('MuteToggle', MuteToggle); exports['default'] = MuteToggle; },{"../button":221,"../component":224,"../utils/dom.js":300}],231:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _button = require('../button.js'); var _button2 = _interopRequireDefault(_button); var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file play-toggle.js */ /** * Button to toggle between play and pause. * * @extends Button */ var PlayToggle = function (_Button) { _inherits(PlayToggle, _Button); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function PlayToggle(player, options) { _classCallCheck(this, PlayToggle); var _this = _possibleConstructorReturn(this, _Button.call(this, player, options)); _this.on(player, 'play', _this.handlePlay); _this.on(player, 'pause', _this.handlePause); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ PlayToggle.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this); }; /** * This gets called when an `PlayToggle` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ PlayToggle.prototype.handleClick = function handleClick(event) { if (this.player_.paused()) { this.player_.play(); } else { this.player_.pause(); } }; /** * Add the vjs-playing class to the element so it can change appearance. * * @param {EventTarget~Event} [event] * The event that caused this function to run. * * @listens Player#play */ PlayToggle.prototype.handlePlay = function handlePlay(event) { this.removeClass('vjs-paused'); this.addClass('vjs-playing'); // change the button text to "Pause" this.controlText('Pause'); }; /** * Add the vjs-paused class to the element so it can change appearance. * * @param {EventTarget~Event} [event] * The event that caused this function to run. * * @listens Player#pause */ PlayToggle.prototype.handlePause = function handlePause(event) { this.removeClass('vjs-playing'); this.addClass('vjs-paused'); // change the button text to "Play" this.controlText('Play'); }; return PlayToggle; }(_button2['default']); /** * The text that should display over the `PlayToggle`s controls. Added for localization. * * @type {string} * @private */ PlayToggle.prototype.controlText_ = 'Play'; _component2['default'].registerComponent('PlayToggle', PlayToggle); exports['default'] = PlayToggle; },{"../button.js":221,"../component.js":224}],232:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _menuButton = require('../../menu/menu-button.js'); var _menuButton2 = _interopRequireDefault(_menuButton); var _menu = require('../../menu/menu.js'); var _menu2 = _interopRequireDefault(_menu); var _playbackRateMenuItem = require('./playback-rate-menu-item.js'); var _playbackRateMenuItem2 = _interopRequireDefault(_playbackRateMenuItem); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); 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 _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; } /** * @file playback-rate-menu-button.js */ /** * The component for controlling the playback rate. * * @extends MenuButton */ var PlaybackRateMenuButton = function (_MenuButton) { _inherits(PlaybackRateMenuButton, _MenuButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function PlaybackRateMenuButton(player, options) { _classCallCheck(this, PlaybackRateMenuButton); var _this = _possibleConstructorReturn(this, _MenuButton.call(this, player, options)); _this.updateVisibility(); _this.updateLabel(); _this.on(player, 'loadstart', _this.updateVisibility); _this.on(player, 'ratechange', _this.updateLabel); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ 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; }; /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ 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 {@link PlaybackRateMenuItem}s */ PlaybackRateMenuButton.prototype.createMenu = function createMenu() { var menu = new _menu2['default'](this.player()); var rates = this.playbackRates(); if (rates) { for (var i = rates.length - 1; i >= 0; i--) { menu.addChild(new _playbackRateMenuItem2['default'](this.player(), { rate: rates[i] + 'x' })); } } return menu; }; /** * Updates ARIA accessibility attributes */ PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() { // Current playback rate this.el().setAttribute('aria-valuenow', this.player().playbackRate()); }; /** * This gets called when an `PlaybackRateMenuButton` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) { // 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} * All possible playback rates */ 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 */ 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 * * @param {EventTarget~Event} [event] * The event that caused this function to run. * * @listens Player#loadstart */ PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) { if (this.playbackRateSupported()) { this.removeClass('vjs-hidden'); } else { this.addClass('vjs-hidden'); } }; /** * Update button label when rate changed * * @param {EventTarget~Event} [event] * The event that caused this function to run. * * @listens Player#ratechange */ PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) { if (this.playbackRateSupported()) { this.labelEl_.innerHTML = this.player().playbackRate() + 'x'; } }; return PlaybackRateMenuButton; }(_menuButton2['default']); /** * The text that should display over the `FullscreenToggle`s controls. Added for localization. * * @type {string} * @private */ PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate'; _component2['default'].registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton); exports['default'] = PlaybackRateMenuButton; },{"../../component.js":224,"../../menu/menu-button.js":266,"../../menu/menu.js":268,"../../utils/dom.js":300,"./playback-rate-menu-item.js":233}],233:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _menuItem = require('../../menu/menu-item.js'); var _menuItem2 = _interopRequireDefault(_menuItem); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file playback-rate-menu-item.js */ /** * The specific menu item type for selecting a playback rate. * * @extends MenuItem */ var PlaybackRateMenuItem = function (_MenuItem) { _inherits(PlaybackRateMenuItem, _MenuItem); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ 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; var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options)); _this.label = label; _this.rate = rate; _this.on(player, 'ratechange', _this.update); return _this; } /** * This gets called when an `PlaybackRateMenuItem` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) { _MenuItem.prototype.handleClick.call(this); this.player().playbackRate(this.rate); }; /** * Update the PlaybackRateMenuItem when the playbackrate changes. * * @param {EventTarget~Event} [event] * The `ratechange` event that caused this function to run. * * @listens Player#ratechange */ PlaybackRateMenuItem.prototype.update = function update(event) { this.selected(this.player().playbackRate() === this.rate); }; return PlaybackRateMenuItem; }(_menuItem2['default']); /** * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization. * * @type {string} * @private */ PlaybackRateMenuItem.prototype.contentElType = 'button'; _component2['default'].registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem); exports['default'] = PlaybackRateMenuItem; },{"../../component.js":224,"../../menu/menu-item.js":267}],234:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); 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 _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; } /** * @file load-progress-bar.js */ /** * Shows loading progress * * @extends Component */ var LoadProgressBar = function (_Component) { _inherits(LoadProgressBar, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function LoadProgressBar(player, options) { _classCallCheck(this, LoadProgressBar); var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _this.partEls_ = []; _this.on(player, 'progress', _this.update); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ LoadProgressBar.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-load-progress', innerHTML: '' + this.localize('Loaded') + ': 0%' }); }; /** * Update progress bar * * @param {EventTarget~Event} [event] * The `progress` event that caused this function to run. * * @listens Player#progress */ LoadProgressBar.prototype.update = function update(event) { var buffered = this.player_.buffered(); var duration = this.player_.duration(); var bufferedEnd = this.player_.bufferedEnd(); var children = this.partEls_; // get the percent width of a time compared to the total end var percentify = function percentify(time, end) { // no NaN var percent = time / end || 0; 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()); children[i] = part; } // 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]); } children.length = buffered.length; }; return LoadProgressBar; }(_component2['default']); _component2['default'].registerComponent('LoadProgressBar', LoadProgressBar); exports['default'] = LoadProgressBar; },{"../../component.js":224,"../../utils/dom.js":300}],235:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _formatTime = require('../../utils/format-time.js'); var _formatTime2 = _interopRequireDefault(_formatTime); var _computedStyle = require('../../utils/computed-style.js'); var _computedStyle2 = _interopRequireDefault(_computedStyle); 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 _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; } /** * @file mouse-time-display.js */ /** * The Mouse Time Display component shows the time you will seek to * when hovering over the progress bar * * @extends Component */ var MouseTimeDisplay = function (_Component) { _inherits(MouseTimeDisplay, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function MouseTimeDisplay(player, options) { _classCallCheck(this, MouseTimeDisplay); var _this = _possibleConstructorReturn(this, _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); player.on('ready', function () { _this.on(player.controlBar.progressControl.el(), 'mousemove', Fn.throttle(Fn.bind(_this, _this.handleMouseMove), 25)); }); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ MouseTimeDisplay.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-mouse-display' }); }; /** * Handle the mouse move event on the `MouseTimeDisplay`. * * @param {EventTarget~Event} event * The `mousemove` event that caused this to event to run. * * @listen mousemove */ MouseTimeDisplay.prototype.handleMouseMove = function handleMouseMove(event) { var duration = this.player_.duration(); var newTime = this.calculateDistance(event) * duration; var position = event.pageX - Dom.findElPosition(this.el().parentNode).left; this.update(newTime, position); }; /** * Update the time and posistion of the `MouseTimeDisplay`. * * @param {number} newTime * Time to change the `MouseTimeDisplay` to. * * @param {nubmer} position * Postion from the left of the in pixels. */ MouseTimeDisplay.prototype.update = function update(newTime, position) { var time = (0, _formatTime2['default'])(newTime, this.player_.duration()); this.el().style.left = position + 'px'; this.el().setAttribute('data-current-time', time); if (this.keepTooltipsInside) { var clampedPosition = this.clampPosition_(position); var difference = position - clampedPosition + 1; var tooltipWidth = parseFloat((0, _computedStyle2['default'])(this.tooltip, 'width')); var tooltipWidthHalf = tooltipWidth / 2; this.tooltip.innerHTML = time; this.tooltip.style.right = '-' + (tooltipWidthHalf - difference) + 'px'; } }; /** * Get the mouse pointers x coordinate in pixels. * * @param {EventTarget~Event} [event] * The `mousemove` event that was passed to this function by * {@link MouseTimeDisplay#handleMouseMove} * * @return {number} * THe x position in pixels of the mouse pointer. */ 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} * The (potentially) new clamped position. * * @private */ MouseTimeDisplay.prototype.clampPosition_ = function clampPosition_(position) { if (!this.keepTooltipsInside) { return position; } var playerWidth = parseFloat((0, _computedStyle2['default'])(this.player().el(), 'width')); var tooltipWidth = parseFloat((0, _computedStyle2['default'])(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; }(_component2['default']); _component2['default'].registerComponent('MouseTimeDisplay', MouseTimeDisplay); exports['default'] = MouseTimeDisplay; },{"../../component.js":224,"../../utils/computed-style.js":299,"../../utils/dom.js":300,"../../utils/fn.js":302,"../../utils/format-time.js":303}],236:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _formatTime = require('../../utils/format-time.js'); var _formatTime2 = _interopRequireDefault(_formatTime); 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 _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; } /** * @file play-progress-bar.js */ /** * Shows play progress * * @extends Component */ var PlayProgressBar = function (_Component) { _inherits(PlayProgressBar, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function PlayProgressBar(player, options) { _classCallCheck(this, PlayProgressBar); var _this = _possibleConstructorReturn(this, _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'); } return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ PlayProgressBar.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-play-progress vjs-slider-bar', innerHTML: '' + this.localize('Progress') + ': 0%' }); }; /** * Update the data-current-time attribute on the `PlayProgressBar`. * * @param {EventTarget~Event} [event] * The `timeupdate` event that caused this to run. * * @listens Player#timeupdate */ PlayProgressBar.prototype.updateDataAttr = function updateDataAttr(event) { var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime(); this.el_.setAttribute('data-current-time', (0, _formatTime2['default'])(time, this.player_.duration())); }; return PlayProgressBar; }(_component2['default']); _component2['default'].registerComponent('PlayProgressBar', PlayProgressBar); exports['default'] = PlayProgressBar; },{"../../component.js":224,"../../utils/fn.js":302,"../../utils/format-time.js":303}],237:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); require('./seek-bar.js'); require('./mouse-time-display.js'); 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; } /** * @file progress-control.js */ /** * The Progress Control component contains the seek bar, load progress, * and play progress. * * @extends Component */ var ProgressControl = function (_Component) { _inherits(ProgressControl, _Component); function ProgressControl() { _classCallCheck(this, ProgressControl); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ ProgressControl.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-progress-control vjs-control' }); }; return ProgressControl; }(_component2['default']); /** * Default options for `ProgressControl` * * @type {Object} * @private */ ProgressControl.prototype.options_ = { children: ['seekBar'] }; _component2['default'].registerComponent('ProgressControl', ProgressControl); exports['default'] = ProgressControl; },{"../../component.js":224,"./mouse-time-display.js":235,"./seek-bar.js":238}],238:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _slider = require('../../slider/slider.js'); var _slider2 = _interopRequireDefault(_slider); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _formatTime = require('../../utils/format-time.js'); var _formatTime2 = _interopRequireDefault(_formatTime); var _computedStyle = require('../../utils/computed-style.js'); var _computedStyle2 = _interopRequireDefault(_computedStyle); require('./load-progress-bar.js'); require('./play-progress-bar.js'); require('./tooltip-progress-bar.js'); 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 _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; } /** * @file seek-bar.js */ /** * Seek Bar and holder for the progress bars * * @extends Slider */ var SeekBar = function (_Slider) { _inherits(SeekBar, _Slider); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function SeekBar(player, options) { _classCallCheck(this, SeekBar); var _this = _possibleConstructorReturn(this, _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'); } return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ SeekBar.prototype.createEl = function createEl() { return _Slider.prototype.createEl.call(this, 'div', { className: 'vjs-progress-holder' }, { 'aria-label': 'progress bar' }); }; /** * Update the seek bars tooltip and width. * * @param {EventTarget~Event} [event] * The `timeupdate` or `ended` event that caused this to run. * * @listens Player#timeupdate * @listens Player#ended */ SeekBar.prototype.updateProgress = function updateProgress(event) { 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((0, _computedStyle2['default'])(this.player().el(), 'width')); var tooltipWidth = parseFloat((0, _computedStyle2['default'])(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'; } }; /** * Update ARIA accessibility attributes * * @param {Element} el * The element to update with aria accessibility attributes. */ 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(); // machine readable value of progress bar (percentage complete) el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); // human readable value of progress bar (time complete) el.setAttribute('aria-valuetext', (0, _formatTime2['default'])(time, this.player_.duration())); }; /** * Get percentage of video played * * @return {number} * The percentage played */ SeekBar.prototype.getPercent = function getPercent() { var percent = this.player_.currentTime() / this.player_.duration(); return percent >= 1 ? 1 : percent; }; /** * Handle mouse down on seek bar * * @param {EventTarget~Event} event * The `mousedown` event that caused this to run. * * @listens mousedown */ 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 * * @param {EventTarget~Event} event * The `mousemove` event that caused this to run. * * @listens mousemove */ 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 * * @param {EventTarget~Event} event * The `mouseup` event that caused this to run. * * @listens mouseup */ 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 */ SeekBar.prototype.stepForward = function stepForward() { // more quickly fast forward for keyboard-only users this.player_.currentTime(this.player_.currentTime() + 5); }; /** * Move more quickly rewind for keyboard-only users */ SeekBar.prototype.stepBack = function stepBack() { // more quickly rewind for keyboard-only users this.player_.currentTime(this.player_.currentTime() - 5); }; return SeekBar; }(_slider2['default']); /** * Default options for the `SeekBar` * * @type {Object} * @private */ SeekBar.prototype.options_ = { children: ['loadProgressBar', 'mouseTimeDisplay', 'playProgressBar'], barName: 'playProgressBar' }; /** * Call the update event for this Slider when this event happens on the player. * * @type {string} */ SeekBar.prototype.playerEvent = 'timeupdate'; _component2['default'].registerComponent('SeekBar', SeekBar); exports['default'] = SeekBar; },{"../../component.js":224,"../../slider/slider.js":276,"../../utils/computed-style.js":299,"../../utils/fn.js":302,"../../utils/format-time.js":303,"./load-progress-bar.js":234,"./play-progress-bar.js":236,"./tooltip-progress-bar.js":239}],239:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _formatTime = require('../../utils/format-time.js'); var _formatTime2 = _interopRequireDefault(_formatTime); 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 _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; } /** * @file play-progress-bar.js */ /** * Shows play progress * * @extends Component */ var TooltipProgressBar = function (_Component) { _inherits(TooltipProgressBar, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function TooltipProgressBar(player, options) { _classCallCheck(this, TooltipProgressBar); var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _this.updateDataAttr(); _this.on(player, 'timeupdate', _this.updateDataAttr); player.ready(Fn.bind(_this, _this.updateDataAttr)); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ TooltipProgressBar.prototype.createEl = function createEl() { var el = _Component.prototype.createEl.call(this, 'div', { className: 'vjs-tooltip-progress-bar vjs-slider-bar', innerHTML: '
\n ' + this.localize('Progress') + ': 0%' }); this.tooltip = el.querySelector('.vjs-time-tooltip'); return el; }; /** * Updatet the data-current-time attribute for TooltipProgressBar * * @param {EventTarget~Event} [event] * The `timeupdate` event that caused this function to run. * * @listens Player#timeupdate */ TooltipProgressBar.prototype.updateDataAttr = function updateDataAttr(event) { var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime(); var formattedTime = (0, _formatTime2['default'])(time, this.player_.duration()); this.el_.setAttribute('data-current-time', formattedTime); this.tooltip.innerHTML = formattedTime; }; return TooltipProgressBar; }(_component2['default']); _component2['default'].registerComponent('TooltipProgressBar', TooltipProgressBar); exports['default'] = TooltipProgressBar; },{"../../component.js":224,"../../utils/fn.js":302,"../../utils/format-time.js":303}],240:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _spacer = require('./spacer.js'); var _spacer2 = _interopRequireDefault(_spacer); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file custom-control-spacer.js */ /** * Spacer specifically meant to be used as an insertion point for new plugins, etc. * * @extends Spacer */ var CustomControlSpacer = function (_Spacer) { _inherits(CustomControlSpacer, _Spacer); function CustomControlSpacer() { _classCallCheck(this, CustomControlSpacer); return _possibleConstructorReturn(this, _Spacer.apply(this, arguments)); } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this); }; /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ 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 = ' '; return el; }; return CustomControlSpacer; }(_spacer2['default']); _component2['default'].registerComponent('CustomControlSpacer', CustomControlSpacer); exports['default'] = CustomControlSpacer; },{"../../component.js":224,"./spacer.js":241}],241:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file spacer.js */ /** * 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 */ var Spacer = function (_Component) { _inherits(Spacer, _Component); function Spacer() { _classCallCheck(this, Spacer); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ Spacer.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this); }; /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ Spacer.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: this.buildCSSClass() }); }; return Spacer; }(_component2['default']); _component2['default'].registerComponent('Spacer', Spacer); exports['default'] = Spacer; },{"../../component.js":224}],242:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _textTrackMenuItem = require('./text-track-menu-item.js'); var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file caption-settings-menu-item.js */ /** * The menu item for caption track settings menu * * @extends TextTrackMenuItem */ var CaptionSettingsMenuItem = function (_TextTrackMenuItem) { _inherits(CaptionSettingsMenuItem, _TextTrackMenuItem); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function CaptionSettingsMenuItem(player, options) { _classCallCheck(this, CaptionSettingsMenuItem); options.track = { player: player, kind: options.kind, label: options.kind + ' settings', selectable: false, 'default': false, mode: 'disabled' }; // CaptionSettingsMenuItem has no concept of 'selected' options.selectable = false; var _this = _possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options)); _this.addClass('vjs-texttrack-settings'); _this.controlText(', opens ' + options.kind + ' settings dialog'); return _this; } /** * This gets called when an `CaptionSettingsMenuItem` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) { this.player().getChild('textTrackSettings').show(); this.player().getChild('textTrackSettings').el_.focus(); }; return CaptionSettingsMenuItem; }(_textTrackMenuItem2['default']); _component2['default'].registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem); exports['default'] = CaptionSettingsMenuItem; },{"../../component.js":224,"./text-track-menu-item.js":250}],243:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _textTrackButton = require('./text-track-button.js'); var _textTrackButton2 = _interopRequireDefault(_textTrackButton); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _captionSettingsMenuItem = require('./caption-settings-menu-item.js'); var _captionSettingsMenuItem2 = _interopRequireDefault(_captionSettingsMenuItem); 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; } /** * @file captions-button.js */ /** * The button component for toggling and selecting captions * * @extends TextTrackButton */ var CaptionsButton = function (_TextTrackButton) { _inherits(CaptionsButton, _TextTrackButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. * * @param {Component~ReadyCallback} [ready] * The function to call when this component is ready. */ function CaptionsButton(player, options, ready) { _classCallCheck(this, CaptionsButton); var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); _this.el_.setAttribute('aria-label', 'Captions Menu'); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ CaptionsButton.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); }; /** * Update caption menu items * * @param {EventTarget~Event} [event] * The `addtrack` or `removetrack` event that caused this function to be * called. * * @listens TextTrackList#addtrack * @listens TextTrackList#removetrack */ CaptionsButton.prototype.update = function update(event) { 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 {CaptionSettingsMenuItem[]} * The array of current menu items. */ CaptionsButton.prototype.createItems = function createItems() { var items = []; if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) { items.push(new _captionSettingsMenuItem2['default'](this.player_, { kind: this.kind_ })); } return _TextTrackButton.prototype.createItems.call(this, items); }; return CaptionsButton; }(_textTrackButton2['default']); /** * `kind` of TextTrack to look for to associate it with this menu. * * @type {string} * @private */ CaptionsButton.prototype.kind_ = 'captions'; /** * The text that should display over the `CaptionsButton`s controls. Added for localization. * * @type {string} * @private */ CaptionsButton.prototype.controlText_ = 'Captions'; _component2['default'].registerComponent('CaptionsButton', CaptionsButton); exports['default'] = CaptionsButton; },{"../../component.js":224,"./caption-settings-menu-item.js":242,"./text-track-button.js":249}],244:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _textTrackButton = require('./text-track-button.js'); var _textTrackButton2 = _interopRequireDefault(_textTrackButton); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _chaptersTrackMenuItem = require('./chapters-track-menu-item.js'); var _chaptersTrackMenuItem2 = _interopRequireDefault(_chaptersTrackMenuItem); var _toTitleCase = require('../../utils/to-title-case.js'); var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 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; } /** * @file chapters-button.js */ /** * 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 * * @extends TextTrackButton */ var ChaptersButton = function (_TextTrackButton) { _inherits(ChaptersButton, _TextTrackButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. * * @param {Component~ReadyCallback} [ready] * The function to call when this function is ready. */ function ChaptersButton(player, options, ready) { _classCallCheck(this, ChaptersButton); var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); _this.el_.setAttribute('aria-label', 'Chapters Menu'); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ ChaptersButton.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); }; /** * Update the menu based on the current state of its items. * * @param {EventTarget~Event} [event] * An event that triggered this function to run. * * @listens TextTrackList#addtrack * @listens TextTrackList#removetrack * @listens TextTrackList#change */ ChaptersButton.prototype.update = function update(event) { if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) { this.setTrack(this.findChaptersTrack()); } _TextTrackButton.prototype.update.call(this); }; /** * Set the currently selected track for the chapters button. * * @param {TextTrack} track * The new track to select. Nothing will change if this is the currently selected * track. */ ChaptersButton.prototype.setTrack = function setTrack(track) { if (this.track_ === track) { return; } if (!this.updateHandler_) { this.updateHandler_ = this.update.bind(this); } // here this.track_ refers to the old track instance if (this.track_) { var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_); if (remoteTextTrackEl) { remoteTextTrackEl.removeEventListener('load', this.updateHandler_); } this.track_ = null; } this.track_ = track; // here this.track_ refers to the new track instance if (this.track_) { this.track_.mode = 'hidden'; var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_); if (_remoteTextTrackEl) { _remoteTextTrackEl.addEventListener('load', this.updateHandler_); } } }; /** * Find the track object that is currently in use by this ChaptersButton * * @return {TextTrack|undefined} * The current track or undefined if none was found. */ ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() { var tracks = this.player_.textTracks() || []; for (var i = tracks.length - 1; i >= 0; i--) { // We will always choose the last track as our chaptersTrack var track = tracks[i]; if (track.kind === this.kind_) { return track; } } }; /** * Get the caption for the ChaptersButton based on the track label. This will also * use the current tracks localized kind as a fallback if a label does not exist. * * @return {string} * The tracks current label or the localized track kind. */ ChaptersButton.prototype.getMenuCaption = function getMenuCaption() { if (this.track_ && this.track_.label) { return this.track_.label; } return this.localize((0, _toTitleCase2['default'])(this.kind_)); }; /** * Create menu from chapter track * * @return {Menu} * New menu for the chapter buttons */ ChaptersButton.prototype.createMenu = function createMenu() { this.options_.title = this.getMenuCaption(); return _TextTrackButton.prototype.createMenu.call(this); }; /** * Create a menu item for each text track * * @return {TextTrackMenuItem[]} * Array of menu items */ ChaptersButton.prototype.createItems = function createItems() { var items = []; if (!this.track_) { return items; } var cues = this.track_.cues; if (!cues) { return items; } for (var i = 0, l = cues.length; i < l; i++) { var cue = cues[i]; var mi = new _chaptersTrackMenuItem2['default'](this.player_, { track: this.track_, cue: cue }); items.push(mi); } return items; }; return ChaptersButton; }(_textTrackButton2['default']); /** * `kind` of TextTrack to look for to associate it with this menu. * * @type {string} * @private */ ChaptersButton.prototype.kind_ = 'chapters'; /** * The text that should display over the `ChaptersButton`s controls. Added for localization. * * @type {string} * @private */ ChaptersButton.prototype.controlText_ = 'Chapters'; _component2['default'].registerComponent('ChaptersButton', ChaptersButton); exports['default'] = ChaptersButton; },{"../../component.js":224,"../../utils/to-title-case.js":310,"./chapters-track-menu-item.js":245,"./text-track-button.js":249}],245:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _menuItem = require('../../menu/menu-item.js'); var _menuItem2 = _interopRequireDefault(_menuItem); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); 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 _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; } /** * @file chapters-track-menu-item.js */ /** * The chapter track menu item * * @extends MenuItem */ var ChaptersTrackMenuItem = function (_MenuItem) { _inherits(ChaptersTrackMenuItem, _MenuItem); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ 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.selectable = true; options.label = cue.text; options.selected = cue.startTime <= currentTime && currentTime < cue.endTime; var _this = _possibleConstructorReturn(this, _MenuItem.call(this, player, options)); _this.track = track; _this.cue = cue; track.addEventListener('cuechange', Fn.bind(_this, _this.update)); return _this; } /** * This gets called when an `ChaptersTrackMenuItem` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) { _MenuItem.prototype.handleClick.call(this); this.player_.currentTime(this.cue.startTime); this.update(this.cue.startTime); }; /** * Update chapter menu item * * @param {EventTarget~Event} [event] * The `cuechange` event that caused this function to run. * * @listens TextTrack#cuechange */ ChaptersTrackMenuItem.prototype.update = function update(event) { var cue = this.cue; var currentTime = this.player_.currentTime(); // vjs.log(currentTime, cue.startTime); this.selected(cue.startTime <= currentTime && currentTime < cue.endTime); }; return ChaptersTrackMenuItem; }(_menuItem2['default']); _component2['default'].registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem); exports['default'] = ChaptersTrackMenuItem; },{"../../component.js":224,"../../menu/menu-item.js":267,"../../utils/fn.js":302}],246:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _textTrackButton = require('./text-track-button.js'); var _textTrackButton2 = _interopRequireDefault(_textTrackButton); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); 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 _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; } /** * @file descriptions-button.js */ /** * The button component for toggling and selecting descriptions * * @extends TextTrackButton */ var DescriptionsButton = function (_TextTrackButton) { _inherits(DescriptionsButton, _TextTrackButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. * * @param {Component~ReadyCallback} [ready] * The function to call when this component is ready. */ function DescriptionsButton(player, options, ready) { _classCallCheck(this, DescriptionsButton); var _this = _possibleConstructorReturn(this, _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); }); })(); } return _this; } /** * Handle text track change * * @param {EventTarget~Event} event * The event that caused this function to run * * @listens TextTrackList#change */ 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(); } }; /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); }; return DescriptionsButton; }(_textTrackButton2['default']); /** * `kind` of TextTrack to look for to associate it with this menu. * * @type {string} * @private */ DescriptionsButton.prototype.kind_ = 'descriptions'; /** * The text that should display over the `DescriptionsButton`s controls. Added for localization. * * @type {string} * @private */ DescriptionsButton.prototype.controlText_ = 'Descriptions'; _component2['default'].registerComponent('DescriptionsButton', DescriptionsButton); exports['default'] = DescriptionsButton; },{"../../component.js":224,"../../utils/fn.js":302,"./text-track-button.js":249}],247:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _textTrackMenuItem = require('./text-track-menu-item.js'); var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file off-text-track-menu-item.js */ /** * A special menu item for turning of a specific type of text track * * @extends TextTrackMenuItem */ var OffTextTrackMenuItem = function (_TextTrackMenuItem) { _inherits(OffTextTrackMenuItem, _TextTrackMenuItem); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function OffTextTrackMenuItem(player, options) { _classCallCheck(this, OffTextTrackMenuItem); // Create pseudo track info // Requires options['kind'] options.track = { player: player, kind: options.kind, label: options.kind + ' off', 'default': false, mode: 'disabled' }; // MenuItem is selectable options.selectable = true; var _this = _possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options)); _this.selected(true); return _this; } /** * Handle text track change * * @param {EventTarget~Event} event * The event that caused this function to run */ 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; }(_textTrackMenuItem2['default']); _component2['default'].registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem); exports['default'] = OffTextTrackMenuItem; },{"../../component.js":224,"./text-track-menu-item.js":250}],248:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _textTrackButton = require('./text-track-button.js'); var _textTrackButton2 = _interopRequireDefault(_textTrackButton); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file subtitles-button.js */ /** * The button component for toggling and selecting subtitles * * @extends TextTrackButton */ var SubtitlesButton = function (_TextTrackButton) { _inherits(SubtitlesButton, _TextTrackButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. * * @param {Component~ReadyCallback} [ready] * The function to call when this component is ready. */ function SubtitlesButton(player, options, ready) { _classCallCheck(this, SubtitlesButton); var _this = _possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready)); _this.el_.setAttribute('aria-label', 'Subtitles Menu'); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this); }; return SubtitlesButton; }(_textTrackButton2['default']); /** * `kind` of TextTrack to look for to associate it with this menu. * * @type {string} * @private */ SubtitlesButton.prototype.kind_ = 'subtitles'; /** * The text that should display over the `SubtitlesButton`s controls. Added for localization. * * @type {string} * @private */ SubtitlesButton.prototype.controlText_ = 'Subtitles'; _component2['default'].registerComponent('SubtitlesButton', SubtitlesButton); exports['default'] = SubtitlesButton; },{"../../component.js":224,"./text-track-button.js":249}],249:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _trackButton = require('../track-button.js'); var _trackButton2 = _interopRequireDefault(_trackButton); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _textTrackMenuItem = require('./text-track-menu-item.js'); var _textTrackMenuItem2 = _interopRequireDefault(_textTrackMenuItem); var _offTextTrackMenuItem = require('./off-text-track-menu-item.js'); var _offTextTrackMenuItem2 = _interopRequireDefault(_offTextTrackMenuItem); 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; } /** * @file text-track-button.js */ /** * The base class for buttons that toggle specific text track types (e.g. subtitles) * * @extends MenuButton */ var TextTrackButton = function (_TrackButton) { _inherits(TextTrackButton, _TrackButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options={}] * The key/value store of player options. */ function TextTrackButton(player) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, TextTrackButton); options.tracks = player.textTracks(); return _possibleConstructorReturn(this, _TrackButton.call(this, player, options)); } /** * Create a menu item for each text track * * @param {TextTrackMenuItem[]} [items=[]] * Existing array of items to use during creation * * @return {TextTrackMenuItem[]} * Array of menu items that were created */ 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 _offTextTrackMenuItem2['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 _textTrackMenuItem2['default'](this.player_, { track: track, // MenuItem is selectable selectable: true })); } } return items; }; return TextTrackButton; }(_trackButton2['default']); _component2['default'].registerComponent('TextTrackButton', TextTrackButton); exports['default'] = TextTrackButton; },{"../../component.js":224,"../track-button.js":255,"./off-text-track-menu-item.js":247,"./text-track-menu-item.js":250}],250:[function(require,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; }; var _menuItem = require('../../menu/menu-item.js'); var _menuItem2 = _interopRequireDefault(_menuItem); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); 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 _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; } /** * @file text-track-menu-item.js */ /** * The specific menu item type for selecting a language within a text track kind * * @extends MenuItem */ var TextTrackMenuItem = function (_MenuItem) { _inherits(TextTrackMenuItem, _MenuItem); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function TextTrackMenuItem(player, options) { _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'; var _this = _possibleConstructorReturn(this, _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 = void 0; _this.on(['tap', 'click'], function () { if (_typeof(_window2['default'].Event) !== 'object') { // Android 2.3 throws an Illegal Constructor error for window.Event try { event = new _window2['default'].Event('change'); } catch (err) { // continue regardless of error } } if (!event) { event = _document2['default'].createEvent('Event'); event.initEvent('change', true, true); } tracks.dispatchEvent(event); }); })(); } return _this; } /** * This gets called when an `TextTrackMenuItem` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} event * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ 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 list change * * @param {EventTarget~Event} event * The `change` event that caused this function to be called. * * @listens TextTrackList#change */ TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) { this.selected(this.track.mode === 'showing'); }; return TextTrackMenuItem; }(_menuItem2['default']); _component2['default'].registerComponent('TextTrackMenuItem', TextTrackMenuItem); exports['default'] = TextTrackMenuItem; },{"../../component.js":224,"../../menu/menu-item.js":267,"../../utils/fn.js":302,"global/document":193,"global/window":194}],251:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _formatTime = require('../../utils/format-time.js'); var _formatTime2 = _interopRequireDefault(_formatTime); 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 _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; } /** * @file current-time-display.js */ /** * Displays the current time * * @extends Component */ var CurrentTimeDisplay = function (_Component) { _inherits(CurrentTimeDisplay, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function CurrentTimeDisplay(player, options) { _classCallCheck(this, CurrentTimeDisplay); var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _this.on(player, 'timeupdate', _this.updateContent); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ 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: 'Current Time ' + '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 * * @param {EventTarget~Event} [event] * The `timeupdate` event that caused this function to run. * * @listens Player#timeupdate */ CurrentTimeDisplay.prototype.updateContent = function updateContent(event) { // 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 = (0, _formatTime2['default'])(time, this.player_.duration()); if (formattedTime !== this.formattedTime_) { this.formattedTime_ = formattedTime; this.contentEl_.innerHTML = '' + localizedText + ' ' + formattedTime; } }; return CurrentTimeDisplay; }(_component2['default']); _component2['default'].registerComponent('CurrentTimeDisplay', CurrentTimeDisplay); exports['default'] = CurrentTimeDisplay; },{"../../component.js":224,"../../utils/dom.js":300,"../../utils/format-time.js":303}],252:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _formatTime = require('../../utils/format-time.js'); var _formatTime2 = _interopRequireDefault(_formatTime); 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 _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; } /** * @file duration-display.js */ /** * Displays the duration * * @extends Component */ var DurationDisplay = function (_Component) { _inherits(DurationDisplay, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function DurationDisplay(player, options) { _classCallCheck(this, DurationDisplay); var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _this.on(player, 'durationchange', _this.updateContent); // Also listen for timeupdate and loadedmetadata because removing those // listeners could have broken dependent applications/libraries. These // can likely be removed for 6.0. _this.on(player, 'timeupdate', _this.updateContent); _this.on(player, 'loadedmetadata', _this.updateContent); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ 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: '' + this.localize('Duration Time') + ' 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. * * @param {EventTarget~Event} [event] * The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused * this function to be called. * * @listens Player#durationchange * @listens Player#timeupdate * @listens Player#loadedmetadata */ DurationDisplay.prototype.updateContent = function updateContent(event) { var duration = this.player_.duration(); if (duration && this.duration_ !== duration) { this.duration_ = duration; var localizedText = this.localize('Duration Time'); var formattedTime = (0, _formatTime2['default'])(duration); // label the duration time for screen reader users this.contentEl_.innerHTML = '' + localizedText + ' ' + formattedTime; } }; return DurationDisplay; }(_component2['default']); _component2['default'].registerComponent('DurationDisplay', DurationDisplay); exports['default'] = DurationDisplay; },{"../../component.js":224,"../../utils/dom.js":300,"../../utils/format-time.js":303}],253:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _formatTime = require('../../utils/format-time.js'); var _formatTime2 = _interopRequireDefault(_formatTime); 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 _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; } /** * @file remaining-time-display.js */ /** * Displays the time left in the video * * @extends Component */ var RemainingTimeDisplay = function (_Component) { _inherits(RemainingTimeDisplay, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function RemainingTimeDisplay(player, options) { _classCallCheck(this, RemainingTimeDisplay); var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _this.on(player, 'timeupdate', _this.updateContent); _this.on(player, 'durationchange', _this.updateContent); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ 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: '' + this.localize('Remaining Time') + ' -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. * * @param {EventTarget~Event} [event] * The `timeupdate` or `durationchange` event that caused this to run. * * @listens Player#timeupdate * @listens Player#durationchange */ RemainingTimeDisplay.prototype.updateContent = function updateContent(event) { if (this.player_.duration()) { var localizedText = this.localize('Remaining Time'); var formattedTime = (0, _formatTime2['default'])(this.player_.remainingTime()); if (formattedTime !== this.formattedTime_) { this.formattedTime_ = formattedTime; this.contentEl_.innerHTML = '' + localizedText + ' -' + 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; }(_component2['default']); _component2['default'].registerComponent('RemainingTimeDisplay', RemainingTimeDisplay); exports['default'] = RemainingTimeDisplay; },{"../../component.js":224,"../../utils/dom.js":300,"../../utils/format-time.js":303}],254:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file time-divider.js */ /** * The separator between the current time and duration. * Can be hidden if it's not needed in the design. * * @extends Component */ var TimeDivider = function (_Component) { _inherits(TimeDivider, _Component); function TimeDivider() { _classCallCheck(this, TimeDivider); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } /** * Create the component's DOM element * * @return {Element} * The element that was created. */ TimeDivider.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-time-control vjs-time-divider', innerHTML: '
/
' }); }; return TimeDivider; }(_component2['default']); _component2['default'].registerComponent('TimeDivider', TimeDivider); exports['default'] = TimeDivider; },{"../../component.js":224}],255:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _menuButton = require('../menu/menu-button.js'); var _menuButton2 = _interopRequireDefault(_menuButton); var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); 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 _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; } /** * @file track-button.js */ /** * The base class for buttons that toggle specific track types (e.g. subtitles). * * @extends MenuButton */ var TrackButton = function (_MenuButton) { _inherits(TrackButton, _MenuButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function TrackButton(player, options) { _classCallCheck(this, TrackButton); var tracks = options.tracks; var _this = _possibleConstructorReturn(this, _MenuButton.call(this, player, options)); if (_this.items.length <= 1) { _this.hide(); } if (!tracks) { return _possibleConstructorReturn(_this); } 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 _this; } return TrackButton; }(_menuButton2['default']); _component2['default'].registerComponent('TrackButton', TrackButton); exports['default'] = TrackButton; },{"../component.js":224,"../menu/menu-button.js":266,"../utils/fn.js":302}],256:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _slider = require('../../slider/slider.js'); var _slider2 = _interopRequireDefault(_slider); var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('../../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); require('./volume-level.js'); 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 _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; } /** * @file volume-bar.js */ // Required children /** * The bar that contains the volume level and can be clicked on to adjust the level * * @extends Slider */ var VolumeBar = function (_Slider) { _inherits(VolumeBar, _Slider); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function VolumeBar(player, options) { _classCallCheck(this, VolumeBar); var _this = _possibleConstructorReturn(this, _Slider.call(this, player, options)); _this.on(player, 'volumechange', _this.updateARIAAttributes); player.ready(Fn.bind(_this, _this.updateARIAAttributes)); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ VolumeBar.prototype.createEl = function createEl() { return _Slider.prototype.createEl.call(this, 'div', { className: 'vjs-volume-bar vjs-slider-bar' }, { 'aria-label': 'volume level' }); }; /** * Handle movement events on the {@link VolumeMenuButton}. * * @param {EventTarget~Event} event * The event that caused this function to run. * * @listens mousemove */ VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) { this.checkMuted(); this.player_.volume(this.calculateDistance(event)); }; /** * If the player is muted unmute it. */ VolumeBar.prototype.checkMuted = function checkMuted() { if (this.player_.muted()) { this.player_.muted(false); } }; /** * Get percent of volume level * * @return {number} * Volume level percent as a decimal number. */ VolumeBar.prototype.getPercent = function getPercent() { if (this.player_.muted()) { return 0; } return this.player_.volume(); }; /** * Increase volume level for keyboard users */ VolumeBar.prototype.stepForward = function stepForward() { this.checkMuted(); this.player_.volume(this.player_.volume() + 0.1); }; /** * Decrease volume level for keyboard users */ VolumeBar.prototype.stepBack = function stepBack() { this.checkMuted(); this.player_.volume(this.player_.volume() - 0.1); }; /** * Update ARIA accessibility attributes * * @param {EventTarget~Event} [event] * The `volumechange` event that caused this function to run. * * @listens Player#volumechange */ VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) { // 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; }(_slider2['default']); /** * Default options for the `VolumeBar` * * @type {Object} * @private */ VolumeBar.prototype.options_ = { children: ['volumeLevel'], barName: 'volumeLevel' }; /** * Call the update event for this Slider when this event happens on the player. * * @type {string} */ VolumeBar.prototype.playerEvent = 'volumechange'; _component2['default'].registerComponent('VolumeBar', VolumeBar); exports['default'] = VolumeBar; },{"../../component.js":224,"../../slider/slider.js":276,"../../utils/fn.js":302,"./volume-level.js":258}],257:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); require('./volume-bar.js'); 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; } /** * @file volume-control.js */ // Required children /** * The component for controlling the volume level * * @extends Component */ var VolumeControl = function (_Component) { _inherits(VolumeControl, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options={}] * The key/value store of player options. */ function VolumeControl(player, options) { _classCallCheck(this, VolumeControl); // hide volume controls when they're not supported by the current tech var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); 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'); } }); return _this; } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ VolumeControl.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-volume-control vjs-control' }); }; return VolumeControl; }(_component2['default']); /** * Default options for the `VolumeControl` * * @type {Object} * @private */ VolumeControl.prototype.options_ = { children: ['volumeBar'] }; _component2['default'].registerComponent('VolumeControl', VolumeControl); exports['default'] = VolumeControl; },{"../../component.js":224,"./volume-bar.js":256}],258:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file volume-level.js */ /** * Shows volume level * * @extends Component */ var VolumeLevel = function (_Component) { _inherits(VolumeLevel, _Component); function VolumeLevel() { _classCallCheck(this, VolumeLevel); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } /** * Create the `Component`'s DOM element * * @return {Element} * The element that was created. */ VolumeLevel.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-volume-level', innerHTML: '' }); }; return VolumeLevel; }(_component2['default']); _component2['default'].registerComponent('VolumeLevel', VolumeLevel); exports['default'] = VolumeLevel; },{"../../component.js":224}],259:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); var _popup = require('../popup/popup.js'); var _popup2 = _interopRequireDefault(_popup); var _popupButton = require('../popup/popup-button.js'); var _popupButton2 = _interopRequireDefault(_popupButton); var _muteToggle = require('./mute-toggle.js'); var _muteToggle2 = _interopRequireDefault(_muteToggle); var _volumeBar = require('./volume-control/volume-bar.js'); var _volumeBar2 = _interopRequireDefault(_volumeBar); 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 _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; } /** * @file volume-menu-button.js */ /** * Button for volume popup * * @extends PopupButton */ var VolumeMenuButton = function (_PopupButton) { _inherits(VolumeMenuButton, _PopupButton); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options={}] * The key/value store of player options. */ 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; // Same listeners as MuteToggle var _this = _possibleConstructorReturn(this, _PopupButton.call(this, player, options)); _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() { if (player.tech_ && player.tech_.featuresVolumeControl === false) { this.addClass('vjs-hidden'); } else { this.removeClass('vjs-hidden'); } } 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'); }); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ 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; }; /** * Create the VolumeMenuButton popup * * @return {Popup} * The popup that was created */ VolumeMenuButton.prototype.createPopup = function createPopup() { var popup = new _popup2['default'](this.player_, { contentElType: 'div' }); var vb = new _volumeBar2['default'](this.player_, this.options_.volumeBar); popup.addChild(vb); this.menuContent = popup; this.volumeBar = vb; this.attachVolumeBarEvents(); return popup; }; /** * This gets called when an `VolumeMenuButton` is "clicked". See * {@link ClickableComponent} for more detailed information on what a click can be. * * @param {EventTarget~Event} [event] * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ VolumeMenuButton.prototype.handleClick = function handleClick(event) { _muteToggle2['default'].prototype.handleClick.call(this); _PopupButton.prototype.handleClick.call(this); }; /** * Add events listeners to the created `VolumeBar`. */ VolumeMenuButton.prototype.attachVolumeBarEvents = function attachVolumeBarEvents() { this.menuContent.on(['mousedown', 'touchdown'], Fn.bind(this, this.handleMouseDown)); }; /** * Handle the `mousedown` and `touchdown` events on the `VolumeBar` * * @param {EventTarget~Event} [event] * The `mousedown` or `touchdown` event that caused this to run. * * @listens mousedown * @listens touchdown */ 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); }; /** * Handle the `mouseup` and `touchend` events on the `VolumeBar` * * @param {EventTarget~Event} [event] * The `mouseup` or `touchend` event that caused this to run. * * @listens mouseup * @listens touchend */ VolumeMenuButton.prototype.handleMouseUp = function handleMouseUp(event) { this.off(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove)); }; return VolumeMenuButton; }(_popupButton2['default']); /** * @borrows MuteToggle#update as VolumeMenuButton#volumeUpdate */ VolumeMenuButton.prototype.volumeUpdate = _muteToggle2['default'].prototype.update; /** * The text that should display over the `VolumeMenuButton`s controls. Added for localization. * * @type {string} * @private */ VolumeMenuButton.prototype.controlText_ = 'Mute'; _component2['default'].registerComponent('VolumeMenuButton', VolumeMenuButton); exports['default'] = VolumeMenuButton; },{"../component.js":224,"../popup/popup-button.js":272,"../popup/popup.js":273,"../utils/fn.js":302,"./mute-toggle.js":230,"./volume-control/volume-bar.js":256}],260:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('./component'); var _component2 = _interopRequireDefault(_component); var _modalDialog = require('./modal-dialog'); var _modalDialog2 = _interopRequireDefault(_modalDialog); var _mergeOptions = require('./utils/merge-options'); var _mergeOptions2 = _interopRequireDefault(_mergeOptions); 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; } /** * @file error-display.js */ /** * A display that indicates an error has occurred. This means that the video * is unplayable. * * @extends ModalDialog */ var ErrorDisplay = function (_ModalDialog) { _inherits(ErrorDisplay, _ModalDialog); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function ErrorDisplay(player, options) { _classCallCheck(this, ErrorDisplay); var _this = _possibleConstructorReturn(this, _ModalDialog.call(this, player, options)); _this.on(player, 'error', _this.open); return _this; } /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. * * @deprecated Since version 5. */ ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() { return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this); }; /** * Gets the localized error message based on the `Player`s error. * * @return {string} * The `Player`s error message localized or an empty string. */ ErrorDisplay.prototype.content = function content() { var error = this.player().error(); return error ? this.localize(error.message) : ''; }; return ErrorDisplay; }(_modalDialog2['default']); /** * The default options for an `ErrorDisplay`. * * @private */ ErrorDisplay.prototype.options_ = (0, _mergeOptions2['default'])(_modalDialog2['default'].prototype.options_, { fillAlways: true, temporary: false, uncloseable: true }); _component2['default'].registerComponent('ErrorDisplay', ErrorDisplay); exports['default'] = ErrorDisplay; },{"./component":224,"./modal-dialog":269,"./utils/merge-options":306}],261:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _events = require('./utils/events.js'); var Events = _interopRequireWildcard(_events); 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; } } /** * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It * adds shorthand functions that wrap around lengthy functions. For example: * the `on` function is a wrapper around `addEventListener`. * * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget} * @class EventTarget */ var EventTarget = function EventTarget() {}; /** * A Custom DOM event. * * @typedef {Object} EventTarget~Event * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent} */ /** * All event listeners should follow the following format. * * @callback EventTarget~EventListener * @this {EventTarget} * * @param {EventTarget~Event} event * the event that triggered this function * * @param {Object} [hash] * hash of data sent during the event */ /** * An object containing event names as keys and booleans as values. * * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger} * will have extra functionality. See that function for more information. * * @property EventTarget.prototype.allowedEvents_ * @private */ /** * @file src/js/event-target.js */ EventTarget.prototype.allowedEvents_ = {}; /** * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a * function that will get called when an event with a certain name gets triggered. * * @param {string|string[]} type * An event name or an array of event names. * * @param {EventTarget~EventListener} fn * The function to call with `EventTarget`s */ 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; }; /** * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic * the standard DOM API. * * @function * @see {@link EventTarget#on} */ EventTarget.prototype.addEventListener = EventTarget.prototype.on; /** * Removes an `event listener` for a specific event from an instance of `EventTarget`. * This makes it so that the `event listener` will no longer get called when the * named event happens. * * @param {string|string[]} type * An event name or an array of event names. * * @param {EventTarget~EventListener} fn * The function to remove. */ EventTarget.prototype.off = function (type, fn) { Events.off(this, type, fn); }; /** * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic * the standard DOM API. * * @function * @see {@link EventTarget#off} */ EventTarget.prototype.removeEventListener = EventTarget.prototype.off; /** * This function will add an `event listener` that gets triggered only once. After the * first trigger it will get removed. This is like adding an `event listener` * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself. * * @param {string|string[]} type * An event name or an array of event names. * * @param {EventTarget~EventListener} fn * The function to be called once for each event name. */ EventTarget.prototype.one = function (type, fn) { // Remove the addEventListener alialing 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; }; /** * This function causes an event to happen. This will then cause any `event listeners` * that are waiting for that event, to get called. If there are no `event listeners` * for an event then nothing will happen. * * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`. * Trigger will also call the `on` + `uppercaseEventName` function. * * Example: * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call * `onClick` if it exists. * * @param {string|EventTarget~Event|Object} event * The name of the event, an `Event`, or an object with a key of type set to * an event name. */ 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); }; /** * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic * the standard DOM API. * * @function * @see {@link EventTarget#trigger} */ EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger; exports['default'] = EventTarget; },{"./utils/events.js":301}],262:[function(require,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; }; var _log = require('./utils/log'); var _log2 = _interopRequireDefault(_log); var _obj = require('./utils/obj'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } /** * @file extend.js * @module extend */ /** * 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. * * @param {Object} subClass * The class to inherit to * * @param {Object} superClass * The class to inherit from * * @private */ 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 === 'undefined' ? 'undefined' : _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 * * @param {Object} superClass * The class to inherit from * * @param {Object} [subClassMethods={}] * The class to inherit to * * @return {Object} * The new object with subClassMethods that inherited superClass. */ 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 ((0, _obj.isObject)(subClassMethods)) { if (typeof subClassMethods.init === 'function') { _log2['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; },{"./utils/log":305,"./utils/obj":307}],263:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } /** * Store the browser-specific methods for the fullscreen API. * * @type {Object} * @see [Specification]{@link https://fullscreen.spec.whatwg.org} * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js} */ var FullscreenApi = {}; // browser API methods /** * @file fullscreen-api.js * @module fullscreen-api * @private */ var apiMap = [['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 = void 0; // determine the supported set of functions for (var i = 0; i < apiMap.length; i++) { // check for exitFullscreen function if (apiMap[i][1] in _document2['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; },{"global/document":193}],264:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('./component'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file loading-spinner.js */ /** * A loading spinner for use during waiting/loading events. * * @extends Component */ var LoadingSpinner = function (_Component) { _inherits(LoadingSpinner, _Component); function LoadingSpinner() { _classCallCheck(this, LoadingSpinner); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } /** * Create the `LoadingSpinner`s DOM element. * * @return {Element} * The dom element that gets created. */ 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; },{"./component":224}],265:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _obj = require('./utils/obj'); /** * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class. * * @param {number|string|Object|MediaError} value * This can be of multiple types: * - number: should be a standard error code * - string: an error message (the code will be 0) * - Object: arbitrary properties * - `MediaError` (native): used to populate a video.js `MediaError` object * - `MediaError` (video.js): will return itself if it's already a * video.js `MediaError` object. * * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror} * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes} * * @class MediaError */ function MediaError(value) { // Allow redundant calls to this constructor to avoid having `instanceof` // checks peppered around the code. if (value instanceof MediaError) { return value; } if (typeof value === 'number') { this.code = value; } else if (typeof value === 'string') { // default code is zero, so this is a custom error this.message = value; } else if ((0, _obj.isObject)(value)) { // We assign the `code` property manually because native `MediaError` objects // do not expose it as an own/enumerable property of the object. if (typeof value.code === 'number') { this.code = value.code; } (0, _obj.assign)(this, value); } if (!this.message) { this.message = MediaError.defaultMessages[this.code] || ''; } } /** * The error code that refers two one of the defined `MediaError` types * * @type {Number} */ /** * @file media-error.js */ MediaError.prototype.code = 0; /** * An optional message that to show 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 a plugin might provide a specific HTTP status code and an * error message for that code. Then when the plugin gets that error this class will * know how to display an error message for it. This allows a custom message to show * up on the `Player` error overlay. * * @type {Array} */ MediaError.prototype.status = null; /** * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the * specification listed under {@link MediaError} for more information. * * @enum {array} * @readonly * @property {string} 0 - MEDIA_ERR_CUSTOM * @property {string} 1 - MEDIA_ERR_CUSTOM * @property {string} 2 - MEDIA_ERR_ABORTED * @property {string} 3 - MEDIA_ERR_NETWORK * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED * @property {string} 5 - MEDIA_ERR_ENCRYPTED */ MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED']; /** * The default `MediaError` messages based on the {@link MediaError.errorTypes}. * * @type {Array} * @constant */ 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; } // jsdocs for instance/static members added above // instance methods use `#` and static methods use `.` /** * W3C error code for any custom error. * * @member MediaError#MEDIA_ERR_CUSTOM * @constant {number} * @default 0 */ /** * W3C error code for any custom error. * * @member MediaError.MEDIA_ERR_CUSTOM * @constant {number} * @default 0 */ /** * W3C error code for media error aborted. * * @member MediaError#MEDIA_ERR_ABORTED * @constant {number} * @default 1 */ /** * W3C error code for media error aborted. * * @member MediaError.MEDIA_ERR_ABORTED * @constant {number} * @default 1 */ /** * W3C error code for any network error. * * @member MediaError#MEDIA_ERR_NETWORK * @constant {number} * @default 2 */ /** * W3C error code for any network error. * * @member MediaError.MEDIA_ERR_NETWORK * @constant {number} * @default 2 */ /** * W3C error code for any decoding error. * * @member MediaError#MEDIA_ERR_DECODE * @constant {number} * @default 3 */ /** * W3C error code for any decoding error. * * @member MediaError.MEDIA_ERR_DECODE * @constant {number} * @default 3 */ /** * W3C error code for any time that a source is not supported. * * @member MediaError#MEDIA_ERR_SRC_NOT_SUPPORTED * @constant {number} * @default 4 */ /** * W3C error code for any time that a source is not supported. * * @member MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED * @constant {number} * @default 4 */ /** * W3C error code for any time that a source is encrypted. * * @member MediaError#MEDIA_ERR_ENCRYPTED * @constant {number} * @default 5 */ /** * W3C error code for any time that a source is encrypted. * * @member MediaError.MEDIA_ERR_ENCRYPTED * @constant {number} * @default 5 */ exports['default'] = MediaError; },{"./utils/obj":307}],266:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _clickableComponent = require('../clickable-component.js'); var _clickableComponent2 = _interopRequireDefault(_clickableComponent); var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); var _menu = require('./menu.js'); var _menu2 = _interopRequireDefault(_menu); var _dom = require('../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _toTitleCase = require('../utils/to-title-case.js'); var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 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 _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; } /** * @file menu-button.js */ /** * A `MenuButton` class for any popup {@link Menu}. * * @extends ClickableComponent */ var MenuButton = function (_ClickableComponent) { _inherits(MenuButton, _ClickableComponent); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options={}] * The key/value store of player options. */ function MenuButton(player) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, MenuButton); var _this = _possibleConstructorReturn(this, _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); return _this; } /** * Update the menu based on the current state of its items. */ 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 the menu and add all items to it. * * @return {Menu} * The constructed menu */ MenuButton.prototype.createMenu = function createMenu() { var menu = new _menu2['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: (0, _toTitleCase2['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. * * @abstract */ MenuButton.prototype.createItems = function createItems() {}; /** * Create the `MenuButtons`s DOM element. * * @return {Element} * The element that gets created. */ MenuButton.prototype.createEl = function createEl() { return _ClickableComponent.prototype.createEl.call(this, 'div', { className: this.buildCSSClass() }); }; /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ 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); }; /** * Handle a click on a `MenuButton`. * See {@link ClickableComponent#handleClick} for instances where this is called. * * @param {EventTarget~Event} event * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ MenuButton.prototype.handleClick = function handleClick(event) { // When you click the button it adds focus, which will show the menu. // So we'll remove focus when the mouse leaves the button. Focus is needed // for tab navigation. this.one(this.menu.contentEl(), 'mouseleave', Fn.bind(this, function (e) { this.unpressButton(); this.el_.blur(); })); if (this.buttonPressed_) { this.unpressButton(); } else { this.pressButton(); } }; /** * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See * {@link ClickableComponent#handleKeyPress} for instances where this is called. * * @param {EventTarget~Event} event * The `keydown` event that caused this function to be called. * * @listens keydown */ 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 a `keydown` event on a sub-menu. The listener for this is added in * the constructor. * * @param {EventTarget~Event} event * Key press event * * @listens keydown */ 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(); } } }; /** * Put the current `MenuButton` into a pressed state. */ MenuButton.prototype.pressButton = function pressButton() { if (this.enabled_) { this.buttonPressed_ = true; this.menu.lockShowing(); this.el_.setAttribute('aria-expanded', 'true'); // set the focus into the submenu this.menu.focus(); } }; /** * Take the current `MenuButton` out of a pressed state. */ MenuButton.prototype.unpressButton = function unpressButton() { if (this.enabled_) { this.buttonPressed_ = false; this.menu.unlockShowing(); this.el_.setAttribute('aria-expanded', 'false'); // Set focus back to this menu button this.el_.focus(); } }; /** * Disable the `MenuButton`. Don't allow it to be clicked. * * @return {MenuButton} * Returns itself; method can be chained. */ 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 `MenuButton`. Allow it to be clicked. * * @return {MenuButton} * Returns itself; method can be chained. */ MenuButton.prototype.enable = function enable() { this.enabled_ = true; return _ClickableComponent.prototype.enable.call(this); }; return MenuButton; }(_clickableComponent2['default']); _component2['default'].registerComponent('MenuButton', MenuButton); exports['default'] = MenuButton; },{"../clickable-component.js":222,"../component.js":224,"../utils/dom.js":300,"../utils/fn.js":302,"../utils/to-title-case.js":310,"./menu.js":268}],267:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _clickableComponent = require('../clickable-component.js'); var _clickableComponent2 = _interopRequireDefault(_clickableComponent); var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); var _obj = require('../utils/obj'); 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; } /** * @file menu-item.js */ /** * The component for a menu item. `
  • ` * * @extends ClickableComponent */ var MenuItem = function (_ClickableComponent) { _inherits(MenuItem, _ClickableComponent); /** * Creates an instance of the this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options={}] * The key/value store of player options. * */ function MenuItem(player, options) { _classCallCheck(this, MenuItem); var _this = _possibleConstructorReturn(this, _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'); } return _this; } /** * Create the `MenuItem's DOM element * * @param {string} [type=li] * Element's node type, not actually used, always set to `li`. * * @param {Object} [props={}] * An object of properties that should be set on the element * * @param {Object} [attrs={}] * An object of attributes that should be set on the element * * @return {Element} * The element that gets created. */ MenuItem.prototype.createEl = function createEl(type, props, attrs) { return _ClickableComponent.prototype.createEl.call(this, 'li', (0, _obj.assign)({ className: 'vjs-menu-item', innerHTML: this.localize(this.options_.label), tabIndex: -1 }, props), attrs); }; /** * Any click on a `MenuItem` puts int into the selected state. * See {@link ClickableComponent#handleClick} for instances where this is called. * * @param {EventTarget~Event} event * The `keydown`, `tap`, or `click` event that caused this function to be * called. * * @listens tap * @listens click */ MenuItem.prototype.handleClick = function handleClick(event) { this.selected(true); }; /** * Set the state for this menu item as selected or not. * * @param {boolean} selected * if the menu item is selected or not */ 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 MenuItem; }(_clickableComponent2['default']); _component2['default'].registerComponent('MenuItem', MenuItem); exports['default'] = MenuItem; },{"../clickable-component.js":222,"../component.js":224,"../utils/obj":307}],268:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _events = require('../utils/events.js'); var Events = _interopRequireWildcard(_events); 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 _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; } /** * @file menu.js */ /** * The Menu component is used to build popup menus, including subtitle and * captions selection menus. * * @extends Component */ var Menu = function (_Component) { _inherits(Menu, _Component); /** * Create an instance of this class. * * @param {Player} player * the player that this component should attach to * * @param {Object} [options] * Object of option names and values * */ function Menu(player, options) { _classCallCheck(this, Menu); var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _this.focusedChild_ = -1; _this.on('keydown', _this.handleKeyPress); return _this; } /** * Add a {@link MenuItem} to the menu. * * @param {Object|string} component * The name or instance of the `MenuItem` to add. * */ Menu.prototype.addItem = function addItem(component) { this.addChild(component); component.on('click', Fn.bind(this, function (event) { this.unlockShowing(); // TODO: Need to set keyboard focus back to the menuButton })); }; /** * Create the `Menu`s DOM element. * * @return {Element} * the element that was created */ 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 a `keydown` event on this menu. This listener is added in the constructor. * * @param {EventTarget~Event} event * A `keydown` event that happened on the menu. * * @listens keydown */ Menu.prototype.handleKeyPress = function handleKeyPress(event) { // Left and Down Arrows if (event.which === 37 || event.which === 40) { event.preventDefault(); this.stepForward(); // Up and Right Arrows } else if (event.which === 38 || event.which === 39) { event.preventDefault(); this.stepBack(); } }; /** * Move to next (lower) menu item for keyboard users. */ 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. */ Menu.prototype.stepBack = function stepBack() { var stepChild = 0; if (this.focusedChild_ !== undefined) { stepChild = this.focusedChild_ - 1; } this.focus(stepChild); }; /** * Set focus on a {@link MenuItem} in the `Menu`. * * @param {Object|string} [item=0] * Index of child item set focus on. */ Menu.prototype.focus = function focus() { var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 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; }(_component2['default']); _component2['default'].registerComponent('Menu', Menu); exports['default'] = Menu; },{"../component.js":224,"../utils/dom.js":300,"../utils/events.js":301,"../utils/fn.js":302}],269:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _dom = require('./utils/dom'); var Dom = _interopRequireWildcard(_dom); var _fn = require('./utils/fn'); var Fn = _interopRequireWildcard(_fn); var _component = require('./component'); var _component2 = _interopRequireDefault(_component); 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 _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; } /** * @file modal-dialog.js */ 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 */ var ModalDialog = function (_Component) { _inherits(ModalDialog, _Component); /** * Create an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player 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); var _this = _possibleConstructorReturn(this, _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_); return _this; } /** * Create the `ModalDialog`'s DOM element * * @return {Element} * The DOM element that gets created. */ 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' }); }; /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ ModalDialog.prototype.buildCSSClass = function buildCSSClass() { return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this); }; /** * Handles `keydown` events on the document, looking for ESC, which closes * the modal. * * @param {EventTarget~Event} e * The keypress that triggered this event. * * @listens keydown */ 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} * the localized or raw label of this modal. */ 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} * The localized or raw description of this modal. */ 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. * * @fires ModalDialog#beforemodalopen * @fires ModalDialog#modalopen * * @return {ModalDialog} * Returns itself; method can be chained. */ ModalDialog.prototype.open = function open() { if (!this.opened_) { var player = this.player(); /** * Fired just before a `ModalDialog` is opened. * * @event ModalDialog#beforemodalopen * @type {EventTarget~Event} */ 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'); /** * Fired just after a `ModalDialog` is opened. * * @event ModalDialog#modalopen * @type {EventTarget~Event} */ this.trigger('modalopen'); this.hasBeenOpened_ = true; } return this; }; /** * If the `ModalDialog` is currently open or closed. * * @param {boolean} [value] * If given, it will open (`true`) or close (`false`) the modal. * * @return {boolean} * the current open state of the modaldialog */ ModalDialog.prototype.opened = function opened(value) { if (typeof value === 'boolean') { this[value ? 'open' : 'close'](); } return this.opened_; }; /** * Closes the modal, does nothing if the `ModalDialog` is * not open. * * @fires ModalDialog#beforemodalclose * @fires ModalDialog#modalclose * * @return {ModalDialog} * Returns itself; method can be chained. */ ModalDialog.prototype.close = function close() { if (this.opened_) { var player = this.player(); /** * Fired just before a `ModalDialog` is closed. * * @event ModalDialog#beforemodalclose * @type {EventTarget~Event} */ 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'); /** * Fired just after a `ModalDialog` is closed. * * @event ModalDialog#modalclose * @type {EventTarget~Event} */ this.trigger('modalclose'); if (this.options_.temporary) { this.dispose(); } } return this; }; /** * Check to see if the `ModalDialog` is closeable via the UI. * * @param {boolean} [value] * If given as a boolean, it will set the `closeable` option. * * @return {boolean} * Returns the final value of the closable option. */ 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', { controlText: 'Close Modal Dialog' }); 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. * * @return {ModalDialog} * Returns itself; method can be chained. */ 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. * * @fires ModalDialog#beforemodalfill * @fires ModalDialog#modalfill * * @param {Mixed} [content] * The same rules apply to this as apply to the `content` option. * * @return {ModalDialog} * Returns itself; method can be chained. */ ModalDialog.prototype.fillWith = function fillWith(content) { var contentEl = this.contentEl(); var parentEl = contentEl.parentNode; var nextSiblingEl = contentEl.nextSibling; /** * Fired just before a `ModalDialog` is filled with content. * * @event ModalDialog#beforemodalfill * @type {EventTarget~Event} */ 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); /** * Fired just after a `ModalDialog` is filled with content. * * @event ModalDialog#modalfill * @type {EventTarget~Event} */ 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 anytime the modal is filled. * * @fires ModalDialog#beforemodalempty * @fires ModalDialog#modalempty * * @return {ModalDialog} * Returns itself; method can be chained. */ ModalDialog.prototype.empty = function empty() { /** * Fired just before a `ModalDialog` is emptied. * * @event ModalDialog#beforemodalempty * @type {EventTarget~Event} */ this.trigger('beforemodalempty'); Dom.emptyEl(this.contentEl()); /** * Fired just after a `ModalDialog` is emptied. * * @event ModalDialog#modalempty * @type {EventTarget~Event} */ 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. * * @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} * The current content of the modal dialog */ ModalDialog.prototype.content = function content(value) { if (typeof value !== 'undefined') { this.content_ = value; } return this.content_; }; return ModalDialog; }(_component2['default']); /** * Default options for `ModalDialog` default options. * * @type {Object} * @private */ ModalDialog.prototype.options_ = { temporary: true }; _component2['default'].registerComponent('ModalDialog', ModalDialog); exports['default'] = ModalDialog; },{"./component":224,"./utils/dom":300,"./utils/fn":302}],270:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('./component.js'); var _component2 = _interopRequireDefault(_component); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); var _events = require('./utils/events.js'); var Events = _interopRequireWildcard(_events); var _dom = require('./utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _fn = require('./utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _guid = require('./utils/guid.js'); var Guid = _interopRequireWildcard(_guid); var _browser = require('./utils/browser.js'); var browser = _interopRequireWildcard(_browser); var _log = require('./utils/log.js'); var _log2 = _interopRequireDefault(_log); var _toTitleCase = require('./utils/to-title-case.js'); var _toTitleCase2 = _interopRequireDefault(_toTitleCase); var _timeRanges = require('./utils/time-ranges.js'); var _buffer = require('./utils/buffer.js'); var _stylesheet = require('./utils/stylesheet.js'); var stylesheet = _interopRequireWildcard(_stylesheet); var _fullscreenApi = require('./fullscreen-api.js'); var _fullscreenApi2 = _interopRequireDefault(_fullscreenApi); var _mediaError = require('./media-error.js'); var _mediaError2 = _interopRequireDefault(_mediaError); var _tuple = require('safe-json-parse/tuple'); var _tuple2 = _interopRequireDefault(_tuple); var _obj = require('./utils/obj'); var _mergeOptions = require('./utils/merge-options.js'); var _mergeOptions2 = _interopRequireDefault(_mergeOptions); var _textTrackListConverter = require('./tracks/text-track-list-converter.js'); var _textTrackListConverter2 = _interopRequireDefault(_textTrackListConverter); var _modalDialog = require('./modal-dialog'); var _modalDialog2 = _interopRequireDefault(_modalDialog); var _tech = require('./tech/tech.js'); var _tech2 = _interopRequireDefault(_tech); var _audioTrackList = require('./tracks/audio-track-list.js'); var _audioTrackList2 = _interopRequireDefault(_audioTrackList); var _videoTrackList = require('./tracks/video-track-list.js'); var _videoTrackList2 = _interopRequireDefault(_videoTrackList); require('./tech/loader.js'); require('./tech/flash.js'); require('./poster-image.js'); require('./tracks/text-track-display.js'); require('./loading-spinner.js'); require('./big-play-button.js'); require('./close-button.js'); require('./control-bar/control-bar.js'); require('./error-display.js'); require('./tracks/text-track-settings.js'); require('./tech/html5.js'); 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 _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; } /** * @file player.js */ // Subclasses Component // The following imports are used only to ensure that the corresponding modules // are always included in the video.js package. Importing the modules will // execute them and they will register themselves with video.js. // Import Html5 tech, at least for disposing the original video tag. // The following tech events are simply re-triggered // on the player when they happen var TECH_EVENTS_RETRIGGER = [ /** * Fired while the user agent is downloading media data. * * @event Player#progress * @type {EventTarget~Event} */ /** * Retrigger the `progress` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechProgress_ * @fires Player#progress * @listens Tech#progress */ 'progress', /** * Fires when the loading of an audio/video is aborted. * * @event Player#abort * @type {EventTarget~Event} */ /** * Retrigger the `abort` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechAbort_ * @fires Player#abort * @listens Tech#abort */ 'abort', /** * Fires when the browser is intentionally not getting media data. * * @event Player#suspend * @type {EventTarget~Event} */ /** * Retrigger the `suspend` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechSuspend_ * @fires Player#suspend * @listens Tech#suspend */ 'suspend', /** * Fires when the current playlist is empty. * * @event Player#emptied * @type {EventTarget~Event} */ /** * Retrigger the `emptied` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechEmptied_ * @fires Player#emptied * @listens Tech#emptied */ 'emptied', /** * Fires when the browser is trying to get media data, but data is not available. * * @event Player#stalled * @type {EventTarget~Event} */ /** * Retrigger the `stalled` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechStalled_ * @fires Player#stalled * @listens Tech#stalled */ 'stalled', /** * Fires when the browser has loaded meta data for the audio/video. * * @event Player#loadedmetadata * @type {EventTarget~Event} */ /** * Retrigger the `stalled` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechLoadedmetadata_ * @fires Player#loadedmetadata * @listens Tech#loadedmetadata */ 'loadedmetadata', /** * Fires when the browser has loaded the current frame of the audio/video. * * @event player#loadeddata * @type {event} */ /** * Retrigger the `loadeddata` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechLoaddeddata_ * @fires Player#loadeddata * @listens Tech#loadeddata */ 'loadeddata', /** * Fires when the current playback position has changed. * * @event player#timeupdate * @type {event} */ /** * Retrigger the `timeupdate` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechTimeUpdate_ * @fires Player#timeupdate * @listens Tech#timeupdate */ 'timeupdate', /** * Fires when the playing speed of the audio/video is changed * * @event player#ratechange * @type {event} */ /** * Retrigger the `ratechange` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechRatechange_ * @fires Player#ratechange * @listens Tech#ratechange */ 'ratechange', /** * Fires when the volume has been changed * * @event player#volumechange * @type {event} */ /** * Retrigger the `volumechange` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechVolumechange_ * @fires Player#volumechange * @listens Tech#volumechange */ 'volumechange', /** * Fires when the text track has been changed * * @event player#texttrackchange * @type {event} */ /** * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}. * * @private * @method Player#handleTechTexttrackchange_ * @fires Player#texttrackchange * @listens Tech#texttrackchange */ 'texttrackchange']; /** * An instance of the `Player` class is created when any of the Video.js setup methods * are used to initialize a video. * * After an instance has been created it can be accessed globally in two ways: * 1. By calling `videojs('example_video_1');` * 2. By using it directly via `videojs.players.example_video_1;` * * @extends Component */ var Player = function (_Component) { _inherits(Player, _Component); /** * Create an instance of this class. * * @param {Element} tag * The original video DOM element used for configuring options. * * @param {Object} [options] * Object of option names and values. * * @param {Component~ReadyCallback} [ready] * Ready callback function. */ function Player(tag, options, ready) { _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 = (0, _obj.assign)(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; // If language is not set, get the closest lang attribute if (!options.language) { if (typeof tag.closest === 'function') { var closest = tag.closest('[lang]'); if (closest) { options.language = closest.getAttribute('lang'); } } else { var element = tag; while (element && element.nodeType === 1) { if (Dom.getElAttributes(element).hasOwnProperty('lang')) { options.language = element.getAttribute('lang'); break; } element = element.parentNode; } } } // Run base component initializing with new options // if the global option object was accidentally blown away by // someone, bail early with an informative error var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready)); 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?'); } // Store the original tag used to set options _this.tag = tag; // 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 = (0, _mergeOptions2['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 { _log2['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_); return _this; } /** * Destroys the video player and does any necessary cleanup. * * This is especially helpful if you are dynamically adding and removing videos * to/from the DOM. * * @fires Player#dispose */ Player.prototype.dispose = function dispose() { /** * Called when the player is being disposed of. * * @event Player#dispose * @type {EventTarget~Event} */ 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 `Player`'s DOM element. * * @return {Element} * The DOM element that gets created. */ Player.prototype.createEl = function createEl() { var tag = this.tag; var el = void 0; var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player'); if (playerElIngest) { el = this.el_ = tag.parentNode; } else { el = this.el_ = _Component.prototype.createEl.call(this, 'div'); } // 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 (_window2['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 && !playerElIngest) { 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 // // Breaks iPhone, fixed in HTML5 setup. Dom.insertElFirst(tag, el); this.children_.unshift(tag); this.el_ = el; return el; }; /** * A getter/setter for the `Player`'s width. * * @param {number} [value] * The value to set the `Player's width to. * * @return {number} * The current width of the `Player`. */ Player.prototype.width = function width(value) { return this.dimension('width', value); }; /** * A getter/setter for the `Player`'s height. * * @param {number} [value] * The value to set the `Player's heigth to. * * @return {number} * The current heigth of the `Player`. */ Player.prototype.height = function height(value) { return this.dimension('height', value); }; /** * A getter/setter for the `Player`'s width & height. * * @param {string} dimension * This string can be: * - 'width' * - 'height' * * @param {number} [value] * Value for dimension specified in the first argument. * * @return {Player|number} * - Returns itself when setting; method can be chained. * - The dimension arguments value when getting (width/height). */ 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)) { _log2['default'].error('Improper value "' + value + '" supplied for for ' + _dimension); return this; } this[privDimension] = parsedVal; } this.updateStyleEl_(); return this; }; /** * A getter/setter/toggler for the vjs-fluid `className` on the `Player`. * * @param {boolean} [bool] * - A value of true adds the class. * - A value of false removes the class. * - No value will toggle the fluid class. * * @return {boolean|undefined} * - The value of fluid when getting. * - `undefined` when setting. */ 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'); } this.updateStyleEl_(); }; /** * Get/Set the aspect ratio * * @param {string} [ratio] * Aspect ratio for player * * @return {string|undefined} * returns the current aspect ratio when getting */ /** * A getter/setter for the `Player`'s aspect ratio. * * @param {string} [ratio] * The value to set the `Player's aspect ratio to. * * @return {string|undefined} * - The current aspect ratio of the `Player` when getting. * - undefined when setting */ 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). * * @private * @listens Tech#loadedmetadata */ Player.prototype.updateStyleEl_ = function updateStyleEl_() { if (_window2['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 = void 0; var height = void 0; var aspectRatio = void 0; var idClass = void 0; // 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() > 0) { // 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/Create an instance of playback {@link Tech} including element * and API methods. Then append the `Tech` element in `Player` as a child. * * @param {string} techName * name of the playback technology * * @param {string} source * video source * * @private */ Player.prototype.loadTech_ = function loadTech_(techName, source) { var _this2 = this; // 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) { _tech2['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 = (0, _obj.assign)({ source: source, 'nativeControlsForTouch': this.options_.nativeControlsForTouch, 'playerId': this.id(), 'techId': this.id() + '_' + techName + '_api', 'videoTracks': this.videoTracks_, 'textTracks': this.textTracks_, 'audioTracks': this.audioTracks_, 'autoplay': this.options_.autoplay, 'preload': this.options_.preload, 'loop': this.options_.loop, 'muted': this.options_.muted, 'poster': this.poster(), 'language': this.language(), 'playerElIngest': this.playerElIngest_ || false, '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_.sources = null; this.cache_.source = source; this.cache_.src = source.src; } // Initialize tech instance var TechComponent = _tech2['default'].getTech(techName); // Support old behavior of techs being registered as components. // Remove once that deprecated behavior is removed. if (!TechComponent) { TechComponent = _component2['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); _textTrackListConverter2['default'].jsonToTextTracks(this.textTracksJson_ || [], this.tech_); // Listen to all HTML5-defined events and trigger them on the player TECH_EVENTS_RETRIGGER.forEach(function (event) { _this2.on(_this2.tech_, event, _this2['handleTech' + (0, _toTitleCase2['default'])(event) + '_']); }); 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_, 'durationchange', this.handleTechDurationChange_); this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_); this.on(this.tech_, 'error', this.handleTechError_); this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_); this.on(this.tech_, 'posterchange', this.handleTechPosterChange_); this.on(this.tech_, 'textdata', this.handleTechTextData_); this.usingNativeControls(this.techGet_('controls')); if (this.controls() && !this.usingNativeControls()) { this.addTechControlsListeners_(); } // 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 and dispose of the current playback {@link Tech}. * * @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_ = _textTrackListConverter2['default'].textTracksToJson(this.tech_); this.isReady_ = false; this.tech_.dispose(); this.tech_ = false; }; /** * Return a reference to the current {@link Tech}, but only if given an object with the * `IWillNotUseThisInPlugins` property having a true value. This is try and prevent misuse * of techs by plugins. * * @param {Object} safety * An object that must contain `{IWillNotUseThisInPlugins: true}` * * @param {boolean} safety.IWillNotUseThisInPlugins * Must be set to true or else this function will throw an error. * * @return {Tech} * The 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 '; _window2['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 mobile devices: a click on the video toggles controls * which 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 */ 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_); // 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. * * @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_); }; /** * Player waits for the tech to be ready * * @private */ Player.prototype.handleTechReady_ = function handleTechReady_() { 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.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) { try { // Chrome Fix. Fixed in Chrome v16. delete this.tag.poster; } catch (e) { (0, _log2['default'])('deleting tag.poster throws in some browsers', e); } this.play(); } }; /** * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This * function will also trigger {@link Player#firstplay} if it is the first loadstart * for a video. * * @fires Player#loadstart * @fires Player#firstplay * @listens Tech#loadstart * @private */ 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()) { /** * Fired when the user agent begins looking for media data * * @event Player#loadstart * @type {EventTarget~Event} */ this.trigger('loadstart'); this.trigger('firstplay'); } else { // reset the hasStarted state this.hasStarted(false); this.trigger('loadstart'); } }; /** * Add/remove the vjs-has-started class * * @fires Player#firstplay * * @param {boolean} hasStarted * - true: adds the class * - false: remove the class * * @return {boolean} * the boolean value of 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 * * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play} * @fires Player#play * @listens Tech#play * @private */ 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 this.hasStarted(true); /** * Triggered whenever an {@link Tech#play} event happens. Indicates that * playback has started or resumed. * * @event Player#play * @type {EventTarget~Event} */ this.trigger('play'); }; /** * Retrigger the `waiting` event that was triggered by the {@link Tech}. * * @fires Player#waiting * @listens Tech#waiting * @private */ Player.prototype.handleTechWaiting_ = function handleTechWaiting_() { var _this3 = this; this.addClass('vjs-waiting'); /** * A readyState change on the DOM element has caused playback to stop. * * @event Player#waiting * @type {EventTarget~Event} */ this.trigger('waiting'); this.one('timeupdate', function () { return _this3.removeClass('vjs-waiting'); }); }; /** * Retrigger the `canplay` event that was triggered by the {@link Tech}. * > Note: This is not consistent between browsers. See #1351 * * @fires Player#canplay * @listens Tech#canplay * @private */ Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() { this.removeClass('vjs-waiting'); /** * The media has a readyState of HAVE_FUTURE_DATA or greater. * * @event Player#canplay * @type {EventTarget~Event} */ this.trigger('canplay'); }; /** * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}. * * @fires Player#canplaythrough * @listens Tech#canplaythrough * @private */ Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() { this.removeClass('vjs-waiting'); /** * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the * entire media file can be played without buffering. * * @event Player#canplaythrough * @type {EventTarget~Event} */ this.trigger('canplaythrough'); }; /** * Retrigger the `playing` event that was triggered by the {@link Tech}. * * @fires Player#playing * @listens Tech#playing * @private */ Player.prototype.handleTechPlaying_ = function handleTechPlaying_() { this.removeClass('vjs-waiting'); /** * The media is no longer blocked from playback, and has started playing. * * @event Player#playing * @type {EventTarget~Event} */ this.trigger('playing'); }; /** * Retrigger the `seeking` event that was triggered by the {@link Tech}. * * @fires Player#seeking * @listens Tech#seeking * @private */ Player.prototype.handleTechSeeking_ = function handleTechSeeking_() { this.addClass('vjs-seeking'); /** * Fired whenever the player is jumping to a new time * * @event Player#seeking * @type {EventTarget~Event} */ this.trigger('seeking'); }; /** * Retrigger the `seeked` event that was triggered by the {@link Tech}. * * @fires Player#seeked * @listens Tech#seeked * @private */ Player.prototype.handleTechSeeked_ = function handleTechSeeked_() { this.removeClass('vjs-seeking'); /** * Fired when the player has finished jumping to a new time * * @event Player#seeked * @type {EventTarget~Event} */ this.trigger('seeked'); }; /** * Retrigger the `firstplay` event that was triggered by the {@link Tech}. * * @fires Player#firstplay * @listens Tech#firstplay * @deprecated As of 6.0 passing the `starttime` option to the player will be deprecated * @private */ 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) { _log2['default'].warn('Passing the `starttime` option to the player will be deprecated in 6.0'); this.currentTime(this.options_.starttime); } this.addClass('vjs-has-started'); /** * Fired the first time a video is played. Not part of the HLS spec, and this is * probably not the best implementation yet, so use sparingly. If you don't have a * reason to prevent playback, use `myPlayer.one('play');` instead. * * @event Player#firstplay * @type {EventTarget~Event} */ this.trigger('firstplay'); }; /** * Retrigger the `pause` event that was triggered by the {@link Tech}. * * @fires Player#pause * @listens Tech#pause * @private */ Player.prototype.handleTechPause_ = function handleTechPause_() { this.removeClass('vjs-playing'); this.addClass('vjs-paused'); /** * Fired whenever the media has been paused * * @event Player#pause * @type {EventTarget~Event} */ this.trigger('pause'); }; /** * Retrigger the `ended` event that was triggered by the {@link Tech}. * * @fires Player#ended * @listens Tech#ended * @private */ Player.prototype.handleTechEnded_ = function handleTechEnded_() { this.addClass('vjs-ended'); if (this.options_.loop) { this.currentTime(0); this.play(); } else if (!this.paused()) { this.pause(); } /** * Fired when the end of the media resource is reached (currentTime == duration) * * @event Player#ended * @type {EventTarget~Event} */ this.trigger('ended'); }; /** * Fired when the duration of the media resource is first known or changed * * @listens Tech#durationchange * @private */ Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() { this.duration(this.techGet_('duration')); }; /** * Handle a click on the media element to play/pause * * @param {EventTarget~Event} event * the event that caused this function to trigger * * @listens Tech#mousedown * @private */ 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 if (event.button !== 0) { return; } // When controls are disabled a click should not toggle playback because // the click is considered a control if (this.controls()) { if (this.paused()) { this.play(); } else { this.pause(); } } }; /** * Handle a tap on the media element. It will toggle the user * activity state, which hides and shows the controls. * * @listens Tech#tap * @private */ Player.prototype.handleTechTap_ = function handleTechTap_() { this.userActive(!this.userActive()); }; /** * Handle touch to start * * @listens Tech#touchstart * @private */ Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() { this.userWasActive = this.userActive(); }; /** * Handle touch to move * * @listens Tech#touchmove * @private */ Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() { if (this.userWasActive) { this.reportUserActivity(); } }; /** * Handle touch to end * * @param {EventTarget~Event} event * the touchend event that triggered * this function * * @listens Tech#touchend * @private */ 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 * @listens Player#fullscreenchange */ 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 * @listens stageclick */ Player.prototype.handleStageClick_ = function handleStageClick_() { this.reportUserActivity(); }; /** * Handle Tech Fullscreen Change * * @param {EventTarget~Event} event * the fullscreenchange event that triggered this function * * @param {Object} data * the data that was sent with the event * * @private * @listens Tech#fullscreenchange * @fires Player#fullscreenchange */ Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) { if (data) { this.isFullscreen(data.isFullscreen); } /** * Fired when going in and out of fullscreen. * * @event Player#fullscreenchange * @type {EventTarget~Event} */ this.trigger('fullscreenchange'); }; /** * Fires when an error occurred during the loading of an audio/video. * * @private * @listens Tech#error */ Player.prototype.handleTechError_ = function handleTechError_() { var error = this.tech_.error(); this.error(error); }; /** * Retrigger the `textdata` event that was triggered by the {@link Tech}. * * @fires Player#textdata * @listens Tech#textdata * @private */ Player.prototype.handleTechTextData_ = function handleTechTextData_() { var data = null; if (arguments.length > 1) { data = arguments[1]; } /** * Fires when we get a textdata event from tech * * @event Player#textdata * @type {EventTarget~Event} */ this.trigger('textdata', data); }; /** * Get object for cached values. * * @return {Object} * get the current object cache */ Player.prototype.getCache = function getCache() { return this.cache_; }; /** * Pass values to the playback tech * * @param {string} [method] * the method to call * * @param {Object} arg * the argument to pass * * @private */ Player.prototype.techCall_ = function techCall_(method, arg) { // 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) { (0, _log2['default'])(e); throw e; } } }; /** * Get calls can't wait for the tech, and sometimes don't need to. * * @param {string} method * Tech method * * @return {Function|undefined} * the method or undefined * * @private */ 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) { (0, _log2['default'])('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e); // When a method isn't available on the object it throws a TypeError } else if (e.name === 'TypeError') { (0, _log2['default'])('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e); this.tech_.isReady_ = false; } else { (0, _log2['default'])(e); } throw e; } } return; }; /** * start media playback * * @return {Player} * A reference to the player object this function was called on */ Player.prototype.play = function play() { // Only calls the tech's play if we already have a src loaded if (this.src() || this.currentSrc()) { this.techCall_('play'); } else { this.tech_.one('loadstart', function () { this.play(); }); } return this; }; /** * Pause the video playback * * @return {Player} * A reference to the player object this function was called on */ Player.prototype.pause = function pause() { this.techCall_('pause'); return this; }; /** * Check if the player is paused or has yet to play * * @return {boolean} * - false: if the media is currently playing * - true: if media is not currently playing */ 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] * wether the user is or is not scrubbing * * @return {boolean|Player} * A instance of the player that called this function when setting, * and the value of scrubbing when getting */ 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) * * @param {number|string} [seconds] * The time to seek to in seconds * * @return {Player|number} * - the current time in seconds when getting * - a reference to the current player object when * getting */ 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. this.cache_.currentTime = this.techGet_('currentTime') || 0; return this.cache_.currentTime; }; /** * Normally gets the length in time of the video in seconds; * in all but the rarest use cases an argument will NOT be passed to the method * * > **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. * * @fires Player#durationchange * * @param {number} [seconds] * The duration of the video to set in seconds * * @return {number|Player} * - The duration of the video in seconds when getting * - A reference to the player that called this function * when setting */ 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'); } /** * @event Player#durationchange * @type {EventTarget~Event} */ this.trigger('durationchange'); } return this; }; /** * Calculates how much time is left in the video. Not part * of the native video API. * * @return {number} * The time remaining in seconds */ Player.prototype.remainingTime = function remainingTime() { return this.duration() - this.currentTime(); }; // // Kind of like an array of portions of the video that have been downloaded. /** * Get a TimeRange object with an array of the times of the video * that have been downloaded. If you just want the percent of the * video that's been downloaded, use bufferedPercent. * * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered} * * @return {TimeRange} * A mock TimeRange object (following HTML spec) */ Player.prototype.buffered = function buffered() { var buffered = this.techGet_('buffered'); if (!buffered || !buffered.length) { buffered = (0, _timeRanges.createTimeRange)(0, 0); } return buffered; }; /** * Get the percent (as a decimal) of the video that's been downloaded. * This method is not a part of the native HTML video API. * * @return {number} * A decimal between 0 and 1 representing the percent * that is bufferred 0 being 0% and 1 being 100% */ Player.prototype.bufferedPercent = function bufferedPercent() { return (0, _buffer.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 */ Player.prototype.bufferedEnd = function bufferedEnd() { var buffered = this.buffered(); var duration = this.duration(); var end = buffered.end(buffered.length - 1); if (end > duration) { end = duration; } return end; }; /** * Get or set the current volume of the media * * @param {number} [percentAsDecimal] * The new volume as a decimal percent: * - 0 is muted/0%/off * - 1.0 is 100%/full * - 0.5 is half volume or 50% * * @return {Player|number} * a reference to the calling player when setting and the * current volume as a percent when getting */ Player.prototype.volume = function volume(percentAsDecimal) { var vol = void 0; if (percentAsDecimal !== undefined) { // Force value to between 0 and 1 vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); 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 * * @param {boolean} [muted] * - true to mute * - false to unmute * * @return {boolean|Player} * - true if mute is on and getting * - false if mute is off and getting * - A reference to the current player when setting */ Player.prototype.muted = function muted(_muted) { if (_muted !== undefined) { this.techCall_('setMuted', _muted); return this; } return this.techGet_('muted') || false; }; /** * Check if current tech can support native fullscreen * (e.g. with built in controls like iOS, so not our flash swf) * * @return {boolean} * if native fullscreen is supported */ Player.prototype.supportsFullScreen = function supportsFullScreen() { return this.techGet_('supportsFullScreen') || false; }; /** * Check if the player is in fullscreen mode or tell the player that it * is or is not in fullscreen mode. * * > 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] * Set the players current fullscreen state * * @return {boolean|Player} * - true if fullscreen is on and getting * - false if fullscreen is off and getting * - A reference to the current player when setting */ 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 * 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. * * @fires Player#fullscreenchange * @return {Player} * A reference to the current player */ Player.prototype.requestFullscreen = function requestFullscreen() { var fsApi = _fullscreenApi2['default']; this.isFullscreen(true); if (fsApi.requestFullscreen) { // 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(_document2['default'], fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) { this.isFullscreen(_document2['default'][fsApi.fullscreenElement]); // If cancelling fullscreen, remove event listener. if (this.isFullscreen() === false) { Events.off(_document2['default'], fsApi.fullscreenchange, documentFullscreenChange); } /** * @event Player#fullscreenchange * @type {EventTarget~Event} */ this.trigger('fullscreenchange'); })); this.el_[fsApi.requestFullscreen](); } else if (this.tech_.supportsFullScreen()) { // 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(); /** * @event Player#fullscreenchange * @type {EventTarget~Event} */ this.trigger('fullscreenchange'); } return this; }; /** * Return the video to its normal size after having been in full screen mode * * @fires Player#fullscreenchange * * @return {Player} * A reference to the current player */ Player.prototype.exitFullscreen = function exitFullscreen() { var fsApi = _fullscreenApi2['default']; this.isFullscreen(false); // Check for browser element fullscreen support if (fsApi.requestFullscreen) { _document2['default'][fsApi.exitFullscreen](); } else if (this.tech_.supportsFullScreen()) { this.techCall_('exitFullScreen'); } else { this.exitFullWindow(); /** * @event Player#fullscreenchange * @type {EventTarget~Event} */ 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. * * @fires Player#enterFullWindow */ Player.prototype.enterFullWindow = function enterFullWindow() { this.isFullWindow = true; // Storing original doc overflow value to return to when fullscreen is off this.docOrigOverflow = _document2['default'].documentElement.style.overflow; // Add listener for esc key to exit fullscreen Events.on(_document2['default'], 'keydown', Fn.bind(this, this.fullWindowOnEscKey)); // Hide any scroll bars _document2['default'].documentElement.style.overflow = 'hidden'; // Apply fullscreen styles Dom.addElClass(_document2['default'].body, 'vjs-full-window'); /** * @event Player#enterFullWindow * @type {EventTarget~Event} */ 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 */ Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) { if (event.keyCode === 27) { if (this.isFullscreen() === true) { this.exitFullscreen(); } else { this.exitFullWindow(); } } }; /** * Exit full window * * @fires Player#exitFullWindow */ Player.prototype.exitFullWindow = function exitFullWindow() { this.isFullWindow = false; Events.off(_document2['default'], 'keydown', this.fullWindowOnEscKey); // Unhide scroll bars. _document2['default'].documentElement.style.overflow = this.docOrigOverflow; // Remove fullscreen styles Dom.removeElClass(_document2['default'].body, 'vjs-full-window'); // Resize the box, controller, and poster to original sizes // this.positionAll(); /** * @event Player#exitFullWindow * @type {EventTarget~Event} */ this.trigger('exitFullWindow'); }; /** * Check whether the player can play a given mimetype * * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype * * @param {string} type * The mimetype to check * * @return {string} * 'probably', 'maybe', or '' (empty string) */ Player.prototype.canPlayType = function canPlayType(type) { var can = void 0; // Loop through each playback technology in the options order for (var i = 0, j = this.options_.techOrder; i < j.length; i++) { var techName = (0, _toTitleCase2['default'])(j[i]); var tech = _tech2['default'].getTech(techName); // Support old behavior of techs being registered as components. // Remove once that deprecated behavior is removed. if (!tech) { tech = _component2['default'].getComponent(techName); } // Check if the current tech is defined before continuing if (!tech) { _log2['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 or false */ Player.prototype.selectSource = function selectSource(sources) { var _this4 = this; // Get only the techs specified in `techOrder` that exist and are supported by the // current platform var techs = this.options_.techOrder.map(_toTitleCase2['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, _tech2['default'].getTech(techName) || _component2['default'].getComponent(techName)]; }).filter(function (_ref) { var techName = _ref[0], tech = _ref[1]; // Check if the current tech is defined before continuing if (tech) { // Check if the browser supports this technology return tech.isSupported(); } _log2['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 = void 0; outerArray.some(function (outerChoice) { return innerArray.some(function (innerChoice) { found = tester(outerChoice, innerChoice); if (found) { return true; } }); }); return found; }; var foundSourceAndTech = void 0; var flip = function flip(fn) { return function (a, b) { return fn(b, a); }; }; var finder = function finder(_ref2, source) { var techName = _ref2[0], tech = _ref2[1]; if (tech.canPlaySource(source, _this4.options_[techName.toLowerCase()])) { 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. * * @param {Tech~SourceObject|Tech~SourceObject[]} [source] * One SourceObject or an array of SourceObjects * * @return {string|Player} * - The current video source when getting * - The player when setting */ Player.prototype.src = function src(source) { if (source === undefined) { return this.techGet_('src'); } var currentTech = _tech2['default'].getTech(this.techName_); // Support old behavior of techs being registered as components. // Remove once that deprecated behavior is removed. if (!currentTech) { currentTech = _component2['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, this.options_[this.techName_.toLowerCase()])) { // 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_.sources = null; this.cache_.source = source; 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 {Tech~SourceObject[]} sources * Array of source objects * * @private */ 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); } this.cache_.sources = sources; } 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} * A reference to the player */ 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} * A reference to the player */ Player.prototype.reset = function reset() { this.loadTech_((0, _toTitleCase2['default'])(this.options_.techOrder[0]), null); this.techCall_('reset'); return this; }; /** * Returns all of the current source objects. * * @return {Tech~SourceObject[]} * The current source objects */ Player.prototype.currentSources = function currentSources() { var source = this.currentSource(); var sources = []; // assume `{}` or `{ src }` if (Object.keys(source).length !== 0) { sources.push(source); } return this.cache_.sources || sources; }; /** * Returns the current source object. * * @return {Tech~SourceObject} * The current source object */ Player.prototype.currentSource = function currentSource() { var source = {}; var src = this.currentSrc(); if (src) { source.src = src; } return this.cache_.source || source; }; /** * 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 */ 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 */ Player.prototype.currentType = function currentType() { return this.currentType_ || ''; }; /** * Get or set the preload attribute * * @param {boolean} [value] * - true means that we should preload * - false maens that we should not preload * * @return {string|Player} * - the preload attribute value when getting * - the player when setting */ 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] * - true means that we should autoplay * - false maens that we should not autoplay * * @return {string|Player} * - the current value of autoplay * - the player when setting */ Player.prototype.autoplay = function autoplay(value) { if (value !== undefined) { this.techCall_('setAutoplay', value); this.options_.autoplay = value; return this; } return this.techGet_('autoplay', value); }; /** * Get or set the loop attribute on the video element. * * @param {boolean} [value] * - true means that we should loop the video * - false means that we should not loop the video * * @return {string|Player} * - the current value of loop when getting * - the player when setting */ 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 * * @fires Player#posterchange * * @param {string} [src] * Poster image source URL * * @return {string|Player} * - the current value of poster when getting * - the player when setting */ 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 event fires when the poster image is changed on the player. * * @event Player#posterchange * @type {EventTarget~Event} */ 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. * * @fires Player#posterchange * @listens Tech#posterchange * @private */ 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. * * @fires Player#controlsenabled * * @param {boolean} [bool] * - true to turn controls on * - false to turn controls off * * @return {boolean|Player} * - the current value of controls when getting * - the player when setting */ Player.prototype.controls = function controls(bool) { if (bool !== undefined) { bool = !!bool; // 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'); /** * @event Player#controlsenabled * @type {EventTarget~Event} */ this.trigger('controlsenabled'); if (!this.usingNativeControls()) { this.addTechControlsListeners_(); } } else { this.removeClass('vjs-controls-enabled'); this.addClass('vjs-controls-disabled'); /** * @event Player#controlsdisabled * @type {EventTarget~Event} */ 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** * * @fires Player#usingnativecontrols * @fires Player#usingcustomcontrols * * @param {boolean} [bool] * - true to turn native controls on * - false to turn native controls off * * @return {boolean|Player} * - the current value of native controls when getting * - the player when setting */ Player.prototype.usingNativeControls = function usingNativeControls(bool) { if (bool !== undefined) { bool = !!bool; // 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 Player#usingnativecontrols * @type {EventTarget~Event} */ this.trigger('usingnativecontrols'); } else { this.removeClass('vjs-using-native-controls'); /** * player is using the custom HTML controls * * @event Player#usingcustomcontrols * @type {EventTarget~Event} */ this.trigger('usingcustomcontrols'); } } return this; } return !!this.usingNativeControls_; }; /** * Set or get the current MediaError * * @fires Player#error * * @param {MediaError|string|number} [err] * A MediaError or a string/number to be turned * into a MediaError * * @return {MediaError|null|Player} * - The current MediaError when getting (or null) * - The player when setting */ 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'); if (this.errorDisplay) { this.errorDisplay.close(); } return this; } this.error_ = new _mediaError2['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 _log2['default'].error('(CODE:' + this.error_.code + ' ' + _mediaError2['default'].errorTypes[this.error_.code] + ')', this.error_.message, this.error_); /** * @event Player#error * @type {EventTarget~Event} */ this.trigger('error'); return this; }; /** * Report user activity * * @param {Object} event * Event object */ Player.prototype.reportUserActivity = function reportUserActivity(event) { this.userActivity_ = true; }; /** * Get/set if user is active * * @fires Player#useractive * @fires Player#userinactive * * @param {boolean} [bool] * - true if the user is active * - false if the user is inactive * @return {boolean|Player} * - the current value of userActive when getting * - the player when setting */ 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'); /** * @event Player#useractive * @type {EventTarget~Event} */ 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'); /** * @event Player#userinactive * @type {EventTarget~Event} */ this.trigger('userinactive'); } } return this; } return this.userActive_; }; /** * Listen for user activity based on timeout value * * @private */ Player.prototype.listenForUserActivity_ = function listenForUserActivity_() { var mouseInProgress = void 0; var lastMoveX = void 0; var lastMoveY = void 0; 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() { 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 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 = void 0; 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 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 activity check 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|Player} * - The current playback rate when getting or 1.0 * - the player when setting */ 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'); } return 1.0; }; /** * Gets or sets the audio flag * * @param {boolean} bool * - true signals that this is an audio player * - false signals that this is not an audio player * * @return {Player|boolean} * - the current value of isAudio when getting * - the player if setting */ Player.prototype.isAudio = function isAudio(bool) { if (bool !== undefined) { this.isAudio_ = !!bool; return this; } return !!this.isAudio_; }; /** * Get the {@link VideoTrackList} * * @see https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist * * @return {VideoTrackList} * the current video track list */ 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 _videoTrackList2['default'](); return this.videoTracks_; } return this.tech_.videoTracks(); }; /** * Get the {@link AudioTrackList} * * @see https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist * * @return {AudioTrackList} * the current audio track list */ 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 _audioTrackList2['default'](); return this.audioTracks_; } return this.tech_.audioTracks(); }; /** * Get the {@link TextTrackList} * * 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 yet implemented) audio descriptions that are read back to * the user by a screen reading device * * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks * * @return {TextTrackList|undefined} * The current TextTrackList or undefined if * or undefined if we don't have a tech */ 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. if (this.tech_) { return this.tech_.textTracks(); } }; /** * Get the "remote" {@link TextTrackList}. Remote Text Tracks * are tracks that were added to the HTML video element and can * be removed, whereas normal texttracks cannot be removed. * * * @return {TextTrackList|undefined} * The current remote text track list or undefined * if we don't have a tech */ Player.prototype.remoteTextTracks = function remoteTextTracks() { if (this.tech_) { return this.tech_.remoteTextTracks(); } }; /** * Get the "remote" {@link HTMLTrackElementList}. * This gives the user all of the DOM elements that match up * with the remote {@link TextTrackList}. * * @return {HTMLTrackElementList} * The current remote text track list elements * or undefined if we don't have a tech */ Player.prototype.remoteTextTrackEls = function remoteTextTrackEls() { if (this.tech_) { return this.tech_.remoteTextTrackEls(); } }; /** * A helper method for adding a {@link TextTrack} to our * {@link TextTrackList}. * * In addition to the W3C settings we allow adding additional info through options. * * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack * * @param {string} [kind] * the kind of TextTrack you are adding * * @param {string} [label] * the label to give the TextTrack label * * @param {string} [language] * the language to set on the TextTrack * * @return {TextTrack|undefined} * the TextTrack that was added or undefined * if there is no tech */ Player.prototype.addTextTrack = function addTextTrack(kind, label, language) { if (this.tech_) { return this.tech_.addTextTrack(kind, label, language); } }; /** * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will * automatically removed from the video element whenever the source changes, unless * manualCleanup is set to false. * * @param {Object} options * Options to pass to {@link HTMLTrackElement} during creation. See * {@link HTMLTrackElement} for object properties that you should use. * * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be * * @return {HTMLTrackElement} * the HTMLTrackElement that was created and added * to the HTMLTrackElementList and the remote * TextTrackList * * @deprecated The default value of the "manualCleanup" parameter will default * to "false" in upcoming versions of Video.js */ Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) { if (this.tech_) { return this.tech_.addRemoteTextTrack(options, manualCleanup); } }; /** * Remove a remote {@link TextTrack} from the respective * {@link TextTrackList} and {@link HTMLTrackElementList}. * * @param {Object} track * Remote {@link TextTrack} to remove * * @return {undefined} * does not return anything */ Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() { var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref3$track = _ref3.track, track = _ref3$track === undefined ? arguments[0] : _ref3$track; // 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 if (this.tech_) { return this.tech_.removeRemoteTextTrack(track); } }; /** * Get video width * * @return {number} * current video width */ Player.prototype.videoWidth = function videoWidth() { return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0; }; /** * Get video height * * @return {number} * current video height */ 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 language code to set the player to * * @return {string|Player} * - The current language code when getting * - A reference to the player when setting */ Player.prototype.language = function language(code) { if (code === undefined) { return this.language_; } this.language_ = String(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} * An array of of supported languages */ Player.prototype.languages = function languages() { return (0, _mergeOptions2['default'])(Player.prototype.options_.languages, this.languages_); }; /** * returns a JavaScript object reperesenting the current track * information. **DOES not return it as JSON** * * @return {Object} * Object representing the current of track info */ Player.prototype.toJSON = function toJSON() { var options = (0, _mergeOptions2['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 = (0, _mergeOptions2['default'])(track); track.player = undefined; options.tracks[i] = track; } return options; }; /** * Creates a simple modal dialog (an instance of the {@link ModalDialog} * component) that immediately overlays the player with arbitrary * content and removes itself when closed. * * @param {string|Function|Element|Array|null} content * Same as {@link 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 {@link ModalDialog}. * * @return {ModalDialog} * the {@link ModalDialog} that was created */ Player.prototype.createModal = function createModal(content, options) { var _this5 = this; options = options || {}; options.content = content || ''; var modal = new _modalDialog2['default'](this, options); this.addChild(modal); modal.on('dispose', function () { _this5.removeChild(modal); }); return modal.open(); }; /** * Gets tag settings * * @param {Element} tag * The player tag * * @return {Object} * An object containing all of the settings * for a player tag */ Player.getTagSettings = function getTagSettings(tag) { var baseOptions = { sources: [], tracks: [] }; var tagOptions = Dom.getElAttributes(tag); var dataSetup = tagOptions['data-setup']; if (Dom.hasElClass(tag, 'vjs-fluid')) { tagOptions.fluid = true; } // Check if data-setup attr exists. if (dataSetup !== null) { // Parse options JSON // If empty string, make it a parsable json object. var _safeParseTuple = (0, _tuple2['default'])(dataSetup || '{}'), err = _safeParseTuple[0], data = _safeParseTuple[1]; if (err) { _log2['default'].error(err); } (0, _obj.assign)(tagOptions, data); } (0, _obj.assign)(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; }; /** * Determine wether or not flexbox is supported * * @return {boolean} * - true if flexbox is supported * - false if flexbox is not supported */ Player.prototype.flexNotSupported_ = function flexNotSupported_() { var elem = _document2['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 || // IE10-specific (2012 flex spec) 'msFlexOrder' in elem.style); }; return Player; }(_component2['default']); /** * Global player list * * @type {Object} */ Player.players = {}; var navigator = _window2['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, // 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: navigator && (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.' }; [ /** * 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 Player#ended */ '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 Player#seeking */ 'seeking', /** * Returns the TimeRanges of the media that are currently available * for seeking to. * * @return {TimeRanges} the seekable intervals of the media timeline * @method Player#seekable */ 'seekable', /** * 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 Player#networkState */ '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 Player#readyState */ 'readyState'].forEach(function (fn) { Player.prototype[fn] = function () { return this.techGet_(fn); }; }); TECH_EVENTS_RETRIGGER.forEach(function (event) { Player.prototype['handleTech' + (0, _toTitleCase2['default'])(event) + '_'] = function () { return this.trigger(event); }; }); /** * Fired when the player has initial duration and dimension information * * @event Player#loadedmetadata * @type {EventTarget~Event} */ /** * Fired when the player has downloaded data at the current playback position * * @event Player#loadeddata * @type {EventTarget~Event} */ /** * 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 Player#timeupdate * @type {EventTarget~Event} */ /** * Fired when the volume changes * * @event Player#volumechange * @type {EventTarget~Event} */ _component2['default'].registerComponent('Player', Player); exports['default'] = Player; },{"./big-play-button.js":220,"./close-button.js":223,"./component.js":224,"./control-bar/control-bar.js":227,"./error-display.js":260,"./fullscreen-api.js":263,"./loading-spinner.js":264,"./media-error.js":265,"./modal-dialog":269,"./poster-image.js":274,"./tech/flash.js":278,"./tech/html5.js":279,"./tech/loader.js":280,"./tech/tech.js":281,"./tracks/audio-track-list.js":282,"./tracks/text-track-display.js":287,"./tracks/text-track-list-converter.js":288,"./tracks/text-track-settings.js":290,"./tracks/video-track-list.js":295,"./utils/browser.js":297,"./utils/buffer.js":298,"./utils/dom.js":300,"./utils/events.js":301,"./utils/fn.js":302,"./utils/guid.js":304,"./utils/log.js":305,"./utils/merge-options.js":306,"./utils/obj":307,"./utils/stylesheet.js":308,"./utils/time-ranges.js":309,"./utils/to-title-case.js":310,"global/document":193,"global/window":194,"safe-json-parse/tuple":213}],271:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _player = require('./player.js'); var _player2 = _interopRequireDefault(_player); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } /** * The method for registering a video.js plugin. {@link videojs:videojs.registerPlugin]. * * @param {string} name * The name of the plugin that is being registered * * @param {plugins:PluginFn} init * The function that gets run when a `Player` initializes. */ var plugin = function plugin(name, init) { _player2['default'].prototype[name] = init; }; /** * @file plugins.js * @module plugins */ exports['default'] = plugin; },{"./player.js":270}],272:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _clickableComponent = require('../clickable-component.js'); var _clickableComponent2 = _interopRequireDefault(_clickableComponent); var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); 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; } /** * @file popup-button.js */ /** * A button class for use with {@link Popup} controls * * @extends ClickableComponent */ var PopupButton = function (_ClickableComponent) { _inherits(PopupButton, _ClickableComponent); /** * Create an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function PopupButton(player) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck(this, PopupButton); var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options)); _this.update(); return _this; } /** * Update the `Popup` that this button is attached to. */ 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 a `Popup`. - Override with specific functionality for component * * @abstract */ PopupButton.prototype.createPopup = function createPopup() {}; /** * Create the `PopupButton`s DOM element. * * @return {Element} * The element that gets created. */ PopupButton.prototype.createEl = function createEl() { return _ClickableComponent.prototype.createEl.call(this, 'div', { className: this.buildCSSClass() }); }; /** * Builds the default DOM `className`. * * @return {string} * The DOM `className` for this object. */ 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 PopupButton; }(_clickableComponent2['default']); _component2['default'].registerComponent('PopupButton', PopupButton); exports['default'] = PopupButton; },{"../clickable-component.js":222,"../component.js":224}],273:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _events = require('../utils/events.js'); var Events = _interopRequireWildcard(_events); 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 _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; } /** * @file popup.js */ /** * The Popup component is used to build pop up controls. * * @extends Component */ var Popup = function (_Component) { _inherits(Popup, _Component); function Popup() { _classCallCheck(this, Popup); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } /** * Add a popup item to the popup * * @param {Object|string} component * Component or component type to add * */ Popup.prototype.addItem = function addItem(component) { this.addChild(component); component.on('click', Fn.bind(this, function () { this.unlockShowing(); })); }; /** * Create the `PopupButton`s DOM element. * * @return {Element} * The element that gets created. */ 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; }(_component2['default']); _component2['default'].registerComponent('Popup', Popup); exports['default'] = Popup; },{"../component.js":224,"../utils/dom.js":300,"../utils/events.js":301,"../utils/fn.js":302}],274:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _clickableComponent = require('./clickable-component.js'); var _clickableComponent2 = _interopRequireDefault(_clickableComponent); var _component = require('./component.js'); var _component2 = _interopRequireDefault(_component); var _fn = require('./utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _dom = require('./utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _browser = require('./utils/browser.js'); var browser = _interopRequireWildcard(_browser); 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 _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; } /** * @file poster-image.js */ /** * A `ClickableComponent` that handles showing the poster image for the player. * * @extends ClickableComponent */ var PosterImage = function (_ClickableComponent) { _inherits(PosterImage, _ClickableComponent); /** * Create an instance of this class. * * @param {Player} player * The `Player` that this class should attach to. * * @param {Object} [options] * The key/value store of player options. */ function PosterImage(player, options) { _classCallCheck(this, PosterImage); var _this = _possibleConstructorReturn(this, _ClickableComponent.call(this, player, options)); _this.update(); player.on('posterchange', Fn.bind(_this, _this.update)); return _this; } /** * Clean up and dispose of the `PosterImage`. */ PosterImage.prototype.dispose = function dispose() { this.player().off('posterchange', this.update); _ClickableComponent.prototype.dispose.call(this); }; /** * Create the `PosterImage`s DOM element. * * @return {Element} * The element that gets created. */ 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; }; /** * An {@link EventTarget~EventListener} for {@link Player#posterchange} events. * * @listens Player#posterchange * * @param {EventTarget~Event} [event] * The `Player#posterchange` event that triggered this function. */ PosterImage.prototype.update = function update(event) { 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 source of the `PosterImage` depending on the display method. * * @param {string} url * The URL to the source for the `PosterImage`. */ 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; } }; /** * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See * {@link ClickableComponent#handleClick} for instances where this will be triggered. * * @listens tap * @listens click * @listens keydown * * @param {EventTarget~Event} event + The `click`, `tap` or `keydown` event that caused this function to be called. */ PosterImage.prototype.handleClick = function handleClick(event) { // We don't want a click to trigger playback when controls are disabled if (!this.player_.controls()) { return; } if (this.player_.paused()) { this.player_.play(); } else { this.player_.pause(); } }; return PosterImage; }(_clickableComponent2['default']); _component2['default'].registerComponent('PosterImage', PosterImage); exports['default'] = PosterImage; },{"./clickable-component.js":222,"./component.js":224,"./utils/browser.js":297,"./utils/dom.js":300,"./utils/fn.js":302}],275:[function(require,module,exports){ 'use strict'; exports.__esModule = true; exports.hasLoaded = exports.autoSetupTimeout = exports.autoSetup = undefined; var _dom = require('./utils/dom'); var Dom = _interopRequireWildcard(_dom); var _events = require('./utils/events.js'); var Events = _interopRequireWildcard(_events); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); 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; } } /** * @file setup.js - Functions for setting up a player without * user interaction based on the data-setup `attribute` of the video tag. * * @module setup */ var _windowLoaded = false; var videojs = void 0; /** * Set up any tags that have a data-setup `attribute` when the player is started. */ var autoSetup = function autoSetup() { // Protect against breakage in non-browser environments. if (!Dom.isReal()) { return; } // 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 = _document2['default'].getElementsByTagName('video'); var audios = _document2['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 _i2 = 0, _e2 = mediaEls.length; _i2 < _e2; _i2++) { var mediaEl = mediaEls[_i2]; // 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. 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); } }; /** * Wait until the page is loaded before running autoSetup. This will be called in * autoSetup if `hasLoaded` returns false. * * @param {number} wait * How long to wait in ms * * @param {videojs} [vjs] * The videojs library function */ function autoSetupTimeout(wait, vjs) { if (vjs) { videojs = vjs; } _window2['default'].setTimeout(autoSetup, wait); } if (Dom.isReal() && _document2['default'].readyState === 'complete') { _windowLoaded = true; } else { /** * Listen for the load event on window, and set _windowLoaded to true. * * @listens load */ Events.one(_window2['default'], 'load', function () { _windowLoaded = true; }); } /** * check if the document has been loaded */ var hasLoaded = function hasLoaded() { return _windowLoaded; }; exports.autoSetup = autoSetup; exports.autoSetupTimeout = autoSetupTimeout; exports.hasLoaded = hasLoaded; },{"./utils/dom":300,"./utils/events.js":301,"global/document":193,"global/window":194}],276:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); var _dom = require('../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _obj = require('../utils/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 _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; } /** * @file slider.js */ /** * The base functionality for a slider. Can be vertical or horizontal. * For instance the volume bar or the seek bar on a video is a slider. * * @extends Component */ var Slider = function (_Component) { _inherits(Slider, _Component); /** * Create an instance of this class * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. */ function Slider(player, options) { _classCallCheck(this, Slider); // Set property names to bar to match with the child Slider class is looking for var _this = _possibleConstructorReturn(this, _Component.call(this, player, options)); _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); return _this; } /** * Create the `Button`s DOM element. * * @param {string} type * Type of element to create. * * @param {Object} [props={}] * List of properties in Object form. * * @param {Object} [attributes={}] * list of attributes in Object form. * * @return {Element} * The element that gets created. */ 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 = (0, _obj.assign)({ tabIndex: 0 }, props); attributes = (0, _obj.assign)({ 'role': 'slider', 'aria-valuenow': 0, 'aria-valuemin': 0, 'aria-valuemax': 100, 'tabIndex': 0 }, attributes); return _Component.prototype.createEl.call(this, type, props, attributes); }; /** * Handle `mousedown` or `touchstart` events on the `Slider`. * * @param {EventTarget~Event} event * `mousedown` or `touchstart` event that triggered this function * * @listens mousedown * @listens touchstart * @fires Slider#slideractive */ Slider.prototype.handleMouseDown = function handleMouseDown(event) { var doc = this.bar.el_.ownerDocument; event.preventDefault(); Dom.blockTextSelection(); this.addClass('vjs-sliding'); /** * Triggered when the slider is in an active state * * @event Slider#slideractive * @type {EventTarget~Event} */ 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); }; /** * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`. * The `mousemove` and `touchmove` events will only only trigger this function during * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and * {@link Slider#handleMouseUp}. * * @param {EventTarget~Event} event * `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered * this function * * @listens mousemove * @listens touchmove */ Slider.prototype.handleMouseMove = function handleMouseMove(event) {}; /** * Handle `mouseup` or `touchend` events on the `Slider`. * * @param {EventTarget~Event} event * `mouseup` or `touchend` event that triggered this function. * * @listens touchend * @listens mouseup * @fires Slider#sliderinactive */ Slider.prototype.handleMouseUp = function handleMouseUp() { var doc = this.bar.el_.ownerDocument; Dom.unblockTextSelection(); this.removeClass('vjs-sliding'); /** * Triggered when the slider is no longer in an active state. * * @event Slider#sliderinactive * @type {EventTarget~Event} */ 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 the progress bar of the `Slider`. */ 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 {EventTarget~Event} event * The event that caused this function to run. * * @return {number} * The current position of the Slider. * - postition.x for vertical `Slider`s * - postition.y for horizontal `Slider`s */ Slider.prototype.calculateDistance = function calculateDistance(event) { var position = Dom.getPointerPosition(this.el_, event); if (this.vertical()) { return position.y; } return position.x; }; /** * Handle a `focus` event on this `Slider`. * * @param {EventTarget~Event} event * The `focus` event that caused this function to run. * * @listens focus */ Slider.prototype.handleFocus = function handleFocus() { this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress); }; /** * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down * arrow keys. This function will only be called when the slider has focus. See * {@link Slider#handleFocus} and {@link Slider#handleBlur}. * * @param {EventTarget~Event} event * the `keydown` event that caused this function to run. * * @listens keydown */ Slider.prototype.handleKeyPress = function handleKeyPress(event) { // Left and Down Arrows if (event.which === 37 || event.which === 40) { event.preventDefault(); this.stepBack(); // Up and Right Arrows } else if (event.which === 38 || event.which === 39) { event.preventDefault(); this.stepForward(); } }; /** * Handle a `blur` event on this `Slider`. * * @param {EventTarget~Event} event * The `blur` event that caused this function to run. * * @listens blur */ 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 that caused this object to run */ 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|Slider} * - true if slider is vertical, and getting * - false is horizontal, and getting * - a reference to this object when setting */ 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; }(_component2['default']); _component2['default'].registerComponent('Slider', Slider); exports['default'] = Slider; },{"../component.js":224,"../utils/dom.js":300,"../utils/obj":307}],277:[function(require,module,exports){ 'use strict'; exports.__esModule = true; /** * @file flash-rtmp.js * @module flash-rtmp */ /** * Add RTMP properties to the {@link Flash} Tech. * * @param {Flash} Flash * The flash tech class. * * @mixin FlashRtmpDecorator */ function FlashRtmpDecorator(Flash) { Flash.streamingFormats = { 'rtmp/mp4': 'MP4', 'rtmp/flv': 'FLV' }; /** * Join connection and stream with an ampersand. * * @param {string} connection * The connection string. * * @param {string} stream * The stream string. */ Flash.streamFromParts = function (connection, stream) { return connection + '&' + stream; }; /** * The flash parts object that contains connection and stream info. * * @typedef {Object} Flash~PartsObject * * @property {string} connection * The connection string of a source, defaults to an empty string. * * @property {string} stream * The stream string of the source, defaults to an empty string. */ /** * Convert a source url into a stream and connection parts. * * @param {string} src * the source url * * @return {Flash~PartsObject} * The parts object that contains a connection and a 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 = void 0; 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; }; /** * Check if the source type is a streaming type. * * @param {string} srcType * The mime type to check. * * @return {boolean} * - True if the source type is a streaming type. * - False if the source type is not a streaming type. */ Flash.isStreamingType = function (srcType) { return srcType in Flash.streamingFormats; }; // RTMP has four variations, any string starting // with one of these protocols should be valid /** * Regular expression used to check if the source is an rtmp source. * * @property {RegExp} Flash.RTMP_RE */ Flash.RTMP_RE = /^rtmp[set]?:\/\//i; /** * Check if the source itself is a streaming type. * * @param {string} src * The url to the source. * * @return {boolean} * - True if the source url indicates that the source is streaming. * - False if the shource url indicates that the source url is not streaming. */ 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 mime type. * * @param {string} type * The mime type to check * * @return {string} * '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 * * @param {Object} [options] * The options passed to the tech * * @return {string} * 'maybe', or '' (empty string) */ Flash.rtmpSourceHandler.canHandleSource = function (source, options) { 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. * * @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; },{}],278:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _tech = require('./tech'); var _tech2 = _interopRequireDefault(_tech); var _dom = require('../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _url = require('../utils/url.js'); var Url = _interopRequireWildcard(_url); var _timeRanges = require('../utils/time-ranges.js'); var _flashRtmp = require('./flash-rtmp'); var _flashRtmp2 = _interopRequireDefault(_flashRtmp); var _component = require('../component'); var _component2 = _interopRequireDefault(_component); var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); var _obj = require('../utils/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 _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; } /** * @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 */ var navigator = _window2['default'].navigator; /** * Flash Media Controller - Wrapper for Flash Media API * * @mixes FlashRtmpDecorator * @mixes Tech~SouceHandlerAdditions * @extends Tech */ var Flash = function (_Tech) { _inherits(Flash, _Tech); /** * Create an instance of this Tech. * * @param {Object} [options] * The key/value store of player options. * * @param {Component~ReadyCallback} ready * Callback function to call when the `Flash` Tech is ready. */ function Flash(options, ready) { _classCallCheck(this, Flash); // Set the source when ready var _this = _possibleConstructorReturn(this, _Tech.call(this, options, 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 _window2['default'].videojs = _window2['default'].videojs || {}; _window2['default'].videojs.Flash = _window2['default'].videojs.Flash || {}; _window2['default'].videojs.Flash.onReady = Flash.onReady; _window2['default'].videojs.Flash.onEvent = Flash.onEvent; _window2['default'].videojs.Flash.onError = Flash.onError; _this.on('seeked', function () { this.lastSeekTarget_ = undefined; }); return _this; } /** * Create the `Flash` Tech's DOM element. * * @return {Element} * The element that gets created. */ 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) { var ver = '5.1.0'; options.swf = '//vjs.zencdn.net/swf/' + ver + '/video-js.swf'; } // Generate ID for swf object var objId = options.techId; // Merge default flashvars with ones passed in to init var flashVars = (0, _obj.assign)({ // SWF Callback Functions readyFunction: 'videojs.Flash.onReady', eventProxyFunction: 'videojs.Flash.onEvent', errorEventProxyFunction: 'videojs.Flash.onError', // Player Settings autoplay: options.autoplay, preload: options.preload, loop: options.loop, muted: options.muted }, options.flashVars); // Merge default parames with ones passed in var params = (0, _obj.assign)({ // Opaque is needed to overlay controls, but can affect playback performance wmode: 'opaque', // Using bgcolor prevents a white flash when the object is loading bgcolor: '#000000' }, options.params); // Merge default attributes with ones passed in var attributes = (0, _obj.assign)({ // Both ID and Name needed or swf to identify itself id: objId, name: objId, 'class': 'vjs-tech' }, options.attributes); this.el_ = Flash.embed(options.swf, flashVars, params, attributes); this.el_.tech = this; return this.el_; }; /** * Called by {@link Player#play} to play using the `Flash` `Tech`. */ Flash.prototype.play = function play() { if (this.ended()) { this.setCurrentTime(0); } this.el_.vjs_play(); }; /** * Called by {@link Player#pause} to pause using the `Flash` `Tech`. */ Flash.prototype.pause = function pause() { this.el_.vjs_pause(); }; /** * A getter/setter for the `Flash` Tech's source object. * > Note: Please use {@link Flash#setSource} * * @param {Tech~SourceObject} [src] * The source object you want to set on the `Flash` techs. * * @return {Tech~SourceObject|undefined} * - The current source object when a source is not passed in. * - undefined when setting * * @deprecated Since version 5. */ 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); }; /** * A getter/setter for the `Flash` Tech's source object. * * @param {Tech~SourceObject} [src] * The source object you want to set on the `Flash` techs. * * @return {Tech~SourceObject|undefined} * - The current source object when a source is not passed in. * - undefined when setting */ Flash.prototype.setSrc = function setSrc(src) { var _this2 = this; // 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()) { this.setTimeout(function () { return _this2.play(); }, 0); } }; /** * Indicates whether the media is currently seeking to a new position or not. * * @return {boolean} * - True if seeking to a new position * - False otherwise */ Flash.prototype.seeking = function seeking() { return this.lastSeekTarget_ !== undefined; }; /** * Returns the current time in seconds that the media is at in playback. * * @param {number} time * Current playtime of the media in seconds. */ 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 the current playback time in seconds * * @return {number} * The current time of playback in seconds. */ Flash.prototype.currentTime = function currentTime() { // 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 the current source * * @method currentSrc * @return {Tech~SourceObject} * The current source */ Flash.prototype.currentSrc = function currentSrc() { if (this.currentSource_) { return this.currentSource_.src; } return this.el_.vjs_getProperty('currentSrc'); }; /** * Get the total duration of the current media. * * @return {number} 8 The total duration of the current media. */ Flash.prototype.duration = function duration() { if (this.readyState() === 0) { return NaN; } var duration = this.el_.vjs_getProperty('duration'); return duration >= 0 ? duration : Infinity; }; /** * Load media into Tech. */ Flash.prototype.load = function load() { this.el_.vjs_load(); }; /** * Get the poster image that was set on the tech. */ Flash.prototype.poster = function poster() { this.el_.vjs_getProperty('poster'); }; /** * Poster images are not handled by the Flash tech so make this is a no-op. */ Flash.prototype.setPoster = function setPoster() {}; /** * Determine the time ranges that can be seeked to in the media. * * @return {TimeRange} * Returns the time ranges that can be seeked to. */ Flash.prototype.seekable = function seekable() { var duration = this.duration(); if (duration === 0) { return (0, _timeRanges.createTimeRange)(); } return (0, _timeRanges.createTimeRange)(0, duration); }; /** * Get and create a `TimeRange` object for buffering. * * @return {TimeRange} * The time range object that was created. */ Flash.prototype.buffered = function buffered() { var ranges = this.el_.vjs_getProperty('buffered'); if (ranges.length === 0) { return (0, _timeRanges.createTimeRange)(); } return (0, _timeRanges.createTimeRange)(ranges[0][0], ranges[0][1]); }; /** * Get fullscreen support - * * Flash does not allow fullscreen through javascript * so this always returns false. * * @return {boolean} * The Flash tech does not support fullscreen, so it will always return false. */ Flash.prototype.supportsFullScreen = function supportsFullScreen() { // Flash does not allow fullscreen through javascript return false; }; /** * Flash does not allow fullscreen through javascript * so this always returns false. * * @return {boolean} * The Flash tech does not support fullscreen, so it will always return false. */ Flash.prototype.enterFullScreen = function enterFullScreen() { return false; }; return Flash; }(_tech2['default']); // Create setters and getters for attributes var _api = Flash.prototype; var _readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted'.split(','); var _readOnly = 'networkState,readyState,initialTime,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]); } /** ------------------------------ Getters ------------------------------ **/ /** * Get the value of `rtmpConnection` from the swf. * * @method Flash#rtmpConnection * @return {string} * The current value of `rtmpConnection` on the swf. */ /** * Get the value of `rtmpStream` from the swf. * * @method Flash#rtmpStream * @return {string} * The current value of `rtmpStream` on the swf. */ /** * Get the value of `preload` from the swf. `preload` indicates * what should download before the media is interacted with. It can have the following * values: * - none: nothing should be downloaded * - metadata: poster and the first few frames of the media may be downloaded to get * media dimensions and other metadata * - auto: allow the media and metadata for the media to be downloaded before * interaction * * @method Flash#preload * @return {string} * The value of `preload` from the swf. Will be 'none', 'metadata', * or 'auto'. */ /** * Get the value of `defaultPlaybackRate` from the swf. * * @method Flash#defaultPlaybackRate * @return {number} * The current value of `defaultPlaybackRate` on the swf. */ /** * Get the value of `playbackRate` from the swf. `playbackRate` indicates * the rate at which the media is currently playing back. Examples: * - if playbackRate is set to 2, media will play twice as fast. * - if playbackRate is set to 0.5, media will play half as fast. * * @method Flash#playbackRate * @return {number} * The value of `playbackRate` from the swf. A number indicating * the current playback speed of the media, where 1 is normal speed. */ /** * Get the value of `autoplay` from the swf. `autoplay` indicates * that the media should start to play as soon as the page is ready. * * @method Flash#autoplay * @return {boolean} * - The value of `autoplay` from the swf. * - True indicates that the media ashould start as soon as the page loads. * - False indicates that the media should not start as soon as the page loads. */ /** * Get the value of `loop` from the swf. `loop` indicates * that the media should return to the start of the media and continue playing once * it reaches the end. * * @method Flash#loop * @return {boolean} * - The value of `loop` from the swf. * - True indicates that playback should seek back to start once * the end of a media is reached. * - False indicates that playback should not loop back to the start when the * end of the media is reached. */ /** * Get the value of `mediaGroup` from the swf. * * @method Flash#mediaGroup * @return {string} * The current value of `mediaGroup` on the swf. */ /** * Get the value of `controller` from the swf. * * @method Flash#controller * @return {string} * The current value of `controller` on the swf. */ /** * Get the value of `controls` from the swf. `controls` indicates * whether the native flash controls should be shown or hidden. * * @method Flash#controls * @return {boolean} * - The value of `controls` from the swf. * - True indicates that native controls should be showing. * - False indicates that native controls should be hidden. */ /** * Get the value of the `volume` from the swf. `volume` indicates the current * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and * so on. * * @method Flash#volume * @return {number} * The volume percent as a decimal. Value will be between 0-1. */ /** * Get the value of the `muted` from the swf. `muted` indicates the current * audio level should be silent. * * @method Flash#muted * @return {boolean} * - True if the audio should be set to silent * - False otherwise */ /** * Get the value of `defaultMuted` from the swf. `defaultMuted` indicates * whether the media should start muted or not. Only changes the default state of the * media. `muted` and `defaultMuted` can have different values. `muted` indicates the * current state. * * @method Flash#defaultMuted * @return {boolean} * - The value of `defaultMuted` from the swf. * - True indicates that the media should start muted. * - False indicates that the media should not start muted. */ /** * Get the value of `networkState` from the swf. `networkState` indicates * the current network state. It returns an enumeration from the following list: * - 0: NETWORK_EMPTY * - 1: NEWORK_IDLE * - 2: NETWORK_LOADING * - 3: NETWORK_NO_SOURCE * * @method Flash#networkState * @return {number} * The value of `networkState` from the swf. This will be a number * from the list in the description. */ /** * Get the value of `readyState` from the swf. `readyState` indicates * the current state of the media element. It returns an enumeration from the * following list: * - 0: HAVE_NOTHING * - 1: HAVE_METADATA * - 2: HAVE_CURRENT_DATA * - 3: HAVE_FUTURE_DATA * - 4: HAVE_ENOUGH_DATA * * @method Flash#readyState * @return {number} * The value of `readyState` from the swf. This will be a number * from the list in the description. */ /** * Get the value of `readyState` from the swf. `readyState` indicates * the current state of the media element. It returns an enumeration from the * following list: * - 0: HAVE_NOTHING * - 1: HAVE_METADATA * - 2: HAVE_CURRENT_DATA * - 3: HAVE_FUTURE_DATA * - 4: HAVE_ENOUGH_DATA * * @method Flash#readyState * @return {number} * The value of `readyState` from the swf. This will be a number * from the list in the description. */ /** * Get the value of `initialTime` from the swf. * * @method Flash#initialTime * @return {number} * The `initialTime` proprety on the swf. */ /** * Get the value of `startOffsetTime` from the swf. * * @method Flash#startOffsetTime * @return {number} * The `startOffsetTime` proprety on the swf. */ /** * Get the value of `paused` from the swf. `paused` indicates whether the swf * is current paused or not. * * @method Flash#paused * @return {boolean} * The value of `paused` from the swf. */ /** * Get the value of `ended` from the swf. `ended` indicates whether * the media has reached the end or not. * * @method Flash#ended * @return {boolean} * - True indicates that the media has ended. * - False indicates that the media has not ended. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended} */ /** * Get the value of `videoWidth` from the swf. `videoWidth` indicates * the current width of the media in css pixels. * * @method Flash#videoWidth * @return {number} * The value of `videoWidth` from the swf. This will be a number * in css pixels. */ /** * Get the value of `videoHeight` from the swf. `videoHeigth` indicates * the current height of the media in css pixels. * * @method Flassh.prototype.videoHeight * @return {number} * The value of `videoHeight` from the swf. This will be a number * in css pixels. */ /** ------------------------------ Setters ------------------------------ **/ /** * Set the value of `rtmpConnection` on the swf. * * @method Flash#setRtmpConnection * @param {string} rtmpConnection * New value to set the `rtmpConnection` property to. */ /** * Set the value of `rtmpStream` on the swf. * * @method Flash#setRtmpStream * @param {string} rtmpStream * New value to set the `rtmpStream` property to. */ /** * Set the value of `preload` on the swf. `preload` indicates * what should download before the media is interacted with. It can have the following * values: * - none: nothing should be downloaded * - metadata: poster and the first few frames of the media may be downloaded to get * media dimensions and other metadata * - auto: allow the media and metadata for the media to be downloaded before * interaction * * @method Flash#setPreload * @param {string} preload * The value of `preload` to set on the swf. Should be 'none', 'metadata', * or 'auto'. */ /** * Set the value of `defaultPlaybackRate` on the swf. * * @method Flash#setDefaultPlaybackRate * @param {number} defaultPlaybackRate * New value to set the `defaultPlaybackRate` property to. */ /** * Set the value of `playbackRate` on the swf. `playbackRate` indicates * the rate at which the media is currently playing back. Examples: * - if playbackRate is set to 2, media will play twice as fast. * - if playbackRate is set to 0.5, media will play half as fast. * * @method Flash#setPlaybackRate * @param {number} playbackRate * New value of `playbackRate` on the swf. A number indicating * the current playback speed of the media, where 1 is normal speed. */ /** * Set the value of `autoplay` on the swf. `autoplay` indicates * that the media should start to play as soon as the page is ready. * * @method Flash#setAutoplay * @param {boolean} autoplay * - The value of `autoplay` from the swf. * - True indicates that the media ashould start as soon as the page loads. * - False indicates that the media should not start as soon as the page loads. */ /** * Set the value of `loop` on the swf. `loop` indicates * that the media should return to the start of the media and continue playing once * it reaches the end. * * @method Flash#setLoop * @param {boolean} loop * - True indicates that playback should seek back to start once * the end of a media is reached. * - False indicates that playback should not loop back to the start when the * end of the media is reached. */ /** * Set the value of `mediaGroup` on the swf. * * @method Flash#setMediaGroup * @param {string} mediaGroup * New value of `mediaGroup` to set on the swf. */ /** * Set the value of `controller` on the swf. * * @method Flash#setController * @param {string} controller * New value the current value of `controller` on the swf. */ /** * Get the value of `controls` from the swf. `controls` indicates * whether the native flash controls should be shown or hidden. * * @method Flash#controls * @return {boolean} * - The value of `controls` from the swf. * - True indicates that native controls should be showing. * - False indicates that native controls should be hidden. */ /** * Set the value of the `volume` on the swf. `volume` indicates the current * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and * so on. * * @method Flash#setVolume * @param {number} percentAsDecimal * The volume percent as a decimal. Value will be between 0-1. */ /** * Set the value of the `muted` on the swf. `muted` indicates that the current * audio level should be silent. * * @method Flash#setMuted * @param {boolean} muted * - True if the audio should be set to silent * - False otherwise */ /** * Set the value of `defaultMuted` on the swf. `defaultMuted` indicates * whether the media should start muted or not. Only changes the default state of the * media. `muted` and `defaultMuted` can have different values. `muted` indicates the * current state. * * @method Flash#setDefaultMuted * @param {boolean} defaultMuted * - True indicates that the media should start muted. * - False indicates that the media should not start muted. */ /* Flash Support Testing -------------------------------------------------------- */ /** * Check if the Flash tech is currently supported. * * @return {boolean} * - True if the flash tech is supported. * - False otherwise. */ Flash.isSupported = function () { return Flash.version()[0] >= 10; // return swfobject.hasFlashPlayerVersion('10'); }; // Add Source Handler pattern functions to this tech _tech2['default'].withSourceHandlers(Flash); /* * Native source handler for flash, simply passes the source to the swf element. * * @property {Tech~SourceObject} source * The source object * * @property {Flash} tech * The instance of the Flash tech */ Flash.nativeSourceHandler = {}; /** * Check if the Flash can play the given mime type. * * @param {string} type * The mimetype to check * * @return {string} * 'maybe', or '' (empty string) */ Flash.nativeSourceHandler.canPlayType = function (type) { if (type in Flash.formats) { return 'maybe'; } return ''; }; /** * Check if the media element can handle a source natively. * * @param {Tech~SourceObject} source * The source object * * @param {Object} [options] * Options to be passed to the tech. * * @return {string} * 'maybe', or '' (empty string). */ Flash.nativeSourceHandler.canHandleSource = function (source, options) { var type = void 0; 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 swf. * * @param {Tech~SourceObject} 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); }; /** * noop for native source handler dispose, as cleanup will happen automatically. */ Flash.nativeSourceHandler.dispose = function () {}; // Register the native source handler Flash.registerSourceHandler(Flash.nativeSourceHandler); /** * Flash supported mime types. * * @constant {Object} */ Flash.formats = { 'video/flv': 'FLV', 'video/x-flv': 'FLV', 'video/mp4': 'MP4', 'video/m4v': 'MP4' }; /** * Called when the the swf is "ready", and makes sure that the swf is really * ready using {@link Flash#checkReady} */ 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. * * @param {Flash} tech * The instance of the flash tech to check. */ 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 Flash Tech. * * @param {number} swfID * The id of the swf that had the event * * @param {string} eventName * The name of the event to trigger */ Flash.onEvent = function (swfID, eventName) { var tech = Dom.getEl(swfID).tech; var args = Array.prototype.slice.call(arguments, 2); // dispatch Flash events asynchronously for two reasons: // - Flash swallows any exceptions generated by javascript it // invokes // - Flash is suspended until the javascript returns which may cause // playback performance issues tech.setTimeout(function () { tech.trigger(eventName, args); }, 1); }; /** * Log errors from the swf on the Flash tech. * * @param {number} swfID * The id of the swf that had an error. * * @param {string} The error string * The error to set on the Flash Tech. * * @return {MediaError|undefined} * - Returns a MediaError when err is 'srcnotfound' * - Returns undefined otherwise. */ 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); }; /** * Get the current version of Flash that is in use on the page. * * @return {Array} * an array of versions that are available. */ Flash.version = function () { var version = '0,0,0'; // IE try { version = new _window2['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) { // satisfy linter } } return version.split(','); }; /** * Only use for non-iframe embeds. * * @param {Object} swf * The videojs-swf object. * * @param {Object} flashVars * Names and values to use as flash option variables. * * @param {Object} params * Style parameters to set on the object. * * @param {Object} attributes * Attributes to set on the element. * * @return {Element} * The embeded Flash DOM element. */ 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; }; /** * Only use for non-iframe embeds. * * @param {Object} swf * The videojs-swf object. * * @param {Object} flashVars * Names and values to use as flash option variables. * * @param {Object} params * Style parameters to set on the object. * * @param {Object} attributes * Attributes to set on the element. * * @return {Element} * The embeded Flash DOM element. */ Flash.getEmbedCode = function (swf, flashVars, params, attributes) { var objTag = ''; }); attributes = (0, _obj.assign)({ // 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 + ''; }; // Run Flash through the RTMP decorator (0, _flashRtmp2['default'])(Flash); _component2['default'].registerComponent('Flash', Flash); _tech2['default'].registerTech('Flash', Flash); exports['default'] = Flash; },{"../component":224,"../utils/dom.js":300,"../utils/obj":307,"../utils/time-ranges.js":309,"../utils/url.js":311,"./flash-rtmp":277,"./tech":281,"global/window":194}],279:[function(require,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; }; 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.']); var _tech = require('./tech.js'); var _tech2 = _interopRequireDefault(_tech); var _component = require('../component'); var _component2 = _interopRequireDefault(_component); var _dom = require('../utils/dom.js'); var Dom = _interopRequireWildcard(_dom); var _url = require('../utils/url.js'); var Url = _interopRequireWildcard(_url); var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _log = require('../utils/log.js'); var _log2 = _interopRequireDefault(_log); var _tsml = require('tsml'); var _tsml2 = _interopRequireDefault(_tsml); var _browser = require('../utils/browser.js'); var browser = _interopRequireWildcard(_browser); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); var _obj = require('../utils/obj'); var _mergeOptions = require('../utils/merge-options.js'); var _mergeOptions2 = _interopRequireDefault(_mergeOptions); var _toTitleCase = require('../utils/to-title-case.js'); var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 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; } 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; } /** * @file html5.js */ /** * HTML5 Media Controller - Wrapper for HTML5 Media API * * @mixes Tech~SouceHandlerAdditions * @extends Tech */ var Html5 = function (_Tech) { _inherits(Html5, _Tech); /** * Create an instance of this Tech. * * @param {Object} [options] * The key/value store of player options. * * @param {Component~ReadyCallback} ready * Callback function to call when the `HTML5` Tech is ready. */ function Html5(options, ready) { _classCallCheck(this, Html5); var _this = _possibleConstructorReturn(this, _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]); } } // TODO: add text tracks into this list var trackTypes = ['audio', 'video']; // ProxyNative Video/Audio Track trackTypes.forEach(function (type) { var elTracks = _this.el()[type + 'Tracks']; var techTracks = _this[type + 'Tracks'](); var capitalType = (0, _toTitleCase2['default'])(type); if (!_this['featuresNative' + capitalType + 'Tracks'] || !elTracks || !elTracks.addEventListener) { return; } _this['handle' + capitalType + 'TrackChange_'] = function (e) { techTracks.trigger({ type: 'change', target: techTracks, currentTarget: techTracks, srcElement: techTracks }); }; _this['handle' + capitalType + 'TrackAdd_'] = function (e) { return techTracks.addTrack(e.track); }; _this['handle' + capitalType + 'TrackRemove_'] = function (e) { return techTracks.removeTrack(e.track); }; elTracks.addEventListener('change', _this['handle' + capitalType + 'TrackChange_']); elTracks.addEventListener('addtrack', _this['handle' + capitalType + 'TrackAdd_']); elTracks.addEventListener('removetrack', _this['handle' + capitalType + 'TrackRemove_']); _this['removeOld' + capitalType + 'Tracks_'] = function (e) { return _this.removeOldTracks_(techTracks, elTracks); }; // Remove (native) tracks that are not used anymore _this.on('loadstart', _this['removeOld' + capitalType + 'Tracks_']); }); if (_this.featuresNativeTextTracks) { if (crossoriginTracks) { _log2['default'].warn((0, _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); } // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen` // into a `fullscreenchange` event _this.proxyWebkitFullscreen_(); _this.triggerReady(); return _this; } /** * Dispose of `HTML5` media element and remove all tracks. */ Html5.prototype.dispose = function dispose() { var _this2 = this; // Un-ProxyNativeTracks ['audio', 'video', 'text'].forEach(function (type) { var capitalType = (0, _toTitleCase2['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_']); } // Stop removing old text tracks if (tl) { _this2.off('loadstart', _this2['removeOld' + capitalType + 'Tracks_']); } }); Html5.disposeMediaElement(this.el_); // tech will handle clearing of the emulated track list _Tech.prototype.dispose.call(this); }; /** * Create the `Html5` Tech's DOM element. * * @return {Element} * The element that gets created. */ 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 we ingested the player div, we do not need to move the media element. if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) { // If the original tag is still there, clone and remove it. if (el) { var clone = el.cloneNode(true); if (el.parentNode) { el.parentNode.insertBefore(clone, el); } Html5.disposeMediaElement(el); el = clone; } else { el = _document2['default'].createElement('video'); // determine if native controls should be used var tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag); var attributes = (0, _mergeOptions2['default'])({}, tagAttributes); if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) { delete attributes.controls; } Dom.setElAttributes(el, (0, _obj.assign)(attributes, { id: this.options_.techId, 'class': 'vjs-tech' })); } el.playerId = this.options_.playerId; } // Update specific tag settings, in case they were overridden var settingsAttrs = ['autoplay', 'preload', 'loop', 'muted']; 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; }; /** * This will be triggered if the loadstart event has already fired, before videojs was * ready. Two known examples of when this can happen are: * 1. If we're loading the playback object after it has started loading * 2. The media is already playing the (often with autoplay on) then * * This function will fire another loadstart so that videojs can catchup. * * @fires Tech#loadstart * * @return {undefined} * returns nothing. */ 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: void 0 }; }(); if ((typeof _ret === 'undefined' ? 'undefined' : _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); }); }; /** * Add event listeners to native text track events. This adds the native text tracks * to our emulated {@link TextTrackList}. */ 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_); } // Remove (native) texttracks that are not used anymore this.on('loadstart', this.removeOldTextTracks_); } }; /** * Handle any {@link TextTrackList} `change` event. * * @param {EventTarget~Event} e * The `change` event that caused this to run. * * @listens TextTrackList#change */ Html5.prototype.handleTextTrackChange = function handleTextTrackChange(e) { var tt = this.textTracks(); this.textTracks().trigger({ type: 'change', target: tt, currentTarget: tt, srcElement: tt }); }; /** * Handle any {@link TextTrackList} `addtrack` event. * * @param {EventTarget~Event} e * The `addtrack` event that caused this to run. * * @listens TextTrackList#addtrack */ Html5.prototype.handleTextTrackAdd = function handleTextTrackAdd(e) { this.textTracks().addTrack_(e.track); }; /** * Handle any {@link TextTrackList} `removetrack` event. * * @param {EventTarget~Event} e * The `removetrack` event that caused this to run. * * @listens TextTrackList#removetrack */ Html5.prototype.handleTextTrackRemove = function handleTextTrackRemove(e) { this.textTracks().removeTrack_(e.track); }; /** * This function removes any {@link AudioTrack}s, {@link VideoTrack}s, or * {@link TextTrack}s that are not in the media elements TrackList. * * @param {TrackList} techTracks * HTML5 Tech's TrackList to search through * * @param {TrackList} elTracks * HTML5 media elements TrackList to search trough. * * @private */ Html5.prototype.removeOldTracks_ = function removeOldTracks_(techTracks, elTracks) { // This will loop over the techTracks and check if they are still used by the HTML5 media element // If not, they will be removed from the emulated list var removeTracks = []; if (!elTracks) { return; } for (var i = 0; i < techTracks.length; i++) { var techTrack = techTracks[i]; var found = false; for (var j = 0; j < elTracks.length; j++) { if (elTracks[j] === techTrack) { found = true; break; } } if (!found) { removeTracks.push(techTrack); } } for (var _i = 0; _i < removeTracks.length; _i++) { var track = removeTracks[_i]; techTracks.removeTrack_(track); } }; /** * Remove {@link TextTrack}s that dont exist in the native track list from our * emulated {@link TextTrackList}. * * @listens Tech#loadstart */ Html5.prototype.removeOldTextTracks_ = function removeOldTextTracks_(e) { var techTracks = this.textTracks(); var elTracks = this.el().textTracks; this.removeOldTracks_(techTracks, elTracks); }; /** * Called by {@link Player#play} to play using the `Html5` `Tech`. */ 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) {}); } }; /** * Set current time for the `HTML5` tech. * * @param {number} seconds * Set the current time of the media to this. */ Html5.prototype.setCurrentTime = function setCurrentTime(seconds) { try { this.el_.currentTime = seconds; } catch (e) { (0, _log2['default'])(e, 'Video is not ready. (Video.js)'); // this.warning(VideoJS.warnings.videoNotReady); } }; /** * Get the current duration of the HTML5 media element. * * @return {number} * The duration of the media or 0 if there is no duration. */ Html5.prototype.duration = function duration() { var _this4 = this; // Android Chrome will report duration as Infinity for VOD HLS until after // playback has started, which triggers the live display erroneously. // Return NaN if playback has not started and trigger a durationupdate once // the duration can be reliably known. if (this.el_.duration === Infinity && browser.IS_ANDROID && browser.IS_CHROME) { if (this.el_.currentTime === 0) { var _ret2 = function () { // Wait for the first `timeupdate` with currentTime > 0 - there may be // several with 0 var checkProgress = function checkProgress() { if (_this4.el_.currentTime > 0) { // Trigger durationchange for genuinely live video if (_this4.el_.duration === Infinity) { _this4.trigger('durationchange'); } _this4.off('timeupdate', checkProgress); } }; _this4.on('timeupdate', checkProgress); return { v: NaN }; }(); if ((typeof _ret2 === 'undefined' ? 'undefined' : _typeof(_ret2)) === "object") return _ret2.v; } } return this.el_.duration || NaN; }; /** * Get the current width of the HTML5 media element. * * @return {number} * The width of the HTML5 media element. */ Html5.prototype.width = function width() { return this.el_.offsetWidth; }; /** * Get the current height of the HTML5 media element. * * @return {number} * The heigth of the HTML5 media element. */ Html5.prototype.height = function height() { return this.el_.offsetHeight; }; /** * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into * `fullscreenchange` event. * * @private * @fires fullscreenchange * @listens webkitendfullscreen * @listens webkitbeginfullscreen * @listens webkitbeginfullscreen */ Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() { var _this5 = this; if (!('webkitDisplayingFullscreen' in this.el_)) { return; } var endFn = function endFn() { this.trigger('fullscreenchange', { isFullscreen: false }); }; var beginFn = function beginFn() { this.one('webkitendfullscreen', endFn); this.trigger('fullscreenchange', { isFullscreen: true }); }; this.on('webkitbeginfullscreen', beginFn); this.on('dispose', function () { _this5.off('webkitbeginfullscreen', beginFn); _this5.off('webkitendfullscreen', endFn); }); }; /** * Check if fullscreen is supported on the current playback device. * * @return {boolean} * - True if fullscreen is supported. * - False if fullscreen is not supported. */ Html5.prototype.supportsFullScreen = function supportsFullScreen() { if (typeof this.el_.webkitEnterFullScreen === 'function') { var userAgent = _window2['default'].navigator && _window2['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 that the `HTML5` Tech enter fullscreen. */ Html5.prototype.enterFullScreen = function enterFullScreen() { var video = this.el_; 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.el_.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 that the `HTML5` Tech exit fullscreen. */ Html5.prototype.exitFullScreen = function exitFullScreen() { this.el_.webkitExitFullScreen(); }; /** * A getter/setter for the `Html5` Tech's source object. * > Note: Please use {@link Html5#setSource} * * @param {Tech~SourceObject} [src] * The source object you want to set on the `HTML5` techs element. * * @return {Tech~SourceObject|undefined} * - The current source object when a source is not passed in. * - undefined when setting * * @deprecated Since version 5. */ Html5.prototype.src = function src(_src) { if (_src === undefined) { return this.el_.src; } // Setting src through `src` instead of `setSrc` will be deprecated this.setSrc(_src); }; /** * Reset the tech by removing all sources and then calling * {@link Html5.resetMediaElement}. */ Html5.prototype.reset = function reset() { Html5.resetMediaElement(this.el_); }; /** * Get the current source on the `HTML5` Tech. Falls back to returning the source from * the HTML5 media element. * * @return {Tech~SourceObject} * The current source object from the HTML5 tech. With a fallback to the * elements source. */ Html5.prototype.currentSrc = function currentSrc() { if (this.currentSource_) { return this.currentSource_.src; } return this.el_.currentSrc; }; /** * Set controls attribute for the HTML5 media Element. * * @param {string} val * Value to set the controls attribute to */ Html5.prototype.setControls = function setControls(val) { this.el_.controls = !!val; }; /** * Create and returns a remote {@link TextTrack} object. * * @param {string} kind * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) * * @param {string} [label] * Label to identify the text track * * @param {string} [language] * Two letter language abbreviation * * @return {TextTrack} * The TextTrack that gets created. */ 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 either native TextTrack or an emulated TextTrack depending * on the value of `featuresNativeTextTracks` * * @param {Object} options * The object should contain the options to intialize the TextTrack with. * * @param {string} [options.kind] * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata). * * @param {string} [options.label]. * Label to identify the text track * * @param {string} [options.language] * Two letter language abbreviation. * * @param {boolean} [options.default] * Default this track to on. * * @param {string} [options.id] * The internal id to assign this track. * * @param {string} [options.src] * A source url for the track. * * @return {HTMLTrackElement} * The track element that gets created. */ Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) { if (!this.featuresNativeTextTracks) { return _Tech.prototype.createRemoteTextTrack.call(this, options); } var htmlTrackElement = _document2['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; } return htmlTrackElement; }; /** * Creates a remote text track object and returns an html track element. * * @param {Object} options The object should contain values for * kind, language, label, and src (location of the WebVTT file) * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be * automatically removed from the video element whenever the source changes * @return {HTMLTrackElement} An Html Track Element. * This can be an emulated {@link HTMLTrackElement} or a native one. * @deprecated The default value of the "manualCleanup" parameter will default * to "false" in upcoming versions of Video.js */ Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) { var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup); if (this.featuresNativeTextTracks) { this.el().appendChild(htmlTrackElement); } return htmlTrackElement; }; /** * Remove remote `TextTrack` from `TextTrackList` object * * @param {TextTrack} track * `TextTrack` object to remove */ Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) { _Tech.prototype.removeRemoteTextTrack.call(this, track); if (this.featuresNativeTextTracks) { var tracks = this.$$('track'); var i = tracks.length; while (i--) { if (track === tracks[i] || track === tracks[i].track) { this.el().removeChild(tracks[i]); } } } }; return Html5; }(_tech2['default']); /* HTML5 Support Testing ---------------------------------------------------- */ if (Dom.isReal()) { /** * Element for testing browser HTML5 media capabilities * * @type {Element} * @constant * @private */ Html5.TEST_VID = _document2['default'].createElement('video'); var track = _document2['default'].createElement('track'); track.kind = 'captions'; track.srclang = 'en'; track.label = 'English'; Html5.TEST_VID.appendChild(track); } /** * Check if HTML5 media is supported by this browser/device. * * @return {boolean} * - True if HTML5 media is supported. * - False if HTML5 media is not supported. */ 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 && Html5.TEST_VID.canPlayType); }; /** * 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} * - True if volume can be controlled * - False otherwise */ Html5.canControlVolume = function () { // IE will error if Windows Media Player not installed #3315 try { var volume = Html5.TEST_VID.volume; Html5.TEST_VID.volume = volume / 2 + 0.1; return volume !== Html5.TEST_VID.volume; } catch (e) { return false; } }; /** * Check if the playback rate can be changed in this browser/device. * * @return {boolean} * - True if playback rate can be controlled * - False otherwise */ Html5.canControlPlaybackRate = function () { // Playback rate API is implemented in Android Chrome, but doesn't do anything // https://github.com/videojs/video.js/issues/3180 if (browser.IS_ANDROID && browser.IS_CHROME) { return false; } // IE will error if Windows Media Player not installed #3315 try { var playbackRate = Html5.TEST_VID.playbackRate; Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1; return playbackRate !== Html5.TEST_VID.playbackRate; } catch (e) { return false; } }; /** * Check to see if native `TextTrack`s are supported by this browser/device. * * @return {boolean} * - True if native `TextTrack`s are supported. * - False otherwise */ Html5.supportsNativeTextTracks = function () { return browser.IS_ANY_SAFARI; }; /** * Check to see if native `VideoTrack`s are supported by this browser/device * * @return {boolean} * - True if native `VideoTrack`s are supported. * - False otherwise */ Html5.supportsNativeVideoTracks = function () { return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks); }; /** * Check to see if native `AudioTrack`s are supported by this browser/device * * @return {boolean} * - True if native `AudioTrack`s are supported. * - False otherwise */ Html5.supportsNativeAudioTracks = function () { return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks); }; /** * 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']; /** * Boolean indicating whether the `Tech` supports volume control. * * @type {boolean} * @default {@link Html5.canControlVolume} */ Html5.prototype.featuresVolumeControl = Html5.canControlVolume(); /** * Boolean indicating whether the `Tech` supports changing the speed at which the media * plays. Examples: * - Set player to play 2x (twice) as fast * - Set player to play 0.5x (half) as fast * * @type {boolean} * @default {@link Html5.canControlPlaybackRate} */ Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate(); /** * Boolean indicating whether the `HTML5` tech currently supports the media element * moving in the DOM. iOS breaks if you move the media element, so this is set this to * false there. Everywhere else this should be true. * * @type {boolean} * @default */ Html5.prototype.movingMediaElementInDOM = !browser.IS_IOS; // TODO: Previous comment: No longer appears to be used. Can probably be removed. // Is this true? /** * Boolean indicating whether the `HTML5` tech currently supports automatic media resize * when going into fullscreen. * * @type {boolean} * @default */ Html5.prototype.featuresFullscreenResize = true; /** * Boolean indicating whether the `HTML5` tech currently supports the progress event. * If this is false, manual `progress` events will be triggred instead. * * @type {boolean} * @default */ Html5.prototype.featuresProgressEvents = true; /** * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event. * If this is false, manual `timeupdate` events will be triggred instead. * * @default */ Html5.prototype.featuresTimeupdateEvents = true; /** * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s. * * @type {boolean} * @default {@link Html5.supportsNativeTextTracks} */ Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks(); /** * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s. * * @type {boolean} * @default {@link Html5.supportsNativeVideoTracks} */ Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks(); /** * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s. * * @type {boolean} * @default {@link Html5.supportsNativeAudioTracks} */ Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks(); // HTML5 Feature detection and Device Fixes --------------------------------- // var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType; 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 && !browser.IS_FIREFOX) { 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 } else if (browser.IS_OLD_ANDROID) { 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; return r; }; // by default, patch the media 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) { // satisfy linter } })(); } }; /* Native HTML5 element property wrapping ----------------------------------- */ // Wrap native properties with a getter [ /** * Get the value of `paused` from the media element. `paused` indicates whether the media element * is currently paused or not. * * @method Html5#paused * @return {boolean} * The value of `paused` from the media element. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused} */ 'paused', /** * Get the value of `currentTime` from the media element. `currentTime` indicates * the current second that the media is at in playback. * * @method Html5#currentTime * @return {number} * The value of `currentTime` from the media element. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime} */ 'currentTime', /** * Get the value of `buffered` from the media element. `buffered` is a `TimeRange` * object that represents the parts of the media that are already downloaded and * available for playback. * * @method Html5#buffered * @return {TimeRange} * The value of `buffered` from the media element. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered} */ 'buffered', /** * Get the value of `volume` from the media element. `volume` indicates * the current playback volume of audio for a media. `volume` will be a value from 0 * (silent) to 1 (loudest and default). * * @method Html5#volume * @return {number} * The value of `volume` from the media element. Value will be between 0-1. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume} */ 'volume', /** * Get the value of `muted` from the media element. `muted` indicates * that the volume for the media should be set to silent. This does not actually change * the `volume` attribute. * * @method Html5#muted * @return {boolean} * - True if the value of `volume` should be ignored and the audio set to silent. * - False if the value of `volume` should be used. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted} */ 'muted', /** * Get the value of `poster` from the media element. `poster` indicates * that the url of an image file that can/will be shown when no media data is available. * * @method Html5#poster * @return {string} * The value of `poster` from the media element. Value will be a url to an * image. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster} */ 'poster', /** * Get the value of `preload` from the media element. `preload` indicates * what should download before the media is interacted with. It can have the following * values: * - none: nothing should be downloaded * - metadata: poster and the first few frames of the media may be downloaded to get * media dimensions and other metadata * - auto: allow the media and metadata for the media to be downloaded before * interaction * * @method Html5#preload * @return {string} * The value of `preload` from the media element. Will be 'none', 'metadata', * or 'auto'. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload} */ 'preload', /** * Get the value of `autoplay` from the media element. `autoplay` indicates * that the media should start to play as soon as the page is ready. * * @method Html5#autoplay * @return {boolean} * - The value of `autoplay` from the media element. * - True indicates that the media should start as soon as the page loads. * - False indicates that the media should not start as soon as the page loads. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay} */ 'autoplay', /** * Get the value of `controls` from the media element. `controls` indicates * whether the native media controls should be shown or hidden. * * @method Html5#controls * @return {boolean} * - The value of `controls` from the media element. * - True indicates that native controls should be showing. * - False indicates that native controls should be hidden. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls} */ 'controls', /** * Get the value of `loop` from the media element. `loop` indicates * that the media should return to the start of the media and continue playing once * it reaches the end. * * @method Html5#loop * @return {boolean} * - The value of `loop` from the media element. * - True indicates that playback should seek back to start once * the end of a media is reached. * - False indicates that playback should not loop back to the start when the * end of the media is reached. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop} */ 'loop', /** * Get the value of the `error` from the media element. `error` indicates any * MediaError that may have occured during playback. If error returns null there is no * current error. * * @method Html5#error * @return {MediaError|null} * The value of `error` from the media element. Will be `MediaError` if there * is a current error and null otherwise. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error} */ 'error', /** * Get the value of `seeking` from the media element. `seeking` indicates whether the * media is currently seeking to a new position or not. * * @method Html5#seeking * @return {boolean} * - The value of `seeking` from the media element. * - True indicates that the media is currently seeking to a new position. * - Flase indicates that the media is not seeking to a new position at this time. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking} */ 'seeking', /** * Get the value of `seekable` from the media element. `seekable` returns a * `TimeRange` object indicating ranges of time that can currently be `seeked` to. * * @method Html5#seekable * @return {TimeRange} * The value of `seekable` from the media element. A `TimeRange` object * indicating the current ranges of time that can be seeked to. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable} */ 'seekable', /** * Get the value of `ended` from the media element. `ended` indicates whether * the media has reached the end or not. * * @method Html5#ended * @return {boolean} * - The value of `ended` from the media element. * - True indicates that the media has ended. * - False indicates that the media has not ended. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended} */ 'ended', /** * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates * whether the media should start muted or not. Only changes the default state of the * media. `muted` and `defaultMuted` can have different values. `muted` indicates the * current state. * * @method Html5#defaultMuted * @return {boolean} * - The value of `defaultMuted` from the media element. * - True indicates that the media should start muted. * - False indicates that the media should not start muted * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted} */ 'defaultMuted', /** * Get the value of `playbackRate` from the media element. `playbackRate` indicates * the rate at which the media is currently playing back. Examples: * - if playbackRate is set to 2, media will play twice as fast. * - if playbackRate is set to 0.5, media will play half as fast. * * @method Html5#playbackRate * @return {number} * The value of `playbackRate` from the media element. A number indicating * the current playback speed of the media, where 1 is normal speed. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate} */ 'playbackRate', /** * Get the value of `played` from the media element. `played` returns a `TimeRange` * object representing points in the media timeline that have been played. * * @method Html5#played * @return {TimeRange} * The value of `played` from the media element. A `TimeRange` object indicating * the ranges of time that have been played. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played} */ 'played', /** * Get the value of `networkState` from the media element. `networkState` indicates * the current network state. It returns an enumeration from the following list: * - 0: NETWORK_EMPTY * - 1: NEWORK_IDLE * - 2: NETWORK_LOADING * - 3: NETWORK_NO_SOURCE * * @method Html5#networkState * @return {number} * The value of `networkState` from the media element. This will be a number * from the list in the description. * * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate} */ 'networkState', /** * Get the value of `readyState` from the media element. `readyState` indicates * the current state of the media element. It returns an enumeration from the * following list: * - 0: HAVE_NOTHING * - 1: HAVE_METADATA * - 2: HAVE_CURRENT_DATA * - 3: HAVE_FUTURE_DATA * - 4: HAVE_ENOUGH_DATA * * @method Html5#readyState * @return {number} * The value of `readyState` from the media element. This will be a number * from the list in the description. * * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states} */ 'readyState', /** * Get the value of `videoWidth` from the video element. `videoWidth` indicates * the current width of the video in css pixels. * * @method Html5#videoWidth * @return {number} * The value of `videoWidth` from the video element. This will be a number * in css pixels. * * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth} */ 'videoWidth', /** * Get the value of `videoHeight` from the video element. `videoHeigth` indicates * the current height of the video in css pixels. * * @method Html5#videoHeight * @return {number} * The value of `videoHeight` from the video element. This will be a number * in css pixels. * * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth} */ 'videoHeight'].forEach(function (prop) { Html5.prototype[prop] = function () { return this.el_[prop]; }; }); // Wrap native properties with a setter in this format: // set + toTitleCase(name) [ /** * Set the value of `volume` on the media element. `volume` indicates the current * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and * so on. * * @method Html5#setVolume * @param {number} percentAsDecimal * The volume percent as a decimal. Valid range is from 0-1. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume} */ 'volume', /** * Set the value of `muted` on the media element. `muted` indicates the current * audio level should be silent. * * @method Html5#setMuted * @param {boolean} muted * - True if the audio should be set to silent * - False otherwise * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted} */ 'muted', /** * Set the value of `src` on the media element. `src` indicates the current * {@link Tech~SourceObject} for the media. * * @method Html5#setSrc * @param {Tech~SourceObject} src * The source object to set as the current source. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src} */ 'src', /** * Set the value of `poster` on the media element. `poster` is the url to * an image file that can/will be shown when no media data is available. * * @method Html5#setPoster * @param {string} poster * The url to an image that should be used as the `poster` for the media * element. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster} */ 'poster', /** * Set the value of `preload` on the media element. `preload` indicates * what should download before the media is interacted with. It can have the following * values: * - none: nothing should be downloaded * - metadata: poster and the first few frames of the media may be downloaded to get * media dimensions and other metadata * - auto: allow the media and metadata for the media to be downloaded before * interaction * * @method Html5#setPreload * @param {string} preload * The value of `preload` to set on the media element. Must be 'none', 'metadata', * or 'auto'. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload} */ 'preload', /** * Set the value of `autoplay` on the media element. `autoplay` indicates * that the media should start to play as soon as the page is ready. * * @method Html5#setAutoplay * @param {boolean} autoplay * - True indicates that the media should start as soon as the page loads. * - False indicates that the media should not start as soon as the page loads. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay} */ 'autoplay', /** * Set the value of `loop` on the media element. `loop` indicates * that the media should return to the start of the media and continue playing once * it reaches the end. * * @method Html5#setLoop * @param {boolean} loop * - True indicates that playback should seek back to start once * the end of a media is reached. * - False indicates that playback should not loop back to the start when the * end of the media is reached. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop} */ 'loop', /** * Set the value of `playbackRate` on the media element. `playbackRate` indicates * the rate at which the media should play back. Examples: * - if playbackRate is set to 2, media will play twice as fast. * - if playbackRate is set to 0.5, media will play half as fast. * * @method Html5#setPlaybackRate * @return {number} * The value of `playbackRate` from the media element. A number indicating * the current playback speed of the media, where 1 is normal speed. * * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate} */ 'playbackRate'].forEach(function (prop) { Html5.prototype['set' + (0, _toTitleCase2['default'])(prop)] = function (v) { this.el_[prop] = v; }; }); // wrap native functions with a function [ /** * A wrapper around the media elements `pause` function. This will call the `HTML5` * media elements `pause` function. * * @method Html5#pause * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause} */ 'pause', /** * A wrapper around the media elements `load` function. This will call the `HTML5`s * media element `load` function. * * @method Html5#load * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load} */ 'load'].forEach(function (prop) { Html5.prototype[prop] = function () { return this.el_[prop](); }; }); _tech2['default'].withSourceHandlers(Html5); /** * Native source handler for Html5, simply passes the source to the media element. * * @proprety {Tech~SourceObject} source * The source object * * @proprety {Html5} tech * The instance of the HTML5 tech. */ Html5.nativeSourceHandler = {}; /** * Check if the media element can play the given mime type. * * @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 media element can handle a source natively. * * @param {Tech~SourceObject} source * The source object * * @param {Object} [options] * Options to be passed to the tech. * * @return {string} * 'probably', 'maybe', or '' (empty string). */ Html5.nativeSourceHandler.canHandleSource = function (source, options) { // If a type was provided we should rely on that if (source.type) { return Html5.nativeSourceHandler.canPlayType(source.type); // If no type, fall back to checking 'video/[EXTENSION]' } else if (source.src) { var ext = Url.getFileExtension(source.src); return Html5.nativeSourceHandler.canPlayType('video/' + ext); } return ''; }; /** * Pass the source to the native media element. * * @param {Tech~SourceObject} 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); }; /** * A noop for the native dispose function, as cleanup is not needed. */ Html5.nativeSourceHandler.dispose = function () {}; // Register the native source handler Html5.registerSourceHandler(Html5.nativeSourceHandler); _component2['default'].registerComponent('Html5', Html5); _tech2['default'].registerTech('Html5', Html5); exports['default'] = Html5; },{"../component":224,"../utils/browser.js":297,"../utils/dom.js":300,"../utils/fn.js":302,"../utils/log.js":305,"../utils/merge-options.js":306,"../utils/obj":307,"../utils/to-title-case.js":310,"../utils/url.js":311,"./tech.js":281,"global/document":193,"global/window":194,"tsml":218}],280:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../component.js'); var _component2 = _interopRequireDefault(_component); var _tech = require('./tech.js'); var _tech2 = _interopRequireDefault(_tech); var _toTitleCase = require('../utils/to-title-case.js'); var _toTitleCase2 = _interopRequireDefault(_toTitleCase); 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; } /** * @file loader.js */ /** * The `MediaLoader` is the `Component` that decides which playback technology to load * when a player is initialized. * * @extends Component */ var MediaLoader = function (_Component) { _inherits(MediaLoader, _Component); /** * Create an instance of this class. * * @param {Player} player * The `Player` that this class should attach to. * * @param {Object} [options] * The key/value stroe of player options. * * @param {Component~ReadyCallback} [ready] * The function that is run when this component is ready. */ function MediaLoader(player, options, ready) { _classCallCheck(this, MediaLoader); // If there are no sources when the player is initialized, // load the first supported playback technology. var _this = _possibleConstructorReturn(this, _Component.call(this, player, options, ready)); if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) { for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) { var techName = (0, _toTitleCase2['default'])(j[i]); var tech = _tech2['default'].getTech(techName); // Support old behavior of techs being registered as components. // Remove once that deprecated behavior is removed. if (!techName) { tech = _component2['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 _this; } return MediaLoader; }(_component2['default']); _component2['default'].registerComponent('MediaLoader', MediaLoader); exports['default'] = MediaLoader; },{"../component.js":224,"../utils/to-title-case.js":310,"./tech.js":281}],281:[function(require,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; }; var _component = require('../component'); var _component2 = _interopRequireDefault(_component); var _htmlTrackElement = require('../tracks/html-track-element'); var _htmlTrackElement2 = _interopRequireDefault(_htmlTrackElement); var _htmlTrackElementList = require('../tracks/html-track-element-list'); var _htmlTrackElementList2 = _interopRequireDefault(_htmlTrackElementList); var _mergeOptions = require('../utils/merge-options.js'); var _mergeOptions2 = _interopRequireDefault(_mergeOptions); var _textTrack = require('../tracks/text-track'); var _textTrack2 = _interopRequireDefault(_textTrack); var _textTrackList = require('../tracks/text-track-list'); var _textTrackList2 = _interopRequireDefault(_textTrackList); var _videoTrackList = require('../tracks/video-track-list'); var _videoTrackList2 = _interopRequireDefault(_videoTrackList); var _audioTrackList = require('../tracks/audio-track-list'); var _audioTrackList2 = _interopRequireDefault(_audioTrackList); var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _log = require('../utils/log.js'); var _log2 = _interopRequireDefault(_log); var _timeRanges = require('../utils/time-ranges.js'); var _buffer = require('../utils/buffer.js'); var _mediaError = require('../media-error.js'); var _mediaError2 = _interopRequireDefault(_mediaError); var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); var _obj = require('../utils/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 _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; } /** * @file tech.js */ /** * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string * that just contains the src url alone. * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};` * `var SourceString = 'http://example.com/some-video.mp4';` * * @typedef {Object|string} Tech~SourceObject * * @property {string} src * The url to the source * * @property {string} type * The mime type of the source */ /** * A function used by {@link Tech} to create a new {@link TextTrack}. * * @param {Tech} self * An instance of the Tech class. * * @param {string} kind * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) * * @param {string} [label] * Label to identify the text track * * @param {string} [language] * Two letter language abbreviation * * @param {Object} [options={}] * An object with additional text track options * * @return {TextTrack} * The text track that was created. */ 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 _textTrack2['default'](options); tracks.addTrack_(track); return track; } /** * This is the base class for media playback technology controllers, such as * {@link Flash} and {@link HTML5} * * @extends Component */ var Tech = function (_Component) { _inherits(Tech, _Component); /** * Create an instance of this Tech. * * @param {Object} [options] * The key/value store of player options. * * @param {Component~ReadyCallback} ready * Callback function to call when the `HTML5` Tech is ready. */ function Tech() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; _classCallCheck(this, Tech); // we don't want the tech to report user activity automatically. // This is done manually in addControlsListeners options.reportTouchActivity = false; // keep track of whether the current source has played at all to // implement a very limited played() var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready)); _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(); } ['Text', 'Audio', 'Video'].forEach(function (track) { if (options['native' + track + 'Tracks'] === false) { _this['featuresNative' + track + 'Tracks'] = false; } }); if (options.nativeCaptions === false) { _this.featuresNativeTextTracks = false; } if (!_this.featuresNativeTextTracks) { _this.emulateTextTracks(); } _this.autoRemoteTextTracks_ = new _textTrackList2['default'](); _this.initTextTrackListeners(); _this.initTrackListeners(); // Turn on component tap events only if not using native controls if (!options.nativeControlsForTouch) { _this.emitTapEvents(); } if (_this.constructor) { _this.name_ = _this.constructor.name || 'Unknown Tech'; } return _this; } /* Fallbacks for unsupported event types ================================================================================ */ /** * Polyfill the `progress` event for browsers that don't support it natively. * * @see {@link Tech#trackProgress} */ 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 the polyfill for `progress` events that was created in * {@link Tech#manualProgressOn} */ Tech.prototype.manualProgressOff = function manualProgressOff() { this.manualProgress = false; this.stopTrackingProgress(); this.off('durationchange', this.onDurationChange); }; /** * This is used to trigger a `progress` event when the buffered percent changes. It * sets an interval function that will be called every 500 milliseconds to check if the * buffer end percent has changed. * * > This function is called by {@link Tech#manualProgressOn} * * @param {EventTarget~Event} event * The `ready` event that caused this to run. * * @listens Tech#ready * @fires Tech#progress */ Tech.prototype.trackProgress = function trackProgress(event) { 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) { /** * See {@link Player#progress} * * @event Tech#progress * @type {EventTarget~Event} */ this.trigger('progress'); } this.bufferedPercent_ = numBufferedPercent; if (numBufferedPercent === 1) { this.stopTrackingProgress(); } }), 500); }; /** * Update our internal duration on a `durationchange` event by calling * {@link Tech#duration}. * * @param {EventTarget~Event} event * The `durationchange` event that caused this to run. * * @listens Tech#durationchange */ Tech.prototype.onDurationChange = function onDurationChange(event) { this.duration_ = this.duration(); }; /** * Get and create a `TimeRange` object for buffering. * * @return {TimeRange} * The time range object that was created. */ Tech.prototype.buffered = function buffered() { return (0, _timeRanges.createTimeRange)(0, 0); }; /** * Get the percentage of the current video that is currently buffered. * * @return {number} * A number from 0 to 1 that represents the decimal percentage of the * video that is buffered. * */ Tech.prototype.bufferedPercent = function bufferedPercent() { return (0, _buffer.bufferedPercent)(this.buffered(), this.duration_); }; /** * Turn off the polyfill for `progress` events that was created in * {@link Tech#manualProgressOn} * Stop manually tracking progress events by clearing the interval that was set in * {@link Tech#trackProgress}. */ Tech.prototype.stopTrackingProgress = function stopTrackingProgress() { this.clearInterval(this.progressInterval); }; /** * Polyfill the `timeupdate` event for browsers that don't support it. * * @see {@link Tech#trackCurrentTime} */ Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() { this.manualTimeUpdates = true; this.on('play', this.trackCurrentTime); this.on('pause', this.stopTrackingCurrentTime); }; /** * Turn off the polyfill for `timeupdate` events that was created in * {@link Tech#manualTimeUpdatesOn} */ Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() { this.manualTimeUpdates = false; this.stopTrackingCurrentTime(); this.off('play', this.trackCurrentTime); this.off('pause', this.stopTrackingCurrentTime); }; /** * Sets up an interval function to track current time and trigger `timeupdate` every * 250 milliseconds. * * @listens Tech#play * @triggers Tech#timeupdate */ Tech.prototype.trackCurrentTime = function trackCurrentTime() { if (this.currentTimeInterval) { this.stopTrackingCurrentTime(); } this.currentTimeInterval = this.setInterval(function () { /** * Triggered at an interval of 250ms to indicated that time is passing in the video. * * @event Tech#timeupdate * @type {EventTarget~Event} */ this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15 }, 250); }; /** * Stop the interval function created in {@link Tech#trackCurrentTime} so that the * `timeupdate` event is no longer triggered. * * @listens {Tech#pause} */ 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 all event polyfills, clear the `Tech`s {@link AudioTrackList}, * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech. * * @fires Component#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 single `TrackList` or an array of `TrackLists` given their names. * * > Note: Techs without source handlers should call this between sources for `video` * & `audio` tracks. You don't want to use them between tracks! * * @param {string[]|string} types * TrackList names to clear, valid names are `video`, `audio`, and * `text`. */ Tech.prototype.clearTracks = function clearTracks(types) { var _this2 = this; types = [].concat(types); // clear out all tracks because we can't reuse them between techs types.forEach(function (type) { var list = _this2[type + 'Tracks']() || []; var i = list.length; while (i--) { var track = list[i]; if (type === 'text') { _this2.removeRemoteTextTrack(track); } list.removeTrack_(track); } }); }; /** * Remove any TextTracks added via addRemoteTextTrack that are * flagged for automatic garbage collection */ Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() { var list = this.autoRemoteTextTracks_ || []; var i = list.length; while (i--) { var track = list[i]; this.removeRemoteTextTrack(track); } }; /** * Reset the tech, which will removes all sources and reset the internal readyState. * * @abstract */ Tech.prototype.reset = function reset() {}; /** * Get or set an error on the Tech. * * @param {MediaError} [err] * Error to set on the Tech * * @return {MediaError|null} * The current error object on the tech, or null if there isn't one. */ Tech.prototype.error = function error(err) { if (err !== undefined) { this.error_ = new _mediaError2['default'](err); this.trigger('error'); } return this.error_; }; /** * Returns the `TimeRange`s that have been played through for the current source. * * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`. * It only checks wether the source has played at all or not. * * @return {TimeRange} * - A single time range if this video has played * - An empty set of ranges if not. */ Tech.prototype.played = function played() { if (this.hasStarted_) { return (0, _timeRanges.createTimeRange)(0, 0); } return (0, _timeRanges.createTimeRange)(); }; /** * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was * previously called. * * @fires Tech#timeupdate */ Tech.prototype.setCurrentTime = function setCurrentTime() { // improve the accuracy of manual timeupdates if (this.manualTimeUpdates) { /** * A manual `timeupdate` event. * * @event Tech#timeupdate * @type {EventTarget~Event} */ this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true }); } }; /** * Turn on listeners for {@link TextTrackList} events. This adds * {@link EventTarget~EventListeners} for `texttrackchange`, `addtrack` and * `removetrack`. * * @fires Tech#texttrackchange */ Tech.prototype.initTextTrackListeners = function initTextTrackListeners() { var textTrackListChanges = Fn.bind(this, function () { /** * Triggered when tracks are added or removed on the Tech {@link TextTrackList} * * @event Tech#texttrackchange * @type {EventTarget~Event} */ 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); })); }; /** * Turn on listeners for {@link VideoTrackList} and {@link {AudioTrackList} events. * This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`. * * @fires Tech#audiotrackchange * @fires Tech#videotrackchange */ Tech.prototype.initTrackListeners = function initTrackListeners() { var _this3 = this; var trackTypes = ['video', 'audio']; trackTypes.forEach(function (type) { /** * Triggered when tracks are added or removed on the Tech {@link AudioTrackList} * * @event Tech#audiotrackchange * @type {EventTarget~Event} */ /** * Triggered when tracks are added or removed on the Tech {@link VideoTrackList} * * @event Tech#videotrackchange * @type {EventTarget~Event} */ var trackListChanges = function trackListChanges() { _this3.trigger(type + 'trackchange'); }; var tracks = _this3[type + 'Tracks'](); tracks.addEventListener('removetrack', trackListChanges); tracks.addEventListener('addtrack', trackListChanges); _this3.on('dispose', function () { tracks.removeEventListener('removetrack', trackListChanges); tracks.removeEventListener('addtrack', trackListChanges); }); }); }; /** * Emulate TextTracks using vtt.js if necessary * * @fires Tech#vttjsloaded * @fires Tech#vttjserror */ Tech.prototype.addWebVttScript_ = function addWebVttScript_() { var _this4 = this; if (!_window2['default'].WebVTT && this.el().parentNode !== null && this.el().parentNode !== undefined) { var _ret = function () { var vtt = require('videojs-vtt.js'); // load via require if available and vtt.js script location was not passed in // as an option. novtt builds will turn the above require call into an empty object // which will cause this if check to always fail. if (!_this4.options_['vtt.js'] && (0, _obj.isPlain)(vtt) && Object.keys(vtt).length > 0) { Object.keys(vtt).forEach(function (k) { _window2['default'][k] = vtt[k]; }); _this4.trigger('vttjsloaded'); return { v: void 0 }; } // load vtt.js via the script location option or the cdn of no location was // passed in var script = _document2['default'].createElement('script'); script.src = _this4.options_['vtt.js'] || 'https://cdn.rawgit.com/gkatsev/vtt.js/vjs-v0.12.1/dist/vtt.min.js'; script.onload = function () { /** * Fired when vtt.js is loaded. * * @event Tech#vttjsloaded * @type {EventTarget~Event} */ _this4.trigger('vttjsloaded'); }; script.onerror = function () { /** * Fired when vtt.js was not loaded due to an error * * @event Tech#vttjsloaded * @type {EventTarget~Event} */ _this4.trigger('vttjserror'); }; _this4.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 _window2['default'].WebVTT = true; _this4.el().parentNode.appendChild(script); }(); if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; } }; /** * Emulate texttracks * * @method emulateTextTracks */ Tech.prototype.emulateTextTracks = function emulateTextTracks() { var _this5 = this; var tracks = this.textTracks(); if (!tracks) { return; } this.remoteTextTracks().on('addtrack', function (e) { _this5.textTracks().addTrack_(e.track); }); this.remoteTextTracks().on('removetrack', function (e) { _this5.textTracks().removeTrack_(e.track); }); // Initially, Tech.el_ is a child of a dummy-div wait until the Component system // signals that the Tech is ready at which point Tech.el_ is part of the DOM // before inserting the WebVTT script this.on('ready', this.addWebVttScript_); var updateDisplay = function updateDisplay() { return _this5.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 the `Tech`s {@link VideoTrackList}. * * @return {VideoTrackList} * The video track list that the Tech is currently using. */ Tech.prototype.videoTracks = function videoTracks() { this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default'](); return this.videoTracks_; }; /** * Get the `Tech`s {@link AudioTrackList}. * * @return {AudioTrackList} * The audio track list that the Tech is currently using. */ Tech.prototype.audioTracks = function audioTracks() { this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default'](); return this.audioTracks_; }; /** * Get the `Tech`s {@link TextTrackList}. * * @return {TextTrackList} * The text track list that the Tech is currently using. */ Tech.prototype.textTracks = function textTracks() { this.textTracks_ = this.textTracks_ || new _textTrackList2['default'](); return this.textTracks_; }; /** * Get the `Tech`s remote {@link TextTrackList}, which is created from elements * that were added to the DOM. * * @return {TextTrackList} * The remote text track list that the Tech is currently using. */ Tech.prototype.remoteTextTracks = function remoteTextTracks() { this.remoteTextTracks_ = this.remoteTextTracks_ || new _textTrackList2['default'](); return this.remoteTextTracks_; }; /** * Get The `Tech`s {HTMLTrackElementList}, which are the elements in the DOM that are * being used as TextTracks. * * @return {HTMLTrackElementList} * The current HTML track elements that exist for the tech. */ Tech.prototype.remoteTextTrackEls = function remoteTextTrackEls() { this.remoteTextTrackEls_ = this.remoteTextTrackEls_ || new _htmlTrackElementList2['default'](); return this.remoteTextTrackEls_; }; /** * Create and returns a remote {@link TextTrack} object. * * @param {string} kind * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata) * * @param {string} [label] * Label to identify the text track * * @param {string} [language] * Two letter language abbreviation * * @return {TextTrack} * The TextTrack that gets created. */ 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); }; /** * Create an emulated TextTrack for use by addRemoteTextTrack * * This is intended to be overridden by classes that inherit from * Tech in order to create native or custom TextTracks. * * @param {Object} options * The object should contain the options to initialize the TextTrack with. * * @param {string} [options.kind] * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata). * * @param {string} [options.label]. * Label to identify the text track * * @param {string} [options.language] * Two letter language abbreviation. * * @return {HTMLTrackElement} * The track element that gets created. */ Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) { var track = (0, _mergeOptions2['default'])(options, { tech: this }); return new _htmlTrackElement2['default'](track); }; /** * Creates a remote text track object and returns an html track element. * * > Note: This can be an emulated {@link HTMLTrackElement} or a native one. * * @param {Object} options * See {@link Tech#createRemoteTextTrack} for more detailed properties. * * @param {boolean} [manualCleanup=true] * - When false: the TextTrack will be automatically removed from the video * element whenever the source changes * - When True: The TextTrack will have to be cleaned up manually * * @return {HTMLTrackElement} * An Html Track Element. * * @deprecated The default functionality for this function will be equivalent * to "manualCleanup=false" in the future. The manualCleanup parameter will * also be removed. */ Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var manualCleanup = arguments[1]; var htmlTrackElement = this.createRemoteTextTrack(options); if (manualCleanup !== true && manualCleanup !== false) { // deprecation warning _log2['default'].warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js'); manualCleanup = true; } // store HTMLTrackElement and TextTrack to remote list this.remoteTextTrackEls().addTrackElement_(htmlTrackElement); this.remoteTextTracks().addTrack_(htmlTrackElement.track); if (manualCleanup !== true) { // create the TextTrackList if it doesn't exist this.autoRemoteTextTracks_.addTrack_(htmlTrackElement.track); } return htmlTrackElement; }; /** * Remove a remote text track from the remote `TextTrackList`. * * @param {TextTrack} track * `TextTrack` to remove from the `TextTrackList` */ Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) { var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track); // remove HTMLTrackElement and TextTrack from remote list this.remoteTextTrackEls().removeTrackElement_(trackElement); this.remoteTextTracks().removeTrack_(track); this.autoRemoteTextTracks_.removeTrack_(track); }; /** * A method to set a poster from a `Tech`. * * @abstract */ Tech.prototype.setPoster = function setPoster() {}; /* * Check if the tech can support the given mime-type. * * The base tech does not support any type, but source handlers might * overwrite this. * * @param {string} type * The mimetype to check for support * * @return {string} * 'probably', 'maybe', or empty string * * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType} * * @abstract */ 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 * The item to check * * @return {boolean} * Whether it is a tech or not * - True if it is a tech * - False if it is not */ Tech.isTech = function isTech(component) { return component.prototype instanceof Tech || component instanceof Tech || component === Tech; }; /** * Registers a `Tech` into a shared list for videojs. * * @param {string} name * Name of the `Tech` to register. * * @param {Object} tech * The `Tech` class to register. */ 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; }; /** * Get a `Tech` from the shared list by name. * * @param {string} name * Name of the component to get * * @return {Tech|undefined} * The `Tech` or undefined if there was no tech with the name requsted. */ Tech.getTech = function getTech(name) { if (Tech.techs_ && Tech.techs_[name]) { return Tech.techs_[name]; } if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) { _log2['default'].warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)'); return _window2['default'].videojs[name]; } }; return Tech; }(_component2['default']); /** * List of associated text tracks. * * @type {TextTrackList} * @private */ Tech.prototype.textTracks_; // eslint-disable-line /** * List of associated audio tracks. * * @type {AudioTrackList} * @private */ Tech.prototype.audioTracks_; // eslint-disable-line /** * List of associated video tracks. * * @type {VideoTrackList} * @private */ Tech.prototype.videoTracks_; // eslint-disable-line /** * Boolean indicating wether the `Tech` supports volume control. * * @type {boolean} * @default */ Tech.prototype.featuresVolumeControl = true; /** * Boolean indicating wether the `Tech` support fullscreen resize control. * Resizing plugins using request fullscreen reloads the plugin * * @type {boolean} * @default */ Tech.prototype.featuresFullscreenResize = false; /** * Boolean indicating wether the `Tech` supports changing the speed at which the video * plays. Examples: * - Set player to play 2x (twice) as fast * - Set player to play 0.5x (half) as fast * * @type {boolean} * @default */ Tech.prototype.featuresPlaybackRate = false; /** * Boolean indicating wether the `Tech` supports the `progress` event. This is currently * not triggered by video-js-swf. This will be used to determine if * {@link Tech#manualProgressOn} should be called. * * @type {boolean} * @default */ Tech.prototype.featuresProgressEvents = false; /** * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently * not triggered by video-js-swf. This will be used to determine if * {@link Tech#manualTimeUpdates} should be called. * * @type {boolean} * @default */ Tech.prototype.featuresTimeupdateEvents = false; /** * Boolean indicating wether the `Tech` supports the native `TextTrack`s. * This will help us integrate with native `TextTrack`s if the browser supports them. * * @type {boolean} * @default */ Tech.prototype.featuresNativeTextTracks = false; /** * A functional mixin for techs that want to use the Source Handler pattern. * 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) * Example: `Tech.withSourceHandlers.call(MyTech);` * * @param {Tech} _Tech * The tech to add source handler functions to. * * @mixes Tech~SourceHandlerAdditions */ Tech.withSourceHandlers = function (_Tech) { /** * Register a source handler * * @param {Function} handler * The source handler class * * @param {number} [index] * Register it at the following index */ _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. Also checks the * Techs sourceHandlers. * * @param {string} type * The mimetype to check. * * @return {string} * 'probably', 'maybe', or '' (empty string) */ _Tech.canPlayType = function (type) { var handlers = _Tech.sourceHandlers || []; var can = void 0; for (var i = 0; i < handlers.length; i++) { can = handlers[i].canPlayType(type); if (can) { return can; } } return ''; }; /** * Returns the first source handler that supports the source. * * TODO: Answer question: should 'probably' be prioritized over 'maybe' * * @param {Tech~SourceObject} source * The source object * * @param {Object} options * The options passed to the tech * * @return {SourceHandler|null} * The first source handler that supports the source or null if * no SourceHandler supports the source */ _Tech.selectSourceHandler = function (source, options) { var handlers = _Tech.sourceHandlers || []; var can = void 0; for (var i = 0; i < handlers.length; i++) { can = handlers[i].canHandleSource(source, options); if (can) { return handlers[i]; } } return null; }; /** * Check if the tech can support the given source. * * @param {Tech~SourceObject} srcObj * The source object * * @param {Object} options * The options passed to the tech * * @return {string} * 'probably', 'maybe', or '' (empty string) */ _Tech.canPlaySource = function (srcObj, options) { var sh = _Tech.selectSourceHandler(srcObj, options); if (sh) { return sh.canHandleSource(srcObj, options); } return ''; }; /** * When using a source handler, prefer its implementation of * any function normally provided by the tech. */ var deferrable = ['seekable', 'duration']; /** * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable * function if it exists, with a fallback to the Techs seekable function. * * @method _Tech.seekable */ /** * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration * function if it exists, otherwise it will fallback to the techs duration function. * * @method _Tech.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 {Tech~SourceObject} source * A source object with src and type keys * * @return {Tech} * Returns itself; this method is chainable */ _Tech.prototype.setSource = function (source) { var sh = _Tech.selectSourceHandler(source, this.options_); if (!sh) { // Fall back to a native source hander when unsupported sources are // deliberately set if (_Tech.nativeSourceHandler) { sh = _Tech.nativeSourceHandler; } else { _log2['default'].error('No source hander found for the current source.'); } } // Dispose any existing source handler this.disposeSourceHandler(); this.off('dispose', this.disposeSourceHandler); 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; }; /** * Called once for the first loadstart of a video. * * @listens Tech#loadstart */ _Tech.prototype.firstLoadStartListener_ = function () { this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_); }; // On successive loadstarts when setSource has not been called again /** * Called after the first loadstart for a video occurs. * * @listens Tech#loadstart */ _Tech.prototype.successiveLoadStartListener_ = function () { this.disposeSourceHandler(); this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_); }; /** * Clean up any existing SourceHandlers and listeners when the Tech is disposed. * * @listens Tech#dispose */ _Tech.prototype.disposeSourceHandler = function () { // 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; } // always clean up auto-text tracks this.cleanupAutoTextTracks(); if (this.sourceHandler_) { this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_); this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_); if (this.sourceHandler_.dispose) { this.sourceHandler_.dispose(); } this.sourceHandler_ = null; } }; }; _component2['default'].registerComponent('Tech', Tech); // Old name for Tech // @deprecated _component2['default'].registerComponent('MediaTechController', Tech); Tech.registerTech('Tech', Tech); exports['default'] = Tech; },{"../component":224,"../media-error.js":265,"../tracks/audio-track-list":282,"../tracks/html-track-element":285,"../tracks/html-track-element-list":284,"../tracks/text-track":291,"../tracks/text-track-list":289,"../tracks/video-track-list":295,"../utils/buffer.js":298,"../utils/fn.js":302,"../utils/log.js":305,"../utils/merge-options.js":306,"../utils/obj":307,"../utils/time-ranges.js":309,"global/document":193,"global/window":194,"videojs-vtt.js":465}],282:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _trackList = require('./track-list'); var _trackList2 = _interopRequireDefault(_trackList); var _browser = require('../utils/browser.js'); var browser = _interopRequireWildcard(_browser); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); 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 _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; } /** * @file audio-track-list.js */ /** * Anywhere we call this function we diverge from the spec * as we only support one enabled audiotrack at a time * * @param {AudioTrackList} list * list to work on * * @param {AudioTrack} track * The track to skip * * @private */ 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; } }; /** * The current list of {@link AudioTrack} for a media file. * * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist} * @extends TrackList */ var AudioTrackList = function (_TrackList) { _inherits(AudioTrackList, _TrackList); /** * Create an instance of this class. * * @param {AudioTrack[]} [tracks=[]] * A list of `AudioTrack` to instantiate the list with. */ function AudioTrackList() { var _this, _ret; var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; _classCallCheck(this, AudioTrackList); var list = void 0; // 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 = _document2['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 = (_this = _possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this); list.changing_ = false; return _ret = list, _possibleConstructorReturn(_this, _ret); } /** * Add an {@link AudioTrack} to the `AudioTrackList`. * * @param {AudioTrack} track * The AudioTrack to add to the list * * @fires Track#addtrack * @private */ AudioTrackList.prototype.addTrack_ = function addTrack_(track) { var _this2 = this; if (track.enabled) { disableOthers(this, track); } _TrackList.prototype.addTrack_.call(this, track); // native tracks don't have this if (!track.addEventListener) { return; } /** * @listens AudioTrack#enabledchange * @fires TrackList#change */ 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 (_this2.changing_) { return; } _this2.changing_ = true; disableOthers(_this2, track); _this2.changing_ = false; _this2.trigger('change'); }); }; /** * Add an {@link AudioTrack} to the `AudioTrackList`. * * @param {AudioTrack} track * The AudioTrack to add to the list * * @fires Track#addtrack */ AudioTrackList.prototype.addTrack = function addTrack(track) { this.addTrack_(track); }; /** * Remove an {@link AudioTrack} from the `AudioTrackList`. * * @param {AudioTrack} track * The AudioTrack to remove from the list * * @fires Track#removetrack */ AudioTrackList.prototype.removeTrack = function removeTrack(track) { _TrackList.prototype.removeTrack_.call(this, track); }; return AudioTrackList; }(_trackList2['default']); exports['default'] = AudioTrackList; },{"../utils/browser.js":297,"./track-list":293,"global/document":193}],283:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _trackEnums = require('./track-enums'); var _track = require('./track'); var _track2 = _interopRequireDefault(_track); var _mergeOptions = require('../utils/merge-options'); var _mergeOptions2 = _interopRequireDefault(_mergeOptions); var _browser = require('../utils/browser.js'); var browser = _interopRequireWildcard(_browser); 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 _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; } /** * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList} * only one `AudioTrack` in the list will be enabled at a time. * * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack} * @extends Track */ var AudioTrack = function (_Track) { _inherits(AudioTrack, _Track); /** * Create an instance of this class. * * @param {Object} [options={}] * Object of option names and values * * @param {AudioTrack~Kind} [options.kind=''] * A valid audio track kind * * @param {string} [options.id='vjs_track_' + Guid.newGUID()] * A unique id for this AudioTrack. * * @param {string} [options.label=''] * The menu label for this track. * * @param {string} [options.language=''] * A valid two character language code. * * @param {boolean} [options.enabled] * If this track is the one that is currently playing. If this track is part of * an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled. */ function AudioTrack() { var _this, _ret; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, AudioTrack); var settings = (0, _mergeOptions2['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 = (_this = _possibleConstructorReturn(this, _Track.call(this, settings)), _this); var enabled = false; if (browser.IS_IE8) { for (var prop in AudioTrack.prototype) { if (prop !== 'constructor') { track[prop] = AudioTrack.prototype[prop]; } } } /** * @member {boolean} enabled * If this `AudioTrack` is enabled or not. When setting this will * fire {@link AudioTrack#enabledchange} if the state of enabled is changed. * * @fires VideoTrack#selectedchange */ 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; /** * An event that fires when enabled changes on this track. This allows * the AudioTrackList that holds this track to act accordingly. * * > Note: This is not part of the spec! Native tracks will do * this internally without an event. * * @event AudioTrack#enabledchange * @type {EventTarget~Event} */ 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 _ret = track, _possibleConstructorReturn(_this, _ret); } return AudioTrack; }(_track2['default']); exports['default'] = AudioTrack; },{"../utils/browser.js":297,"../utils/merge-options":306,"./track":294,"./track-enums":292}],284:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _browser = require('../utils/browser.js'); var browser = _interopRequireWildcard(_browser); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); 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"); } } /** * @file html-track-element-list.js */ /** * The current list of {@link HtmlTrackElement}s. */ var HtmlTrackElementList = function () { /** * Create an instance of this class. * * @param {HtmlTrackElement[]} [tracks=[]] * A list of `HtmlTrackElement` to instantiate the list with. */ function HtmlTrackElementList() { var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; _classCallCheck(this, HtmlTrackElementList); var list = this; // eslint-disable-line if (browser.IS_IE8) { list = _document2['default'].createElement('custom'); for (var prop in HtmlTrackElementList.prototype) { if (prop !== 'constructor') { list[prop] = HtmlTrackElementList.prototype[prop]; } } } list.trackElements_ = []; /** * @member {number} length * The current number of `Track`s in the this Trackist. */ 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; } } /** * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList` * * @param {HtmlTrackElement} trackElement * The track element to add to the list. * * @private */ HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) { var index = this.trackElements_.length; if (!('' + index in this)) { Object.defineProperty(this, index, { get: function get() { return this.trackElements_[index]; } }); } // Do not add duplicate elements if (this.trackElements_.indexOf(trackElement) === -1) { this.trackElements_.push(trackElement); } }; /** * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an * {@link TextTrack}. * * @param {TextTrack} track * The track associated with a track element. * * @return {HtmlTrackElement|undefined} * The track element that was found or undefined. * * @private */ HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) { var trackElement_ = void 0; for (var i = 0, length = this.trackElements_.length; i < length; i++) { if (track === this.trackElements_[i].track) { trackElement_ = this.trackElements_[i]; break; } } return trackElement_; }; /** * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList` * * @param {HtmlTrackElement} trackElement * The track element to remove from the list. * * @private */ HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) { for (var i = 0, length = this.trackElements_.length; i < length; i++) { if (trackElement === this.trackElements_[i]) { this.trackElements_.splice(i, 1); break; } } }; return HtmlTrackElementList; }(); exports['default'] = HtmlTrackElementList; },{"../utils/browser.js":297,"global/document":193}],285:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _browser = require('../utils/browser.js'); var browser = _interopRequireWildcard(_browser); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); var _eventTarget = require('../event-target'); var _eventTarget2 = _interopRequireDefault(_eventTarget); var _textTrack = require('../tracks/text-track'); var _textTrack2 = _interopRequireDefault(_textTrack); 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 _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; } /** * @file html-track-element.js */ /** * @typedef {HTMLTrackElement~ReadyState} * @enum {number} */ var NONE = 0; var LOADING = 1; var LOADED = 2; var ERROR = 3; /** * A single track represented in the DOM. * * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement} * @extends EventTarget */ var HTMLTrackElement = function (_EventTarget) { _inherits(HTMLTrackElement, _EventTarget); /** * Create an instance of this class. * * @param {Object} options={} * Object of option names and values * * @param {Tech} options.tech * A reference to the tech that owns this HTMLTrackElement. * * @param {TextTrack~Kind} [options.kind='subtitles'] * A valid text track kind. * * @param {TextTrack~Mode} [options.mode='disabled'] * A valid text track mode. * * @param {string} [options.id='vjs_track_' + Guid.newGUID()] * A unique id for this TextTrack. * * @param {string} [options.label=''] * The menu label for this track. * * @param {string} [options.language=''] * A valid two character language code. * * @param {string} [options.srclang=''] * A valid two character language code. An alternative, but deprioritized * vesion of `options.language` * * @param {string} [options.src] * A url to TextTrack cues. * * @param {boolean} [options.default] * If this track should default to on or off. */ function HTMLTrackElement() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, HTMLTrackElement); var _this = _possibleConstructorReturn(this, _EventTarget.call(this)); var readyState = void 0; var trackElement = _this; // eslint-disable-line if (browser.IS_IE8) { trackElement = _document2['default'].createElement('custom'); for (var prop in HTMLTrackElement.prototype) { if (prop !== 'constructor') { trackElement[prop] = HTMLTrackElement.prototype[prop]; } } } var track = new _textTrack2['default'](options); trackElement.kind = track.kind; trackElement.src = track.src; trackElement.srclang = track.language; trackElement.label = track.label; trackElement['default'] = track['default']; /** * @member {HTMLTrackElement~ReadyState} readyState * The current ready state of the track element. */ Object.defineProperty(trackElement, 'readyState', { get: function get() { return readyState; } }); /** * @member {TextTrack} track * The underlying TextTrack object. */ Object.defineProperty(trackElement, 'track', { get: function get() { return track; } }); readyState = NONE; /** * @listens TextTrack#loadeddata * @fires HTMLTrackElement#load */ track.addEventListener('loadeddata', function () { readyState = LOADED; trackElement.trigger({ type: 'load', target: trackElement }); }); if (browser.IS_IE8) { var _ret; return _ret = trackElement, _possibleConstructorReturn(_this, _ret); } return _this; } return HTMLTrackElement; }(_eventTarget2['default']); HTMLTrackElement.prototype.allowedEvents_ = { load: 'load' }; HTMLTrackElement.NONE = NONE; HTMLTrackElement.LOADING = LOADING; HTMLTrackElement.LOADED = LOADED; HTMLTrackElement.ERROR = ERROR; exports['default'] = HTMLTrackElement; },{"../event-target":261,"../tracks/text-track":291,"../utils/browser.js":297,"global/document":193}],286:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _browser = require('../utils/browser.js'); var browser = _interopRequireWildcard(_browser); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); 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"); } } /** * @file text-track-cue-list.js */ /** * @typedef {Object} TextTrackCue * * @property {string} id * The unique id for this text track cue * * @property {number} startTime * The start time for this text track cue * * @property {number} endTime * The end time for this text track cue * * @property {boolean} pauseOnExit * Pause when the end time is reached if true. * * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue} */ /** * A List of TextTrackCues. * * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist} */ var TextTrackCueList = function () { /** * Create an instance of this class.. * * @param {Array} cues * A list of cues to be initialized with */ function TextTrackCueList(cues) { _classCallCheck(this, TextTrackCueList); var list = this; // eslint-disable-line if (browser.IS_IE8) { list = _document2['default'].createElement('custom'); for (var prop in TextTrackCueList.prototype) { if (prop !== 'constructor') { list[prop] = TextTrackCueList.prototype[prop]; } } } TextTrackCueList.prototype.setCues_.call(list, cues); /** * @member {number} length * The current number of `TextTrackCue`s in the TextTrackCueList. */ Object.defineProperty(list, 'length', { get: function get() { return this.length_; } }); if (browser.IS_IE8) { return list; } } /** * A setter for cues in this list. Creates getters * an an index for the cues. * * @param {Array} cues * An array of cues to set * * @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 `TextTrackCue` that is currently in the `TextTrackCueList` by id. * * @param {string} id * The id of the cue that should be searched for. * * @return {TextTrackCue|null} * A single cue or null if none was found. */ 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; },{"../utils/browser.js":297,"global/document":193}],287:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _component = require('../component'); var _component2 = _interopRequireDefault(_component); var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); 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 _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; } /** * @file text-track-display.js */ 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' }; /** * Construct an rgba color from a given hex color code. * * @param {number} color * Hex number for color, like #f0e. * * @param {number} opacity * Value for opacity, 0.0 - 1.0. * * @return {string} * The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'. * * @private */ 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 the style of a DOM element. Some style changes will throw an error, * particularly in IE8. Those should be noops. * * @param {Element} el * The DOM element to be styled. * * @param {string} style * The CSS property on the element that should be styled. * * @param {string} rule * The style rule that should be applied to the property. */ function tryUpdateStyle(el, style, rule) { try { el.style[style] = rule; } catch (e) { // Satisfies linter. return; } } /** * The component for displaying text track cues. * * @extends Component */ var TextTrackDisplay = function (_Component) { _inherits(TextTrackDisplay, _Component); /** * Creates an instance of this class. * * @param {Player} player * The `Player` that this class should be attached to. * * @param {Object} [options] * The key/value store of player options. * * @param {Component~ReadyCallback} [ready] * The function to call when `TextTrackDisplay` is ready. */ function TextTrackDisplay(player, options, ready) { _classCallCheck(this, TextTrackDisplay); var _this = _possibleConstructorReturn(this, _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++) { this.player_.addRemoteTextTrack(tracks[i], true); } var modes = { captions: 1, subtitles: 1 }; var trackList = this.player_.textTracks(); var firstDesc = void 0; var firstCaptions = void 0; 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'; } } })); return _this; } /** * Turn display of {@link TextTrack}'s from the current state into the other state. * There are only two states: * - 'shown' * - 'hidden' * * @listens Player#loadstart */ TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() { if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) { this.hide(); } else { this.show(); } }; /** * Create the {@link Component}'s DOM element. * * @return {Element} * The element that was created. */ TextTrackDisplay.prototype.createEl = function createEl() { return _Component.prototype.createEl.call(this, 'div', { className: 'vjs-text-track-display' }, { 'aria-live': 'off', 'aria-atomic': 'true' }); }; /** * Clear all displayed {@link TextTrack}s. */ TextTrackDisplay.prototype.clearDisplay = function clearDisplay() { if (typeof _window2['default'].WebVTT === 'function') { _window2['default'].WebVTT.processCues(_window2['default'], [], this.el_); } }; /** * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or * a {@link Player#fullscreenchange} is fired. * * @listens Player#texttrackchange * @listens Player#fullscreenchange */ 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) { if (this.getAttribute('aria-live') !== 'off') { this.setAttribute('aria-live', 'off'); } this.updateForTrack(captionsSubtitlesTrack); } else if (descriptionsTrack) { if (this.getAttribute('aria-live') !== 'assertive') { this.setAttribute('aria-live', 'assertive'); } this.updateForTrack(descriptionsTrack); } }; /** * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}. * * @param {TextTrack} track * Text track object to be added to the list. */ TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) { if (typeof _window2['default'].WebVTT !== 'function' || !track.activeCues) { return; } var overrides = this.player_.textTrackSettings.getValues(); var cues = []; for (var _i2 = 0; _i2 < track.activeCues.length; _i2++) { cues.push(track.activeCues[_i2]); } _window2['default'].WebVTT.processCues(_window2['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 = _window2['default'].parseFloat(cueDiv.style.fontSize); cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px'; cueDiv.style.height = 'auto'; cueDiv.style.top = 'auto'; cueDiv.style.bottom = '2px'; } 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']); _component2['default'].registerComponent('TextTrackDisplay', TextTrackDisplay); exports['default'] = TextTrackDisplay; },{"../component":224,"../utils/fn.js":302,"global/window":194}],288:[function(require,module,exports){ 'use strict'; exports.__esModule = true; /** * @file text-track-list-converter.js Utilities for capturing text track state and * re-creating tracks based on a capture. * * @module text-track-list-converter */ /** * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that * represents the {@link TextTrack}'s state. * * @param {TextTrack} track * The text track to query. * * @return {Object} * A serializable javascript representation of the TextTrack. * @private */ 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 {@link Tech} and return a JSON-compatible javascript array that represents the * state of all {@link TextTrack}s currently configured. The return array is compatible with * {@link text-track-list-converter:jsonToTextTracks}. * * @param {Tech} tech * The tech object to query * * @return {Array} * A serializable javascript representation of the {@link Tech}s * {@link TextTrackList}. */ 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_)); }; /** * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript * object {@link TextTrack} representations. * * @param {Array} json * An array of `TextTrack` representation objects, like those that would be * produced by `textTracksToJson`. * * @param {Tech} tech * The `Tech` to create the `TextTrack`s on. */ 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_ }; },{}],289:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _trackList = require('./track-list'); var _trackList2 = _interopRequireDefault(_trackList); var _fn = require('../utils/fn.js'); var Fn = _interopRequireWildcard(_fn); var _browser = require('../utils/browser.js'); var browser = _interopRequireWildcard(_browser); var _document = require('global/document'); var _document2 = _interopRequireDefault(_document); 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 _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; } /** * @file text-track-list.js */ /** * The current list of {@link TextTrack} for a media file. * * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist} * @extends TrackList */ var TextTrackList = function (_TrackList) { _inherits(TextTrackList, _TrackList); /** * Create an instance of this class. * * @param {TextTrack[]} [tracks=[]] * A list of `TextTrack` to instantiate the list with. */ function TextTrackList() { var _this, _ret; var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; _classCallCheck(this, TextTrackList); var list = void 0; // IE8 forces us to implement inheritance ourselves // as it does not support Object.defineProperty properly if (browser.IS_IE8) { list = _document2['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 = (_this = _possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this); return _ret = list, _possibleConstructorReturn(_this, _ret); } /** * Add a {@link TextTrack} to the `TextTrackList` * * @param {TextTrack} track * The text track to add to the list. * * @fires TrackList#addtrack * @private */ TextTrackList.prototype.addTrack_ = function addTrack_(track) { _TrackList.prototype.addTrack_.call(this, track); /** * @listens TextTrack#modechange * @fires TrackList#change */ track.addEventListener('modechange', Fn.bind(this, function () { this.trigger('change'); })); }; return TextTrackList; }(_trackList2['default']); exports['default'] = TextTrackList; },{"../utils/browser.js":297,"../utils/fn.js":302,"./track-list":293,"global/document":193}],290:[function(require,module,exports){ 'use strict'; exports.__esModule = true; var _window = require('global/window'); var _window2 = _interopRequireDefault(_window); var _component = require('../component'); var _component2 = _interopRequireDefault(_component); var _dom = require('../utils/dom'); var _fn = require('../utils/fn'); var Fn = _interopRequireWildcard(_fn); var _obj = require('../utils/obj'); var Obj = _interopRequireWildcard(_obj); var _log = require('../utils/log'); var _log2 = _interopRequireDefault(_log); 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 _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; } /** * @file text-track-settings.js */ var LOCAL_STORAGE_KEY = 'vjs-text-track-settings'; var COLOR_BLACK = ['#000', 'Black']; var COLOR_BLUE = ['#00F', 'Blue']; var COLOR_CYAN = ['#0FF', 'Cyan']; var COLOR_GREEN = ['#0F0', 'Green']; var COLOR_MAGENTA = ['#F0F', 'Magenta']; var COLOR_RED = ['#F00', 'Red']; var COLOR_WHITE = ['#FFF', 'White']; var COLOR_YELLOW = ['#FF0', 'Yellow']; var OPACITY_OPAQUE = ['1', 'Opaque']; var OPACITY_SEMI = ['0.5', 'Semi-Transparent']; var OPACITY_TRANS = ['0', 'Transparent']; // Configuration for the various element. var selectConfigs = { backgroundColor: { selector: '.vjs-bg-color > select', id: 'captions-background-color-%s', label: 'Color', options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN] }, backgroundOpacity: { selector: '.vjs-bg-opacity > select', id: 'captions-background-opacity-%s', label: 'Transparency', options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS] }, color: { selector: '.vjs-fg-color > select', id: 'captions-foreground-color-%s', label: 'Color', options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN] }, edgeStyle: { selector: '.vjs-edge-style > select', id: '%s', label: 'Text Edge Style', options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']] }, fontFamily: { selector: '.vjs-font-family > select', id: 'captions-font-family-%s', label: 'Font Family', options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']] }, fontPercent: { selector: '.vjs-font-percent > select', id: 'captions-font-size-%s', label: 'Font Size', options: [['0.50', '50%'], ['0.75', '75%'], ['1.00', '100%'], ['1.25', '125%'], ['1.50', '150%'], ['1.75', '175%'], ['2.00', '200%'], ['3.00', '300%'], ['4.00', '400%']], 'default': 2, parser: function parser(v) { return v === '1.00' ? null : Number(v); } }, textOpacity: { selector: '.vjs-text-opacity > select', id: 'captions-foreground-opacity-%s', label: 'Transparency', options: [OPACITY_OPAQUE, OPACITY_SEMI] }, // Options for this object are defined below. windowColor: { selector: '.vjs-window-color > select', id: 'captions-window-color-%s', label: 'Color' }, // Options for this object are defined below. windowOpacity: { selector: '.vjs-window-opacity > select', id: 'captions-window-opacity-%s', label: 'Transparency', options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE] } }; selectConfigs.windowColor.options = selectConfigs.backgroundColor.options; /** * Get the actual value of an option. * * @param {string} value * The value to get * * @param {Function} [parser] * Optional function to adjust the value. * * @return {Mixed} * - Will be `undefined` if no value exists * - Will be `undefined` if the given value is "none". * - Will be the actual value otherwise. * * @private */ function parseOptionValue(value, parser) { if (parser) { value = parser(value); } if (value && value !== 'none') { return value; } } /** * Gets the value of the selected