UNPKG

41 kBJavaScriptView Raw
1// Copyright (c) Microsoft, All rights reserved. See License.txt in the project root for license information.
2
3;(function (factory) {
4 var objectTypes = {
5 'function': true,
6 'object': true
7 };
8
9 function checkGlobal(value) {
10 return (value && value.Object === Object) ? value : null;
11 }
12
13 var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType) ? exports : null;
14 var freeModule = (objectTypes[typeof module] && module && !module.nodeType) ? module : null;
15 var freeGlobal = checkGlobal(freeExports && freeModule && typeof global === 'object' && global);
16 var freeSelf = checkGlobal(objectTypes[typeof self] && self);
17 var freeWindow = checkGlobal(objectTypes[typeof window] && window);
18 var moduleExports = (freeModule && freeModule.exports === freeExports) ? freeExports : null;
19 var thisGlobal = checkGlobal(objectTypes[typeof this] && this);
20 var root = freeGlobal || ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) || freeSelf || thisGlobal || Function('return this')();
21
22 // Because of build optimizers
23 if (typeof define === 'function' && define.amd) {
24 define(['./rx.core'], function (Rx, exports) {
25 return factory(root, exports, Rx);
26 });
27 } else if (typeof module === 'object' && module && module.exports === freeExports) {
28 module.exports = factory(root, module.exports, require('./rx.core'));
29 } else {
30 root.Rx = factory(root, {}, root.Rx);
31 }
32}.call(this, function (root, exp, Rx, undefined) {
33
34 // Defaults
35 var Observer = Rx.Observer,
36 Observable = Rx.Observable,
37 Disposable = Rx.Disposable,
38 disposableEmpty = Disposable.empty,
39 disposableCreate = Disposable.create,
40 CompositeDisposable = Rx.CompositeDisposable,
41 SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
42 Scheduler = Rx.Scheduler,
43 ScheduledItem = Rx.internals.ScheduledItem,
44 SchedulePeriodicRecursive = Rx.internals.SchedulePeriodicRecursive,
45 PriorityQueue = Rx.internals.PriorityQueue,
46 inherits = Rx.internals.inherits,
47 notImplemented = Rx.helpers.notImplemented,
48 defaultComparer = Rx.helpers.defaultComparer = function (a, b) { return isEqual(a, b); };
49
50 /**
51 * Represents a notification to an observer.
52 */
53 var Notification = Rx.Notification = (function () {
54 function Notification() {
55
56 }
57
58 Notification.prototype._accept = function (onNext, onError, onCompleted) {
59 throw new NotImplementedError();
60 };
61
62 Notification.prototype._acceptObserver = function (onNext, onError, onCompleted) {
63 throw new NotImplementedError();
64 };
65
66 /**
67 * Invokes the delegate corresponding to the notification or the observer's method corresponding to the notification and returns the produced result.
68 * @param {Function | Observer} observerOrOnNext Function to invoke for an OnNext notification or Observer to invoke the notification on..
69 * @param {Function} onError Function to invoke for an OnError notification.
70 * @param {Function} onCompleted Function to invoke for an OnCompleted notification.
71 * @returns {Any} Result produced by the observation.
72 */
73 Notification.prototype.accept = function (observerOrOnNext, onError, onCompleted) {
74 return observerOrOnNext && typeof observerOrOnNext === 'object' ?
75 this._acceptObserver(observerOrOnNext) :
76 this._accept(observerOrOnNext, onError, onCompleted);
77 };
78
79 /**
80 * Returns an observable sequence with a single notification.
81 *
82 * @memberOf Notifications
83 * @param {Scheduler} [scheduler] Scheduler to send out the notification calls on.
84 * @returns {Observable} The observable sequence that surfaces the behavior of the notification upon subscription.
85 */
86 Notification.prototype.toObservable = function (scheduler) {
87 var self = this;
88 isScheduler(scheduler) || (scheduler = immediateScheduler);
89 return new AnonymousObservable(function (o) {
90 return scheduler.schedule(self, function (_, notification) {
91 notification._acceptObserver(o);
92 notification.kind === 'N' && o.onCompleted();
93 });
94 });
95 };
96
97 return Notification;
98 })();
99
100 var OnNextNotification = (function (__super__) {
101 inherits(OnNextNotification, __super__);
102 function OnNextNotification(value) {
103 this.value = value;
104 this.kind = 'N';
105 }
106
107 OnNextNotification.prototype._accept = function (onNext) {
108 return onNext(this.value);
109 };
110
111 OnNextNotification.prototype._acceptObserver = function (o) {
112 return o.onNext(this.value);
113 };
114
115 OnNextNotification.prototype.toString = function () {
116 return 'OnNext(' + this.value + ')';
117 };
118
119 return OnNextNotification;
120 }(Notification));
121
122 var OnErrorNotification = (function (__super__) {
123 inherits(OnErrorNotification, __super__);
124 function OnErrorNotification(error) {
125 this.error = error;
126 this.kind = 'E';
127 }
128
129 OnErrorNotification.prototype._accept = function (onNext, onError) {
130 return onError(this.error);
131 };
132
133 OnErrorNotification.prototype._acceptObserver = function (o) {
134 return o.onError(this.error);
135 };
136
137 OnErrorNotification.prototype.toString = function () {
138 return 'OnError(' + this.error + ')';
139 };
140
141 return OnErrorNotification;
142 }(Notification));
143
144 var OnCompletedNotification = (function (__super__) {
145 inherits(OnCompletedNotification, __super__);
146 function OnCompletedNotification() {
147 this.kind = 'C';
148 }
149
150 OnCompletedNotification.prototype._accept = function (onNext, onError, onCompleted) {
151 return onCompleted();
152 };
153
154 OnCompletedNotification.prototype._acceptObserver = function (o) {
155 return o.onCompleted();
156 };
157
158 OnCompletedNotification.prototype.toString = function () {
159 return 'OnCompleted()';
160 };
161
162 return OnCompletedNotification;
163 }(Notification));
164
165 /**
166 * Creates an object that represents an OnNext notification to an observer.
167 * @param {Any} value The value contained in the notification.
168 * @returns {Notification} The OnNext notification containing the value.
169 */
170 var notificationCreateOnNext = Notification.createOnNext = function (value) {
171 return new OnNextNotification(value);
172 };
173
174 /**
175 * Creates an object that represents an OnError notification to an observer.
176 * @param {Any} error The exception contained in the notification.
177 * @returns {Notification} The OnError notification containing the exception.
178 */
179 var notificationCreateOnError = Notification.createOnError = function (error) {
180 return new OnErrorNotification(error);
181 };
182
183 /**
184 * Creates an object that represents an OnCompleted notification to an observer.
185 * @returns {Notification} The OnCompleted notification.
186 */
187 var notificationCreateOnCompleted = Notification.createOnCompleted = function () {
188 return new OnCompletedNotification();
189 };
190
191 /** Used to determine if values are of the language type Object */
192 var dontEnums = ['toString',
193 'toLocaleString',
194 'valueOf',
195 'hasOwnProperty',
196 'isPrototypeOf',
197 'propertyIsEnumerable',
198 'constructor'],
199 dontEnumsLength = dontEnums.length;
200
201var argsTag = '[object Arguments]',
202 arrayTag = '[object Array]',
203 boolTag = '[object Boolean]',
204 dateTag = '[object Date]',
205 errorTag = '[object Error]',
206 funcTag = '[object Function]',
207 mapTag = '[object Map]',
208 numberTag = '[object Number]',
209 objectTag = '[object Object]',
210 regexpTag = '[object RegExp]',
211 setTag = '[object Set]',
212 stringTag = '[object String]',
213 weakMapTag = '[object WeakMap]';
214
215var arrayBufferTag = '[object ArrayBuffer]',
216 float32Tag = '[object Float32Array]',
217 float64Tag = '[object Float64Array]',
218 int8Tag = '[object Int8Array]',
219 int16Tag = '[object Int16Array]',
220 int32Tag = '[object Int32Array]',
221 uint8Tag = '[object Uint8Array]',
222 uint8ClampedTag = '[object Uint8ClampedArray]',
223 uint16Tag = '[object Uint16Array]',
224 uint32Tag = '[object Uint32Array]';
225
226var typedArrayTags = {};
227typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
228typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
229typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
230typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
231typedArrayTags[uint32Tag] = true;
232typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
233typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
234typedArrayTags[dateTag] = typedArrayTags[errorTag] =
235typedArrayTags[funcTag] = typedArrayTags[mapTag] =
236typedArrayTags[numberTag] = typedArrayTags[objectTag] =
237typedArrayTags[regexpTag] = typedArrayTags[setTag] =
238typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
239
240var objectProto = Object.prototype,
241 hasOwnProperty = objectProto.hasOwnProperty,
242 objToString = objectProto.toString,
243 MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
244
245var keys = Object.keys || (function() {
246 var hasOwnProperty = Object.prototype.hasOwnProperty,
247 hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
248 dontEnums = [
249 'toString',
250 'toLocaleString',
251 'valueOf',
252 'hasOwnProperty',
253 'isPrototypeOf',
254 'propertyIsEnumerable',
255 'constructor'
256 ],
257 dontEnumsLength = dontEnums.length;
258
259 return function(obj) {
260 if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
261 throw new TypeError('Object.keys called on non-object');
262 }
263
264 var result = [], prop, i;
265
266 for (prop in obj) {
267 if (hasOwnProperty.call(obj, prop)) {
268 result.push(prop);
269 }
270 }
271
272 if (hasDontEnumBug) {
273 for (i = 0; i < dontEnumsLength; i++) {
274 if (hasOwnProperty.call(obj, dontEnums[i])) {
275 result.push(dontEnums[i]);
276 }
277 }
278 }
279 return result;
280 };
281 }());
282
283function equalObjects(object, other, equalFunc, isLoose, stackA, stackB) {
284 var objProps = keys(object),
285 objLength = objProps.length,
286 othProps = keys(other),
287 othLength = othProps.length;
288
289 if (objLength !== othLength && !isLoose) {
290 return false;
291 }
292 var index = objLength, key;
293 while (index--) {
294 key = objProps[index];
295 if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) {
296 return false;
297 }
298 }
299 var skipCtor = isLoose;
300 while (++index < objLength) {
301 key = objProps[index];
302 var objValue = object[key],
303 othValue = other[key],
304 result;
305
306 if (!(result === undefined ? equalFunc(objValue, othValue, isLoose, stackA, stackB) : result)) {
307 return false;
308 }
309 skipCtor || (skipCtor = key === 'constructor');
310 }
311 if (!skipCtor) {
312 var objCtor = object.constructor,
313 othCtor = other.constructor;
314
315 if (objCtor !== othCtor &&
316 ('constructor' in object && 'constructor' in other) &&
317 !(typeof objCtor === 'function' && objCtor instanceof objCtor &&
318 typeof othCtor === 'function' && othCtor instanceof othCtor)) {
319 return false;
320 }
321 }
322 return true;
323}
324
325function equalByTag(object, other, tag) {
326 switch (tag) {
327 case boolTag:
328 case dateTag:
329 return +object === +other;
330
331 case errorTag:
332 return object.name === other.name && object.message === other.message;
333
334 case numberTag:
335 return (object !== +object) ?
336 other !== +other :
337 object === +other;
338
339 case regexpTag:
340 case stringTag:
341 return object === (other + '');
342 }
343 return false;
344}
345
346var isObject = Rx.internals.isObject = function(value) {
347 var type = typeof value;
348 return !!value && (type === 'object' || type === 'function');
349};
350
351function isObjectLike(value) {
352 return !!value && typeof value === 'object';
353}
354
355function isLength(value) {
356 return typeof value === 'number' && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER;
357}
358
359var isHostObject = (function() {
360 try {
361 Object({ 'toString': 0 } + '');
362 } catch(e) {
363 return function() { return false; };
364 }
365 return function(value) {
366 return typeof value.toString !== 'function' && typeof (value + '') === 'string';
367 };
368}());
369
370function isTypedArray(value) {
371 return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
372}
373
374var isArray = Array.isArray || function(value) {
375 return isObjectLike(value) && isLength(value.length) && objToString.call(value) === arrayTag;
376};
377
378function arraySome (array, predicate) {
379 var index = -1,
380 length = array.length;
381
382 while (++index < length) {
383 if (predicate(array[index], index, array)) {
384 return true;
385 }
386 }
387 return false;
388}
389
390function equalArrays(array, other, equalFunc, isLoose, stackA, stackB) {
391 var index = -1,
392 arrLength = array.length,
393 othLength = other.length;
394
395 if (arrLength !== othLength && !(isLoose && othLength > arrLength)) {
396 return false;
397 }
398 // Ignore non-index properties.
399 while (++index < arrLength) {
400 var arrValue = array[index],
401 othValue = other[index],
402 result;
403
404 if (result !== undefined) {
405 if (result) {
406 continue;
407 }
408 return false;
409 }
410 // Recursively compare arrays (susceptible to call stack limits).
411 if (isLoose) {
412 if (!arraySome(other, function(othValue) {
413 return arrValue === othValue || equalFunc(arrValue, othValue, isLoose, stackA, stackB);
414 })) {
415 return false;
416 }
417 } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, isLoose, stackA, stackB))) {
418 return false;
419 }
420 }
421 return true;
422}
423
424function baseIsEqualDeep(object, other, equalFunc, isLoose, stackA, stackB) {
425 var objIsArr = isArray(object),
426 othIsArr = isArray(other),
427 objTag = arrayTag,
428 othTag = arrayTag;
429
430 if (!objIsArr) {
431 objTag = objToString.call(object);
432 if (objTag === argsTag) {
433 objTag = objectTag;
434 } else if (objTag !== objectTag) {
435 objIsArr = isTypedArray(object);
436 }
437 }
438 if (!othIsArr) {
439 othTag = objToString.call(other);
440 if (othTag === argsTag) {
441 othTag = objectTag;
442 }
443 }
444 var objIsObj = objTag === objectTag && !isHostObject(object),
445 othIsObj = othTag === objectTag && !isHostObject(other),
446 isSameTag = objTag === othTag;
447
448 if (isSameTag && !(objIsArr || objIsObj)) {
449 return equalByTag(object, other, objTag);
450 }
451 if (!isLoose) {
452 var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
453 othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
454
455 if (objIsWrapped || othIsWrapped) {
456 return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, isLoose, stackA, stackB);
457 }
458 }
459 if (!isSameTag) {
460 return false;
461 }
462 // Assume cyclic values are equal.
463 // For more information on detecting circular references see https://es5.github.io/#JO.
464 stackA || (stackA = []);
465 stackB || (stackB = []);
466
467 var length = stackA.length;
468 while (length--) {
469 if (stackA[length] === object) {
470 return stackB[length] === other;
471 }
472 }
473 // Add `object` and `other` to the stack of traversed objects.
474 stackA.push(object);
475 stackB.push(other);
476
477 var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, isLoose, stackA, stackB);
478
479 stackA.pop();
480 stackB.pop();
481
482 return result;
483}
484
485function baseIsEqual(value, other, isLoose, stackA, stackB) {
486 if (value === other) {
487 return true;
488 }
489 if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
490 return value !== value && other !== other;
491 }
492 return baseIsEqualDeep(value, other, baseIsEqual, isLoose, stackA, stackB);
493}
494
495var isEqual = Rx.internals.isEqual = function (value, other) {
496 return baseIsEqual(value, other);
497};
498
499 var SchedulePeriodicRecursive = Rx.internals.SchedulePeriodicRecursive = (function () {
500 function createTick(self) {
501 return function tick(command, recurse) {
502 recurse(0, self._period);
503 var state = tryCatch(self._action)(self._state);
504 if (state === errorObj) {
505 self._cancel.dispose();
506 thrower(state.e);
507 }
508 self._state = state;
509 };
510 }
511
512 function SchedulePeriodicRecursive(scheduler, state, period, action) {
513 this._scheduler = scheduler;
514 this._state = state;
515 this._period = period;
516 this._action = action;
517 }
518
519 SchedulePeriodicRecursive.prototype.start = function () {
520 var d = new SingleAssignmentDisposable();
521 this._cancel = d;
522 d.setDisposable(this._scheduler.scheduleRecursiveFuture(0, this._period, createTick(this)));
523
524 return d;
525 };
526
527 return SchedulePeriodicRecursive;
528 }());
529
530 /** Provides a set of extension methods for virtual time scheduling. */
531 var VirtualTimeScheduler = Rx.VirtualTimeScheduler = (function (__super__) {
532 inherits(VirtualTimeScheduler, __super__);
533
534 /**
535 * Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer.
536 *
537 * @constructor
538 * @param {Number} initialClock Initial value for the clock.
539 * @param {Function} comparer Comparer to determine causality of events based on absolute time.
540 */
541 function VirtualTimeScheduler(initialClock, comparer) {
542 this.clock = initialClock;
543 this.comparer = comparer;
544 this.isEnabled = false;
545 this.queue = new PriorityQueue(1024);
546 __super__.call(this);
547 }
548
549 var VirtualTimeSchedulerPrototype = VirtualTimeScheduler.prototype;
550
551 VirtualTimeSchedulerPrototype.now = function () {
552 return this.toAbsoluteTime(this.clock);
553 };
554
555 VirtualTimeSchedulerPrototype.schedule = function (state, action) {
556 return this.scheduleAbsolute(state, this.clock, action);
557 };
558
559 VirtualTimeSchedulerPrototype.scheduleFuture = function (state, dueTime, action) {
560 var dt = dueTime instanceof Date ?
561 this.toRelativeTime(dueTime - this.now()) :
562 this.toRelativeTime(dueTime);
563
564 return this.scheduleRelative(state, dt, action);
565 };
566
567 /**
568 * Adds a relative time value to an absolute time value.
569 * @param {Number} absolute Absolute virtual time value.
570 * @param {Number} relative Relative virtual time value to add.
571 * @return {Number} Resulting absolute virtual time sum value.
572 */
573 VirtualTimeSchedulerPrototype.add = notImplemented;
574
575 /**
576 * Converts an absolute time to a number
577 * @param {Any} The absolute time.
578 * @returns {Number} The absolute time in ms
579 */
580 VirtualTimeSchedulerPrototype.toAbsoluteTime = notImplemented;
581
582 /**
583 * Converts the TimeSpan value to a relative virtual time value.
584 * @param {Number} timeSpan TimeSpan value to convert.
585 * @return {Number} Corresponding relative virtual time value.
586 */
587 VirtualTimeSchedulerPrototype.toRelativeTime = notImplemented;
588
589 /**
590 * Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities. The periodic task will be emulated using recursive scheduling.
591 * @param {Mixed} state Initial state passed to the action upon the first iteration.
592 * @param {Number} period Period for running the work periodically.
593 * @param {Function} action Action to be executed, potentially updating the state.
594 * @returns {Disposable} The disposable object used to cancel the scheduled recurring action (best effort).
595 */
596 VirtualTimeSchedulerPrototype.schedulePeriodic = function (state, period, action) {
597 var s = new SchedulePeriodicRecursive(this, state, period, action);
598 return s.start();
599 };
600
601 /**
602 * Schedules an action to be executed after dueTime.
603 * @param {Mixed} state State passed to the action to be executed.
604 * @param {Number} dueTime Relative time after which to execute the action.
605 * @param {Function} action Action to be executed.
606 * @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
607 */
608 VirtualTimeSchedulerPrototype.scheduleRelative = function (state, dueTime, action) {
609 var runAt = this.add(this.clock, dueTime);
610 return this.scheduleAbsolute(state, runAt, action);
611 };
612
613 /**
614 * Starts the virtual time scheduler.
615 */
616 VirtualTimeSchedulerPrototype.start = function () {
617 if (!this.isEnabled) {
618 this.isEnabled = true;
619 do {
620 var next = this.getNext();
621 if (next !== null) {
622 this.comparer(next.dueTime, this.clock) > 0 && (this.clock = next.dueTime);
623 next.invoke();
624 } else {
625 this.isEnabled = false;
626 }
627 } while (this.isEnabled);
628 }
629 };
630
631 /**
632 * Stops the virtual time scheduler.
633 */
634 VirtualTimeSchedulerPrototype.stop = function () {
635 this.isEnabled = false;
636 };
637
638 /**
639 * Advances the scheduler's clock to the specified time, running all work till that point.
640 * @param {Number} time Absolute time to advance the scheduler's clock to.
641 */
642 VirtualTimeSchedulerPrototype.advanceTo = function (time) {
643 var dueToClock = this.comparer(this.clock, time);
644 if (this.comparer(this.clock, time) > 0) { throw new ArgumentOutOfRangeError(); }
645 if (dueToClock === 0) { return; }
646 if (!this.isEnabled) {
647 this.isEnabled = true;
648 do {
649 var next = this.getNext();
650 if (next !== null && this.comparer(next.dueTime, time) <= 0) {
651 this.comparer(next.dueTime, this.clock) > 0 && (this.clock = next.dueTime);
652 next.invoke();
653 } else {
654 this.isEnabled = false;
655 }
656 } while (this.isEnabled);
657 this.clock = time;
658 }
659 };
660
661 /**
662 * Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan.
663 * @param {Number} time Relative time to advance the scheduler's clock by.
664 */
665 VirtualTimeSchedulerPrototype.advanceBy = function (time) {
666 var dt = this.add(this.clock, time),
667 dueToClock = this.comparer(this.clock, dt);
668 if (dueToClock > 0) { throw new ArgumentOutOfRangeError(); }
669 if (dueToClock === 0) { return; }
670
671 this.advanceTo(dt);
672 };
673
674 /**
675 * Advances the scheduler's clock by the specified relative time.
676 * @param {Number} time Relative time to advance the scheduler's clock by.
677 */
678 VirtualTimeSchedulerPrototype.sleep = function (time) {
679 var dt = this.add(this.clock, time);
680 if (this.comparer(this.clock, dt) >= 0) { throw new ArgumentOutOfRangeError(); }
681
682 this.clock = dt;
683 };
684
685 /**
686 * Gets the next scheduled item to be executed.
687 * @returns {ScheduledItem} The next scheduled item.
688 */
689 VirtualTimeSchedulerPrototype.getNext = function () {
690 while (this.queue.length > 0) {
691 var next = this.queue.peek();
692 if (next.isCancelled()) {
693 this.queue.dequeue();
694 } else {
695 return next;
696 }
697 }
698 return null;
699 };
700
701 /**
702 * Schedules an action to be executed at dueTime.
703 * @param {Mixed} state State passed to the action to be executed.
704 * @param {Number} dueTime Absolute time at which to execute the action.
705 * @param {Function} action Action to be executed.
706 * @returns {Disposable} The disposable object used to cancel the scheduled action (best effort).
707 */
708 VirtualTimeSchedulerPrototype.scheduleAbsolute = function (state, dueTime, action) {
709 var self = this;
710
711 function run(scheduler, state1) {
712 self.queue.remove(si);
713 return action(scheduler, state1);
714 }
715
716 var si = new ScheduledItem(this, state, run, dueTime, this.comparer);
717 this.queue.enqueue(si);
718
719 return si.disposable;
720 };
721
722 return VirtualTimeScheduler;
723 }(Scheduler));
724
725function OnNextPredicate(predicate) {
726 this.predicate = predicate;
727}
728
729OnNextPredicate.prototype.equals = function (other) {
730 if (other === this) { return true; }
731 if (other == null) { return false; }
732 if (other.kind !== 'N') { return false; }
733 return this.predicate(other.value);
734};
735
736function OnErrorPredicate(predicate) {
737 this.predicate = predicate;
738}
739
740OnErrorPredicate.prototype.equals = function (other) {
741 if (other === this) { return true; }
742 if (other == null) { return false; }
743 if (other.kind !== 'E') { return false; }
744 return this.predicate(other.error);
745};
746
747var ReactiveTest = Rx.ReactiveTest = {
748 /** Default virtual time used for creation of observable sequences in unit tests. */
749 created: 100,
750 /** Default virtual time used to subscribe to observable sequences in unit tests. */
751 subscribed: 200,
752 /** Default virtual time used to dispose subscriptions in unit tests. */
753 disposed: 1000,
754
755 /**
756 * Factory method for an OnNext notification record at a given time with a given value or a predicate function.
757 *
758 * 1 - ReactiveTest.onNext(200, 42);
759 * 2 - ReactiveTest.onNext(200, function (x) { return x.length == 2; });
760 *
761 * @param ticks Recorded virtual time the OnNext notification occurs.
762 * @param value Recorded value stored in the OnNext notification or a predicate.
763 * @return Recorded OnNext notification.
764 */
765 onNext: function (ticks, value) {
766 return typeof value === 'function' ?
767 new Recorded(ticks, new OnNextPredicate(value)) :
768 new Recorded(ticks, Notification.createOnNext(value));
769 },
770 /**
771 * Factory method for an OnError notification record at a given time with a given error.
772 *
773 * 1 - ReactiveTest.onNext(200, new Error('error'));
774 * 2 - ReactiveTest.onNext(200, function (e) { return e.message === 'error'; });
775 *
776 * @param ticks Recorded virtual time the OnError notification occurs.
777 * @param exception Recorded exception stored in the OnError notification.
778 * @return Recorded OnError notification.
779 */
780 onError: function (ticks, error) {
781 return typeof error === 'function' ?
782 new Recorded(ticks, new OnErrorPredicate(error)) :
783 new Recorded(ticks, Notification.createOnError(error));
784 },
785 /**
786 * Factory method for an OnCompleted notification record at a given time.
787 *
788 * @param ticks Recorded virtual time the OnCompleted notification occurs.
789 * @return Recorded OnCompleted notification.
790 */
791 onCompleted: function (ticks) {
792 return new Recorded(ticks, Notification.createOnCompleted());
793 },
794 /**
795 * Factory method for a subscription record based on a given subscription and disposal time.
796 *
797 * @param start Virtual time indicating when the subscription was created.
798 * @param end Virtual time indicating when the subscription was disposed.
799 * @return Subscription object.
800 */
801 subscribe: function (start, end) {
802 return new Subscription(start, end);
803 }
804};
805
806 /**
807 * Creates a new object recording the production of the specified value at the given virtual time.
808 *
809 * @constructor
810 * @param {Number} time Virtual time the value was produced on.
811 * @param {Mixed} value Value that was produced.
812 * @param {Function} comparer An optional comparer.
813 */
814 var Recorded = Rx.Recorded = function (time, value, comparer) {
815 this.time = time;
816 this.value = value;
817 this.comparer = comparer || defaultComparer;
818 };
819
820 /**
821 * Checks whether the given recorded object is equal to the current instance.
822 *
823 * @param {Recorded} other Recorded object to check for equality.
824 * @returns {Boolean} true if both objects are equal; false otherwise.
825 */
826 Recorded.prototype.equals = function (other) {
827 return this.time === other.time && this.comparer(this.value, other.value);
828 };
829
830 /**
831 * Returns a string representation of the current Recorded value.
832 *
833 * @returns {String} String representation of the current Recorded value.
834 */
835 Recorded.prototype.toString = function () {
836 return this.value.toString() + '@' + this.time;
837 };
838
839 /**
840 * Creates a new subscription object with the given virtual subscription and unsubscription time.
841 *
842 * @constructor
843 * @param {Number} subscribe Virtual time at which the subscription occurred.
844 * @param {Number} unsubscribe Virtual time at which the unsubscription occurred.
845 */
846 var Subscription = Rx.Subscription = function (start, end) {
847 this.subscribe = start;
848 this.unsubscribe = end || Number.MAX_VALUE;
849 };
850
851 /**
852 * Checks whether the given subscription is equal to the current instance.
853 * @param other Subscription object to check for equality.
854 * @returns {Boolean} true if both objects are equal; false otherwise.
855 */
856 Subscription.prototype.equals = function (other) {
857 return this.subscribe === other.subscribe && this.unsubscribe === other.unsubscribe;
858 };
859
860 /**
861 * Returns a string representation of the current Subscription value.
862 * @returns {String} String representation of the current Subscription value.
863 */
864 Subscription.prototype.toString = function () {
865 return '(' + this.subscribe + ', ' + (this.unsubscribe === Number.MAX_VALUE ? 'Infinite' : this.unsubscribe) + ')';
866 };
867
868 var MockDisposable = Rx.MockDisposable = function (scheduler) {
869 this.scheduler = scheduler;
870 this.disposes = [];
871 this.disposes.push(this.scheduler.clock);
872 };
873
874 MockDisposable.prototype.dispose = function () {
875 this.disposes.push(this.scheduler.clock);
876 };
877
878 var MockObserver = (function (__super__) {
879 inherits(MockObserver, __super__);
880
881 function MockObserver(scheduler) {
882 __super__.call(this);
883 this.scheduler = scheduler;
884 this.messages = [];
885 }
886
887 var MockObserverPrototype = MockObserver.prototype;
888
889 MockObserverPrototype.onNext = function (value) {
890 this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnNext(value)));
891 };
892
893 MockObserverPrototype.onError = function (e) {
894 this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnError(e)));
895 };
896
897 MockObserverPrototype.onCompleted = function () {
898 this.messages.push(new Recorded(this.scheduler.clock, Notification.createOnCompleted()));
899 };
900
901 return MockObserver;
902 })(Observer);
903
904 function MockPromise(scheduler, messages) {
905 var self = this;
906 this.scheduler = scheduler;
907 this.messages = messages;
908 this.subscriptions = [];
909 this.observers = [];
910 for (var i = 0, len = this.messages.length; i < len; i++) {
911 var message = this.messages[i],
912 notification = message.value;
913 (function (innerNotification) {
914 scheduler.scheduleAbsolute(null, message.time, function () {
915 var obs = self.observers.slice(0);
916
917 for (var j = 0, jLen = obs.length; j < jLen; j++) {
918 innerNotification.accept(obs[j]);
919 }
920 return disposableEmpty;
921 });
922 })(notification);
923 }
924 }
925
926 MockPromise.prototype.then = function (onResolved, onRejected) {
927 var self = this;
928
929 this.subscriptions.push(new Subscription(this.scheduler.clock));
930 var index = this.subscriptions.length - 1;
931
932 var newPromise;
933
934 var observer = Rx.Observer.create(
935 function (x) {
936 var retValue = onResolved(x);
937 if (retValue && typeof retValue.then === 'function') {
938 newPromise = retValue;
939 } else {
940 var ticks = self.scheduler.clock;
941 newPromise = new MockPromise(self.scheduler, [Rx.ReactiveTest.onNext(ticks, undefined), Rx.ReactiveTest.onCompleted(ticks)]);
942 }
943 var idx = self.observers.indexOf(observer);
944 self.observers.splice(idx, 1);
945 self.subscriptions[index] = new Subscription(self.subscriptions[index].subscribe, self.scheduler.clock);
946 },
947 function (err) {
948 onRejected(err);
949 var idx = self.observers.indexOf(observer);
950 self.observers.splice(idx, 1);
951 self.subscriptions[index] = new Subscription(self.subscriptions[index].subscribe, self.scheduler.clock);
952 }
953 );
954 this.observers.push(observer);
955
956 return newPromise || new MockPromise(this.scheduler, this.messages);
957 };
958
959 var HotObservable = (function (__super__) {
960 inherits(HotObservable, __super__);
961
962 function HotObservable(scheduler, messages) {
963 __super__.call(this);
964 var message, notification, observable = this;
965 this.scheduler = scheduler;
966 this.messages = messages;
967 this.subscriptions = [];
968 this.observers = [];
969 for (var i = 0, len = this.messages.length; i < len; i++) {
970 message = this.messages[i];
971 notification = message.value;
972 (function (innerNotification) {
973 scheduler.scheduleAbsolute(null, message.time, function () {
974 var obs = observable.observers.slice(0);
975
976 for (var j = 0, jLen = obs.length; j < jLen; j++) {
977 innerNotification.accept(obs[j]);
978 }
979 return disposableEmpty;
980 });
981 })(notification);
982 }
983 }
984
985 HotObservable.prototype._subscribe = function (o) {
986 var observable = this;
987 this.observers.push(o);
988 this.subscriptions.push(new Subscription(this.scheduler.clock));
989 var index = this.subscriptions.length - 1;
990 return disposableCreate(function () {
991 var idx = observable.observers.indexOf(o);
992 observable.observers.splice(idx, 1);
993 observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
994 });
995 };
996
997 return HotObservable;
998 })(Observable);
999
1000 var ColdObservable = (function (__super__) {
1001 inherits(ColdObservable, __super__);
1002
1003 function ColdObservable(scheduler, messages) {
1004 __super__.call(this);
1005 this.scheduler = scheduler;
1006 this.messages = messages;
1007 this.subscriptions = [];
1008 }
1009
1010 ColdObservable.prototype._subscribe = function (o) {
1011 var message, notification, observable = this;
1012 this.subscriptions.push(new Subscription(this.scheduler.clock));
1013 var index = this.subscriptions.length - 1;
1014 var d = new CompositeDisposable();
1015 for (var i = 0, len = this.messages.length; i < len; i++) {
1016 message = this.messages[i];
1017 notification = message.value;
1018 (function (innerNotification) {
1019 d.add(observable.scheduler.scheduleRelative(null, message.time, function () {
1020 innerNotification.accept(o);
1021 return disposableEmpty;
1022 }));
1023 })(notification);
1024 }
1025 return disposableCreate(function () {
1026 observable.subscriptions[index] = new Subscription(observable.subscriptions[index].subscribe, observable.scheduler.clock);
1027 d.dispose();
1028 });
1029 };
1030
1031 return ColdObservable;
1032 })(Observable);
1033
1034 /** Virtual time scheduler used for testing applications and libraries built using Reactive Extensions. */
1035 Rx.TestScheduler = (function (__super__) {
1036 inherits(TestScheduler, __super__);
1037
1038 function baseComparer(x, y) {
1039 return x > y ? 1 : (x < y ? -1 : 0);
1040 }
1041
1042 function TestScheduler() {
1043 __super__.call(this, 0, baseComparer);
1044 }
1045
1046 /**
1047 * Schedules an action to be executed at the specified virtual time.
1048 *
1049 * @param state State passed to the action to be executed.
1050 * @param dueTime Absolute virtual time at which to execute the action.
1051 * @param action Action to be executed.
1052 * @return Disposable object used to cancel the scheduled action (best effort).
1053 */
1054 TestScheduler.prototype.scheduleAbsolute = function (state, dueTime, action) {
1055 dueTime <= this.clock && (dueTime = this.clock + 1);
1056 return __super__.prototype.scheduleAbsolute.call(this, state, dueTime, action);
1057 };
1058 /**
1059 * Adds a relative virtual time to an absolute virtual time value.
1060 *
1061 * @param absolute Absolute virtual time value.
1062 * @param relative Relative virtual time value to add.
1063 * @return Resulting absolute virtual time sum value.
1064 */
1065 TestScheduler.prototype.add = function (absolute, relative) {
1066 return absolute + relative;
1067 };
1068 /**
1069 * Converts the absolute virtual time value to a DateTimeOffset value.
1070 *
1071 * @param absolute Absolute virtual time value to convert.
1072 * @return Corresponding DateTimeOffset value.
1073 */
1074 TestScheduler.prototype.toAbsoluteTime = function (absolute) {
1075 return new Date(absolute).getTime();
1076 };
1077 /**
1078 * Converts the TimeSpan value to a relative virtual time value.
1079 *
1080 * @param timeSpan TimeSpan value to convert.
1081 * @return Corresponding relative virtual time value.
1082 */
1083 TestScheduler.prototype.toRelativeTime = function (timeSpan) {
1084 return timeSpan;
1085 };
1086 /**
1087 * Starts the test scheduler and uses the specified virtual times to invoke the factory function, subscribe to the resulting sequence, and dispose the subscription.
1088 *
1089 * @param create Factory method to create an observable sequence.
1090 * @param created Virtual time at which to invoke the factory to create an observable sequence.
1091 * @param subscribed Virtual time at which to subscribe to the created observable sequence.
1092 * @param disposed Virtual time at which to dispose the subscription.
1093 * @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.
1094 */
1095 TestScheduler.prototype.startScheduler = function (createFn, settings) {
1096 settings || (settings = {});
1097 settings.created == null && (settings.created = ReactiveTest.created);
1098 settings.subscribed == null && (settings.subscribed = ReactiveTest.subscribed);
1099 settings.disposed == null && (settings.disposed = ReactiveTest.disposed);
1100
1101 var observer = this.createObserver(), source, subscription;
1102
1103 this.scheduleAbsolute(null, settings.created, function () {
1104 source = createFn();
1105 return disposableEmpty;
1106 });
1107
1108 this.scheduleAbsolute(null, settings.subscribed, function () {
1109 subscription = source.subscribe(observer);
1110 return disposableEmpty;
1111 });
1112
1113 this.scheduleAbsolute(null, settings.disposed, function () {
1114 subscription.dispose();
1115 return disposableEmpty;
1116 });
1117
1118 this.start();
1119
1120 return observer;
1121 };
1122
1123 /**
1124 * Creates a hot observable using the specified timestamped notification messages either as an array or arguments.
1125 * @param messages Notifications to surface through the created sequence at their specified absolute virtual times.
1126 * @return Hot observable sequence that can be used to assert the timing of subscriptions and notifications.
1127 */
1128 TestScheduler.prototype.createHotObservable = function () {
1129 var len = arguments.length, args;
1130 if (Array.isArray(arguments[0])) {
1131 args = arguments[0];
1132 } else {
1133 args = new Array(len);
1134 for (var i = 0; i < len; i++) { args[i] = arguments[i]; }
1135 }
1136 return new HotObservable(this, args);
1137 };
1138
1139 /**
1140 * Creates a cold observable using the specified timestamped notification messages either as an array or arguments.
1141 * @param messages Notifications to surface through the created sequence at their specified virtual time offsets from the sequence subscription time.
1142 * @return Cold observable sequence that can be used to assert the timing of subscriptions and notifications.
1143 */
1144 TestScheduler.prototype.createColdObservable = function () {
1145 var len = arguments.length, args;
1146 if (Array.isArray(arguments[0])) {
1147 args = arguments[0];
1148 } else {
1149 args = new Array(len);
1150 for (var i = 0; i < len; i++) { args[i] = arguments[i]; }
1151 }
1152 return new ColdObservable(this, args);
1153 };
1154
1155 /**
1156 * Creates a resolved promise with the given value and ticks
1157 * @param {Number} ticks The absolute time of the resolution.
1158 * @param {Any} value The value to yield at the given tick.
1159 * @returns {MockPromise} A mock Promise which fulfills with the given value.
1160 */
1161 TestScheduler.prototype.createResolvedPromise = function (ticks, value) {
1162 return new MockPromise(this, [Rx.ReactiveTest.onNext(ticks, value), Rx.ReactiveTest.onCompleted(ticks)]);
1163 };
1164
1165 /**
1166 * Creates a rejected promise with the given reason and ticks
1167 * @param {Number} ticks The absolute time of the resolution.
1168 * @param {Any} reason The reason for rejection to yield at the given tick.
1169 * @returns {MockPromise} A mock Promise which rejects with the given reason.
1170 */
1171 TestScheduler.prototype.createRejectedPromise = function (ticks, reason) {
1172 return new MockPromise(this, [Rx.ReactiveTest.onError(ticks, reason)]);
1173 };
1174
1175 /**
1176 * Creates an observer that records received notification messages and timestamps those.
1177 * @return Observer that can be used to assert the timing of received notifications.
1178 */
1179 TestScheduler.prototype.createObserver = function () {
1180 return new MockObserver(this);
1181 };
1182
1183 return TestScheduler;
1184 })(VirtualTimeScheduler);
1185
1186 return Rx;
1187}));