UNPKG

18.4 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.binding', 'exports'], function (Rx, exports) {
25 root.Rx = factory(root, exports, Rx);
26 return root.Rx;
27 });
28 } else if (typeof module === 'object' && module && module.exports === freeExports) {
29 module.exports = factory(root, module.exports, require('./rx'));
30 } else {
31 root.Rx = factory(root, {}, root.Rx);
32 }
33}.call(this, function (root, exp, Rx, undefined) {
34
35 // Aliases
36 var Observable = Rx.Observable,
37 observableFromPromise = Observable.fromPromise,
38 observableThrow = Observable.throwError,
39 AnonymousObservable = Rx.AnonymousObservable,
40 ObservableBase = Rx.ObservableBase,
41 AsyncSubject = Rx.AsyncSubject,
42 disposableCreate = Rx.Disposable.create,
43 CompositeDisposable = Rx.CompositeDisposable,
44 immediateScheduler = Rx.Scheduler.immediate,
45 defaultScheduler = Rx.Scheduler['default'],
46 inherits = Rx.internals.inherits,
47 isScheduler = Rx.Scheduler.isScheduler,
48 isPromise = Rx.helpers.isPromise,
49 isFunction = Rx.helpers.isFunction,
50 isIterable = Rx.helpers.isIterable,
51 isArrayLike = Rx.helpers.isArrayLike;
52
53 var errorObj = {e: {}};
54
55 function tryCatcherGen(tryCatchTarget) {
56 return function tryCatcher() {
57 try {
58 return tryCatchTarget.apply(this, arguments);
59 } catch (e) {
60 errorObj.e = e;
61 return errorObj;
62 }
63 };
64 }
65
66 var tryCatch = Rx.internals.tryCatch = function tryCatch(fn) {
67 if (!isFunction(fn)) { throw new TypeError('fn must be a function'); }
68 return tryCatcherGen(fn);
69 };
70
71 function thrower(e) {
72 throw e;
73 }
74
75 Observable.wrap = function (fn) {
76 function createObservable() {
77 return Observable.spawn.call(this, fn.apply(this, arguments));
78 }
79
80 createObservable.__generatorFunction__ = fn;
81 return createObservable;
82 };
83
84 var spawn = Observable.spawn = function () {
85 var gen = arguments[0], self = this, args = [];
86 for (var i = 1, len = arguments.length; i < len; i++) { args.push(arguments[i]); }
87
88 return new AnonymousObservable(function (o) {
89 var g = new CompositeDisposable();
90
91 if (isFunction(gen)) { gen = gen.apply(self, args); }
92 if (!gen || !isFunction(gen.next)) {
93 o.onNext(gen);
94 return o.onCompleted();
95 }
96
97 function processGenerator(res) {
98 var ret = tryCatch(gen.next).call(gen, res);
99 if (ret === errorObj) { return o.onError(ret.e); }
100 next(ret);
101 }
102
103 processGenerator();
104
105 function onError(err) {
106 var ret = tryCatch(gen.next).call(gen, err);
107 if (ret === errorObj) { return o.onError(ret.e); }
108 next(ret);
109 }
110
111 function next(ret) {
112 if (ret.done) {
113 o.onNext(ret.value);
114 o.onCompleted();
115 return;
116 }
117 var obs = toObservable.call(self, ret.value);
118 var value = null;
119 var hasValue = false;
120 if (Observable.isObservable(obs)) {
121 g.add(obs.subscribe(function(val) {
122 hasValue = true;
123 value = val;
124 }, onError, function() {
125 hasValue && processGenerator(value);
126 }));
127 } else {
128 onError(new TypeError('type not supported'));
129 }
130 }
131
132 return g;
133 });
134 };
135
136 function toObservable(obj) {
137 if (!obj) { return obj; }
138 if (Observable.isObservable(obj)) { return obj; }
139 if (isPromise(obj)) { return Observable.fromPromise(obj); }
140 if (isGeneratorFunction(obj) || isGenerator(obj)) { return spawn.call(this, obj); }
141 if (isFunction(obj)) { return thunkToObservable.call(this, obj); }
142 if (isArrayLike(obj) || isIterable(obj)) { return arrayToObservable.call(this, obj); }
143 if (isObject(obj)) {return objectToObservable.call(this, obj);}
144 return obj;
145 }
146
147 function arrayToObservable (obj) {
148 return Observable.from(obj).concatMap(function(o) {
149 if(Observable.isObservable(o) || isObject(o)) {
150 return toObservable.call(null, o);
151 } else {
152 return Rx.Observable.just(o);
153 }
154 }).toArray();
155 }
156
157 function objectToObservable (obj) {
158 var results = new obj.constructor(), keys = Object.keys(obj), observables = [];
159 for (var i = 0, len = keys.length; i < len; i++) {
160 var key = keys[i];
161 var observable = toObservable.call(this, obj[key]);
162
163 if(observable && Observable.isObservable(observable)) {
164 defer(observable, key);
165 } else {
166 results[key] = obj[key];
167 }
168 }
169
170 return Observable.forkJoin.apply(Observable, observables).map(function() {
171 return results;
172 });
173
174
175 function defer (observable, key) {
176 results[key] = undefined;
177 observables.push(observable.map(function (next) {
178 results[key] = next;
179 }));
180 }
181 }
182
183 function thunkToObservable(fn) {
184 var self = this;
185 return new AnonymousObservable(function (o) {
186 fn.call(self, function () {
187 var err = arguments[0], res = arguments[1];
188 if (err) { return o.onError(err); }
189 if (arguments.length > 2) {
190 var args = [];
191 for (var i = 1, len = arguments.length; i < len; i++) { args.push(arguments[i]); }
192 res = args;
193 }
194 o.onNext(res);
195 o.onCompleted();
196 });
197 });
198 }
199
200 function isGenerator(obj) {
201 return isFunction (obj.next) && isFunction (obj['throw']);
202 }
203
204 function isGeneratorFunction(obj) {
205 var ctor = obj.constructor;
206 if (!ctor) { return false; }
207 if (ctor.name === 'GeneratorFunction' || ctor.displayName === 'GeneratorFunction') { return true; }
208 return isGenerator(ctor.prototype);
209 }
210
211 function isObject(val) {
212 return Object == val.constructor;
213 }
214
215 /**
216 * Invokes the specified function asynchronously on the specified scheduler, surfacing the result through an observable sequence.
217 *
218 * @example
219 * var res = Rx.Observable.start(function () { console.log('hello'); });
220 * var res = Rx.Observable.start(function () { console.log('hello'); }, Rx.Scheduler.timeout);
221 * var res = Rx.Observable.start(function () { this.log('hello'); }, Rx.Scheduler.timeout, console);
222 *
223 * @param {Function} func Function to run asynchronously.
224 * @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
225 * @param [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
226 * @returns {Observable} An observable sequence exposing the function's result value, or an exception.
227 *
228 * Remarks
229 * * The function is called immediately, not during the subscription of the resulting sequence.
230 * * Multiple subscriptions to the resulting sequence can observe the function's result.
231 */
232 Observable.start = function (func, context, scheduler) {
233 return observableToAsync(func, context, scheduler)();
234 };
235
236 /**
237 * Converts the function into an asynchronous function. Each invocation of the resulting asynchronous function causes an invocation of the original synchronous function on the specified scheduler.
238 * @param {Function} function Function to convert to an asynchronous function.
239 * @param {Scheduler} [scheduler] Scheduler to run the function on. If not specified, defaults to Scheduler.timeout.
240 * @param {Mixed} [context] The context for the func parameter to be executed. If not specified, defaults to undefined.
241 * @returns {Function} Asynchronous function.
242 */
243 var observableToAsync = Observable.toAsync = function (func, context, scheduler) {
244 isScheduler(scheduler) || (scheduler = defaultScheduler);
245 return function () {
246 var args = arguments,
247 subject = new AsyncSubject();
248
249 scheduler.schedule(null, function () {
250 var result;
251 try {
252 result = func.apply(context, args);
253 } catch (e) {
254 subject.onError(e);
255 return;
256 }
257 subject.onNext(result);
258 subject.onCompleted();
259 });
260 return subject.asObservable();
261 };
262 };
263
264function createCbObservable(fn, ctx, selector, args) {
265 var o = new AsyncSubject();
266
267 args.push(createCbHandler(o, ctx, selector));
268 fn.apply(ctx, args);
269
270 return o.asObservable();
271}
272
273function createCbHandler(o, ctx, selector) {
274 return function handler () {
275 var len = arguments.length, results = new Array(len);
276 for(var i = 0; i < len; i++) { results[i] = arguments[i]; }
277
278 if (isFunction(selector)) {
279 results = tryCatch(selector).apply(ctx, results);
280 if (results === errorObj) { return o.onError(results.e); }
281 o.onNext(results);
282 } else {
283 if (results.length <= 1) {
284 o.onNext(results[0]);
285 } else {
286 o.onNext(results);
287 }
288 }
289
290 o.onCompleted();
291 };
292}
293
294/**
295 * Converts a callback function to an observable sequence.
296 *
297 * @param {Function} fn Function with a callback as the last parameter to convert to an Observable sequence.
298 * @param {Mixed} [ctx] The context for the func parameter to be executed. If not specified, defaults to undefined.
299 * @param {Function} [selector] A selector which takes the arguments from the callback to produce a single item to yield on next.
300 * @returns {Function} A function, when executed with the required parameters minus the callback, produces an Observable sequence with a single value of the arguments to the callback as an array.
301 */
302Observable.fromCallback = function (fn, ctx, selector) {
303 return function () {
304 typeof ctx === 'undefined' && (ctx = this);
305
306 var len = arguments.length, args = new Array(len)
307 for(var i = 0; i < len; i++) { args[i] = arguments[i]; }
308 return createCbObservable(fn, ctx, selector, args);
309 };
310};
311
312function createNodeObservable(fn, ctx, selector, args) {
313 var o = new AsyncSubject();
314
315 args.push(createNodeHandler(o, ctx, selector));
316 fn.apply(ctx, args);
317
318 return o.asObservable();
319}
320
321function createNodeHandler(o, ctx, selector) {
322 return function handler () {
323 var err = arguments[0];
324 if (err) { return o.onError(err); }
325
326 var len = arguments.length, results = [];
327 for(var i = 1; i < len; i++) { results[i - 1] = arguments[i]; }
328
329 if (isFunction(selector)) {
330 var results = tryCatch(selector).apply(ctx, results);
331 if (results === errorObj) { return o.onError(results.e); }
332 o.onNext(results);
333 } else {
334 if (results.length <= 1) {
335 o.onNext(results[0]);
336 } else {
337 o.onNext(results);
338 }
339 }
340
341 o.onCompleted();
342 };
343}
344
345/**
346 * Converts a Node.js callback style function to an observable sequence. This must be in function (err, ...) format.
347 * @param {Function} fn The function to call
348 * @param {Mixed} [ctx] The context for the func parameter to be executed. If not specified, defaults to undefined.
349 * @param {Function} [selector] A selector which takes the arguments from the callback minus the error to produce a single item to yield on next.
350 * @returns {Function} An async function which when applied, returns an observable sequence with the callback arguments as an array.
351 */
352Observable.fromNodeCallback = function (fn, ctx, selector) {
353 return function () {
354 typeof ctx === 'undefined' && (ctx = this);
355 var len = arguments.length, args = new Array(len);
356 for(var i = 0; i < len; i++) { args[i] = arguments[i]; }
357 return createNodeObservable(fn, ctx, selector, args);
358 };
359};
360
361 function isNodeList(el) {
362 if (root.StaticNodeList) {
363 // IE8 Specific
364 // instanceof is slower than Object#toString, but Object#toString will not work as intended in IE8
365 return el instanceof root.StaticNodeList || el instanceof root.NodeList;
366 } else {
367 return Object.prototype.toString.call(el) === '[object NodeList]';
368 }
369 }
370
371 function ListenDisposable(e, n, fn) {
372 this._e = e;
373 this._n = n;
374 this._fn = fn;
375 this._e.addEventListener(this._n, this._fn, false);
376 this.isDisposed = false;
377 }
378 ListenDisposable.prototype.dispose = function () {
379 if (!this.isDisposed) {
380 this._e.removeEventListener(this._n, this._fn, false);
381 this.isDisposed = true;
382 }
383 };
384
385 function createEventListener (el, eventName, handler) {
386 var disposables = new CompositeDisposable();
387
388 // Asume NodeList or HTMLCollection
389 var elemToString = Object.prototype.toString.call(el);
390 if (isNodeList(el) || elemToString === '[object HTMLCollection]') {
391 for (var i = 0, len = el.length; i < len; i++) {
392 disposables.add(createEventListener(el.item(i), eventName, handler));
393 }
394 } else if (el) {
395 disposables.add(new ListenDisposable(el, eventName, handler));
396 }
397
398 return disposables;
399 }
400
401 /**
402 * Configuration option to determine whether to use native events only
403 */
404 Rx.config.useNativeEvents = false;
405
406 var EventObservable = (function(__super__) {
407 inherits(EventObservable, __super__);
408 function EventObservable(el, name, fn) {
409 this._el = el;
410 this._n = name;
411 this._fn = fn;
412 __super__.call(this);
413 }
414
415 function createHandler(o, fn) {
416 return function handler () {
417 var results = arguments[0];
418 if (isFunction(fn)) {
419 results = tryCatch(fn).apply(null, arguments);
420 if (results === errorObj) { return o.onError(results.e); }
421 }
422 o.onNext(results);
423 };
424 }
425
426 EventObservable.prototype.subscribeCore = function (o) {
427 return createEventListener(
428 this._el,
429 this._n,
430 createHandler(o, this._fn));
431 };
432
433 return EventObservable;
434 }(ObservableBase));
435
436 /**
437 * Creates an observable sequence by adding an event listener to the matching DOMElement or each item in the NodeList.
438 * @param {Object} element The DOMElement or NodeList to attach a listener.
439 * @param {String} eventName The event name to attach the observable sequence.
440 * @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
441 * @returns {Observable} An observable sequence of events from the specified element and the specified event.
442 */
443 Observable.fromEvent = function (element, eventName, selector) {
444 // Node.js specific
445 if (element.addListener) {
446 return fromEventPattern(
447 function (h) { element.addListener(eventName, h); },
448 function (h) { element.removeListener(eventName, h); },
449 selector);
450 }
451
452 // Use only if non-native events are allowed
453 if (!Rx.config.useNativeEvents) {
454 // Handles jq, Angular.js, Zepto, Marionette, Ember.js
455 if (typeof element.on === 'function' && typeof element.off === 'function') {
456 return fromEventPattern(
457 function (h) { element.on(eventName, h); },
458 function (h) { element.off(eventName, h); },
459 selector);
460 }
461 }
462
463 return new EventObservable(element, eventName, selector).publish().refCount();
464 };
465
466 var EventPatternObservable = (function(__super__) {
467 inherits(EventPatternObservable, __super__);
468 function EventPatternObservable(add, del, fn) {
469 this._add = add;
470 this._del = del;
471 this._fn = fn;
472 __super__.call(this);
473 }
474
475 function createHandler(o, fn) {
476 return function handler () {
477 var results = arguments[0];
478 if (isFunction(fn)) {
479 results = tryCatch(fn).apply(null, arguments);
480 if (results === errorObj) { return o.onError(results.e); }
481 }
482 o.onNext(results);
483 };
484 }
485
486 EventPatternObservable.prototype.subscribeCore = function (o) {
487 var fn = createHandler(o, this._fn);
488 var returnValue = this._add(fn);
489 return new EventPatternDisposable(this._del, fn, returnValue);
490 };
491
492 function EventPatternDisposable(del, fn, ret) {
493 this._del = del;
494 this._fn = fn;
495 this._ret = ret;
496 this.isDisposed = false;
497 }
498
499 EventPatternDisposable.prototype.dispose = function () {
500 if(!this.isDisposed) {
501 isFunction(this._del) && this._del(this._fn, this._ret);
502 }
503 };
504
505 return EventPatternObservable;
506 }(ObservableBase));
507
508 /**
509 * Creates an observable sequence from an event emitter via an addHandler/removeHandler pair.
510 * @param {Function} addHandler The function to add a handler to the emitter.
511 * @param {Function} [removeHandler] The optional function to remove a handler from an emitter.
512 * @param {Function} [selector] A selector which takes the arguments from the event handler to produce a single item to yield on next.
513 * @returns {Observable} An observable sequence which wraps an event from an event emitter
514 */
515 var fromEventPattern = Observable.fromEventPattern = function (addHandler, removeHandler, selector) {
516 return new EventPatternObservable(addHandler, removeHandler, selector).publish().refCount();
517 };
518
519 /**
520 * Invokes the asynchronous function, surfacing the result through an observable sequence.
521 * @param {Function} functionAsync Asynchronous function which returns a Promise to run.
522 * @returns {Observable} An observable sequence exposing the function's result value, or an exception.
523 */
524 Observable.startAsync = function (functionAsync) {
525 var promise = tryCatch(functionAsync)();
526 if (promise === errorObj) { return observableThrow(promise.e); }
527 return observableFromPromise(promise);
528 };
529
530 return Rx;
531}));