Comparing version 0.4.0 to 0.4.1
{ | ||
"name": "baobab", | ||
"main": "build/baobab.min.js", | ||
"version": "0.4.0", | ||
"version": "0.4.1", | ||
"homepage": "https://github.com/Yomguithereal/baobab", | ||
@@ -6,0 +6,0 @@ "author": { |
@@ -12,3 +12,3 @@ /** | ||
Object.defineProperty(Baobab, 'version', { | ||
value: '0.4.0' | ||
value: '0.4.1' | ||
}); | ||
@@ -15,0 +15,0 @@ |
{ | ||
"name": "baobab", | ||
"version": "0.4.0", | ||
"version": "0.4.1", | ||
"description": "JavaScript data tree with cursors.", | ||
@@ -18,2 +18,3 @@ "main": "index.js", | ||
"gulp-rename": "^1.2.0", | ||
"gulp-replace": "^0.5.3", | ||
"gulp-uglify": "^1.0.2", | ||
@@ -20,0 +21,0 @@ "jsdom": "^3.1.0", |
@@ -133,5 +133,5 @@ [![Build Status](https://travis-ci.org/Yomguithereal/baobab.svg)](https://travis-ci.org/Yomguithereal/baobab) | ||
A *baobab* tree can obviously be updated. However, one has to to understand that he won't do it, at least by default, synchronously. | ||
A *baobab* tree can obviously be updated. However, one has to understand that he won't do it, at least by default, synchronously. | ||
Rather, the tree will stack and merge every update order you give him and will only commit them at the next frame or next tick in node. | ||
Rather, the tree will stack and merge every update order you give it and will only commit them at the next frame or next tick in node. | ||
@@ -490,2 +490,13 @@ This enables the tree to perform efficient mutations and to be able to notify any relevant cursors that the data they are watching over has changed. | ||
*Getting root cursor* | ||
```js | ||
var tree = new Baobab({first: {second: 'yeah'}}), | ||
cursor = tree.select('first'); | ||
var rootCursor = tree.root(); | ||
// or | ||
var rootCursor = cursor.root(); | ||
``` | ||
*Check information about the cursor's location in the tree* | ||
@@ -492,0 +503,0 @@ |
@@ -206,2 +206,6 @@ /** | ||
Baobab.prototype.root = function() { | ||
return this.select(); | ||
}; | ||
Baobab.prototype.reference = function(path) { | ||
@@ -237,4 +241,15 @@ var data; | ||
var spec = {}; | ||
spec[key] = {$set: val}; | ||
if (type.Array(key)) { | ||
var path = helpers.solvePath(this.data, key); | ||
if (!path) | ||
throw Error('Baobab.set: could not solve dynamic path.'); | ||
spec = helpers.pathObject(path, {$set: val}); | ||
} | ||
else { | ||
spec[key] = {$set: val}; | ||
} | ||
return this.update(spec); | ||
@@ -293,3 +308,3 @@ }; | ||
Baobab.prototype.release = function() { | ||
this.kill(); | ||
delete this.data; | ||
@@ -303,2 +318,5 @@ delete this._transaction; | ||
delete this._cursors; | ||
// Killing event emitter | ||
this.kill(); | ||
}; | ||
@@ -305,0 +323,0 @@ |
@@ -16,2 +16,4 @@ /** | ||
cursor.on('update', c.cursorListener); | ||
c.tree.off('update', c.treeListener); | ||
c.tree.on('update', c.treeListener); | ||
} | ||
@@ -49,3 +51,3 @@ | ||
this.operators = []; | ||
this.root = first.root; | ||
this.tree = first.tree; | ||
@@ -78,6 +80,27 @@ // State | ||
// Initial bindings | ||
this.root.on('update', this.treeListener); | ||
bindCursor(this, first); | ||
// Lazy binding | ||
this.bound = false; | ||
var regularOn = this.on, | ||
regularOnce = this.once; | ||
var lazyBind = function() { | ||
if (self.bound) | ||
return; | ||
self.bound = true; | ||
self.cursors.forEach(function(cursor) { | ||
bindCursor(self, cursor); | ||
}); | ||
}; | ||
this.on = function() { | ||
lazyBind(); | ||
return regularOn.apply(this, arguments); | ||
}; | ||
this.once = function() { | ||
lazyBind(); | ||
return regularOnce.apply(this, arguments); | ||
}; | ||
// Attaching any other passed cursors | ||
@@ -98,7 +121,11 @@ rest.forEach(function(cursor) { | ||
// Safeguard | ||
if (!type.Cursor(cursor)) | ||
if (!type.Cursor(cursor)) { | ||
this.release(); | ||
throw Error('baobab.Combination.' + operator + ': argument should be a cursor.'); | ||
} | ||
if (~this.cursors.indexOf(cursor)) | ||
if (~this.cursors.indexOf(cursor)) { | ||
this.release(); | ||
throw Error('baobab.Combination.' + operator + ': cursor already in combination.'); | ||
} | ||
@@ -108,4 +135,6 @@ this.cursors.push(cursor); | ||
this.updates.length++; | ||
bindCursor(this, cursor); | ||
if (this.bound) | ||
bindCursor(this, cursor); | ||
return this; | ||
@@ -120,5 +149,2 @@ }; | ||
// Dropping own listeners | ||
this.kill(); | ||
// Dropping cursors listeners | ||
@@ -130,3 +156,3 @@ this.cursors.forEach(function(cursor) { | ||
// Dropping tree listener | ||
this.root.off('update', this.treeListener); | ||
this.tree.off('update', this.treeListener); | ||
@@ -136,4 +162,7 @@ // Cleaning | ||
this.operators = null; | ||
this.root = null; | ||
this.tree = null; | ||
this.updates = null; | ||
// Dropping own listeners | ||
this.kill(); | ||
}; | ||
@@ -140,0 +169,0 @@ |
@@ -16,3 +16,3 @@ /** | ||
*/ | ||
function Cursor(root, path, solvedPath, hash) { | ||
function Cursor(tree, path, solvedPath, hash) { | ||
var self = this; | ||
@@ -27,3 +27,3 @@ | ||
// Properties | ||
this.root = root; | ||
this.tree = tree; | ||
this.path = path; | ||
@@ -45,9 +45,5 @@ this.hash = hash; | ||
if (self.complexPath) | ||
self.solvedPath = helpers.solvePath(self.root.data, self.path); | ||
self.solvedPath = helpers.solvePath(self.tree.data, self.path); | ||
// If no handlers are attached, we stop | ||
if (!this._handlers.update.length && !this._handlersAll.length) | ||
return; | ||
// If selector listens at root, we fire | ||
// If selector listens at tree, we fire | ||
if (!self.path.length) | ||
@@ -57,3 +53,3 @@ return self.emit('update'); | ||
// Checking update log to see whether the cursor should update. | ||
root: | ||
outer: | ||
for (i = 0, l = log.length; i < l; i++) { | ||
@@ -72,3 +68,3 @@ c = log[i]; | ||
shouldFire = true; | ||
break root; | ||
break outer; | ||
} | ||
@@ -99,7 +95,26 @@ } | ||
// Listening | ||
this.root.on('update', this.updateHandler); | ||
// Making mixin | ||
this.mixin = mixins.cursor(this); | ||
// Lazy binding | ||
var bound = false, | ||
regularOn = this.on, | ||
regularOnce = this.once; | ||
var lazyBind = function() { | ||
if (bound) | ||
return; | ||
bound = true; | ||
self.tree.on('update', self.updateHandler); | ||
}; | ||
this.on = function() { | ||
lazyBind(); | ||
return regularOn.apply(this, arguments); | ||
}; | ||
this.once = function() { | ||
lazyBind(); | ||
return regularOnce.apply(this, arguments); | ||
}; | ||
} | ||
@@ -127,2 +142,6 @@ | ||
*/ | ||
Cursor.prototype.root = function() { | ||
return this.tree.root(); | ||
}; | ||
Cursor.prototype.select = function(path) { | ||
@@ -134,3 +153,3 @@ if (arguments.length > 1) | ||
throw Error('baobab.Cursor.select: invalid path.'); | ||
return this.root.select(this.path.concat(path)); | ||
return this.tree.select(this.path.concat(path)); | ||
}; | ||
@@ -140,3 +159,3 @@ | ||
if (this.solvedPath && this.solvedPath.length) | ||
return this.root.select(this.path.slice(0, -1)); | ||
return this.tree.select(this.path.slice(0, -1)); | ||
else | ||
@@ -153,3 +172,3 @@ return null; | ||
return last ? | ||
this.root.select(this.solvedPath.slice(0, -1).concat(last - 1)) : | ||
this.tree.select(this.solvedPath.slice(0, -1).concat(last - 1)) : | ||
null; | ||
@@ -164,3 +183,3 @@ }; | ||
return this.root.select(this.solvedPath.slice(0, -1).concat(0)); | ||
return this.tree.select(this.solvedPath.slice(0, -1).concat(0)); | ||
}; | ||
@@ -177,3 +196,3 @@ | ||
return this.root.select(this.solvedPath.slice(0, -1).concat(last + 1)); | ||
return this.tree.select(this.solvedPath.slice(0, -1).concat(last + 1)); | ||
}; | ||
@@ -189,3 +208,3 @@ | ||
return this.root.select(this.solvedPath.slice(0, -1).concat(list.length - 1)); | ||
return this.tree.select(this.solvedPath.slice(0, -1).concat(list.length - 1)); | ||
}; | ||
@@ -199,3 +218,3 @@ | ||
return this.root.select(this.solvedPath.concat(0)); | ||
return this.tree.select(this.solvedPath.concat(0)); | ||
}; | ||
@@ -211,5 +230,5 @@ | ||
if (type.Step(path)) | ||
return this.root.get(this.solvedPath.concat(path)); | ||
return this.tree.get(this.solvedPath.concat(path)); | ||
else | ||
return this.root.get(this.solvedPath); | ||
return this.tree.get(this.solvedPath); | ||
}; | ||
@@ -222,5 +241,5 @@ | ||
if (type.Step(path)) | ||
return this.root.reference(this.solvedPath.concat(path)); | ||
return this.tree.reference(this.solvedPath.concat(path)); | ||
else | ||
return this.root.reference(this.solvedPath); | ||
return this.tree.reference(this.solvedPath); | ||
}; | ||
@@ -233,5 +252,5 @@ | ||
if (type.Step(path)) | ||
return this.root.clone(this.solvedPath.concat(path)); | ||
return this.tree.clone(this.solvedPath.concat(path)); | ||
else | ||
return this.root.clone(this.solvedPath); | ||
return this.tree.clone(this.solvedPath); | ||
}; | ||
@@ -242,13 +261,30 @@ | ||
*/ | ||
Cursor.prototype.set = function(key, value) { | ||
Cursor.prototype.set = function(key, val) { | ||
if (arguments.length < 2) | ||
throw Error('baobab.Cursor.set: expecting at least key/value.'); | ||
var data = this.reference(); | ||
if (typeof data !== 'object') | ||
throw Error('baobab.Cursor.set: trying to set key to a non-object.'); | ||
var spec = {}; | ||
spec[key] = {$set: value}; | ||
if (type.Array(key)) { | ||
var path = helpers.solvePath(data, key); | ||
if (!path) | ||
throw Error('baobab.Cursor.set: could not solve dynamic path.'); | ||
spec = helpers.pathObject(path, {$set: val}); | ||
} | ||
else { | ||
spec[key] = {$set: val}; | ||
} | ||
return this.update(spec); | ||
}; | ||
Cursor.prototype.edit = function(value) { | ||
return this.update({$set: value}); | ||
Cursor.prototype.edit = function(val) { | ||
return this.update({$set: val}); | ||
}; | ||
@@ -260,2 +296,5 @@ | ||
if (typeof this.reference() !== 'object') | ||
throw Error('baobab.Cursor.set: trying to set key to a non-object.'); | ||
var spec = {}; | ||
@@ -267,2 +306,5 @@ spec[key] = {$unset: true}; | ||
Cursor.prototype.remove = function() { | ||
if (this.isRoot()) | ||
throw Error('baobab.Cursor.remove: cannot remove root node.'); | ||
return this.update({$unset: true}); | ||
@@ -278,11 +320,9 @@ }; | ||
// TODO: maybe composing should be done here rather than in the merge | ||
Cursor.prototype.thread = function(fn) { | ||
Cursor.prototype.chain = function(fn) { | ||
if (typeof fn !== 'function') | ||
throw Error('baobab.Cursor.thread: argument is not a function.'); | ||
throw Error('baobab.Cursor.chain: argument is not a function.'); | ||
return this.update({$thread: fn}); | ||
return this.update({$chain: fn}); | ||
}; | ||
// TODO: consider dropping the ahead testing | ||
Cursor.prototype.push = function(value) { | ||
@@ -319,3 +359,3 @@ if (!(this.reference() instanceof Array)) | ||
Cursor.prototype.update = function(spec) { | ||
this.root.update(helpers.pathObject(this.solvedPath, spec)); | ||
this.tree.update(helpers.pathObject(this.solvedPath, spec)); | ||
return this; | ||
@@ -339,6 +379,16 @@ }; | ||
Cursor.prototype.release = function() { | ||
this.root.off('update', this.updateHandler); | ||
// Removing listener on parent | ||
this.tree.off('update', this.updateHandler); | ||
// If the cursor is hashed, we unsubscribe from the parent | ||
if (this.hash) | ||
delete this.root._cursors[this.hash]; | ||
this.root = null; | ||
delete this.tree._cursors[this.hash]; | ||
// Dereferencing | ||
delete this.tree; | ||
delete this.path; | ||
delete this.solvePath; | ||
// Killing emitter | ||
this.kill(); | ||
@@ -345,0 +395,0 @@ }; |
@@ -209,3 +209,3 @@ /** | ||
solvedPath.push(path[i]); | ||
c = c[path[i]]; | ||
c = c[path[i]] || {}; | ||
} | ||
@@ -212,0 +212,0 @@ } |
@@ -138,5 +138,5 @@ /** | ||
} | ||
}].concat(cursor.root.options.mixins) | ||
}].concat(cursor.tree.options.mixins) | ||
}; | ||
} | ||
}; |
@@ -33,7 +33,7 @@ /** | ||
function update(target, spec, opts) { | ||
opts = opts || {}; | ||
opts = opts || {shiftReferences: false}; | ||
var log = {}; | ||
// Closure mutating the internal object | ||
(function mutator(o, spec, path) { | ||
(function mutator(o, spec, path, parent) { | ||
path = path || []; | ||
@@ -54,2 +54,3 @@ | ||
// TODO: this could be before in the recursion | ||
// Applying | ||
@@ -84,3 +85,12 @@ switch (k) { | ||
log[h] = true; | ||
delete o[k]; | ||
if (type.Array(o)) { | ||
if (!opts.shiftReferences) | ||
o.splice(k, 1); | ||
else | ||
parent[path[path.length - 1]] = o.slice(0, +k).concat(o.slice(+k + 1)); | ||
} | ||
else { | ||
delete o[k]; | ||
} | ||
} | ||
@@ -146,6 +156,8 @@ else if ('$set' in (spec[k] || {})) { | ||
// Recur | ||
// TODO: fix this horrendous behaviour. | ||
mutator( | ||
o[k], | ||
spec[k], | ||
path.concat(k) | ||
path.concat(k), | ||
o | ||
); | ||
@@ -152,0 +164,0 @@ } |
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
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
60963
1421
790
14