baconjs
Version:
A small functional reactive programming lib for JavaScript.
1,679 lines (1,631 loc) • 179 kB
JavaScript
(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