webappengine
Version:
A web application platform that can host multiple web apps running with Node.js.
1,243 lines (1,086 loc) • 45.3 kB
JavaScript
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
;(function (factory) {
var objectTypes = {
'function': true,
'object': true
};
var
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
freeSelf = objectTypes[typeof self] && self.Object && self,
freeWindow = objectTypes[typeof window] && window && window.Object && window,
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global;
var root = root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this;
// Because of build optimizers
if (typeof define === 'function' && define.amd) {
define(['rx.core'], function (Rx, exports) {
return factory(root, exports, Rx);
});
} else if (typeof module === 'object' && module && module.exports === freeExports) {
module.exports = factory(root, module.exports, require('./rx.core'));
} else {
root.Rx = factory(root, {}, root.Rx);
}
}.call(this, function (root, exp, Rx, undefined) {
// Defaults
var Observer = Rx.Observer,
Observable = Rx.Observable,
Disposable = Rx.Disposable,
disposableEmpty = Disposable.empty,
disposableCreate = Disposable.create,
CompositeDisposable = Rx.CompositeDisposable,
SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
Scheduler = Rx.Scheduler,
ScheduledItem = Rx.internals.ScheduledItem,
SchedulePeriodicRecursive = Rx.internals.SchedulePeriodicRecursive,
inherits = Rx.internals.inherits,
notImplemented = Rx.helpers.notImplemented,
defaultComparer = Rx.helpers.defaultComparer = function (a, b) { return isEqual(a, b); };
/**
* Represents a notification to an observer.
*/
var Notification = Rx.Notification = (function () {
function Notification(kind, value, exception, accept, acceptObservable, toString) {
this.kind = kind;
this.value = value;
this.exception = exception;
this._accept = accept;
this._acceptObservable = acceptObservable;
this.toString = toString;
}
/**
* Invokes the delegate corresponding to the notification or the observer's method corresponding to the notification and returns the produced result.
*
* @memberOf Notification
* @param {Any} observerOrOnNext Delegate to invoke for an OnNext notification or Observer to invoke the notification on..
* @param {Function} onError Delegate to invoke for an OnError notification.
* @param {Function} onCompleted Delegate to invoke for an OnCompleted notification.
* @returns {Any} Result produced by the observation.
*/
Notification.prototype.accept = function (observerOrOnNext, onError, onCompleted) {
return observerOrOnNext && typeof observerOrOnNext === 'object' ?
this._acceptObservable(observerOrOnNext) :
this._accept(observerOrOnNext, onError, onCompleted);
};
/**
* Returns an observable sequence with a single notification.
*
* @memberOf Notifications
* @param {Scheduler} [scheduler] Scheduler to send out the notification calls on.
* @returns {Observable} The observable sequence that surfaces the behavior of the notification upon subscription.
*/
Notification.prototype.toObservable = function (scheduler) {
var self = this;
isScheduler(scheduler) || (scheduler = immediateScheduler);
return new AnonymousObservable(function (observer) {
return scheduler.scheduleWithState(self, function (_, notification) {
notification._acceptObservable(observer);
notification.kind === 'N' && observer.onCompleted();
});
});
};
return Notification;
})();
/**
* Creates an object that represents an OnNext notification to an observer.
* @param {Any} value The value contained in the notification.
* @returns {Notification} The OnNext notification containing the value.
*/
var notificationCreateOnNext = Notification.createOnNext = (function () {
function _accept(onNext) { return onNext(this.value); }
function _acceptObservable(observer) { return observer.onNext(this.value); }
function toString() { return 'OnNext(' + this.value + ')'; }
return function (value) {
return new Notification('N', value, null, _accept, _acceptObservable, toString);
};
}());
/**
* Creates an object that represents an OnError notification to an observer.
* @param {Any} error The exception contained in the notification.
* @returns {Notification} The OnError notification containing the exception.
*/
var notificationCreateOnError = Notification.createOnError = (function () {
function _accept (onNext, onError) { return onError(this.exception); }
function _acceptObservable(observer) { return observer.onError(this.exception); }
function toString () { return 'OnError(' + this.exception + ')'; }
return function (e) {
return new Notification('E', null, e, _accept, _acceptObservable, toString);
};
}());
/**
* Creates an object that represents an OnCompleted notification to an observer.
* @returns {Notification} The OnCompleted notification.
*/
var notificationCreateOnCompleted = Notification.createOnCompleted = (function () {
function _accept (onNext, onError, onCompleted) { return onCompleted(); }
function _acceptObservable(observer) { return observer.onCompleted(); }
function toString () { return 'OnCompleted()'; }
return function () {
return new Notification('C', null, null, _accept, _acceptObservable, toString);
};
}());
/** Used to determine if values are of the language type Object */
var dontEnums = ['toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'],
dontEnumsLength = dontEnums.length;
/** `Object#toString` result shortcuts */
var argsClass = '[object Arguments]',
arrayClass = '[object Array]',
boolClass = '[object Boolean]',
dateClass = '[object Date]',
errorClass = '[object Error]',
funcClass = '[object Function]',
numberClass = '[object Number]',
objectClass = '[object Object]',
regexpClass = '[object RegExp]',
stringClass = '[object String]';
var toString = Object.prototype.toString,
hasOwnProperty = Object.prototype.hasOwnProperty,
supportsArgsClass = toString.call(arguments) == argsClass, // For less <IE9 && FF<4
supportNodeClass,
errorProto = Error.prototype,
objectProto = Object.prototype,
stringProto = String.prototype,
propertyIsEnumerable = objectProto.propertyIsEnumerable;
try {
supportNodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + ''));
} catch (e) {
supportNodeClass = true;
}
var nonEnumProps = {};
nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true };
nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true };
nonEnumProps[objectClass] = { 'constructor': true };
var support = {};
(function () {
var ctor = function() { this.x = 1; },
props = [];
ctor.prototype = { 'valueOf': 1, 'y': 1 };
for (var key in new ctor) { props.push(key); }
for (key in arguments) { }
// Detect if `name` or `message` properties of `Error.prototype` are enumerable by default.
support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name');
// Detect if `prototype` properties are enumerable by default.
support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype');
// Detect if `arguments` object indexes are non-enumerable
support.nonEnumArgs = key != 0;
// Detect if properties shadowing those on `Object.prototype` are non-enumerable.
support.nonEnumShadows = !/valueOf/.test(props);
}(1));
var isObject = Rx.internals.isObject = function(value) {
var type = typeof value;
return value && (type == 'function' || type == 'object') || false;
};
function keysIn(object) {
var result = [];
if (!isObject(object)) {
return result;
}
if (support.nonEnumArgs && object.length && isArguments(object)) {
object = slice.call(object);
}
var skipProto = support.enumPrototypes && typeof object == 'function',
skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error);
for (var key in object) {
if (!(skipProto && key == 'prototype') &&
!(skipErrorProps && (key == 'message' || key == 'name'))) {
result.push(key);
}
}
if (support.nonEnumShadows && object !== objectProto) {
var ctor = object.constructor,
index = -1,
length = dontEnumsLength;
if (object === (ctor && ctor.prototype)) {
var className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object),
nonEnum = nonEnumProps[className];
}
while (++index < length) {
key = dontEnums[index];
if (!(nonEnum && nonEnum[key]) && hasOwnProperty.call(object, key)) {
result.push(key);
}
}
}
return result;
}
function internalFor(object, callback, keysFunc) {
var index = -1,
props = keysFunc(object),
length = props.length;
while (++index < length) {
var key = props[index];
if (callback(object[key], key, object) === false) {
break;
}
}
return object;
}
function internalForIn(object, callback) {
return internalFor(object, callback, keysIn);
}
function isNode(value) {
// IE < 9 presents DOM nodes as `Object` objects except they have `toString`
// methods that are `typeof` "string" and still can coerce nodes to strings
return typeof value.toString != 'function' && typeof (value + '') == 'string';
}
var isArguments = function(value) {
return (value && typeof value == 'object') ? toString.call(value) == argsClass : false;
}
// fallback for browsers that can't detect `arguments` objects by [[Class]]
if (!supportsArgsClass) {
isArguments = function(value) {
return (value && typeof value == 'object') ? hasOwnProperty.call(value, 'callee') : false;
};
}
var isEqual = Rx.internals.isEqual = function (x, y) {
return deepEquals(x, y, [], []);
};
/** @private
* Used for deep comparison
**/
function deepEquals(a, b, stackA, stackB) {
// exit early for identical values
if (a === b) {
// treat `+0` vs. `-0` as not equal
return a !== 0 || (1 / a == 1 / b);
}
var type = typeof a,
otherType = typeof b;
// exit early for unlike primitive values
if (a === a && (a == null || b == null ||
(type != 'function' && type != 'object' && otherType != 'function' && otherType != 'object'))) {
return false;
}
// compare [[Class]] names
var className = toString.call(a),
otherClass = toString.call(b);
if (className == argsClass) {
className = objectClass;
}
if (otherClass == argsClass) {
otherClass = objectClass;
}
if (className != otherClass) {
return false;
}
switch (className) {
case boolClass:
case dateClass:
// coerce dates and booleans to numbers, dates to milliseconds and booleans
// to `1` or `0` treating invalid dates coerced to `NaN` as not equal
return +a == +b;
case numberClass:
// treat `NaN` vs. `NaN` as equal
return (a != +a) ?
b != +b :
// but treat `-0` vs. `+0` as not equal
(a == 0 ? (1 / a == 1 / b) : a == +b);
case regexpClass:
case stringClass:
// coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
// treat string primitives and their corresponding object instances as equal
return a == String(b);
}
var isArr = className == arrayClass;
if (!isArr) {
// exit for functions and DOM nodes
if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) {
return false;
}
// in older versions of Opera, `arguments` objects have `Array` constructors
var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor,
ctorB = !support.argsObject && isArguments(b) ? Object : b.constructor;
// non `Object` object instances with different constructors are not equal
if (ctorA != ctorB &&
!(hasOwnProperty.call(a, 'constructor') && hasOwnProperty.call(b, 'constructor')) &&
!(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
('constructor' in a && 'constructor' in b)
) {
return false;
}
}
// assume cyclic structures are equal
// the algorithm for detecting cyclic structures is adapted from ES 5.1
// section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
var initedStack = !stackA;
stackA || (stackA = []);
stackB || (stackB = []);
var length = stackA.length;
while (length--) {
if (stackA[length] == a) {
return stackB[length] == b;
}
}
var size = 0;
var result = true;
// add `a` and `b` to the stack of traversed objects
stackA.push(a);
stackB.push(b);
// recursively compare objects and arrays (susceptible to call stack limits)
if (isArr) {
// compare lengths to determine if a deep comparison is necessary
length = a.length;
size = b.length;
result = size == length;
if (result) {
// deep compare the contents, ignoring non-numeric properties
while (size--) {
var index = length,
value = b[size];
if (!(result = deepEquals(a[size], value, stackA, stackB))) {
break;
}
}
}
}
else {
// deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
// which, in this case, is more costly
internalForIn(b, function(value, key, b) {
if (hasOwnProperty.call(b, key)) {
// count the number of properties.
size++;
// deep compare each property value.
return (result = hasOwnProperty.call(a, key) && deepEquals(a[key], value, stackA, stackB));
}
});
if (result) {
// ensure both objects have the same number of properties
internalForIn(a, function(value, key, a) {
if (hasOwnProperty.call(a, key)) {
// `size` will be `-1` if `a` has more properties than `b`
return (result = --size > -1);
}
});
}
}
stackA.pop();
stackB.pop();
return result;
}
var SchedulePeriodicRecursive = Rx.internals.SchedulePeriodicRecursive = (function () {
function tick(command, recurse) {
recurse(0, this._period);
try {
this._state = this._action(this._state);
} catch (e) {
this._cancel.dispose();
throw e;
}
}
function SchedulePeriodicRecursive(scheduler, state, period, action) {
this._scheduler = scheduler;
this._state = state;
this._period = period;
this._action = action;
}
SchedulePeriodicRecursive.prototype.start = function () {
var d = new SingleAssignmentDisposable();
this._cancel = d;
d.setDisposable(this._scheduler.scheduleRecursiveWithRelativeAndState(0, this._period, tick.bind(this)));
return d;
};
return SchedulePeriodicRecursive;
}());
// Collections
function IndexedItem(id, value) {
this.id = id;
this.value = value;
}
IndexedItem.prototype.compareTo = function (other) {
var c = this.value.compareTo(other.value);
c === 0 && (c = this.id - other.id);
return c;
};
// Priority Queue for Scheduling
var PriorityQueue = Rx.internals.PriorityQueue = function (capacity) {
this.items = new Array(capacity);
this.length = 0;
};
var priorityProto = PriorityQueue.prototype;
priorityProto.isHigherPriority = function (left, right) {
return this.items[left].compareTo(this.items[right]) < 0;
};
priorityProto.percolate = function (index) {
if (index >= this.length || index < 0) { return; }
var parent = index - 1 >> 1;
if (parent < 0 || parent === index) { return; }
if (this.isHigherPriority(index, parent)) {
var temp = this.items[index];
this.items[index] = this.items[parent];
this.items[parent] = temp;
this.percolate(parent);
}
};
priorityProto.heapify = function (index) {
+index || (index = 0);
if (index >= this.length || index < 0) { return; }
var left = 2 * index + 1,
right = 2 * index + 2,
first = index;
if (left < this.length && this.isHigherPriority(left, first)) {
first = left;
}
if (right < this.length && this.isHigherPriority(right, first)) {
first = right;
}
if (first !== index) {
var temp = this.items[index];
this.items[index] = this.items[first];
this.items[first] = temp;
this.heapify(first);
}
};
priorityProto.peek = function () { return this.items[0].value; };
priorityProto.removeAt = function (index) {
this.items[index] = this.items[--this.length];
this.items[this.length] = undefined;
this.heapify();
};
priorityProto.dequeue = function () {
var result = this.peek();
this.removeAt(0);
return result;
};
priorityProto.enqueue = function (item) {
var index = this.length++;
this.items[index] = new IndexedItem(PriorityQueue.count++, item);
this.percolate(index);
};
priorityProto.remove = function (item) {
for (var i = 0; i < this.length; i++) {
if (this.items[i].value === item) {
this.removeAt(i);
return true;
}
}
return false;
};
PriorityQueue.count = 0;
/** Provides a set of extension methods for virtual time scheduling. */
var VirtualTimeScheduler = Rx.VirtualTimeScheduler = (function (__super__) {
function localNow() {
return this.toDateTimeOffset(this.clock);
}
function scheduleNow(state, action) {
return this.scheduleAbsoluteWithState(state, this.clock, action);
}
function scheduleRelative(state, dueTime, action) {
return this.scheduleRelativeWithState(state, this.toRelative(dueTime), action);
}
function scheduleAbsolute(state, dueTime, action) {
return this.scheduleRelativeWithState(state, this.toRelative(dueTime - this.now()), action);
}
function invokeAction(scheduler, action) {
action();
return disposableEmpty;
}
inherits(VirtualTimeScheduler, __super__);
/**
* Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer.
*
* @constructor
* @param {Number} initialClock Initial value for the clock.
* @param {Function} comparer Comparer to determine causality of events based on absolute time.
*/
function VirtualTimeScheduler(initialClock, comparer) {
this.clock = initialClock;
this.comparer = comparer;
this.isEnabled = false;
this.queue = new PriorityQueue(1024);
__super__.call(this, localNow, scheduleNow, scheduleRelative, scheduleAbsolute);
}
var VirtualTimeSchedulerPrototype = VirtualTimeScheduler.prototype;
/**
* Adds a relative time value to an absolute time value.
* @param {Number} absolute Absolute virtual time value.
* @param {Number} relative Relative virtual time value to add.
* @return {Number} Resulting absolute virtual time sum value.
*/
VirtualTimeSchedulerPrototype.add = notImplemented;
/**
* Converts an absolute time to a number
* @param {Any} The absolute time.
* @returns {Number} The absolute time in ms
*/
VirtualTimeSchedulerPrototype.toDateTimeOffset = notImplemented;
/**
* Converts the TimeSpan value to a relative virtual time value.
* @param {Number} timeSpan TimeSpan value to convert.
* @return {Number} Corresponding relative virtual time value.
*/
VirtualTimeSchedulerPrototype.toRelative = notImplemented;
/**
* Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities. The periodic task will be emulated using recursive scheduling.
* @param {Mixed} state Initial state passed to the action upon the first iteration.
* @param {Number} period Period for running the work periodically.
* @param {Function} action Action to be executed, potentially updating the state.
* @returns {Disposable} The disposable object used to cancel the scheduled recurring action (best effort).
*/
VirtualTimeSchedulerPrototype.schedulePeriodicWithState = function (state, period, action) {
var s = new SchedulePeriodicRecursive(this, state, period, action);
return s.start();
};
/**
* Schedules an action to be executed after dueTime.
* @param {Mixed} state State passed to the action to be executed.
* @param {Number} dueTime Relative time after which to execute the action.
* @param {Function} action Action to be executed.
* @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
*/
VirtualTimeSchedulerPrototype.scheduleRelativeWithState = function (state, dueTime, action) {
var runAt = this.add(this.clock, dueTime);
return this.scheduleAbsoluteWithState(state, runAt, action);
};
/**
* Schedules an action to be executed at dueTime.
* @param {Number} dueTime Relative time after which to execute the action.
* @param {Function} action Action to be executed.
* @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
*/
VirtualTimeSchedulerPrototype.scheduleRelative = function (dueTime, action) {
return this.scheduleRelativeWithState(action, dueTime, invokeAction);
};
/**
* Starts the virtual time scheduler.
*/
VirtualTimeSchedulerPrototype.start = function () {
if (!this.isEnabled) {
this.isEnabled = true;
do {
var next = this.getNext();
if (next !== null) {
this.comparer(next.dueTime, this.clock) > 0 && (this.clock = next.dueTime);
next.invoke();
} else {
this.isEnabled = false;
}
} while (this.isEnabled);
}
};
/**
* Stops the virtual time scheduler.
*/
VirtualTimeSchedulerPrototype.stop = function () {
this.isEnabled = false;
};
/**
* Advances the scheduler's clock to the specified time, running all work till that point.
* @param {Number} time Absolute time to advance the scheduler's clock to.
*/
VirtualTimeSchedulerPrototype.advanceTo = function (time) {
var dueToClock = this.comparer(this.clock, time);
if (this.comparer(this.clock, time) > 0) { throw new ArgumentOutOfRangeError(); }
if (dueToClock === 0) { return; }
if (!this.isEnabled) {
this.isEnabled = true;
do {
var next = this.getNext();
if (next !== null && this.comparer(next.dueTime, time) <= 0) {
this.comparer(next.dueTime, this.clock) > 0 && (this.clock = next.dueTime);
next.invoke();
} else {
this.isEnabled = false;
}
} while (this.isEnabled);
this.clock = time;
}
};
/**
* Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan.
* @param {Number} time Relative time to advance the scheduler's clock by.
*/
VirtualTimeSchedulerPrototype.advanceBy = function (time) {
var dt = this.add(this.clock, time),
dueToClock = this.comparer(this.clock, dt);
if (dueToClock > 0) { throw new ArgumentOutOfRangeError(); }
if (dueToClock === 0) { return; }
this.advanceTo(dt);
};
/**
* Advances the scheduler's clock by the specified relative time.
* @param {Number} time Relative time to advance the scheduler's clock by.
*/
VirtualTimeSchedulerPrototype.sleep = function (time) {
var dt = this.add(this.clock, time);
if (this.comparer(this.clock, dt) >= 0) { throw new ArgumentOutOfRangeError(); }
this.clock = dt;
};
/**
* Gets the next scheduled item to be executed.
* @returns {ScheduledItem} The next scheduled item.
*/
VirtualTimeSchedulerPrototype.getNext = function () {
while (this.queue.length > 0) {
var next = this.queue.peek();
if (next.isCancelled()) {
this.queue.dequeue();
} else {
return next;
}
}
return null;
};
/**
* Schedules an action to be executed at dueTime.
* @param {Scheduler} scheduler Scheduler to execute the action on.
* @param {Number} dueTime Absolute time at which to execute the action.
* @param {Function} action Action to be executed.
* @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
*/
VirtualTimeSchedulerPrototype.scheduleAbsolute = function (dueTime, action) {
return this.scheduleAbsoluteWithState(action, dueTime, invokeAction);
};
/**
* Schedules an action to be executed at dueTime.
* @param {Mixed} state State passed to the action to be executed.
* @param {Number} dueTime Absolute time at which to execute the action.
* @param {Function} action Action to be executed.
* @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
*/
VirtualTimeSchedulerPrototype.scheduleAbsoluteWithState = function (state, dueTime, action) {
var self = this;
function run(scheduler, state1) {
self.queue.remove(si);
return action(scheduler, state1);
}
var si = new ScheduledItem(this, state, run, dueTime, this.comparer);
this.queue.enqueue(si);
return si.disposable;
};
return VirtualTimeScheduler;
}(Scheduler));
function OnNextPredicate(predicate) {
this.predicate = predicate;
};
OnNextPredicate.prototype.equals = function (other) {
if (other === this) { return true; }
if (other == null) { return false; }
if (other.kind !== 'N') { return false; }
return this.predicate(other.value);
};
function OnErrorPredicate(predicate) {
this.predicate = predicate;
};
OnErrorPredicate.prototype.equals = function (other) {
if (other === this) { return true; }
if (other == null) { return false; }
if (other.kind !== 'E') { return false; }
return this.predicate(other.exception);
};
var ReactiveTest = Rx.ReactiveTest = {
/** Default virtual time used for creation of observable sequences in unit tests. */
created: 100,
/** Default virtual time used to subscribe to observable sequences in unit tests. */
subscribed: 200,
/** Default virtual time used to dispose subscriptions in unit tests. */
disposed: 1000,
/**
* Factory method for an OnNext notification record at a given time with a given value or a predicate function.
*
* 1 - ReactiveTest.onNext(200, 42);
* 2 - ReactiveTest.onNext(200, function (x) { return x.length == 2; });
*
* @param ticks Recorded virtual time the OnNext notification occurs.
* @param value Recorded value stored in the OnNext notification or a predicate.
* @return Recorded OnNext notification.
*/
onNext: function (ticks, value) {
return typeof value === 'function' ?
new Recorded(ticks, new OnNextPredicate(value)) :
new Recorded(ticks, Notification.createOnNext(value));
},
/**
* Factory method for an OnError notification record at a given time with a given error.
*
* 1 - ReactiveTest.onNext(200, new Error('error'));
* 2 - ReactiveTest.onNext(200, function (e) { return e.message === 'error'; });
*
* @param ticks Recorded virtual time the OnError notification occurs.
* @param exception Recorded exception stored in the OnError notification.
* @return Recorded OnError notification.
*/
onError: function (ticks, error) {
return typeof error === 'function' ?
new Recorded(ticks, new OnErrorPredicate(error)) :
new Recorded(ticks, Notification.createOnError(error));
},
/**
* Factory method for an OnCompleted notification record at a given time.
*
* @param ticks Recorded virtual time the OnCompleted notification occurs.
* @return Recorded OnCompleted notification.
*/
onCompleted: function (ticks) {
return new Recorded(ticks, Notification.createOnCompleted());
},
/**
* Factory method for a subscription record based on a given subscription and disposal time.
*
* @param start Virtual time indicating when the subscription was created.
* @param end Virtual time indicating when the subscription was disposed.
* @return Subscription object.
*/
subscribe: function (start, end) {
return new Subscription(start, end);
}
};
/**
* Creates a new object recording the production of the specified value at the given virtual time.
*
* @constructor
* @param {Number} time Virtual time the value was produced on.
* @param {Mixed} value Value that was produced.
* @param {Function} comparer An optional comparer.
*/
var Recorded = Rx.Recorded = function (time, value, comparer) {
this.time = time;
this.value = value;
this.comparer = comparer || defaultComparer;
};
/**
* Checks whether the given recorded object is equal to the current instance.
*
* @param {Recorded} other Recorded object to check for equality.
* @returns {Boolean} true if both objects are equal; false otherwise.
*/
Recorded.prototype.equals = function (other) {
return this.time === other.time && this.comparer(this.value, other.value);
};
/**
* Returns a string representation of the current Recorded value.
*
* @returns {String} String representation of the current Recorded value.
*/
Recorded.prototype.toString = function () {
return this.value.toString() + '@' + this.time;
};
/**
* Creates a new subscription object with the given virtual subscription and unsubscription time.
*
* @constructor
* @param {Number} subscribe Virtual time at which the subscription occurred.
* @param {Number} unsubscribe Virtual time at which the unsubscription occurred.
*/
var Subscription = Rx.Subscription = function (start, end) {
this.subscribe = start;
this.unsubscribe = end || Number.MAX_VALUE;
};
/**
* Checks whether the given subscription is equal to the current instance.
* @param other Subscription object to check for equality.
* @returns {Boolean} true if both objects are equal; false otherwise.
*/
Subscription.prototype.equals = function (other) {
return this.subscribe === other.subscribe && this.unsubscribe === other.unsubscribe;
};
/**
* Returns a string representation of the current Subscription value.
* @returns {String} String representation of the current Subscription value.
*/
Subscription.prototype.toString = function () {
return '(' + this.subscribe + ', ' + (this.unsubscribe === Number.MAX_VALUE ? 'Infinite' : this.unsubscribe) + ')';
};
var MockDisposable = Rx.MockDisposable = function (scheduler) {
this.scheduler = scheduler;
this.disposes = [];
this.disposes.push(this.scheduler.clock);
};
MockDisposable.prototype.dispose = function () {
this.disposes.push(this.scheduler.clock);
};
var MockObserver = (function (__super__) {
inherits(MockObserver, __super__);
function MockObserver(scheduler) {
__super__.call(this);
this.scheduler = scheduler;
this.messages = [];
}
var MockObserverPrototype = MockObserver.prototype;
MockObserverPrototype.onNext = function (value) {
this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnNext(value)));
};
MockObserverPrototype.onError = function (exception) {
this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnError(exception)));
};
MockObserverPrototype.onCompleted = function () {
this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnCompleted()));
};
return MockObserver;
})(Observer);
function MockPromise(scheduler, messages) {
var self = this;
this.scheduler = scheduler;
this.messages = messages;
this.subscriptions = [];
this.observers = [];
for (var i = 0, len = this.messages.length; i < len; i++) {
var message = this.messages[i],
notification = message.value;
(function (innerNotification) {
scheduler.scheduleAbsoluteWithState(null, message.time, function () {
var obs = self.observers.slice(0);
for (var j = 0, jLen = obs.length; j < jLen; j++) {
innerNotification.accept(obs[j]);
}
return disposableEmpty;
});
})(notification);
}
}
MockPromise.prototype.then = function (onResolved, onRejected) {
var self = this;
this.subscriptions.push(new Subscription(this.scheduler.clock));
var index = this.subscriptions.length - 1;
var newPromise;
var observer = Rx.Observer.create(
function (x) {
var retValue = onResolved(x);
if (retValue && typeof retValue.then === 'function') {
newPromise = retValue;
} else {
var ticks = self.scheduler.clock;
newPromise = new MockPromise(self.scheduler, [Rx.ReactiveTest.onNext(ticks, undefined), Rx.ReactiveTest.onCompleted(ticks)]);
}
var idx = self.observers.indexOf(observer);
self.observers.splice(idx, 1);
self.subscriptions[index] = new Subscription(self.subscriptions[index].subscribe, self.scheduler.clock);
},
function (err) {
onRejected(err);
var idx = self.observers.indexOf(observer);
self.observers.splice(idx, 1);
self.subscriptions[index] = new Subscription(self.subscriptions[index].subscribe, self.scheduler.clock);
}
);
this.observers.push(observer);
return newPromise || new MockPromise(this.scheduler, this.messages);
};
var HotObservable = (function (__super__) {
function subscribe(observer) {
var observable = this;
this.observers.push(observer);
this.subscriptions.push(new Subscription(this.scheduler.clock));
var index = this.subscriptions.length - 1;
return disposableCreate(function () {
var idx = observable.observers.indexOf(observer);
observable.observers.splice(idx, 1);
observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
});
}
inherits(HotObservable, __super__);
function HotObservable(scheduler, messages) {
__super__.call(this, subscribe);
var message, notification, observable = this;
this.scheduler = scheduler;
this.messages = messages;
this.subscriptions = [];
this.observers = [];
for (var i = 0, len = this.messages.length; i < len; i++) {
message = this.messages[i];
notification = message.value;
(function (innerNotification) {
scheduler.scheduleAbsoluteWithState(null, message.time, function () {
var obs = observable.observers.slice(0);
for (var j = 0, jLen = obs.length; j < jLen; j++) {
innerNotification.accept(obs[j]);
}
return disposableEmpty;
});
})(notification);
}
}
return HotObservable;
})(Observable);
var ColdObservable = (function (__super__) {
function subscribe(observer) {
var message, notification, observable = this;
this.subscriptions.push(new Subscription(this.scheduler.clock));
var index = this.subscriptions.length - 1;
var d = new CompositeDisposable();
for (var i = 0, len = this.messages.length; i < len; i++) {
message = this.messages[i];
notification = message.value;
(function (innerNotification) {
d.add(observable.scheduler.scheduleRelativeWithState(null, message.time, function () {
innerNotification.accept(observer);
return disposableEmpty;
}));
})(notification);
}
return disposableCreate(function () {
observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
d.dispose();
});
}
inherits(ColdObservable, __super__);
function ColdObservable(scheduler, messages) {
__super__.call(this, subscribe);
this.scheduler = scheduler;
this.messages = messages;
this.subscriptions = [];
}
return ColdObservable;
})(Observable);
/** Virtual time scheduler used for testing applications and libraries built using Reactive Extensions. */
Rx.TestScheduler = (function (__super__) {
inherits(TestScheduler, __super__);
function baseComparer(x, y) {
return x > y ? 1 : (x < y ? -1 : 0);
}
function TestScheduler() {
__super__.call(this, 0, baseComparer);
}
/**
* Schedules an action to be executed at the specified virtual time.
*
* @param state State passed to the action to be executed.
* @param dueTime Absolute virtual time at which to execute the action.
* @param action Action to be executed.
* @return Disposable object used to cancel the scheduled action (best effort).
*/
TestScheduler.prototype.scheduleAbsoluteWithState = function (state, dueTime, action) {
dueTime <= this.clock && (dueTime = this.clock + 1);
return __super__.prototype.scheduleAbsoluteWithState.call(this, state, dueTime, action);
};
/**
* Adds a relative virtual time to an absolute virtual time value.
*
* @param absolute Absolute virtual time value.
* @param relative Relative virtual time value to add.
* @return Resulting absolute virtual time sum value.
*/
TestScheduler.prototype.add = function (absolute, relative) {
return absolute + relative;
};
/**
* Converts the absolute virtual time value to a DateTimeOffset value.
*
* @param absolute Absolute virtual time value to convert.
* @return Corresponding DateTimeOffset value.
*/
TestScheduler.prototype.toDateTimeOffset = function (absolute) {
return new Date(absolute).getTime();
};
/**
* Converts the TimeSpan value to a relative virtual time value.
*
* @param timeSpan TimeSpan value to convert.
* @return Corresponding relative virtual time value.
*/
TestScheduler.prototype.toRelative = function (timeSpan) {
return timeSpan;
};
/**
* Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and dispose the subscription.
*
* @param create Factory method to create an observable sequence.
* @param created Virtual time at which to invoke the factory to create an observable sequence.
* @param subscribed Virtual time at which to subscribe to the created observable sequence.
* @param disposed Virtual time at which to dispose the subscription.
* @return Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
*/
TestScheduler.prototype.startWithTiming = function (create, created, subscribed, disposed) {
var observer = this.createObserver(), source, subscription;
this.scheduleAbsoluteWithState(null, created, function () {
source = create();
return disposableEmpty;
});
this.scheduleAbsoluteWithState(null, subscribed, function () {
subscription = source.subscribe(observer);
return disposableEmpty;
});
this.scheduleAbsoluteWithState(null, disposed, function () {
subscription.dispose();
return disposableEmpty;
});
this.start();
return observer;
};
/**
* Starts the test scheduler and uses the specified virtual time to dispose the subscription to the sequence obtained through the factory function.
* Default virtual times are used for factory invocation and sequence subscription.
*
* @param create Factory method to create an observable sequence.
* @param disposed Virtual time at which to dispose the subscription.
* @return Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
*/
TestScheduler.prototype.startWithDispose = function (create, disposed) {
return this.startWithTiming(create, ReactiveTest.created, ReactiveTest.subscribed, disposed);
};
/**
* Starts the test scheduler and uses default virtual times to invoke the factory function, to subscribe to the resulting sequence, and to dispose the subscription.
*
* @param create Factory method to create an observable sequence.
* @return Observer with timestamped recordings of notification messages that were received during the virtual time window when the subscription to the source sequence was active.
*/
TestScheduler.prototype.startWithCreate = function (create) {
return this.startWithTiming(create, ReactiveTest.created, ReactiveTest.subscribed, ReactiveTest.disposed);
};
/**
* Creates a hot observable using the specified timestamped notification messages either as an array or arguments.
* @param messages Notifications to surface through the created sequence at their specified absolute virtual times.
* @return Hot observable sequence that can be used to assert the timing of subscriptions and notifications.
*/
TestScheduler.prototype.createHotObservable = function () {
var len = arguments.length, args;
if (Array.isArray(arguments[0])) {
args = arguments[0];
} else {
args = new Array(len);
for (var i = 0; i < len; i++) { args[i] = arguments[i]; }
}
return new HotObservable(this, args);
};
/**
* Creates a cold observable using the specified timestamped notification messages either as an array or arguments.
* @param messages Notifications to surface through the created sequence at their specified virtual time offsets from the sequence subscription time.
* @return Cold observable sequence that can be used to assert the timing of subscriptions and notifications.
*/
TestScheduler.prototype.createColdObservable = function () {
var len = arguments.length, args;
if (Array.isArray(arguments[0])) {
args = arguments[0];
} else {
args = new Array(len);
for (var i = 0; i < len; i++) { args[i] = arguments[i]; }
}
return new ColdObservable(this, args);
};
/**
* Creates a resolved promise with the given value and ticks
* @param {Number} ticks The absolute time of the resolution.
* @param {Any} value The value to yield at the given tick.
* @returns {MockPromise} A mock Promise which fulfills with the given value.
*/
TestScheduler.prototype.createResolvedPromise = function (ticks, value) {
return new MockPromise(this, [Rx.ReactiveTest.onNext(ticks, value), Rx.ReactiveTest.onCompleted(ticks)]);
};
/**
* Creates a rejected promise with the given reason and ticks
* @param {Number} ticks The absolute time of the resolution.
* @param {Any} reason The reason for rejection to yield at the given tick.
* @returns {MockPromise} A mock Promise which rejects with the given reason.
*/
TestScheduler.prototype.createRejectedPromise = function (ticks, reason) {
return new MockPromise(this, [Rx.ReactiveTest.onError(ticks, reason)]);
};
/**
* Creates an observer that records received notification messages and timestamps those.
* @return Observer that can be used to assert the timing of received notifications.
*/
TestScheduler.prototype.createObserver = function () {
return new MockObserver(this);
};
return TestScheduler;
})(VirtualTimeScheduler);
return Rx;
}));