dvbcss-clocks
Version:
Javascript library implementing clock objects for modelling timelines. Useful in DVB CSS implementations.
1,476 lines (1,301 loc) • 104 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["dvbcss-clocks"] = factory();
else
root["dvbcss-clocks"] = factory();
})(this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1);
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
/****************************************************************************
* Copyright 2017 British Broadcasting Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*****************************************************************************/
var ClockBase = __webpack_require__(2);
var DateNowClock = __webpack_require__(6);
var CorrelatedClock = __webpack_require__(8);
var Correlation = __webpack_require__(9);
var OffsetClock = __webpack_require__(10);
/**
* @module dvbcss-clocks
*
* @description
* The dvbcss-clocks library consists of this module containing the clock classes:
*
* <ul>
* <li> dvbcss-clocks.{@link ClockBase} - base class for all clock implementations.
* <li> cdvbcss-locks.{@link DateNowClock} - a root clock based on <tt>Date.now()</tt>
* <li> dvbcss-clocks.{@link CorrelatedClock} - a clock based on a parent using a correlation.
* <li> dvbcss-clocks.{@link Correlation} - a correlation.
* <li> dvbcss-clocks.{@link OffsetClock} - a clock that applies a fixed offset to enable compensating for rendering latency.
* </ul>
*
* <p>Clock can be built into hierarchies, where one clock is the root, and other
* clocks use it as their parent, and others use those as their parents etc.
*
* <p>Clocks raise events, and listen to events from their parents:
* <ul>
* <li> {@link event:change} ... when any change occurs to a clock, or it is affected by a change of its parents.
* <li> {@link event:available} ... when aa clock becomes flagged available
* <li> {@link event:unavailable} ... when aa clock becomes flagged unavailable
* </ul>
*/
module.exports = {
/**
* base class for all clock implementations
* @see ClockBase
*/
ClockBase: ClockBase,
/**
* a root clock based on <tt>Date.now()</tt>
* @see DateNowClock
*/
DateNowClock: DateNowClock,
/**
* a clock based on a parent using a correlation.
* @see CorrelatedClock
*/
CorrelatedClock: CorrelatedClock,
/**
* a correlation.
* @see Correlation
*/
Correlation: Correlation,
/**
* a clock that applies a fixed offset to enable compensating for rendering latency.
* @see OffsetClock
*/
OffsetClock: OffsetClock
};
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
/****************************************************************************
* Copyright 2017 British Broadcasting Corporation
* and contributions Copyright 2017 British Telecommunications (BT) PLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* --------------------------------------------------------------------------
* Summary of parts containing contributions
* by British Telecommunications (BT) PLC:
* CorrelatedClock.prototype.setAtTime
* CorrelatedClock.prototype._rescheduleTimers
*****************************************************************************/
var EventEmitter = __webpack_require__(3);
var inherits = __webpack_require__(4);
var WeakMap = __webpack_require__(5);
var PRIVATE = new WeakMap();
var nextIdNum = 0;
var nextTimeoutHandle = 0;
/**
* There has been a change in the timing of this clock.
* This might be due to a change made directly to this clock, or a change
* made to a parent in the hierarchy that affected this clock.
*
* <p>Causes of changes to clocks include: changes to
* [speed]{@link ClockBase#speed},
* [tick rate]{@link ClockBase#tickRate},
* [correlation]{@link CorrelatedClock#correlation}, or
* [parentage]{@link ClockBase#parent}.
* Changes to availability do not cause this event to fire.
*
* <p>The following parameters are passed as arguments to the event handler:
* @event change
* @param {ClockBase} source The clock that fired the event.
*/
/**
* This clock has become available.
*
* This might be because [availabilityFlag]{@link ClockBase#availabilityFlag}
* became true for this clock, or one of its parents in the hierarchy, causing this
* clock and all its parents to now be flagged as available.
*
* <p>The following parameters are passed as arguments to the event handler:
* @event available
* @param {ClockBase} source The clock that fired the event.
*/
/**
* This clock has become unavailable.
*
* This might be because [availabilityFlag]{@link ClockBase#availabilityFlag}
* became false for this clock, or one of its parents in the hierarchy.
*
* <p>The following parameters are passed as arguments to the event handler:
* @event unavailable
* @param {ClockBase} source The clock that fired the event.
*/
/**
* @module clocks
* @exports ClockBase
* @class ClockBase
*
* @classdesc
* Abstract Base class for clock implementations.
*
* <p>Implementations that can be used are:
* {@link DateNowClock} and
* {@link CorrelatedClock}.
*
* <p>This is the base class on which other clocks are implemented. It provides
* the basic framework of properties, getter and setters for common properties
* such as availability, speed, tick rate and parents, and provides the basic
* events framework, some standard helper methods for time conversion between clocks, comparisons
* between clocks and calculating disperison (error/uncertainty).
*
* <p>Clocks may fire the following events:
* <ul>
* <li> [change]{@link event:change}
* <li> [available]{@link event:available}
* <li> [unavailable]{@link event:unavailable}
* </ul>
*
* <p>Clock implementations should inherit from this class and implement some
* or all of the following method stubs:
* [now()]{@link ClockBase#now}
* [calcWhen()]{@link ClockBase#calcWhen}
* [getTickRate()]{@link ClockBase#getTickRate}
* [setTickRate()]{@link ClockBase#setTickRate}
* [getSpeed()]{@link ClockBase#getSpeed}
* [setSpeed()]{@link ClockBase#setSpeed}
* [getParent()]{@link ClockBase#getParent}
* [setParent()]{@link ClockBase#setParent}
* [toParentTime()]{@link ClockBase#toParentTime}
* [fromParentTime()]{@link ClockBase#fromParentTime}
* [_errorAtTime()]{@link ClockBase#_errorAtTime}
*
* @listens change
* @constructor
* @abstract
*/
var ClockBase = function() {
EventEmitter.call(this);
PRIVATE.set(this, {});
var priv = PRIVATE.get(this);
this._availability = true;
/**
* Every clock instance has a unique ID assigned to it for convenience. This is always of the form "clock_N" where N is a unique number.
* @var {String} id
* @memberof ClockBase
* @instance
*/
this.id = "clock_"+nextIdNum;
nextIdNum = nextIdNum+1;
priv.timerHandles = {};
this.on('change', this._rescheduleTimers.bind(this));
priv.availablePrev = this._availability;
};
inherits(ClockBase, EventEmitter);
/**
* @returns the current time value of this clock in units of ticks of the clock, or NaN if it cannot be determined (e.g. if the clock is missinga parent)
* @abstract
*/
ClockBase.prototype.now = function() {
throw "Unimplemented";
};
/**
* @var {Number} speed The speed at which this clock is running.
* 1.0 = normal. 0.0 = pause. negative values mean it ticks in reverse.
*
* For some implementations this can be changed, as well as read.
*
* <p>The underlying implementation of this property uses the
* [getSpeed]{@link ClockBase#getSpeed} and
* [setSpeed]{@link ClockBase#setSpeed} methods.
* @default 1.0
* @memberof ClockBase
* @instance
* @fires change
*/
Object.defineProperty(ClockBase.prototype, "speed", {
get: function() { return this.getSpeed(); },
set: function(v) { return this.setSpeed(v); },
});
/**
* @var {Number} tickRate The rate of this clock (in ticks per second).
*
* For some implementations this can be changed, as well as read.
*
* <p>The underlying implementation of this property uses the
* [getTickRate]{@link ClockBase#getTickRate} and
* [setTickRate]{@link ClockBase#setTickRate} methods.
*
* @memberof ClockBase
* @instance
* @fires change
*/
Object.defineProperty(ClockBase.prototype, "tickRate", {
get: function() { return this.getTickRate(); },
set: function(v) { return this.setTickRate(v); },
});
/**
* @var {ClockBase} parent The parent of this clock, or <tt>null</tt> if it has no parent.
*
* For some implementations this can be changed, as well as read.
*
* <p>The underlying implementation of this property uses the
* [getParent]{@link ClockBase#getParent} and
* [setParent]{@link ClockBase#setParent} methods.
*
* @memberof ClockBase
* @instance
* @fires change
*/
Object.defineProperty(ClockBase.prototype, "parent", {
get: function() { return this.getParent(); },
set: function(v) { return this.setParent(v); },
});
/**
* @var {Boolean} availabilityFlag The availability flag for this clock.
*
* For some implementations this can be changed, as well as read.
*
* <p>This is only the flag for this clock. Its availability may also be affected
* by the flags on its parents. To determine true availability, call the
* [isAvailable]{@link ClockBase#isAvailable} method.
*
* <p>The underlying implementation of this property uses the
* [getAvailabilityFlag]{@link ClockBase#getAvailabilityFlag} and
* [setAvailabilityFlag]{@link ClockBase#setAvailabilityFlag} methods.
*
* @default true
* @memberof ClockBase
* @instance
* @fires change
* @fires available
* @fires unavailable
*/
Object.defineProperty(ClockBase.prototype, "availabilityFlag", {
get: function() { return this.getAvailabilityFlag(); },
set: function(v) { return this.setAvailabilityFlag(v); },
});
/**
* Returns the current speed of this clock.
* @returns {Number} Speed of this clock.
* @abstract
*/
ClockBase.prototype.getSpeed = function() {
return 1.0;
};
/**
* Sets the current speed of this clock, or throws an exception if this is not possible
* @param {Number} newSpeed The new speed for this clock.
* @abstract
* @fires change
*/
ClockBase.prototype.setSpeed = function(newSpeed) {
throw "Unimplemented";
};
/**
* Calculates the effective speed of this clock, taking into account the effects
* of the speed settings for all of its parents.
* @returns {Number} the effective speed.
*/
ClockBase.prototype.getEffectiveSpeed = function() {
var s = 1.0;
var clock = this;
while (clock !== null) {
s = s * clock.getSpeed();
clock = clock.getParent();
}
return s;
};
/**
* Returns the current tick rate of this clock.
* @returns {Number} Tick rate in ticks/second.
* @abstract
*/
ClockBase.prototype.getTickRate = function() {
throw "Unimplemented";
};
/**
* Sets the current tick rate of this clock, or throws an exception if this is not possible.
* @param {Number} newRate New tick rate in ticks/second.
* @abstract
* @fires change
*/
ClockBase.prototype.setTickRate = function(newRate) {
throw "Unimplemented";
};
/**
* Return the current time of this clock but converted to units of nanoseconds, instead of the normal units of the tick rate.
* @returns {Number} current time of this clock in nanoseconds.
*/
ClockBase.prototype.getNanos = function() {
return this.now() * 1000000000 / this.getTickRate();
};
/**
* Convert a timevalue from nanoseconds to the units of this clock, given its current [tickRate]{@link ClockBase#tickRate}
* @param {Number} time in nanoseconds.
* @returns {Number} the supplied time converted to units of its tick rate.
*/
ClockBase.prototype.fromNanos = function(nanos) {
return nanos * this.getTickRate() / 1000000000;
};
/**
* Is this clock currently available? Given its availability flag and the availability of its parents.
* @returns {Boolean} True if this clock is available, and all its parents are available; otherwise false.
*/
ClockBase.prototype.isAvailable = function() {
var parent = this.getParent();
return this._availability && (!parent || parent.isAvailable());
};
/**
* Sets the availability flag for this clock.
*
* <p>This is only the flag for this clock. Its availability may also be affected
* by the flags on its parents. To determine true availability, call the
* [isAvailable]{@link ClockBase#isAvailable} method.
*
* @param {Boolean} availability The availability flag for this clock
* @fires unavailable
* @fires available
*/
ClockBase.prototype.setAvailabilityFlag = function(availability) {
this._availability = availability;
this.notifyAvailabilityChange();
};
/**
* Cause the "available" or "unavailable" events to fire if availability has
* changed since last time this method was called. Subclasses should call this
* to robustly generate "available" or "unavailable" events instead of trying
* to figure out if there has been a change for themselves.
* @fires unavailable
* @fires available
*/
ClockBase.prototype.notifyAvailabilityChange = function() {
var priv = PRIVATE.get(this);
var availableNow = this.isAvailable();
if (Boolean(availableNow) != Boolean(priv.availablePrev)) {
priv.availablePrev = availableNow;
this.emit(availableNow?"available":"unavailable", this);
}
};
/**
* Returns the availability flag for this clock (without taking into account whether its parents are available).
*
* <p>This is only the flag for this clock. Its availability may also be affected
* by the flags on its parents. To determine true availability, call the
* [isAvailable]{@link ClockBase#isAvailable} method.
*
* @returns {Boolean} The availability flag of this clock
*/
ClockBase.prototype.getAvailabilityFlag = function() {
return this._availability;
};
/**
* Convert a time value for this clock into a time value corresponding to teh underlying system time source being used by the root clock.
*
* <p>For example: if this clock is part of a hierarchy, where the root clock of the hierarchy is a [DateNowClock]{@link DateNowClock} then
* this method converts the supplied time to be in the same units as <tt>Date.now()</tt>.
*
* @param {Number} ticksWhen Time value of this clock.
* @return {Number} The corresponding time value in the units of the underlying system clock that is being used by the root clock, or <tt>NaN</tt> if this conversion is not possible.
* @abstract
*/
ClockBase.prototype.calcWhen = function(ticksWhen) {
throw "Unimplemented";
};
/**
* Return the root clock for the hierarchy that this clock is part of.
*
* <p>If this clock is the root clock (it has no parent), then it will return itself.
*
* @return {ClockBase} The root clock of the hierarchy
*/
ClockBase.prototype.getRoot = function() {
var p = this;
var p2 = p.getParent();
while (p2) {
p=p2;
p2=p.getParent();
}
return p;
};
/**
* Convert a time for the root clock to a time for this clock.
* @param {Number} t A time value of the root clock.
* @returns {Number} The corresponding time value for this clock.
*/
ClockBase.prototype.fromRootTime = function(t) {
var p = this.getParent();
if (!p) {
return t;
} else {
var x = p.fromRootTime(t);
return this.fromParentTime(x);
}
};
/**
* Convert a time for this clock to a time for the root clock.
* @param {Number} t A time value for this clock.
* @returns {Number} The corresponding time value of the root clock, or <tt>NaN</tt> if this is not possible.
*/
ClockBase.prototype.toRootTime = function(t) {
var p = this.getParent();
if (!p) {
return t;
} else {
var x = this.toParentTime(t);
return p.toRootTime(x);
}
};
/**
* Convert a time value for this clock to a time value for any other clock in the same hierarchy as this one.
* @param {ClockBase} otherClock The clock to convert the value value to.
* @param {Number} t Time value of this clock.
* @returns {Number} The corresponding time value for the specified <tt>otherClock</tt>, or <tt>NaN</tt> if this is not possible.
* @throws if this clock is not part of the same hierarchy as the other clock.
*/
ClockBase.prototype.toOtherClockTime = function(otherClock, t) {
var selfAncestry = this.getAncestry();
var otherAncestry = otherClock.getAncestry();
var clock;
var common = false;
while (selfAncestry.length && otherAncestry.length && selfAncestry[selfAncestry.length-1] === otherAncestry[otherAncestry.length-1]) {
selfAncestry.pop();
otherAncestry.pop();
common=true;
}
if (!common) {
throw "No common ancestor clock.";
}
selfAncestry.forEach(function(clock) {
t = clock.toParentTime(t);
});
otherAncestry.reverse();
otherAncestry.forEach(function(clock) {
t = clock.fromParentTime(t);
});
return t;
};
/**
* Get an array of the clocks that are the parents and ancestors of this clock.
* @returns {ClockBase[]} an array starting with this clock and ending with the root clock.
*/
ClockBase.prototype.getAncestry = function() {
var ancestry = [this];
var c = this;
while (c) {
var p = c.getParent();
if (p) {
ancestry.push(p);
}
c=p;
}
return ancestry;
};
/**
* Convert time value of this clock to the equivalent time of its parent.
*
* @param {Number} t Time value of this clock
* @returns {Number} corresponding time of the parent clock, or <tt>NaN</tt> if this is not possible.
* @abstract
*/
ClockBase.prototype.toParentTime = function(t) {
throw "Unimplemented";
};
/**
* Convert time value of this clock's parent to the equivalent time of this clock.
* @param {Number} t Time value of this clock's parent
* @returns {Number} corresponding time of this clock.
* @abstract
*/
ClockBase.prototype.fromParentTime = function(t) {
throw "Unimplemented";
};
/**
* Returns the parent of this clock, or <tt>null</tt> if it has no parent.
* @returns {ClockBase} parent clock, or <tt>null</tt>
* @abstract
*/
ClockBase.prototype.getParent = function() {
throw "Unimplemented";
};
/**
* Set/change the parent of this clock.
* @param {ClockBase} parent clock, or <tt>null</tt>
* @throws if it is not allowed to set this clock's parent.
* @abstract
* @fires change
*/
ClockBase.prototype.setParent = function(newParent) {
throw "Unimplemented";
};
/**
* Calculate the potential for difference between this clock and another clock.
* @param {ClockBase} otherClock The clock to compare with.
* @returns {Number} The potential difference in units of seconds. If effective speeds or tick rates differ, this will always be <tt>Number.POSITIVE_INFINITY</tt>
*
* If the clocks differ in effective speed or tick rate, even slightly, then
* this means that the clocks will eventually diverge to infinity, and so the
* returned difference will equal +infinity.
*
* If the clocks do not differ in effective speed or tick rate, then there will
* be a constant time difference between them. This is what is returned.
*/
ClockBase.prototype.clockDiff = function(otherClock) {
var thisSpeed = this.getEffectiveSpeed();
var otherSpeed = otherClock.getEffectiveSpeed();
if (thisSpeed !== otherSpeed) {
return Number.POSITIVE_INFINITY;
} else if (this.getTickRate() !== otherClock.getTickRate()) {
return Number.POSITIVE_INFINITY;
} else {
var root = this.getRoot();
var t = root.now();
var t1 = this.fromRootTime(t);
var t2 = otherClock.fromRootTime(t);
return Math.abs(t1-t2) / this.getTickRate();
}
};
/**
* Calculates the dispersion (maximum error bounds) at the specified clock time.
* This takes into account the contribution to error of this clock and its ancestors.
* @param {Number} t The time position of this clock for which the dispersion is to be calculated.
* @returns {Number} Dispersion (in seconds) at the specified clock time.
*/
ClockBase.prototype.dispersionAtTime = function(t) {
var disp = this._errorAtTime(t);
var p = this.getParent();
if (p) {
var pt = this.toParentTime(t);
disp += p.dispersionAtTime(pt);
}
return disp;
};
/**
* Calculates the error/uncertainty contribution of this clock at a given time position.
*
* <p>It is not intended that this function is called directly. Instead, call
* [dispersionAtTime()]{@link ClockBase.dispersionAtTime} which uses this function
* as part of calculating the total dispersion.
*
* @param {Number} t A time position of this clock
* @returns {Number} the potential for error (in seconds) arising from this clock
* at a given time of this clock. Does not include the contribution of
* any parent clocks.
*
* @abstract
*/
ClockBase.prototype._errorAtTime = function(t) {
throw "Unimplemented";
};
/**
* Retrieve the maximium frequency error (in ppm) of the root clock in the hierarchy.
*
* <p>This method contains an implementation for non-root clocks only. It must
* be overriden for root clock implementations.
*
* @returns {Number} The maximum frequency error of the root clock (in parts per million)
* @abstract
*/
ClockBase.prototype.getRootMaxFreqError = function() {
var root = this.getRoot();
if (root === this) {
throw "Unimplemented";
} else {
return root.getRootMaxFreqError();
}
};
/**
* A callback that is called when using [setTimeout]{@link ClockBase#setTimeout} or [setAtTime][@link ClockBase#setAtTime].
*
* @callback setTimeoutCallback
* @param {...*} args The parameters that were passed when the callback was scheduled.
* @this ClockBase
*/
/**
* Request a timeout callback when the time of this clock passes the current time plus
* the number of specified ticks.
*
* <p>If there are changes to timing caused by changes to this clock or its parents, then this timer will be automatically
* rescheduled to compensate.
*
* @param {setTimeoutCallback} func The function to callback
* @param {Number} ticks The callback is triggered when the clock passes (reaches or jumps past) this number of ticks beyond the current time.
* @param {...*} args Other arguments are passed to the callback
* @returns A handle for the timer. Pass this handle to [clearTimeout]{@link ClockBase#clearTimeout} to cancel this timer callback.
*/
ClockBase.prototype.setTimeout = function(func, ticks) {
arguments[1] = arguments[1] + this.now();
return this.setAtTime.apply(this, arguments);
};
/**
* Request a timeout callback when the time of this clock passes the specified time.
*
* <p>If there are changes to timing caused by changes to this clock or its parents, then this timer will be automatically
* rescheduled to compensate.
*
* @param {setTimeoutCallBack} func The function to callback
* @param {Number} when The callback is triggered when the clock passes (reaches or jumps past) this time.
* @param {...*} args Other arguments are passed to the callback
* @returns A handle for the timer. Pass this handle to [clearTimeout]{@link ClockBase#clearTimeout} to cancel this timer callback.
*/
ClockBase.prototype.setAtTime = function(func, when) {
var priv = PRIVATE.get(this);
var self = this;
var handle = self.id + ":timeout-" + nextTimeoutHandle++;
var root = self.getRoot();
if (root === null) {
root = self;
}
// remove first two args
var args = new Array(arguments.length-2);
for(var i=2; i<arguments.length; i++) {
args[i-2] = arguments[i];
}
var callback = function() {
delete priv.timerHandles[handle];
func.apply(self, args);
}
;
var numRootTicks = self.toRootTime(when) - root.now();
if (numRootTicks !== 0) {
numRootTicks = root.getSpeed() !== 0 ? numRootTicks / root.getSpeed() : NaN;
}
var millis = numRootTicks * (1000 / root.getTickRate());
var realHandle;
if (!isNaN(millis)) {
realHandle = setTimeout(callback, millis);
}
priv.timerHandles[handle] = { realHandle:realHandle, when:when, callback:callback };
return handle;
};
ClockBase.prototype._rescheduleTimers = function() {
// clock timing has changed, we need to re-schedule all timers
var priv = PRIVATE.get(this);
var root = this.getRoot();
for(var handle in priv.timerHandles) {
if (priv.timerHandles.hasOwnProperty(handle)) {
var d = priv.timerHandles[handle];
// clear existing timer
if (d.realHandle !== null && d.realHandle !== undefined) {
clearTimeout(d.realHandle);
}
// re-calculate when this timer is due and re-schedule
var numRootTicks = this.toRootTime(d.when) - root.now();
if (numRootTicks !== 0) {
numRootTicks = root.getSpeed() !== 0 ? numRootTicks / root.getSpeed() : NaN;
}
var millis = numRootTicks * (1000 / root.getTickRate());
if (!isNaN(millis)) {
d.realHandle = setTimeout(d.callback, Math.max(0,millis));
} else {
delete d.realHandle;
}
}
}
};
/**
* Clear (cancel) a timer that was scheduled using [setTimeout]{@link ClockBase#setTimeout} or [setAtTime][@link ClockBase#setAtTime].
*
* @param handle - The handle for the previously scheduled callback.
*
* If the handle does not represent a callback that was scheduled against this clock, then this method returns without doing anything.
*/
ClockBase.prototype.clearTimeout = function(handle) {
var priv = PRIVATE.get(this);
var d = priv.timerHandles[handle];
if (d !== undefined) {
clearTimeout(d.realHandle);
delete priv.timerHandles[handle];
}
};
module.exports = ClockBase;
/***/ }),
/* 3 */
/***/ (function(module, exports) {
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
function EventEmitter() {
this._events = this._events || {};
this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
if (!isNumber(n) || n < 0 || isNaN(n))
throw TypeError('n must be a positive number');
this._maxListeners = n;
return this;
};
EventEmitter.prototype.emit = function(type) {
var er, handler, len, args, i, listeners;
if (!this._events)
this._events = {};
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._events.error ||
(isObject(this._events.error) && !this._events.error.length)) {
er = arguments[1];
if (er instanceof Error) {
throw er; // Unhandled 'error' event
} else {
// At least give some kind of context to the user
var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
err.context = er;
throw err;
}
}
}
handler = this._events[type];
if (isUndefined(handler))
return false;
if (isFunction(handler)) {
switch (arguments.length) {
// fast cases
case 1:
handler.call(this);
break;
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
args = Array.prototype.slice.call(arguments, 1);
handler.apply(this, args);
}
} else if (isObject(handler)) {
args = Array.prototype.slice.call(arguments, 1);
listeners = handler.slice();
len = listeners.length;
for (i = 0; i < len; i++)
listeners[i].apply(this, args);
}
return true;
};
EventEmitter.prototype.addListener = function(type, listener) {
var m;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events)
this._events = {};
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (this._events.newListener)
this.emit('newListener', type,
isFunction(listener.listener) ?
listener.listener : listener);
if (!this._events[type])
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
else if (isObject(this._events[type]))
// If we've already got an array, just append.
this._events[type].push(listener);
else
// Adding the second element, need to change to array.
this._events[type] = [this._events[type], listener];
// Check for listener leak
if (isObject(this._events[type]) && !this._events[type].warned) {
if (!isUndefined(this._maxListeners)) {
m = this._maxListeners;
} else {
m = EventEmitter.defaultMaxListeners;
}
if (m && m > 0 && this._events[type].length > m) {
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
this._events[type].length);
if (typeof console.trace === 'function') {
// not supported in IE 10
console.trace();
}
}
}
return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.once = function(type, listener) {
if (!isFunction(listener))
throw TypeError('listener must be a function');
var fired = false;
function g() {
this.removeListener(type, g);
if (!fired) {
fired = true;
listener.apply(this, arguments);
}
}
g.listener = listener;
this.on(type, g);
return this;
};
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
var list, position, length, i;
if (!isFunction(listener))
throw TypeError('listener must be a function');
if (!this._events || !this._events[type])
return this;
list = this._events[type];
length = list.length;
position = -1;
if (list === listener ||
(isFunction(list.listener) && list.listener === listener)) {
delete this._events[type];
if (this._events.removeListener)
this.emit('removeListener', type, listener);
} else if (isObject(list)) {
for (i = length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
break;
}
}
if (position < 0)
return this;
if (list.length === 1) {
list.length = 0;
delete this._events[type];
} else {
list.splice(position, 1);
}
if (this._events.removeListener)
this.emit('removeListener', type, listener);
}
return this;
};
EventEmitter.prototype.removeAllListeners = function(type) {
var key, listeners;
if (!this._events)
return this;
// not listening for removeListener, no need to emit
if (!this._events.removeListener) {
if (arguments.length === 0)
this._events = {};
else if (this._events[type])
delete this._events[type];
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
for (key in this._events) {
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = {};
return this;
}
listeners = this._events[type];
if (isFunction(listeners)) {
this.removeListener(type, listeners);
} else if (listeners) {
// LIFO order
while (listeners.length)
this.removeListener(type, listeners[listeners.length - 1]);
}
delete this._events[type];
return this;
};
EventEmitter.prototype.listeners = function(type) {
var ret;
if (!this._events || !this._events[type])
ret = [];
else if (isFunction(this._events[type]))
ret = [this._events[type]];
else
ret = this._events[type].slice();
return ret;
};
EventEmitter.prototype.listenerCount = function(type) {
if (this._events) {
var evlistener = this._events[type];
if (isFunction(evlistener))
return 1;
else if (evlistener)
return evlistener.length;
}
return 0;
};
EventEmitter.listenerCount = function(emitter, type) {
return emitter.listenerCount(type);
};
function isFunction(arg) {
return typeof arg === 'function';
}
function isNumber(arg) {
return typeof arg === 'number';
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
function isUndefined(arg) {
return arg === void 0;
}
/***/ }),
/* 4 */
/***/ (function(module, exports) {
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
/***/ }),
/* 5 */
/***/ (function(module, exports) {
// Copyright (C) 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Install a leaky WeakMap emulation on platforms that
* don't provide a built-in one.
*
* <p>Assumes that an ES5 platform where, if {@code WeakMap} is
* already present, then it conforms to the anticipated ES6
* specification. To run this file on an ES5 or almost ES5
* implementation where the {@code WeakMap} specification does not
* quite conform, run <code>repairES5.js</code> first.
*
* <p>Even though WeakMapModule is not global, the linter thinks it
* is, which is why it is in the overrides list below.
*
* <p>NOTE: Before using this WeakMap emulation in a non-SES
* environment, see the note below about hiddenRecord.
*
* @author Mark S. Miller
* @requires crypto, ArrayBuffer, Uint8Array, navigator, console
* @overrides WeakMap, ses, Proxy
* @overrides WeakMapModule
*/
/**
* This {@code WeakMap} emulation is observably equivalent to the
* ES-Harmony WeakMap, but with leakier garbage collection properties.
*
* <p>As with true WeakMaps, in this emulation, a key does not
* retain maps indexed by that key and (crucially) a map does not
* retain the keys it indexes. A map by itself also does not retain
* the values associated with that map.
*
* <p>However, the values associated with a key in some map are
* retained so long as that key is retained and those associations are
* not overridden. For example, when used to support membranes, all
* values exported from a given membrane will live for the lifetime
* they would have had in the absence of an interposed membrane. Even
* when the membrane is revoked, all objects that would have been
* reachable in the absence of revocation will still be reachable, as
* far as the GC can tell, even though they will no longer be relevant
* to ongoing computation.
*
* <p>The API implemented here is approximately the API as implemented
* in FF6.0a1 and agreed to by MarkM, Andreas Gal, and Dave Herman,
* rather than the offially approved proposal page. TODO(erights):
* upgrade the ecmascript WeakMap proposal page to explain this API
* change and present to EcmaScript committee for their approval.
*
* <p>The first difference between the emulation here and that in
* FF6.0a1 is the presence of non enumerable {@code get___, has___,
* set___, and delete___} methods on WeakMap instances to represent
* what would be the hidden internal properties of a primitive
* implementation. Whereas the FF6.0a1 WeakMap.prototype methods
* require their {@code this} to be a genuine WeakMap instance (i.e.,
* an object of {@code [[Class]]} "WeakMap}), since there is nothing
* unforgeable about the pseudo-internal method names used here,
* nothing prevents these emulated prototype methods from being
* applied to non-WeakMaps with pseudo-internal methods of the same
* names.
*
* <p>Another difference is that our emulated {@code
* WeakMap.prototype} is not itself a WeakMap. A problem with the
* current FF6.0a1 API is that WeakMap.prototype is itself a WeakMap
* providing ambient mutability and an ambient communications
* channel. Thus, if a WeakMap is already present and has this
* problem, repairES5.js wraps it in a safe wrappper in order to
* prevent access to this channel. (See
* PATCH_MUTABLE_FROZEN_WEAKMAP_PROTO in repairES5.js).
*/
/**
* If this is a full <a href=
* "http://code.google.com/p/es-lab/wiki/SecureableES5"
* >secureable ES5</a> platform and the ES-Harmony {@code WeakMap} is
* absent, install an approximate emulation.
*
* <p>If WeakMap is present but cannot store some objects, use our approximate
* emulation as a wrapper.
*
* <p>If this is almost a secureable ES5 platform, then WeakMap.js
* should be run after repairES5.js.
*
* <p>See {@code WeakMap} for documentation of the garbage collection
* properties of this WeakMap emulation.
*/
(function WeakMapModule() {
"use strict";
if (typeof ses !== 'undefined' && ses.ok && !ses.ok()) {
// already too broken, so give up
return;
}
/**
* In some cases (current Firefox), we must make a choice betweeen a
* WeakMap which is capable of using all varieties of host objects as
* keys and one which is capable of safely using proxies as keys. See
* comments below about HostWeakMap and DoubleWeakMap for details.
*
* This function (which is a global, not exposed to guests) marks a
* WeakMap as permitted to do what is necessary to index all host
* objects, at the cost of making it unsafe for proxies.
*
* Do not apply this function to anything which is not a genuine
* fresh WeakMap.
*/
function weakMapPermitHostObjects(map) {
// identity of function used as a secret -- good enough and cheap
if (map.permitHostObjects___) {
map.permitHostObjects___(weakMapPermitHostObjects);
}
}
if (typeof ses !== 'undefined') {
ses.weakMapPermitHostObjects = weakMapPermitHostObjects;
}
// IE 11 has no Proxy but has a broken WeakMap such that we need to patch
// it using DoubleWeakMap; this flag tells DoubleWeakMap so.
var doubleWeakMapCheckSilentFailure = false;
// Check if there is already a good-enough WeakMap implementation, and if so
// exit without replacing it.
if (typeof WeakMap === 'function') {
var HostWeakMap = WeakMap;
// There is a WeakMap -- is it good enough?
if (typeof navigator !== 'undefined' &&
/Firefox/.test(navigator.userAgent)) {
// We're now *assuming not*, because as of this writing (2013-05-06)
// Firefox's WeakMaps have a miscellany of objects they won't accept, and
// we don't want to make an exhaustive list, and testing for just one
// will be a problem if that one is fixed alone (as they did for Event).
// If there is a platform that we *can* reliably test on, here's how to
// do it:
// var problematic = ... ;
// var testHostMap = new HostWeakMap();
// try {
// testHostMap.set(problematic, 1); // Firefox 20 will throw here
// if (testHostMap.get(problematic) === 1) {
// return;
// }
// } catch (e) {}
} else {
// IE 11 bug: WeakMaps silently fail to store frozen objects.
var testMap = new HostWeakMap();
var testObject = Object.freeze({});
testMap.set(testObject, 1);
if (testMap.get(testObject) !== 1) {
doubleWeakMapCheckSilentFailure = true;
// Fall through to installing our WeakMap.
} else {
module.exports = WeakMap;
return;
}
}
}
var hop = Object.prototype.hasOwnProperty;
var gopn = Object.getOwnPropertyNames;
var defProp = Object.defineProperty;
var isExtensible = Object.isExtensible;
/**
* Security depends on HIDDEN_NAME being both <i>unguessable</i> and
* <i>undiscoverable</i> by untrusted code.
*
* <p>Given the known weaknesses of Math.random() on existing
* browsers, it does not generate unguessability we can be confident
* of.
*
* <p>It is the monkey patching logic in this file that is intended
* to ensure undiscoverability. The basic idea is that there are
* three fundamental means of discovering properties of an object:
* The for/in loop, Object.keys(), and Object.getOwnPropertyNames(),
* as well as some proposed ES6 extensions that appear on our
* whitelist. The first two only discover enumerable properties, and
* we only use HIDDEN_NAME to name a non-enumerable property, so the
* only remaining threat should be getOwnPropertyNames and some
* proposed ES6 extensions that appear on our whitelist. We monkey
* patch them to remove HIDDEN_NAME from the list of properties they
* returns.
*
* <p>TODO(erights): On a platform with built-in Proxies, proxies
* could be used to trap and thereby discover the HIDDEN_NAME, so we
* need to monkey patch Proxy.create, Proxy.createFunction, etc, in
* order to wrap the provided handler with the real handler which
* filters out all traps using HIDDEN_NAME.
*
* <p>TODO(erights): Revisit Mike Stay's suggestion that we use an
* encapsulated function at a not-necessarily-secret name, which
* uses the Stiegler shared-state rights amplification pattern to
* reveal the associated value only to the WeakMap in which this key
* is associated with that value. Since only the key retains the
* function, the function can also remember the key without causing
* leakage of the key, so this doesn't violate our general gc
* goals. In addition, because the name need not be a guarded
* secret, we could efficiently handle cross-frame frozen keys.
*/
var HIDDEN_NAME_PREFIX = 'weakmap:';
var HIDDEN_NAME = HIDDEN_NAME_PREFIX + 'ident:' + Math.random() + '___';
if (typeof crypto !== 'undefined' &&
typeof crypto.getRandomValues === 'function' &&
typeof ArrayBuffer === 'function' &&
typeof Uint8Array === 'function') {
var ab = new ArrayBuffer(25);
var u8s = new Uint8Array(ab);
crypto.getRandomValues(u8s);
HIDDEN_NAME = HIDDEN_NAME_PREFIX + 'rand:' +
Array.prototype.map.call(u8s, function(u8) {
return (u8 % 36).toString(36);
}).join('') + '___';
}
function isNotHiddenName(name) {
return !(
name.substr(0, HIDDEN_NAME_PREFIX.length) == HIDDEN_NAME_PREFIX &&
name.substr(name.length - 3) === '___');
}
/**
* Monkey patch getOwnPropertyNames to avoid revealing the
* HIDDEN_NAME.
*
* <p>The ES5.1 spec requires each name to appear only once, but as
* of this writing, this requirement is controversial for ES6, so we
* made this code robust against this case. If the resulting extra
* search turns out to be expensive, we can probably relax this once
* ES6 is adequately supported on all major browsers, iff no browser
* versions we support at that time have relaxed this constraint
* without providing built-in ES6 WeakMaps.
*/
defProp(Object, 'getOwnPropertyNames', {
value: function fakeGetOwnPropertyNames(obj) {
return gopn(obj).filter(isNotHiddenName);
}
});
/**
* getPropertyNames is not in ES5 but it is proposed for ES6 and
* does appear in our whitelist, so we need to clean it too.
*/
if ('getPropertyNames' in Object) {
var originalGetPropertyNames = Object.getPropertyNames;
defProp(Object, 'getPropertyNames', {
value: function fakeGetPropertyNames(obj) {
return originalGetPropertyNames(obj).filter(isNotHiddenName);
}
});
}
/**
* <p>To treat objects as identity-keys with reasonable efficiency
* on ES5 by itself (i.e., without any object-keyed collections), we
* need to add a hidden property to such key objects when we
* c