Skip to content
Browse files

see readme for v1.0.6

  • Loading branch information...
1 parent d7a6a2a commit 0b0bb0e064ddbaea2fcbab88239320af574792a7 @rhysbrettbowen committed Nov 26, 2012
Showing with 253 additions and 43 deletions.
  1. +41 −27 collection.js
  2. +1 −1 layout.js
  3. +95 −0 mod.js
  4. +32 −8 model.js
  5. +1 −1 plovr/test.js
  6. +6 −5 sync/ajax.js
  7. +59 −0 tests/mod_test.js
  8. +17 −0 tests/model_test.js
  9. +1 −1 tests/test_deps.js
View
68 collection.js
@@ -8,6 +8,8 @@ goog.provide('mvc.Collection');
goog.require('goog.events.Event');
goog.require('goog.events.EventTarget');
goog.require('mvc.Model');
+goog.require('mvc.Mod');
+goog.require('mvc.Filter');
@@ -37,12 +39,8 @@ mvc.Collection = function(opt_options) {
*/
this.models_ = [];
- /**
- * @private
- * @type {?function(mvc.Model, mvc.Model):number}
- */
- this.comparator_ = defaults['comparator'] &&
- goog.bind(defaults['comparator'], this);
+ this.comparator_ = [defaults['comparator'] &&
+ goog.bind(defaults['comparator'], this)];
/**
* @private
@@ -85,11 +83,9 @@ mvc.Collection = function(opt_options) {
this.removedModelsFns_ = [];
- /**
- * @private
- * @type {boolean}
- */
- this.modelChange_ = false;
+ this.modelChange_ = [false];
+
+ this.anyModelChange_ = [false];
/**
* @private
@@ -146,11 +142,29 @@ mvc.Collection.prototype.pluck = function(key) {
* @param {boolean=} opt_silent to suppress change event.
*/
mvc.Collection.prototype.setComparator = function(fn, opt_silent) {
- this.comparator_ = goog.bind(fn, this);
+ this.comparator_[0] = goog.bind(fn, this);
this.sort(opt_silent);
};
+
+mvc.Collection.prototype.getFiltered = function(fn) {
+ /** @constructor */
+ var Filter = function() {};
+
+ Filter.prototype = this;
+ var filter = new Filter();
+ filter.init = function(a,b) {};
+
+ goog.mixin(filter, mvc.Mod);
+ goog.mixin(filter, mvc.Filter);
+
+ filter.init(this, fn);
+
+ return filter;
+};
+
+
/**
* returns the number of models in the collection
*
@@ -168,8 +182,8 @@ mvc.Collection.prototype.getLength = function() {
*/
mvc.Collection.prototype.sort = function(opt_silent) {
var changeOrder = false;
- if (this.comparator_) {
- var comp = this.comparator_;
+ if (this.comparator_[0]) {
+ var comp = this.comparator_[0];
// need to wrap comparator in function to record a change
this.models_.sort(function(a, b) {
@@ -178,7 +192,7 @@ mvc.Collection.prototype.sort = function(opt_silent) {
changeOrder = true;
return ret;
});
- this.modelChange_ = this.modelChange_ || changeOrder;
+ this.modelChange_[0] = this.modelChange_[0] || changeOrder;
}
if (!opt_silent) {
this.dispatchEvent(goog.events.EventType.CHANGE);
@@ -220,10 +234,10 @@ mvc.Collection.prototype.add = function(model, opt_ind, opt_silent) {
// insert model and setup listeners for changes
insert = true;
- this.modelChange_ = true;
- this.anyModelChange_ = true;
+ this.modelChange_[0] = true;
+ this.anyModelChange_[0] = true;
var changeId = mod.bindAll(goog.bind(function() {
- this.anyModelChange_ = true;
+ this.anyModelChange_[0] = true;
this.sort();
}, this));
var unloadId = mod.bindUnload(function(e) {
@@ -298,8 +312,8 @@ mvc.Collection.prototype.remove = function(model, opt_silent) {
if (modelObj) {
// remove listeners and remove model
- this.modelChange_ = true;
- this.anyModelChange_ = true;
+ this.modelChange_[0] = true;
+ this.anyModelChange_[0] = true;
model.unbind(modelObj.unload);
model.unbind(modelObj.change);
goog.array.remove(this.models_, modelObj);
@@ -408,7 +422,7 @@ mvc.Collection.prototype.clear = function(opt_silent, opt_filter) {
modelsToClear = goog.array.filter(modelsToClear, /** @type {Function} */(opt_filter));
}
this.remove(modelsToClear, true);
- this.modelChange_ = true;
+ this.modelChange_[0] = true;
if (!opt_silent) {
this.dispatchEvent(goog.events.EventType.CHANGE);
}
@@ -549,15 +563,15 @@ mvc.Collection.prototype.change_ = function() {
goog.base(this, 'change_');
// if the models have changed then fire listeners
- if (this.modelChange_) {
+ if (this.modelChange_[0]) {
goog.array.forEach(goog.array.clone(this.modelChangeFns_), function(fn) {
fn(this);
}, this);
- this.modelChange_ = false;
+ this.modelChange_[0] = false;
}
// if the models have changed any values then fire listeners
- if (this.anyModelChange_) {
+ if (this.anyModelChange_[0]) {
goog.array.forEach(goog.array.clone(this.anyModelChangeFns_), function(fn) {
fn(this);
}, this);
@@ -577,20 +591,20 @@ mvc.Collection.prototype.change_ = function() {
}
}, this);
}
- this.anyModelChange_ = false;
+ this.anyModelChange_[0] = false;
}
goog.array.forEach(this.removedModelsFns_, function(fn) {
goog.array.forEach(this.removedModels_, function(mod) {
fn(mod.model, mod.id);
});
}, this);
- this.removedModels_ = [];
+ goog.array.clear(this.removedModels_);
goog.array.forEach(this.addedModelsFns_, function(fn) {
goog.array.forEach(this.addedModels_, function(mod) {
fn(mod);
});
}, this);
- this.addedModels_ = [];
+ goog.array.clear(this.addedModels_);
};
View
2 layout.js
@@ -329,4 +329,4 @@ mvc.Layout.prototype.removeChild = function(
/**
* @type {?Function}
*/
-mvc.Layout.prototype.placeChild_ = null;
+mvc.Layout.prototype.placeChild_ = null;
View
95 mod.js
@@ -0,0 +1,95 @@
+goog.provide('mvc.Mod');
+goog.provide('mvc.Filter');
+
+
+
+mvc.Mod = {
+
+ /** @this {mvc.Mod} */
+ init_: function() {
+ goog.events.listen(this.collection, goog.events.EventType.CHANGE,
+ this.change_, false, this);
+ },
+
+ /** @this {mvc.Mod} */
+ bindUnload: function(fn, opt_handler) {
+ var ret = this.collection.bind(fn, opt_handler);
+ },
+
+ /** @this {mvc.Mod} */
+ bind: function(name, fn, opt_handler) {
+ return this.passBind_(this.collection.bind, arguments);
+ },
+
+ /** @this {mvc.Mod} */
+ bindAll: function(fn, opt_handler) {
+ return this.passBind_(this.collection.bindAll, arguments);
+ },
+
+ /** @this {mvc.Mod} */
+ modelChange: function(fn, opt_handler) {
+ return this.passBind_(this.collection.modelChange, arguments);
+ },
+
+ /** @this {mvc.Mod} */
+ anyModelChange: function(fn, opt_handler) {
+ return this.passBind_(this.collection.anyModelChange, arguments);
+ },
+
+ /** @this {mvc.Mod} */
+ bindAdd: function(fn, opt_handler) {
+ return this.passBind_(this.collection.bindAdd, arguments);
+ },
+
+ /** @this {mvc.Mod} */
+ bindRemove: function(fn, opt_handler) {
+ return this.passBind_(this.collection.bindRemove, arguments);
+ },
+
+ /** @this {mvc.Mod} */
+ passBind_: function(fn, args) {
+ var ret = fn.apply(this.collection, args);
+ this.bound_.push(ret);
+ return ret;
+ },
+
+ change_: goog.nullFunction
+};
+
+mvc.Filter = {
+
+ /** @this {mvc.Mod} */
+ init: function(collection, filter) {
+ this.filter_ = filter;
+ this.collection = collection;
+ this.lastFilter_ = collection.getModels(filter);
+ this.modelChangeFns_ = [];
+ this.init_();
+ },
+
+ /** @this {mvc.Mod} */
+ getModels: function() {
+ return this.collection.getModels(this.filter_);
+ },
+
+ /** @this {mvc.Mod} */
+ change_: function() {
+ if (!goog.array.equals(this.lastFilter_,
+ goog.array.map(this.getModels(), function(model) {
+ return model.cid_;
+ }))) {
+ goog.array.forEach(goog.array.clone(this.modelChangeFns_), function(fn) {
+ fn(this);
+ }, this);
+ }
+ this.lastFilter_ = goog.array.map(this.getModels(), function(model) {
+ return model.cid_;
+ });
+ },
+
+ /** @this {mvc.Mod} */
+ modelChange: function(fn, opt_handler) {
+ return mvc.Collection.prototype.modelChange.apply(this, arguments);
+ }
+
+};
View
40 model.js
@@ -88,8 +88,13 @@ mvc.Model = function(opt_options) {
*/
this.cid_ = '' + goog.getUid(this);
+ this.changing_ = [false];
+ this.waitChange_ = {};
+ this.waitChangeSilent_ = [true];
+
this.set(defaults['attr']);
+
this.dispatchEvent(goog.events.EventType.LOAD);
};
goog.inherits(mvc.Model, goog.events.EventTarget);
@@ -362,6 +367,17 @@ mvc.Model.prototype.has = function(key) {
*/
mvc.Model.prototype.set = function(key, opt_val, opt_silent) {
+ if (this.changing_[0]) {
+ if (goog.isString(key)) {
+ this.waitChange_[key] = opt_val;
+ this.waitChangeSilent_[0] = this.waitChangeSilent_[0] && opt_silent;
+ } else {
+ goog.object.extend(this.waitChangeSilent_, key);
+ this.waitChangeSilent_[0] = this.waitChangeSilent_[0] && opt_val;
+ }
+ return;
+ }
+
// handle key value as string or object
var success = false;
if (goog.isString(key)) {
@@ -451,8 +467,6 @@ mvc.Model.prototype.unset = function(key, opt_silent) {
*/
mvc.Model.prototype.change = function() {
this.dispatchEvent(goog.events.EventType.CHANGE);
- this.prev_ = /** @type {Object.<(Object|null)>|null} */
- (goog.object.unsafeClone(this.attr_));
};
@@ -627,6 +641,7 @@ mvc.Model.prototype.revert = function(opt_silent) {
/**
* @param {boolean=} opt_sync pass true to del the sync to delete the model.
* @param {Function=} opt_callback function to call after sync delete finishes
+ * @override
*/
mvc.Model.prototype.dispose = function(opt_sync, opt_callback) {
if (opt_sync)
@@ -635,7 +650,7 @@ mvc.Model.prototype.dispose = function(opt_sync, opt_callback) {
goog.array.forEach(goog.array.clone(this.onUnload_), function(fn) {
fn(this);
}, this);
- this.disposeInternal();
+ goog.base(this, 'dispose');
};
@@ -730,6 +745,7 @@ mvc.Model.prototype.getBinder = function(key) {
* @private
*/
mvc.Model.prototype.change_ = function() {
+ this.changing_[0] = true;
var changes = this.getChanges();
goog.array.forEach(this.bound_, function(val) {
if (goog.array.some(val.attr, function(attr) {
@@ -741,11 +757,19 @@ mvc.Model.prototype.change_ = function() {
},this)));
}
}, this);
- if (!changes.length)
- return;
- goog.object.forEach(this.boundAll_, function(val) {
- val(this);
- }, this);
+ if (changes.length) {
+ goog.object.forEach(this.boundAll_, function(val) {
+ val(this);
+ }, this);
+ }
+ this.prev_ = /** @type {Object.<(Object|null)>|null} */
+ (goog.object.unsafeClone(this.attr_));
+ this.changing_[0] = false;
+ var clone = goog.object.clone(this.waitChange_);
+ goog.object.clear(this.waitChange_);
+ var silent = this.waitChangeSilent_[0];
+ this.waitChangeSilent_[0] = true;
+ this.set(clone, silent);
};
View
2 plovr/test.js
@@ -1,6 +1,6 @@
{
"id": "testing",
- "inputs": ["../collection.js", "../layout.js", "../control.js", "../mediator.js", "../model.js", "../router.js", "../store.js", "../sync/sync.js", "../sync/ajax.js"],
+ "inputs": ["../collection.js", "../mod.js", "../layout.js", "../control.js", "../mediator.js", "../model.js", "../router.js", "../store.js", "../sync/sync.js", "../sync/ajax.js"],
"paths": ["../"],
"output-file": "../tests/test_deps.js",
"test-template": "../tests/test.soy"
View
11 sync/ajax.js
@@ -60,7 +60,8 @@ mvc.AjaxSync.prototype.urlifyString = function(val) {
v = v(model);
return v.replace(/:(\w+)/g,
function(id) {
- return escape(model.get(id.substring(1)));
+ return model.get(id.substring(1), '')
+ .replace(/#/g, '%23').replace(/&/g, '%26');
});
};
};
@@ -124,7 +125,7 @@ mvc.AjaxSync.prototype.del = function(model, opt_callback) {
mvc.AjaxSync.prototype.onCreateComplete_ = function(model, callback, e) {
var xhr = e.target;
model.set('id', xhr.getResponseJson()['result']['id']);
- callback.call(model, model);
+ callback.call(model, model, e);
};
@@ -140,7 +141,7 @@ mvc.AjaxSync.prototype.onReadComplete_ = function(model, callback, e) {
var xhr = e.target;
var json = xhr.getResponseJson()['result'];
model.set(json);
- callback.call(model, model);
+ callback.call(model, model, e);
};
@@ -153,7 +154,7 @@ mvc.AjaxSync.prototype.onReadComplete_ = function(model, callback, e) {
* @param {goog.events.Event} e the completed xhr event.
*/
mvc.AjaxSync.prototype.onUpdateComplete_ = function(model, callback, e) {
- callback.call(model, model);
+ callback.call(model, model, e);
};
@@ -166,5 +167,5 @@ mvc.AjaxSync.prototype.onUpdateComplete_ = function(model, callback, e) {
* @param {goog.events.Event} e the completed xhr event.
*/
mvc.AjaxSync.prototype.onDelComplete_ = function(model, callback, e) {
- callback.call(model, model);
+ callback.call(model, model, e);
};
View
59 tests/mod_test.js
@@ -0,0 +1,59 @@
+goog.require('goog.testing.PropertyReplacer');
+goog.require('goog.testing.jsunit');
+goog.require('mvc.Mod');
+goog.require('mvc.Collection');
+goog.require('mvc.Model');
+
+
+var coll;
+var model1, model2, model3, model4, model5, model6;
+
+var setUp = function() {
+ model1 = new mvc.Model({'a': 1});
+ model2 = new mvc.Model({'a': 2});
+ model3 = new mvc.Model({'a': 3});
+ model4 = new mvc.Model({'a': 4});
+ model5 = new mvc.Model({'a': 5});
+ model6 = new mvc.Model({'a': 6});
+
+ coll = new mvc.Collection();
+
+ coll.add(model1);
+ coll.add(model2);
+ coll.add(model3);
+ coll.add(model4);
+ coll.add(model5);
+ coll.add(model6);
+
+};
+
+var testFilter = function() {
+
+ var fn = function(a) {return a.get('a')%2;};
+
+ var test = function(arg) {
+ console.log(arg, 'change found');
+ };
+
+ var Filter = function() {};
+ Filter.prototype = coll;
+ var filter = new Filter();
+
+ goog.mixin(filter, mvc.Mod);
+ goog.mixin(filter, mvc.Filter);
+
+ filter.init(coll, fn);
+
+ filter.modelChange(test);
+
+ coll.modelChange(test);
+
+ console.log('add Odd');
+
+ filter.add(new mvc.Model({'a': 7}));
+
+ console.log('add Even');
+
+ filter.add(new mvc.Model({'a': 8}));
+};
+
View
17 tests/model_test.js
@@ -606,3 +606,20 @@ var testDotPropertyBind = function() {
assertFalse(bindAB);
};
+var testBindFnWithSetter = function() {
+ var arr = 0, curItem = 0, silent = false;
+ var m = mvc.Model.create({
+ 'arr': [1,2,3],
+ 'curItem': 0
+ });
+ m.bind('curItem', function() {
+ curItem++;
+ m.set('arr', [4,5], silent);
+ });
+ m.bind('arr', function() { arr++; });
+ m.set('curItem', 1);
+ assertEquals('Should have fired "arr" listener', arr, 1);
+ assertEquals('Should have changed "arr"', m.get('arr').length, 2);
+ assertEquals('Should have fired "curItem" listener', curItem, 1);
+}
+
View
2 tests/test_deps.js
@@ -482,4 +482,4 @@ goog.net.XhrManager.Request.prototype.setAborted=function(a){this.aborted_=a};go
goog.net.XhrManager.Request.prototype.disposeInternal=function(){goog.net.XhrManager.Request.superClass_.disposeInternal.call(this);delete this.xhrEventCallback_;delete this.completeCallback_};mvc.AjaxSync=function(a){var b=function(){return""};this.baseUrls_={create:b,read:b,update:b,del:b};if(goog.isString(a)||goog.isFunction(a))a={create:a,read:a,update:a,del:a};goog.object.extend(this.baseUrls_,goog.object.map(a,this.urlifyString));this.xhr_=new goog.net.XhrManager;this.sendCount_=0};mvc.AjaxSync.prototype.urlifyString=function(a){return goog.isString(a)?function(b){return a.replace(/:(\w+)/g,function(a){return b.get(a.substring(1))})}:a};
mvc.AjaxSync.prototype.create=function(a,b){this.xhr_.send(""+this.sendCount_++,this.baseUrls_.create(a),"POST",goog.Uri.QueryData.createFromMap(a.toJson()).toString(),void 0,void 0,goog.bind(this.onCreateComplete_,this,a,b||goog.nullFunction))};mvc.AjaxSync.prototype.read=function(a,b){this.xhr_.send(""+this.sendCount_++,this.baseUrls_.read(a),"GET",void 0,void 0,void 0,goog.bind(this.onReadComplete_,this,a,b||goog.nullFunction))};
mvc.AjaxSync.prototype.update=function(a,b){this.xhr_.send(""+this.sendCount_++,this.baseUrls_.update(a),"PUT",goog.Uri.QueryData.createFromMap(a.toJson()).toString(),{"Content-Type":"application/x-www-form-urlencoded"},void 0,goog.bind(this.onUpdateComplete_,this,a,b||goog.nullFunction))};mvc.AjaxSync.prototype.del=function(a,b){this.xhr_.send(""+this.sendCount_++,this.baseUrls_.del(a),"DELETE",void 0,void 0,void 0,goog.bind(this.onDelComplete_,this,a,b||goog.nullFunction))};
-mvc.AjaxSync.prototype.onCreateComplete_=function(a,b,c){a.set("id",c.target.getResponseJson().result.id)};mvc.AjaxSync.prototype.onReadComplete_=function(a,b,c){b=c.target.getResponseJson().result;a.set(b)};mvc.AjaxSync.prototype.onUpdateComplete_=function(){};mvc.AjaxSync.prototype.onDelComplete_=function(){};
+mvc.AjaxSync.prototype.onCreateComplete_=function(a,b,c){a.set("id",c.target.getResponseJson().result.id)};mvc.AjaxSync.prototype.onReadComplete_=function(a,b,c){b=c.target.getResponseJson().result;a.set(b)};mvc.AjaxSync.prototype.onUpdateComplete_=function(){};mvc.AjaxSync.prototype.onDelComplete_=function(){};

0 comments on commit 0b0bb0e

Please sign in to comment.
Something went wrong with that request. Please try again.