Socket
Socket
Sign inDemoInstall

mobservable

Package Overview
Dependencies
Maintainers
1
Versions
79
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mobservable - npm Package Compare versions

Comparing version 0.5.10 to 0.6.0

docs/api.md

139

dist/mobservable.d.ts
/**
* mobservable
* (c) 2015 - Michel Weststrate
* https://mweststrate.github.io/mobservable
* https: //mweststrate.github.io/mobservable
*/
interface IMobservableStatic extends _IMobservableStatic {
// value is the default exported function of the module
<T>(value?:T[]):Mobservable.IObservableArray<T>;
<T>(value?:T|{():T}, scope?:Object):Mobservable.IObservableValue<T>;
}
interface _IMobservableStatic {
makeReactive : IMakeReactive;
makeReactive<T>(value:T[], opts?:Mobservable.IMakeReactiveOptions):Mobservable.IObservableArray<T>;
makeReactive<T>(value:()=>T, opts?:Mobservable.IMakeReactiveOptions):Mobservable.IObservableValue<T>;
makeReactive<T extends Object>(value:T, opts?:Mobservable.IMakeReactiveOptions):T;
makeReactive<T>(value:T, opts?:Mobservable.IMakeReactiveOptions):Mobservable.IObservableValue<T>;
extendReactive(target: Object, properties: Object);
asReference(value);
isReactive(value:any):boolean;
isReactive(value: any): boolean;
sideEffect(func:Mobservable.Lambda,scope?):Mobservable.Lambda;
defineReactiveProperties(target:Object, properties:Object);
asReference(value);
reactiveMixin;
reactiveComponent<T>(componentClass:T):T;
observable(target: Object, key: string); // decorator / annotation
observable(target:Object, key:string); // annotation
toJson<T>(value:T):T;
observeUntilInvalid<T>(func:()=>T, onInvalidate:Mobservable.Lambda):[T,Mobservable.Lambda];
sideEffect(func: Mobservable.Lambda,scope?): Mobservable.Lambda;
// change a lot of observables at once
transaction<T>(action:()=>T):T;
/** old api TODO: remove */
// ways of creating observables.
value<T>(value?:T[]):Mobservable.IObservableArray<T>;
value<T>(value?:T|{():T}, scope?:Object):Mobservable.IObservableValue<T>;
observeUntilInvalid<T>(func: ()=>T, onInvalidate: Mobservable.Lambda): [T,Mobservable.Lambda];
array<T>(values?:T[]):Mobservable.IObservableArray<T>;
primitive<T>(value?:T):Mobservable.IObservableValue<T>;
reference<T>(value?:T):Mobservable.IObservableValue<T>;
computed<T>(func:()=>T,scope?):Mobservable.IObservableValue<T>;
expr<T>(expr:()=>T,scope?):T;
sideEffect(func:Mobservable.Lambda,scope?):Mobservable.Lambda;
transaction<T>(action: ()=>T): T;
// create observable properties
props(object:Object, name:string, initalValue: any);
props(object:Object, props:Object);
props(object:Object);
fromJson<T>(value:T):T;
toJson<T>(value: T): T;
// convert observables to not observables
toPlainValue<T>(any:T):T;
// decorator
reactiveComponent<T>(componentClass: T): T;
// observe observables
observeProperty(object:Object, key:string, listener:Function, invokeImmediately?:boolean):Mobservable.Lambda;
watch<T>(func:()=>T, onInvalidate:Mobservable.Lambda):[T,Mobservable.Lambda];
reactiveMixin: Object;
// change a lot of observables at once
batch<T>(action:()=>T):T;
debugLevel: number;
}
// Utils
debugLevel: number;
interface IMakeReactive {
<T>(value: T[], opts?: Mobservable.IMakeReactiveOptions): Mobservable.IObservableArray<T>;
<T>(value: ()=>T, opts?: Mobservable.IMakeReactiveOptions): Mobservable.IObservableValue<T>;
<T extends string|number|boolean|Date|RegExp|Function|void>(value: T, opts?: Mobservable.IMakeReactiveOptions): Mobservable.IObservableValue<T>;
<T extends Object>(value: Object, opts?: Mobservable.IMakeReactiveOptions): T;
}
// ReactJS
ObserverMixin;
ObservingComponent<T>(componentClass:T):T;
interface IMobservableStatic extends _IMobservableStatic, IMakeReactive {
}

@@ -77,46 +46,46 @@

interface IMakeReactiveOptions {
as?: string /* "auto" | "reference" | TODO: see #8 "structure" */
scope?: Object,
recurse?: boolean;
// protected: boolean TODO: see #9
as?: string /* "auto" | "reference" | TODO: see #8 "structure" */
scope?: Object,
recurse?: boolean;
// protected: boolean TODO: see #9
}
interface Lambda {
():void;
(): void;
}
interface IObservable {
observe(callback:(...args:any[])=>void, fireImmediately?:boolean):Lambda;
observe(callback: (...args: any[])=>void, fireImmediately?: boolean): Lambda;
}
interface IObservableValue<T> extends IObservable {
():T;
(value:T);
observe(callback:(newValue:T, oldValue:T)=>void, fireImmediately?:boolean):Lambda;
(): T;
(value: T);
observe(callback: (newValue: T, oldValue: T)=>void, fireImmediately?: boolean): Lambda;
}
interface IObservableArray<T> extends IObservable, Array<T> {
spliceWithArray(index:number, deleteCount?:number, newItems?:T[]):T[];
observe(listener:(changeData:IArrayChange<T>|IArraySplice<T>)=>void, fireImmediately?:boolean):Lambda;
clear(): T[];
replace(newItems:T[]);
values(): T[];
clone(): IObservableArray<T>;
find(predicate:(item:T,index:number,array:IObservableArray<T>)=>boolean,thisArg?,fromIndex?:number):T;
remove(value:T):boolean;
spliceWithArray(index: number, deleteCount?: number, newItems?: T[]): T[];
observe(listener: (changeData: IArrayChange<T>|IArraySplice<T>)=>void, fireImmediately?: boolean): Lambda;
clear(): T[];
replace(newItems: T[]);
values(): T[];
clone(): IObservableArray<T>;
find(predicate: (item: T,index: number,array: IObservableArray<T>)=>boolean,thisArg?,fromIndex?: number): T;
remove(value: T): boolean;
}
interface IArrayChange<T> {
type: string; // Always: 'update'
object: IObservableArray<T>;
index: number;
oldValue: T;
type: string; // Always: 'update'
object: IObservableArray<T>;
index: number;
oldValue: T;
}
interface IArraySplice<T> {
type: string; // Always: 'splice'
object: IObservableArray<T>;
index: number;
removed: T[];
addedCount: number;
type: string; // Always: 'splice'
object: IObservableArray<T>;
index: number;
removed: T[];
addedCount: number;
}

@@ -123,0 +92,0 @@ }

/**
* mobservable
* (c) 2015 - Michel Weststrate
* https://mweststrate.github.io/mobservable
* https: //mweststrate.github.io/mobservable
*/

@@ -366,3 +366,3 @@ var mobservable;

case ValueType.PlainObject:
return _.makeReactiveObject({}, value, recurse);
return _.extendReactive({}, value, recurse);
}

@@ -387,28 +387,90 @@ throw "Illegal State";

