UNPKG

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