UNPKG

baconjs

Version:

A small functional reactive programming lib for JavaScript.

1,679 lines (1,631 loc) 179 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.Bacon = {})); }(this, (function (exports) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } function __spreadArrays() { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; } /** @hidden */ function nop() { } /** @hidden */ var isArray = Array.isArray || function (xs) { return xs instanceof Array; }; /** @hidden */ function isObservable(x) { return x && x._isObservable; } /** @hidden */ function all(xs, f) { for (var i = 0, x; i < xs.length; i++) { x = xs[i]; if (!f(x)) { return false; } } return true; } /** @hidden */ function always(x) { return function () { return x; }; } /** @hidden */ function any(xs, f) { for (var i = 0, x; i < xs.length; i++) { x = xs[i]; if (f(x)) { return true; } } return false; } /** @hidden */ function bind(fn, me) { return function () { return fn.apply(me, arguments); }; } /** @hidden */ function contains(xs, x) { return indexOf(xs, x) !== -1; } /** @hidden */ function each(xs, f) { for (var key in xs) { if (Object.prototype.hasOwnProperty.call(xs, key)) { var value = xs[key]; f(key, value); } } } /** @hidden */ function empty(xs) { return xs.length === 0; } /** @hidden */ function filter(f, xs) { var filtered = []; for (var i = 0, x; i < xs.length; i++) { x = xs[i]; if (f(x)) { filtered.push(x); } } return filtered; } /** @hidden */ function flatMap(f, xs) { return fold(xs, [], (function (ys, x) { return ys.concat(f(x)); })); } /** @hidden */ function flip(f) { return function (a, b) { return f(b, a); }; } /** @hidden */ function fold(xs, seed, f) { for (var i = 0, x; i < xs.length; i++) { x = xs[i]; seed = f(seed, x); } return seed; } /** @hidden */ function head(xs) { return xs[0]; } /** @hidden */ function id(x) { return x; } /** @hidden */ function indexOfDefault(xs, x) { return xs.indexOf(x); } /** @hidden */ function indexOfFallback(xs, x) { for (var i = 0, y; i < xs.length; i++) { y = xs[i]; if (x === y) { return i; } } return -1; } /** @hidden */ var indexOf = Array.prototype.indexOf ? indexOfDefault : indexOfFallback; /** @hidden */ function indexWhere(xs, f) { for (var i = 0, y; i < xs.length; i++) { y = xs[i]; if (f(y)) { return i; } } return -1; } /** @hidden */ function isFunction(f) { return typeof f === "function"; } /** @hidden */ function last(xs) { return xs[xs.length - 1]; } /** @hidden */ function map(f, xs) { var result = []; for (var i = 0, x; i < xs.length; i++) { x = xs[i]; result.push(f(x)); } return result; } /** @hidden */ function negate(f) { return function (x) { return !f(x); }; } /** @hidden */ function remove(x, xs) { var i = indexOf(xs, x); if (i >= 0) { return xs.splice(i, 1); } } /** @hidden */ function tail(xs) { return xs.slice(1, xs.length); } /** @hidden */ function toArray(xs) { return (isArray(xs) ? xs : [xs]); } /** @hidden */ function toFunction(f) { if (typeof f == "function") { return f; } return function (x) { return f; }; } /** @hidden */ function toString(obj) { var hasProp = {}.hasOwnProperty; try { recursionDepth++; if (obj == null) { return "undefined"; } else if (isFunction(obj)) { return "function"; } else if (isArray(obj)) { if (recursionDepth > 5) { return "[..]"; } return "[" + map(toString, obj).toString() + "]"; } else if (((obj != null ? obj.toString : void 0) != null) && obj.toString !== Object.prototype.toString) { return obj.toString(); } else if (typeof obj === "object") { if (recursionDepth > 5) { return "{..}"; } var results = []; for (var key in obj) { if (!hasProp.call(obj, key)) continue; var value = (function () { try { return obj[key]; } catch (error) { return error; } })(); results.push(toString(key) + ":" + toString(value)); } return "{" + results + "}"; } else { return obj; } } finally { recursionDepth--; } } /** @hidden */ function without(x, xs) { return filter((function (y) { return y !== x; }), xs); } var _ = { indexOf: indexOf, indexWhere: indexWhere, head: head, always: always, negate: negate, empty: empty, tail: tail, filter: filter, map: map, each: each, toArray: toArray, contains: contains, id: id, last: last, all: all, any: any, without: without, remove: remove, fold: fold, flatMap: flatMap, bind: bind, isFunction: isFunction, toFunction: toFunction, toString: toString }; var recursionDepth = 0; /** * Reply for "more data, please". */ var more = undefined; /** * Reply for "no more data, please". */ var noMore = "<no-more>"; /** @hidden */ function assert(message, condition) { if (!condition) { throw new Error(message); } } /** @hidden */ function assertEventStream(event) { if (!(event != null ? event._isEventStream : void 0)) { throw new Error("not an EventStream : " + event); } } /** @hidden */ function assertObservable(observable) { if (!(observable != null ? observable._isObservable : void 0)) { throw new Error("not an Observable : " + observable); } } /** @hidden */ function assertFunction(f) { return assert("not a function : " + f, _.isFunction(f)); } /** @hidden */ function assertArray(xs) { if (!isArray(xs)) { throw new Error("not an array : " + xs); } } /** @hidden */ function assertNoArguments(args) { return assert("no arguments supported", args.length === 0); } /** @hidden */ var defaultScheduler = { setTimeout: function (f, d) { return setTimeout(f, d); }, setInterval: function (f, i) { return setInterval(f, i); }, clearInterval: function (id) { return clearInterval(id); }, clearTimeout: function (id) { return clearTimeout(id); }, now: function () { return new Date().getTime(); } }; var GlobalScheduler = { scheduler: defaultScheduler }; function getScheduler() { return GlobalScheduler.scheduler; } function setScheduler(newScheduler) { GlobalScheduler.scheduler = newScheduler; } var rootEvent = undefined; var waiterObs = []; var waiters = {}; var aftersStack = []; var aftersStackHeight = 0; var flushed = {}; var processingAfters = false; function toString$1() { return _.toString({ rootEvent: rootEvent, processingAfters: processingAfters, waiterObs: waiterObs, waiters: waiters, aftersStack: aftersStack, aftersStackHeight: aftersStackHeight, flushed: flushed }); } function ensureStackHeight(h) { if (h <= aftersStackHeight) return; if (!aftersStack[h - 1]) { aftersStack[h - 1] = [[], 0]; } aftersStackHeight = h; } function isInTransaction() { return rootEvent !== undefined; } function soonButNotYet(obs, f) { if (rootEvent) { // If in transaction -> perform within transaction //console.log('in tx') whenDoneWith(obs, f); } else { // Otherwise -> perform with timeout //console.log('with timeout') GlobalScheduler.scheduler.setTimeout(f, 0); } } function afterTransaction(obs, f) { if (rootEvent || processingAfters) { ensureStackHeight(1); var stackIndexForThisObs = 0; while (stackIndexForThisObs < aftersStackHeight - 1) { if (containsObs(obs, aftersStack[stackIndexForThisObs][0])) { // this observable is already being processed at this stack index -> use this index break; } stackIndexForThisObs++; } var listFromStack = aftersStack[stackIndexForThisObs][0]; listFromStack.push([obs, f]); if (!rootEvent) { processAfters(); // wouldn't be called otherwise } } else { return f(); } } function containsObs(obs, aftersList) { for (var i = 0; i < aftersList.length; i++) { if (aftersList[i][0].id == obs.id) return true; } return false; } function processAfters() { var stackSizeAtStart = aftersStackHeight; if (!stackSizeAtStart) return; var isRoot = !processingAfters; processingAfters = true; try { while (aftersStackHeight >= stackSizeAtStart) { // to prevent sinking to levels started by others var topOfStack = aftersStack[aftersStackHeight - 1]; if (!topOfStack) throw new Error("Unexpected stack top: " + topOfStack); var topAfters = topOfStack[0], index = topOfStack[1]; if (index < topAfters.length) { var _a = topAfters[index], after = _a[1]; topOfStack[1]++; // increase index already here to indicate that this level is being processed ensureStackHeight(aftersStackHeight + 1); // to ensure there's a new level for recursively added afters var callSuccess = false; try { after(); callSuccess = true; while (aftersStackHeight > stackSizeAtStart && aftersStack[aftersStackHeight - 1][0].length == 0) { aftersStackHeight--; } } finally { if (!callSuccess) { aftersStack = []; aftersStackHeight = 0; // reset state to prevent stale updates after error } } } else { topOfStack[0] = []; topOfStack[1] = 0; // reset this level break; } } } finally { if (isRoot) processingAfters = false; } } function whenDoneWith(obs, f) { if (rootEvent) { var obsWaiters = waiters[obs.id]; if (obsWaiters === undefined) { obsWaiters = waiters[obs.id] = [f]; return waiterObs.push(obs); } else { return obsWaiters.push(f); } } else { return f(); } } function flush() { while (waiterObs.length > 0) { flushWaiters(0, true); } flushed = {}; } function flushWaiters(index, deps) { var obs = waiterObs[index]; var obsId = obs.id; var obsWaiters = waiters[obsId]; waiterObs.splice(index, 1); delete waiters[obsId]; if (deps && waiterObs.length > 0) { flushDepsOf(obs); } for (var i = 0, f; i < obsWaiters.length; i++) { f = obsWaiters[i]; f(); } } function flushDepsOf(obs) { if (flushed[obs.id]) return; var deps = obs.internalDeps(); for (var i = 0, dep; i < deps.length; i++) { dep = deps[i]; flushDepsOf(dep); if (waiters[dep.id]) { var index = _.indexOf(waiterObs, dep); flushWaiters(index, false); } } flushed[obs.id] = true; } function inTransaction(event, context, f, args) { if (rootEvent) { //console.log("in tx") return f.apply(context, args); } else { //console.log("start tx") rootEvent = event; try { var result = f.apply(context, args); //console.log("done with tx") flush(); } finally { rootEvent = undefined; processAfters(); } return result; } } function currentEventId() { return rootEvent ? rootEvent.id : undefined; } function wrappedSubscribe(obs, subscribe, sink) { assertFunction(sink); var unsubd = false; var shouldUnsub = false; var doUnsub = function () { shouldUnsub = true; }; var unsub = function () { unsubd = true; doUnsub(); }; doUnsub = subscribe(function (event) { afterTransaction(obs, function () { if (!unsubd) { var reply = sink(event); if (reply === noMore) { unsub(); } } }); return more; }); if (shouldUnsub) { doUnsub(); } return unsub; } function hasWaiters() { return waiterObs.length > 0; } var UpdateBarrier = { toString: toString$1, whenDoneWith: whenDoneWith, hasWaiters: hasWaiters, inTransaction: inTransaction, currentEventId: currentEventId, wrappedSubscribe: wrappedSubscribe, afterTransaction: afterTransaction, soonButNotYet: soonButNotYet, isInTransaction: isInTransaction }; var Desc = /** @class */ (function () { function Desc(context, method, args) { if (args === void 0) { args = []; } /** @hidden */ this._isDesc = true; //assert("context missing", context) //assert("method missing", method) //assert("args missing", args) this.context = context; this.method = method; this.args = args; } Desc.prototype.deps = function () { if (!this.cachedDeps) { this.cachedDeps = findDeps([this.context].concat(this.args)); } return this.cachedDeps; }; Desc.prototype.toString = function () { var args = _.map(_.toString, this.args); return _.toString(this.context) + "." + _.toString(this.method) + "(" + args + ")"; }; return Desc; }()); /** @hidden */ function describe(context, method) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } var ref = context || method; if (ref && ref._isDesc) { return context || method; } else { return new Desc(context, method, args); } } /** @hidden */ function findDeps(x) { if (isArray(x)) { return _.flatMap(findDeps, x); } else if (isObservable(x)) { return [x]; } else if ((typeof x !== "undefined" && x !== null) ? x._isSource : undefined) { return [x.obs]; } else { return []; } } /** @hidden */ var nullSink = function () { return more; }; /** @hidden */ var nullVoidSink = function () { return more; }; /** @hidden */ function withStateMachine(initState, f, src) { return src.transform(withStateMachineT(initState, f), new Desc(src, "withStateMachine", [initState, f])); } function withStateMachineT(initState, f) { var state = initState; return function (event, sink) { var fromF = f(state, event); var newState = fromF[0], outputs = fromF[1]; state = newState; var reply = more; for (var i = 0; i < outputs.length; i++) { var output = outputs[i]; reply = sink(output); if (reply === noMore) { return reply; } } return reply; }; } /** @hidden */ var Some = /** @class */ (function () { function Some(value) { this._isSome = true; this.isDefined = true; this.value = value; } Some.prototype.getOrElse = function (arg) { return this.value; }; Some.prototype.get = function () { return this.value; }; Some.prototype.filter = function (f) { if (f(this.value)) { return new Some(this.value); } else { return None; } }; Some.prototype.map = function (f) { return new Some(f(this.value)); }; Some.prototype.forEach = function (f) { f(this.value); }; Some.prototype.toArray = function () { return [this.value]; }; Some.prototype.inspect = function () { return "Some(" + this.value + ")"; }; Some.prototype.toString = function () { return this.inspect(); }; return Some; }()); /** @hidden */ var None = { _isNone: true, getOrElse: function (value) { return value; }, get: function () { throw new Error("None.get()"); }, filter: function () { return None; }, map: function () { return None; }, forEach: function () { }, isDefined: false, toArray: function () { return []; }, inspect: function () { return "None"; }, toString: function () { return this.inspect(); } }; function none() { return None; } function toOption(v) { if (v && (v._isSome || v._isNone)) { return v; } else { return new Some(v); } } function isNone(object) { return ((typeof object !== "undefined" && object !== null) ? object._isNone : false); } /** @hidden */ var eventIdCounter = 0; /** * Base class for all events passed through [EventStreams](eventstream.html) and [Properties](property.html). */ var Event = /** @class */ (function () { function Event() { this.id = ++eventIdCounter; /** @hidden */ this.isEvent = true; /** @hidden */ this._isEvent = true; this.isEnd = false; this.isInitial = false; this.isNext = false; this.isError = false; this.hasValue = false; } /** @hidden */ Event.prototype.filter = function (f) { return true; }; /** @hidden */ Event.prototype.inspect = function () { return this.toString(); }; /** @hidden */ Event.prototype.log = function () { return this.toString(); }; /** @hidden */ Event.prototype.toNext = function () { return this; }; return Event; }()); /** * Base class for all [Events](event.html) carrying a value. * * Can be distinguished from other events using [hasValue](../globals.html#hasvalue) **/ var Value = /** @class */ (function (_super) { __extends(Value, _super); function Value(value) { var _this = _super.call(this) || this; _this.hasValue = true; if (value instanceof Event) { throw new Error$1("Wrapping an event inside other event"); } _this.value = value; return _this; } /** @hidden */ Value.prototype.fmap = function (f) { return this.apply(f(this.value)); }; /** @hidden */ Value.prototype.filter = function (f) { return f(this.value); }; /** @hidden */ Value.prototype.toString = function () { return _.toString(this.value); }; //toString(): string { return "<value " + this.id + ">" + _.toString(this.value) } /** @hidden */ Value.prototype.log = function () { return this.value; }; return Value; }(Event)); /** * Indicates a new value in an [EventStream](eventstream.html) or a [Property](property.html). * * Can be distinguished from other events using [isNext](../globals.html#isnext) */ var Next = /** @class */ (function (_super) { __extends(Next, _super); function Next(value) { var _this = _super.call(this, value) || this; _this.isNext = true; /** @hidden */ _this._isNext = true; // some compatibility stuff? return _this; } /** @hidden */ Next.prototype.apply = function (value) { return new Next(value); }; return Next; }(Value)); /** * An event carrying the initial value of a [Property](classes/property.html). This event can be emitted by a property * immediately when subscribing to it. * * Can be distinguished from other events using [isInitial](../globals.html#isinitial) */ var Initial = /** @class */ (function (_super) { __extends(Initial, _super); function Initial(value) { var _this = _super.call(this, value) || this; _this.isInitial = true; /** @hidden */ _this._isInitial = true; return _this; } /** @hidden */ Initial.prototype.apply = function (value) { return new Initial(value); }; /** @hidden */ Initial.prototype.toNext = function () { return new Next(this.value); }; return Initial; }(Value)); /** * Base class for events not carrying a value. */ var NoValue = /** @class */ (function (_super) { __extends(NoValue, _super); function NoValue() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.hasValue = false; return _this; } /** @hidden */ NoValue.prototype.fmap = function (f) { return this; }; return NoValue; }(Event)); /** * An event that indicates the end of an [EventStream](classes/eventstream.html) or a [Property](classes/property.html). * No more events can be emitted after this one. * * Can be distinguished from other events using [isEnd](../globals.html#isend) */ var End = /** @class */ (function (_super) { __extends(End, _super); function End() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.isEnd = true; return _this; } /** @hidden */ End.prototype.toString = function () { return "<end>"; }; return End; }(NoValue)); /** * An event carrying an error. You can use [onError](observable.html#onerror) to subscribe to errors. */ var Error$1 = /** @class */ (function (_super) { __extends(Error, _super); function Error(error) { var _this = _super.call(this) || this; _this.isError = true; _this.error = error; return _this; } /** @hidden */ Error.prototype.toString = function () { return "<error> " + _.toString(this.error); }; return Error; }(NoValue)); /** @hidden */ function initialEvent(value) { return new Initial(value); } /** @hidden */ function nextEvent(value) { return new Next(value); } /** @hidden */ function endEvent() { return new End(); } /** @hidden */ function toEvent(x) { if (x && x._isEvent) { return x; } else { return nextEvent(x); } } /** * Returns true if the given object is an [Event](classes/event.html). */ function isEvent(e) { return e && e._isEvent; } /** * Returns true if the given event is an [Initial](classes/initial.html) value of a [Property](classes/property.html). */ function isInitial(e) { return e && e._isInitial; } /** * Returns true if the given event is an [Error](classes/error.html) event of an [Observable](classes/observable.html). */ function isError(e) { return e.isError; } /** * Returns true if the given event is a [Value](classes/value.html), i.e. a [Next](classes/next.html) or * an [Initial](classes/error.html) value of an [Observable](classes/observable.html). */ function hasValue(e) { return e.hasValue; } /** * Returns true if the given event is an [End](classes/end.html) */ function isEnd(e) { return e.isEnd; } /** * Returns true if the given event is a [Next](classes/next.html) */ function isNext(e) { return e.isNext; } /** @hidden */ function equals(a, b) { return a === b; } /** @hidden */ function skipDuplicates(src, isEqual) { if (isEqual === void 0) { isEqual = equals; } var desc = new Desc(src, "skipDuplicates", []); return withStateMachine(none(), function (prev, event) { if (!hasValue(event)) { return [prev, [event]]; } else if (event.isInitial || isNone(prev) || !isEqual(prev.get(), event.value)) { return [new Some(event.value), [event]]; } else { return [prev, []]; } }, src).withDesc(desc); } /** @hidden */ function take(count, src, desc) { return src.transform(takeT(count), desc || new Desc(src, "take", [count])); } /** @hidden */ function takeT(count) { return function (e, sink) { if (!e.hasValue) { return sink(e); } else { count--; if (count > 0) { return sink(e); } else { if (count === 0) { sink(e); } sink(endEvent()); return noMore; } } }; } /** @hidden */ function log(args, src) { src.subscribe(function (event) { if (typeof console !== "undefined" && typeof console.log === "function") { console.log.apply(console, args.concat([event.log()])); } return more; }); } /** @hidden */ function doLogT(args) { return function (event, sink) { if (typeof console !== "undefined" && console !== null && typeof console.log === "function") { console.log.apply(console, args.concat([event.log()])); } return sink(event); }; } /** @hidden */ function doErrorT(f) { return function (event, sink) { if (isError(event)) { f(event.error); } return sink(event); }; } /** @hidden */ function doActionT(f) { return function (event, sink) { if (hasValue(event)) { f(event.value); } return sink(event); }; } /** @hidden */ function doEndT(f) { return function (event, sink) { if (isEnd(event)) { f(); } return sink(event); }; } /** @hidden */ function scan(src, seed, f) { var resultProperty; var acc = seed; var initHandled = false; var subscribe = function (sink) { var initSent = false; var unsub = nop; var reply = more; var sendInit = function () { if (!initSent) { initSent = initHandled = true; reply = sink(new Initial(acc)); if (reply === noMore) { unsub(); unsub = nop; } } return reply; }; unsub = src.subscribeInternal(function (event) { if (hasValue(event)) { if (initHandled && event.isInitial) { //console.log "skip INITIAL" return more; // init already sent, skip this one } else { if (!event.isInitial) { sendInit(); } initSent = initHandled = true; var prev = acc; var next = f(prev, event.value); //console.log prev , ",", event.value, "->", next acc = next; return sink(event.apply(next)); } } else { if (event.isEnd) { reply = sendInit(); } if (reply !== noMore) { return sink(event); } return reply; } }); UpdateBarrier.whenDoneWith(resultProperty, sendInit); return unsub; }; return resultProperty = new Property(new Desc(src, "scan", [seed, f]), subscribe); } /** @hidden */ function mapEndT(f) { var theF = _.toFunction(f); return function (event, sink) { if (isEnd(event)) { sink(nextEvent(theF(event))); sink(endEvent()); return noMore; } else { return sink(event); } }; } /** @hidden */ function mapErrorT(f) { var theF = _.toFunction(f); return function (event, sink) { if (isError(event)) { return sink(nextEvent(theF(event.error))); } else { return sink(event); } }; } /** @hidden */ function skipErrors(src) { return src.transform(function (event, sink) { if (isError(event)) { return more; } else { return sink(event); } }, new Desc(src, "skipErrors", [])); } /** @hidden */ function last$1(src) { var lastEvent; return src.transform(function (event, sink) { if (isEnd(event)) { if (lastEvent) { sink(lastEvent); } sink(endEvent()); return noMore; } else if (hasValue(event)) { lastEvent = event; return more; } else { return sink(event); } }).withDesc(new Desc(src, "last", [])); } /** @hidden */ var CompositeUnsubscribe = /** @class */ (function () { function CompositeUnsubscribe(ss) { if (ss === void 0) { ss = []; } this.unsubscribed = false; this.unsubscribe = _.bind(this.unsubscribe, this); this.unsubscribed = false; this.subscriptions = []; this.starting = []; for (var i = 0, s; i < ss.length; i++) { s = ss[i]; this.add(s); } } CompositeUnsubscribe.prototype.add = function (subscription) { var _this = this; if (!this.unsubscribed) { var ended = false; var unsub = nop; this.starting.push(subscription); var unsubMe = function () { if (_this.unsubscribed) { return; } ended = true; _this.remove(unsub); _.remove(subscription, _this.starting); }; unsub = subscription(this.unsubscribe, unsubMe); if (!(this.unsubscribed || ended)) { this.subscriptions.push(unsub); } else { unsub(); } _.remove(subscription, this.starting); } }; CompositeUnsubscribe.prototype.remove = function (unsub) { if (this.unsubscribed) { return; } if ((_.remove(unsub, this.subscriptions)) !== undefined) { return unsub(); } }; CompositeUnsubscribe.prototype.unsubscribe = function () { if (this.unsubscribed) { return; } this.unsubscribed = true; var iterable = this.subscriptions; for (var i = 0; i < iterable.length; i++) { iterable[i](); } this.subscriptions = []; this.starting = []; }; CompositeUnsubscribe.prototype.count = function () { if (this.unsubscribed) { return 0; } return this.subscriptions.length + this.starting.length; }; CompositeUnsubscribe.prototype.empty = function () { return this.count() === 0; }; return CompositeUnsubscribe; }()); /** @hidden */ function streamSubscribeToPropertySubscribe(initValue, streamSubscribe) { //assertFunction(streamSubscribe) return function (sink) { var initSent = false; var subbed = false; var unsub = nop; var reply = more; var sendInit = function () { if (!initSent) { return initValue.forEach(function (value) { initSent = true; reply = sink(new Initial(value)); if (reply === noMore) { unsub(); unsub = nop; return nop; } }); } }; unsub = streamSubscribe(function (event) { if (event instanceof Value) { if (event.isInitial && !subbed) { initValue = new Some(event.value); return more; } else { if (!event.isInitial) { sendInit(); } initSent = true; initValue = new Some(event.value); return sink(event); } } else { if (event.isEnd) { reply = sendInit(); } if (reply !== noMore) { return sink(event); } return reply; } }); subbed = true; sendInit(); return unsub; }; } /** @hidden */ function propertyFromStreamSubscribe(desc, subscribe) { assertFunction(subscribe); return new Property(desc, streamSubscribeToPropertySubscribe(none(), subscribe)); } /** Creates an EventStream that delivers the given single value for the first subscriber. The stream will end immediately after this value. You can also send an [`Bacon.Error`](#bacon-error) event instead of a value: `Bacon.once(new Bacon.Error("fail"))`. @param value the value or event to emit @typeparam V Type of stream elements */ function once(value) { var s = new EventStream(new Desc("Bacon", "once", [value]), function (sink) { UpdateBarrier.soonButNotYet(s, function () { sink(toEvent(value)); sink(endEvent()); }); return nop; }); return s; } /** @hidden */ function flatMap_(spawner, src, params) { if (params === void 0) { params = {}; } var root = src; var rootDep = [root]; var childDeps = []; var isProperty = src._isProperty; var ctor = (isProperty ? propertyFromStreamSubscribe : newEventStreamAllowSync); var initialSpawned = false; var desc = params.desc || new Desc(src, "flatMap_", [spawner]); var result = ctor(desc, function (sink) { var composite = new CompositeUnsubscribe(); var queue = []; function spawn(event) { if (isProperty && event.isInitial) { if (initialSpawned) { return more; } initialSpawned = true; } var child = makeObservable(spawner(event)); childDeps.push(child); return composite.add(function (unsubAll, unsubMe) { return child.subscribeInternal(function (event) { if (event.isEnd) { _.remove(child, childDeps); checkQueue(); checkEnd(unsubMe); return noMore; } else { event = event.toNext(); // To support Property as the spawned stream var reply = sink(event); if (reply === noMore) { unsubAll(); } return reply; } }); }); } function checkQueue() { var event = queue.shift(); if (event) { spawn(event); } } function checkEnd(unsub) { unsub(); if (composite.empty()) { return sink(endEvent()); } return more; } composite.add(function (__, unsubRoot) { return root.subscribeInternal(function (event) { if (event.isEnd) { return checkEnd(unsubRoot); } else if (event.isError && !params.mapError) { return sink(event); } else if (params.firstOnly && composite.count() > 1) { return more; } else { if (composite.unsubscribed) { return noMore; } if (params.limit && composite.count() > params.limit) { queue.push(event); } else { spawn(event); } return more; } }); }); return composite.unsubscribe; }); result.internalDeps = function () { if (childDeps.length) { return rootDep.concat(childDeps); } else { return rootDep; } }; return result; } /** @hidden */ function handleEventValueWith(f) { if (typeof f == "function") { return (function (event) { if (hasValue(event)) { return f(event.value); } return event; }); } return (function (event) { return f; }); } /** @hidden */ function makeObservable(x) { if (isObservable(x)) { return x; } else { return once(x); } } /** @hidden */ function flatMapEvent(src, f) { return flatMap_(f, src, { mapError: true, desc: new Desc(src, "flatMapEvent", [f]) }); } /** @hidden */ function endAsValue(src) { return src.transform(function (event, sink) { if (isEnd(event)) { sink(nextEvent({})); sink(endEvent()); return noMore; } return more; }); } /** @hidden */ function endOnError(src, predicate) { if (predicate === void 0) { predicate = function (x) { return true; }; } return src.transform(function (event, sink) { if (isError(event) && predicate(event.error)) { sink(event); return sink(endEvent()); } else { return sink(event); } }, new Desc(src, "endOnError", [])); } /** @hidden */ var Source = /** @class */ (function () { function Source(obs, sync) { this._isSource = true; this.flatten = true; this.ended = false; this.obs = obs; this.sync = sync; } Source.prototype.subscribe = function (sink) { return this.obs.subscribeInternal(sink); }; Source.prototype.toString = function () { return this.obs.toString(); }; Source.prototype.markEnded = function () { this.ended = true; }; Source.prototype.mayHave = function (count) { return true; }; return Source; }()); /** @hidden */ var DefaultSource = /** @class */ (function (_super) { __extends(DefaultSource, _super); function DefaultSource() { return _super !== null && _super.apply(this, arguments) || this; } DefaultSource.prototype.consume = function () { return this.value; }; DefaultSource.prototype.push = function (x) { this.value = x; }; DefaultSource.prototype.hasAtLeast = function (c) { return !!this.value; }; return DefaultSource; }(Source)); /** @hidden */ var ConsumingSource = /** @class */ (function (_super) { __extends(ConsumingSource, _super); function ConsumingSource(obs, sync) { var _this = _super.call(this, obs, sync) || this; _this.flatten = false; _this.queue = []; return _this; } ConsumingSource.prototype.consume = function () { return this.queue.shift(); }; ConsumingSource.prototype.push = function (x) { this.queue.push(x); }; ConsumingSource.prototype.mayHave = function (count) { return !this.ended || this.queue.length >= count; }; ConsumingSource.prototype.hasAtLeast = function (count) { return this.queue.length >= count; }; return ConsumingSource; }(Source)); /** @hidden */ var BufferingSource = /** @class */ (function (_super) { __extends(BufferingSource, _super); function BufferingSource(obs) { var _this = _super.call(this, obs, true) || this; _this.queue = []; return _this; } BufferingSource.prototype.consume = function () { var values = this.queue; this.queue = []; return { value: values }; }; BufferingSource.prototype.push = function (x) { return this.queue.push(x.value); }; BufferingSource.prototype.hasAtLeast = function (count) { return true; }; return BufferingSource; }(Source)); /** @hidden */ function isTrigger(s) { if (s == null) return false; if (s._isSource) { return s.sync; } else { return s._isEventStream; } } /** @hidden */ function fromObservable(s) { if (s != null && s._isSource) { return s; } else if (s != null && s._isProperty) { return new DefaultSource(s, false); } else { return new ConsumingSource(s, true); } } /** Creates an EventStream that immediately ends. @typeparam V Type of stream elements */ function never() { return new EventStream(describe("Bacon", "never"), function (sink) { sink(endEvent()); return nop; }); } /** The `when` method provides a generalization of the [`zip`](classes/observable.html#zip) function. While zip synchronizes events from multiple streams pairwse, the join patterns used in `when` allow the implementation of more advanced synchronization patterns. Consider implementing a game with discrete time ticks. We want to handle key-events synchronized on tick-events, with at most one key event handled per tick. If there are no key events, we want to just process a tick. ```js Bacon.when( [tick, keyEvent, function(_, k) { handleKeyEvent(k); return handleTick(); }], [tick, handleTick]) ``` Order is important here. If the [tick] patterns had been written first, this would have been tried first, and preferred at each tick. Join patterns are indeed a generalization of zip, and for EventStreams, zip is equivalent to a single-rule join pattern. The following observables have the same output, assuming that all sources are EventStreams. ```js Bacon.zipWith(a,b,c, combine) Bacon.when([a,b,c], combine) ``` Note that [`Bacon.when`](#bacon-when) does not trigger updates for events from Properties though; if you use a Property in your pattern, its value will be just sampled when all the other sources (EventStreams) have a value. This is useful when you need a value of a Property in your calculations. If you want your pattern to fire for a Property too, you can convert it into an EventStream using [`property.changes()`](#property-changes) or [`property.toEventStream()`](#property-toeventstream) * @param {Pattern<O>} patterns Join patterns * @typeparam O result type */ function when() { var patterns = []; for (var _i = 0; _i < arguments.length; _i++) { patterns[_i] = arguments[_i]; } return when_(newEventStream, patterns); } /** @hidden */ function whenP() { var patterns = []; for (var _i = 0; _i < arguments.length; _i++) { patterns[_i] = arguments[_i]; } return when_(propertyFromStreamSubscribe, patterns); } /** @hidden */ function when_(ctor, patterns) { if (patterns.length === 0) { return never(); } var _a = processRawPatterns(extractRawPatterns(patterns)), sources = _a[0], ixPats = _a[1]; if (!sources.length) { return never(); } var needsBarrier = (any(sources, function (s) { return s.flatten; })) && containsDuplicateDeps(map((function (s) { return s.obs; }), sources)); var desc = new Desc("Bacon", "when", Array.prototype.slice.call(patterns)); var resultStream = ctor(desc, function (sink) { var triggers = []; var ends = false; function match(p) { for (var i = 0; i < p.ixs.length; i++) { var ix = p.ixs[i]; if (!sources[ix.index].hasAtLeast(ix.count)) { return false; } } return true; } function cannotMatch(p) { for (var i = 0; i < p.ixs.length; i++) { var ix = p.ixs[i]; if (!sources[ix.index].mayHave(ix.count)) { return true; } } return false; } function nonFlattened(trigger) { return !trigger.source.flatten; } function part(source) { return function (unsubAll) { function flushLater() { return UpdateBarrier.whenDoneWith(resultStream, flush); } function flushWhileTriggers() { var trigger; if ((trigger = triggers.pop()) !== undefined) { var reply = more; for (var i = 0, p; i < ixPats.length; i++) { p = ixPats[i]; if (match(p)) { var values = []; for (var j = 0; j < p.ixs.length; j++) { var event_1 = sources[p.ixs[j].index].consume(); if (!event_1) throw new Error("Event was undefined"); values.push(event_1.value); } //console.log("flushing values", values) var applied = p.f.apply(null, values); //console.log('sinking', applied) reply = sink((trigger).e.apply(applied)); if (triggers.length) { triggers = filter(nonFlattened, triggers); } if (reply === noMore) { return reply; } else { return flushWhileTriggers(); } } } } return more; } function flush() { //console.log "flushing", _.toString(resultStream) var reply = flushWhileTriggers(); if (ends) { //console.log "ends detected" if (all(sources, cannotSync) || all(ixPats, cannotMatch)) { //console.log "actually ending" reply = noMore; sink(endEvent()); } } if (reply === noMore) { unsubAll(); } } return source.subscribe(function (e) { var reply = more; if (e.isEnd) { //console.log "got end" ends = true; source.markEnded(); flushLater(); } else if (e.isError) { reply = sink(e); } else { var valueEvent = e; //console.log "got value", e.value source.push(valueEvent); if (source.sync) { //console.log "queuing", e.toString(), toString(resultStream) triggers.push({ source: source, e: valueEvent }); if (needsBarrier || UpdateBarrier.hasWaiters()) { flushLater(); } else { flush(); } } } if (reply === noMore) { unsubAll(); } return reply; }); }; } return new CompositeUnsubscribe(map(part, sources)).unsubscribe; }); return resultStream; } function processRawPatterns(rawPatterns) { var sources = []; var pats = []; for (var i = 0; i < rawPatterns.length; i++) { var _a = rawPatterns[i], patSources = _a[0], f = _a[1]; var pat = { f: f, ixs: [] }; var triggerFound = false; for (var j = 0, s; j < patSources.length; j++) { s = patSources[j]; var index = indexOf(sources, s); if (!triggerFound) { triggerFound = isTrigger(s); } if (index < 0) { sources.push(s); index = sources.length