object-observer
Advanced tools
Comparing version 0.1.1 to 0.2.0
(function (scope) { | ||
'use strict'; | ||
'use strict'; | ||
var api, | ||
proxiesToTargetsMap = new WeakMap(); | ||
var proxiesToTargetsMap = new WeakMap(), | ||
targetsToObserved = new WeakMap(), | ||
observedToObservable = new WeakMap(); | ||
function copyShallow(target) { | ||
var result; | ||
if (Array.isArray(target)) { | ||
result = target.slice(); | ||
} else { | ||
result = Object.assign({}, target); | ||
} | ||
return result; | ||
} | ||
function copyShallow(target) { | ||
var result; | ||
if (Array.isArray(target)) { | ||
result = target.slice(); | ||
} else { | ||
result = Object.assign({}, target); | ||
} | ||
return result; | ||
} | ||
function processArraySubgraph(subGraph, observableData, basePath) { | ||
var path, copy; | ||
subGraph.forEach(function (element, index) { | ||
if (element && typeof element === 'object') { | ||
path = basePath.concat(index); | ||
copy = copyShallow(element); | ||
subGraph[index] = proxify(copy, observableData, path); | ||
} | ||
}); | ||
} | ||
function proxiedArrayGet(target, key) { | ||
var result, | ||
observed = targetsToObserved.get(target), | ||
observable = observedToObservable.get(observed.root); | ||
if (key === 'pop') { | ||
result = function proxiedPop() { | ||
var poppedIndex, popResult, changes; | ||
poppedIndex = target.length - 1; | ||
popResult = Reflect.apply(target[key], target, arguments); | ||
if (popResult && typeof popResult === 'object') { | ||
popResult = proxiesToTargetsMap.get(popResult); | ||
targetsToObserved.get(popResult).revoke(); | ||
} | ||
changes = [new DeleteChange(observed.path.concat(poppedIndex), popResult)]; | ||
publishChanges(observable.callbacks, changes); | ||
return popResult; | ||
}; | ||
} else if (key === 'push') { | ||
result = function proxiedPush() { | ||
var pushContent, pushResult, changes = [], startingLength; | ||
pushContent = Array.from(arguments); | ||
startingLength = target.length; | ||
pushContent.forEach(function (item, index) { | ||
if (item && typeof item === 'object') { | ||
pushContent[index] = new Observed(item, startingLength + index, observed).proxy; | ||
} | ||
changes.push(new InsertChange(observed.path.concat(startingLength + index), item)); | ||
}); | ||
pushResult = Reflect.apply(target[key], target, pushContent); | ||
publishChanges(observable.callbacks, changes); | ||
return pushResult; | ||
}; | ||
} else if (key === 'shift') { | ||
result = function proxiedShift() { | ||
var shiftResult, changes, tmpObserved; | ||
shiftResult = Reflect.apply(target[key], target, arguments); | ||
if (shiftResult && typeof shiftResult === 'object') { | ||
shiftResult = proxiesToTargetsMap.get(shiftResult); | ||
targetsToObserved.get(shiftResult).revoke(); | ||
} | ||
target.forEach(function (element, index) { | ||
if (element && typeof element === 'object') { | ||
tmpObserved = targetsToObserved.get(proxiesToTargetsMap.get(element)); | ||
if (tmpObserved) { | ||
tmpObserved.ownKey = index; | ||
} else { | ||
console.error('failed to resolve proxy -> target -> observed'); | ||
} | ||
} | ||
}); | ||
changes = [new DeleteChange(observed.path.concat(0), shiftResult)]; | ||
publishChanges(observable.callbacks, changes); | ||
return shiftResult; | ||
}; | ||
} else if (key === 'unshift') { | ||
result = function proxiedUnshift() { | ||
var unshiftContent, unshiftResult, changes = [], tmpObserved; | ||
unshiftContent = Array.from(arguments); | ||
unshiftContent.forEach(function (item, index) { | ||
if (item && typeof item === 'object') { | ||
unshiftContent[index] = new Observed(item, index, observed).proxy; | ||
} | ||
}); | ||
unshiftResult = Reflect.apply(target[key], target, unshiftContent); | ||
target.forEach(function (item, index) { | ||
if (item && typeof item === 'object') { | ||
tmpObserved = targetsToObserved.get(proxiesToTargetsMap.get(item)); | ||
if (tmpObserved) { | ||
tmpObserved.ownKey = index; | ||
} else { | ||
console.error('failed to resolve proxy -> target -> observed'); | ||
} | ||
} | ||
}); | ||
for (var i = 0; i < unshiftContent.length; i++) { | ||
changes.push(new InsertChange(observed.path.concat(i), target[i])); | ||
} | ||
publishChanges(observable.callbacks, changes); | ||
return unshiftResult; | ||
}; | ||
} else if (key === 'reverse') { | ||
result = function proxiedReverse() { | ||
var reverseResult, changes = [], tmpObserved; | ||
reverseResult = Reflect.apply(target[key], target, arguments); | ||
target.forEach(function (element, index) { | ||
if (element && typeof element === 'object') { | ||
tmpObserved = targetsToObserved.get(proxiesToTargetsMap.get(element)); | ||
if (tmpObserved) { | ||
tmpObserved.ownKey = index; | ||
} else { | ||
console.error('failed to resolve proxy -> target -> observed'); | ||
} | ||
} | ||
}); | ||
changes.push(new ReverseChange()); | ||
publishChanges(observable.callbacks, changes); | ||
return reverseResult; | ||
}; | ||
} else if (key === 'sort') { | ||
result = function proxiedSort() { | ||
var sortResult, changes = [], tmpObserved; | ||
sortResult = Reflect.apply(target[key], target, arguments); | ||
target.forEach(function (element, index) { | ||
if (element && typeof element === 'object') { | ||
tmpObserved = targetsToObserved.get(proxiesToTargetsMap.get(element)); | ||
if (tmpObserved) { | ||
tmpObserved.ownKey = index; | ||
} else { | ||
console.error('failed to resolve proxy -> target -> observed'); | ||
} | ||
} | ||
}); | ||
changes.push(new ShuffleChange()); | ||
publishChanges(observable.callbacks, changes); | ||
return sortResult; | ||
}; | ||
} else if (key === 'fill') { | ||
result = function proxiedFill() { | ||
var fillResult, start, end, changes = [], prev; | ||
start = arguments.length < 2 ? 0 : (arguments[1] < 0 ? target.length + arguments[1] : arguments[1]); | ||
end = arguments.length < 3 ? target.length : (arguments[2] < 0 ? target.length + arguments[2] : arguments[2]); | ||
prev = target.slice(); | ||
fillResult = Reflect.apply(target[key], target, arguments); | ||
for (var i = start; i < end; i++) { | ||
if (target[i] && typeof target[i] === 'object') { | ||
target[i] = new Observed(target[i], i, observed).proxy; | ||
} | ||
if (prev.hasOwnProperty(i)) { | ||
changes.push(new UpdateChange(observed.path.concat(i), target[i], prev[i] && typeof prev[i] === 'object' ? proxiesToTargetsMap.get(prev[i]) : prev[i])); | ||
if (prev[i] && typeof prev[i] === 'object') { | ||
targetsToObserved.get(proxiesToTargetsMap.get(prev[i])).revoke(); | ||
} | ||
} else { | ||
changes.push(new InsertChange(observed.path.concat(i), target[i])); | ||
} | ||
} | ||
publishChanges(observable.callbacks, changes); | ||
return fillResult; | ||
}; | ||
} else if (key === 'splice') { | ||
result = function proxiedSplice() { | ||
var spliceContent, spliceResult, changes = [], tmpObserved, | ||
index, startIndex, removed, inserted; | ||
function processObjectSubgraph(subGraph, observableData, basePath) { | ||
var path, copy; | ||
Reflect.ownKeys(subGraph).forEach(function (key) { | ||
if (subGraph[key] && typeof subGraph[key] === 'object') { | ||
path = basePath.concat(key); | ||
copy = copyShallow(subGraph[key]); | ||
subGraph[key] = proxify(copy, observableData, path); | ||
} | ||
}); | ||
} | ||
spliceContent = Array.from(arguments); | ||
function proxify(target, observableData, basePath) { | ||
var proxy; | ||
// obserify the newcomers | ||
spliceContent.forEach(function (item, index) { | ||
if (index > 1 && item && typeof item === 'object') { | ||
spliceContent[index] = new Observed(item, index, observed).proxy; | ||
} | ||
}); | ||
function proxiedArrayGet(target, key) { | ||
var result; | ||
if (key === 'pop') { | ||
result = function proxiedPop() { | ||
var poppedIndex, popResult, changes; | ||
poppedIndex = target.length - 1; | ||
observableData.preventCallbacks = true; | ||
popResult = Reflect.apply(target[key], target, arguments); | ||
observableData.preventCallbacks = false; | ||
changes = [new DeleteChange(basePath.concat(poppedIndex), popResult)]; | ||
publishChanges(observableData.callbacks, changes); | ||
return popResult; | ||
}; | ||
} else if (key === 'push') { | ||
result = function proxiedPush() { | ||
var pushResult, changes = []; | ||
observableData.preventCallbacks = true; | ||
pushResult = Reflect.apply(target[key], target, arguments); | ||
processArraySubgraph(target, observableData, basePath); | ||
observableData.preventCallbacks = false; | ||
for (var i = arguments.length; i > 0; i--) { | ||
changes.push(new InsertChange(basePath.concat(pushResult - i), target[pushResult - i])); | ||
} | ||
publishChanges(observableData.callbacks, changes); | ||
return pushResult; | ||
}; | ||
} else if (key === 'shift') { | ||
result = function proxiedShift() { | ||
var shiftResult, changes; | ||
observableData.preventCallbacks = true; | ||
shiftResult = Reflect.apply(target[key], target, arguments); | ||
processArraySubgraph(target, observableData, basePath); | ||
observableData.preventCallbacks = false; | ||
changes = [new DeleteChange(basePath.concat(0), shiftResult)]; | ||
publishChanges(observableData.callbacks, changes); | ||
return shiftResult; | ||
}; | ||
} else if (key === 'unshift') { | ||
result = function proxiedUnshift() { | ||
var unshiftResult, unshiftContent = [], changes = []; | ||
Array.from(arguments).forEach(function (arg, index) { | ||
var pArg; | ||
if (arg && typeof arg === 'object') { | ||
pArg = proxify(arg, observableData, basePath.concat(index)); | ||
} else { | ||
pArg = arg; | ||
} | ||
unshiftContent.push(pArg); | ||
}); | ||
unshiftContent.forEach(function (pe, index) { | ||
changes.push(new InsertChange(basePath.concat(index), pe)); | ||
}); | ||
unshiftResult = Reflect.apply(target[key], target, unshiftContent); | ||
processArraySubgraph(target, observableData, basePath); | ||
publishChanges(observableData.callbacks, changes); | ||
return unshiftResult; | ||
}; | ||
} else if (key === 'reverse') { | ||
result = function proxiedReverse() { | ||
var reverseResult, changes = []; | ||
observableData.preventCallbacks = true; | ||
reverseResult = Reflect.apply(target[key], target, arguments); | ||
processArraySubgraph(target, observableData, basePath); | ||
observableData.preventCallbacks = false; | ||
changes.push(new ReverseChange()); | ||
publishChanges(observableData.callbacks, changes); | ||
return reverseResult; | ||
}; | ||
} else if (key === 'sort') { | ||
result = function proxiedSort() { | ||
var sortResult, changes = []; | ||
observableData.preventCallbacks = true; | ||
sortResult = Reflect.apply(target[key], target, arguments); | ||
processArraySubgraph(target, observableData, basePath); | ||
observableData.preventCallbacks = false; | ||
changes.push(new ShuffleChange()); | ||
publishChanges(observableData.callbacks, changes); | ||
return sortResult; | ||
}; | ||
} else if (key === 'fill') { | ||
result = function proxiedFill() { | ||
var fillResult, start, end, changes = [], prev; | ||
start = arguments.length < 2 ? 0 : (arguments[1] < 0 ? target.length + arguments[1] : arguments[1]); | ||
end = arguments.length < 3 ? target.length : (arguments[2] < 0 ? target.length + arguments[2] : arguments[2]); | ||
prev = target.slice(start, end); | ||
observableData.preventCallbacks = true; | ||
fillResult = Reflect.apply(target[key], target, arguments); | ||
processArraySubgraph(target, observableData, basePath); | ||
observableData.preventCallbacks = false; | ||
for (var i = start; i < end; i++) { | ||
if (target.hasOwnProperty(i - start)) { | ||
changes.push(new UpdateChange(basePath.concat(i), target[i], prev[i - start])); | ||
} else { | ||
changes.push(new InsertChange(basePath.concat(i), target[i])); | ||
} | ||
} | ||
publishChanges(observableData.callbacks, changes); | ||
return fillResult; | ||
}; | ||
} else if (key === 'splice') { | ||
result = function proxiedSplice() { | ||
var changes = [], | ||
index, | ||
startIndex, | ||
removed, | ||
inserted, | ||
spliceResult; | ||
observableData.preventCallbacks = true; | ||
startIndex = arguments.length === 0 ? 0 : (arguments[0] < 0 ? target.length + arguments[0] : arguments[0]); | ||
removed = arguments.length < 2 ? (target.length - startIndex) : arguments[1]; | ||
inserted = Math.max(arguments.length - 2, 0); | ||
spliceResult = Reflect.apply(target[key], target, arguments); | ||
processArraySubgraph(target, observableData, basePath); | ||
observableData.preventCallbacks = false; | ||
for (index = 0; index < removed; index++) { | ||
if (index < inserted) { | ||
changes.push(new UpdateChange(basePath.concat(startIndex + index), target[startIndex + index], spliceResult[index])); | ||
} else { | ||
changes.push(new DeleteChange(basePath.concat(startIndex + index), spliceResult[index])); | ||
} | ||
} | ||
for (; index < inserted; index++) { | ||
changes.push(new InsertChange(basePath.concat(startIndex + index), target[startIndex + index])); | ||
} | ||
// calculate pointers | ||
startIndex = spliceContent.length === 0 ? 0 : (spliceContent[0] < 0 ? target.length + spliceContent[0] : spliceContent[0]); | ||
removed = spliceContent.length < 2 ? target.length - startIndex : spliceContent[1]; | ||
inserted = Math.max(spliceContent.length - 2, 0); | ||
spliceResult = Reflect.apply(target[key], target, spliceContent); | ||
publishChanges(observableData.callbacks, changes); | ||
return spliceResult; | ||
}; | ||
} else { | ||
result = Reflect.get(target, key); | ||
} | ||
return result; | ||
} | ||
// reindex the paths | ||
target.forEach(function (element, index) { | ||
if (element && typeof element === 'object') { | ||
tmpObserved = targetsToObserved.get(proxiesToTargetsMap.get(element)); | ||
if (tmpObserved) { | ||
tmpObserved.ownKey = index; | ||
} else { | ||
console.error('failed to resolve proxy -> target -> observed'); | ||
} | ||
} | ||
}); | ||
function proxiedSet(target, key, value) { | ||
var oldValuePresent = target.hasOwnProperty(key), | ||
oldValue = target[key], | ||
result, | ||
changes = Array.isArray(observableData.eventsCollector) ? observableData.eventsCollector : [], | ||
path; | ||
// revoke removed Observed | ||
spliceResult.forEach(function (removed, index) { | ||
if (removed && typeof removed === 'object') { | ||
spliceResult[index] = proxiesToTargetsMap.get(removed); | ||
targetsToObserved.get(spliceResult[index]).revoke(); | ||
} | ||
}); | ||
result = Reflect.set(target, key, value); | ||
if (observableData.callbacks.length && result && value !== oldValue) { | ||
path = basePath.concat(key); | ||
// publish changes | ||
for (index = 0; index < removed; index++) { | ||
if (index < inserted) { | ||
changes.push(new UpdateChange(observed.path.concat(startIndex + index), target[startIndex + index], spliceResult[index])); | ||
} else { | ||
changes.push(new DeleteChange(observed.path.concat(startIndex + index), spliceResult[index])); | ||
} | ||
} | ||
for (; index < inserted; index++) { | ||
changes.push(new InsertChange(observed.path.concat(startIndex + index), target[startIndex + index])); | ||
} | ||
publishChanges(observable.callbacks, changes); | ||
if (typeof oldValue === 'object' && oldValue) { | ||
if (proxiesToTargetsMap.has(oldValue)) { | ||
proxiesToTargetsMap.delete(oldValue); | ||
} | ||
} | ||
if (typeof value === 'object' && value) { | ||
target[key] = proxify(value, observableData, path); | ||
} | ||
if (oldValuePresent) { | ||
changes.push(new UpdateChange(path, value, oldValue)); | ||
} else { | ||
changes.push(new InsertChange(path, value)); | ||
} | ||
if (!observableData.preventCallbacks) { | ||
publishChanges(observableData.callbacks, changes); | ||
} | ||
} | ||
return result; | ||
} | ||
return spliceResult; | ||
}; | ||
} else { | ||
result = Reflect.get(target, key); | ||
} | ||
return result; | ||
} | ||
function proxiedDelete(target, key) { | ||
var oldValue = target[key], | ||
result, | ||
changes = Array.isArray(observableData.eventsCollector) ? observableData.eventsCollector : [], | ||
path; | ||
function proxiedSet(target, key, value) { | ||
var oldValuePresent = target.hasOwnProperty(key), | ||
oldValue = target[key], | ||
result, | ||
observed = targetsToObserved.get(target), | ||
observable = observedToObservable.get(observed.root), | ||
changes = [], | ||
path; | ||
result = Reflect.deleteProperty(target, key); | ||
if (observableData.callbacks.length && result) { | ||
if (typeof oldValue === 'object' && oldValue) { | ||
if (proxiesToTargetsMap.has(oldValue)) { | ||
proxiesToTargetsMap.delete(oldValue); | ||
} | ||
} | ||
path = basePath.concat(key); | ||
changes.push(new DeleteChange(path, oldValue)); | ||
if (!observableData.preventCallbacks) { | ||
publishChanges(observableData.callbacks, changes); | ||
} | ||
} | ||
return result; | ||
} | ||
result = Reflect.set(target, key, value); | ||
if (observable.callbacks.length && result && value !== oldValue) { | ||
path = observed.path.concat(key); | ||
if (proxiesToTargetsMap.has(target)) { | ||
var tmp = target; | ||
target = proxiesToTargetsMap.get(target); | ||
proxiesToTargetsMap.delete(tmp); | ||
} | ||
if (Array.isArray(target)) { | ||
processArraySubgraph(target, observableData, basePath); | ||
proxy = new Proxy(target, { | ||
get: proxiedArrayGet, | ||
set: proxiedSet, | ||
deleteProperty: proxiedDelete | ||
}); | ||
} else { | ||
processObjectSubgraph(target, observableData, basePath); | ||
proxy = new Proxy(target, { | ||
set: proxiedSet, | ||
deleteProperty: proxiedDelete | ||
}); | ||
} | ||
proxiesToTargetsMap.set(proxy, target); | ||
if (oldValue && typeof oldValue === 'object') { | ||
targetsToObserved.get(proxiesToTargetsMap.get(oldValue)).revoke(); | ||
if (proxiesToTargetsMap.has(oldValue)) { | ||
proxiesToTargetsMap.delete(oldValue); | ||
} | ||
} | ||
if (value && typeof value === 'object') { | ||
target[key] = new Observed(value, key, observed).proxy; | ||
} | ||
if (oldValuePresent) { | ||
changes.push(new UpdateChange(path, value, oldValue)); | ||
} else { | ||
changes.push(new InsertChange(path, value)); | ||
} | ||
if (!observed.preventCallbacks) { | ||
publishChanges(observable.callbacks, changes); | ||
} | ||
} | ||
return result; | ||
} | ||
return proxy; | ||
} | ||
function proxiedDelete(target, key) { | ||
var oldValue = target[key], | ||
result, | ||
observed = targetsToObserved.get(target), | ||
observable = observedToObservable.get(observed.root), | ||
changes = [], | ||
path; | ||
function ObservableData(target) { | ||
var proxy, | ||
callbacks = [], | ||
eventsCollector, | ||
preventCallbacks = false; | ||
result = Reflect.deleteProperty(target, key); | ||
if (observable.callbacks.length && result) { | ||
if (typeof oldValue === 'object' && oldValue) { | ||
if (proxiesToTargetsMap.has(oldValue)) { | ||
proxiesToTargetsMap.delete(oldValue); | ||
} | ||
} | ||
path = observed.path.concat(key); | ||
changes.push(new DeleteChange(path, oldValue)); | ||
if (!observed.preventCallbacks) { | ||
publishChanges(observable.callbacks, changes); | ||
} | ||
} | ||
return result; | ||
} | ||
function observe(callback) { | ||
if (typeof callback !== 'function') { throw new Error('callback parameter MUST be a function'); } | ||
function processArraySubgraph(graph, parentObserved) { | ||
graph.forEach(function (element, index) { | ||
if (element && typeof element === 'object') { | ||
graph[index] = new Observed(element, index, parentObserved).proxy; | ||
} | ||
}); | ||
} | ||
if (callbacks.indexOf(callback) < 0) { | ||
callbacks.push(callback); | ||
} else { | ||
console.info('observer callback may be bound only once for an observable'); | ||
} | ||
} | ||
function processObjectSubgraph(graph, parentObserved) { | ||
Reflect.ownKeys(graph).forEach(function (key) { | ||
if (graph[key] && typeof graph[key] === 'object') { | ||
graph[key] = new Observed(graph[key], key, parentObserved).proxy; | ||
} | ||
}); | ||
} | ||
function unobserve() { | ||
if (arguments.length) { | ||
Array.from(arguments).forEach(function (argument) { | ||
var i = callbacks.indexOf(argument); | ||
if (i) { | ||
callbacks.splice(i, 1); | ||
} | ||
}); | ||
} else { | ||
callbacks.splice(0, callbacks.length); | ||
} | ||
} | ||
function Observed(origin, ownKey, parent) { | ||
var targetClone, revokableProxy; | ||
proxy = proxify(copyShallow(target), this, []); | ||
Reflect.defineProperty(proxy, 'observe', { value: observe }); | ||
Reflect.defineProperty(proxy, 'unobserve', { value: unobserve }); | ||
if (!origin || typeof origin !== 'object') { | ||
throw new Error('Observed MUST be created from a non null object origin'); | ||
} | ||
if (parent && (typeof ownKey === 'undefined' || ownKey === null)) { | ||
throw new Error('any non-root (parent-less) Observed MUST have an own path; now parent is ' + parent + '; key is ' + ownKey); | ||
} | ||
if (parent && !(parent instanceof Observed)) { | ||
throw new Error('parent, when supplied, MUST be an instance of Observed'); | ||
} | ||
Reflect.defineProperty(this, 'callbacks', { get: function () { return callbacks.slice(); } }); | ||
Reflect.defineProperty(this, 'eventsCollector', { value: eventsCollector, writable: true }); | ||
Reflect.defineProperty(this, 'preventCallbacks', { value: preventCallbacks, writable: true }); | ||
Reflect.defineProperty(this, 'proxy', { value: proxy }); | ||
} | ||
targetClone = copyShallow(origin); | ||
function InsertChange(path, value) { | ||
Reflect.defineProperty(this, 'type', { value: 'insert' }); | ||
Reflect.defineProperty(this, 'path', { value: path }); | ||
Reflect.defineProperty(this, 'value', { value: value }); | ||
} | ||
function UpdateChange(path, value, oldValue) { | ||
Reflect.defineProperty(this, 'type', { value: 'update' }); | ||
Reflect.defineProperty(this, 'path', { value: path }); | ||
Reflect.defineProperty(this, 'value', { value: value }); | ||
Reflect.defineProperty(this, 'oldValue', { value: oldValue }); | ||
} | ||
function DeleteChange(path, oldValue) { | ||
Reflect.defineProperty(this, 'type', { value: 'delete' }); | ||
Reflect.defineProperty(this, 'path', { value: path }); | ||
Reflect.defineProperty(this, 'oldValue', { value: oldValue }); | ||
} | ||
function ReverseChange() { | ||
Reflect.defineProperty(this, 'type', { value: 'reverse' }); | ||
} | ||
function ShuffleChange() { | ||
Reflect.defineProperty(this, 'type', { value: 'shuffle' }); | ||
} | ||
if (Array.isArray(targetClone)) { | ||
processArraySubgraph(targetClone, this); | ||
revokableProxy = Proxy.revocable(targetClone, { | ||
set: proxiedSet, | ||
get: proxiedArrayGet, | ||
deleteProperty: proxiedDelete | ||
}); | ||
} else { | ||
processObjectSubgraph(targetClone, this); | ||
revokableProxy = Proxy.revocable(targetClone, { | ||
set: proxiedSet, | ||
deleteProperty: proxiedDelete | ||
}); | ||
} | ||
function publishChanges(callbacks, changes) { | ||
for (var i = 0; i < callbacks.length; i++) { | ||
try { | ||
callbacks[i](changes); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
} | ||
targetsToObserved.set(targetClone, this); | ||
proxiesToTargetsMap.set(revokableProxy.proxy, targetClone); | ||
Reflect.defineProperty(this, 'revokable', { value: revokableProxy }); | ||
Reflect.defineProperty(this, 'proxy', { value: revokableProxy.proxy }); | ||
Reflect.defineProperty(this, 'parent', { value: parent }); | ||
Reflect.defineProperty(this, 'ownKey', { value: ownKey, writable: true }); | ||
} | ||
api = {}; | ||
Reflect.defineProperty(Observed.prototype, 'root', { | ||
get: function () { | ||
var result = this; | ||
while (result.parent) { | ||
result = result.parent; | ||
} | ||
return result; | ||
} | ||
}); | ||
Reflect.defineProperty(Observed.prototype, 'path', { | ||
get: function () { | ||
var result = [], pointer = this; | ||
while (typeof pointer.ownKey !== 'undefined') { | ||
result.push(pointer.ownKey); | ||
pointer = pointer.parent; | ||
} | ||
return result.reverse(); | ||
} | ||
}); | ||
Reflect.defineProperty(Observed.prototype, 'revoke', { | ||
value: function () { | ||
var proxy = this.proxy; | ||
Reflect.ownKeys(proxy).forEach(function (key) { | ||
var child = proxy[key]; | ||
if (child && typeof child === 'object') { | ||
targetsToObserved.get(proxiesToTargetsMap.get(child)).revoke(); | ||
proxiesToTargetsMap.get(proxy)[key] = proxiesToTargetsMap.get(child); | ||
} | ||
}); | ||
this.revokable.revoke(); | ||
// TODO: ensure if there are any other cleanups to do here (probably remove observed?) | ||
} | ||
}) | ||
Reflect.defineProperty(api, 'from', { | ||
value: function (target) { | ||
if (!target || typeof target !== 'object') { | ||
throw new Error('observable MAY ONLY be created from non-null object only'); | ||
} else if ('observe' in target || 'unobserve' in target) { | ||
throw new Error('target object MUST NOT have not own nor inherited properties "observe" and/or "unobserve"'); | ||
} | ||
var observableData = new ObservableData(target); | ||
return observableData.proxy; | ||
} | ||
}); | ||
function Observable(observed) { | ||
var isRevoked = false, callbacks = []; | ||
Reflect.defineProperty(scope, 'Observable', { value: api }); | ||
function observe(callback) { | ||
if (isRevoked) { throw new TypeError('revoked Observable MAY NOT be observed anymore'); } | ||
if (typeof callback !== 'function') { throw new Error('observer (callback) parameter MUST be a function'); } | ||
if (callbacks.indexOf(callback) < 0) { | ||
callbacks.push(callback); | ||
} else { | ||
console.info('observer (callback) may be bound to an observable only once'); | ||
} | ||
} | ||
function unobserve() { | ||
if (isRevoked) { throw new TypeError('revoked Observable MAY NOT be unobserved amymore'); } | ||
if (arguments.length) { | ||
Array.from(arguments).forEach(function (argument) { | ||
var i = callbacks.indexOf(argument); | ||
if (i >= 0) { | ||
callbacks.splice(i, 1); | ||
} | ||
}); | ||
} else { | ||
callbacks.splice(0, callbacks.length); | ||
} | ||
} | ||
function revoke() { | ||
if (!isRevoked) { | ||
isRevoked = true; | ||
observed.revoke(); | ||
} else { | ||
console.log('revokation of Observable have an effect only once'); | ||
} | ||
} | ||
Reflect.defineProperty(observed.proxy, 'observe', { value: observe }); | ||
Reflect.defineProperty(observed.proxy, 'unobserve', { value: unobserve }); | ||
Reflect.defineProperty(observed.proxy, 'revoke', { value: revoke }); | ||
Reflect.defineProperty(this, 'callbacks', { value: callbacks }); | ||
} | ||
function InsertChange(path, value) { | ||
Reflect.defineProperty(this, 'type', { value: 'insert' }); | ||
Reflect.defineProperty(this, 'path', { value: path }); | ||
Reflect.defineProperty(this, 'value', { value: value }); | ||
} | ||
function UpdateChange(path, value, oldValue) { | ||
Reflect.defineProperty(this, 'type', { value: 'update' }); | ||
Reflect.defineProperty(this, 'path', { value: path }); | ||
Reflect.defineProperty(this, 'value', { value: value }); | ||
Reflect.defineProperty(this, 'oldValue', { value: oldValue }); | ||
} | ||
function DeleteChange(path, oldValue) { | ||
Reflect.defineProperty(this, 'type', { value: 'delete' }); | ||
Reflect.defineProperty(this, 'path', { value: path }); | ||
Reflect.defineProperty(this, 'oldValue', { value: oldValue }); | ||
} | ||
function ReverseChange() { | ||
Reflect.defineProperty(this, 'type', { value: 'reverse' }); | ||
} | ||
function ShuffleChange() { | ||
Reflect.defineProperty(this, 'type', { value: 'shuffle' }); | ||
} | ||
function publishChanges(callbacks, changes) { | ||
for (var i = 0; i < callbacks.length; i++) { | ||
try { | ||
callbacks[i](changes); | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
} | ||
Reflect.defineProperty(Observable, 'from', { | ||
value: function (target) { | ||
if (!target || typeof target !== 'object') { | ||
throw new Error('observable MAY ONLY be created from non-null object only'); | ||
} else if ('observe' in target || 'unobserve' in target || 'revoke' in target) { | ||
throw new Error('target object MUST NOT have nor own neither inherited properties from the following list: "observe", "unobserve", "revoke"'); | ||
} | ||
var observed = new Observed(target), | ||
observable = new Observable(observed); | ||
observedToObservable.set(observed, observable); | ||
return observed.proxy; | ||
} | ||
}); | ||
Reflect.defineProperty(scope, 'Observable', { value: Observable }); | ||
})(this); |
@@ -1,1 +0,1 @@ | ||
!function(a){"use strict";function b(a){var b;return b=Array.isArray(a)?a.slice():Object.assign({},a)}function c(a,c,d){var f,g;a.forEach(function(h,i){h&&"object"==typeof h&&(f=d.concat(i),g=b(h),a[i]=e(g,c,f))})}function d(a,c,d){var f,g;Reflect.ownKeys(a).forEach(function(h){a[h]&&"object"==typeof a[h]&&(f=d.concat(h),g=b(a[h]),a[h]=e(g,c,f))})}function e(a,b,f){function m(a,d){var m;return m="pop"===d?function(){var c,e,g;return c=a.length-1,b.preventCallbacks=!0,e=Reflect.apply(a[d],a,arguments),b.preventCallbacks=!1,g=[new i(f.concat(c),e)],l(b.callbacks,g),e}:"push"===d?function(){var e,h=[];b.preventCallbacks=!0,e=Reflect.apply(a[d],a,arguments),c(a,b,f),b.preventCallbacks=!1;for(var i=arguments.length;i>0;i--)h.push(new g(f.concat(e-i),a[e-i]));return l(b.callbacks,h),e}:"shift"===d?function(){var e,g;return b.preventCallbacks=!0,e=Reflect.apply(a[d],a,arguments),c(a,b,f),b.preventCallbacks=!1,g=[new i(f.concat(0),e)],l(b.callbacks,g),e}:"unshift"===d?function(){var h,i=[],j=[];return Array.from(arguments).forEach(function(a,c){var d;d=a&&"object"==typeof a?e(a,b,f.concat(c)):a,i.push(d)}),i.forEach(function(a,b){j.push(new g(f.concat(b),a))}),h=Reflect.apply(a[d],a,i),c(a,b,f),l(b.callbacks,j),h}:"reverse"===d?function(){var e,g=[];return b.preventCallbacks=!0,e=Reflect.apply(a[d],a,arguments),c(a,b,f),b.preventCallbacks=!1,g.push(new j),l(b.callbacks,g),e}:"sort"===d?function(){var e,g=[];return b.preventCallbacks=!0,e=Reflect.apply(a[d],a,arguments),c(a,b,f),b.preventCallbacks=!1,g.push(new k),l(b.callbacks,g),e}:"fill"===d?function(){var e,i,j,k,m=[];i=arguments.length<2?0:arguments[1]<0?a.length+arguments[1]:arguments[1],j=arguments.length<3?a.length:arguments[2]<0?a.length+arguments[2]:arguments[2],k=a.slice(i,j),b.preventCallbacks=!0,e=Reflect.apply(a[d],a,arguments),c(a,b,f),b.preventCallbacks=!1;for(var n=i;j>n;n++)a.hasOwnProperty(n-i)?m.push(new h(f.concat(n),a[n],k[n-i])):m.push(new g(f.concat(n),a[n]));return l(b.callbacks,m),e}:"splice"===d?function(){var e,j,k,m,n,o=[];for(b.preventCallbacks=!0,j=0===arguments.length?0:arguments[0]<0?a.length+arguments[0]:arguments[0],k=arguments.length<2?a.length-j:arguments[1],m=Math.max(arguments.length-2,0),n=Reflect.apply(a[d],a,arguments),c(a,b,f),b.preventCallbacks=!1,e=0;k>e;e++)m>e?o.push(new h(f.concat(j+e),a[j+e],n[e])):o.push(new i(f.concat(j+e),n[e]));for(;m>e;e++)o.push(new g(f.concat(j+e),a[j+e]));return l(b.callbacks,o),n}:Reflect.get(a,d)}function o(a,c,d){var i,j,k=a.hasOwnProperty(c),m=a[c],o=Array.isArray(b.eventsCollector)?b.eventsCollector:[];return i=Reflect.set(a,c,d),b.callbacks.length&&i&&d!==m&&(j=f.concat(c),"object"==typeof m&&m&&n.has(m)&&n.delete(m),"object"==typeof d&&d&&(a[c]=e(d,b,j)),k?o.push(new h(j,d,m)):o.push(new g(j,d)),b.preventCallbacks||l(b.callbacks,o)),i}function p(a,c){var d,e,g=a[c],h=Array.isArray(b.eventsCollector)?b.eventsCollector:[];return d=Reflect.deleteProperty(a,c),b.callbacks.length&&d&&("object"==typeof g&&g&&n.has(g)&&n.delete(g),e=f.concat(c),h.push(new i(e,g)),b.preventCallbacks||l(b.callbacks,h)),d}var q;if(n.has(a)){var r=a;a=n.get(a),n.delete(r)}return Array.isArray(a)?(c(a,b,f),q=new Proxy(a,{get:m,set:o,deleteProperty:p})):(d(a,b,f),q=new Proxy(a,{set:o,deleteProperty:p})),n.set(q,a),q}function f(a){function c(a){if("function"!=typeof a)throw new Error("callback parameter MUST be a function");h.indexOf(a)<0?h.push(a):console.info("observer callback may be bound only once for an observable")}function d(){arguments.length?Array.from(arguments).forEach(function(a){var b=h.indexOf(a);b&&h.splice(b,1)}):h.splice(0,h.length)}var f,g,h=[],i=!1;f=e(b(a),this,[]),Reflect.defineProperty(f,"observe",{value:c}),Reflect.defineProperty(f,"unobserve",{value:d}),Reflect.defineProperty(this,"callbacks",{get:function(){return h.slice()}}),Reflect.defineProperty(this,"eventsCollector",{value:g,writable:!0}),Reflect.defineProperty(this,"preventCallbacks",{value:i,writable:!0}),Reflect.defineProperty(this,"proxy",{value:f})}function g(a,b){Reflect.defineProperty(this,"type",{value:"insert"}),Reflect.defineProperty(this,"path",{value:a}),Reflect.defineProperty(this,"value",{value:b})}function h(a,b,c){Reflect.defineProperty(this,"type",{value:"update"}),Reflect.defineProperty(this,"path",{value:a}),Reflect.defineProperty(this,"value",{value:b}),Reflect.defineProperty(this,"oldValue",{value:c})}function i(a,b){Reflect.defineProperty(this,"type",{value:"delete"}),Reflect.defineProperty(this,"path",{value:a}),Reflect.defineProperty(this,"oldValue",{value:b})}function j(){Reflect.defineProperty(this,"type",{value:"reverse"})}function k(){Reflect.defineProperty(this,"type",{value:"shuffle"})}function l(a,b){for(var c=0;c<a.length;c++)try{a[c](b)}catch(a){console.error(a)}}var m,n=new WeakMap;m={},Reflect.defineProperty(m,"from",{value:function(a){if(!a||"object"!=typeof a)throw new Error("observable MAY ONLY be created from non-null object only");if("observe"in a||"unobserve"in a)throw new Error('target object MUST NOT have not own nor inherited properties "observe" and/or "unobserve"');var b=new f(a);return b.proxy}}),Reflect.defineProperty(a,"Observable",{value:m})}(this); | ||
!function(a){"use strict";function b(a){var b;return b=Array.isArray(a)?a.slice():Object.assign({},a)}function c(a,b){var c,d=q.get(a),e=r.get(d.root);return c="pop"===b?function(){var c,f,g;return c=a.length-1,f=Reflect.apply(a[b],a,arguments),f&&"object"==typeof f&&(f=p.get(f),q.get(f).revoke()),g=[new l(d.path.concat(c),f)],o(e.callbacks,g),f}:"push"===b?function(){var c,f,g,i=[];return c=Array.from(arguments),g=a.length,c.forEach(function(a,b){a&&"object"==typeof a&&(c[b]=new h(a,g+b,d).proxy),i.push(new j(d.path.concat(g+b),a))}),f=Reflect.apply(a[b],a,c),o(e.callbacks,i),f}:"shift"===b?function(){var c,f,g;return c=Reflect.apply(a[b],a,arguments),c&&"object"==typeof c&&(c=p.get(c),q.get(c).revoke()),a.forEach(function(a,b){a&&"object"==typeof a&&(g=q.get(p.get(a)),g?g.ownKey=b:console.error("failed to resolve proxy -> target -> observed"))}),f=[new l(d.path.concat(0),c)],o(e.callbacks,f),c}:"unshift"===b?function(){var c,f,g,i=[];c=Array.from(arguments),c.forEach(function(a,b){a&&"object"==typeof a&&(c[b]=new h(a,b,d).proxy)}),f=Reflect.apply(a[b],a,c),a.forEach(function(a,b){a&&"object"==typeof a&&(g=q.get(p.get(a)),g?g.ownKey=b:console.error("failed to resolve proxy -> target -> observed"))});for(var k=0;k<c.length;k++)i.push(new j(d.path.concat(k),a[k]));return o(e.callbacks,i),f}:"reverse"===b?function(){var c,d,f=[];return c=Reflect.apply(a[b],a,arguments),a.forEach(function(a,b){a&&"object"==typeof a&&(d=q.get(p.get(a)),d?d.ownKey=b:console.error("failed to resolve proxy -> target -> observed"))}),f.push(new m),o(e.callbacks,f),c}:"sort"===b?function(){var c,d,f=[];return c=Reflect.apply(a[b],a,arguments),a.forEach(function(a,b){a&&"object"==typeof a&&(d=q.get(p.get(a)),d?d.ownKey=b:console.error("failed to resolve proxy -> target -> observed"))}),f.push(new n),o(e.callbacks,f),c}:"fill"===b?function(){var c,f,g,i,l=[];f=arguments.length<2?0:arguments[1]<0?a.length+arguments[1]:arguments[1],g=arguments.length<3?a.length:arguments[2]<0?a.length+arguments[2]:arguments[2],i=a.slice(),c=Reflect.apply(a[b],a,arguments);for(var m=f;m<g;m++)a[m]&&"object"==typeof a[m]&&(a[m]=new h(a[m],m,d).proxy),i.hasOwnProperty(m)?(l.push(new k(d.path.concat(m),a[m],i[m]&&"object"==typeof i[m]?p.get(i[m]):i[m])),i[m]&&"object"==typeof i[m]&&q.get(p.get(i[m])).revoke()):l.push(new j(d.path.concat(m),a[m]));return o(e.callbacks,l),c}:"splice"===b?function(){var c,f,g,i,m,n,r,s=[];for(c=Array.from(arguments),c.forEach(function(a,b){b>1&&a&&"object"==typeof a&&(c[b]=new h(a,b,d).proxy)}),m=0===c.length?0:c[0]<0?a.length+c[0]:c[0],n=c.length<2?a.length-m:c[1],r=Math.max(c.length-2,0),f=Reflect.apply(a[b],a,c),a.forEach(function(a,b){a&&"object"==typeof a&&(g=q.get(p.get(a)),g?g.ownKey=b:console.error("failed to resolve proxy -> target -> observed"))}),f.forEach(function(a,b){a&&"object"==typeof a&&(f[b]=p.get(a),q.get(f[b]).revoke())}),i=0;i<n;i++)i<r?s.push(new k(d.path.concat(m+i),a[m+i],f[i])):s.push(new l(d.path.concat(m+i),f[i]));for(;i<r;i++)s.push(new j(d.path.concat(m+i),a[m+i]));return o(e.callbacks,s),f}:Reflect.get(a,b)}function d(a,b,c){var d,e,f=a.hasOwnProperty(b),g=a[b],i=q.get(a),l=r.get(i.root),m=[];return d=Reflect.set(a,b,c),l.callbacks.length&&d&&c!==g&&(e=i.path.concat(b),g&&"object"==typeof g&&(q.get(p.get(g)).revoke(),p.has(g)&&p.delete(g)),c&&"object"==typeof c&&(a[b]=new h(c,b,i).proxy),f?m.push(new k(e,c,g)):m.push(new j(e,c)),i.preventCallbacks||o(l.callbacks,m)),d}function e(a,b){var c,d,e=a[b],f=q.get(a),g=r.get(f.root),h=[];return c=Reflect.deleteProperty(a,b),g.callbacks.length&&c&&("object"==typeof e&&e&&p.has(e)&&p.delete(e),d=f.path.concat(b),h.push(new l(d,e)),f.preventCallbacks||o(g.callbacks,h)),c}function f(a,b){a.forEach(function(c,d){c&&"object"==typeof c&&(a[d]=new h(c,d,b).proxy)})}function g(a,b){Reflect.ownKeys(a).forEach(function(c){a[c]&&"object"==typeof a[c]&&(a[c]=new h(a[c],c,b).proxy)})}function h(a,i,j){var k,l;if(!a||"object"!=typeof a)throw new Error("Observed MUST be created from a non null object origin");if(j&&("undefined"==typeof i||null===i))throw new Error("any non-root (parent-less) Observed MUST have an own path; now parent is "+j+"; key is "+i);if(j&&!(j instanceof h))throw new Error("parent, when supplied, MUST be an instance of Observed");k=b(a),Array.isArray(k)?(f(k,this),l=Proxy.revocable(k,{set:d,get:c,deleteProperty:e})):(g(k,this),l=Proxy.revocable(k,{set:d,deleteProperty:e})),q.set(k,this),p.set(l.proxy,k),Reflect.defineProperty(this,"revokable",{value:l}),Reflect.defineProperty(this,"proxy",{value:l.proxy}),Reflect.defineProperty(this,"parent",{value:j}),Reflect.defineProperty(this,"ownKey",{value:i,writable:!0})}function i(a){function b(a){if(e)throw new TypeError("revoked Observable MAY NOT be observed anymore");if("function"!=typeof a)throw new Error("observer (callback) parameter MUST be a function");f.indexOf(a)<0?f.push(a):console.info("observer (callback) may be bound to an observable only once")}function c(){if(e)throw new TypeError("revoked Observable MAY NOT be unobserved amymore");arguments.length?Array.from(arguments).forEach(function(a){var b=f.indexOf(a);b>=0&&f.splice(b,1)}):f.splice(0,f.length)}function d(){e?console.log("revokation of Observable have an effect only once"):(e=!0,a.revoke())}var e=!1,f=[];Reflect.defineProperty(a.proxy,"observe",{value:b}),Reflect.defineProperty(a.proxy,"unobserve",{value:c}),Reflect.defineProperty(a.proxy,"revoke",{value:d}),Reflect.defineProperty(this,"callbacks",{value:f})}function j(a,b){Reflect.defineProperty(this,"type",{value:"insert"}),Reflect.defineProperty(this,"path",{value:a}),Reflect.defineProperty(this,"value",{value:b})}function k(a,b,c){Reflect.defineProperty(this,"type",{value:"update"}),Reflect.defineProperty(this,"path",{value:a}),Reflect.defineProperty(this,"value",{value:b}),Reflect.defineProperty(this,"oldValue",{value:c})}function l(a,b){Reflect.defineProperty(this,"type",{value:"delete"}),Reflect.defineProperty(this,"path",{value:a}),Reflect.defineProperty(this,"oldValue",{value:b})}function m(){Reflect.defineProperty(this,"type",{value:"reverse"})}function n(){Reflect.defineProperty(this,"type",{value:"shuffle"})}function o(a,b){for(var c=0;c<a.length;c++)try{a[c](b)}catch(a){console.error(a)}}var p=new WeakMap,q=new WeakMap,r=new WeakMap;Reflect.defineProperty(h.prototype,"root",{get:function(){for(var a=this;a.parent;)a=a.parent;return a}}),Reflect.defineProperty(h.prototype,"path",{get:function(){for(var a=[],b=this;"undefined"!=typeof b.ownKey;)a.push(b.ownKey),b=b.parent;return a.reverse()}}),Reflect.defineProperty(h.prototype,"revoke",{value:function(){var a=this.proxy;Reflect.ownKeys(a).forEach(function(b){var c=a[b];c&&"object"==typeof c&&(q.get(p.get(c)).revoke(),p.get(a)[b]=p.get(c))}),this.revokable.revoke()}}),Reflect.defineProperty(i,"from",{value:function(a){if(!a||"object"!=typeof a)throw new Error("observable MAY ONLY be created from non-null object only");if("observe"in a||"unobserve"in a||"revoke"in a)throw new Error('target object MUST NOT have nor own neither inherited properties from the following list: "observe", "unobserve", "revoke"');var b=new h(a),c=new i(b);return r.set(b,c),b.proxy}}),Reflect.defineProperty(a,"Observable",{value:i})}(this); |
{ | ||
"name": "object-observer", | ||
"version": "0.1.01", | ||
"version": "0.2.0", | ||
"homepage": "https://github.com/gullerya/object-observer-js/README.md", | ||
@@ -14,4 +14,8 @@ "author": { | ||
"grunt-cli": "~1.2.0", | ||
"grunt-contrib-jshint": "~1.0.0", | ||
"grunt-contrib-uglify": "~1.0.1" | ||
"grunt-contrib-uglify": "^2.0.0", | ||
"grunt-karma": "~2.0.0", | ||
"babel-eslint": "6.1.2", | ||
"eslint-plugin-react": "6.1.2", | ||
"gruntify-eslint": "^3.1.0", | ||
"karma": "~0.13.0" | ||
}, | ||
@@ -33,4 +37,3 @@ "keywords": [ | ||
"files": [ | ||
"dist/object-observer.js", | ||
"dist/object-observer.min.js" | ||
"dist" | ||
], | ||
@@ -45,5 +48,5 @@ "scripts": { | ||
"bugs": { | ||
"url": "https://github.com/gullerya/data-tier/issues", | ||
"url": "https://github.com/gullerya/object-observer-js/issues", | ||
"email": "gullerya@gmail.com" | ||
} | ||
} |
@@ -11,3 +11,3 @@ [![npm version](https://badge.fury.io/js/object-observer.svg)](https://badge.fury.io/js/object-observer) | ||
Present library attempts to provide this functionality in a most clean and performant way. Main aspects: | ||
- Implementation relies on __Proxy__ mechanism | ||
- Implementation relies on __Proxy__ mechanism (specifically, revokable Proxy) | ||
- Observation is 'deep', yielding changes from a __sub-graphs__ too | ||
@@ -28,9 +28,17 @@ - Changes delivered in a __synchronous__ way | ||
#### Backlog: | ||
- Optimization for the cases of Array massive mutations | ||
- Add `readPath` and `writePath` utility methods in `DataPath` object (part of change data)? | ||
- Create build process including test automation on CI and probably minification/reorg of a consumable code | ||
- Changes, probably based on my own consumption of this library in [data-tier](https://github.com/gullerya/data-tier) module and/or community feedback | ||
- ~~Optimization for the cases of Array massive mutations~~ __Status__: done, any array change that previously was recalculating the whole array graph including all sub-graphs, now just updates the immediate indices of the changed array | ||
- Add `readPath` and `writePath` utility methods in `DataPath` object (part of change data)? __Status__: NA | ||
- Create build process including test automation on CI and probably minification/reorg of a consumable code. __Status__: added eslinting, minification as part of the build | ||
- Changes, probably based on my own consumption of this library in [data-tier](https://github.com/gullerya/data-tier) module and/or community feedback. __Status__: in progress | ||
- Consider adding support for a Symbol defined object properties. __Status__: in progress | ||
- Consider adding support for special native objects Map/WeakMap/Set/WeakSet (track this [issue](https://github.com/gullerya/object-observer-js/issues/1)). __Status__: in progress | ||
#### Versions | ||
- 0.1.0 | ||
- __0.2.0__ | ||
- Tech: moved proxy implementation to revokable | ||
- Tech: refactored algorithm of sub-graphs indexing and management; speed and memory improved, arrays massive changes improved significantly | ||
- API: added revokability to an Observable | ||
- 'detached' (`pop`, `shift`, `splice` actions on arrays) and replaced (simple update on objects and arrays, `fill` on arrays) observed sub-graphs are being revoked as well | ||
- results of 'detach' actions (`pop`, `shift`, `splice`) are turned back to the plain object (yet having all of the changes done to the observable) when returned by APIs | ||
- __0.1.0__ | ||
- First stable API release | ||
@@ -117,2 +125,9 @@ | ||
- __`revoke`__ - parameterless. All of the proxies along the observed graph will be revoked and thus become unusable. `observe` and `unobserve` methods will mimic the revoked `Proxy` behaviour and throw `TypeError` if used on the revoked `Observable`. Subsequent `revoke` invokations will have no effect: | ||
```javascript | ||
... | ||
observablePerson.revoke(); | ||
... | ||
``` | ||
# Examples | ||
@@ -190,3 +205,2 @@ | ||
Arrays notes: | ||
- Some of array operations are effectively moving/reindexing the whole rest of an array (shift, unshift, splice, reverse, sort). In cases of massive changes touching presumable the whole array I took pessimistic approach and opt for a special non-detailed event: 'reverse' for `reverse` and 'shuffle' for `sort`. The rest of these methods I'm handling in optimistic way opting to deliver the changes that directly related to the method invokation, while leaving out the implicit outcomes like reindexing of the rest of the Array. | ||
- am I missed something? | ||
- Some of array operations are effectively moving/reindexing the whole array (shift, unshift, splice, reverse, sort). In cases of massive changes touching presumably the whole array I took a pessimistic approach with a special non-detailed events: 'reverse' for `reverse`, 'shuffle' for `sort`. The rest of these methods I'm handling in an optimistic way delivering the changes that are directly related to the method invokation, while leaving out the implicit outcomes like reindexing of the rest of the Array. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
34224
421
203
1
8