1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-04 18:29:39 +02:00
Oinktube/plugin/Chromecast/videojs-chromecast/silvermine-videojs-chromecast.js
2019-04-02 23:17:08 -03:00

5726 lines
188 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
// Create a new Class that inherits from this class
Class.extend = function(className, prop) {
if(prop == undefined) {
prop = className;
className = "Class";
}
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
var func = new Function(
"return function " + className + "(){ }"
)();
Class.prototype.constructor = func;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
//I only added this line
module.exports = Class;
})();
},{}],2:[function(require,module,exports){
// Underscore.js 1.8.3
// http://underscorejs.org
// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind,
nativeCreate = Object.create;
// Naked function reference for surrogate-prototype-swapping.
var Ctor = function(){};
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
_.VERSION = '1.8.3';
// Internal function that returns an efficient (for current engines) version
// of the passed-in callback, to be repeatedly applied in other Underscore
// functions.
var optimizeCb = function(func, context, argCount) {
if (context === void 0) return func;
switch (argCount == null ? 3 : argCount) {
case 1: return function(value) {
return func.call(context, value);
};
case 2: return function(value, other) {
return func.call(context, value, other);
};
case 3: return function(value, index, collection) {
return func.call(context, value, index, collection);
};
case 4: return function(accumulator, value, index, collection) {
return func.call(context, accumulator, value, index, collection);
};
}
return function() {
return func.apply(context, arguments);
};
};
// A mostly-internal function to generate callbacks that can be applied
// to each element in a collection, returning the desired result — either
// identity, an arbitrary callback, a property matcher, or a property accessor.
var cb = function(value, context, argCount) {
if (value == null) return _.identity;
if (_.isFunction(value)) return optimizeCb(value, context, argCount);
if (_.isObject(value)) return _.matcher(value);
return _.property(value);
};
_.iteratee = function(value, context) {
return cb(value, context, Infinity);
};
// An internal function for creating assigner functions.
var createAssigner = function(keysFunc, undefinedOnly) {
return function(obj) {
var length = arguments.length;
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
}
}
return obj;
};
};
// An internal function for creating a new object that inherits from another.
var baseCreate = function(prototype) {
if (!_.isObject(prototype)) return {};
if (nativeCreate) return nativeCreate(prototype);
Ctor.prototype = prototype;
var result = new Ctor;
Ctor.prototype = null;
return result;
};
var property = function(key) {
return function(obj) {
return obj == null ? void 0 : obj[key];
};
};
// Helper for collection methods to determine whether a collection
// should be iterated as an array or as an object
// Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
// Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = property('length');
var isArrayLike = function(collection) {
var length = getLength(collection);
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles raw objects in addition to array-likes. Treats all
// sparse array-likes as if they were dense.
_.each = _.forEach = function(obj, iteratee, context) {
iteratee = optimizeCb(iteratee, context);
var i, length;
if (isArrayLike(obj)) {
for (i = 0, length = obj.length; i < length; i++) {
iteratee(obj[i], i, obj);
}
} else {
var keys = _.keys(obj);
for (i = 0, length = keys.length; i < length; i++) {
iteratee(obj[keys[i]], keys[i], obj);
}
}
return obj;
};
// Return the results of applying the iteratee to each element.
_.map = _.collect = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
results = Array(length);
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
results[index] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
// Create a reducing function iterating left or right.
function createReduce(dir) {
// Optimized iterator function as using arguments.length
// in the main function will deoptimize the, see #1991.
function iterator(obj, iteratee, memo, keys, index, length) {
for (; index >= 0 && index < length; index += dir) {
var currentKey = keys ? keys[index] : index;
memo = iteratee(memo, obj[currentKey], currentKey, obj);
}
return memo;
}
return function(obj, iteratee, memo, context) {
iteratee = optimizeCb(iteratee, context, 4);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length,
index = dir > 0 ? 0 : length - 1;
// Determine the initial value if none is provided.
if (arguments.length < 3) {
memo = obj[keys ? keys[index] : index];
index += dir;
}
return iterator(obj, iteratee, memo, keys, index, length);
};
}
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`.
_.reduce = _.foldl = _.inject = createReduce(1);
// The right-associative version of reduce, also known as `foldr`.
_.reduceRight = _.foldr = createReduce(-1);
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var key;
if (isArrayLike(obj)) {
key = _.findIndex(obj, predicate, context);
} else {
key = _.findKey(obj, predicate, context);
}
if (key !== void 0 && key !== -1) return obj[key];
};
// Return all the elements that pass a truth test.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
predicate = cb(predicate, context);
_.each(obj, function(value, index, list) {
if (predicate(value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) {
return _.filter(obj, _.negate(cb(predicate)), context);
};
// Determine whether all of the elements match a truth test.
// Aliased as `all`.
_.every = _.all = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
if (!predicate(obj[currentKey], currentKey, obj)) return false;
}
return true;
};
// Determine if at least one element in the object matches a truth test.
// Aliased as `any`.
_.some = _.any = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = !isArrayLike(obj) && _.keys(obj),
length = (keys || obj).length;
for (var index = 0; index < length; index++) {
var currentKey = keys ? keys[index] : index;
if (predicate(obj[currentKey], currentKey, obj)) return true;
}
return false;
};
// Determine if the array or object contains a given item (using `===`).
// Aliased as `includes` and `include`.
_.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
if (!isArrayLike(obj)) obj = _.values(obj);
if (typeof fromIndex != 'number' || guard) fromIndex = 0;
return _.indexOf(obj, item, fromIndex) >= 0;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
var func = isFunc ? method : value[method];
return func == null ? func : func.apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, _.property(key));
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs) {
return _.filter(obj, _.matcher(attrs));
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.find(obj, _.matcher(attrs));
};
// Return the maximum element (or element-based computation).
_.max = function(obj, iteratee, context) {
var result = -Infinity, lastComputed = -Infinity,
value, computed;
if (iteratee == null && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value > result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(value, index, list) {
computed = iteratee(value, index, list);
if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
result = value;
lastComputed = computed;
}
});
}
return result;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iteratee, context) {
var result = Infinity, lastComputed = Infinity,
value, computed;
if (iteratee == null && obj != null) {
obj = isArrayLike(obj) ? obj : _.values(obj);
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value < result) {
result = value;
}
}
} else {
iteratee = cb(iteratee, context);
_.each(obj, function(value, index, list) {
computed = iteratee(value, index, list);
if (computed < lastComputed || computed === Infinity && result === Infinity) {
result = value;
lastComputed = computed;
}
});
}
return result;
};
// Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/FisherYates_shuffle).
_.shuffle = function(obj) {
var set = isArrayLike(obj) ? obj : _.values(obj);
var length = set.length;
var shuffled = Array(length);
for (var index = 0, rand; index < length; index++) {
rand = _.random(0, index);
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = set[index];
}
return shuffled;
};
// Sample **n** random values from a collection.
// If **n** is not specified, returns a single random element.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
if (n == null || guard) {
if (!isArrayLike(obj)) obj = _.values(obj);
return obj[_.random(obj.length - 1)];
}
return _.shuffle(obj).slice(0, Math.max(0, n));
};
// Sort the object's values by a criterion produced by an iteratee.
_.sortBy = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
criteria: iteratee(value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
return function(obj, iteratee, context) {
var result = {};
iteratee = cb(iteratee, context);
_.each(obj, function(value, index) {
var key = iteratee(value, index, obj);
behavior(result, value, key);
});
return result;
};
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, value, key) {
if (_.has(result, key)) result[key].push(value); else result[key] = [value];
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_.indexBy = group(function(result, value, key) {
result[key] = value;
});
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = group(function(result, value, key) {
if (_.has(result, key)) result[key]++; else result[key] = 1;
});
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (isArrayLike(obj)) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return isArrayLike(obj) ? obj.length : _.keys(obj).length;
};
// Split a collection into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(obj, predicate, context) {
predicate = cb(predicate, context);
var pass = [], fail = [];
_.each(obj, function(value, key, obj) {
(predicate(value, key, obj) ? pass : fail).push(value);
});
return [pass, fail];
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
if (n == null || guard) return array[0];
return _.initial(array, array.length - n);
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N.
_.initial = function(array, n, guard) {
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array.
_.last = function(array, n, guard) {
if (array == null) return void 0;
if (n == null || guard) return array[array.length - 1];
return _.rest(array, Math.max(0, array.length - n));
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, n == null || guard ? 1 : n);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, strict, startIndex) {
var output = [], idx = 0;
for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
//flatten current level of array or arguments object
if (!shallow) value = flatten(value, shallow, strict);
var j = 0, len = value.length;
output.length += len;
while (j < len) {
output[idx++] = value[j++];
}
} else if (!strict) {
output[idx++] = value;
}
}
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
return flatten(array, shallow, false);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iteratee, context) {
if (!_.isBoolean(isSorted)) {
context = iteratee;
iteratee = isSorted;
isSorted = false;
}
if (iteratee != null) iteratee = cb(iteratee, context);
var result = [];
var seen = [];
for (var i = 0, length = getLength(array); i < length; i++) {
var value = array[i],
computed = iteratee ? iteratee(value, i, array) : value;
if (isSorted) {
if (!i || seen !== computed) result.push(value);
seen = computed;
} else if (iteratee) {
if (!_.contains(seen, computed)) {
seen.push(computed);
result.push(value);
}
} else if (!_.contains(result, value)) {
result.push(value);
}
}
return result;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(flatten(arguments, true, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var result = [];
var argsLength = arguments.length;
for (var i = 0, length = getLength(array); i < length; i++) {
var item = array[i];
if (_.contains(result, item)) continue;
for (var j = 1; j < argsLength; j++) {
if (!_.contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
}
return result;
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = flatten(arguments, true, true, 1);
return _.filter(array, function(value){
return !_.contains(rest, value);
});
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
return _.unzip(arguments);
};
// Complement of _.zip. Unzip accepts an array of arrays and groups
// each array's elements on shared indices
_.unzip = function(array) {
var length = array && _.max(array, getLength).length || 0;
var result = Array(length);
for (var index = 0; index < length; index++) {
result[index] = _.pluck(array, index);
}
return result;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
var result = {};
for (var i = 0, length = getLength(list); i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// Generator function to create the findIndex and findLastIndex functions
function createPredicateIndexFinder(dir) {
return function(array, predicate, context) {
predicate = cb(predicate, context);
var length = getLength(array);
var index = dir > 0 ? 0 : length - 1;
for (; index >= 0 && index < length; index += dir) {
if (predicate(array[index], index, array)) return index;
}
return -1;
};
}
// Returns the first index on an array-like that passes a predicate test
_.findIndex = createPredicateIndexFinder(1);
_.findLastIndex = createPredicateIndexFinder(-1);
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iteratee, context) {
iteratee = cb(iteratee, context, 1);
var value = iteratee(obj);
var low = 0, high = getLength(array);
while (low < high) {
var mid = Math.floor((low + high) / 2);
if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
}
return low;
};
// Generator function to create the indexOf and lastIndexOf functions
function createIndexFinder(dir, predicateFind, sortedIndex) {
return function(array, item, idx) {
var i = 0, length = getLength(array);
if (typeof idx == 'number') {
if (dir > 0) {
i = idx >= 0 ? idx : Math.max(idx + length, i);
} else {
length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
}
} else if (sortedIndex && idx && length) {
idx = sortedIndex(array, item);
return array[idx] === item ? idx : -1;
}
if (item !== item) {
idx = predicateFind(slice.call(array, i, length), _.isNaN);
return idx >= 0 ? idx + i : -1;
}
for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
if (array[idx] === item) return idx;
}
return -1;
};
}
// Return the position of the first occurrence of an item in an array,
// or -1 if the item is not included in the array.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (stop == null) {
stop = start || 0;
start = 0;
}
step = step || 1;
var length = Math.max(Math.ceil((stop - start) / step), 0);
var range = Array(length);
for (var idx = 0; idx < length; idx++, start += step) {
range[idx] = start;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Determines whether to execute a function as a constructor
// or a normal function with the provided arguments
var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
var self = baseCreate(sourceFunc.prototype);
var result = sourceFunc.apply(self, args);
if (_.isObject(result)) return result;
return self;
};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
var args = slice.call(arguments, 2);
var bound = function() {
return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
};
return bound;
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context. _ acts
// as a placeholder, allowing any combination of arguments to be pre-filled.
_.partial = function(func) {
var boundArgs = slice.call(arguments, 1);
var bound = function() {
var position = 0, length = boundArgs.length;
var args = Array(length);
for (var i = 0; i < length; i++) {
args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
}
while (position < arguments.length) args.push(arguments[position++]);
return executeBound(func, bound, this, this, args);
};
return bound;
};
// Bind a number of an object's methods to that object. Remaining arguments
// are the method names to be bound. Useful for ensuring that all callbacks
// defined on an object belong to it.
_.bindAll = function(obj) {
var i, length = arguments.length, key;
if (length <= 1) throw new Error('bindAll must be passed function names');
for (i = 1; i < length; i++) {
key = arguments[i];
obj[key] = _.bind(obj[key], obj);
}
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memoize = function(key) {
var cache = memoize.cache;
var address = '' + (hasher ? hasher.apply(this, arguments) : key);
if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
return cache[address];
};
memoize.cache = {};
return memoize;
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){
return func.apply(null, args);
}, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = _.partial(_.delay, _, 1);
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var later = function() {
var last = _.now() - timestamp;
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};
return function() {
context = this;
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return _.partial(wrapper, func);
};
// Returns a negated version of the passed-in predicate.
_.negate = function(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var args = arguments;
var start = args.length - 1;
return function() {
var i = start;
var result = args[start].apply(this, arguments);
while (i--) result = args[i].call(this, result);
return result;
};
};
// Returns a function that will only be executed on and after the Nth call.
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
// Returns a function that will only be executed up to (but not including) the Nth call.
_.before = function(times, func) {
var memo;
return function() {
if (--times > 0) {
memo = func.apply(this, arguments);
}
if (times <= 1) func = null;
return memo;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = _.partial(_.before, 2);
// Object Functions
// ----------------
// Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
function collectNonEnumProps(obj, keys) {
var nonEnumIdx = nonEnumerableProps.length;
var constructor = obj.constructor;
var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
// Constructor is a special case.
var prop = 'constructor';
if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
while (nonEnumIdx--) {
prop = nonEnumerableProps[nonEnumIdx];
if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
keys.push(prop);
}
}
}
// Retrieve the names of an object's own properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = function(obj) {
if (!_.isObject(obj)) return [];
if (nativeKeys) return nativeKeys(obj);
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
// Ahem, IE < 9.
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
};
// Retrieve all the property names of an object.
_.allKeys = function(obj) {
if (!_.isObject(obj)) return [];
var keys = [];
for (var key in obj) keys.push(key);
// Ahem, IE < 9.
if (hasEnumBug) collectNonEnumProps(obj, keys);
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
// Returns the results of applying the iteratee to each element of the object
// In contrast to _.map it returns an object
_.mapObject = function(obj, iteratee, context) {
iteratee = cb(iteratee, context);
var keys = _.keys(obj),
length = keys.length,
results = {},
currentKey;
for (var index = 0; index < length; index++) {
currentKey = keys[index];
results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
}
return results;
};
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var pairs = Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]];
}
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
result[obj[keys[i]]] = keys[i];
}
return result;
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = createAssigner(_.allKeys);
// Assigns a given object with all the own properties in the passed-in object(s)
// (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
_.extendOwn = _.assign = createAssigner(_.keys);
// Returns the first key on an object that passes a predicate test
_.findKey = function(obj, predicate, context) {
predicate = cb(predicate, context);
var keys = _.keys(obj), key;
for (var i = 0, length = keys.length; i < length; i++) {
key = keys[i];
if (predicate(obj[key], key, obj)) return key;
}
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(object, oiteratee, context) {
var result = {}, obj = object, iteratee, keys;
if (obj == null) return result;
if (_.isFunction(oiteratee)) {
keys = _.allKeys(obj);
iteratee = optimizeCb(oiteratee, context);
} else {
keys = flatten(arguments, false, false, 1);
iteratee = function(value, key, obj) { return key in obj; };
obj = Object(obj);
}
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i];
var value = obj[key];
if (iteratee(value, key, obj)) result[key] = value;
}
return result;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj, iteratee, context) {
if (_.isFunction(iteratee)) {
iteratee = _.negate(iteratee);
} else {
var keys = _.map(flatten(arguments, false, false, 1), String);
iteratee = function(value, key) {
return !_.contains(keys, key);
};
}
return _.pick(obj, iteratee, context);
};
// Fill in a given object with default properties.
_.defaults = createAssigner(_.allKeys, true);
// Creates an object that inherits from the given prototype object.
// If additional properties are provided then they will be added to the
// created object.
_.create = function(prototype, props) {
var result = baseCreate(prototype);
if (props) _.extendOwn(result, props);
return result;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Returns whether an object has a given set of `key:value` pairs.
_.isMatch = function(object, attrs) {
var keys = _.keys(attrs), length = keys.length;
if (object == null) return !length;
var obj = Object(object);
for (var i = 0; i < length; i++) {
var key = keys[i];
if (attrs[key] !== obj[key] || !(key in obj)) return false;
}
return true;
};
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a === 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className !== toString.call(b)) return false;
switch (className) {
// Strings, numbers, regular expressions, dates, and booleans are compared by value.
case '[object RegExp]':
// RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return '' + a === '' + b;
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive.
// Object(NaN) is equivalent to NaN
if (+a !== +a) return +b !== +b;
// An `egal` comparison is performed for other numeric values.
return +a === 0 ? 1 / +a === 1 / b : +a === +b;
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a === +b;
}
var areArrays = className === '[object Array]';
if (!areArrays) {
if (typeof a != 'object' || typeof b != 'object') return false;
// Objects with different constructors are not equivalent, but `Object`s or `Array`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
_.isFunction(bCtor) && bCtor instanceof bCtor)
&& ('constructor' in a && 'constructor' in b)) {
return false;
}
}
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
// Initializing stack of traversed objects.
// It's done here since we only need them for objects and arrays comparison.
aStack = aStack || [];
bStack = bStack || [];
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] === a) return bStack[length] === b;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
// Recursively compare objects and arrays.
if (areArrays) {
// Compare array lengths to determine if a deep comparison is necessary.
length = a.length;
if (length !== b.length) return false;
// Deep compare the contents, ignoring non-numeric properties.
while (length--) {
if (!eq(a[length], b[length], aStack, bStack)) return false;
}
} else {
// Deep compare objects.
var keys = _.keys(a), key;
length = keys.length;
// Ensure that both objects contain the same number of properties before comparing deep equality.
if (_.keys(b).length !== length) return false;
while (length--) {
// Deep compare each member
key = keys[length];
if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return true;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
return _.keys(obj).length === 0;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) === '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE < 9), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return _.has(obj, 'callee');
};
}
// Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
// IE 11 (#1621), and in Safari 8 (#1929).
if (typeof /./ != 'function' && typeof Int8Array != 'object') {
_.isFunction = function(obj) {
return typeof obj == 'function' || false;
};
}
// Is a given object a finite number?
_.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
return _.isNumber(obj) && obj !== +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return obj != null && hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iteratees.
_.identity = function(value) {
return value;
};
// Predicate-generating functions. Often useful outside of Underscore.
_.constant = function(value) {
return function() {
return value;
};
};
_.noop = function(){};
_.property = property;
// Generates a function for a given object that returns a given property.
_.propertyOf = function(obj) {
return obj == null ? function(){} : function(key) {
return obj[key];
};
};
// Returns a predicate for checking whether an object has a given set of
// `key:value` pairs.
_.matcher = _.matches = function(attrs) {
attrs = _.extendOwn({}, attrs);
return function(obj) {
return _.isMatch(obj, attrs);
};
};
// Run a function **n** times.
_.times = function(n, iteratee, context) {
var accum = Array(Math.max(0, n));
iteratee = optimizeCb(iteratee, context, 1);
for (var i = 0; i < n; i++) accum[i] = iteratee(i);
return accum;
};
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
// A (possibly faster) way to get the current timestamp as an integer.
_.now = Date.now || function() {
return new Date().getTime();
};
// List of HTML entities for escaping.
var escapeMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;'
};
var unescapeMap = _.invert(escapeMap);
// Functions for escaping and unescaping strings to/from HTML interpolation.
var createEscaper = function(map) {
var escaper = function(match) {
return map[match];
};
// Regexes for identifying a key that needs to be escaped
var source = '(?:' + _.keys(map).join('|') + ')';
var testRegexp = RegExp(source);
var replaceRegexp = RegExp(source, 'g');
return function(string) {
string = string == null ? '' : '' + string;
return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
};
};
_.escape = createEscaper(escapeMap);
_.unescape = createEscaper(unescapeMap);
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
_.result = function(object, property, fallback) {
var value = object == null ? void 0 : object[property];
if (value === void 0) {
value = fallback;
}
return _.isFunction(value) ? value.call(object) : value;
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
var escapeChar = function(match) {
return '\\' + escapes[match];
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
// NB: `oldSettings` only exists for backwards compatibility.
_.template = function(text, settings, oldSettings) {
if (!settings && oldSettings) settings = oldSettings;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset).replace(escaper, escapeChar);
index = offset + match.length;
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
} else if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
} else if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
// Adobe VMs need the match returned to produce the correct offest.
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + 'return __p;\n';
try {
var render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled source as a convenience for precompilation.
var argument = settings.variable || 'obj';
template.source = 'function(' + argument + '){\n' + source + '}';
return template;
};
// Add a "chain" function. Start chaining a wrapped Underscore object.
_.chain = function(obj) {
var instance = _(obj);
instance._chain = true;
return instance;
};
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function(instance, obj) {
return instance._chain ? _(obj).chain() : obj;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
_.each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result(this, func.apply(_, args));
};
});
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
return result(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
_.each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result(this, method.apply(this._wrapped, arguments));
};
});
// Extracts the result from a wrapped and chained object.
_.prototype.value = function() {
return this._wrapped;
};
// Provide unwrapping proxy for some methods used in engine operations
// such as arithmetic and JSON stringification.
_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
_.prototype.toString = function() {
return '' + this._wrapped;
};
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define === 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
}.call(this));
},{}],3:[function(require,module,exports){
/**
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
// @version 0.7.24
(function() {
window.WebComponents = window.WebComponents || {
flags: {}
};
var file = "webcomponents-lite.js";
var script = document.querySelector('script[src*="' + file + '"]');
var flags = {};
if (!flags.noOpts) {
location.search.slice(1).split("&").forEach(function(option) {
var parts = option.split("=");
var match;
if (parts[0] && (match = parts[0].match(/wc-(.+)/))) {
flags[match[1]] = parts[1] || true;
}
});
if (script) {
for (var i = 0, a; a = script.attributes[i]; i++) {
if (a.name !== "src") {
flags[a.name] = a.value || true;
}
}
}
if (flags.log && flags.log.split) {
var parts = flags.log.split(",");
flags.log = {};
parts.forEach(function(f) {
flags.log[f] = true;
});
} else {
flags.log = {};
}
}
if (flags.register) {
window.CustomElements = window.CustomElements || {
flags: {}
};
window.CustomElements.flags.register = flags.register;
}
WebComponents.flags = flags;
})();
(function(scope) {
"use strict";
var hasWorkingUrl = false;
if (!scope.forceJURL) {
try {
var u = new URL("b", "http://a");
u.pathname = "c%20d";
hasWorkingUrl = u.href === "http://a/c%20d";
} catch (e) {}
}
if (hasWorkingUrl) return;
var relative = Object.create(null);
relative["ftp"] = 21;
relative["file"] = 0;
relative["gopher"] = 70;
relative["http"] = 80;
relative["https"] = 443;
relative["ws"] = 80;
relative["wss"] = 443;
var relativePathDotMapping = Object.create(null);
relativePathDotMapping["%2e"] = ".";
relativePathDotMapping[".%2e"] = "..";
relativePathDotMapping["%2e."] = "..";
relativePathDotMapping["%2e%2e"] = "..";
function isRelativeScheme(scheme) {
return relative[scheme] !== undefined;
}
function invalid() {
clear.call(this);
this._isInvalid = true;
}
function IDNAToASCII(h) {
if ("" == h) {
invalid.call(this);
}
return h.toLowerCase();
}
function percentEscape(c) {
var unicode = c.charCodeAt(0);
if (unicode > 32 && unicode < 127 && [ 34, 35, 60, 62, 63, 96 ].indexOf(unicode) == -1) {
return c;
}
return encodeURIComponent(c);
}
function percentEscapeQuery(c) {
var unicode = c.charCodeAt(0);
if (unicode > 32 && unicode < 127 && [ 34, 35, 60, 62, 96 ].indexOf(unicode) == -1) {
return c;
}
return encodeURIComponent(c);
}
var EOF = undefined, ALPHA = /[a-zA-Z]/, ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
function parse(input, stateOverride, base) {
function err(message) {
errors.push(message);
}
var state = stateOverride || "scheme start", cursor = 0, buffer = "", seenAt = false, seenBracket = false, errors = [];
loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
var c = input[cursor];
switch (state) {
case "scheme start":
if (c && ALPHA.test(c)) {
buffer += c.toLowerCase();
state = "scheme";
} else if (!stateOverride) {
buffer = "";
state = "no scheme";
continue;
} else {
err("Invalid scheme.");
break loop;
}
break;
case "scheme":
if (c && ALPHANUMERIC.test(c)) {
buffer += c.toLowerCase();
} else if (":" == c) {
this._scheme = buffer;
buffer = "";
if (stateOverride) {
break loop;
}
if (isRelativeScheme(this._scheme)) {
this._isRelative = true;
}
if ("file" == this._scheme) {
state = "relative";
} else if (this._isRelative && base && base._scheme == this._scheme) {
state = "relative or authority";
} else if (this._isRelative) {
state = "authority first slash";
} else {
state = "scheme data";
}
} else if (!stateOverride) {
buffer = "";
cursor = 0;
state = "no scheme";
continue;
} else if (EOF == c) {
break loop;
} else {
err("Code point not allowed in scheme: " + c);
break loop;
}
break;
case "scheme data":
if ("?" == c) {
this._query = "?";
state = "query";
} else if ("#" == c) {
this._fragment = "#";
state = "fragment";
} else {
if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
this._schemeData += percentEscape(c);
}
}
break;
case "no scheme":
if (!base || !isRelativeScheme(base._scheme)) {
err("Missing scheme.");
invalid.call(this);
} else {
state = "relative";
continue;
}
break;
case "relative or authority":
if ("/" == c && "/" == input[cursor + 1]) {
state = "authority ignore slashes";
} else {
err("Expected /, got: " + c);
state = "relative";
continue;
}
break;
case "relative":
this._isRelative = true;
if ("file" != this._scheme) this._scheme = base._scheme;
if (EOF == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = base._query;
this._username = base._username;
this._password = base._password;
break loop;
} else if ("/" == c || "\\" == c) {
if ("\\" == c) err("\\ is an invalid code point.");
state = "relative slash";
} else if ("?" == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = "?";
this._username = base._username;
this._password = base._password;
state = "query";
} else if ("#" == c) {
this._host = base._host;
this._port = base._port;
this._path = base._path.slice();
this._query = base._query;
this._fragment = "#";
this._username = base._username;
this._password = base._password;
state = "fragment";
} else {
var nextC = input[cursor + 1];
var nextNextC = input[cursor + 2];
if ("file" != this._scheme || !ALPHA.test(c) || nextC != ":" && nextC != "|" || EOF != nextNextC && "/" != nextNextC && "\\" != nextNextC && "?" != nextNextC && "#" != nextNextC) {
this._host = base._host;
this._port = base._port;
this._username = base._username;
this._password = base._password;
this._path = base._path.slice();
this._path.pop();
}
state = "relative path";
continue;
}
break;
case "relative slash":
if ("/" == c || "\\" == c) {
if ("\\" == c) {
err("\\ is an invalid code point.");
}
if ("file" == this._scheme) {
state = "file host";
} else {
state = "authority ignore slashes";
}
} else {
if ("file" != this._scheme) {
this._host = base._host;
this._port = base._port;
this._username = base._username;
this._password = base._password;
}
state = "relative path";
continue;
}
break;
case "authority first slash":
if ("/" == c) {
state = "authority second slash";
} else {
err("Expected '/', got: " + c);
state = "authority ignore slashes";
continue;
}
break;
case "authority second slash":
state = "authority ignore slashes";
if ("/" != c) {
err("Expected '/', got: " + c);
continue;
}
break;
case "authority ignore slashes":
if ("/" != c && "\\" != c) {
state = "authority";
continue;
} else {
err("Expected authority, got: " + c);
}
break;
case "authority":
if ("@" == c) {
if (seenAt) {
err("@ already seen.");
buffer += "%40";
}
seenAt = true;
for (var i = 0; i < buffer.length; i++) {
var cp = buffer[i];
if ("\t" == cp || "\n" == cp || "\r" == cp) {
err("Invalid whitespace in authority.");
continue;
}
if (":" == cp && null === this._password) {
this._password = "";
continue;
}
var tempC = percentEscape(cp);
null !== this._password ? this._password += tempC : this._username += tempC;
}
buffer = "";
} else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
cursor -= buffer.length;
buffer = "";
state = "host";
continue;
} else {
buffer += c;
}
break;
case "file host":
if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ":" || buffer[1] == "|")) {
state = "relative path";
} else if (buffer.length == 0) {
state = "relative path start";
} else {
this._host = IDNAToASCII.call(this, buffer);
buffer = "";
state = "relative path start";
}
continue;
} else if ("\t" == c || "\n" == c || "\r" == c) {
err("Invalid whitespace in file host.");
} else {
buffer += c;
}
break;
case "host":
case "hostname":
if (":" == c && !seenBracket) {
this._host = IDNAToASCII.call(this, buffer);
buffer = "";
state = "port";
if ("hostname" == stateOverride) {
break loop;
}
} else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c) {
this._host = IDNAToASCII.call(this, buffer);
buffer = "";
state = "relative path start";
if (stateOverride) {
break loop;
}
continue;
} else if ("\t" != c && "\n" != c && "\r" != c) {
if ("[" == c) {
seenBracket = true;
} else if ("]" == c) {
seenBracket = false;
}
buffer += c;
} else {
err("Invalid code point in host/hostname: " + c);
}
break;
case "port":
if (/[0-9]/.test(c)) {
buffer += c;
} else if (EOF == c || "/" == c || "\\" == c || "?" == c || "#" == c || stateOverride) {
if ("" != buffer) {
var temp = parseInt(buffer, 10);
if (temp != relative[this._scheme]) {
this._port = temp + "";
}
buffer = "";
}
if (stateOverride) {
break loop;
}
state = "relative path start";
continue;
} else if ("\t" == c || "\n" == c || "\r" == c) {
err("Invalid code point in port: " + c);
} else {
invalid.call(this);
}
break;
case "relative path start":
if ("\\" == c) err("'\\' not allowed in path.");
state = "relative path";
if ("/" != c && "\\" != c) {
continue;
}
break;
case "relative path":
if (EOF == c || "/" == c || "\\" == c || !stateOverride && ("?" == c || "#" == c)) {
if ("\\" == c) {
err("\\ not allowed in relative path.");
}
var tmp;
if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
buffer = tmp;
}
if (".." == buffer) {
this._path.pop();
if ("/" != c && "\\" != c) {
this._path.push("");
}
} else if ("." == buffer && "/" != c && "\\" != c) {
this._path.push("");
} else if ("." != buffer) {
if ("file" == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == "|") {
buffer = buffer[0] + ":";
}
this._path.push(buffer);
}
buffer = "";
if ("?" == c) {
this._query = "?";
state = "query";
} else if ("#" == c) {
this._fragment = "#";
state = "fragment";
}
} else if ("\t" != c && "\n" != c && "\r" != c) {
buffer += percentEscape(c);
}
break;
case "query":
if (!stateOverride && "#" == c) {
this._fragment = "#";
state = "fragment";
} else if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
this._query += percentEscapeQuery(c);
}
break;
case "fragment":
if (EOF != c && "\t" != c && "\n" != c && "\r" != c) {
this._fragment += c;
}
break;
}
cursor++;
}
}
function clear() {
this._scheme = "";
this._schemeData = "";
this._username = "";
this._password = null;
this._host = "";
this._port = "";
this._path = [];
this._query = "";
this._fragment = "";
this._isInvalid = false;
this._isRelative = false;
}
function jURL(url, base) {
if (base !== undefined && !(base instanceof jURL)) base = new jURL(String(base));
this._url = url;
clear.call(this);
var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, "");
parse.call(this, input, null, base);
}
jURL.prototype = {
toString: function() {
return this.href;
},
get href() {
if (this._isInvalid) return this._url;
var authority = "";
if ("" != this._username || null != this._password) {
authority = this._username + (null != this._password ? ":" + this._password : "") + "@";
}
return this.protocol + (this._isRelative ? "//" + authority + this.host : "") + this.pathname + this._query + this._fragment;
},
set href(href) {
clear.call(this);
parse.call(this, href);
},
get protocol() {
return this._scheme + ":";
},
set protocol(protocol) {
if (this._isInvalid) return;
parse.call(this, protocol + ":", "scheme start");
},
get host() {
return this._isInvalid ? "" : this._port ? this._host + ":" + this._port : this._host;
},
set host(host) {
if (this._isInvalid || !this._isRelative) return;
parse.call(this, host, "host");
},
get hostname() {
return this._host;
},
set hostname(hostname) {
if (this._isInvalid || !this._isRelative) return;
parse.call(this, hostname, "hostname");
},
get port() {
return this._port;
},
set port(port) {
if (this._isInvalid || !this._isRelative) return;
parse.call(this, port, "port");
},
get pathname() {
return this._isInvalid ? "" : this._isRelative ? "/" + this._path.join("/") : this._schemeData;
},
set pathname(pathname) {
if (this._isInvalid || !this._isRelative) return;
this._path = [];
parse.call(this, pathname, "relative path start");
},
get search() {
return this._isInvalid || !this._query || "?" == this._query ? "" : this._query;
},
set search(search) {
if (this._isInvalid || !this._isRelative) return;
this._query = "?";
if ("?" == search[0]) search = search.slice(1);
parse.call(this, search, "query");
},
get hash() {
return this._isInvalid || !this._fragment || "#" == this._fragment ? "" : this._fragment;
},
set hash(hash) {
if (this._isInvalid) return;
this._fragment = "#";
if ("#" == hash[0]) hash = hash.slice(1);
parse.call(this, hash, "fragment");
},
get origin() {
var host;
if (this._isInvalid || !this._scheme) {
return "";
}
switch (this._scheme) {
case "data":
case "file":
case "javascript":
case "mailto":
return "null";
}
host = this.host;
if (!host) {
return "";
}
return this._scheme + "://" + host;
}
};
var OriginalURL = scope.URL;
if (OriginalURL) {
jURL.createObjectURL = function(blob) {
return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
};
jURL.revokeObjectURL = function(url) {
OriginalURL.revokeObjectURL(url);
};
}
scope.URL = jURL;
})(self);
if (typeof WeakMap === "undefined") {
(function() {
var defineProperty = Object.defineProperty;
var counter = Date.now() % 1e9;
var WeakMap = function() {
this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
};
WeakMap.prototype = {
set: function(key, value) {
var entry = key[this.name];
if (entry && entry[0] === key) entry[1] = value; else defineProperty(key, this.name, {
value: [ key, value ],
writable: true
});
return this;
},
get: function(key) {
var entry;
return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
},
"delete": function(key) {
var entry = key[this.name];
if (!entry || entry[0] !== key) return false;
entry[0] = entry[1] = undefined;
return true;
},
has: function(key) {
var entry = key[this.name];
if (!entry) return false;
return entry[0] === key;
}
};
window.WeakMap = WeakMap;
})();
}
(function(global) {
if (global.JsMutationObserver) {
return;
}
var registrationsTable = new WeakMap();
var setImmediate;
if (/Trident|Edge/.test(navigator.userAgent)) {
setImmediate = setTimeout;
} else if (window.setImmediate) {
setImmediate = window.setImmediate;
} else {
var setImmediateQueue = [];
var sentinel = String(Math.random());
window.addEventListener("message", function(e) {
if (e.data === sentinel) {
var queue = setImmediateQueue;
setImmediateQueue = [];
queue.forEach(function(func) {
func();
});
}
});
setImmediate = function(func) {
setImmediateQueue.push(func);
window.postMessage(sentinel, "*");
};
}
var isScheduled = false;
var scheduledObservers = [];
function scheduleCallback(observer) {
scheduledObservers.push(observer);
if (!isScheduled) {
isScheduled = true;
setImmediate(dispatchCallbacks);
}
}
function wrapIfNeeded(node) {
return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
}
function dispatchCallbacks() {
isScheduled = false;
var observers = scheduledObservers;
scheduledObservers = [];
observers.sort(function(o1, o2) {
return o1.uid_ - o2.uid_;
});
var anyNonEmpty = false;
observers.forEach(function(observer) {
var queue = observer.takeRecords();
removeTransientObserversFor(observer);
if (queue.length) {
observer.callback_(queue, observer);
anyNonEmpty = true;
}
});
if (anyNonEmpty) dispatchCallbacks();
}
function removeTransientObserversFor(observer) {
observer.nodes_.forEach(function(node) {
var registrations = registrationsTable.get(node);
if (!registrations) return;
registrations.forEach(function(registration) {
if (registration.observer === observer) registration.removeTransientObservers();
});
});
}
function forEachAncestorAndObserverEnqueueRecord(target, callback) {
for (var node = target; node; node = node.parentNode) {
var registrations = registrationsTable.get(node);
if (registrations) {
for (var j = 0; j < registrations.length; j++) {
var registration = registrations[j];
var options = registration.options;
if (node !== target && !options.subtree) continue;
var record = callback(options);
if (record) registration.enqueue(record);
}
}
}
}
var uidCounter = 0;
function JsMutationObserver(callback) {
this.callback_ = callback;
this.nodes_ = [];
this.records_ = [];
this.uid_ = ++uidCounter;
}
JsMutationObserver.prototype = {
observe: function(target, options) {
target = wrapIfNeeded(target);
if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
throw new SyntaxError();
}
var registrations = registrationsTable.get(target);
if (!registrations) registrationsTable.set(target, registrations = []);
var registration;
for (var i = 0; i < registrations.length; i++) {
if (registrations[i].observer === this) {
registration = registrations[i];
registration.removeListeners();
registration.options = options;
break;
}
}
if (!registration) {
registration = new Registration(this, target, options);
registrations.push(registration);
this.nodes_.push(target);
}
registration.addListeners();
},
disconnect: function() {
this.nodes_.forEach(function(node) {
var registrations = registrationsTable.get(node);
for (var i = 0; i < registrations.length; i++) {
var registration = registrations[i];
if (registration.observer === this) {
registration.removeListeners();
registrations.splice(i, 1);
break;
}
}
}, this);
this.records_ = [];
},
takeRecords: function() {
var copyOfRecords = this.records_;
this.records_ = [];
return copyOfRecords;
}
};
function MutationRecord(type, target) {
this.type = type;
this.target = target;
this.addedNodes = [];
this.removedNodes = [];
this.previousSibling = null;
this.nextSibling = null;
this.attributeName = null;
this.attributeNamespace = null;
this.oldValue = null;
}
function copyMutationRecord(original) {
var record = new MutationRecord(original.type, original.target);
record.addedNodes = original.addedNodes.slice();
record.removedNodes = original.removedNodes.slice();
record.previousSibling = original.previousSibling;
record.nextSibling = original.nextSibling;
record.attributeName = original.attributeName;
record.attributeNamespace = original.attributeNamespace;
record.oldValue = original.oldValue;
return record;
}
var currentRecord, recordWithOldValue;
function getRecord(type, target) {
return currentRecord = new MutationRecord(type, target);
}
function getRecordWithOldValue(oldValue) {
if (recordWithOldValue) return recordWithOldValue;
recordWithOldValue = copyMutationRecord(currentRecord);
recordWithOldValue.oldValue = oldValue;
return recordWithOldValue;
}
function clearRecords() {
currentRecord = recordWithOldValue = undefined;
}
function recordRepresentsCurrentMutation(record) {
return record === recordWithOldValue || record === currentRecord;
}
function selectRecord(lastRecord, newRecord) {
if (lastRecord === newRecord) return lastRecord;
if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
return null;
}
function Registration(observer, target, options) {
this.observer = observer;
this.target = target;
this.options = options;
this.transientObservedNodes = [];
}
Registration.prototype = {
enqueue: function(record) {
var records = this.observer.records_;
var length = records.length;
if (records.length > 0) {
var lastRecord = records[length - 1];
var recordToReplaceLast = selectRecord(lastRecord, record);
if (recordToReplaceLast) {
records[length - 1] = recordToReplaceLast;
return;
}
} else {
scheduleCallback(this.observer);
}
records[length] = record;
},
addListeners: function() {
this.addListeners_(this.target);
},
addListeners_: function(node) {
var options = this.options;
if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
},
removeListeners: function() {
this.removeListeners_(this.target);
},
removeListeners_: function(node) {
var options = this.options;
if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
},
addTransientObserver: function(node) {
if (node === this.target) return;
this.addListeners_(node);
this.transientObservedNodes.push(node);
var registrations = registrationsTable.get(node);
if (!registrations) registrationsTable.set(node, registrations = []);
registrations.push(this);
},
removeTransientObservers: function() {
var transientObservedNodes = this.transientObservedNodes;
this.transientObservedNodes = [];
transientObservedNodes.forEach(function(node) {
this.removeListeners_(node);
var registrations = registrationsTable.get(node);
for (var i = 0; i < registrations.length; i++) {
if (registrations[i] === this) {
registrations.splice(i, 1);
break;
}
}
}, this);
},
handleEvent: function(e) {
e.stopImmediatePropagation();
switch (e.type) {
case "DOMAttrModified":
var name = e.attrName;
var namespace = e.relatedNode.namespaceURI;
var target = e.target;
var record = new getRecord("attributes", target);
record.attributeName = name;
record.attributeNamespace = namespace;
var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
forEachAncestorAndObserverEnqueueRecord(target, function(options) {
if (!options.attributes) return;
if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
return;
}
if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
return record;
});
break;
case "DOMCharacterDataModified":
var target = e.target;
var record = getRecord("characterData", target);
var oldValue = e.prevValue;
forEachAncestorAndObserverEnqueueRecord(target, function(options) {
if (!options.characterData) return;
if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
return record;
});
break;
case "DOMNodeRemoved":
this.addTransientObserver(e.target);
case "DOMNodeInserted":
var changedNode = e.target;
var addedNodes, removedNodes;
if (e.type === "DOMNodeInserted") {
addedNodes = [ changedNode ];
removedNodes = [];
} else {
addedNodes = [];
removedNodes = [ changedNode ];
}
var previousSibling = changedNode.previousSibling;
var nextSibling = changedNode.nextSibling;
var record = getRecord("childList", e.target.parentNode);
record.addedNodes = addedNodes;
record.removedNodes = removedNodes;
record.previousSibling = previousSibling;
record.nextSibling = nextSibling;
forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
if (!options.childList) return;
return record;
});
}
clearRecords();
}
};
global.JsMutationObserver = JsMutationObserver;
if (!global.MutationObserver) {
global.MutationObserver = JsMutationObserver;
JsMutationObserver._isPolyfilled = true;
}
})(self);
(function() {
var needsTemplate = typeof HTMLTemplateElement === "undefined";
if (/Trident/.test(navigator.userAgent)) {
(function() {
var importNode = document.importNode;
document.importNode = function() {
var n = importNode.apply(document, arguments);
if (n.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
var f = document.createDocumentFragment();
f.appendChild(n);
return f;
} else {
return n;
}
};
})();
}
var needsCloning = function() {
if (!needsTemplate) {
var t = document.createElement("template");
var t2 = document.createElement("template");
t2.content.appendChild(document.createElement("div"));
t.content.appendChild(t2);
var clone = t.cloneNode(true);
return clone.content.childNodes.length === 0 || clone.content.firstChild.content.childNodes.length === 0;
}
}();
var TEMPLATE_TAG = "template";
var TemplateImpl = function() {};
if (needsTemplate) {
var contentDoc = document.implementation.createHTMLDocument("template");
var canDecorate = true;
var templateStyle = document.createElement("style");
templateStyle.textContent = TEMPLATE_TAG + "{display:none;}";
var head = document.head;
head.insertBefore(templateStyle, head.firstElementChild);
TemplateImpl.prototype = Object.create(HTMLElement.prototype);
TemplateImpl.decorate = function(template) {
if (template.content) {
return;
}
template.content = contentDoc.createDocumentFragment();
var child;
while (child = template.firstChild) {
template.content.appendChild(child);
}
template.cloneNode = function(deep) {
return TemplateImpl.cloneNode(this, deep);
};
if (canDecorate) {
try {
Object.defineProperty(template, "innerHTML", {
get: function() {
var o = "";
for (var e = this.content.firstChild; e; e = e.nextSibling) {
o += e.outerHTML || escapeData(e.data);
}
return o;
},
set: function(text) {
contentDoc.body.innerHTML = text;
TemplateImpl.bootstrap(contentDoc);
while (this.content.firstChild) {
this.content.removeChild(this.content.firstChild);
}
while (contentDoc.body.firstChild) {
this.content.appendChild(contentDoc.body.firstChild);
}
},
configurable: true
});
} catch (err) {
canDecorate = false;
}
}
TemplateImpl.bootstrap(template.content);
};
TemplateImpl.bootstrap = function(doc) {
var templates = doc.querySelectorAll(TEMPLATE_TAG);
for (var i = 0, l = templates.length, t; i < l && (t = templates[i]); i++) {
TemplateImpl.decorate(t);
}
};
document.addEventListener("DOMContentLoaded", function() {
TemplateImpl.bootstrap(document);
});
var createElement = document.createElement;
document.createElement = function() {
"use strict";
var el = createElement.apply(document, arguments);
if (el.localName === "template") {
TemplateImpl.decorate(el);
}
return el;
};
var escapeDataRegExp = /[&\u00A0<>]/g;
function escapeReplace(c) {
switch (c) {
case "&":
return "&amp;";
case "<":
return "&lt;";
case ">":
return "&gt;";
case " ":
return "&nbsp;";
}
}
function escapeData(s) {
return s.replace(escapeDataRegExp, escapeReplace);
}
}
if (needsTemplate || needsCloning) {
var nativeCloneNode = Node.prototype.cloneNode;
TemplateImpl.cloneNode = function(template, deep) {
var clone = nativeCloneNode.call(template, false);
if (this.decorate) {
this.decorate(clone);
}
if (deep) {
clone.content.appendChild(nativeCloneNode.call(template.content, true));
this.fixClonedDom(clone.content, template.content);
}
return clone;
};
TemplateImpl.fixClonedDom = function(clone, source) {
if (!source.querySelectorAll) return;
var s$ = source.querySelectorAll(TEMPLATE_TAG);
var t$ = clone.querySelectorAll(TEMPLATE_TAG);
for (var i = 0, l = t$.length, t, s; i < l; i++) {
s = s$[i];
t = t$[i];
if (this.decorate) {
this.decorate(s);
}
t.parentNode.replaceChild(s.cloneNode(true), t);
}
};
var originalImportNode = document.importNode;
Node.prototype.cloneNode = function(deep) {
var dom = nativeCloneNode.call(this, deep);
if (deep) {
TemplateImpl.fixClonedDom(dom, this);
}
return dom;
};
document.importNode = function(element, deep) {
if (element.localName === TEMPLATE_TAG) {
return TemplateImpl.cloneNode(element, deep);
} else {
var dom = originalImportNode.call(document, element, deep);
if (deep) {
TemplateImpl.fixClonedDom(dom, element);
}
return dom;
}
};
if (needsCloning) {
HTMLTemplateElement.prototype.cloneNode = function(deep) {
return TemplateImpl.cloneNode(this, deep);
};
}
}
if (needsTemplate) {
window.HTMLTemplateElement = TemplateImpl;
}
})();
(function(scope) {
"use strict";
if (!(window.performance && window.performance.now)) {
var start = Date.now();
window.performance = {
now: function() {
return Date.now() - start;
}
};
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function() {
var nativeRaf = window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
return nativeRaf ? function(callback) {
return nativeRaf(function() {
callback(performance.now());
});
} : function(callback) {
return window.setTimeout(callback, 1e3 / 60);
};
}();
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function() {
return window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function(id) {
clearTimeout(id);
};
}();
}
var workingDefaultPrevented = function() {
var e = document.createEvent("Event");
e.initEvent("foo", true, true);
e.preventDefault();
return e.defaultPrevented;
}();
if (!workingDefaultPrevented) {
var origPreventDefault = Event.prototype.preventDefault;
Event.prototype.preventDefault = function() {
if (!this.cancelable) {
return;
}
origPreventDefault.call(this);
Object.defineProperty(this, "defaultPrevented", {
get: function() {
return true;
},
configurable: true
});
};
}
var isIE = /Trident/.test(navigator.userAgent);
if (!window.CustomEvent || isIE && typeof window.CustomEvent !== "function") {
window.CustomEvent = function(inType, params) {
params = params || {};
var e = document.createEvent("CustomEvent");
e.initCustomEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable), params.detail);
return e;
};
window.CustomEvent.prototype = window.Event.prototype;
}
if (!window.Event || isIE && typeof window.Event !== "function") {
var origEvent = window.Event;
window.Event = function(inType, params) {
params = params || {};
var e = document.createEvent("Event");
e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable));
return e;
};
window.Event.prototype = origEvent.prototype;
}
})(window.WebComponents);
window.HTMLImports = window.HTMLImports || {
flags: {}
};
(function(scope) {
var IMPORT_LINK_TYPE = "import";
var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement("link"));
var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
var wrap = function(node) {
return hasShadowDOMPolyfill ? window.ShadowDOMPolyfill.wrapIfNeeded(node) : node;
};
var rootDocument = wrap(document);
var currentScriptDescriptor = {
get: function() {
var script = window.HTMLImports.currentScript || document.currentScript || (document.readyState !== "complete" ? document.scripts[document.scripts.length - 1] : null);
return wrap(script);
},
configurable: true
};
Object.defineProperty(document, "_currentScript", currentScriptDescriptor);
Object.defineProperty(rootDocument, "_currentScript", currentScriptDescriptor);
var isIE = /Trident/.test(navigator.userAgent);
function whenReady(callback, doc) {
doc = doc || rootDocument;
whenDocumentReady(function() {
watchImportsLoad(callback, doc);
}, doc);
}
var requiredReadyState = isIE ? "complete" : "interactive";
var READY_EVENT = "readystatechange";
function isDocumentReady(doc) {
return doc.readyState === "complete" || doc.readyState === requiredReadyState;
}
function whenDocumentReady(callback, doc) {
if (!isDocumentReady(doc)) {
var checkReady = function() {
if (doc.readyState === "complete" || doc.readyState === requiredReadyState) {
doc.removeEventListener(READY_EVENT, checkReady);
whenDocumentReady(callback, doc);
}
};
doc.addEventListener(READY_EVENT, checkReady);
} else if (callback) {
callback();
}
}
function markTargetLoaded(event) {
event.target.__loaded = true;
}
function watchImportsLoad(callback, doc) {
var imports = doc.querySelectorAll("link[rel=import]");
var parsedCount = 0, importCount = imports.length, newImports = [], errorImports = [];
function checkDone() {
if (parsedCount == importCount && callback) {
callback({
allImports: imports,
loadedImports: newImports,
errorImports: errorImports
});
}
}
function loadedImport(e) {
markTargetLoaded(e);
newImports.push(this);
parsedCount++;
checkDone();
}
function errorLoadingImport(e) {
errorImports.push(this);
parsedCount++;
checkDone();
}
if (importCount) {
for (var i = 0, imp; i < importCount && (imp = imports[i]); i++) {
if (isImportLoaded(imp)) {
newImports.push(this);
parsedCount++;
checkDone();
} else {
imp.addEventListener("load", loadedImport);
imp.addEventListener("error", errorLoadingImport);
}
}
} else {
checkDone();
}
}
function isImportLoaded(link) {
return useNative ? link.__loaded || link.import && link.import.readyState !== "loading" : link.__importParsed;
}
if (useNative) {
new MutationObserver(function(mxns) {
for (var i = 0, l = mxns.length, m; i < l && (m = mxns[i]); i++) {
if (m.addedNodes) {
handleImports(m.addedNodes);
}
}
}).observe(document.head, {
childList: true
});
function handleImports(nodes) {
for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
if (isImport(n)) {
handleImport(n);
}
}
}
function isImport(element) {
return element.localName === "link" && element.rel === "import";
}
function handleImport(element) {
var loaded = element.import;
if (loaded) {
markTargetLoaded({
target: element
});
} else {
element.addEventListener("load", markTargetLoaded);
element.addEventListener("error", markTargetLoaded);
}
}
(function() {
if (document.readyState === "loading") {
var imports = document.querySelectorAll("link[rel=import]");
for (var i = 0, l = imports.length, imp; i < l && (imp = imports[i]); i++) {
handleImport(imp);
}
}
})();
}
whenReady(function(detail) {
window.HTMLImports.ready = true;
window.HTMLImports.readyTime = new Date().getTime();
var evt = rootDocument.createEvent("CustomEvent");
evt.initCustomEvent("HTMLImportsLoaded", true, true, detail);
rootDocument.dispatchEvent(evt);
});
scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
scope.useNative = useNative;
scope.rootDocument = rootDocument;
scope.whenReady = whenReady;
scope.isIE = isIE;
})(window.HTMLImports);
(function(scope) {
var modules = [];
var addModule = function(module) {
modules.push(module);
};
var initializeModules = function() {
modules.forEach(function(module) {
module(scope);
});
};
scope.addModule = addModule;
scope.initializeModules = initializeModules;
})(window.HTMLImports);
window.HTMLImports.addModule(function(scope) {
var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
var path = {
resolveUrlsInStyle: function(style, linkUrl) {
var doc = style.ownerDocument;
var resolver = doc.createElement("a");
style.textContent = this.resolveUrlsInCssText(style.textContent, linkUrl, resolver);
return style;
},
resolveUrlsInCssText: function(cssText, linkUrl, urlObj) {
var r = this.replaceUrls(cssText, urlObj, linkUrl, CSS_URL_REGEXP);
r = this.replaceUrls(r, urlObj, linkUrl, CSS_IMPORT_REGEXP);
return r;
},
replaceUrls: function(text, urlObj, linkUrl, regexp) {
return text.replace(regexp, function(m, pre, url, post) {
var urlPath = url.replace(/["']/g, "");
if (linkUrl) {
urlPath = new URL(urlPath, linkUrl).href;
}
urlObj.href = urlPath;
urlPath = urlObj.href;
return pre + "'" + urlPath + "'" + post;
});
}
};
scope.path = path;
});
window.HTMLImports.addModule(function(scope) {
var xhr = {
async: true,
ok: function(request) {
return request.status >= 200 && request.status < 300 || request.status === 304 || request.status === 0;
},
load: function(url, next, nextContext) {
var request = new XMLHttpRequest();
if (scope.flags.debug || scope.flags.bust) {
url += "?" + Math.random();
}
request.open("GET", url, xhr.async);
request.addEventListener("readystatechange", function(e) {
if (request.readyState === 4) {
var redirectedUrl = null;
try {
var locationHeader = request.getResponseHeader("Location");
if (locationHeader) {
redirectedUrl = locationHeader.substr(0, 1) === "/" ? location.origin + locationHeader : locationHeader;
}
} catch (e) {
console.error(e.message);
}
next.call(nextContext, !xhr.ok(request) && request, request.response || request.responseText, redirectedUrl);
}
});
request.send();
return request;
},
loadDocument: function(url, next, nextContext) {
this.load(url, next, nextContext).responseType = "document";
}
};
scope.xhr = xhr;
});
window.HTMLImports.addModule(function(scope) {
var xhr = scope.xhr;
var flags = scope.flags;
var Loader = function(onLoad, onComplete) {
this.cache = {};
this.onload = onLoad;
this.oncomplete = onComplete;
this.inflight = 0;
this.pending = {};
};
Loader.prototype = {
addNodes: function(nodes) {
this.inflight += nodes.length;
for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
this.require(n);
}
this.checkDone();
},
addNode: function(node) {
this.inflight++;
this.require(node);
this.checkDone();
},
require: function(elt) {
var url = elt.src || elt.href;
elt.__nodeUrl = url;
if (!this.dedupe(url, elt)) {
this.fetch(url, elt);
}
},
dedupe: function(url, elt) {
if (this.pending[url]) {
this.pending[url].push(elt);
return true;
}
var resource;
if (this.cache[url]) {
this.onload(url, elt, this.cache[url]);
this.tail();
return true;
}
this.pending[url] = [ elt ];
return false;
},
fetch: function(url, elt) {
flags.load && console.log("fetch", url, elt);
if (!url) {
setTimeout(function() {
this.receive(url, elt, {
error: "href must be specified"
}, null);
}.bind(this), 0);
} else if (url.match(/^data:/)) {
var pieces = url.split(",");
var header = pieces[0];
var body = pieces[1];
if (header.indexOf(";base64") > -1) {
body = atob(body);
} else {
body = decodeURIComponent(body);
}
setTimeout(function() {
this.receive(url, elt, null, body);
}.bind(this), 0);
} else {
var receiveXhr = function(err, resource, redirectedUrl) {
this.receive(url, elt, err, resource, redirectedUrl);
}.bind(this);
xhr.load(url, receiveXhr);
}
},
receive: function(url, elt, err, resource, redirectedUrl) {
this.cache[url] = resource;
var $p = this.pending[url];
for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
this.onload(url, p, resource, err, redirectedUrl);
this.tail();
}
this.pending[url] = null;
},
tail: function() {
--this.inflight;
this.checkDone();
},
checkDone: function() {
if (!this.inflight) {
this.oncomplete();
}
}
};
scope.Loader = Loader;
});
window.HTMLImports.addModule(function(scope) {
var Observer = function(addCallback) {
this.addCallback = addCallback;
this.mo = new MutationObserver(this.handler.bind(this));
};
Observer.prototype = {
handler: function(mutations) {
for (var i = 0, l = mutations.length, m; i < l && (m = mutations[i]); i++) {
if (m.type === "childList" && m.addedNodes.length) {
this.addedNodes(m.addedNodes);
}
}
},
addedNodes: function(nodes) {
if (this.addCallback) {
this.addCallback(nodes);
}
for (var i = 0, l = nodes.length, n, loading; i < l && (n = nodes[i]); i++) {
if (n.children && n.children.length) {
this.addedNodes(n.children);
}
}
},
observe: function(root) {
this.mo.observe(root, {
childList: true,
subtree: true
});
}
};
scope.Observer = Observer;
});
window.HTMLImports.addModule(function(scope) {
var path = scope.path;
var rootDocument = scope.rootDocument;
var flags = scope.flags;
var isIE = scope.isIE;
var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
var IMPORT_SELECTOR = "link[rel=" + IMPORT_LINK_TYPE + "]";
var importParser = {
documentSelectors: IMPORT_SELECTOR,
importsSelectors: [ IMPORT_SELECTOR, "link[rel=stylesheet]:not([type])", "style:not([type])", "script:not([type])", 'script[type="application/javascript"]', 'script[type="text/javascript"]' ].join(","),
map: {
link: "parseLink",
script: "parseScript",
style: "parseStyle"
},
dynamicElements: [],
parseNext: function() {
var next = this.nextToParse();
if (next) {
this.parse(next);
}
},
parse: function(elt) {
if (this.isParsed(elt)) {
flags.parse && console.log("[%s] is already parsed", elt.localName);
return;
}
var fn = this[this.map[elt.localName]];
if (fn) {
this.markParsing(elt);
fn.call(this, elt);
}
},
parseDynamic: function(elt, quiet) {
this.dynamicElements.push(elt);
if (!quiet) {
this.parseNext();
}
},
markParsing: function(elt) {
flags.parse && console.log("parsing", elt);
this.parsingElement = elt;
},
markParsingComplete: function(elt) {
elt.__importParsed = true;
this.markDynamicParsingComplete(elt);
if (elt.__importElement) {
elt.__importElement.__importParsed = true;
this.markDynamicParsingComplete(elt.__importElement);
}
this.parsingElement = null;
flags.parse && console.log("completed", elt);
},
markDynamicParsingComplete: function(elt) {
var i = this.dynamicElements.indexOf(elt);
if (i >= 0) {
this.dynamicElements.splice(i, 1);
}
},
parseImport: function(elt) {
elt.import = elt.__doc;
if (window.HTMLImports.__importsParsingHook) {
window.HTMLImports.__importsParsingHook(elt);
}
if (elt.import) {
elt.import.__importParsed = true;
}
this.markParsingComplete(elt);
if (elt.__resource && !elt.__error) {
elt.dispatchEvent(new CustomEvent("load", {
bubbles: false
}));
} else {
elt.dispatchEvent(new CustomEvent("error", {
bubbles: false
}));
}
if (elt.__pending) {
var fn;
while (elt.__pending.length) {
fn = elt.__pending.shift();
if (fn) {
fn({
target: elt
});
}
}
}
this.parseNext();
},
parseLink: function(linkElt) {
if (nodeIsImport(linkElt)) {
this.parseImport(linkElt);
} else {
linkElt.href = linkElt.href;
this.parseGeneric(linkElt);
}
},
parseStyle: function(elt) {
var src = elt;
elt = cloneStyle(elt);
src.__appliedElement = elt;
elt.__importElement = src;
this.parseGeneric(elt);
},
parseGeneric: function(elt) {
this.trackElement(elt);
this.addElementToDocument(elt);
},
rootImportForElement: function(elt) {
var n = elt;
while (n.ownerDocument.__importLink) {
n = n.ownerDocument.__importLink;
}
return n;
},
addElementToDocument: function(elt) {
var port = this.rootImportForElement(elt.__importElement || elt);
port.parentNode.insertBefore(elt, port);
},
trackElement: function(elt, callback) {
var self = this;
var done = function(e) {
elt.removeEventListener("load", done);
elt.removeEventListener("error", done);
if (callback) {
callback(e);
}
self.markParsingComplete(elt);
self.parseNext();
};
elt.addEventListener("load", done);
elt.addEventListener("error", done);
if (isIE && elt.localName === "style") {
var fakeLoad = false;
if (elt.textContent.indexOf("@import") == -1) {
fakeLoad = true;
} else if (elt.sheet) {
fakeLoad = true;
var csr = elt.sheet.cssRules;
var len = csr ? csr.length : 0;
for (var i = 0, r; i < len && (r = csr[i]); i++) {
if (r.type === CSSRule.IMPORT_RULE) {
fakeLoad = fakeLoad && Boolean(r.styleSheet);
}
}
}
if (fakeLoad) {
setTimeout(function() {
elt.dispatchEvent(new CustomEvent("load", {
bubbles: false
}));
});
}
}
},
parseScript: function(scriptElt) {
var script = document.createElement("script");
script.__importElement = scriptElt;
script.src = scriptElt.src ? scriptElt.src : generateScriptDataUrl(scriptElt);
scope.currentScript = scriptElt;
this.trackElement(script, function(e) {
if (script.parentNode) {
script.parentNode.removeChild(script);
}
scope.currentScript = null;
});
this.addElementToDocument(script);
},
nextToParse: function() {
this._mayParse = [];
return !this.parsingElement && (this.nextToParseInDoc(rootDocument) || this.nextToParseDynamic());
},
nextToParseInDoc: function(doc, link) {
if (doc && this._mayParse.indexOf(doc) < 0) {
this._mayParse.push(doc);
var nodes = doc.querySelectorAll(this.parseSelectorsForNode(doc));
for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
if (!this.isParsed(n)) {
if (this.hasResource(n)) {
return nodeIsImport(n) ? this.nextToParseInDoc(n.__doc, n) : n;
} else {
return;
}
}
}
}
return link;
},
nextToParseDynamic: function() {
return this.dynamicElements[0];
},
parseSelectorsForNode: function(node) {
var doc = node.ownerDocument || node;
return doc === rootDocument ? this.documentSelectors : this.importsSelectors;
},
isParsed: function(node) {
return node.__importParsed;
},
needsDynamicParsing: function(elt) {
return this.dynamicElements.indexOf(elt) >= 0;
},
hasResource: function(node) {
if (nodeIsImport(node) && node.__doc === undefined) {
return false;
}
return true;
}
};
function nodeIsImport(elt) {
return elt.localName === "link" && elt.rel === IMPORT_LINK_TYPE;
}
function generateScriptDataUrl(script) {
var scriptContent = generateScriptContent(script);
return "data:text/javascript;charset=utf-8," + encodeURIComponent(scriptContent);
}
function generateScriptContent(script) {
return script.textContent + generateSourceMapHint(script);
}
function generateSourceMapHint(script) {
var owner = script.ownerDocument;
owner.__importedScripts = owner.__importedScripts || 0;
var moniker = script.ownerDocument.baseURI;
var num = owner.__importedScripts ? "-" + owner.__importedScripts : "";
owner.__importedScripts++;
return "\n//# sourceURL=" + moniker + num + ".js\n";
}
function cloneStyle(style) {
var clone = style.ownerDocument.createElement("style");
clone.textContent = style.textContent;
path.resolveUrlsInStyle(clone);
return clone;
}
scope.parser = importParser;
scope.IMPORT_SELECTOR = IMPORT_SELECTOR;
});
window.HTMLImports.addModule(function(scope) {
var flags = scope.flags;
var IMPORT_LINK_TYPE = scope.IMPORT_LINK_TYPE;
var IMPORT_SELECTOR = scope.IMPORT_SELECTOR;
var rootDocument = scope.rootDocument;
var Loader = scope.Loader;
var Observer = scope.Observer;
var parser = scope.parser;
var importer = {
documents: {},
documentPreloadSelectors: IMPORT_SELECTOR,
importsPreloadSelectors: [ IMPORT_SELECTOR ].join(","),
loadNode: function(node) {
importLoader.addNode(node);
},
loadSubtree: function(parent) {
var nodes = this.marshalNodes(parent);
importLoader.addNodes(nodes);
},
marshalNodes: function(parent) {
return parent.querySelectorAll(this.loadSelectorsForNode(parent));
},
loadSelectorsForNode: function(node) {
var doc = node.ownerDocument || node;
return doc === rootDocument ? this.documentPreloadSelectors : this.importsPreloadSelectors;
},
loaded: function(url, elt, resource, err, redirectedUrl) {
flags.load && console.log("loaded", url, elt);
elt.__resource = resource;
elt.__error = err;
if (isImportLink(elt)) {
var doc = this.documents[url];
if (doc === undefined) {
doc = err ? null : makeDocument(resource, redirectedUrl || url);
if (doc) {
doc.__importLink = elt;
this.bootDocument(doc);
}
this.documents[url] = doc;
}
elt.__doc = doc;
}
parser.parseNext();
},
bootDocument: function(doc) {
this.loadSubtree(doc);
this.observer.observe(doc);
parser.parseNext();
},
loadedAll: function() {
parser.parseNext();
}
};
var importLoader = new Loader(importer.loaded.bind(importer), importer.loadedAll.bind(importer));
importer.observer = new Observer();
function isImportLink(elt) {
return isLinkRel(elt, IMPORT_LINK_TYPE);
}
function isLinkRel(elt, rel) {
return elt.localName === "link" && elt.getAttribute("rel") === rel;
}
function hasBaseURIAccessor(doc) {
return !!Object.getOwnPropertyDescriptor(doc, "baseURI");
}
function makeDocument(resource, url) {
var doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
doc._URL = url;
var base = doc.createElement("base");
base.setAttribute("href", url);
if (!doc.baseURI && !hasBaseURIAccessor(doc)) {
Object.defineProperty(doc, "baseURI", {
value: url
});
}
var meta = doc.createElement("meta");
meta.setAttribute("charset", "utf-8");
doc.head.appendChild(meta);
doc.head.appendChild(base);
doc.body.innerHTML = resource;
if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
HTMLTemplateElement.bootstrap(doc);
}
return doc;
}
if (!document.baseURI) {
var baseURIDescriptor = {
get: function() {
var base = document.querySelector("base");
return base ? base.href : window.location.href;
},
configurable: true
};
Object.defineProperty(document, "baseURI", baseURIDescriptor);
Object.defineProperty(rootDocument, "baseURI", baseURIDescriptor);
}
scope.importer = importer;
scope.importLoader = importLoader;
});
window.HTMLImports.addModule(function(scope) {
var parser = scope.parser;
var importer = scope.importer;
var dynamic = {
added: function(nodes) {
var owner, parsed, loading;
for (var i = 0, l = nodes.length, n; i < l && (n = nodes[i]); i++) {
if (!owner) {
owner = n.ownerDocument;
parsed = parser.isParsed(owner);
}
loading = this.shouldLoadNode(n);
if (loading) {
importer.loadNode(n);
}
if (this.shouldParseNode(n) && parsed) {
parser.parseDynamic(n, loading);
}
}
},
shouldLoadNode: function(node) {
return node.nodeType === 1 && matches.call(node, importer.loadSelectorsForNode(node));
},
shouldParseNode: function(node) {
return node.nodeType === 1 && matches.call(node, parser.parseSelectorsForNode(node));
}
};
importer.observer.addCallback = dynamic.added.bind(dynamic);
var matches = HTMLElement.prototype.matches || HTMLElement.prototype.matchesSelector || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector || HTMLElement.prototype.msMatchesSelector;
});
(function(scope) {
var initializeModules = scope.initializeModules;
var isIE = scope.isIE;
if (scope.useNative) {
return;
}
initializeModules();
var rootDocument = scope.rootDocument;
function bootstrap() {
window.HTMLImports.importer.bootDocument(rootDocument);
}
if (document.readyState === "complete" || document.readyState === "interactive" && !window.attachEvent) {
bootstrap();
} else {
document.addEventListener("DOMContentLoaded", bootstrap);
}
})(window.HTMLImports);
window.CustomElements = window.CustomElements || {
flags: {}
};
(function(scope) {
var flags = scope.flags;
var modules = [];
var addModule = function(module) {
modules.push(module);
};
var initializeModules = function() {
modules.forEach(function(module) {
module(scope);
});
};
scope.addModule = addModule;
scope.initializeModules = initializeModules;
scope.hasNative = Boolean(document.registerElement);
scope.isIE = /Trident/.test(navigator.userAgent);
scope.useNative = !flags.register && scope.hasNative && !window.ShadowDOMPolyfill && (!window.HTMLImports || window.HTMLImports.useNative);
})(window.CustomElements);
window.CustomElements.addModule(function(scope) {
var IMPORT_LINK_TYPE = window.HTMLImports ? window.HTMLImports.IMPORT_LINK_TYPE : "none";
function forSubtree(node, cb) {
findAllElements(node, function(e) {
if (cb(e)) {
return true;
}
forRoots(e, cb);
});
forRoots(node, cb);
}
function findAllElements(node, find, data) {
var e = node.firstElementChild;
if (!e) {
e = node.firstChild;
while (e && e.nodeType !== Node.ELEMENT_NODE) {
e = e.nextSibling;
}
}
while (e) {
if (find(e, data) !== true) {
findAllElements(e, find, data);
}
e = e.nextElementSibling;
}
return null;
}
function forRoots(node, cb) {
var root = node.shadowRoot;
while (root) {
forSubtree(root, cb);
root = root.olderShadowRoot;
}
}
function forDocumentTree(doc, cb) {
_forDocumentTree(doc, cb, []);
}
function _forDocumentTree(doc, cb, processingDocuments) {
doc = window.wrap(doc);
if (processingDocuments.indexOf(doc) >= 0) {
return;
}
processingDocuments.push(doc);
var imports = doc.querySelectorAll("link[rel=" + IMPORT_LINK_TYPE + "]");
for (var i = 0, l = imports.length, n; i < l && (n = imports[i]); i++) {
if (n.import) {
_forDocumentTree(n.import, cb, processingDocuments);
}
}
cb(doc);
}
scope.forDocumentTree = forDocumentTree;
scope.forSubtree = forSubtree;
});
window.CustomElements.addModule(function(scope) {
var flags = scope.flags;
var forSubtree = scope.forSubtree;
var forDocumentTree = scope.forDocumentTree;
function addedNode(node, isAttached) {
return added(node, isAttached) || addedSubtree(node, isAttached);
}
function added(node, isAttached) {
if (scope.upgrade(node, isAttached)) {
return true;
}
if (isAttached) {
attached(node);
}
}
function addedSubtree(node, isAttached) {
forSubtree(node, function(e) {
if (added(e, isAttached)) {
return true;
}
});
}
var hasThrottledAttached = window.MutationObserver._isPolyfilled && flags["throttle-attached"];
scope.hasPolyfillMutations = hasThrottledAttached;
scope.hasThrottledAttached = hasThrottledAttached;
var isPendingMutations = false;
var pendingMutations = [];
function deferMutation(fn) {
pendingMutations.push(fn);
if (!isPendingMutations) {
isPendingMutations = true;
setTimeout(takeMutations);
}
}
function takeMutations() {
isPendingMutations = false;
var $p = pendingMutations;
for (var i = 0, l = $p.length, p; i < l && (p = $p[i]); i++) {
p();
}
pendingMutations = [];
}
function attached(element) {
if (hasThrottledAttached) {
deferMutation(function() {
_attached(element);
});
} else {
_attached(element);
}
}
function _attached(element) {
if (element.__upgraded__ && !element.__attached) {
element.__attached = true;
if (element.attachedCallback) {
element.attachedCallback();
}
}
}
function detachedNode(node) {
detached(node);
forSubtree(node, function(e) {
detached(e);
});
}
function detached(element) {
if (hasThrottledAttached) {
deferMutation(function() {
_detached(element);
});
} else {
_detached(element);
}
}
function _detached(element) {
if (element.__upgraded__ && element.__attached) {
element.__attached = false;
if (element.detachedCallback) {
element.detachedCallback();
}
}
}
function inDocument(element) {
var p = element;
var doc = window.wrap(document);
while (p) {
if (p == doc) {
return true;
}
p = p.parentNode || p.nodeType === Node.DOCUMENT_FRAGMENT_NODE && p.host;
}
}
function watchShadow(node) {
if (node.shadowRoot && !node.shadowRoot.__watched) {
flags.dom && console.log("watching shadow-root for: ", node.localName);
var root = node.shadowRoot;
while (root) {
observe(root);
root = root.olderShadowRoot;
}
}
}
function handler(root, mutations) {
if (flags.dom) {
var mx = mutations[0];
if (mx && mx.type === "childList" && mx.addedNodes) {
if (mx.addedNodes) {
var d = mx.addedNodes[0];
while (d && d !== document && !d.host) {
d = d.parentNode;
}
var u = d && (d.URL || d._URL || d.host && d.host.localName) || "";
u = u.split("/?").shift().split("/").pop();
}
}
console.group("mutations (%d) [%s]", mutations.length, u || "");
}
var isAttached = inDocument(root);
mutations.forEach(function(mx) {
if (mx.type === "childList") {
forEach(mx.addedNodes, function(n) {
if (!n.localName) {
return;
}
addedNode(n, isAttached);
});
forEach(mx.removedNodes, function(n) {
if (!n.localName) {
return;
}
detachedNode(n);
});
}
});
flags.dom && console.groupEnd();
}
function takeRecords(node) {
node = window.wrap(node);
if (!node) {
node = window.wrap(document);
}
while (node.parentNode) {
node = node.parentNode;
}
var observer = node.__observer;
if (observer) {
handler(node, observer.takeRecords());
takeMutations();
}
}
var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
function observe(inRoot) {
if (inRoot.__observer) {
return;
}
var observer = new MutationObserver(handler.bind(this, inRoot));
observer.observe(inRoot, {
childList: true,
subtree: true
});
inRoot.__observer = observer;
}
function upgradeDocument(doc) {
doc = window.wrap(doc);
flags.dom && console.group("upgradeDocument: ", doc.baseURI.split("/").pop());
var isMainDocument = doc === window.wrap(document);
addedNode(doc, isMainDocument);
observe(doc);
flags.dom && console.groupEnd();
}
function upgradeDocumentTree(doc) {
forDocumentTree(doc, upgradeDocument);
}
var originalCreateShadowRoot = Element.prototype.createShadowRoot;
if (originalCreateShadowRoot) {
Element.prototype.createShadowRoot = function() {
var root = originalCreateShadowRoot.call(this);
window.CustomElements.watchShadow(this);
return root;
};
}
scope.watchShadow = watchShadow;
scope.upgradeDocumentTree = upgradeDocumentTree;
scope.upgradeDocument = upgradeDocument;
scope.upgradeSubtree = addedSubtree;
scope.upgradeAll = addedNode;
scope.attached = attached;
scope.takeRecords = takeRecords;
});
window.CustomElements.addModule(function(scope) {
var flags = scope.flags;
function upgrade(node, isAttached) {
if (node.localName === "template") {
if (window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
HTMLTemplateElement.decorate(node);
}
}
if (!node.__upgraded__ && node.nodeType === Node.ELEMENT_NODE) {
var is = node.getAttribute("is");
var definition = scope.getRegisteredDefinition(node.localName) || scope.getRegisteredDefinition(is);
if (definition) {
if (is && definition.tag == node.localName || !is && !definition.extends) {
return upgradeWithDefinition(node, definition, isAttached);
}
}
}
}
function upgradeWithDefinition(element, definition, isAttached) {
flags.upgrade && console.group("upgrade:", element.localName);
if (definition.is) {
element.setAttribute("is", definition.is);
}
implementPrototype(element, definition);
element.__upgraded__ = true;
created(element);
if (isAttached) {
scope.attached(element);
}
scope.upgradeSubtree(element, isAttached);
flags.upgrade && console.groupEnd();
return element;
}
function implementPrototype(element, definition) {
if (Object.__proto__) {
element.__proto__ = definition.prototype;
} else {
customMixin(element, definition.prototype, definition.native);
element.__proto__ = definition.prototype;
}
}
function customMixin(inTarget, inSrc, inNative) {
var used = {};
var p = inSrc;
while (p !== inNative && p !== HTMLElement.prototype) {
var keys = Object.getOwnPropertyNames(p);
for (var i = 0, k; k = keys[i]; i++) {
if (!used[k]) {
Object.defineProperty(inTarget, k, Object.getOwnPropertyDescriptor(p, k));
used[k] = 1;
}
}
p = Object.getPrototypeOf(p);
}
}
function created(element) {
if (element.createdCallback) {
element.createdCallback();
}
}
scope.upgrade = upgrade;
scope.upgradeWithDefinition = upgradeWithDefinition;
scope.implementPrototype = implementPrototype;
});
window.CustomElements.addModule(function(scope) {
var isIE = scope.isIE;
var upgradeDocumentTree = scope.upgradeDocumentTree;
var upgradeAll = scope.upgradeAll;
var upgradeWithDefinition = scope.upgradeWithDefinition;
var implementPrototype = scope.implementPrototype;
var useNative = scope.useNative;
function register(name, options) {
var definition = options || {};
if (!name) {
throw new Error("document.registerElement: first argument `name` must not be empty");
}
if (name.indexOf("-") < 0) {
throw new Error("document.registerElement: first argument ('name') must contain a dash ('-'). Argument provided was '" + String(name) + "'.");
}
if (isReservedTag(name)) {
throw new Error("Failed to execute 'registerElement' on 'Document': Registration failed for type '" + String(name) + "'. The type name is invalid.");
}
if (getRegisteredDefinition(name)) {
throw new Error("DuplicateDefinitionError: a type with name '" + String(name) + "' is already registered");
}
if (!definition.prototype) {
definition.prototype = Object.create(HTMLElement.prototype);
}
definition.__name = name.toLowerCase();
if (definition.extends) {
definition.extends = definition.extends.toLowerCase();
}
definition.lifecycle = definition.lifecycle || {};
definition.ancestry = ancestry(definition.extends);
resolveTagName(definition);
resolvePrototypeChain(definition);
overrideAttributeApi(definition.prototype);
registerDefinition(definition.__name, definition);
definition.ctor = generateConstructor(definition);
definition.ctor.prototype = definition.prototype;
definition.prototype.constructor = definition.ctor;
if (scope.ready) {
upgradeDocumentTree(document);
}
return definition.ctor;
}
function overrideAttributeApi(prototype) {
if (prototype.setAttribute._polyfilled) {
return;
}
var setAttribute = prototype.setAttribute;
prototype.setAttribute = function(name, value) {
changeAttribute.call(this, name, value, setAttribute);
};
var removeAttribute = prototype.removeAttribute;
prototype.removeAttribute = function(name) {
changeAttribute.call(this, name, null, removeAttribute);
};
prototype.setAttribute._polyfilled = true;
}
function changeAttribute(name, value, operation) {
name = name.toLowerCase();
var oldValue = this.getAttribute(name);
operation.apply(this, arguments);
var newValue = this.getAttribute(name);
if (this.attributeChangedCallback && newValue !== oldValue) {
this.attributeChangedCallback(name, oldValue, newValue);
}
}
function isReservedTag(name) {
for (var i = 0; i < reservedTagList.length; i++) {
if (name === reservedTagList[i]) {
return true;
}
}
}
var reservedTagList = [ "annotation-xml", "color-profile", "font-face", "font-face-src", "font-face-uri", "font-face-format", "font-face-name", "missing-glyph" ];
function ancestry(extnds) {
var extendee = getRegisteredDefinition(extnds);
if (extendee) {
return ancestry(extendee.extends).concat([ extendee ]);
}
return [];
}
function resolveTagName(definition) {
var baseTag = definition.extends;
for (var i = 0, a; a = definition.ancestry[i]; i++) {
baseTag = a.is && a.tag;
}
definition.tag = baseTag || definition.__name;
if (baseTag) {
definition.is = definition.__name;
}
}
function resolvePrototypeChain(definition) {
if (!Object.__proto__) {
var nativePrototype = HTMLElement.prototype;
if (definition.is) {
var inst = document.createElement(definition.tag);
nativePrototype = Object.getPrototypeOf(inst);
}
var proto = definition.prototype, ancestor;
var foundPrototype = false;
while (proto) {
if (proto == nativePrototype) {
foundPrototype = true;
}
ancestor = Object.getPrototypeOf(proto);
if (ancestor) {
proto.__proto__ = ancestor;
}
proto = ancestor;
}
if (!foundPrototype) {
console.warn(definition.tag + " prototype not found in prototype chain for " + definition.is);
}
definition.native = nativePrototype;
}
}
function instantiate(definition) {
return upgradeWithDefinition(domCreateElement(definition.tag), definition);
}
var registry = {};
function getRegisteredDefinition(name) {
if (name) {
return registry[name.toLowerCase()];
}
}
function registerDefinition(name, definition) {
registry[name] = definition;
}
function generateConstructor(definition) {
return function() {
return instantiate(definition);
};
}
var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
function createElementNS(namespace, tag, typeExtension) {
if (namespace === HTML_NAMESPACE) {
return createElement(tag, typeExtension);
} else {
return domCreateElementNS(namespace, tag);
}
}
function createElement(tag, typeExtension) {
if (tag) {
tag = tag.toLowerCase();
}
if (typeExtension) {
typeExtension = typeExtension.toLowerCase();
}
var definition = getRegisteredDefinition(typeExtension || tag);
if (definition) {
if (tag == definition.tag && typeExtension == definition.is) {
return new definition.ctor();
}
if (!typeExtension && !definition.is) {
return new definition.ctor();
}
}
var element;
if (typeExtension) {
element = createElement(tag);
element.setAttribute("is", typeExtension);
return element;
}
element = domCreateElement(tag);
if (tag.indexOf("-") >= 0) {
implementPrototype(element, HTMLElement);
}
return element;
}
var domCreateElement = document.createElement.bind(document);
var domCreateElementNS = document.createElementNS.bind(document);
var isInstance;
if (!Object.__proto__ && !useNative) {
isInstance = function(obj, ctor) {
if (obj instanceof ctor) {
return true;
}
var p = obj;
while (p) {
if (p === ctor.prototype) {
return true;
}
p = p.__proto__;
}
return false;
};
} else {
isInstance = function(obj, base) {
return obj instanceof base;
};
}
function wrapDomMethodToForceUpgrade(obj, methodName) {
var orig = obj[methodName];
obj[methodName] = function() {
var n = orig.apply(this, arguments);
upgradeAll(n);
return n;
};
}
wrapDomMethodToForceUpgrade(Node.prototype, "cloneNode");
wrapDomMethodToForceUpgrade(document, "importNode");
document.registerElement = register;
document.createElement = createElement;
document.createElementNS = createElementNS;
scope.registry = registry;
scope.instanceof = isInstance;
scope.reservedTagList = reservedTagList;
scope.getRegisteredDefinition = getRegisteredDefinition;
document.register = document.registerElement;
});
(function(scope) {
var useNative = scope.useNative;
var initializeModules = scope.initializeModules;
var isIE = scope.isIE;
if (useNative) {
var nop = function() {};
scope.watchShadow = nop;
scope.upgrade = nop;
scope.upgradeAll = nop;
scope.upgradeDocumentTree = nop;
scope.upgradeSubtree = nop;
scope.takeRecords = nop;
scope.instanceof = function(obj, base) {
return obj instanceof base;
};
} else {
initializeModules();
}
var upgradeDocumentTree = scope.upgradeDocumentTree;
var upgradeDocument = scope.upgradeDocument;
if (!window.wrap) {
if (window.ShadowDOMPolyfill) {
window.wrap = window.ShadowDOMPolyfill.wrapIfNeeded;
window.unwrap = window.ShadowDOMPolyfill.unwrapIfNeeded;
} else {
window.wrap = window.unwrap = function(node) {
return node;
};
}
}
if (window.HTMLImports) {
window.HTMLImports.__importsParsingHook = function(elt) {
if (elt.import) {
upgradeDocument(wrap(elt.import));
}
};
}
function bootstrap() {
upgradeDocumentTree(window.wrap(document));
window.CustomElements.ready = true;
var requestAnimationFrame = window.requestAnimationFrame || function(f) {
setTimeout(f, 16);
};
requestAnimationFrame(function() {
setTimeout(function() {
window.CustomElements.readyTime = Date.now();
if (window.HTMLImports) {
window.CustomElements.elapsed = window.CustomElements.readyTime - window.HTMLImports.readyTime;
}
document.dispatchEvent(new CustomEvent("WebComponentsReady", {
bubbles: true
}));
});
});
}
if (document.readyState === "complete" || scope.flags.eager) {
bootstrap();
} else if (document.readyState === "interactive" && !window.attachEvent && (!window.HTMLImports || window.HTMLImports.ready)) {
bootstrap();
} else {
var loadEvent = window.HTMLImports && !window.HTMLImports.ready ? "HTMLImportsLoaded" : "DOMContentLoaded";
window.addEventListener(loadEvent, bootstrap);
}
})(window.CustomElements);
(function(scope) {
var style = document.createElement("style");
style.textContent = "" + "body {" + "transition: opacity ease-in 0.2s;" + " } \n" + "body[unresolved] {" + "opacity: 0; display: block; overflow: hidden; position: relative;" + " } \n";
var head = document.querySelector("head");
head.insertBefore(style, head.firstChild);
})(window.WebComponents);
},{}],4:[function(require,module,exports){
'use strict';
var Class = require('class.extend'),
_ = require('underscore'),
hasConnected = false, // See the `isChromecastConnected` function.
ChromecastSessionManager;
function getCastContext() {
return cast.framework.CastContext.getInstance();
}
ChromecastSessionManager = Class.extend(/** @lends ChromecastSessionManager.prototype **/ {
/**
* Stores the state of the current Chromecast session and its associated objects such
* as the
* [RemotePlayerController](https://developers.google.com/cast/docs/reference/chrome/cast.framework.RemotePlayerController),
* and the
* [RemotePlayer](https://developers.google.com/cast/docs/reference/chrome/cast.framework.RemotePlayer).
*
* WARNING: Do not instantiate this class until the
* [CastContext](https://developers.google.com/cast/docs/reference/chrome/cast.framework.CastContext)
* has been configured.
*
* For an undocumented (and thus unknown) reason, RemotePlayer and
* RemotePlayerController instances created before the cast context has been configured
* or after requesting a session or loading media will not stay in sync with media
* items that are loaded later.
*
* For example, the first item that you cast will work as expected: events on
* RemotePlayerController will fire and the state (currentTime, duration, etc) of the
* RemotePlayer instance will update as the media item plays. However, if a new media
* item is loaded via a `loadMedia` request, the media item will play, but the
* remotePlayer will be in a "media unloaded" state where the duration is 0, the
* currentTime does not update, and no change events are fired (except, strangely,
* displayStatus updates).
*
* @param player {object} Video.js Player
* @constructs ChromecastSessionManager
*/
init: function(player) {
this.player = player;
this._addCastContextEventListeners();
// Remove global event listeners when this player instance is destroyed to prevent
// memory leaks.
this.player.on('dispose', this._removeCastContextEventListeners.bind(this));
this._notifyPlayerOfDevicesAvailabilityChange(this.getCastContext().getCastState());
this.remotePlayer = new cast.framework.RemotePlayer();
this.remotePlayerController = new cast.framework.RemotePlayerController(this.remotePlayer);
},
/**
* Add event listeners for events triggered on the current CastContext.
*
* @private
*/
_addCastContextEventListeners: function() {
var sessionStateChangedEvt = cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
castStateChangedEvt = cast.framework.CastContextEventType.CAST_STATE_CHANGED;
this.getCastContext().addEventListener(sessionStateChangedEvt, this._onSessionStateChange.bind(this));
this.getCastContext().addEventListener(castStateChangedEvt, this._onCastStateChange.bind(this));
},
/**
* Remove event listeners that were added in {@link
* ChromecastSessionManager#_addCastContextEventListeners}.
*
* @private
*/
_removeCastContextEventListeners: function() {
var sessionStateChangedEvt = cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
castStateChangedEvt = cast.framework.CastContextEventType.CAST_STATE_CHANGED;
this.getCastContext().removeEventListener(sessionStateChangedEvt);
this.getCastContext().removeEventListener(castStateChangedEvt);
},
/**
* Handle the CastContext's SessionState change event.
*
* @private
*/
_onSessionStateChange: function(event) {
if (event.sessionState === cast.framework.SessionState.SESSION_ENDED) {
this.player.trigger('chromecastDisconnected');
this._reloadTech();
}
},
/**
* Handle the CastContext's CastState change event.
*
* @private
*/
_onCastStateChange: function(event) {
this._notifyPlayerOfDevicesAvailabilityChange(event.castState);
},
/**
* Triggers player events that notifies listeners that Chromecast devices are
* either available or unavailable.
*
* @private
*/
_notifyPlayerOfDevicesAvailabilityChange: function(castState) {
if (this.hasAvailableDevices(castState)) {
this.player.trigger('chromecastDevicesAvailable');
} else {
this.player.trigger('chromecastDevicesUnavailable');
}
},
/**
* Returns whether or not there are Chromecast devices available to cast to.
*
* @see https://developers.google.com/cast/docs/reference/chrome/cast.framework#.CastState
* @param {String} castState
* @return {boolean} true if there are Chromecast devices available to cast to.
*/
hasAvailableDevices: function(castState) {
castState = castState || this.getCastContext().getCastState();
return castState === cast.framework.CastState.NOT_CONNECTED ||
castState === cast.framework.CastState.CONNECTING ||
castState === cast.framework.CastState.CONNECTED;
},
/**
* Opens the Chromecast casting menu by requesting a CastSession. Does nothing if the
* Video.js player does not have a source.
*/
openCastMenu: function() {
var onSessionSuccess;
if (!this.player.currentSource()) {
// Do not cast if there is no media item loaded in the player
return;
}
onSessionSuccess = function() {
hasConnected = true;
this.player.trigger('chromecastConnected');
this._reloadTech();
}.bind(this);
// It is the `requestSession` function call that actually causes the cast menu to
// open.
// The second parameter to `.then` is an error handler. We use _.noop here
// because we handle errors in the ChromecastTech class and we do not want an
// error to bubble up to the console. This error handler is also triggered when
// the user closes out of the chromecast selector pop-up without choosing a
// casting destination.
this.getCastContext().requestSession()
.then(onSessionSuccess, _.noop);
},
/**
* Reloads the Video.js player's Tech. This causes the player to re-evaluate which
* Tech should be used for the current source by iterating over available Tech and
* calling `Tech.isSupported` and `Tech.canPlaySource`. Video.js uses the first
* Tech that returns true from both of those functions. This is what allows us to
* switch back and forth between the Chromecast Tech and other available Tech when a
* CastSession is connected or disconnected.
*
* @private
*/
_reloadTech: function() {
var player = this.player,
currentTime = player.currentTime(),
wasPaused = player.paused(),
sources = player.currentSources();
// Reload the current source(s) to re-lookup and use the currently available Tech.
// The chromecast Tech gets used if `ChromecastSessionManager.isChromecastConnected`
// is true (effectively, if a chromecast session is currently in progress),
// otherwise Video.js continues to search through the Tech list for other eligible
// Tech to use, such as the HTML5 player.
player.src(sources);
player.ready(function() {
if (wasPaused) {
player.pause();
} else {
player.play();
}
player.currentTime(currentTime || 0);
});
},
/**
* @see https://developers.google.com/cast/docs/reference/chrome/cast.framework.CastContext
* @returns {object} the current CastContext, if one exists
*/
getCastContext: getCastContext,
/**
* @see https://developers.google.com/cast/docs/reference/chrome/cast.framework.RemotePlayer
* @returns {object} the current RemotePlayer, if one exists
*/
getRemotePlayer: function() {
return this.remotePlayer;
},
/**
* @see https://developers.google.com/cast/docs/reference/chrome/cast.framework.RemotePlayerController
* @returns {object} the current RemotePlayerController, if one exists
*/
getRemotePlayerController: function() {
return this.remotePlayerController;
},
});
/**
* Returns whether or not the current Chromecast API is available (that is,
* `window.chrome`, `window.chrome.cast`, and `window.cast` exist).
*
* @static
* @returns {boolean} true if the Chromecast API is available
*/
ChromecastSessionManager.isChromecastAPIAvailable = function() {
return window.chrome && window.chrome.cast && window.cast;
};
/**
* Returns whether or not there is a current CastSession and it is connected.
*
* @static
* @returns {boolean} true if the current CastSession exists and is connected
*/
ChromecastSessionManager.isChromecastConnected = function() {
// We must also check the `hasConnected` flag because
// `getCastContext().getCastState()` returns `CONNECTED` even when the current casting
// session was initiated by another tab in the browser or by another process.
return ChromecastSessionManager.isChromecastAPIAvailable() &&
(getCastContext().getCastState() === cast.framework.CastState.CONNECTED) &&
hasConnected;
};
module.exports = ChromecastSessionManager;
},{"class.extend":1,"underscore":2}],5:[function(require,module,exports){
'use strict';
/**
* The ChromecastButton module contains both the ChromecastButton class definition and
* the function used to register the button as a Video.js Component.
*
* @module ChromecastButton
*/
var ChromecastButton;
/**
* The Video.js Button class is the base class for UI button components.
*
* @external Button
* @see {@link http://docs.videojs.com/Button.html|Button}
*/
/** @lends ChromecastButton.prototype */
ChromecastButton = {
/**
* This class is a button component designed to be displayed in the player UI's control
* bar. It opens the Chromecast menu when clicked.
*
* @constructs
* @extends external:Button
* @param player {Player} the video.js player instance
*/
constructor: function(player) {
this.constructor.super_.apply(this, arguments);
player.on('chromecastConnected', this._onChromecastConnected.bind(this));
player.on('chromecastDisconnected', this._onChromecastDisconnected.bind(this));
player.on('chromecastDevicesAvailable', this._onChromecastDevicesAvailable.bind(this));
player.on('chromecastDevicesUnavailable', this._onChromecastDevicesUnavailable.bind(this));
this.controlText('Open Chromecast menu');
// Use the initial state of `hasAvailableDevices` to call the corresponding event
// handlers because the corresponding events may have already been emitted before
// binding the listeners above.
if (player.chromecastSessionManager && player.chromecastSessionManager.hasAvailableDevices()) {
this._onChromecastDevicesAvailable();
} else {
this._onChromecastDevicesUnavailable();
}
},
/**
* Overrides Button#buildCSSClass to return the classes used on the button element.
*
* @param el {DOMElement}
* @see {@link http://docs.videojs.com/Button.html#buildCSSClass|Button#buildCSSClass}
*/
buildCSSClass: function() {
return 'vjs-chromecast-button ' + (this._isChromecastConnected ? 'vjs-chromecast-casting-state ' : '') +
this.constructor.super_.prototype.buildCSSClass();
},
/**
* Overrides Button#handleClick to handle button click events. Chromecast functionality
* is handled outside of this class, which should be limited to UI related logic. This
* function simply triggers an event on the player.
*
* @fires ChromecastButton#chromecastRequested
* @param el {DOMElement}
* @see {@link http://docs.videojs.com/Button.html#handleClick|Button#handleClick}
*/
handleClick: function() {
this.player().trigger('chromecastRequested');
},
/**
* Handles `chromecastConnected` player events.
*
* @private
*/
_onChromecastConnected: function() {
this._isChromecastConnected = true;
this._reloadCSSClasses();
},
/**
* Handles `chromecastDisconnected` player events.
*
* @private
*/
_onChromecastDisconnected: function() {
this._isChromecastConnected = false;
this._reloadCSSClasses();
},
/**
* Handles `chromecastDevicesAvailable` player events.
*
* @private
*/
_onChromecastDevicesAvailable: function() {
this.show();
},
/**
* Handles `chromecastDevicesUnavailable` player events.
*
* @private
*/
_onChromecastDevicesUnavailable: function() {
this.hide();
},
/**
* Re-calculates which CSS classes the button needs and sets them on the buttons'
* DOMElement.
*
* @private
*/
_reloadCSSClasses: function() {
if (!this.el_) {
return;
}
this.el_.className = this.buildCSSClass();
},
};
/**
* Registers the ChromecastButton Component with Video.js. Calls
* {@link http://docs.videojs.com/Component.html#.registerComponent}, which will add a
* component called `chromecastButton` to the list of globally registered Video.js
* components. The `chromecastButton` is added to the player's control bar UI
* automatically once {@link module:enableChromecast} has been called. If you would like
* to specify the order of the buttons that appear in the control bar, including this
* button, you can do so in the options that you pass to the `videojs` function when
* creating a player:
*
* ```
* videojs('playerID', {
* controlBar: {
* children: [
* 'playToggle',
* 'progressControl',
* 'volumePanel',
* 'fullscreenToggle',
* 'chromecastButton',
* ],
* }
* });
* ```
*
* @param videojs {object} A reference to {@link http://docs.videojs.com/module-videojs.html|Video.js}
* @see http://docs.videojs.com/module-videojs.html#~registerPlugin
*/
module.exports = function(videojs) {
var ChromecastButtonImpl;
ChromecastButtonImpl = videojs.extend(videojs.getComponent('Button'), ChromecastButton);
videojs.registerComponent('chromecastButton', ChromecastButtonImpl);
};
},{}],6:[function(require,module,exports){
'use strict';
/**
* @module enableChromecast
*/
var ChromecastSessionManager = require('./chromecast/ChromecastSessionManager'),
_ = require('underscore'),
CHECK_AVAILABILITY_INTERVAL = 1000, // milliseconds
CHECK_AVAILABILITY_TIMEOUT = 30 * 1000; // milliseconds
/**
* Configures the Chromecast
* [casting context](https://developers.google.com/cast/docs/reference/chrome/cast.framework.CastContext),
* which is required before casting.
*
* @private
* @param options {object} the plugin options
*/
function configureCastContext(options) {
var context = cast.framework.CastContext.getInstance();
context.setOptions({
receiverApplicationId: options.receiverAppID || chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
// Setting autoJoinPolicy to ORIGIN_SCOPED prevents this plugin from automatically
// trying to connect to a preexisting Chromecast session, if one exists. The user
// must end any existing session before trying to cast from this player instance.
autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
});
}
/**
* Handles the `chromecastRequested` event. Delegates to a `chromecastSessionManager`
* instance.
*
* @private
* @param player {object} a Video.js player instance
*/
function onChromecastRequested(player) {
player.chromecastSessionManager.openCastMenu();
}
/**
* Adds the Chromecast button to the player's control bar, if one does not already exist,
* then starts listening for the `chromecastRequested` event.
*
* @private
* @param player {object} a Video.js player instance
* @param options {object} the plugin options
*/
function setUpChromecastButton(player, options) {
var indexOpt;
// Ensure Chromecast button exists
if (options.addButtonToControlBar && !player.controlBar.getChild('chromecastButton')) {
// Figure out Chromecast button's index
indexOpt = player.controlBar.children().length;
if (typeof options.buttonPositionIndex !== 'undefined') {
indexOpt = options.buttonPositionIndex >= 0
? options.buttonPositionIndex
: player.controlBar.children().length + options.buttonPositionIndex;
}
player.controlBar.addChild('chromecastButton', options, indexOpt);
}
// Respond to requests for casting. The ChromecastButton component triggers this event
// when the user clicks the Chromecast button.
player.on('chromecastRequested', onChromecastRequested.bind(null, player));
}
/**
* Creates a {@link ChromecastSessionManager} and assigns it to the player.
*
* @private
* @param player {object} a Video.js player instance
*/
function createSessionManager(player) {
if (!player.chromecastSessionManager) {
player.chromecastSessionManager = new ChromecastSessionManager(player);
}
}
/**
* Sets up and configures the casting context and Chromecast button.
*
* @private
* @param options {object} the plugin options
*/
function enableChromecast(player, options) {
configureCastContext(options);
createSessionManager(player);
setUpChromecastButton(player, options);
}
/**
* Waits for the Chromecast APIs to become available, then configures the casting context
* and configures the Chromecast button. The Chromecast APIs are loaded asynchronously,
* so we must wait until they are available before initializing the casting context and
* Chromecast button.
*
* @private
* @param player {object} a Video.js player instance
* @param options {object} the plugin options
*/
function waitUntilChromecastAPIsAreAvailable(player, options) {
var maxTries = CHECK_AVAILABILITY_TIMEOUT / CHECK_AVAILABILITY_INTERVAL,
tries = 1,
intervalID;
// The Chromecast APIs are loaded asynchronously, so they may not be loaded and
// initialized at this point. The Chromecast APIs do provide a callback function that
// is called after the framework has loaded, but it requires you to define the callback
// function **before** loading the APIs. That would require us to expose some callback
// function to `window` here, and would require users of this plugin to define a
// Chromecast API callback on `window` that calls our callback function in their HTML
// file. To avoid all of this, we simply check to see if the Chromecast API is
// available periodically, and stop after a timeout threshold has passed.
//
// See https://developers.google.com/cast/docs/chrome_sender_integrate#initialization
intervalID = setInterval(function() {
if (tries > maxTries) {
clearInterval(intervalID);
return;
}
if (ChromecastSessionManager.isChromecastAPIAvailable()) {
clearInterval(intervalID);
enableChromecast(player, options);
}
tries = tries + 1;
}, CHECK_AVAILABILITY_INTERVAL);
}
/**
* Registers the Chromecast plugin with Video.js. Calls
* [videojs#registerPlugin](http://docs.videojs.com/module-videojs.html#~registerPlugin),
* which will add a plugin function called `chromecast` to any instance of a Video.js
* player that is created after calling this function. Call `player.chromecast(options)`,
* passing in configuration options, to enable the Chromecast plugin on your Player
* instance.
*
* Currently, there are only two configuration options:
*
* * **`receiverAppID`** - the string ID of a [Chromecast receiver
* app](https://developers.google.com/cast/docs/receiver_apps) to use. Defaults to
* the [default Media Receiver
* ID](https://developers.google.com/cast/docs/receiver_apps#default).
* * **`addButtonToControlBar`** - flag that tells the plugin
* whether or not it should automatically add the Chromecast button the the Video.js
* player's control bar component. Defaults to `true`.
*
* Other configuration options are set through the player's Chromecast Tech configuration:
*
* ```
* var playerOptions, player, pluginOptions;
*
* playerOptions = {
* chromecast: {
* requestTitleFn: function(source) {
* return titles[source.url];
* },
* requestSubtitleFn: function(source) {
* return subtitles[source.url];
* },
* requestCustomDataFn: function(source) {
* return customData[source.url];
* }
* }
* };
*
* pluginOptions = {
* receiverAppID: '1234',
* addButtonToControlBar: false,
* };
*
* player = videojs(document.getElementById('myVideoElement'), playerOptions);
* player.chromecast(pluginOptions); // initializes the Chromecast plugin
* ```
*
* @param {object} videojs
* @see http://docs.videojs.com/module-videojs.html#~registerPlugin
*/
module.exports = function(videojs) {
videojs.registerPlugin('chromecast', function(options) {
var pluginOptions = _.extend({ addButtonToControlBar: true }, options || {});
// `this` is an instance of a Video.js Player.
// Wait until the player is "ready" so that the player's control bar component has
// been created.
this.ready(function() {
if (!this.controlBar) {
return;
}
if (ChromecastSessionManager.isChromecastAPIAvailable()) {
enableChromecast(this, pluginOptions);
} else {
waitUntilChromecastAPIsAreAvailable(this, pluginOptions);
}
}.bind(this));
});
};
},{"./chromecast/ChromecastSessionManager":4,"underscore":2}],7:[function(require,module,exports){
/* eslint-disable global-require */
'use strict';
var _ = require('underscore'),
preloadWebComponents = require('./preloadWebComponents'),
createChromecastButton = require('./components/ChromecastButton'),
createChromecastTech = require('./tech/ChromecastTech'),
enableChromecast = require('./enableChromecast');
/**
* @module index
*/
/**
* Registers the Chromecast plugin and ChromecastButton Component with Video.js. See
* {@link module:ChromecastButton} and {@link module:enableChromecast} for more details
* about how the plugin and button are registered and configured.
*
* @param videojs {object} the videojs library. If `undefined`, this plugin
* will look to `window.videojs`.
* @param userOpts {object} the options to use for configuration
* @see module:enableChromecast
* @see module:ChromecastButton
*/
module.exports = function(videojs, userOpts) {
var options = _.defaults(_.extend({}, userOpts), { preloadWebComponents: false });
if (options.preloadWebComponents) {
preloadWebComponents();
}
videojs = videojs || window.videojs;
createChromecastButton(videojs);
createChromecastTech(videojs);
enableChromecast(videojs);
};
},{"./components/ChromecastButton":5,"./enableChromecast":6,"./preloadWebComponents":8,"./tech/ChromecastTech":10,"underscore":2}],8:[function(require,module,exports){
'use strict';
var _ = require('underscore');
function doesUserAgentContainString(str) {
return _.isString(window.navigator.userAgent) && window.navigator.userAgent.indexOf(str) >= 0;
}
// For information as to why this is needed, please see:
// https://github.com/silvermine/videojs-chromecast/issues/17
// https://github.com/silvermine/videojs-chromecast/issues/22
module.exports = function() {
var needsWebComponents = !document.registerElement,
iosChrome = doesUserAgentContainString('CriOS'),
androidChrome;
androidChrome = doesUserAgentContainString('Android')
&& doesUserAgentContainString('Chrome/')
&& window.navigator.presentation;
// These checks are based on the checks found in `cast_sender.js` which
// determine if `cast_framework.js` needs to be loaded
if ((androidChrome || iosChrome) && needsWebComponents) {
// This is requiring webcomponents.js@0.7.24 because that's what was used
// by the Chromecast framework at the time this was added.
// We are using webcomponents-lite.js because it doesn't interfere with jQuery as
// badly (e.g. it doesn't interfere with jQuery's fix for consistently bubbling
// events, see #21). While the "lite" version does not include the shadow DOM
// polyfills that the Chromecast framework may need for the <google-cast-button>
// component to work properly, this plugin does not use the <google-cast-button>
// component.
require('webcomponents.js/webcomponents-lite.js'); // eslint-disable-line global-require
}
};
},{"underscore":2,"webcomponents.js/webcomponents-lite.js":3}],9:[function(require,module,exports){
'use strict';
// This file is used to create a standalone javascript file for use in a script tag. The
// file that is output assumes that Video.js is available at `window.videojs`.
require('./index')(undefined, window.SILVERMINE_VIDEOJS_CHROMECAST_CONFIG);
},{"./index":7}],10:[function(require,module,exports){
'use strict';
var ChromecastSessionManager = require('../chromecast/ChromecastSessionManager'),
ChromecastTechUI = require('./ChromecastTechUI'),
_ = require('underscore'),
SESSION_TIMEOUT = 10 * 1000, // milliseconds
ChromecastTech;
/**
* @module ChomecastTech
*/
/**
* The Video.js Tech class is the base class for classes that provide media playback
* technology implementations to Video.js such as HTML5, Flash and HLS.
*
* @external Tech
* @see {@link http://docs.videojs.com/Tech.html|Tech}
*/
/** @lends ChromecastTech.prototype */
ChromecastTech = {
/**
* Implements Video.js playback {@link http://docs.videojs.com/tutorial-tech_.html|Tech}
* for {@link https://developers.google.com/cast/|Google's Chromecast}.
*
* @constructs ChromecastTech
* @extends external:Tech
* @param options {object} The options to use for configuration
* @see {@link https://developers.google.com/cast/|Google Cast}
*/
constructor: function(options) {
var subclass;
this._eventListeners = [];
this.videojsPlayer = this.videojs(options.playerId);
this._chromecastSessionManager = this.videojsPlayer.chromecastSessionManager;
// We have to initialize the UI here, before calling super.constructor
// because the constructor calls `createEl`, which references `this._ui`.
this._ui = new ChromecastTechUI();
this._ui.updatePoster(this.videojsPlayer.poster());
// Call the super class' constructor function
subclass = this.constructor.super_.apply(this, arguments);
this._remotePlayer = this._chromecastSessionManager.getRemotePlayer();
this._remotePlayerController = this._chromecastSessionManager.getRemotePlayerController();
this._listenToPlayerControllerEvents();
this.on('dispose', this._removeAllEventListeners.bind(this));
this._hasPlayedAnyItem = false;
this._requestTitle = options.requestTitleFn || _.noop;
this._requestSubtitle = options.requestSubtitleFn || _.noop;
this._requestCustomData = options.requestCustomDataFn || _.noop;
// See `currentTime` function
this._initialStartTime = options.startTime || 0;
this._playSource(options.source, this._initialStartTime);
this.ready(function() {
this.setMuted(options.muted);
}.bind(this));
return subclass;
},
/**
* Creates a DOMElement that Video.js displays in its player UI while this Tech is
* active.
*
* @returns {DOMElement}
* @see {@link http://docs.videojs.com/Tech.html#createEl}
*/
createEl: function() {
return this._ui.getDOMElement();
},
/**
* Resumes playback if a media item is paused or restarts an item from its beginning if
* the item has played and ended.
*
* @see {@link http://docs.videojs.com/Player.html#play}
*/
play: function() {
if (!this.paused()) {
return;
}
if (this.ended() && !this._isMediaLoading) {
// Restart the current item from the beginning
this._playSource({ src: this.videojsPlayer.src() }, 0);
} else {
this._remotePlayerController.playOrPause();
}
},
/**
* Pauses playback if the player is not already paused and if the current media item
* has not ended yet.
*
* @see {@link http://docs.videojs.com/Player.html#pause}
*/
pause: function() {
if (!this.paused() && this._remotePlayer.canPause) {
this._remotePlayerController.playOrPause();
}
},
/**
* Returns whether or not the player is "paused". Video.js' definition of "paused" is
* "playback paused" OR "not playing".
*
* @returns {boolean} true if playback is paused
* @see {@link http://docs.videojs.com/Player.html#paused}
*/
paused: function() {
return this._remotePlayer.isPaused || this.ended() || this._remotePlayer.playerState === null;
},
/**
* Stores the given source and begins playback, starting at the beginning
* of the media item.
*
* @param source {object} the source to store and play
* @see {@link http://docs.videojs.com/Player.html#src}
*/
setSource: function(source) {
if (this._currentSource && this._currentSource.src === source.src && this._currentSource.type === source.type) {
// Skip setting the source if the `source` argument is the same as what's already
// been set. This `setSource` function calls `this._playSource` which sends a
// "load media" request to the Chromecast PlayerController. Because this function
// may be called multiple times in rapid succession with the same `source`
// argument, we need to de-duplicate calls with the same `source` argument to
// prevent overwhelming the Chromecast PlayerController with expensive "load
// media" requests, which it itself does not de-duplicate.
return;
}
// We cannot use `this.videojsPlayer.currentSource()` because the value returned by
// that function is not the same as what's returned by the Video.js Player's
// middleware after they are run. Also, simply using `this.videojsPlayer.src()`
// does not include mimetype information which we pass to the Chromecast player.
this._currentSource = source;
this._playSource(source, 0);
},
/**
* Plays the given source, beginning at an optional starting time.
*
* @private
* @param source {object} the source to play
* @param [startTime] The time to start playback at, in seconds
* @see {@link http://docs.videojs.com/Player.html#src}
*/
_playSource: function(source, startTime) {
var castSession = this._getCastSession(),
mediaInfo = new chrome.cast.media.MediaInfo(source.src, source.type),
title = this._requestTitle(source),
subtitle = this._requestSubtitle(source),
customData = this._requestCustomData(source),
request;
this.trigger('waiting');
this._clearSessionTimeout();
mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata();
mediaInfo.metadata.metadataType = chrome.cast.media.MetadataType.GENERIC;
mediaInfo.metadata.title = title;
mediaInfo.metadata.subtitle = subtitle;
if (customData) {
mediaInfo.customData = customData;
}
this._ui.updateTitle(title);
this._ui.updateSubtitle(subtitle);
request = new chrome.cast.media.LoadRequest(mediaInfo);
request.autoplay = true;
request.currentTime = startTime;
this._isMediaLoading = true;
this._hasPlayedCurrentItem = false;
castSession.loadMedia(request)
.then(function() {
if (!this._hasPlayedAnyItem) {
// `triggerReady` is required here to notify the Video.js player that the
// Tech has been initialized and is ready.
this.triggerReady();
}
this.trigger('loadstart');
this.trigger('loadeddata');
this.trigger('play');
this.trigger('playing');
this._hasPlayedAnyItem = true;
this._isMediaLoading = false;
}.bind(this), this._triggerErrorEvent.bind(this));
},
/**
* Manually updates the current time. The playback position will jump to the given time
* and continue playing if the item was playing when `setCurrentTime` was called, or
* remain paused if the item was paused.
*
* @param time {number} the playback time position to jump to
* @see {@link http://docs.videojs.com/Tech.html#setCurrentTime}
*/
setCurrentTime: function(time) {
var duration = this.duration();
if (time > duration || !this._remotePlayer.canSeek) {
return;
}
// Seeking to any place within (approximately) 1 second of the end of the item
// causes the Video.js player to get stuck in a BUFFERING state. To work around
// this, we only allow seeking to within 1 second of the end of an item.
this._remotePlayer.currentTime = Math.min(duration - 1, time);
this._remotePlayerController.seek();
this._triggerTimeUpdateEvent();
},
/**
* Returns the current playback time position.
*
* @returns {number} the current playback time position
* @see {@link http://docs.videojs.com/Player.html#currentTime}
*/
currentTime: function() {
// There is a brief period of time when Video.js has switched to the chromecast
// Tech, but chromecast has not yet loaded its first media item. During that time,
// Video.js calls this `currentTime` function to update its player UI. In that
// period, `this._remotePlayer.currentTime` will be 0 because the media has not
// loaded yet. To prevent the UI from using a 0 second currentTime, we use the
// currentTime passed in to the first media item that was provided to the Tech until
// chromecast plays its first item.
if (!this._hasPlayedAnyItem) {
return this._initialStartTime;
}
return this._remotePlayer.currentTime;
},
/**
* Returns the duration of the current media item, or `0` if the source is not set or
* if the duration of the item is not available from the Chromecast API yet.
*
* @returns {number} the duration of the current media item
* @see {@link http://docs.videojs.com/Player.html#duration}
*/
duration: function() {
// There is a brief period of time when Video.js has switched to the chromecast
// Tech, but chromecast has not yet loaded its first media item. During that time,
// Video.js calls this `duration` function to update its player UI. In that period,
// `this._remotePlayer.duration` will be 0 because the media has not loaded yet. To
// prevent the UI from using a 0 second duration, we use the duration passed in to
// the first media item that was provided to the Tech until chromecast plays its
// first item.
if (!this._hasPlayedAnyItem) {
return this.videojsPlayer.duration();
}
return this._remotePlayer.duration;
},
/**
* Returns whether or not the current media item has finished playing. Returns `false`
* if a media item has not been loaded, has not been played, or has not yet finished
* playing.
*
* @returns {boolean} true if the current media item has finished playing
* @see {@link http://docs.videojs.com/Player.html#ended}
*/
ended: function() {
var mediaSession = this._getMediaSession();
return mediaSession ? (mediaSession.idleReason === chrome.cast.media.IdleReason.FINISHED) : false;
},
/**
* Returns the current volume level setting as a decimal number between `0` and `1`.
*
* @returns {number} the current volume level
* @see {@link http://docs.videojs.com/Player.html#volume}
*/
volume: function() {
return this._remotePlayer.volumeLevel;
},
/**
* Sets the current volume level. Volume level is a decimal number between `0` and `1`,
* where `0` is muted and `1` is the loudest volume level.
*
* @param volumeLevel {number}
* @returns {number} the current volume level
* @see {@link http://docs.videojs.com/Player.html#volume}
*/
setVolume: function(volumeLevel) {
this._remotePlayer.volumeLevel = volumeLevel;
this._remotePlayerController.setVolumeLevel();
// This event is triggered by the listener on
// `RemotePlayerEventType.VOLUME_LEVEL_CHANGED`, but waiting for that event to fire
// in response to calls to `setVolume` introduces noticeable lag in the updating of
// the player UI's volume slider bar, which makes user interaction with the volume
// slider choppy.
this._triggerVolumeChangeEvent();
},
/**
* Returns whether or not the player is currently muted.
*
* @returns {boolean} true if the player is currently muted
* @see {@link http://docs.videojs.com/Player.html#muted}
*/
muted: function() {
return this._remotePlayer.isMuted;
},
/**
* Mutes or un-mutes the player. Does nothing if the player is currently muted and the
* `isMuted` parameter is true or if the player is not muted and `isMuted` is false.
*
* @param isMuted {boolean} whether or not the player should be muted
* @see {@link http://docs.videojs.com/Html5.html#setMuted} for an example
*/
setMuted: function(isMuted) {
if ((this._remotePlayer.isMuted && !isMuted) || (!this._remotePlayer.isMuted && isMuted)) {
this._remotePlayerController.muteOrUnmute();
}
},
/**
* Gets the URL to the current poster image.
*
* @returns {string} URL to the current poster image or `undefined` if none exists
* @see {@link http://docs.videojs.com/Player.html#poster}
*/
poster: function() {
return this._ui.getPoster();
},
/**
* Sets the URL to the current poster image. The poster image shown in the Chromecast
* Tech UI view is updated with this new URL.
*
* @param poster {string} the URL to the new poster image
* @see {@link http://docs.videojs.com/Tech.html#setPoster}
*/
setPoster: function(poster) {
this._ui.updatePoster(poster);
},
/**
* This function is "required" when implementing {@link external:Tech} and is supposed
* to return a mock
* {@link https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges|TimeRanges}
* object that represents the portions of the current media item that have been
* buffered. However, the Chromecast API does not currently provide a way to determine
* how much the media item has buffered, so we always return `undefined`.
*
* Returning `undefined` is safe: the player will simply not display the buffer amount
* indicator in the scrubber UI.
*
* @returns {undefined} always returns `undefined`
* @see {@link http://docs.videojs.com/Player.html#buffered}
*/
buffered: function() {
return undefined;
},
/**
* This function is "required" when implementing {@link external:Tech} and is supposed
* to return a mock
* {@link https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges|TimeRanges}
* object that represents the portions of the current media item that has playable
* content. However, the Chromecast API does not currently provide a way to determine
* how much the media item has playable content, so we'll just assume the entire video
* is an available seek target.
*
* The risk here lies with live streaming, where there may exist a sliding window of
* playable content and seeking is only possible within the last X number of minutes,
* rather than for the entire video.
*
* Unfortunately we have no way of detecting when this is the case. Returning anything
* other than the full range of the video means that we lose the ability to seek during
* VOD.
*
* @returns {TimeRanges} always returns a `TimeRanges` object with one `TimeRange` that
* starts at `0` and ends at the `duration` of the current media item
* @see {@link http://docs.videojs.com/Player.html#seekable}
*/
seekable: function() {
// TODO Investigate if there's a way to detect if the source is live, so that we can
// possibly adjust the seekable `TimeRanges` accordingly.
return this.videojs.createTimeRange(0, this.duration());
},
/**
* Returns whether the native media controls should be shown (`true`) or hidden
* (`false`). Not applicable to this Tech.
*
* @returns {boolean} always returns `false`
* @see {@link http://docs.videojs.com/Html5.html#controls} for an example
*/
controls: function() {
return false;
},
/**
* Returns whether or not the browser should show the player "inline" (non-fullscreen)
* by default. This function always returns true to tell the browser that non-
* fullscreen playback is preferred.
*
* @returns {boolean} always returns `true`
* @see {@link http://docs.videojs.com/Html5.html#playsinline} for an example
*/
playsinline: function() {
return true;
},
/**
* Returns whether or not fullscreen is supported by this Tech. Always returns `true`
* because fullscreen is always supported.
*
* @returns {boolean} always returns `true`
* @see {@link http://docs.videojs.com/Html5.html#supportsFullScreen} for an example
*/
supportsFullScreen: function() {
return true;
},
/**
* Sets a flag that determines whether or not the media should automatically begin
* playing on page load. This is not supported because a Chromecast session must be
* initiated by casting via the casting menu and cannot autoplay.
*
* @see {@link http://docs.videojs.com/Html5.html#setAutoplay} for an example
*/
setAutoplay: function() {
// Not supported
},
/**
* @returns {number} the chromecast player's playback rate, if available. Otherwise,
* the return value defaults to `1`.
*/
playbackRate: function() {
var mediaSession = this._getMediaSession();
return mediaSession ? mediaSession.playbackRate : 1;
},
/**
* Does nothing. Changing the playback rate is not supported.
*/
setPlaybackRate: function() {
// Not supported
},
/**
* Causes the Tech to begin loading the current source. `load` is not supported in this
* ChromecastTech because setting the source on the `Chromecast` automatically causes
* it to begin loading.
*/
load: function() {
// Not supported
},
/**
* Gets the Chromecast equivalent of HTML5 Media Element's `readyState`.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
*/
readyState: function() {
if (this._remotePlayer.playerState === 'IDLE' || this._remotePlayer.playerState === 'BUFFERING') {
return 0; // HAVE_NOTHING
}
return 4;
},
/**
* Wires up event listeners for
* [RemotePlayerController](https://developers.google.com/cast/docs/reference/chrome/cast.framework.RemotePlayerController)
* events.
*
* @private
*/
_listenToPlayerControllerEvents: function() {
var eventTypes = cast.framework.RemotePlayerEventType;
this._addEventListener(this._remotePlayerController, eventTypes.PLAYER_STATE_CHANGED, this._onPlayerStateChanged, this);
this._addEventListener(this._remotePlayerController, eventTypes.VOLUME_LEVEL_CHANGED, this._triggerVolumeChangeEvent, this);
this._addEventListener(this._remotePlayerController, eventTypes.IS_MUTED_CHANGED, this._triggerVolumeChangeEvent, this);
this._addEventListener(this._remotePlayerController, eventTypes.CURRENT_TIME_CHANGED, this._triggerTimeUpdateEvent, this);
this._addEventListener(this._remotePlayerController, eventTypes.DURATION_CHANGED, this._triggerDurationChangeEvent, this);
},
/**
* Registers an event listener on the given target object. Because many objects in the
* Chromecast API are either singletons or must be shared between instances of
* `ChromecastTech` for the lifetime of the player, we must unbind the listeners when
* this Tech instance is destroyed to prevent memory leaks. To do that, we need to keep
* a reference to listeners that are added to global objects so that we can use those
* references to remove the listener when this Tech is destroyed.
*
* @param target {object} the object to register the event listener on
* @param type {string} the name of the event
* @param callback {Function} the listener's callback function that executes when the
* event is emitted
* @param context {object} the `this` context to use when executing the `callback`
* @private
*/
_addEventListener: function(target, type, callback, context) {
var listener;
listener = {
target: target,
type: type,
callback: callback,
context: context,
listener: callback.bind(context),
};
target.addEventListener(type, listener.listener);
this._eventListeners.push(listener);
},
/**
* Removes all event listeners that were registered with global objects during the
* lifetime of this Tech. See {@link _addEventListener} for more information about why
* this is necessary.
*
* @private
*/
_removeAllEventListeners: function() {
while (this._eventListeners.length > 0) {
this._removeEventListener(this._eventListeners[0]);
}
this._eventListeners = [];
},
/**
* Removes a single event listener that was registered with global objects during the
* lifetime of this Tech. See {@link _addEventListener} for more information about why
* this is necessary.
*
* @private
*/
_removeEventListener: function(listener) {
var index;
listener.target.removeEventListener(listener.type, listener.listener);
index = _.findIndex(this._eventListeners, function(registeredListener) {
return registeredListener.target === listener.target &&
registeredListener.type === listener.type &&
registeredListener.callback === listener.callback &&
registeredListener.context === listener.context;
});
if (index !== -1) {
this._eventListeners.splice(index, 1);
}
},
/**
* Handles Chromecast player state change events. The player may "change state" when
* paused, played, buffering, etc.
*
* @private
*/
_onPlayerStateChanged: function() {
var states = chrome.cast.media.PlayerState,
playerState = this._remotePlayer.playerState;
if (playerState === states.PLAYING) {
this._hasPlayedCurrentItem = true;
this.trigger('play');
this.trigger('playing');
} else if (playerState === states.PAUSED) {
this.trigger('pause');
} else if ((playerState === states.IDLE && this.ended()) || (playerState === null && this._hasPlayedCurrentItem)) {
this._hasPlayedCurrentItem = false;
this._closeSessionOnTimeout();
this.trigger('ended');
this._triggerTimeUpdateEvent();
} else if (playerState === states.BUFFERING) {
this.trigger('waiting');
}
},
/**
* Ends the session after a certain number of seconds of inactivity.
*
* If the Chromecast player is in the "IDLE" state after an item has ended, and no
* further items are queued up to play, the session is considered inactive. Once a
* period of time (currently 10 seconds) has elapsed with no activity, we manually end
* the session to prevent long periods of a blank Chromecast screen that is shown at
* the end of item playback.
*
* @private
*/
_closeSessionOnTimeout: function() {
// Ensure that there's never more than one session timeout active
this._clearSessionTimeout();
this._sessionTimeoutID = setTimeout(function() {
var castSession = this._getCastSession();
if (castSession) {
castSession.endSession(true);
}
this._clearSessionTimeout();
}.bind(this), SESSION_TIMEOUT);
},
/**
* Stops the timeout that is waiting during a period of inactivity in order to close
* the session.
*
* @private
* @see _closeSessionOnTimeout
*/
_clearSessionTimeout: function() {
if (this._sessionTimeoutID) {
clearTimeout(this._sessionTimeoutID);
this._sessionTimeoutID = false;
}
},
/**
* @private
* @return {object} the current CastContext, if one exists
*/
_getCastContext: function() {
return this._chromecastSessionManager.getCastContext();
},
/**
* @private
* @return {object} the current CastSession, if one exists
*/
_getCastSession: function() {
return this._getCastContext().getCurrentSession();
},
/**
* @private
* @return {object} the current MediaSession, if one exists
* @see https://developers.google.com/cast/docs/reference/chrome/chrome.cast.media.Media
*/
_getMediaSession: function() {
var castSession = this._getCastSession();
return castSession ? castSession.getMediaSession() : null;
},
/**
* Triggers a 'volumechange' event
* @private
* @see http://docs.videojs.com/Player.html#event:volumechange
*/
_triggerVolumeChangeEvent: function() {
this.trigger('volumechange');
},
/**
* Triggers a 'timeupdate' event
* @private
* @see http://docs.videojs.com/Player.html#event:timeupdate
*/
_triggerTimeUpdateEvent: function() {
this.trigger('timeupdate');
},
/**
* Triggers a 'durationchange' event
* @private
* @see http://docs.videojs.com/Player.html#event:durationchange
*/
_triggerDurationChangeEvent: function() {
this.trigger('durationchange');
},
/**
* Triggers an 'error' event
* @private
* @see http://docs.videojs.com/Player.html#event:error
*/
_triggerErrorEvent: function() {
this.trigger('error');
},
};
/**
* Registers the ChromecastTech Tech with Video.js. Calls {@link
* http://docs.videojs.com/Tech.html#.registerTech}, which will add a Tech called
* `chromecast` to the list of globally registered Video.js Tech implementations.
*
* [Video.js Tech](http://docs.videojs.com/Tech.html) are initialized and used
* automatically by Video.js Player instances. Whenever a new source is set on the player,
* the player iterates through the list of available Tech to determine which to use to
* play the source.
*
* @param videojs {object} A reference to
* {@link http://docs.videojs.com/module-videojs.html|Video.js}
* @see http://docs.videojs.com/Tech.html#.registerTech
*/
module.exports = function(videojs) {
var Tech = videojs.getComponent('Tech'),
ChromecastTechImpl;
ChromecastTechImpl = videojs.extend(Tech, ChromecastTech);
// Required for Video.js Tech implementations.
// TODO Consider a more comprehensive check based on mimetype.
ChromecastTechImpl.canPlaySource = ChromecastSessionManager.isChromecastConnected.bind(ChromecastSessionManager);
ChromecastTechImpl.isSupported = ChromecastSessionManager.isChromecastConnected.bind(ChromecastSessionManager);
ChromecastTechImpl.prototype.featuresVolumeControl = true;
ChromecastTechImpl.prototype.featuresPlaybackRate = false;
ChromecastTechImpl.prototype.movingMediaElementInDOM = false;
ChromecastTechImpl.prototype.featuresFullscreenResize = true;
ChromecastTechImpl.prototype.featuresTimeupdateEvents = true;
ChromecastTechImpl.prototype.featuresProgressEvents = false;
// Text tracks are not supported in this version
ChromecastTechImpl.prototype.featuresNativeTextTracks = false;
ChromecastTechImpl.prototype.featuresNativeAudioTracks = false;
ChromecastTechImpl.prototype.featuresNativeVideoTracks = false;
// Give ChromecastTech class instances a reference to videojs
ChromecastTechImpl.prototype.videojs = videojs;
videojs.registerTech('chromecast', ChromecastTechImpl);
};
},{"../chromecast/ChromecastSessionManager":4,"./ChromecastTechUI":11,"underscore":2}],11:[function(require,module,exports){
'use strict';
var Class = require('class.extend'),
ChromecastTechUI;
/**
* This class represents the UI that is shown in the player while the Chromecast Tech is
* active. The UI has a single root DOM element that displays the poster image of the
* current item and title and subtitle. This class receives updates to the poster, title
* and subtitle when the media item that the player is playing changes.
*
* @class ChromecastTechUI
*/
ChromecastTechUI = Class.extend(/** @lends ChromecastTechUI.prototype */ {
init: function() {
this._el = this._createDOMElement();
},
/**
* Creates and returns a single DOMElement that contains the UI. This implementation
* of the Chromecast Tech's UI displays a poster image, a title and a subtitle.
*
* @private
* @returns {DOMElement}
*/
_createDOMElement: function() {
var el = this._createElement('div', 'vjs-tech vjs-tech-chromecast'),
posterContainerEl = this._createElement('div', 'vjs-tech-chromecast-poster'),
posterImageEl = this._createElement('img', 'vjs-tech-chromecast-poster-img'),
titleEl = this._createElement('div', 'vjs-tech-chromecast-title'),
subtitleEl = this._createElement('div', 'vjs-tech-chromecast-subtitle'),
titleContainer = this._createElement('div', 'vjs-tech-chromecast-title-container');
posterContainerEl.appendChild(posterImageEl);
titleContainer.appendChild(titleEl);
titleContainer.appendChild(subtitleEl);
el.appendChild(titleContainer);
el.appendChild(posterContainerEl);
return el;
},
/**
* A helper method for creating DOMElements of the given type and with the given class
* name(s).
*
* @param type {string} the kind of DOMElement to create (ex: 'div')
* @param className {string} the class name(s) to give to the DOMElement. May also be
* a space-delimited list of class names.
* @returns {DOMElement}
*/
_createElement: function(type, className) {
var el = document.createElement(type);
el.className = className;
return el;
},
/**
* Gets the root DOMElement to be shown in the player's UI.
*
* @returns {DOMElement}
*/
getDOMElement: function() {
return this._el;
},
/**
* Finds the poster's DOMElement in the root UI element.
*
* @private
* @returns {DOMElement}
*/
_findPosterEl: function() {
return this._el.querySelector('.vjs-tech-chromecast-poster');
},
/**
* Finds the poster's <img> DOMElement in the root UI element.
*
* @private
* @returns {DOMElement}
*/
_findPosterImageEl: function() {
return this._el.querySelector('.vjs-tech-chromecast-poster-img');
},
/**
* Finds the title's DOMElement in the root UI element.
*
* @private
* @returns {DOMElement}
*/
_findTitleEl: function() {
return this._el.querySelector('.vjs-tech-chromecast-title');
},
/**
* Finds the subtitle's DOMElement in the root UI element.
*
* @private
* @returns {DOMElement}
*/
_findSubtitleEl: function() {
return this._el.querySelector('.vjs-tech-chromecast-subtitle');
},
/**
* Sets the current poster image URL and updates the poster image DOMElement with the
* new poster image URL.
*
* @param poster {string} a URL for a poster image
*/
updatePoster: function(poster) {
var posterImageEl = this._findPosterImageEl();
this._poster = poster ? poster : null;
if (poster) {
posterImageEl.setAttribute('src', poster);
posterImageEl.classList.remove('vjs-tech-chromecast-poster-img-empty');
} else {
posterImageEl.removeAttribute('src');
posterImageEl.classList.add('vjs-tech-chromecast-poster-img-empty');
}
},
/**
* Gets the current poster image URL.
*
* @returns {string} the URL for th current poster image
*/
getPoster: function() {
return this._poster;
},
/**
* Sets the current title and updates the title's DOMElement with the new text.
*
* @param title {string} a title to show
*/
updateTitle: function(title) {
var titleEl = this._findTitleEl();
this._title = title;
if (title) {
titleEl.innerHTML = title;
titleEl.classList.remove('vjs-tech-chromecast-title-empty');
} else {
titleEl.classList.add('vjs-tech-chromecast-title-empty');
}
},
/**
* Sets the current subtitle and updates the subtitle's DOMElement with the new text.
*
* @param subtitle {string} a subtitle to show
*/
updateSubtitle: function(subtitle) {
var subtitleEl = this._findSubtitleEl();
this._subtitle = subtitle;
if (subtitle) {
subtitleEl.innerHTML = subtitle;
subtitleEl.classList.remove('vjs-tech-chromecast-subtitle-empty');
} else {
subtitleEl.classList.add('vjs-tech-chromecast-subtitle-empty');
}
},
});
module.exports = ChromecastTechUI;
},{"class.extend":1}]},{},[9]);