UNPKG

relu-core

Version:
512 lines (463 loc) 13.7 kB
var slice = Array.prototype.slice; function ActionsList() { this.x = undefined; this.onChange = function() {}; } module.exports = ActionsList; ActionsList.prototype.update = function(path, newValue) { this.x = ActionsList._makeNested(this.x, path, ActionsList._makeUpdate, newValue); this.onChange(); }; ActionsList.prototype.modify = function(path, fn) { this.x = ActionsList._makeNested(this.x, path, ActionsList._makeModify, fn); this.onChange(); }; ActionsList.prototype.add = function(path, index, newItem) { this.x = ActionsList._makeNested(this.x, path, ActionsList._makeAdd, index, newItem); this.onChange(); }; ActionsList.prototype.addBefore = function(path, index, newItem) { this.x = ActionsList._makeNested(this.x, path, ActionsList._makeAddBefore, index, newItem); this.onChange(); }; ActionsList.prototype.remove = function(path, index) { this.x = ActionsList._makeNested(this.x, path, ActionsList._makeRemove, index); this.onChange(); }; ActionsList.prototype.removeBefore = function(path, index) { this.x = ActionsList._makeNested(this.x, path, ActionsList._makeRemoveBefore, index); this.onChange(); }; ActionsList.prototype.splice = function(path, index, count) { var args = slice.call(arguments, 3); this.x = ActionsList._makeNested(this.x, path, ActionsList._makeSplice, [index, count], args); this.onChange(); }; ActionsList.prototype.spliceBefore = function(path, index, count) { var args = slice.call(arguments, 3); this.x = ActionsList._makeNested(this.x, path, ActionsList._makeSpliceBefore, [index, count], args); this.onChange(); }; ActionsList.prototype.apply = function(def, initialState) { var x = this.x; this.x = undefined; ActionsList._apply(x, def, initialState); }; ActionsList.prototype.reset = function() { this.x = undefined; }; ActionsList.prototype.atPath = function(path) { return new DelegatedActionsList(this, path); }; ActionsList.prototype.atPaths = function(paths) { return new MultiActionsList(paths.map(function(p) { return new DelegatedActionsList(this, p); }, this)); }; /****************************************************************/ function DelegatedActionsList(parent, path) { this.parent = parent; this.path = path; } DelegatedActionsList.prototype.update = function(path, newValue) { this.parent.update(this.path.concat(path), newValue); }; DelegatedActionsList.prototype.modify = function(path, fn) { this.parent.modify(this.path.concat(path), fn); }; DelegatedActionsList.prototype.add = function(path, index, newItem) { this.parent.add(this.path.concat(path), index, newItem); }; DelegatedActionsList.prototype.addBefore = function(path, index, newItem) { this.parent.addBefore(this.path.concat(path), index, newItem); }; DelegatedActionsList.prototype.remove = function(path, index) { this.parent.remove(this.path.concat(path), index); }; DelegatedActionsList.prototype.removeBefore = function(path, index) { this.parent.removeBefore(this.path.concat(path), index); }; DelegatedActionsList.prototype.splice = function(path, index, count) { this.parent.splice.apply(this.parent, [this.path.concat(path)].concat(slice.call(arguments, 1))); }; DelegatedActionsList.prototype.spliceBefore = function(path, index, count) { this.parent.spliceBefore.apply(this.parent, [this.path.concat(path)].concat(slice.call(arguments, 1))); }; DelegatedActionsList.prototype.atPath = function(path) { return this.parent.atPath(this.path.concat(path)); }; DelegatedActionsList.prototype.atPaths = function(paths) { return new MultiActionsList(paths.map(function(p) { return this.atPath(p); }, this)); }; /****************************************************************/ function MultiActionsList(parents) { this.parents = parents; }; MultiActionsList.prototype.update = function(path, newValue) { this.parents.forEach(function(p) { p.update(path, newValue); }); }; MultiActionsList.prototype.modify = function(path, fn) { this.parents.forEach(function(p) { p.modify(path, fn); }); }; MultiActionsList.prototype.add = function(path, index, newItem) { this.parents.forEach(function(p) { p.add(path, index, newItem); }); }; MultiActionsList.prototype.addBefore = function(path, index, newItem) { this.parents.forEach(function(p) { p.addBefore(path, index, newItem); }); }; MultiActionsList.prototype.remove = function(path, index) { this.parents.forEach(function(p) { p.remove(path, index); }); }; MultiActionsList.prototype.removeBefore = function(path, index) { this.parents.forEach(function(p) { p.removeBefore(path, index); }); }; MultiActionsList.prototype.splice = function(path, index, count) { var args = slice.call(arguments, 1); this.parents.forEach(function(p) { p.splice.apply(p, [path].concat(args)); }); }; MultiActionsList.prototype.spliceBefore = function(path, index, count) { var args = slice.call(arguments, 1); this.parents.forEach(function(p) { p.spliceBefore.apply(p, [path].concat(args)); }); }; MultiActionsList.prototype.atPath = function(path) { return new MultiActionsList(this.parents.map(function(p) { return p.atPath(path); })); }; MultiActionsList.prototype.atPaths = function(paths) { return new MultiActionsList(paths.map(function(p) { return this.atPath(p); }, this)); }; /****************************************************************/ ActionsList._makeUpdate = function _makeUpdate(x, newValue) { return {t: 2, v: newValue}; }; ActionsList._makeModify = function _makeModify(x, fn) { if(!x) return {t: 4, m: [fn]}; if(Array.isArray(x) || x.t === 3) throw new Error("Cannot modify non-primitive value:" + x); if(x.t === 4) { x.m.push(fn); return x; } else { x.v = fn(x.v); return x; } }; ActionsList._makeAdd = function _makeAdd(x, idx, item) { if(!x) return [{t: 1, i: idx, v: item}]; if(!Array.isArray(x)) { if(x.t !== 2) throw new Error("Cannot add items to primitive value: " + x); if(!Array.isArray(x.v)) throw new Error("Cannot add items to non-array value: " + x); if(idx === Infinity) x.v.push(item); else x.v.splice(idx, 0, item); return x; } if(idx === Infinity) { x.push({t: 1, i: Infinity, v: item}); return x; } for(var i = x.length-1; i >= 0; i--) { var action = x[i]; var actionIdx = action.i; // Switch action with this action if(action.t !== 1) { x.splice(i+1, 0, {t: 1, i: idx, v: item}); return x; } if(actionIdx < idx) { x.splice(i+1, 0, {t: 1, i: idx, v: item}); return x; } action.i++; } x.unshift({t: 1, i: idx, v: item}); return x; }; ActionsList._makeAddBefore = function _makeAddBefore(x, idx, item) { if(!x) return [{t: 1, i: idx, v: item}]; if(!Array.isArray(x)) { if(x.t !== 2) throw new Error("Cannot add items to primitive value: " + x); return x; } if(idx === Infinity) { x.push({t: 1, i: Infinity, v: item}); return x; } for(var i = 0; i < x.length; i++) { var action = x[i]; var actionIdx = action.i; if(action.t === 1) { if(actionIdx >= idx) { action.i++ } else { idx++; } } else if(action.t === -1) { if(actionIdx < idx) { idx--; } } } for(var i = 0; i < x.length; i++) { var action = x[i]; var actionIdx = action.i; if(action.t === 1 && actionIdx > idx) { x.splice(i, 0, {t: 1, i: idx, v: item}); return x; } } x.push({t: 1, i: idx, v: item}); return x; }; ActionsList._makeRemove = function _makeRemove(x, idx) { if(!x) return [{t: -1, i: idx}]; if(!Array.isArray(x)) { if(x.t !== 2) throw new Error("Cannot remove items from primitive value: " + x); if(!Array.isArray(x.v)) throw new Error("Cannot remove items from non-array value: " + x); if(idx === Infinity) x.v.pop(); else x.v.splice(idx, 1); return x; } if(idx === Infinity) { x.unshift({t:-1, i:Infinity}); return x; } // fix all actions to represent the new action for(var i = x.length-1; i >= 0; i--) { var action = x[i]; var actionIdx = action.i; // Switch action with this action if(action.t === 1) { // switching with add action if(actionIdx === idx) { // if the idx is the same // add + remove = nothing x.splice(i, 1); return x; } if(actionIdx > idx) { // if the remove is before the add // "add" move one left action.i--; } else { // "remove" move one left idx--; } } else if(action.t === 0) { // switching with nested action if(actionIdx === idx) { // if the idx is the same // nested + remove = remove x.splice(i, 1); } else if(actionIdx > idx) { // "nested" move one left action.i--; } } else { // switching with remove action // store the action sorted: left is highest index if(actionIdx > idx) { // if in correct order // insert it here x.splice(i+1, 0, {t:-1, i:idx}); return x; } if(actionIdx <= idx) { idx++; } } } x.unshift({t:-1, i:idx}); return x; }; ActionsList._makeRemoveBefore = function _makeRemoveBefore(x, idx) { if(!x) return [{t: -1, i: idx}]; if(!Array.isArray(x)) { if(x.t !== 2) throw new Error("Cannot remove items from primitive value: " + x); return x; } if(idx === Infinity) { x.unshift({t:-1, i:Infinity}); return x; } // fix all actions to represent the new action for(var i = x.length-1; i >= 0; i--) { var action = x[i]; var actionIdx = action.i; // Switch action with this action if(action.t === -1) { // switching with remove action // store the action sorted: left is highest index if(actionIdx === idx) { // already removed... return x; } else if(actionIdx > idx) { // if in correct order // insert it here x.splice(i+1, 0, {t: -1, i: idx}); for(i++; i < x.length; i++) { var action = x[i]; var actionIdx = action.i; if(action.t === -1) { if(actionIdx < idx) idx--; } else { if(actionIdx > idx) action.i--; } } return x; } } } x.unshift({t: -1, i: idx}); for(i = 1; i < x.length; i++) { var action = x[i]; var actionIdx = action.i; if(action.t === -1) { if(actionIdx < idx) idx--; } else { if(actionIdx > idx) action.i--; } } return x; }; ActionsList._makeSplice = function _makeSplice(x, args, items) { var idx = args[0]; var count = args[1]; for(var i = count - 1; i >= 0; i--) x = ActionsList._makeRemove(x, i + idx); for(i = 0; i < items.length; i++) x = ActionsList._makeAdd(x, i + idx, items[i]); return x; }; ActionsList._makeSpliceBefore = function _makeSplice(x, args, items) { var idx = args[0]; var count = args[1]; for(var i = 0; i < count; i++) x = ActionsList._makeRemoveBefore(x, i + idx); for(i = items.length-1; i >= 0; i--) x = ActionsList._makeAddBefore(x, idx, items[i]); return x; }; ActionsList._makeNested = function _makeNested(x, path, fn, arg1, arg2) { if(!path || path.length === 0) { return fn(x, arg1, arg2); } if(typeof path[0] === "number") { if(!x) return [{t: 0, i: path[0], a: applyNext()}]; if(!Array.isArray(x)) { if(x.t !== 2 || !Array.isArray(x.v)) throw new Error("Cannot access item of non-array value"); var value = x.v[path[0]]; x.v[path[0]] = applyNext({t: 2, v: value}).v; return x; } for(var i = x.length - 1; i >= 0; i--) { var action = x[i]; if(action.t === -1) { x.splice(i + 1, 0, {t: 0, i: path[0], a: applyNext()}); return x; } else if(action.t === 0) { if(action.i === path[0]) { action.a = applyNext(action.a); return x; } else if(action.i < path[0]) { x.splice(i + 1, 0, {t: 0, i: path[0], a: applyNext()}); return x; } } else if(action.t === 1) { if(action.i === path[0]) { action.v = applyNext({t: 2, v: action.v}).v; return x; } if(action.i < path[0]) { path[0]--; } } } x.unshift({t: 0, i: path[0], a: applyNext()}); return x; } else { var prop = path[0]; if(!x) { var p = {}; p[prop] = applyNext(); return {t: 3, p: p}; } if(Array.isArray(x)) { throw new Error("Cannot access property of array value"); } if(x.t === 2) { var value = x.v[prop]; x.v[prop] = applyNext({t: 2, v: value}).v; return x; } else if(x.t === 3) { x.p[prop] = applyNext(x.p[prop]); return x; } else throw new Error("Cannot access property of primitive value"); } function applyNext(x) { path.shift(); if(path.length > 0) { return _makeNested(x, path, fn, arg1, arg2); } else { return fn(x, arg1, arg2); } } }; ActionsList._apply = function _apply(x, def) { if(x) { if(Array.isArray(x)) { for(var i = 0; i < x.length; i++) { var action = x[i]; switch(action.t) { case -1: def.remove(action.i); break; case 0: def.enter(action.i, function(def) { _apply(action.a, def); }); break; case 1: def.add(action.i, action.v); break; default: throw new Error("Unexpected action type " + action.t); } } def.done(); return; } switch(x.t) { case 2: def.update(x.v); break; case 3: Object.keys(x.p).forEach(function(p) { def.enter(p, function(def) { _apply(x.p[p], def); }); }); break; case 4: def.modify(x.m); break; default: throw new Error("Unexpected action type " + x.t); } } def.done(); };