mobservable.asReference = asReference;
var _;
(function (_) {
var deprecations = [];
function deprecated(name) {
if (deprecations.indexOf(name) === -1) {
deprecations.push(name);
console.warn("The method '" + name + "' has been deprecated, for any questions or suggestions, see: https://github.com/mweststrate/mobservable/issues/11");
console.trace();
}
function isReactive(value) {
if (value === null || value === undefined)
return false;
switch (typeof value) {
case "array":
case "object":
case "function":
return value.__isReactive === true;
}
_.deprecated = deprecated;
function wrapDeprecated(name, func) {
return function () {
deprecated(name);
return func.apply(null, arguments);
return false;
}
mobservable.isReactive = isReactive;
function sideEffect(func, scope) {
var observable = new _.ComputedObservable(func, scope);
var disposer = observable.observe(_.noop);
if (observable.dependencyState.observing.length === 0)
_.warn("mobservable.sideEffect: not a single observable was used inside the side-effect function. Side-effect would be a no-op.");
return disposer;
}
mobservable.sideEffect = sideEffect;
function extendReactive(target, properties) {
_.extendReactive(target, properties, true);
}
mobservable.extendReactive = extendReactive;
function observable(target, key, descriptor) {
var baseValue = descriptor ? descriptor.value : null;
if (typeof baseValue === "function") {
delete descriptor.value;
delete descriptor.writable;
descriptor.configurable = true;
descriptor.get = function () {
var observable = this.key = new _.ComputedObservable(baseValue, this).createGetterSetter();
return observable;
};
descriptor.set = function () {
console.trace();
throw new Error("It is not allowed to reassign observable functions");
};
}
_.wrapDeprecated = wrapDeprecated;
function makeReactiveObject(target, properties, recurse) {
else {
Object.defineProperty(target, key, {
configurable: true, enumberable: true,
get: function () {
_.defineReactiveProperty(this, key, undefined, true);
return this[key];
},
set: function (value) {
_.defineReactiveProperty(this, key, value, true);
}
});
}
}
mobservable.observable = observable;
function toJson(source) {
if (!source)
return source;
if (Array.isArray(source) || source instanceof _.ObservableArray)
return source.map(toJson);
if (typeof source === "object") {
var res = {};
for (var key in source)
if (source.hasOwnProperty(key))
res[key] = toJson(source[key]);
return res;
}
return source;
}
mobservable.toJson = toJson;
function transaction(action) {
return _.Scheduler.batch(action);
}
mobservable.transaction = transaction;
function observeUntilInvalid(func, onInvalidate) {
var watch = new _.WatchedExpression(func, onInvalidate);
return [watch.value, function () { return watch.dispose(); }];
}
mobservable.observeUntilInvalid = observeUntilInvalid;
mobservable.debugLevel = 0;
var _;
(function (_) {
function extendReactive(target, properties, recurse) {
markReactive(target);
for (var key in properties)
makeReactiveObjectProperty(target, key, properties[key], recurse);
defineReactiveProperty(target, key, properties[key], recurse);
return target;
}
_.makeReactiveObject = makeReactiveObject;
function makeReactiveObjectProperty(target, name, value, recurse) {
_.extendReactive = extendReactive;
function defineReactiveProperty(target, name, value, recurse) {
var type;

@@ -448,3 +510,3 @@ if (value instanceof AsReference) {

}
_.makeReactiveObjectProperty = makeReactiveObjectProperty;
_.defineReactiveProperty = defineReactiveProperty;
function makeReactiveArrayItem(value) {

@@ -466,3 +528,3 @@ if (isReactive(value))

case ValueType.PlainObject:
return _.makeReactiveObject({}, value, true);
return _.extendReactive({}, value, true);
}

@@ -491,171 +553,2 @@ throw "Illegal State";

})(_ = mobservable._ || (mobservable._ = {}));
function isReactive(value) {
if (value === null || value === undefined)
return false;
switch (typeof value) {
case "array":
case "object":
case "function":
return value.__isReactive === true;
}
return false;
}
mobservable.isReactive = isReactive;
function sideEffect(func, scope) {
var observable = new _.ComputedObservable(func, scope);
var disposer = observable.observe(_.noop);
if (observable.dependencyState.observing.length === 0)
_.warn("mobservable.sideEffect: not a single observable was used inside the side-effect function. Side-effect would be a no-op.");
return disposer;
}
mobservable.sideEffect = sideEffect;
function defineReactiveProperties(target, properties) {
_.makeReactiveObject(target, properties, true);
}
mobservable.defineReactiveProperties = defineReactiveProperties;
function observable(target, key, descriptor) {
var baseValue = descriptor ? descriptor.value : null;
if (typeof baseValue === "function") {
delete descriptor.value;
delete descriptor.writable;
descriptor.configurable = true;
descriptor.get = function () {
var observable = this.key = new _.ComputedObservable(baseValue, this).createGetterSetter();
return observable;
};
descriptor.set = function () {
console.trace();
throw new Error("It is not allowed to reassign observable functions");
};
}
else {
Object.defineProperty(target, key, {
configurable: true, enumberable: true,
get: function () {
_.makeReactiveObjectProperty(this, key, undefined, true);
return this[key];
},
set: function (value) {
_.makeReactiveObjectProperty(this, key, value, true);
}
});
}
}
mobservable.observable = observable;
function toJson(source) {
if (!source)
return source;
if (Array.isArray(source) || source instanceof _.ObservableArray)
return source.map(toJson);
if (typeof source === "object") {
var res = {};
for (var key in source)
if (source.hasOwnProperty(key))
res[key] = toJson(source[key]);
return res;
}
return source;
}
mobservable.toJson = toJson;
function transaction(action) {
return _.Scheduler.batch(action);
}
mobservable.transaction = transaction;
function observeUntilInvalid(func, onInvalidate) {
var watch = new _.WatchedExpression(func, onInvalidate);
return [watch.value, function () { return watch.dispose(); }];
}
mobservable.observeUntilInvalid = observeUntilInvalid;
mobservable.watch = _.wrapDeprecated("watch", observeUntilInvalid);
mobservable.batch = _.wrapDeprecated("batch", transaction);
function value(value, scope) {
_.deprecated("value");
return makeReactive(value, { scope: scope });
}
mobservable.value = value;
function reference(value) {
_.deprecated("reference");
return makeReactive(value, { as: "reference" });
}
mobservable.reference = reference;
mobservable.primitive = _.wrapDeprecated("primitive", reference);
function computed(func, scope) {
_.deprecated("computed");
return makeReactive(func, { scope: scope });
}
mobservable.computed = computed;
function expr(expr, scope) {
_.deprecated("expr");
if (_.DNode.trackingStack.length === 0)
throw new Error("mobservable.expr can only be used inside a computed observable. Probably mobservable.computed should be used instead of .expr");
return new _.ComputedObservable(expr, scope).get();
}
mobservable.expr = expr;
function array(values) {
_.deprecated("array");
return new _.ObservableArray(values, false);
}
mobservable.array = array;
function props(target, properties, initialValue) {
_.deprecated("props");
switch (arguments.length) {
case 1: return _.makeReactiveObject(target, target, false);
case 2: return _.makeReactiveObject(target, properties, false);
case 3: return _.makeReactiveObject(target, (_a = {}, _a[properties] = initialValue, _a), false);
}
throw "Illegal invocation";
var _a;
}
mobservable.props = props;
function fromJson(source) {
_.deprecated("fromJson");
return makeReactive(source);
}
mobservable.fromJson = fromJson;
function toPlainValue(value) {
_.deprecated("toPlainValue");
if (value) {
if (value instanceof Array)
return value.slice();
else if (value instanceof _.ObservableValue)
return value.get();
else if (typeof value === "function" && value.impl) {
if (value.impl instanceof _.ObservableValue)
return value();
else if (value.impl instanceof _.ObservableArray)
return value().slice();
}
else if (typeof value === "object") {
var res = {};
for (var key in value)
res[key] = toPlainValue(value[key]);
return res;
}
}
return value;
}
mobservable.toPlainValue = toPlainValue;
function observeProperty(object, key, listener, invokeImmediately) {
if (invokeImmediately === void 0) { invokeImmediately = false; }
_.deprecated("observeProperty");
if (!object)
throw new Error("Cannot observe property of '" + object + "'");
if (!(key in object))
throw new Error("Object '" + object + "' has no property '" + key + "'.");
if (!listener || typeof listener !== "function")
throw new Error("Third argument to mobservable.observeProperty should be a function");
return sideEffect(function () {
listener(object[key]);
});
var observer = new _.ComputedObservable((function () { return object[key]; }), object);
var disposer = observer.observe(listener, invokeImmediately);
if (observer.dependencyState.observing.length === 0)
throw new Error("mobservable.observeProperty: property '" + key + "' of '" + object + " doesn't seem to be observable. Did you define it as observable using @observable or mobservable.props? You might try to use the .observe() method instead.");
return _.once(function () {
disposer();
observer.dependencyState.dispose();
});
}
mobservable.observeProperty = observeProperty;
mobservable.debugLevel = 0;
})(mobservable || (mobservable = {}));

@@ -956,3 +849,2 @@ var mobservable;

};
mobservable.ObserverMixin = mobservable.reactiveMixin;
function reactiveComponent(componentClass) {

@@ -962,11 +854,11 @@ var baseMount = componentClass.prototype.componentWillMount;

componentClass.prototype.componentWillMount = function () {
mobservable.ObserverMixin.componentWillMount.apply(this, arguments);
mobservable.reactiveMixin.componentWillMount.apply(this, arguments);
baseMount && baseMount.apply(this, arguments);
};
componentClass.prototype.componentWillUnmount = function () {
mobservable.ObserverMixin.componentWillUnmount.apply(this, arguments);
mobservable.reactiveMixin.componentWillUnmount.apply(this, arguments);
baseUnmount && baseUnmount.apply(this, arguments);
};
if (!componentClass.prototype.shouldComponentUpdate)
componentClass.prototype.shouldComponentUpdate = mobservable.ObserverMixin.shouldComponentUpdate;
componentClass.prototype.shouldComponentUpdate = mobservable.reactiveMixin.shouldComponentUpdate;
return componentClass;

@@ -976,3 +868,2 @@ }

;
mobservable.ObservingComponent = mobservable._.wrapDeprecated("ObservingComponent", reactiveComponent);
})(mobservable || (mobservable = {}));

@@ -979,0 +870,0 @@ var mobservable;

@@ -1,1 +0,1 @@

var mobservable;!function(a){var b;!function(b){var c=function(){function c(a,c){this.value=a,this.recurse=c,this.changeEvent=new b.SimpleEventEmitter,this.dependencyState=new b.DNode(this),this._value=this.makeReferenceValueReactive(a)}return c.prototype.makeReferenceValueReactive=function(c){return this.recurse&&(Array.isArray(c)||b.isPlainObject(c))?a.makeReactive(c):c},c.prototype.set=function(a){if(a!==this._value){var b=this._value;this.dependencyState.markStale(),this._value=this.makeReferenceValueReactive(a),this.dependencyState.markReady(!0),this.changeEvent.emit(this._value,b)}},c.prototype.get=function(){return this.dependencyState.notifyObserved(),this._value},c.prototype.observe=function(a,c){var d=this;void 0===c&&(c=!1),this.dependencyState.setRefCount(1),c&&a(this.get(),void 0);var e=this.changeEvent.on(a);return b.once(function(){d.dependencyState.setRefCount(-1),e()})},c.prototype.createGetterSetter=function(){var a=this,c=function(b){return arguments.length>0?void a.set(b):a.get()};return c.impl=this,c.observe=function(b,c){return a.observe(b,c)},c.toString=function(){return a.toString()},b.markReactive(c),c},c.prototype.toString=function(){return"Observable["+this._value+"]"},c}();b.ObservableValue=c}(b=a._||(a._={}))}(mobservable||(mobservable={}));var __extends=this&&this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},mobservable;!function(a){var b;!function(b){var c=function(c){function d(a,b){if(c.call(this,void 0,!1),this.func=a,this.scope=b,this.isComputing=!1,this.hasError=!1,"function"!=typeof a)throw new Error("ComputedObservable requires a function")}return __extends(d,c),d.prototype.get=function(){if(this.isComputing)throw new Error("Cycle detected");var c=this.dependencyState;if(c.isSleeping?b.DNode.trackingStack.length>0?(c.wakeUp(),c.notifyObserved()):this.compute():c.notifyObserved(),c.hasCycle)throw new Error("Cycle detected");if(this.hasError)throw a.debugLevel&&(console.trace(),b.warn(this+": rethrowing caught exception to observer: "+this._value+(this._value.cause||""))),this._value;return this._value},d.prototype.set=function(a){throw new Error(this.toString()+": A computed observable does not accept new values!")},d.prototype.compute=function(){var a;try{if(this.isComputing)throw new Error("Cycle detected");this.isComputing=!0,a=this.func.call(this.scope),this.hasError=!1}catch(b){this.hasError=!0,console.error(this+"Caught error during computation: ",b),b instanceof Error?a=b:(a=new Error("MobservableComputationError"),a.cause=b)}if(this.isComputing=!1,a!==this._value){var c=this._value;return this._value=a,this.changeEvent.emit(a,c),!0}return!1},d.prototype.toString=function(){return"ComputedObservable["+this.func.toString()+"]"},d}(b.ObservableValue);b.ComputedObservable=c}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(b){function c(){return e.trackingStack.length}!function(a){a[a.STALE=0]="STALE",a[a.PENDING=1]="PENDING",a[a.READY=2]="READY"}(b.DNodeState||(b.DNodeState={}));var d=b.DNodeState,e=function(){function c(a){this.owner=a,this.state=d.READY,this.isSleeping=!0,this.hasCycle=!1,this.observing=[],this.prevObserving=null,this.observers=[],this.dependencyChangeCount=0,this.dependencyStaleCount=0,this.isDisposed=!1,this.externalRefenceCount=0,this.isComputed=void 0!==a.compute}return c.prototype.setRefCount=function(a){var b=this.externalRefenceCount+=a;0===b?this.tryToSleep():b===a&&this.wakeUp()},c.prototype.addObserver=function(a){this.observers[this.observers.length]=a},c.prototype.removeObserver=function(a){var b=this.observers,c=b.indexOf(a);-1!==c&&(b.splice(c,1),0===b.length&&this.tryToSleep())},c.prototype.markStale=function(){this.state===d.READY&&(this.state=d.STALE,this.notifyObservers())},c.prototype.markReady=function(a){this.state!==d.READY&&(this.state=d.READY,this.notifyObservers(a))},c.prototype.notifyObservers=function(a){void 0===a&&(a=!1);for(var b=this.observers.slice(),c=b.length,d=0;c>d;d++)b[d].notifyStateChange(this,a)},c.prototype.tryToSleep=function(){if(!this.isSleeping&&this.isComputed&&0===this.observers.length&&0===this.externalRefenceCount){for(var a=0,b=this.observing.length;b>a;a++)this.observing[a].removeObserver(this);this.observing=[],this.isSleeping=!0}},c.prototype.wakeUp=function(){this.isSleeping&&this.isComputed&&(this.isSleeping=!1,this.state=d.PENDING,this.computeNextState())},c.prototype.notifyStateChange=function(a,c){var e=this;a.state===d.STALE?1===++this.dependencyStaleCount&&this.markStale():(c&&(this.dependencyChangeCount+=1),0===--this.dependencyStaleCount&&(this.state=d.PENDING,b.Scheduler.schedule(function(){e.dependencyChangeCount>0?e.computeNextState():e.markReady(!1),e.dependencyChangeCount=0})))},c.prototype.computeNextState=function(){this.trackDependencies();var a=this.owner.compute();this.bindDependencies(),this.markReady(a)},c.prototype.trackDependencies=function(){this.prevObserving=this.observing,c.trackingStack[c.trackingStack.length]=[]},c.prototype.bindDependencies=function(){this.observing=c.trackingStack.pop(),this.isComputed&&0===this.observing.length&&a.debugLevel>1&&!this.isDisposed&&(console.trace(),b.warn("You have created a function that doesn't observe any values, did you forget to make its dependencies observable?"));var d=b.quickDiff(this.observing,this.prevObserving),e=d[0],f=d[1];this.prevObserving=null;for(var g=0,h=f.length;h>g;g++)f[g].removeObserver(this);this.hasCycle=!1;for(var g=0,h=e.length;h>g;g++)this.isComputed&&e[g].findCycle(this)?(this.hasCycle=!0,this.observing.splice(this.observing.indexOf(e[g]),1),e[g].hasCycle=!0):e[g].addObserver(this)},c.prototype.notifyObserved=function(){var a=c.trackingStack,b=a.length;if(b>0){var d=a[b-1],e=d.length;d[e-1]!==this&&d[e-2]!==this&&(d[e]=this)}},c.prototype.findCycle=function(a){var b=this.observing;if(-1!==b.indexOf(a))return!0;for(var c=b.length,d=0;c>d;d++)if(b[d].findCycle(a))return!0;return!1},c.prototype.dispose=function(){if(this.observers.length)throw new Error("Cannot dispose DNode; it is still being observed");if(this.observing)for(var a=this.observing.length,b=0;a>b;b++)this.observing[b].removeObserver(this);this.observing=null,this.isDisposed=!0},c.trackingStack=[],c}();b.DNode=e,b.stackDepth=c}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){function b(a,b){if(e(a))return a;b=b||{},a instanceof v.AsReference&&(a=a.value,b.as="reference");var d=b.recurse!==!1,f="reference"===b.as?u.Reference:c(a);switch(f){case u.Reference:case u.ComplexObject:return v.makeReactiveReference(a,!1);case u.ComplexFunction:throw new Error("[mobservable:error] Creating reactive functions from functions with multiple arguments is currently not supported, see https://github.com/mweststrate/mobservable/issues/12");case u.ViewFunction:return new v.ComputedObservable(a,b.scope).createGetterSetter();case u.Array:return new v.ObservableArray(a,d);case u.PlainObject:return v.makeReactiveObject({},a,d)}throw"Illegal State"}function c(a){return null===a||void 0===a?u.Reference:"function"==typeof a?a.length?u.ComplexFunction:u.ViewFunction:Array.isArray(a)||a instanceof v.ObservableArray?u.Array:"object"==typeof a?v.isPlainObject(a)?u.PlainObject:u.ComplexObject:u.Reference}function d(a){return new v.AsReference(a)}function e(a){if(null===a||void 0===a)return!1;switch(typeof a){case"array":case"object":case"function":return a.__isReactive===!0}return!1}function f(a,b){var c=new v.ComputedObservable(a,b),d=c.observe(v.noop);return 0===c.dependencyState.observing.length&&v.warn("mobservable.sideEffect: not a single observable was used inside the side-effect function. Side-effect would be a no-op."),d}function g(a,b){v.makeReactiveObject(a,b,!0)}function h(a,b,c){var d=c?c.value:null;"function"==typeof d?(delete c.value,delete c.writable,c.configurable=!0,c.get=function(){var a=this.key=new v.ComputedObservable(d,this).createGetterSetter();return a},c.set=function(){throw console.trace(),new Error("It is not allowed to reassign observable functions")}):Object.defineProperty(a,b,{configurable:!0,enumberable:!0,get:function(){return v.makeReactiveObjectProperty(this,b,void 0,!0),this[b]},set:function(a){v.makeReactiveObjectProperty(this,b,a,!0)}})}function i(a){if(!a)return a;if(Array.isArray(a)||a instanceof v.ObservableArray)return a.map(i);if("object"==typeof a){var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=i(a[c]));return b}return a}function j(a){return v.Scheduler.batch(a)}function k(a,b){var c=new v.WatchedExpression(a,b);return[c.value,function(){return c.dispose()}]}function l(a,c){return v.deprecated("value"),b(a,{scope:c})}function m(a){return v.deprecated("reference"),b(a,{as:"reference"})}function n(a,c){return v.deprecated("computed"),b(a,{scope:c})}function o(a,b){if(v.deprecated("expr"),0===v.DNode.trackingStack.length)throw new Error("mobservable.expr can only be used inside a computed observable. Probably mobservable.computed should be used instead of .expr");return new v.ComputedObservable(a,b).get()}function p(a){return v.deprecated("array"),new v.ObservableArray(a,!1)}function q(a,b,c){switch(v.deprecated("props"),arguments.length){case 1:return v.makeReactiveObject(a,a,!1);case 2:return v.makeReactiveObject(a,b,!1);case 3:return v.makeReactiveObject(a,(d={},d[b]=c,d),!1)}throw"Illegal invocation";var d}function r(a){return v.deprecated("fromJson"),b(a)}function s(a){if(v.deprecated("toPlainValue"),a){if(a instanceof Array)return a.slice();if(a instanceof v.ObservableValue)return a.get();if("function"==typeof a&&a.impl){if(a.impl instanceof v.ObservableValue)return a();if(a.impl instanceof v.ObservableArray)return a().slice()}else if("object"==typeof a){var b={};for(var c in a)b[c]=s(a[c]);return b}}return a}function t(a,b,c,d){if(void 0===d&&(d=!1),v.deprecated("observeProperty"),!a)throw new Error("Cannot observe property of '"+a+"'");if(!(b in a))throw new Error("Object '"+a+"' has no property '"+b+"'.");if(!c||"function"!=typeof c)throw new Error("Third argument to mobservable.observeProperty should be a function");return f(function(){c(a[b])})}var u;!function(a){a[a.Reference=0]="Reference",a[a.PlainObject=1]="PlainObject",a[a.ComplexObject=2]="ComplexObject",a[a.Array=3]="Array",a[a.ViewFunction=4]="ViewFunction",a[a.ComplexFunction=5]="ComplexFunction"}(u||(u={})),a.makeReactive=b,a.asReference=d;var v;!function(a){function b(a){-1===k.indexOf(a)&&(k.push(a),console.warn("The method '"+a+"' has been deprecated, for any questions or suggestions, see: https://github.com/mweststrate/mobservable/issues/11"),console.trace())}function d(a,c){return function(){return b(a),c.apply(null,arguments)}}function f(a,b,c){j(a);for(var d in b)g(a,d,b[d],c);return a}function g(b,d,e,f){var g;e instanceof l?(e=e.value,g=u.Reference,f=!1):g=c(e);var h;switch(g){case u.Reference:case u.ComplexObject:h=i(e,!1);break;case u.ViewFunction:h=new a.ComputedObservable(e,b).createGetterSetter();break;case u.ComplexFunction:a.warn("Storing reactive functions in objects is not supported yet, please use flag 'recurse:false' or wrap the function in 'asReference'"),h=i(e,!1);case u.Array:case u.PlainObject:h=i(e,f)}return Object.defineProperty(b,d,{get:h,set:h,enumerable:!0,configurable:!1}),b}function h(b){if(e(b))return b;if(b instanceof l)return b=b.value;switch(c(b)){case u.Reference:case u.ComplexObject:return b;case u.ViewFunction:case u.ComplexFunction:return a.warn("Storing reactive functions in arrays is not supported, please use flag 'recurse:false' or wrap the function in 'asReference'"),b;case u.Array:return new a.ObservableArray(b,!0);case u.PlainObject:return a.makeReactiveObject({},b,!0)}throw"Illegal State"}function i(b,c){return new a.ObservableValue(b,c).createGetterSetter()}function j(a){Object.defineProperty(a,"__isReactive",{enumerable:!1,value:!0})}var k=[];a.deprecated=b,a.wrapDeprecated=d,a.makeReactiveObject=f,a.makeReactiveObjectProperty=g,a.makeReactiveArrayItem=h,a.makeReactiveReference=i,a.markReactive=j;var l=function(){function a(a){this.value=a}return a}();a.AsReference=l}(v=a._||(a._={})),a.isReactive=e,a.sideEffect=f,a.defineReactiveProperties=g,a.observable=h,a.toJson=i,a.transaction=j,a.observeUntilInvalid=k,a.watch=v.wrapDeprecated("watch",k),a.batch=v.wrapDeprecated("batch",j),a.value=l,a.reference=m,a.primitive=v.wrapDeprecated("primitive",m),a.computed=n,a.expr=o,a.array=p,a.props=q,a.fromJson=r,a.toPlainValue=s,a.observeProperty=t,a.debugLevel=0}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(b){function c(a){var b={enumerable:!1,configurable:!1,set:function(b){if(a<this._values.length){var c=this._values[a];c!==b&&(this._values[a]=b,this.notifyChildUpdate(a,c))}else{if(a!==this._values.length)throw new Error("ObservableArray: Index out of bounds, "+a+" is larger than "+this.values.length);this.push(b)}},get:function(){return a<this._values.length?(this.dependencyState.notifyObserved(),this._values[a]):void 0}};Object.defineProperty(f.prototype,""+a,b),b.enumerable=!0,b.configurable=!0,h[a]=b}function d(a){for(var b=g;a>b;b++)c(b);g=a}var e=function(){function a(){}return a}();e.prototype=[];var f=function(c){function e(a,d){c.call(this),b.markReactive(this),Object.defineProperties(this,{recurse:{enumerable:!1,value:d},dependencyState:{enumerable:!1,value:new b.DNode(this)},_values:{enumerable:!1,value:a?d?a.map(b.makeReactiveArrayItem):a.slice():[]},changeEvent:{enumerable:!1,value:new b.SimpleEventEmitter}}),a&&a.length&&this.updateLength(0,a.length)}return __extends(e,c),Object.defineProperty(e.prototype,"length",{get:function(){return this.dependencyState.notifyObserved(),this._values.length},set:function(a){if("number"!=typeof a||0>a)throw new Error("Out of range: "+a);var b=this._values.length;a!==b&&(a>b?this.spliceWithArray(b,0,new Array(a-b)):this.spliceWithArray(a,b-a))},enumerable:!0,configurable:!0}),e.prototype.updateLength=function(a,b){if(0>b)for(var c=a+b;a>c;c++)delete this[c];else if(b>0){a+b>g&&d(a+b);for(var c=a,e=a+b;e>c;c++)Object.defineProperty(this,""+c,h[c])}},e.prototype.spliceWithArray=function(a,c,d){var e=this._values.length;if(!(void 0!==d&&0!==d.length||0!==c&&0!==e))return[];void 0===a?a=0:a>e?a=e:0>a&&(a=Math.max(0,e+a)),c=1===arguments.length?e-a:void 0===c||null===c?0:Math.max(0,Math.min(c,e-a)),void 0===d?d=[]:this.recurse&&(d=d.map(b.makeReactiveArrayItem));var f=d.length-c,g=(h=this._values).splice.apply(h,[a,c].concat(d));return this.updateLength(e,f),this.notifySplice(a,g,d),g;var h},e.prototype.notifyChildUpdate=function(a,b){this.notifyChanged(),this.changeEvent.emit({object:this,type:"update",index:a,oldValue:b})},e.prototype.notifySplice=function(a,b,c){(0!==b.length||0!==c.length)&&(this.notifyChanged(),this.changeEvent.emit({object:this,type:"splice",index:a,addedCount:c.length,removed:b}))},e.prototype.notifyChanged=function(){this.dependencyState.markStale(),this.dependencyState.markReady(!0)},e.prototype.observe=function(a,b){return void 0===b&&(b=!1),b&&a({object:this,type:"splice",index:0,addedCount:this._values.length,removed:[]}),this.changeEvent.on(a)},e.prototype.clear=function(){return this.splice(0)},e.prototype.replace=function(a){return this.spliceWithArray(0,this._values.length,a)},e.prototype.values=function(){return this.dependencyState.notifyObserved(),this._values.slice()},e.prototype.toJSON=function(){return this.dependencyState.notifyObserved(),this._values.slice()},e.prototype.clone=function(){return this.dependencyState.notifyObserved(),new e(this._values,this.recurse)},e.prototype.find=function(a,b,c){void 0===c&&(c=0),this.dependencyState.notifyObserved();for(var d=this._values,e=d.length,f=c;e>f;f++)if(a.call(b,d[f],f,this))return d[f];return null},e.prototype.splice=function(a,b){for(var c=[],d=2;d<arguments.length;d++)c[d-2]=arguments[d];switch(this.sideEffectWarning("splice"),arguments.length){case 0:return[];case 1:return this.spliceWithArray(a);case 2:return this.spliceWithArray(a,b)}return this.spliceWithArray(a,b,c)},e.prototype.push=function(){for(var a=[],b=0;b<arguments.length;b++)a[b-0]=arguments[b];return this.sideEffectWarning("push"),this.spliceWithArray(this._values.length,0,a),this._values.length},e.prototype.pop=function(){return this.sideEffectWarning("pop"),this.splice(Math.max(this._values.length-1,0),1)[0]},e.prototype.shift=function(){return this.sideEffectWarning("shift"),this.splice(0,1)[0]},e.prototype.unshift=function(){for(var a=[],b=0;b<arguments.length;b++)a[b-0]=arguments[b];return this.sideEffectWarning("unshift"),this.spliceWithArray(0,0,a),this._values.length},e.prototype.reverse=function(){return this.sideEffectWarning("reverse"),this.replace(this._values.reverse())},e.prototype.sort=function(a){return this.sideEffectWarning("sort"),this.replace(this._values.sort.apply(this._values,arguments))},e.prototype.remove=function(a){this.sideEffectWarning("remove");var b=this._values.indexOf(a);return b>-1?(this.splice(b,1),!0):!1},e.prototype.toString=function(){return this.wrapReadFunction("toString",arguments)},e.prototype.toLocaleString=function(){return this.wrapReadFunction("toLocaleString",arguments)},e.prototype.concat=function(){return this.wrapReadFunction("concat",arguments)},e.prototype.join=function(a){return this.wrapReadFunction("join",arguments)},e.prototype.slice=function(a,b){return this.wrapReadFunction("slice",arguments)},e.prototype.indexOf=function(a,b){return this.wrapReadFunction("indexOf",arguments)},e.prototype.lastIndexOf=function(a,b){return this.wrapReadFunction("lastIndexOf",arguments)},e.prototype.every=function(a,b){return this.wrapReadFunction("every",arguments)},e.prototype.some=function(a,b){return this.wrapReadFunction("some",arguments)},e.prototype.forEach=function(a,b){return this.wrapReadFunction("forEach",arguments)},e.prototype.map=function(a,b){return this.wrapReadFunction("map",arguments)},e.prototype.filter=function(a,b){return this.wrapReadFunction("filter",arguments)},e.prototype.reduce=function(a,b){return this.wrapReadFunction("reduce",arguments)},e.prototype.reduceRight=function(a,b){return this.wrapReadFunction("reduceRight",arguments)},e.prototype.wrapReadFunction=function(a,b){var c=Array.prototype[a];return(e.prototype[a]=function(){return this.dependencyState.notifyObserved(),c.apply(this._values,arguments)}).apply(this,b)},e.prototype.sideEffectWarning=function(c){a.debugLevel>0&&b.DNode.trackingStack.length>0&&b.warn("[Mobservable.Array] The method array."+c+" should probably not be used inside observable functions since it has side-effects")},e}(e);b.ObservableArray=f;var g=0,h=[];d(1e3)}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){function b(b){var c=b.prototype.componentWillMount,d=b.prototype.componentWillUnmount;return b.prototype.componentWillMount=function(){a.ObserverMixin.componentWillMount.apply(this,arguments),c&&c.apply(this,arguments)},b.prototype.componentWillUnmount=function(){a.ObserverMixin.componentWillUnmount.apply(this,arguments),d&&d.apply(this,arguments)},b.prototype.shouldComponentUpdate||(b.prototype.shouldComponentUpdate=a.ObserverMixin.shouldComponentUpdate),b}a.reactiveMixin={componentWillMount:function(){var b=this.render;this.render=function(){var c=this;this._watchDisposer&&this._watchDisposer();var d=a.observeUntilInvalid(function(){return b.call(c)},function(){c.forceUpdate()}),e=d[0],f=d[1];return this._watchDisposer=f,e}},componentWillUnmount:function(){this._watchDisposer&&this._watchDisposer()},shouldComponentUpdate:function(a,b){if(this.state!==b)return!0;var c,d=Object.keys(this.props);if(d.length!==Object.keys(a).length)return!0;for(var e=d.length-1;c=d[e];e--)if(a[c]!==this.props[c])return!0;return!1}},a.ObserverMixin=a.reactiveMixin,a.reactiveComponent=b,a.ObservingComponent=a._.wrapDeprecated("ObservingComponent",b)}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(a){var b=function(){function a(){}return a.schedule=function(b){a.inBatch<1?b():a.tasks[a.tasks.length]=b},a.runPostBatchActions=function(){for(var b=0;a.tasks.length;)try{for(;b<a.tasks.length;b++)a.tasks[b]();a.tasks=[]}catch(c){console.error("Failed to run scheduled action, the action has been dropped from the queue: "+c,c),a.tasks.splice(0,b+1)}},a.batch=function(b){a.inBatch+=1;try{return b()}finally{0===--a.inBatch&&(a.inBatch+=1,a.runPostBatchActions(),a.inBatch-=1)}},a.inBatch=0,a.tasks=[],a}();a.Scheduler=b}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(a){var b=function(){function b(){this.listeners=[]}return b.prototype.emit=function(){var a=this.listeners.slice(),b=a.length;switch(arguments.length){case 0:for(var c=0;b>c;c++)a[c]();break;case 1:for(var d=arguments[0],c=0;b>c;c++)a[c](d);break;default:for(var c=0;b>c;c++)a[c].apply(null,arguments)}},b.prototype.on=function(b){var c=this;return this.listeners.push(b),a.once(function(){var a=c.listeners.indexOf(b);-1!==a&&c.listeners.splice(a,1)})},b.prototype.once=function(a){var b=this.on(function(){b(),a.apply(this,arguments)});return b},b}();a.SimpleEventEmitter=b}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(a){function b(a){console&&console.warn("[mobservable:warning] "+a)}function c(a){var b=!1;return function(){return b?void 0:(b=!0,a.apply(this,arguments))}}function d(){}function e(a){return null!==a&&"object"==typeof a&&Object.getPrototypeOf(a)===Object.prototype}function f(a,b){if(!b||!b.length)return[a,[]];if(!a||!a.length)return[[],b];for(var c=[],d=[],e=0,f=0,g=a.length,h=!1,i=0,j=0,k=b.length,l=!1,m=!1;!m&&!h;){if(!l){if(g>e&&k>i&&a[e]===b[i]){if(e++,i++,e===g&&i===k)return[c,d];continue}f=e,j=i,l=!0}j+=1,f+=1,j>=k&&(m=!0),f>=g&&(h=!0),h||a[f]!==b[i]?m||b[j]!==a[e]||(d.push.apply(d,b.slice(i,j)),i=j+1,e++,l=!1):(c.push.apply(c,a.slice(e,f)),e=f+1,i++,l=!1)}return c.push.apply(c,a.slice(e)),d.push.apply(d,b.slice(i)),[c,d]}a.warn=b,a.once=c,a.noop=d,a.isPlainObject=e,a.quickDiff=f}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(a){var b=function(){function b(b,c){this.expr=b,this.onInvalidate=c,this.dependencyState=new a.DNode(this),this.didEvaluate=!1,this.dependencyState.computeNextState()}return b.prototype.compute=function(){return this.didEvaluate?(this.dispose(),this.onInvalidate()):(this.didEvaluate=!0,this.value=this.expr()),!1},b.prototype.dispose=function(){this.dependencyState.dispose()},b}();a.WatchedExpression=b}(b=a._||(a._={}))}(mobservable||(mobservable={}));var forCompilerVerificationOnly=mobservable;!function(a,b){"function"==typeof define&&define.amd?define("mobservable",[],function(){return b()}):"object"==typeof exports?module.exports=b():a.mobservable=b()}(this,function(){var a=mobservable.makeReactive;for(var b in mobservable)a[b]=mobservable[b];return a});
var mobservable;!function(a){var b;!function(b){var c=function(){function c(a,c){this.value=a,this.recurse=c,this.changeEvent=new b.SimpleEventEmitter,this.dependencyState=new b.DNode(this),this._value=this.makeReferenceValueReactive(a)}return c.prototype.makeReferenceValueReactive=function(c){return this.recurse&&(Array.isArray(c)||b.isPlainObject(c))?a.makeReactive(c):c},c.prototype.set=function(a){if(a!==this._value){var b=this._value;this.dependencyState.markStale(),this._value=this.makeReferenceValueReactive(a),this.dependencyState.markReady(!0),this.changeEvent.emit(this._value,b)}},c.prototype.get=function(){return this.dependencyState.notifyObserved(),this._value},c.prototype.observe=function(a,c){var d=this;void 0===c&&(c=!1),this.dependencyState.setRefCount(1),c&&a(this.get(),void 0);var e=this.changeEvent.on(a);return b.once(function(){d.dependencyState.setRefCount(-1),e()})},c.prototype.createGetterSetter=function(){var a=this,c=function(b){return arguments.length>0?void a.set(b):a.get()};return c.impl=this,c.observe=function(b,c){return a.observe(b,c)},c.toString=function(){return a.toString()},b.markReactive(c),c},c.prototype.toString=function(){return"Observable["+this._value+"]"},c}();b.ObservableValue=c}(b=a._||(a._={}))}(mobservable||(mobservable={}));var __extends=this&&this.__extends||function(a,b){function c(){this.constructor=a}for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);c.prototype=b.prototype,a.prototype=new c},mobservable;!function(a){var b;!function(b){var c=function(c){function d(a,b){if(c.call(this,void 0,!1),this.func=a,this.scope=b,this.isComputing=!1,this.hasError=!1,"function"!=typeof a)throw new Error("ComputedObservable requires a function")}return __extends(d,c),d.prototype.get=function(){if(this.isComputing)throw new Error("Cycle detected");var c=this.dependencyState;if(c.isSleeping?b.DNode.trackingStack.length>0?(c.wakeUp(),c.notifyObserved()):this.compute():c.notifyObserved(),c.hasCycle)throw new Error("Cycle detected");if(this.hasError)throw a.debugLevel&&(console.trace(),b.warn(this+": rethrowing caught exception to observer: "+this._value+(this._value.cause||""))),this._value;return this._value},d.prototype.set=function(a){throw new Error(this.toString()+": A computed observable does not accept new values!")},d.prototype.compute=function(){var a;try{if(this.isComputing)throw new Error("Cycle detected");this.isComputing=!0,a=this.func.call(this.scope),this.hasError=!1}catch(b){this.hasError=!0,console.error(this+"Caught error during computation: ",b),b instanceof Error?a=b:(a=new Error("MobservableComputationError"),a.cause=b)}if(this.isComputing=!1,a!==this._value){var c=this._value;return this._value=a,this.changeEvent.emit(a,c),!0}return!1},d.prototype.toString=function(){return"ComputedObservable["+this.func.toString()+"]"},d}(b.ObservableValue);b.ComputedObservable=c}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(b){function c(){return e.trackingStack.length}!function(a){a[a.STALE=0]="STALE",a[a.PENDING=1]="PENDING",a[a.READY=2]="READY"}(b.DNodeState||(b.DNodeState={}));var d=b.DNodeState,e=function(){function c(a){this.owner=a,this.state=d.READY,this.isSleeping=!0,this.hasCycle=!1,this.observing=[],this.prevObserving=null,this.observers=[],this.dependencyChangeCount=0,this.dependencyStaleCount=0,this.isDisposed=!1,this.externalRefenceCount=0,this.isComputed=void 0!==a.compute}return c.prototype.setRefCount=function(a){var b=this.externalRefenceCount+=a;0===b?this.tryToSleep():b===a&&this.wakeUp()},c.prototype.addObserver=function(a){this.observers[this.observers.length]=a},c.prototype.removeObserver=function(a){var b=this.observers,c=b.indexOf(a);-1!==c&&(b.splice(c,1),0===b.length&&this.tryToSleep())},c.prototype.markStale=function(){this.state===d.READY&&(this.state=d.STALE,this.notifyObservers())},c.prototype.markReady=function(a){this.state!==d.READY&&(this.state=d.READY,this.notifyObservers(a))},c.prototype.notifyObservers=function(a){void 0===a&&(a=!1);for(var b=this.observers.slice(),c=b.length,d=0;c>d;d++)b[d].notifyStateChange(this,a)},c.prototype.tryToSleep=function(){if(!this.isSleeping&&this.isComputed&&0===this.observers.length&&0===this.externalRefenceCount){for(var a=0,b=this.observing.length;b>a;a++)this.observing[a].removeObserver(this);this.observing=[],this.isSleeping=!0}},c.prototype.wakeUp=function(){this.isSleeping&&this.isComputed&&(this.isSleeping=!1,this.state=d.PENDING,this.computeNextState())},c.prototype.notifyStateChange=function(a,c){var e=this;a.state===d.STALE?1===++this.dependencyStaleCount&&this.markStale():(c&&(this.dependencyChangeCount+=1),0===--this.dependencyStaleCount&&(this.state=d.PENDING,b.Scheduler.schedule(function(){e.dependencyChangeCount>0?e.computeNextState():e.markReady(!1),e.dependencyChangeCount=0})))},c.prototype.computeNextState=function(){this.trackDependencies();var a=this.owner.compute();this.bindDependencies(),this.markReady(a)},c.prototype.trackDependencies=function(){this.prevObserving=this.observing,c.trackingStack[c.trackingStack.length]=[]},c.prototype.bindDependencies=function(){this.observing=c.trackingStack.pop(),this.isComputed&&0===this.observing.length&&a.debugLevel>1&&!this.isDisposed&&(console.trace(),b.warn("You have created a function that doesn't observe any values, did you forget to make its dependencies observable?"));var d=b.quickDiff(this.observing,this.prevObserving),e=d[0],f=d[1];this.prevObserving=null;for(var g=0,h=f.length;h>g;g++)f[g].removeObserver(this);this.hasCycle=!1;for(var g=0,h=e.length;h>g;g++)this.isComputed&&e[g].findCycle(this)?(this.hasCycle=!0,this.observing.splice(this.observing.indexOf(e[g]),1),e[g].hasCycle=!0):e[g].addObserver(this)},c.prototype.notifyObserved=function(){var a=c.trackingStack,b=a.length;if(b>0){var d=a[b-1],e=d.length;d[e-1]!==this&&d[e-2]!==this&&(d[e]=this)}},c.prototype.findCycle=function(a){var b=this.observing;if(-1!==b.indexOf(a))return!0;for(var c=b.length,d=0;c>d;d++)if(b[d].findCycle(a))return!0;return!1},c.prototype.dispose=function(){if(this.observers.length)throw new Error("Cannot dispose DNode; it is still being observed");if(this.observing)for(var a=this.observing.length,b=0;a>b;b++)this.observing[b].removeObserver(this);this.observing=null,this.isDisposed=!0},c.trackingStack=[],c}();b.DNode=e,b.stackDepth=c}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){function b(a,b){if(e(a))return a;b=b||{},a instanceof m.AsReference&&(a=a.value,b.as="reference");var d=b.recurse!==!1,f="reference"===b.as?l.Reference:c(a);switch(f){case l.Reference:case l.ComplexObject:return m.makeReactiveReference(a,!1);case l.ComplexFunction:throw new Error("[mobservable:error] Creating reactive functions from functions with multiple arguments is currently not supported, see https://github.com/mweststrate/mobservable/issues/12");case l.ViewFunction:return new m.ComputedObservable(a,b.scope).createGetterSetter();case l.Array:return new m.ObservableArray(a,d);case l.PlainObject:return m.extendReactive({},a,d)}throw"Illegal State"}function c(a){return null===a||void 0===a?l.Reference:"function"==typeof a?a.length?l.ComplexFunction:l.ViewFunction:Array.isArray(a)||a instanceof m.ObservableArray?l.Array:"object"==typeof a?m.isPlainObject(a)?l.PlainObject:l.ComplexObject:l.Reference}function d(a){return new m.AsReference(a)}function e(a){if(null===a||void 0===a)return!1;switch(typeof a){case"array":case"object":case"function":return a.__isReactive===!0}return!1}function f(a,b){var c=new m.ComputedObservable(a,b),d=c.observe(m.noop);return 0===c.dependencyState.observing.length&&m.warn("mobservable.sideEffect: not a single observable was used inside the side-effect function. Side-effect would be a no-op."),d}function g(a,b){m.extendReactive(a,b,!0)}function h(a,b,c){var d=c?c.value:null;"function"==typeof d?(delete c.value,delete c.writable,c.configurable=!0,c.get=function(){var a=this.key=new m.ComputedObservable(d,this).createGetterSetter();return a},c.set=function(){throw console.trace(),new Error("It is not allowed to reassign observable functions")}):Object.defineProperty(a,b,{configurable:!0,enumberable:!0,get:function(){return m.defineReactiveProperty(this,b,void 0,!0),this[b]},set:function(a){m.defineReactiveProperty(this,b,a,!0)}})}function i(a){if(!a)return a;if(Array.isArray(a)||a instanceof m.ObservableArray)return a.map(i);if("object"==typeof a){var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=i(a[c]));return b}return a}function j(a){return m.Scheduler.batch(a)}function k(a,b){var c=new m.WatchedExpression(a,b);return[c.value,function(){return c.dispose()}]}var l;!function(a){a[a.Reference=0]="Reference",a[a.PlainObject=1]="PlainObject",a[a.ComplexObject=2]="ComplexObject",a[a.Array=3]="Array",a[a.ViewFunction=4]="ViewFunction",a[a.ComplexFunction=5]="ComplexFunction"}(l||(l={})),a.makeReactive=b,a.asReference=d,a.isReactive=e,a.sideEffect=f,a.extendReactive=g,a.observable=h,a.toJson=i,a.transaction=j,a.observeUntilInvalid=k,a.debugLevel=0;var m;!function(a){function b(a,b,c){h(a);for(var e in b)d(a,e,b[e],c);return a}function d(b,d,e,f){var h;e instanceof i?(e=e.value,h=l.Reference,f=!1):h=c(e);var j;switch(h){case l.Reference:case l.ComplexObject:j=g(e,!1);break;case l.ViewFunction:j=new a.ComputedObservable(e,b).createGetterSetter();break;case l.ComplexFunction:a.warn("Storing reactive functions in objects is not supported yet, please use flag 'recurse:false' or wrap the function in 'asReference'"),j=g(e,!1);case l.Array:case l.PlainObject:j=g(e,f)}return Object.defineProperty(b,d,{get:j,set:j,enumerable:!0,configurable:!1}),b}function f(b){if(e(b))return b;if(b instanceof i)return b=b.value;switch(c(b)){case l.Reference:case l.ComplexObject:return b;case l.ViewFunction:case l.ComplexFunction:return a.warn("Storing reactive functions in arrays is not supported, please use flag 'recurse:false' or wrap the function in 'asReference'"),b;case l.Array:return new a.ObservableArray(b,!0);case l.PlainObject:return a.extendReactive({},b,!0)}throw"Illegal State"}function g(b,c){return new a.ObservableValue(b,c).createGetterSetter()}function h(a){Object.defineProperty(a,"__isReactive",{enumerable:!1,value:!0})}a.extendReactive=b,a.defineReactiveProperty=d,a.makeReactiveArrayItem=f,a.makeReactiveReference=g,a.markReactive=h;var i=function(){function a(a){this.value=a}return a}();a.AsReference=i}(m=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(b){function c(a){var b={enumerable:!1,configurable:!1,set:function(b){if(a<this._values.length){var c=this._values[a];c!==b&&(this._values[a]=b,this.notifyChildUpdate(a,c))}else{if(a!==this._values.length)throw new Error("ObservableArray: Index out of bounds, "+a+" is larger than "+this.values.length);this.push(b)}},get:function(){return a<this._values.length?(this.dependencyState.notifyObserved(),this._values[a]):void 0}};Object.defineProperty(f.prototype,""+a,b),b.enumerable=!0,b.configurable=!0,h[a]=b}function d(a){for(var b=g;a>b;b++)c(b);g=a}var e=function(){function a(){}return a}();e.prototype=[];var f=function(c){function e(a,d){c.call(this),b.markReactive(this),Object.defineProperties(this,{recurse:{enumerable:!1,value:d},dependencyState:{enumerable:!1,value:new b.DNode(this)},_values:{enumerable:!1,value:a?d?a.map(b.makeReactiveArrayItem):a.slice():[]},changeEvent:{enumerable:!1,value:new b.SimpleEventEmitter}}),a&&a.length&&this.updateLength(0,a.length)}return __extends(e,c),Object.defineProperty(e.prototype,"length",{get:function(){return this.dependencyState.notifyObserved(),this._values.length},set:function(a){if("number"!=typeof a||0>a)throw new Error("Out of range: "+a);var b=this._values.length;a!==b&&(a>b?this.spliceWithArray(b,0,new Array(a-b)):this.spliceWithArray(a,b-a))},enumerable:!0,configurable:!0}),e.prototype.updateLength=function(a,b){if(0>b)for(var c=a+b;a>c;c++)delete this[c];else if(b>0){a+b>g&&d(a+b);for(var c=a,e=a+b;e>c;c++)Object.defineProperty(this,""+c,h[c])}},e.prototype.spliceWithArray=function(a,c,d){var e=this._values.length;if(!(void 0!==d&&0!==d.length||0!==c&&0!==e))return[];void 0===a?a=0:a>e?a=e:0>a&&(a=Math.max(0,e+a)),c=1===arguments.length?e-a:void 0===c||null===c?0:Math.max(0,Math.min(c,e-a)),void 0===d?d=[]:this.recurse&&(d=d.map(b.makeReactiveArrayItem));var f=d.length-c,g=(h=this._values).splice.apply(h,[a,c].concat(d));return this.updateLength(e,f),this.notifySplice(a,g,d),g;var h},e.prototype.notifyChildUpdate=function(a,b){this.notifyChanged(),this.changeEvent.emit({object:this,type:"update",index:a,oldValue:b})},e.prototype.notifySplice=function(a,b,c){(0!==b.length||0!==c.length)&&(this.notifyChanged(),this.changeEvent.emit({object:this,type:"splice",index:a,addedCount:c.length,removed:b}))},e.prototype.notifyChanged=function(){this.dependencyState.markStale(),this.dependencyState.markReady(!0)},e.prototype.observe=function(a,b){return void 0===b&&(b=!1),b&&a({object:this,type:"splice",index:0,addedCount:this._values.length,removed:[]}),this.changeEvent.on(a)},e.prototype.clear=function(){return this.splice(0)},e.prototype.replace=function(a){return this.spliceWithArray(0,this._values.length,a)},e.prototype.values=function(){return this.dependencyState.notifyObserved(),this._values.slice()},e.prototype.toJSON=function(){return this.dependencyState.notifyObserved(),this._values.slice()},e.prototype.clone=function(){return this.dependencyState.notifyObserved(),new e(this._values,this.recurse)},e.prototype.find=function(a,b,c){void 0===c&&(c=0),this.dependencyState.notifyObserved();for(var d=this._values,e=d.length,f=c;e>f;f++)if(a.call(b,d[f],f,this))return d[f];return null},e.prototype.splice=function(a,b){for(var c=[],d=2;d<arguments.length;d++)c[d-2]=arguments[d];switch(this.sideEffectWarning("splice"),arguments.length){case 0:return[];case 1:return this.spliceWithArray(a);case 2:return this.spliceWithArray(a,b)}return this.spliceWithArray(a,b,c)},e.prototype.push=function(){for(var a=[],b=0;b<arguments.length;b++)a[b-0]=arguments[b];return this.sideEffectWarning("push"),this.spliceWithArray(this._values.length,0,a),this._values.length},e.prototype.pop=function(){return this.sideEffectWarning("pop"),this.splice(Math.max(this._values.length-1,0),1)[0]},e.prototype.shift=function(){return this.sideEffectWarning("shift"),this.splice(0,1)[0]},e.prototype.unshift=function(){for(var a=[],b=0;b<arguments.length;b++)a[b-0]=arguments[b];return this.sideEffectWarning("unshift"),this.spliceWithArray(0,0,a),this._values.length},e.prototype.reverse=function(){return this.sideEffectWarning("reverse"),this.replace(this._values.reverse())},e.prototype.sort=function(a){return this.sideEffectWarning("sort"),this.replace(this._values.sort.apply(this._values,arguments))},e.prototype.remove=function(a){this.sideEffectWarning("remove");var b=this._values.indexOf(a);return b>-1?(this.splice(b,1),!0):!1},e.prototype.toString=function(){return this.wrapReadFunction("toString",arguments)},e.prototype.toLocaleString=function(){return this.wrapReadFunction("toLocaleString",arguments)},e.prototype.concat=function(){return this.wrapReadFunction("concat",arguments)},e.prototype.join=function(a){return this.wrapReadFunction("join",arguments)},e.prototype.slice=function(a,b){return this.wrapReadFunction("slice",arguments)},e.prototype.indexOf=function(a,b){return this.wrapReadFunction("indexOf",arguments)},e.prototype.lastIndexOf=function(a,b){return this.wrapReadFunction("lastIndexOf",arguments)},e.prototype.every=function(a,b){return this.wrapReadFunction("every",arguments)},e.prototype.some=function(a,b){return this.wrapReadFunction("some",arguments)},e.prototype.forEach=function(a,b){return this.wrapReadFunction("forEach",arguments)},e.prototype.map=function(a,b){return this.wrapReadFunction("map",arguments)},e.prototype.filter=function(a,b){return this.wrapReadFunction("filter",arguments)},e.prototype.reduce=function(a,b){return this.wrapReadFunction("reduce",arguments)},e.prototype.reduceRight=function(a,b){return this.wrapReadFunction("reduceRight",arguments)},e.prototype.wrapReadFunction=function(a,b){var c=Array.prototype[a];return(e.prototype[a]=function(){return this.dependencyState.notifyObserved(),c.apply(this._values,arguments)}).apply(this,b)},e.prototype.sideEffectWarning=function(c){a.debugLevel>0&&b.DNode.trackingStack.length>0&&b.warn("[Mobservable.Array] The method array."+c+" should probably not be used inside observable functions since it has side-effects")},e}(e);b.ObservableArray=f;var g=0,h=[];d(1e3)}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){function b(b){var c=b.prototype.componentWillMount,d=b.prototype.componentWillUnmount;return b.prototype.componentWillMount=function(){a.reactiveMixin.componentWillMount.apply(this,arguments),c&&c.apply(this,arguments)},b.prototype.componentWillUnmount=function(){a.reactiveMixin.componentWillUnmount.apply(this,arguments),d&&d.apply(this,arguments)},b.prototype.shouldComponentUpdate||(b.prototype.shouldComponentUpdate=a.reactiveMixin.shouldComponentUpdate),b}a.reactiveMixin={componentWillMount:function(){var b=this.render;this.render=function(){var c=this;this._watchDisposer&&this._watchDisposer();var d=a.observeUntilInvalid(function(){return b.call(c)},function(){c.forceUpdate()}),e=d[0],f=d[1];return this._watchDisposer=f,e}},componentWillUnmount:function(){this._watchDisposer&&this._watchDisposer()},shouldComponentUpdate:function(a,b){if(this.state!==b)return!0;var c,d=Object.keys(this.props);if(d.length!==Object.keys(a).length)return!0;for(var e=d.length-1;c=d[e];e--)if(a[c]!==this.props[c])return!0;return!1}},a.reactiveComponent=b}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(a){var b=function(){function a(){}return a.schedule=function(b){a.inBatch<1?b():a.tasks[a.tasks.length]=b},a.runPostBatchActions=function(){for(var b=0;a.tasks.length;)try{for(;b<a.tasks.length;b++)a.tasks[b]();a.tasks=[]}catch(c){console.error("Failed to run scheduled action, the action has been dropped from the queue: "+c,c),a.tasks.splice(0,b+1)}},a.batch=function(b){a.inBatch+=1;try{return b()}finally{0===--a.inBatch&&(a.inBatch+=1,a.runPostBatchActions(),a.inBatch-=1)}},a.inBatch=0,a.tasks=[],a}();a.Scheduler=b}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(a){var b=function(){function b(){this.listeners=[]}return b.prototype.emit=function(){var a=this.listeners.slice(),b=a.length;switch(arguments.length){case 0:for(var c=0;b>c;c++)a[c]();break;case 1:for(var d=arguments[0],c=0;b>c;c++)a[c](d);break;default:for(var c=0;b>c;c++)a[c].apply(null,arguments)}},b.prototype.on=function(b){var c=this;return this.listeners.push(b),a.once(function(){var a=c.listeners.indexOf(b);-1!==a&&c.listeners.splice(a,1)})},b.prototype.once=function(a){var b=this.on(function(){b(),a.apply(this,arguments)});return b},b}();a.SimpleEventEmitter=b}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(a){function b(a){console&&console.warn("[mobservable:warning] "+a)}function c(a){var b=!1;return function(){return b?void 0:(b=!0,a.apply(this,arguments))}}function d(){}function e(a){return null!==a&&"object"==typeof a&&Object.getPrototypeOf(a)===Object.prototype}function f(a,b){if(!b||!b.length)return[a,[]];if(!a||!a.length)return[[],b];for(var c=[],d=[],e=0,f=0,g=a.length,h=!1,i=0,j=0,k=b.length,l=!1,m=!1;!m&&!h;){if(!l){if(g>e&&k>i&&a[e]===b[i]){if(e++,i++,e===g&&i===k)return[c,d];continue}f=e,j=i,l=!0}j+=1,f+=1,j>=k&&(m=!0),f>=g&&(h=!0),h||a[f]!==b[i]?m||b[j]!==a[e]||(d.push.apply(d,b.slice(i,j)),i=j+1,e++,l=!1):(c.push.apply(c,a.slice(e,f)),e=f+1,i++,l=!1)}return c.push.apply(c,a.slice(e)),d.push.apply(d,b.slice(i)),[c,d]}a.warn=b,a.once=c,a.noop=d,a.isPlainObject=e,a.quickDiff=f}(b=a._||(a._={}))}(mobservable||(mobservable={}));var mobservable;!function(a){var b;!function(a){var b=function(){function b(b,c){this.expr=b,this.onInvalidate=c,this.dependencyState=new a.DNode(this),this.didEvaluate=!1,this.dependencyState.computeNextState()}return b.prototype.compute=function(){return this.didEvaluate?(this.dispose(),this.onInvalidate()):(this.didEvaluate=!0,this.value=this.expr()),!1},b.prototype.dispose=function(){this.dependencyState.dispose()},b}();a.WatchedExpression=b}(b=a._||(a._={}))}(mobservable||(mobservable={}));var forCompilerVerificationOnly=mobservable;!function(a,b){"function"==typeof define&&define.amd?define("mobservable",[],function(){return b()}):"object"==typeof exports?module.exports=b():a.mobservable=b()}(this,function(){var a=mobservable.makeReactive;for(var b in mobservable)a[b]=mobservable[b];return a});
{
"name": "mobservable",
"version": "0.5.10",
"description": "unobtrusive library to make data structures reactive and create seamlessly self-updating view functions",
"version": "0.6.0",
"description": "Unobtrusive reactive library that keeps views automatically in sync with data.",
"main": "dist/mobservable.js",

@@ -37,2 +37,3 @@ "scripts": {

"reactive",
"model",
"frp",

@@ -39,0 +40,0 @@ "functional-reactive-programming"

# mobservable
[![Build Status](https://travis-ci.org/mweststrate/mobservable.svg?branch=master)](https://travis-ci.org/mweststrate/mobservable)
[![Coverage Status](https://coveralls.io/repos/mweststrate/mobservable/badge.svg?branch=master&service=github)](https://coveralls.io/github/mweststrate/mobservable?branch=master)
[![mobservable channel on slack](https://img.shields.io/badge/slack-mobservable-blue.svg)](http://www.reactiflux.com)
<img src="https://mweststrate.github.io/mobservable/images/mobservable.png" align="right" width="120px" />
[Official homepage](http://mweststrate.github.io/mobservable/)
Installation: `npm install mobservable --save`
##### _Unobtrusive reactive library that keeps views automatically in sync with data._
Mobservable is light-weight standalone transparent reactive programming library to create Reactive primitives, functions, arrays and objects.
[![Build Status](https://travis-ci.org/mweststrate/mobservable.svg?branch=master)](https://travis-ci.org/mweststrate/mobservable)
[![Coverage Status](https://coveralls.io/repos/mweststrate/mobservable/badge.svg?branch=master&service=github)](https://coveralls.io/github/mweststrate/mobservable?branch=master)
[![mobservable channel on slack](https://img.shields.io/badge/slack-mobservable-blue.svg)](https://reactiflux.slack.com/messages/mobservable/)
Its goal is to make developers happy and productive by removing boilerplate work such as invalidating derived data or managing event listeners.
It makes sure data changes are automatically, atomically and synchronously propagated through your app without being obtrusive.
Mobservable runs in any ES5 environment but features also some React add-ons.
It is highly efficient and shines when managing large amounts of complex, cyclic, nested or computed data.
[API documentation](https://github.com/mweststrate/mobservable/blob/master/docs/api.md) - [Typings](https://github.com/mweststrate/mobservable/blob/master/dist/mobservable.d.ts)
Some links that may be interesting:
## Philosophy
* [Slack channel on reactiflux](https://reactiflux.slack.com/messages/mobservable/)
* [Blog post: combining React with Mobservable to create high performing and easily maintainable apps](https://www.mendix.com/tech-blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/)
* [Examples](#examples)
* [Design principles](#design-principles)
* [API documentation](#api-documentation)
* [Advanced Tips & Tricks](#advanced-tips--tricks)
Mobservable is light-weight standalone library to create reactive primitives, functions, arrays and objects.
The goal of mobservable is simple:
1. Write simple views. Views should be subscription free.
2. Write simple controllers and stores. Change data without thinking about how this should be reflected in views.
3. Allow flexible model design, be able to use objects, arrays, classes, real references, and cyclic data structures in your app.
4. Performance: find the absolute minimum amount of changes that are needed to update views.
5. Views should be updated atomically and sychronously without showing stale or intermediate values.
# Examples
Mobservable is born as part of an enterprise scale visual editor,
which needs high performance rendering and covers over 400 different domain concepts.
So the best performance and the simplest possible controller and view code are both of the utmost importance.
See [this blog](https://www.mendix.com/tech-blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/) for more details about that journey.
Mobservable applies reactive programming behind the scenes and is inspired by MVVM frameworks like knockout and ember, yet less obtrusive to use.
* [TodoMVC in obobservable + React](https://rawgit.com/mweststrate/todomvc/immutable-to-observable/examples/react-mobservable/index.html#/), ... and the [diff](https://github.com/mweststrate/todomvc/commit/2e30caeb8c690c914f92081ac01d12097a068a1e) of using observables with react instead of immutables.
* [Fiddle demo: Mobservable + React: simple timer](https://jsfiddle.net/mweststrate/wgbe4guu/)
* [Fiddle demo: Mobservable + React: shop](https://jsfiddle.net/mweststrate/46vL0phw)
* [Fiddle demo: Mobservable + JQuery: shop](http://jsfiddle.net/mweststrate/vxn7qgdw)
## The essentials
The source of all demos can also be found in the [example](/example) folder.
Mobservable can be summarized in two functions that will fundamentally simplify the way you write Reactjs applications. Lets take a look at this really really simple timer application:
## Example: Observable values and functions
```javascript
var timerData = {
secondsPassed: 0
};
The core of `mobservable` consists of observable values, i.e. functions that automatically recompute when an observed value changes,
and the possibility to listen to changing values and updated computations.
setInterval(function() {
this.timerData.secondsPassed++;
}, 1000);
```javascript
var nrOfCatz = mobservable(3);
var nrOfDogs = mobservable(8);
// Create a function that automatically observes values:
var nrOfAnimals = mobservable(function() {
// calling an mobservable without arguments acts as getter
return nrOfCatz() * nrOfDogs();
var Timer = React.createClass({
render: function() {
return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> )
}
});
// Print a message whenever the observable changes:
nrOfAnimals.observe(function(amount) {
console.log("Total: " + amount);
}, true);
// -> Prints: "Total: 11"
// Calling an mobservable with a value acts as setter,
// ...and automatically updates all computations in which it was used
nrOfCatz(34);
// -> Prints: "Total: 42"
React.render(<Timer timerData={timerData} />, document.body);
```
## Example: Observable objects & properties
So what will this app do? It does nothing! The timer increases every second, but the UI never responds to that. After the interval updates the timer we should force the UI to update.
But that is the kind of dependency we want to avoid in our code. So let's apply two simple functions of mobservable instead to fix this issue:
By using `.props`, it is possible to create observable values and functions that can be assigned or read as normal properties.
### mobservable.makeReactive
```javascript
var Person = function(firstName, lastName) {
// Define the observable properties firstName, lastName and fullName on 'this':
mobservable.props(this, {
firstName: firstName,
lastName: lastName,
fullName: function() {
return this.firstName + " " + this.lastName;
}
});
}
The first function is `makeReactive`. It is the swiss knife of mobservable and turns any data structure and function into its reactive counterpart. Objects, arrays, functions; they can all be made reactive. Reactiveness is contagious; new data that is put in reactive data will become reactive as well. To make our timer reactive, just change the first three lines of the code:
var jane = new Person("Jane","Dôh");
// (Computed) Properties can be accessed like any other property:
console.log(jane.fullName);
// prints: "Jan Dôh"
// Properties can be observed as well:
mobsevable.observeProperty(jane, "fullName", console.log);
// values can be assigned directly to observable properties
jane.lastName = "Do";
// prints: "Jane Do"
```
## Example: Observable arrays
`mobservable` provides an observable array implementation (as ES7 polyfill) which is fully ES5 compliant but which will notify dependent computations upon each change.
```javascript
// Create an array, that works by all means as a normal array, except that it is observable!
var someNumbers = mobservable.value([1,2,3]);
// A naive function that sums all the values:
var sum = mobservable.value(function() {
for(var s = 0, i = 0; i < someNumbers.length; i++)
s += someNumbers[i];
return s;
var timerData = mobservable.makeReactive({
secondsPassed: 0
});
sum.observe(console.log);
someNumbers.push(4);
// Prints: 10
someNumbers[2] = 0;
// Prints: 7
someNumbers[someNumbers.length] = 5;
// Prints: 12
```
## Example: TypeScript classes and annotations
### mobservable.reactiveComponent
For TypeScript users, `mobservable` ships with module typings and an `@observable` annotation with which class members can be marked as observable.
The second important function is `reactiveComponent`. It turns a Reactjs component into a reactive one, that responds automatically to changes in data that is used by its render method. It can be used to wrap any react component, either created by using ES6 classes or `createClass`. So to fix the example, just update the timer definition to:
```typescript
/// <reference path="./node_modules/mobservable/dist/mobservable.d.ts"/>
import {observable, sideEffect} from "mobservable";
class Order {
@observable orderLines: OrderLine[] = [];
@observable get total() {
return this.orderLines.reduce((sum, orderLine) => sum + orderLine.total, 0)
}
}
class OrderLine {
@observable price:number = 0;
@observable amount:number = 1;
constructor(price) {
this.price = price;
}
@observable get total() {
return "Total: " + this.price * this.amount;
}
}
var order1 = new Order();
sideEffect(() => console.log("Total: " + order1.total));
order1.orderLines.push(new OrderLine(7));
// Prints: Total: 7
order1.orderLines.push(new OrderLine(12));
// Prints: Total: 12
order1.orderLines[0].amount = 3;
// Prints: Total: 33
```
## Example: ObservingComponent for React components
Mobservable ships with a mixin and class decorator that can be used to subscribe React components to observables automatically.
The full JSX example can be found in this [fjsiddle](https://jsfiddle.net/mweststrate/wgbe4guu/)
```javascript
var store = {};
// Add observable properties to the store:
mobservable.props(store, {
timer: 0 // this could be an array, object, function as well..
});
// of course, this could be put flux-style in dispatchable actions, but this is just to demo Model -> View
function resetTimer() {
store.timer = 0;
}
setInterval(function() {
store.timer += 1;
}, 1000);
// This component is actually an observer of all store properties that are accessed during the last rendering
// so there is no need to declare any data use, nor is there (seemingly) any state in this component
// the combination of mobservable.props and ObservingComponent does all the magic for us.
// UI updates are nowhere forced, but all views (un)subscribe to their data automatically
var TimerView = mobservable.ObservingComponent(React.createClass({
render: function() {
return (<span>Seconds passed: {this.props.store.timer}</span>);
}
}));
var TimerApp = React.createClass({
render: function() {
var now = new Date(); // just to demonstrate that TimerView updates independently of TimerApp
return (<div>
<div>Started rendering at: {now.toString()}</div>
<TimerView {...this.props} />
<br/><button onClick={resetTimer}>Reset timer</button>
</div>);
}
});
// pass in the store to the component tree (you could also access it directly through global vars, whatever suits your style)
React.render(<TimerApp store={store} />, document.body);
var Timer = mobservable.reactiveComponent(React.createClass{
/** Omitted */
}));
```
Thats all folks! Its as simple as that. The `Timer` will now automatically update each time `timerData.secondsPassed` is altered.
The actual interesting thing about these changes are the things that are *not* in the code:
# Design principles
* The `setInterval` method didn't alter. It still threads `timerData` as a plain JS object.
* There is no state. Timer is still a dump component.
* There is no magic context being passed through components.
* There are no subscriptions of any kind that need to be managed.
* There is no higher order component that needs configuration; no scopes, lenses or cursors.
* There is no forced UI update in our 'controller'.
* If the `Timer` component would be somewhere deep in our app; only the `Timer` would be re-rendered. Nothing else.
All this missing code... it will scale well into large code-bases!
It does not only work for plain objects, but also for arrays, functions, classes, deeply nested structures.
## Principles
<div align="center">
<img src="https://mweststrate.github.io/mobservable/images/overview.png" height="300"/>
</div>
Mobservable is designed with the following principles in mind.
## A Todo application
- The Model, View (and Controller) of an app should be separated.
Views should be loosely coupled to the UI, so that UI refactorings do not require changes of the data model.
It should be possible to describe views on the data model as naturally as possible, as if data does not change over time, or in other words: as a pure function of state.
- Derived data should be re-calculated automatically and efficiently.
It is the responsibility of mobservable to prevent that views ever become stale.
- Mobservable is unobtrusive and doesnt place any constraints on how you build or work with data structures.
Inheritance, classes, cyclic data structures, or instance methods...? The library does not pose any restrictions on your data.
- Data should be mutable as this is close to the natural mental model of most kinds of data.
<small>Despite some nice properties of immutable data, mutable data is easier to inspect, read, grok and especially more natural to program _explicitly_ against.
`markRead(email) { email.isRead = true; }` is more convenient to write than `markRead(email) { return { ...email, isRead : true }; }` or `markRead(email) { model.set('email', 'isRead', true); }`.
Especially when email is somewhere deep in your model tree.</small>
- Subscriptions should be a breeze to manage, and managed automatically wherever possible.
- Mobservable is only about the model data, not about querying, back-end communication etc. (although observers are really useful there as well).
The following simple todo application can be found up & running on https://mweststrate.github.io/mobservable. A full TodoMVC implementation can be found [here](https://github.com/mweststrate/todomvc/tree/master/examples/react-mobservable)
Note how the array, function and primitive of `todoStore` will all become reactive. There are just three calls to `mobservable` and all the components are kept in sync with the `todoStore`.
## Behavior
Observable values, arrays and functions created by `mobservable` possess the following characteristics:
* _synchronous_ - Updates are processed synchronously, that is, the pseudo expressions `a = 3; b -> a * 2; a = 4; print(b); ` will always print `8`; `b` will never yield a stale value.
* _atomic_ - Computed values will postpone updates until all inputs are settled, to make sure no intermediate values are visible. That is, the expression `a = 3; b -> a * 2; c -> a * b; a = 4; print(c)` will always print `32` and no intermediate values like `24`.
* _real time dependency detection_ - Computed values only depend on values actually used in the last computation, for example, given: `a -> b > 5 ? c : b` the variable `c` will only cause a re-evaluation of `a` if `b > 5`.
* _lazy_ - Computed values will only be evaluated if they are actually being observed. So make sure computed functions are pure and side effect-free; the library might not evaluate expressions as often as you thought it would.
* _cycle detection_ - Cycles in computations, like in `a -> 2 * b; b -> 2 * a;` will be detected.
* _error handling_ - Exceptions that are raised during computations are propagated to consumers.
# API Documentation
The [Typescript typings](https://github.com/mweststrate/mobservable/blob/master/dist/mobservable.d.ts) serve as offline API documentation.
## Creating observables
### mobservable
Shorthand for `mobservable.value`.
### mobservable.value
`mobservable.value<T>(value? : T[], scope? : Object) : IObservableArray<T>`
`mobservable.value<T>(value? : T|()=>T, scope? : Object) : IObservableValue<T>`
Creates an observable given a `value`.
Depending on the type of the function, this function invokes `mobservable.array`, `mobservable.computed` or `mobservable.primitive`.
See the examples above for usage patterns.
The `scope` is only meaningful if a function is passed into this method.
### mobservable.primitive
`mobservable.primitive<T>(value? : T) : IObservableValue<T>`
Creates a new observable, initialized with the given `value` that can change over time.
The returned observable is a function that without arguments acts as getter, and with arguments as setter.
Furthermore, its value can be observed using the `.observe` method, see `IObservableValue.observe`.
Example:
```
var vat = mobservable.primitive(3);
console.log(vat()); // prints '3'
vat.observe(console.log); // register an observer
vat(4); // updates value, also notifies all observers, thus prints '4'
```
### mobservable.reference
`mobservable.reference<T>(value? : T) : IObservableValue<T>`
Synonym for `mobservable.primitive`, since the equality of primitives is determined in the same way as references, namely by strict equality.
(From version 0.6, see `mobservable.struct` if values need to be compared structurally by using deep equality.)
### mobservable.computed
`mobservable.computed<T>(expr : () => T, scope?) : IObservableValue<T>`
Turns a function into an observable value.
The provided `expr` should not have any arguments, but instead rely on other observables that are in scope to determine its value.
The latest value returned by `expr` determines the value of the observable.
When one of the observables used in `expr` changes, `computed` will make sure that the function gets re-evaluated, and all updates are propagated to the children.
```javascript
var amount = mobservable(3);
var price = mobservable(2);
var total = mobservable.computed(function() {
return amount() * price();
});
console.log(total()); // gets the current value, prints '6'
total.observe(console.log); // attach listener
amount(4); // update amount, total gets re-evaluated automatically and will print '8'
amount(4); // update amount, but total will not be re-evaluated since the value didn't change
```
The optional `scope` parameter defines `this` context during the evaluation of `expr`.
`computed` will try to reduce the amount of re-evaluates of `expr` as much as possible.
For that reason the function *should* be pure, that is:
* The result of `expr` should only be defined in terms of other observables, and not depend on any other state.
* Your code shouldn't rely on any side-effects, triggered by `expr`; `expr` should be side-effect free.
* The result of `expr` should always be the same if none of the observed observables did change.
It is not allowed for `expr` to have an (implicit) dependency on its own value.
It is allowed to throw exceptions in an observed function.
The thrown exceptions might only be detected late.
The exception will be re-thrown if somebody inspects the current value, and will be passed as first callback argument
to all the listeners.
### mobservable.expr
`mobservable.expr<T>(expr : ()=>T, scope?) : T`
This function is simply sugar for `mobservable.computed(expr, scope)();`.
`expr` can be used to split up and improve the performance of expensive computations,
as described in this [section](#use-nested-observables-in-expensive-computations).
### mobservable.sideEffect
`mobservable.sideEffect(func:() => void, scope?): ()=>void`
Use this function if you have a function which should produce side effects, even if it is not observed itself.
This is useful for logging, storage backend interaction etc.
Use it whenever you need to transfer observable data to things that don't know how to observe.
`sideEffect` returns a function that can be used to prevent the sideEffect from being triggered in the future.
```javascript
var x = mobservable(3);
var x2 = mobservable(function() {
return x() * 2;
var todoStore = mobservable.makeReactive({
todos: [
{
title: 'Find a clean mug',
completed: true
},
{
title: 'Make coffee',
completed: false
}
],
completedCount: function() {
return this.todos.filter((todo) => todo.completed).length;
},
pending: 0
});
mobservable.sideEffect(function() {
storeInDatabase(x2());
console.log(x2());
});
x(7);
// prints 14
```
todoStore.addTodo = function(title) {
this.todos.push({
title: title,
completed: false
});
};
### mobservable.array
todoStore.removeTodo = function(todo) {
this.todos.splice(this.todos.indexOf(todo), 1);
};
`mobservable.array<T>(values? : T[]) : IObservableArray<T>`
todoStore.loadTodosAsync = function() {
this.pending++;
setTimeout(function() {
this.addTodo('Asynchronously created todo');
this.pending--;
}.bind(this), 2000);
};
**Note: ES5 environments only**
var TodoList = mobservable.reactiveComponent(React.createClass({
render: function() {
var store = this.props.store;
return (<div>
<ul>
{ store.todos.map((todo, idx) =>
(<TodoView store={ store } todo={ todo } key={ idx } />)
) }
{ store.pending ? (<li>Loading more items...</li>) : null }
</ul>
<hr/>
Completed { store.completedCount } of { store.todos.length } items.<br/>
<button onClick={ this.onNewTodo }>New Todo</button>
<button onClick={ this.loadMore }>Load more...</button>
</div>);
},
Constructs an array like, observable structure.
An observable array is a thin abstraction over native arrays and adds observable properties.
The most notable difference between built-in arrays is that these arrays cannot be sparse, i.e. values assigned to an index larger than `length` are considered out-of-bounds and not observed (nor any other property that is assigned to a non-numeric pr negative index).
onNewTodo: function() {
this.props.store.addTodo(prompt('Enter a new todo:', 'Try mobservable at home!'));
},
Furthermore, `Array.isArray(observableArray)` and `typeof observableArray === "array"` will yield `false` for observable arrays, but `observableArray instanceof Array` will return `true`.
```javascript
var numbers = mobservable.array([1,2,3]);
var sum = mobservable.value(function() {
return numbers.reduce(function(a, b) { return a + b }, 0);
});
sum.observe(function(s) { console.log(s); });
numbers[3] = 4;
// prints 10
numbers.push(5,6);
// prints 21
numbers.unshift(10);
// prints 31
```
Observable arrays implement all the ES5 array methods. Besides those, the following methods are available as well:
* `observe(listener:(changeData:IArrayChange<T>|IArraySplice<T>)=>void, fireImmediately?:boolean):Lambda` Listen to changes in this array. The callback will receive arguments that express an array splice or array change, conforming to [ES7 proposal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/observe)
* `clear(): T[]` Remove all current entries from the array.
* `replace(newItems:T[])` Replaces all existing entries in the array with new ones.
* `values(): T[]` Returns a shallow, non-observable clone of the array, similar to `.slice`.
* `clone(): IObservableArray<T>` Create a new observable array containing the same values.
* `find(predicate:(item:T,index:number,array:IObservableArray<T>)=>boolean,thisArg?,fromIndex?:number):T` Find implementation, basically the same as the ES7 Array.find proposal, but with added `fromIndex` parameter.
* `remove(value:T):boolean` Remove a single item by value from the array. Returns true if the item was found and removed.
### mobservable.props
```typescript
props(target:Object, name:string, initialValue: any):Object;
props(target:Object, props:Object):Object;
props(target:Object):Object;
```
**Note: ES5 environments only**
Creates observable properties on the given `target` object.
This function uses `mobservable.value` internally to create observables.
Creating properties has as advantage that they are more convenient to use; see also [value versus props](#value-versus-props).
The original `target`, with the added properties, is returned by this function.
Functions used to created computed observables will automatically be bound to the correct `this`.
```javascript
var order = {};
mobservable.props(order, {
amount: 3,
price: 5,
total: function() {
return this.amount * this.price; // note that no setters are needed
loadMore: function() {
this.props.store.loadTodosAsync();
}
});
order.amount = 4;
console.log(order.total); // Prints '20'
```
}));
Note that observables created by `mobservable.props` do not expose an `.observe` method; to observe properties, see [`mobservable.observeProperty`](#mobservableobserveproperty).
var TodoView = mobservable.reactiveComponent(React.createClass({
render: function() {
var todo = this.props.todo;
return (<li>
<input type='checkbox' checked={ todo.completed } onChange={ this.onToggleCompleted } />
{todo.title}{' '}
<a href='#' onClick={ this.onEdit }>[edit]</a>
<a href='#' onClick={ this.onRemove }>[remove]</a>
</li>);
},
Other forms in which this function can be used:
```javascript
mobservable.props(order, "price", 3); // equivalent to mobservable.props(order, { price: 3 });
var order = mobservable.props({ price: 3}); // uses the original object as target, that is, all values in it are replaced by their observable counterparts
```
onToggleCompleted: function() {
this.props.todo.completed = !this.props.todo.completed;
},
### mobservable.fromJson
`fromJson<T>(value:T):T;`
Converts a JSON object tree into an observable tree that is structurally the same, but completely observable, by combining `.props` and `.array`.
Example:
```javascript
var todos = mobservable.fromJson([
{
title: "write blog"
onEdit: function(e) {
e.preventDefault();
this.props.todo.title = prompt('Todo:', this.props.todo.title);
},
{
title: "improve coverge"
}
]);
mobservable.sideEffect(function() {
console.log(todos.map(todo => todo.title).join(", "));
});
todos[1].title = "improve coverage"; // prints: write blog, improve coverage
todos.push({ title: "take a nap" }); // prints: write blog, improve coverage, take a nap
```
### mobservable.observable annotation
**Note: ES5, TypeScript 1.5+ environments only**
Typescript 1.5 introduces annotations.
The `mobservable.observable` annotation can be used to mark class properties and functions as observable.
This annotations basically wraps `mobservable.props`. Example:
```typescript
/// <reference path='./node_modules/mobservable/dist/mobservable.d.ts'/>
import {observable} from 'mobservable';
class Order {
@observable price:number = 3;
@observable amount:number = 2;
@observable orders = [];
@observable get total() {
return this.amount * this.price * (1 + orders.length);
onRemove: function(e) {
e.preventDefault();
this.props.store.removeTodo(this.props.todo);
}
}
```
}));
Please note that adding the `@observable` annotations to a function does not result in an observable property (as would be the case when using `props`) but in an observable function,
to make sure the compile time type matches the runtime type of the function.
In most cases you probably want to annotate a getter instead.
## Observing changes
### mobservable.observeProperty
`mobservable.observeProperty(object : Object, key : string, listener : Function, invokeImmediately : boolean = false) : Function`
Observes the observable property `key` of `object`.
This is useful if you want to observe *properties* created using the `observable` annotation or the `props` method, since for those properties their own `observe` method is not publicly available.
```javascript
function OrderLine(price) {
mobservable.props(this, {
price: price,
amount: 2,
total: function() {
return this.price * this.amount;
}
});
}
var orderLine = new OrderLine(5);
mobservable.observeProperty(order, 'total', console.log, true); // Prints: '10'
React.render(<TodoList store={todoStore} />, document.getElementById('approot'));
```
### mobservable.watch
`mobservable.watch<T>(func: () => T, onInvalidate : Function) : [T, Function];`
## Getting started
`watch` is quite similar to `mobservable.computed`, but instead of re-evaluating `func` when one of its dependencies has changed, the `onInvalidate` function is triggered.
So `func` will be evaluated only once, and as soon as its value has become stale, the `onInvalidate` callback is triggered.
`watch` returns a tuple consisting of the initial return value of `func` and an `unsubscriber` to be able to abort the watch.
The `onInvalidate` function will be called only once, after that, the watch has finished.
Either:
* `npm install mobservable --save`
* clone the boilerplate repository containing the above example from: https://github.com/mweststrate/react-mobservable-boilerplate
* or fork this [JSFiddle](https://jsfiddle.net/mweststrate/wgbe4guu/)
`watch` is useful in functions where you want to have a function that responds to change, but where the function is actually invoked as side effect or as part of a bigger change flow or where unnecessary recalculations of `func` or either pointless or expensive, e.g. in the `render` method of a React component.
## Examples
### mobservable.batch
* A simple webshop using [React + mobservable](https://jsfiddle.net/mweststrate/46vL0phw) or [JQuery + mobservable](http://jsfiddle.net/mweststrate/vxn7qgdw).
* [Simple timer](https://jsfiddle.net/mweststrate/wgbe4guu/)
* [TodoMVC](https://rawgit.com/mweststrate/todomvc/immutable-to-observable/examples/react-mobservable/index.html#/), based on the ReactJS TodoMVC.
`mobservable.batch<T>(workerFunction : ()=>T):T`
## Read more
Batch postpones the updates of computed properties until the (synchronous) `workerFunction` has completed.
This is useful if you want to apply a bunch of different updates throughout your model before needing the updated computed values, e.g. while refreshing a data from the database.
In practice, you wil probably never need `.batch`, since observables typically update wickedly fast.
* [Making React reactive: the pursuit of high performing, easily maintainable React apps](https://www.mendix.com/tech-blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/)
* [Pure rendering in the light of time and state](https://medium.com/@mweststrate/pure-rendering-in-the-light-of-time-and-state-4b537d8d40b1)
* [Official homepage](http://mweststrate.github.io/mobservable/)
* Advanced [tips & tricks](https://github.com/mweststrate/mobservable/blob/master/docs/tips.md)
```javascript
var amount = mobservable(3);
var price = mobservable(2.5);
var total = mobservable(function() {
return amount() * price();
});
total.observe(console.log);
## Runtime behavior
// without batch:
amount(2); // Prints 5
price(3); // Prints 6
* Reactive views always update synchronously (unless `transaction is used`)
* Reactive views always update atomically, intermediate values will never be visible.
* Reactive functions evaluate lazily and are not processed if they aren't observed.
* Dependency detection is based on actual values to real-time minify the amount of dependencies.
* Cycles are detected automatically.
* Exceptions during computations are propagated to consumers.
// with batch:
mobservable.batch(function() {
amount(3);
price(4);
});
// Prints 12, after completing the batch
```
## FAQ
## Utilities
**Is mobservable a framework?**
### mobservable.toJson
`toJson<T>(value:T):T;`
Mobservabe is *not* a framework. It does not tell you how to structure your code, where to store state or how to process events. Yet it might free you from frameworks that poses all kinds of restrictions on your code in the name of performance.
Converts a non-cyclic tree of observable objects into a JSON structure that is not observable. It is the inverse of `mobservable.fromJson`
**Can I combine flux with mobservable?**
### mobservable.toPlainValue
`mobservable.toPlainValue<T>(any:T):T;`
Flux implementations that do not work on the assumption that the data in their stores is immutable should work well with mobservable.
However, the need for flux is less when using mobservable.
Mobservable already optimizes rendering and since it works with most kinds of data, including cycles and classes.
So other programming paradigms like classic MVC are now can be easily applied in applications that combine ReactJS with mobservable.
Converts a (possibly) observable value into a non-observablue value.
For non-primitive values, this function will always return a shallow copy.
Similar to `.toJson`, but doesn't recurse deeper into the structures
**Can I use mobservable together with framework X?**
### mobservable.ObserverMixin
The observer mixin can be used in [React](https://facebook.github.io/react/index.html) components.
This mixin basically turns the `.render` function of the component into an observable function, and makes sure that the component itself becomes an observer of that function, that the component is re-rendered each time an observable has changed.
This mixin also prevents re-renderings when the *props* of the component have only shallowly changed.
(This is similar to [React PureRender mixin](https://facebook.github.io/react/docs/pure-render-mixin.html), except that *state* changes are still always processed).
This allows for React apps that perform well in apps with large amount of complex data, while avoiding the need to manage a lot of subscriptions.
See the [above example](#example_observingcomponent_for_react_components) or the [JSFiddle demo: Mobservable + React](https://jsfiddle.net/mweststrate/46vL0phw)
For an extensive explanation, read [combing React with Mobservable](https://www.mendix.com/tech-blog/making-react-reactive-pursuit-high-performing-easily-maintainable-react-apps/)
### mobservable.ObservingComponent
`mobservable.ObservingComponent(clazz:ReactComponentClass):ReactComponentClass`
If you want to create a React component based on ES6 where mixins are not supported, you can use the `ObservingComponent` function to wrap around your React `createClass` call (instead of using the mixin `ObserverMixin`):
```javascript
// TODO: change to class
var myComponent = mobservable.ObservingComponent(React.createClass({
// widget specification without mixins
});
```
### mobservable.debugLevel
Numeric property, setting this to value to '1' or higher will cause additional debug information to be printed.
### mobservable.SimpleEventEmitter
Utility class for managing an event. Its instance methods are:
* `new mobservable.SimpleEventEmitter()` Creates a new `SimpleEventEmitter`.
* `emit(...data : any[])` Invokes all registered listeners with the given arguments.
* `on(listener:(...data : any[]) => void) : () => void` Registers a new callback that will be invoked on each `emit`. Returns a method that can be used to unsubscribe the listener.
* `once(listener:(...data : any[]) => void) : () => void` Similar to `.on`, but automatically removes the listener after one invocation.
# Advanced Tips & Tricks
## How to create lazy values?
All computed values are lazy and only evaluated upon first observation (or when their value is explicitly `get`-ted).
## Use local variables in computations
Each time an observable value is read, there is a small performance overhead to keep the dependency tree of computations up-to-date.
Although this might not be noticeable in practice, if you want to squeeze the last bit of performance out of the library:
use local variables as much as possible to reduce the amount of observable reads.
This also holds for array entries and object properties created using `mobservable.props`.
```javascript
var firstName = mobservable('John');
var lastName = mobservable('Do');
// Ok:
var fullName = mobservable(function() {
if (firstName())
return lastName() + ", " + firstName(); // another read of firstName..
return lastName();
}
// Faster:
var fullName = mobservable(function() {
var first = firstName(), last = lastName();
if (first)
return last+ ", " + first;
return last;
}
```
## Use nested observables in expensive computations
It is perfectly fine to create computed observables inside computed observables.
This is a useful pattern if you have an expensive computation that depends on a condition check that is fired often, but not changed often.
For example when your computation contains a cheap threshold check, or when your UI rendering depends on the some selection of the user.
For example:
```javascript
var person; // ...
var total = mobservable(function() {
if (person.age === 42)
doSomeExpensiveComputation();
else
doSomeOtherExpensiveComputation();
});
```
In the example above, every single time the person's `age` changes, `total` is computed by invoking some expensive computations.
However, if the expression `page.age === 42` was put in a separate observable, computing the `total` itself could be avoided in many cases because a re-computation would only occur if the value of the complete expression changes.
Yet, you might not want to create separate stand-alone observables for these expressions, because you don't have a nice place to put them or because it would make the readability of the code worse.
In such cases you can also create an inline observable.
In the following example, the total is only recalculated if the age changes to, or from, 42.
This means that for most other ages, recomputing the expensive computations can be avoided.
```javascript
var person; // ...
var total = mobservable(function() {
var ageEquals42 = mobservable(function() { return person.age === 42 })(); // create observable and invoke getter
if (ageEquals42)
doSomeExpensiveComputation();
else
doSomeOtherExpensiveComputation();
});
```
Note that the dangling `()` (or "dog balls" according to Douglas Crockford) after the expression are meant to invoke the getter of the just created observable to obtain its value.
For convenience the same statement can also be rewritten using the [expr](#mobservableexpr) function:
```javascript
// ...
var ageEquals42 = mobservable.expr(function() { return person.age === 42 });
// ...
```
## Use native array methods
For performance, use built-in array methods as much as possible;
a classic array for loop is registered as multiple reads, while a function call is registered as a single read.
Alternatively, slicing the array before using it will also result in a single read.
```javascript
var numbers = mobservable([1,2,3]);
// Ok:
var sum1 = mobservable(function() {
var s = 0;
for(var i = 0; i < numbers.length; i++) // observable read
s += numbers[i]; // observable reads
return s;
});
// Faster:
var sum2 = mobservable(function() {
var s = 0, localNumbers = numbers.slice(); // observable read
for(var i = 0; i < localNumbers.length; i++)
s += localNumbers[i];
return s;
});
// Faster:
var sum2 = mobservable(function() {
return numbers.reduce(function(a, b) { // single observable read
return a + b;
}, 0);
});
```
## `.value` versus `.props`
The difference between `obj.amount = mobservable.value(3)` and `mobservable.props(obj, { value: 3 })` to create observable values inside an object might seem to be a matter of taste.
Here is a small comparison list between the two approaches.
**.value**
* ES3 compliant
* explicit getter/setter functions: `obj.amount(2)`
* easy to make mistakes in assignments; e.g. `obj.amount = 3` instead of `obj.amount(3)`, or `7 * obj.amount` instead of `7 * obj.amount()`
* easy to manually observe: `obj.amount.observe(listener)`
**.props**
* requires ES5
* object properties with implicit getter/setter: `obj.amount = 2`
* more natural to write / read values, syntactically you won't notice they are observable
* harder to manually observe: `mobservable.observeProperty(obj,'amount',listener)`
## `.reference` versus `.array`
Do *not* confuse `mobservable.reference([])` / `mobservable.primitive([])` with `mobservable([])` / `mobservable.array([])`:
* The first two create a observable reference to an array, but do not observe its contents.
* The later two observe the content of the array you passed into it, which is probably what you intended.
Probably.
Mobservable is framework agnostic and can be applied in any JS environment.
It just ships with a small function to transform Reactjs components into reactive view functions for convenience.
Mobservable works just as well server side, and is already combined with JQuery (see this [Fiddle](http://jsfiddle.net/mweststrate/vxn7qgdw)) and [Deku](https://gist.github.com/mattmccray/d8740ea97013c7505a9b).
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc