UNPKG

relu-core

Version:
559 lines (524 loc) 13.4 kB
var rpPrototype = { _toString: function() { return this.constructor.name + this._info() + "{" + this._getUndependend() + "}" + this._refs; }, _info: function() { return "" }, // events onceChanged: function(l) { if(!this._onceChangedListeners) { this._onceChangedListeners = [l]; } else { this._onceChangedListeners.push(l); } }, onChanged: function(l) { if(!this._changedListeners) { this._changedListeners = [l]; } else { this._changedListeners.push(l); } }, onUpdated: function(l, prio) { if(!this._updatedListeners) { this._updatedListeners = [l]; } else { this._updatedListeners[prio?"unshift":"push"](l); } }, onAdded: function(l, prio) { if(!this._addedListeners) { this._addedListeners = [l]; } else { this._addedListeners[prio?"unshift":"push"](l); } }, onRemoved: function(l, prio) { if(!this._removedListeners) { this._removedListeners = [l]; } else { this._removedListeners[prio?"unshift":"push"](l); } }, onNested: function(l) { if(!this._nestedListeners) { this._nestedListeners = [l]; } else { this._nestedListeners.push(l); } }, onceDisposed: function(l) { if(!this._disposeListeners) { this._disposeListeners = [l]; } else { this._disposeListeners.push(l); } }, removeOnceChangedListener: function(l) { if(!this._onceChangedListeners) return; var idx = this._onceChangedListeners.indexOf(l); if(idx < 0) return; this._onceChangedListeners.splice(idx, 1); }, removeChangedListener: function(l) { if(!this._changedListeners) return; var idx = this._changedListeners.indexOf(l); if(idx < 0) return; this._changedListeners.splice(idx, 1); }, removeUpdatedListener: function(l) { if(!this._updatedListeners) return; var idx = this._updatedListeners.indexOf(l); if(idx < 0) return; this._updatedListeners.splice(idx, 1); }, removeAddedListener: function(l) { if(!this._addedListeners) return; var idx = this._addedListeners.indexOf(l); if(idx < 0) return; this._addedListeners.splice(idx, 1); }, removeRemovedListener: function(l) { if(!this._removedListeners) return; var idx = this._removedListeners.indexOf(l); if(idx < 0) return; this._removedListeners.splice(idx, 1); }, removeNestedListener: function(l) { if(!this._nestedListeners) return; var idx = this._nestedListeners.indexOf(l); if(idx < 0) return; this._nestedListeners.splice(idx, 1); }, removeDisposedListener: function(l) { if(!this._disposeListeners) return; var idx = this._disposeListeners.indexOf(l); if(idx < 0) return; this._disposeListeners.splice(idx, 1); }, _changed: function() { var onceChangedListeners = this._onceChangedListeners && this._onceChangedListeners.slice(); var changedListeners = this._changedListeners && this._changedListeners.slice(); this._onceChangedListeners = undefined; if(onceChangedListeners) for(var i = 0; i < onceChangedListeners.length; i++) onceChangedListeners[i].call(this); if(changedListeners) for(i = 0; i < changedListeners.length; i++) changedListeners[i].call(this); }, _updated: function(newValue, oldValue) { if(!this._updatedListeners) return; var updatedListeners = this._updatedListeners.slice(); for(var i = 0; i < updatedListeners.length; i++) updatedListeners[i].call(this, newValue, oldValue); }, _added: function(idx, newValue) { if(!this._addedListeners) return; var addedListeners = this._addedListeners.slice(); for(var i = 0; i < addedListeners.length; i++) addedListeners[i].call(this, idx, newValue); }, _removed: function(idx, oldValue) { if(!this._removedListeners) return; var removedListeners = this._removedListeners.slice(); for(var i = 0; i < removedListeners.length; i++) removedListeners[i].call(this, idx, oldValue); }, _nested: function(event, path, arg1, arg2) { if(!this._nestedListeners) return; var nestedListeners = this._nestedListeners.slice(); for(var i = 0; i < nestedListeners.length; i++) nestedListeners[i].call(this, event, path, arg1, arg2); }, _disposed: function() { if(!this._disposeListeners) return; var disposeListeners = this._disposeListeners.slice(); for(var i = 0; i < disposeListeners.length; i++) disposeListeners[i].call(this); }, // construct init: function() { var p = this; p._onceChangedListeners = undefined; p._changedListeners = undefined; p._updatedListeners = undefined; p._addedListeners = undefined; p._removedListeners = undefined; p._refs = 1; p._weakRefs = 0; p._temporaryIndex = -1; function _changed() { p._changed(); }; p._changedAtAtomicEnd = function() { base.atAtomicEnd(_changed); }; base.addTemporary(p); }, // tracking depend: function() { base.addDependency(this); }, scope: function() { base.addScopeItem(this); if(base.removeTemporary(this)) this.unref("temporary"); return this; }, // reference counting ref: function() { if(this._refs <= 0) throw new Error("Called ref, but RP is already disposed"); this._refs++; return this; }, unref: function() { this._refs--; if(this._refs === 0) { this._dispose(); } else if(this._refs < this._weakRefs) throw new Error("RP is already disposed (too much unref)"); }, _addWeakRef: function() { this._refs--; this._weakRefs--; }, _dispose: function() { this._disposed(); if(this._attrs) Object.keys(this._attrs).forEach(function(attrName) { this._attrs[attrName].unref(this); }, this); }, temporary: function() { base.addTemporary(this.ref("temporary")); return this; }, isDisposed: function() { return this._refs <= 0; }, // getter g: function() { if(arguments.length === 0) { return this._get(); } else { var p = this; for(var i = 0; i < arguments.length; i++) { var name = arguments[i]; p = p.access(name); } return p; } }, access: function(name) { var me = this; if(base.isRp(name)) { return name.computed(function(name) { return me.access(name); }).delegated(); } if(Array.isArray(name)) { for(var i = 0; i < name.length; i++) me = me.access(name[i]); return me; } if(typeof name === "number") { return this._element(name); } return this._property(name); }, // setter set: function(newValue) { var p = this; var args = arguments; base.atomic(function() { if(args.length <= 1) { p._update(newValue); } else { for(var i = 0; i < args.length - 1; i++) p = p.access(args[i]); p._update(args[args.length-1]); } }); }, modify: function(fn) { var p = this; var args = arguments; base.atomic(function() { if(args.length <= 1) { p._modify(fn); } else { for(var i = 0; i < args.length - 1; i++) p = p.access(args[i]); p._modify(args[args.length-1]); } }); }, // attribute attr: function(name, initial) { if(!this._attrs) this._attrs = {}; var a = this._attrs[name]; if(!a) { a = this._makeAttr(name, initial); this._attrs[name] = a; a.ref(this); } return a.temporary(); }, hasAttr: function(name) { if(!this._attrs) return false; return !!this._attrs[name]; }, // to be overwritten _get: function() { throw new Error("not readable"); }, _getUndependend: function() { throw new Error("not readable"); }, _actionsTarget: function() { throw new Error("not writable"); }, _actionsList: function() { throw new Error("not writable"); }, isConst: function() { return false; }, _update: function(newValue) { var al = this._actionsList(); if(!al) return; al.update([], newValue); }, _modify: function(fn) { var al = this._actionsList(); if(!al) return; al.modify([], fn); }, _splice: function() { var al = this._actionsList(); if(!al) return; al.splice.apply(al, [[]].concat(Array.prototype.slice.call(arguments))); }, _spliceBefore: function() { var al = this._actionsList(); if(!al) return; al.spliceBefore.apply(al, [[]].concat(Array.prototype.slice.call(arguments))); }, }; function base() { var p = function rp() { return p.g.apply(p, arguments); }; p.__proto__ = rpPrototype; p.init(); return p; } base.prototype = rpPrototype; var dependencies = null; var scopeItems = []; base.globalScope = scopeItems; base._refs = []; base.capture = function(fn) { var _d = dependencies; var _s = scopeItems; dependencies = []; scopeItems = []; try { var value = fn(); var result = { dependencies: dependencies, scope: scopeItems, value: value }; return result; } finally { dependencies = _d scopeItems = _s; } }; base.uncaptured = function(fn) { var _d = dependencies; var _s = scopeItems; dependencies = []; scopeItems = []; try { var value = fn(); base.unrefAll(scopeItems, "scope"); return value; } finally { dependencies = _d scopeItems = _s; } }; base.undependend = function(fn) { var _d = dependencies; dependencies = null; try { return fn(); } finally { dependencies = _d } }; base.scope = function(fn) { var result = { items: [], _refs: 1, ref: function() { this._refs++; }, unref: function() { if(--this._refs === 0) { base.unrefAll(this.items.slice(), "scope"); this.items.length = 0; var idx = parentScope.indexOf(this); if(idx < 0) throw new Error("Tried to dispose scope, but it isn't in parentScope anymore"); if(idx >= 0) parentScope.splice(idx, 1); } }, temporary: function() { base.addTemporary(result); }, run: function(fn) { if(this._refs <= 0) return; // Cannot run in disposed scope var _s = scopeItems; scopeItems = this.items; try { return fn(); } finally { scopeItems = _s; } }, add: function() { for(var i = 0; i < arguments.length; i++) { arguments[i].ref(); this.items.push(arguments[i]); } }, scope: function(fn) { return this.run(function() { return base.scope(fn); }); }, value: undefined }; var parentScope = base.addSubScope(result); if(!fn) return result; var _s = scopeItems; scopeItems = result.items; try { result.value = fn(); return result; } finally { scopeItems = _s; } }; base.isCapturing = function() { return !!dependencies; }; base.checkNotCapturing = function() { if(dependencies) throw new Error("modification in a computing function"); }; base.addDependency = function(p) { if(!dependencies) return; if(dependencies.indexOf(p) >= 0) return; dependencies.push(p); }; base.addScopeItem = function(p) { if(scopeItems.indexOf(p) >= 0) return; p.ref("scope"); scopeItems.push(p); }; base.addSubScope = function(s) { scopeItems.push(s); return scopeItems; }; base.refAll = function(ps, o) { for(var i = 0, l = ps.length; i < l; i++) ps[i].ref(o); return ps; }; base.unrefAll = function(ps, o) { for(var i = 0, l = ps.length; i < l; i++) ps[i].unref(o); }; base.atomic = function(fn) { var later = [], after = []; var _aae = base.atAtomicEnd; var _aa = base.afterAtomic; base.atAtomicEnd = function(fn) { if(typeof fn !== "function") throw new Error("fn should be a function"); if(later.indexOf(fn) >= 0) return; later.push(fn); }; base.afterAtomic = function(fn) { after.push(fn); }; fn(); while(later.length > 0) { var l = later; later = []; if(_aae === orginalAtAtomicEnd) { for(var i = 0; i < l.length; i++) l[i](); } else { for(var i = 0; i < l.length; i++) _aae.call(base, l[i]); } } base.atAtomicEnd = _aae; base.afterAtomic = _aa; for(var i = 0; i < after.length; i++) { _aa.call(base, after[i]); } }; function orginalAtAtomicEnd(fn) { throw new Error("Called atAtomicEnd outside of an atomic block"); // atomic checks for this function and execute it directly }; base.atAtomicEnd = orginalAtAtomicEnd; base.afterAtomic = function(fn) { fn(); }; base.isAtomic = function isAtomic() { return base.atAtomicEnd !== orginalAtAtomicEnd; }; var temporary = undefined; base.addTemporary = function(p) { if(!temporary) { temporary = [p]; p._temporaryIndex = 0; base.scheduleTemporary(function() { var t = temporary; temporary = undefined; for(var i = 0; i < t.length; i++) { var p = t[i]; if(p) p._temporaryIndex = -1; } for(var i = 0; i < t.length; i++) { var p = t[i]; if(p) p.unref("temporary"); } }); } else { p._temporaryIndex = temporary.length; temporary.push(p); } }; base.removeTemporary = function(p) { if(!temporary) return false; var idx = p._temporaryIndex; if(idx >= 0) { temporary[idx] = undefined; p._temporaryIndex = -1; return true; } return false; }; base.scheduleTemporary = typeof process === "object" && typeof process.nextTick === "function" ? process.nextTick : function(fn) { setTimeout(fn, 1); }; base.setTemporaryScheduler = function(fn) { base.scheduleTemporary = fn; }; base.isRp = function(x) { return x instanceof base; }; module.exports = base;