temple-backbone
Advanced tools
Comparing version 1.1.2 to 1.2.0
@@ -7,3 +7,3 @@ (function ( global, factory ) { | ||
if ( typeof module !== 'undefined' && module.exports && typeof require === 'function' ) { | ||
factory( require( 'temple-mustache' ), require( 'backbone' ) ); | ||
module.exports = factory( require( 'temple-mustache' ), require( 'backbone' ) ); | ||
} | ||
@@ -32,37 +32,40 @@ | ||
var ModelProxy = Mustache.Proxy.extend({ | ||
constructor: function(target, model) { | ||
this.target = target; | ||
this.model = model; | ||
Mustache.Backbone = {}; | ||
this.target.on("change sync", this._onChange = function() { | ||
for (var k in this.attributes) { | ||
model.set(k, this.attributes[k], { reset: true }); | ||
} | ||
}); | ||
}, | ||
isLeaf: function() { return false; }, | ||
get: function(path) { | ||
return ModelProxy.get(this.target, path); | ||
}, | ||
set: function(path, value) { | ||
this.target.set(path, value); | ||
return true; | ||
}, | ||
keys: function() { | ||
return this.target.keys(); | ||
}, | ||
unset: function(path) { | ||
this.target.unset(path); | ||
return true; | ||
}, | ||
merge: function(mixin) { | ||
if (!Mustache.util.isPlainObject(mixin)) return false; | ||
this.target.set(mixin); | ||
return true; | ||
}, | ||
destroy: function() { | ||
this.target.off("change sync", this._onChange); | ||
} | ||
}, { | ||
// the issue with NPM dependencies is that sometimes we get a real Backbone | ||
// model or collection that did not originate from the same Backbone library | ||
// as this one. A simple instanceof check on this value will fail even | ||
// though our code can handle it just fine. Instead, we'll use duck type | ||
// checking to detect features so any Backbone-looking object will pass. | ||
var isModel = | ||
Mustache.util.isModel = | ||
Mustache.Backbone.isModel = function(val) { | ||
return val instanceof Backbone.Model || ( | ||
val != null && | ||
typeof val.cid === "string" && | ||
typeof val.attributes === "object" && | ||
typeof val.get === "function" && | ||
typeof val.set === "function" | ||
); | ||
} | ||
var isCollection = | ||
Mustache.util.isCollection = | ||
Mustache.Backbone.isCollection = function(val) { | ||
return val instanceof Backbone.Collection || ( | ||
val != null && | ||
Array.isArray(val.models) && | ||
typeof val.model === "function" && | ||
typeof val.add === "function" && | ||
typeof val.remove === "function" | ||
); | ||
} | ||
Mustache.Backbone.track = function(val) { | ||
if (isModel(val)) return trackModel(val); | ||
if (isCollection(val)) return trackCollection(val); | ||
throw new Error("Expecting Backbone model or collection to track."); | ||
} | ||
var ModelProxy = Mustache.Backbone.ModelProxy = { | ||
match: function(target) { | ||
@@ -74,72 +77,162 @@ return target instanceof Backbone.Model; | ||
} | ||
}); | ||
} | ||
var CollectionProxy = Mustache.Proxy.extend({ | ||
constructor: function(target, model) { | ||
this.target = target; | ||
this.model = model; | ||
var trackModel = Mustache.Backbone.trackModel = function(obj) { | ||
if (obj._deps != null) return obj; | ||
var mainDep = obj._main_dep = new Mustache.Dependency(); | ||
var deps = obj._deps = {}; | ||
var onChange; | ||
var model_cache = target.toArray(); | ||
this.set("length", target.length); | ||
obj.on("change", onChange = function() { | ||
Object.keys(this.changedAttributes()).forEach(function(k) { | ||
if (deps[k] != null) deps[k].changed(); | ||
}); | ||
this.listenTo(target, "add remove sort reset", function() { | ||
var models = target.toArray(); | ||
mainDep.changed(); | ||
}); | ||
models.forEach(function(m, index) { | ||
model.set(index.toString(), m); | ||
model.set(m.cid, m); | ||
if (m.id != null) model.set(m.id, m); | ||
}); | ||
var getter = obj.get; | ||
obj.get = function(k, options) { | ||
if (options == null || options.depend !== false) { | ||
if (deps[k] == null) deps[k] = new Mustache.Dependency(); | ||
deps[k].depend(); | ||
} | ||
// calculate difference between old and new | ||
var diff = model_cache.filter(function(m) { | ||
return models.indexOf(m) < 0; | ||
}); | ||
return getter.apply(this, arguments); | ||
} | ||
// remove old models | ||
if (diff.length) { | ||
diff.forEach(function(m) { | ||
model.unset(m.cid); | ||
if (m.id != null) model.unset(m.id); | ||
}); | ||
var toJSON = obj.toJSON; | ||
obj.toJSON = function(options) { | ||
if (options == null || options.depend !== false) { | ||
mainDep.depend(); // depend on any change made to the collection | ||
} | ||
for (var i = diff.length - 1; i >= 0; i--) { | ||
model.unset((models.length + i).toString()); | ||
} | ||
} | ||
return toJSON.apply(this, arguments); | ||
} | ||
model_cache = models; | ||
model.set("length", target.length); | ||
}); | ||
obj.removeTracking = function() { | ||
obj.off("change", onChange); | ||
obj.get = getter; | ||
obj.toJSON = toJSON; | ||
delete obj.removeTracking; | ||
return obj; | ||
} | ||
return obj; | ||
} | ||
var CollectionProxy = Mustache.Backbone.CollectionProxy = { | ||
isList: true, | ||
match: function(t) { return isCollection(t); }, | ||
get: function(t, k) { | ||
if (k === "length" || k === "$length") return this.length(t); | ||
var n = parseInt(k, 10); | ||
return n == k ? t.at(n) : t.get(k); | ||
}, | ||
isLeaf: function() { return false; }, | ||
isArray: function() { return true; }, | ||
get: function(path) { | ||
return CollectionProxy.get(this.target, path); | ||
length: function(t) { | ||
return typeof t.$length === "function" ? t.$length() : t.length; | ||
}, | ||
set: function(path, value) { | ||
return true; | ||
keys: function(t) { | ||
var len = this.length(t), keys = []; | ||
while (len--) { keys.unshift(len); } | ||
return keys; | ||
}, | ||
keys: function() { | ||
return Object.keys(this.target.toArray()); | ||
}, | ||
unset: function(path) { | ||
return true; | ||
}, | ||
merge: function(mixin) { | ||
return false; | ||
}, | ||
destroy: function() { | ||
this.stopListening(this.target); | ||
isEmpty: function(t) { | ||
return !!this.length(t); | ||
} | ||
}, { | ||
match: function(target) { | ||
return target instanceof Backbone.Collection; | ||
}, | ||
get: function(target, path) { | ||
if (path === "length") return target.length; | ||
return target.at(path) || target.get(path); | ||
} | ||
var trackCollection = Mustache.Backbone.trackCollection = function(obj) { | ||
if (obj._deps != null) return obj; | ||
var mainDep = obj._main_dep = new Mustache.Dependency(); | ||
var deps = obj._deps = { length: new Mustache.Dependency() }; | ||
var onChange; | ||
var model_cache = obj.map(function(m) { return m.cid; }); | ||
obj.on("add remove sort reset", onChange = function() { | ||
var models = obj.map(function(m) { return m.cid; }); | ||
var changed = []; | ||
// determine which indexes were modified | ||
for (var i = 0; i < Math.max(models.length, model_cache.length); i++) { | ||
if (models[i] !== model_cache[i] && deps[i]) deps[i].changed(); | ||
} | ||
// get added models | ||
models.forEach(function(cid, i) { | ||
if (model_cache.indexOf(cid) < 0 && | ||
changed.indexOf(cid) < 0) changed.push(cid); | ||
}); | ||
// get removed models | ||
model_cache.forEach(function(cid) { | ||
if (models.indexOf(cid) < 0 && | ||
changed.indexOf(cid) < 0) changed.push(cid); | ||
}); | ||
// notify of all changed ids and cids | ||
changed.forEach(function(cid) { | ||
if (deps[cid]) deps[cid].changed(); | ||
var id = this._byId[cid] && this._byId[cid].id; | ||
if (id && deps[id]) deps[id].changed(); | ||
}, this); | ||
// notify of a change in length | ||
if (model_cache.length !== models.length) deps.length.changed(); | ||
// mark major dep as changed | ||
mainDep.changed(); | ||
// set the model cache to our new values | ||
model_cache = models; | ||
}); | ||
obj.$length = function(options) { | ||
if (options == null || options.depend !== false) deps.length.depend(); | ||
return this.length; | ||
} | ||
}); | ||
var getter = obj.get; | ||
obj.get = function(k, options) { | ||
if (options == null || options.depend !== false) { | ||
if (deps[k] == null) deps[k] = new Mustache.Dependency(); | ||
deps[k].depend(); | ||
} | ||
return getter.apply(this, arguments); | ||
} | ||
var atter = obj.at; | ||
obj.at = function(i, options) { | ||
if (options == null || options.depend !== false) { | ||
if (deps[i] == null) deps[i] = new Mustache.Dependency(); | ||
deps[i].depend(); | ||
} | ||
return atter.apply(this, arguments); | ||
} | ||
var toJSON = obj.toJSON; | ||
obj.toJSON = function(options) { | ||
// depend on all changes made to the collection | ||
if (options == null || options.depend !== false) mainDep.depend(); | ||
return toJSON.apply(this, arguments); | ||
} | ||
obj.removeTracking = function() { | ||
obj.off("add remove sort reset", onChange); | ||
obj.get = getter; | ||
obj.at = atter; | ||
obj.toJSON = toJSON; | ||
delete obj.removeTracking; | ||
return obj; | ||
} | ||
return obj; | ||
} | ||
return Mustache.Backbone; | ||
})); |
{ | ||
"name": "temple-backbone", | ||
"description": "Adds Backbone specific handlers to Temple.", | ||
"version": "1.1.2", | ||
"description": "Adds Backbone specific handlers to Temple Mustache.", | ||
"version": "1.2.0", | ||
"author": "Beneath the Ink <info@beneaththeink.com>", | ||
@@ -20,8 +20,20 @@ "publishConfig": { | ||
}, | ||
"scripts": { | ||
"dev": "node test/browser", | ||
"test": "./node_modules/.bin/mocha test" | ||
}, | ||
"main": "./lib/backbone.js", | ||
"devDependencies": { | ||
"browserify": "5.11.2", | ||
"grunt": "0.4.5", | ||
"grunt-contrib-uglify": "0.5.1", | ||
"grunt-wrap2000": "0.1.0" | ||
"backbone": "~1.1.2", | ||
"chai": "~2.1.0", | ||
"grunt": "~0.4.5", | ||
"grunt-contrib-clean": "~0.6.0", | ||
"grunt-contrib-uglify": "~0.8.0", | ||
"grunt-contrib-watch": "~0.6.1", | ||
"grunt-browserify": "~3.4.0", | ||
"grunt-wrap2000": "~0.1.0", | ||
"minimist": "~1.1.0", | ||
"mocha": "~2.1.0", | ||
"temple-mustache": "~2.0.1", | ||
"underscore": "~1.8.2" | ||
}, | ||
@@ -33,2 +45,2 @@ "license": "MIT", | ||
] | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
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
9596
191
12