Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Version 0.9.4.

  • Loading branch information...
commit a29731604f8e07d33c806d9fe1cb42505f1ffca2 1 parent c9b827e
@benpickles authored
View
7 CHANGELOG.markdown
@@ -1,5 +1,12 @@
# Changelog
+## 0.9.4
+
+ * Add `Model.map()` method.
+ * Protect the collection from accidental modification allowing you to safely `remove()` a model from within an `each` loop.
+ * Added configurable Model.unique_key, exposed as instance.id() and used in finders. Defaults to 'id'. [Ismael Celis and Oliver Nightingale]
+ * Protect an instance's initial attributes from accidental modification.
+
## 0.9.3
* Add the ability to define a custom `initialize()` instance method that is called at the end of the initialization process. Thanks to Ismael Celis for the idea.
View
2  VERSION
@@ -1 +1 @@
-0.9.3
+0.9.4
View
676 dist/js-model-0.9.4.js
@@ -0,0 +1,676 @@
+/* js-model JavaScript library, version 0.9.4
+ * (c) 2010 Ben Pickles
+ *
+ * Released under MIT license.
+ */
+var Model = function(name, class_methods, instance_methods) {
+ class_methods = class_methods || {};
+ instance_methods = instance_methods || {};
+
+ // The model constructor.
+ var model = function(attributes) {
+ this.attributes = jQuery.extend({}, attributes)
+ this.changes = {};
+ this.errors = new Model.Errors(this);
+ this.uid = [name, Model.UID.generate()].join("-")
+ if (jQuery.isFunction(this.initialize)) this.initialize()
+ };
+
+ // Persistence is special, remove it from class_methods.
+ var persistence = class_methods.persistence
+ delete class_methods.persistence
+
+ // Apply class methods and extend with any custom class methods. Make sure
+ // vitals are added last so they can't be overridden.
+ jQuery.extend(model, Model.Callbacks, Model.ClassMethods, class_methods, {
+ _name: name,
+ collection: [],
+
+ // Convenience method to allow a simple method of chaining class methods.
+ chain: function(collection) {
+ return jQuery.extend({}, this, { collection: collection });
+ }
+ });
+
+ // Initialise persistence with a reference to the class.
+ if (persistence) model.persistence = persistence(model)
+
+ // Add default and custom instance methods.
+ jQuery.extend(model.prototype, Model.Callbacks, Model.InstanceMethods,
+ instance_methods);
+
+ return model;
+};
+
+Model.Callbacks = {
+ bind: function(event, callback) {
+ this.callbacks = this.callbacks || {}
+ this.callbacks[event] = this.callbacks[event] || [];
+ this.callbacks[event].push(callback);
+ return this;
+ },
+
+ trigger: function(name, data) {
+ this.callbacks = this.callbacks || {}
+
+ var callbacks = this.callbacks[name];
+
+ if (callbacks) {
+ for (var i = 0; i < callbacks.length; i++) {
+ callbacks[i].apply(this, data || []);
+ }
+ }
+
+ return this;
+ },
+
+ unbind: function(event, callback) {
+ this.callbacks = this.callbacks || {}
+
+ if (callback) {
+ var callbacks = this.callbacks[event] || [];
+
+ for (var i = 0; i < callbacks.length; i++) {
+ if (callbacks[i] === callback) {
+ this.callbacks[event].splice(i, 1);
+ }
+ }
+ } else {
+ delete this.callbacks[event];
+ }
+
+ return this;
+ }
+};
+
+Model.ClassMethods = {
+ unique_key: 'id',
+
+ add: function() {
+ var added = [];
+
+ for (var i = 0; i < arguments.length; i++) {
+ var model = arguments[i];
+ var id = model.id()
+
+ if (jQuery.inArray(model, this.collection) === -1 && !(id && this.find(id))) {
+ this.collection.push(model);
+ added.push(model);
+ }
+ }
+
+ if (added.length > 0) this.trigger("add", added);
+
+ return this;
+ },
+
+ all: function() {
+ return this.collection.slice()
+ },
+
+ count: function() {
+ return this.collection.length;
+ },
+
+ detect: function(func) {
+ var all = this.all(),
+ model
+
+ for (var i = 0, length = all.length; i < length; i++) {
+ model = all[i]
+ if (func.call(model, i)) return model
+ }
+ },
+
+ each: function(func) {
+ var all = this.all()
+
+ for (var i = 0, length = all.length; i < length; i++) {
+ func.call(all[i], i)
+ }
+
+ return this;
+ },
+
+ find: function(id) {
+ return this.detect(function() {
+ return this.id() == id;
+ })
+ },
+
+ first: function() {
+ return this.all()[0]
+ },
+
+ load: function(callback) {
+ if (this.persistence) {
+ var self = this
+
+ this.persistence.read(function(models) {
+ for (var i = 0, length = models.length; i < length; i++) {
+ self.add(models[i])
+ }
+
+ if (callback) callback.call(self, models)
+ })
+ }
+
+ return this
+ },
+
+ last: function() {
+ var all = this.all();
+ return all[all.length - 1]
+ },
+
+ map: function(func) {
+ var all = this.all()
+ var values = []
+
+ for (var i = 0, length = all.length; i < length; i++) {
+ values.push(func.call(all[i], all[i], i))
+ }
+
+ return values
+ },
+
+ pluck: function(attribute) {
+ var all = this.all()
+ var plucked = []
+
+ for (var i = 0, length = all.length; i < length; i++) {
+ plucked.push(all[i].attr(attribute))
+ }
+
+ return plucked
+ },
+
+ remove: function(model) {
+ var index
+
+ for (var i = 0, length = this.collection.length; i < length; i++) {
+ if (this.collection[i] === model) {
+ index = i
+ break
+ }
+ }
+
+ if (index != undefined) {
+ this.collection.splice(index, 1);
+ this.trigger("remove", [model]);
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ reverse: function() {
+ return this.chain(this.all().reverse())
+ },
+
+ select: function(func) {
+ var all = this.all(),
+ selected = [],
+ model
+
+ for (var i = 0, length = all.length; i < length; i++) {
+ model = all[i]
+ if (func.call(model, i)) selected.push(model)
+ }
+
+ return this.chain(selected);
+ },
+
+ sort: function(func) {
+ var sorted = this.all().slice().sort(func)
+ return this.chain(sorted);
+ },
+
+ sortBy: function(attribute_or_func) {
+ var is_func = jQuery.isFunction(attribute_or_func)
+ var extract = function(model) {
+ return attribute_or_func.call(model)
+ }
+
+ return this.sort(function(a, b) {
+ var a_attr = is_func ? extract(a) : a.attr(attribute_or_func)
+ var b_attr = is_func ? extract(b) : b.attr(attribute_or_func)
+
+ if (a_attr < b_attr) {
+ return -1
+ } else if (a_attr > b_attr) {
+ return 1
+ } else {
+ return 0
+ }
+ })
+ }
+};
+
+Model.Errors = function(model) {
+ this.errors = {};
+ this.model = model;
+};
+
+Model.Errors.prototype = {
+ add: function(attribute, message) {
+ if (!this.errors[attribute]) this.errors[attribute] = [];
+ this.errors[attribute].push(message);
+ return this
+ },
+
+ all: function() {
+ return this.errors;
+ },
+
+ clear: function() {
+ this.errors = {};
+ return this
+ },
+
+ each: function(func) {
+ for (var attribute in this.errors) {
+ for (var i = 0; i < this.errors[attribute].length; i++) {
+ func.call(this, attribute, this.errors[attribute][i]);
+ }
+ }
+ return this
+ },
+
+ on: function(attribute) {
+ return this.errors[attribute] || [];
+ },
+
+ size: function() {
+ var count = 0;
+ this.each(function() { count++; });
+ return count;
+ }
+};
+
+Model.InstanceMethods = {
+ attr: function(name, value) {
+ if (arguments.length === 0) {
+ // Combined attributes/changes object.
+ return jQuery.extend({}, this.attributes, this.changes);
+ } else if (arguments.length === 2) {
+ // Don't write to attributes yet, store in changes for now.
+ if (this.attributes[name] === value) {
+ // Clean up any stale changes.
+ delete this.changes[name];
+ } else {
+ this.changes[name] = value;
+ }
+ return this;
+ } else if (typeof name === "object") {
+ // Mass-assign attributes.
+ for (var key in name) {
+ this.attr(key, name[key]);
+ }
+ return this;
+ } else {
+ // Changes take precedent over attributes.
+ return (name in this.changes) ?
+ this.changes[name] :
+ this.attributes[name];
+ }
+ },
+
+ callPersistMethod: function(method, callback) {
+ var self = this;
+
+ // Automatically manage adding and removing from the model's Collection.
+ var manageCollection = function() {
+ if (method === "create") {
+ self.constructor.add(self);
+ } else if (method === "destroy") {
+ self.constructor.remove(self)
+ }
+ };
+
+ // Wrap the existing callback in this function so we always manage the
+ // collection and trigger events from here rather than relying on the
+ // persistence adapter to do it for us. The persistence adapter is
+ // only required to execute the callback with a single argument - a
+ // boolean to indicate whether the call was a success - though any
+ // other arguments will also be forwarded to the original callback.
+ var wrappedCallback = function(success) {
+ if (success) {
+ // Merge any changes into attributes and clear changes.
+ self.merge(self.changes).reset();
+
+ // Add/remove from collection if persist was successful.
+ manageCollection();
+
+ // Trigger the event before executing the callback.
+ self.trigger(method);
+ }
+
+ // Store the return value of the callback.
+ var value;
+
+ // Run the supplied callback.
+ if (callback) value = callback.apply(self, arguments);
+
+ return value;
+ };
+
+ if (this.constructor.persistence) {
+ this.constructor.persistence[method](this, wrappedCallback);
+ } else {
+ wrappedCallback.call(this, true);
+ }
+ },
+
+ destroy: function(callback) {
+ this.callPersistMethod("destroy", callback);
+ return this;
+ },
+
+ id: function() {
+ return this.attributes[this.constructor.unique_key];
+ },
+
+ merge: function(attributes) {
+ jQuery.extend(this.attributes, attributes);
+ return this;
+ },
+
+ newRecord: function() {
+ return this.id() === undefined
+ },
+
+ reset: function() {
+ this.errors.clear();
+ this.changes = {};
+ return this;
+ },
+
+ save: function(callback) {
+ if (this.valid()) {
+ var method = this.newRecord() ? "create" : "update";
+ this.callPersistMethod(method, callback);
+ } else if (callback) {
+ callback(false);
+ }
+
+ return this;
+ },
+
+ valid: function() {
+ this.errors.clear();
+ this.validate();
+ return this.errors.size() === 0;
+ },
+
+ validate: function() {
+ return this;
+ }
+};
+
+Model.localStorage = function() {
+ if (!window.localStorage) {
+ return function() {
+ return {
+ create: function(model, callback) {
+ callback(true)
+ },
+
+ destroy: function(model, callback) {
+ callback(true)
+ },
+
+ read: function(callback) {
+ callback([])
+ },
+
+ update: function(model, callback) {
+ callback(true)
+ }
+ }
+ }
+ }
+
+ return function(klass) {
+ var collection_uid = [klass._name, "collection"].join("-")
+ var readIndex = function() {
+ var data = localStorage[collection_uid]
+ return data ? JSON.parse(data) : []
+ }
+ var writeIndex = function(uids) {
+ localStorage.setItem(collection_uid, JSON.stringify(uids))
+ }
+ var addToIndex = function(uid) {
+ var uids = readIndex()
+
+ if (jQuery.inArray(uid, uids) === -1) {
+ uids.push(uid)
+ writeIndex(uids)
+ }
+ }
+ var removeFromIndex = function(uid) {
+ var uids = readIndex()
+ var index = jQuery.inArray(uid, uids)
+
+ if (index > -1) {
+ uids.splice(index, 1)
+ writeIndex(uids)
+ }
+ }
+ var store = function(model) {
+ var uid = model.uid,
+ data = JSON.stringify(model.attr())
+ localStorage.setItem(uid, data)
+ addToIndex(uid)
+ }
+
+ return {
+ create: function(model, callback) {
+ store(model)
+ callback(true)
+ },
+
+ destroy: function(model, callback) {
+ localStorage.removeItem(model.uid)
+ removeFromIndex(model.uid)
+ callback(true)
+ },
+
+ read: function(callback) {
+ if (!callback) return false
+
+ var existing_uids = klass.map(function() { return this.uid })
+ var uids = readIndex()
+ var models = []
+ var attributes, model, uid
+
+ for (var i = 0, length = uids.length; i < length; i++) {
+ uid = uids[i]
+
+ if (jQuery.inArray(uid, existing_uids) == -1) {
+ attributes = JSON.parse(localStorage[uid])
+ model = new klass(attributes)
+ model.uid = uid
+ models.push(model)
+ }
+ }
+
+ callback(models)
+ },
+
+ update: function(model, callback) {
+ store(model)
+ callback(true)
+ }
+ }
+ }
+};
+
+Model.Log = function() {
+ if (window.console) window.console.log.apply(window.console, arguments);
+};
+
+Model.REST = function(resource, methods) {
+ var PARAM_NAME_MATCHER = /:([\w\d]+)/g;
+ var resource_param_names = (function() {
+ var resource_param_names = []
+ var param_name
+
+ while ((param_name = PARAM_NAME_MATCHER.exec(resource)) !== null) {
+ resource_param_names.push(param_name[1])
+ }
+
+ return resource_param_names
+ })()
+
+ var rest_persistence = jQuery.extend({
+ path: function(model) {
+ var path = resource;
+ $.each(resource_param_names, function(i, param) {
+ path = path.replace(":" + param, model.attributes[param]);
+ });
+ return path;
+ },
+
+ create: function(model, callback) {
+ return this.xhr('POST', this.create_path(model), model, callback);
+ },
+
+ create_path: function(model) {
+ return this.path(model);
+ },
+
+ destroy: function(model, callback) {
+ return this.xhr('DELETE', this.destroy_path(model), model, callback);
+ },
+
+ destroy_path: function(model) {
+ return this.update_path(model);
+ },
+
+ params: function(model) {
+ var params;
+ if (model) {
+ var attributes = model.attr();
+ delete attributes[model.constructor.unique_key];
+ params = {};
+ params[model.constructor._name.toLowerCase()] = attributes;
+ } else {
+ params = null;
+ }
+ if(jQuery.ajaxSettings.data){
+ params = jQuery.extend({}, jQuery.ajaxSettings.data, params)
+ }
+ return JSON.stringify(params)
+ },
+
+ read: function(callback) {
+ var klass = this.klass
+
+ return this.xhr("GET", this.read_path(), null, function(success, xhr, data) {
+ data = jQuery.makeArray(data)
+ var models = []
+
+ for (var i = 0, length = data.length; i < length; i++) {
+ models.push(new klass(data[i]))
+ }
+
+ callback(models)
+ })
+ },
+
+ read_path: function() {
+ return resource
+ },
+
+ update: function(model, callback) {
+ return this.xhr('PUT', this.update_path(model), model, callback);
+ },
+
+ update_path: function(model) {
+ return [this.path(model), model.id()].join('/');
+ },
+
+ xhr: function(method, url, model, callback) {
+ var self = this;
+ var data = method == 'GET' ? undefined : this.params(model);
+
+ return jQuery.ajax({
+ type: method,
+ url: url,
+ contentType: "application/json",
+ dataType: "json",
+ data: data,
+ dataFilter: function(data, type) {
+ return /\S/.test(data) ? data : undefined;
+ },
+ complete: function(xhr, textStatus) {
+ self.xhrComplete(xhr, textStatus, model, callback)
+ }
+ });
+ },
+
+ xhrComplete: function(xhr, textStatus, model, callback) {
+ // Allow custom handlers to be defined per-HTTP status code.
+ var handler = Model.REST["handle" + xhr.status]
+ if (handler) handler.call(this, xhr, textStatus, model)
+
+ var success = textStatus === "success"
+ var data = Model.REST.parseResponseData(xhr)
+
+ // Remote data is the definitive source, update model.
+ if (success && model && data) model.attr(data)
+
+ if (callback) callback.call(model, success, xhr, data)
+ }
+ }, methods)
+
+ return function(klass) {
+ rest_persistence.klass = klass
+ return rest_persistence
+ }
+};
+
+// TODO: Remove in v1 if it ever gets there.
+Model.RestPersistence = Model.REST;
+
+// Rails' preferred failed validation response code, assume the response
+// contains errors and replace current model errors with them.
+Model.REST.handle422 = function(xhr, textStatus, model) {
+ var data = Model.REST.parseResponseData(xhr);
+
+ if (data) {
+ model.errors.clear()
+
+ for (var attribute in data) {
+ for (var i = 0; i < data[attribute].length; i++) {
+ model.errors.add(attribute, data[attribute][i])
+ }
+ }
+ }
+};
+
+Model.REST.parseResponseData = function(xhr) {
+ try {
+ return /\S/.test(xhr.responseText) ?
+ jQuery.parseJSON(xhr.responseText) :
+ undefined;
+ } catch(e) {
+ Model.Log(e);
+ }
+};
+
+Model.UID = {
+ counter: 0,
+
+ generate: function() {
+ return [new Date().valueOf(), this.counter++].join("-")
+ },
+
+ reset: function() {
+ this.counter = 0
+ return this
+ }
+};
+
+Model.VERSION = "0.9.4";
View
22 dist/js-model-0.9.4.min.js
@@ -0,0 +1,22 @@
+/* js-model JavaScript library, version 0.9.4
+ * (c) 2010 Ben Pickles
+ *
+ * Released under MIT license.
+ */
+var Model=function(a,b,c){b=b||{};c=c||{};var d=function(e){this.attributes=jQuery.extend({},e);this.changes={};this.errors=new Model.Errors(this);this.uid=[a,Model.UID.generate()].join("-");jQuery.isFunction(this.initialize)&&this.initialize()},i=b.persistence;delete b.persistence;jQuery.extend(d,Model.Callbacks,Model.ClassMethods,b,{_name:a,collection:[],chain:function(e){return jQuery.extend({},this,{collection:e})}});if(i)d.persistence=i(d);jQuery.extend(d.prototype,Model.Callbacks,Model.InstanceMethods,
+c);return d};Model.Callbacks={bind:function(a,b){this.callbacks=this.callbacks||{};this.callbacks[a]=this.callbacks[a]||[];this.callbacks[a].push(b);return this},trigger:function(a,b){this.callbacks=this.callbacks||{};if(a=this.callbacks[a])for(var c=0;c<a.length;c++)a[c].apply(this,b||[]);return this},unbind:function(a,b){this.callbacks=this.callbacks||{};if(b)for(var c=this.callbacks[a]||[],d=0;d<c.length;d++)c[d]===b&&this.callbacks[a].splice(d,1);else delete this.callbacks[a];return this}};
+Model.ClassMethods={unique_key:"id",add:function(){for(var a=[],b=0;b<arguments.length;b++){var c=arguments[b],d=c.id();if(jQuery.inArray(c,this.collection)===-1&&!(d&&this.find(d))){this.collection.push(c);a.push(c)}}a.length>0&&this.trigger("add",a);return this},all:function(){return this.collection.slice()},count:function(){return this.collection.length},detect:function(a){for(var b=this.all(),c,d=0,i=b.length;d<i;d++){c=b[d];if(a.call(c,d))return c}},each:function(a){for(var b=this.all(),c=0,
+d=b.length;c<d;c++)a.call(b[c],c);return this},find:function(a){return this.detect(function(){return this.id()==a})},first:function(){return this.all()[0]},load:function(a){if(this.persistence){var b=this;this.persistence.read(function(c){for(var d=0,i=c.length;d<i;d++)b.add(c[d]);a&&a.call(b,c)})}return this},last:function(){var a=this.all();return a[a.length-1]},map:function(a){for(var b=this.all(),c=[],d=0,i=b.length;d<i;d++)c.push(a.call(b[d],b[d],d));return c},pluck:function(a){for(var b=this.all(),
+c=[],d=0,i=b.length;d<i;d++)c.push(b[d].attr(a));return c},remove:function(a){for(var b,c=0,d=this.collection.length;c<d;c++)if(this.collection[c]===a){b=c;break}if(b!=undefined){this.collection.splice(b,1);this.trigger("remove",[a]);return true}else return false},reverse:function(){return this.chain(this.all().reverse())},select:function(a){for(var b=this.all(),c=[],d,i=0,e=b.length;i<e;i++){d=b[i];a.call(d,i)&&c.push(d)}return this.chain(c)},sort:function(a){return this.chain(this.all().slice().sort(a))},
+sortBy:function(a){var b=jQuery.isFunction(a),c=function(d){return a.call(d)};return this.sort(function(d,i){d=b?c(d):d.attr(a);i=b?c(i):i.attr(a);return d<i?-1:d>i?1:0})}};Model.Errors=function(a){this.errors={};this.model=a};
+Model.Errors.prototype={add:function(a,b){this.errors[a]||(this.errors[a]=[]);this.errors[a].push(b);return this},all:function(){return this.errors},clear:function(){this.errors={};return this},each:function(a){for(var b in this.errors)for(var c=0;c<this.errors[b].length;c++)a.call(this,b,this.errors[b][c]);return this},on:function(a){return this.errors[a]||[]},size:function(){var a=0;this.each(function(){a++});return a}};
+Model.InstanceMethods={attr:function(a,b){if(arguments.length===0)return jQuery.extend({},this.attributes,this.changes);else if(arguments.length===2){if(this.attributes[a]===b)delete this.changes[a];else this.changes[a]=b;return this}else if(typeof a==="object"){for(var c in a)this.attr(c,a[c]);return this}else return a in this.changes?this.changes[a]:this.attributes[a]},callPersistMethod:function(a,b){var c=this,d=function(){if(a==="create")c.constructor.add(c);else a==="destroy"&&c.constructor.remove(c)},
+i=function(e){if(e){c.merge(c.changes).reset();d();c.trigger(a)}var g;if(b)g=b.apply(c,arguments);return g};this.constructor.persistence?this.constructor.persistence[a](this,i):i.call(this,true)},destroy:function(a){this.callPersistMethod("destroy",a);return this},id:function(){return this.attributes[this.constructor.unique_key]},merge:function(a){jQuery.extend(this.attributes,a);return this},newRecord:function(){return this.id()===undefined},reset:function(){this.errors.clear();this.changes={};return this},
+save:function(a){if(this.valid())this.callPersistMethod(this.newRecord()?"create":"update",a);else a&&a(false);return this},valid:function(){this.errors.clear();this.validate();return this.errors.size()===0},validate:function(){return this}};
+Model.localStorage=function(){if(!window.localStorage)return function(){return{create:function(a,b){b(true)},destroy:function(a,b){b(true)},read:function(a){a([])},update:function(a,b){b(true)}}};return function(a){var b=[a._name,"collection"].join("-"),c=function(){var f=localStorage[b];return f?JSON.parse(f):[]},d=function(f){localStorage.setItem(b,JSON.stringify(f))},i=function(f){var h=c();if(jQuery.inArray(f,h)===-1){h.push(f);d(h)}},e=function(f){var h=c();f=jQuery.inArray(f,h);if(f>-1){h.splice(f,
+1);d(h)}},g=function(f){var h=f.uid;f=JSON.stringify(f.attr());localStorage.setItem(h,f);i(h)};return{create:function(f,h){g(f);h(true)},destroy:function(f,h){localStorage.removeItem(f.uid);e(f.uid);h(true)},read:function(f){if(!f)return false;for(var h=a.map(function(){return this.uid}),j=c(),l=[],k,m,n=0,o=j.length;n<o;n++){m=j[n];if(jQuery.inArray(m,h)==-1){k=JSON.parse(localStorage[m]);k=new a(k);k.uid=m;l.push(k)}}f(l)},update:function(f,h){g(f);h(true)}}}};
+Model.Log=function(){window.console&&window.console.log.apply(window.console,arguments)};
+Model.REST=function(a,b){var c=/:([\w\d]+)/g,d=function(){for(var e=[],g;(g=c.exec(a))!==null;)e.push(g[1]);return e}(),i=jQuery.extend({path:function(e){var g=a;$.each(d,function(f,h){g=g.replace(":"+h,e.attributes[h])});return g},create:function(e,g){return this.xhr("POST",this.create_path(e),e,g)},create_path:function(e){return this.path(e)},destroy:function(e,g){return this.xhr("DELETE",this.destroy_path(e),e,g)},destroy_path:function(e){return this.update_path(e)},params:function(e){var g;if(e){var f=
+e.attr();delete f[e.constructor.unique_key];g={};g[e.constructor._name.toLowerCase()]=f}else g=null;if(jQuery.ajaxSettings.data)g=jQuery.extend({},jQuery.ajaxSettings.data,g);return JSON.stringify(g)},read:function(e){var g=this.klass;return this.xhr("GET",this.read_path(),null,function(f,h,j){j=jQuery.makeArray(j);f=[];h=0;for(var l=j.length;h<l;h++)f.push(new g(j[h]));e(f)})},read_path:function(){return a},update:function(e,g){return this.xhr("PUT",this.update_path(e),e,g)},update_path:function(e){return[this.path(e),
+e.id()].join("/")},xhr:function(e,g,f,h){var j=this,l=e=="GET"?undefined:this.params(f);return jQuery.ajax({type:e,url:g,contentType:"application/json",dataType:"json",data:l,dataFilter:function(k){return/\S/.test(k)?k:undefined},complete:function(k,m){j.xhrComplete(k,m,f,h)}})},xhrComplete:function(e,g,f,h){var j=Model.REST["handle"+e.status];j&&j.call(this,e,g,f);g=g==="success";j=Model.REST.parseResponseData(e);g&&f&&j&&f.attr(j);h&&h.call(f,g,e,j)}},b);return function(e){i.klass=e;return i}};
+Model.RestPersistence=Model.REST;Model.REST.handle422=function(a,b,c){if(a=Model.REST.parseResponseData(a)){c.errors.clear();for(var d in a)for(b=0;b<a[d].length;b++)c.errors.add(d,a[d][b])}};Model.REST.parseResponseData=function(a){try{return/\S/.test(a.responseText)?jQuery.parseJSON(a.responseText):undefined}catch(b){Model.Log(b)}};Model.UID={counter:0,generate:function(){return[(new Date).valueOf(),this.counter++].join("-")},reset:function(){this.counter=0;return this}};Model.VERSION="0.9.4";
View
25 index.html
@@ -16,18 +16,16 @@
</script>
</head>
<body>
- <h1>js-model (0.9.3)</h1>
+ <h1>js-model (0.9.4)</h1>
<p class="intro">js-model is a library built on top of <a href="http://jquery.com/">jQuery</a> that allows you to work with models in your JavaScript.</p>
<dl>
<dt>Download</dt>
<dd>
-<a href="dist/js-model-0.9.3.min.js">js-model-0.9.3.min.js</a> (7.6Kb)</dd>
+<a href="dist/js-model-0.9.4.min.js">js-model-0.9.4.min.js</a> (7.7Kb)</dd>
<dd>
-<a href="dist/js-model-0.9.3.js">js-model-0.9.3.js</a> (16.0Kb)</dd>
- <dd>Also available on <a href="http://cachedcommons.org/">Cached Commons</a>
-</dd>
+<a href="dist/js-model-0.9.4.js">js-model-0.9.4.js</a> (16.2Kb)</dd>
<dt>Source</dt>
<dd><a href="http://github.com/benpickles/js-model">github.com/benpickles/js-model</a></dd>
@@ -77,6 +75,7 @@
<li><a href="#first">first()</a></li>
<li><a href="#last">last()</a></li>
<li><a href="#load">load()</a></li>
+<li><a href="#map">map()</a></li>
<li><a href="#new">new()</a></li>
<li><a href="#pluck">pluck()</a></li>
<li><a href="#remove">remove()</a></li>
@@ -484,6 +483,22 @@ <h4 id="load"><code>load(callback)</code></h4>
<span class="c1">// do something...</span>
<span class="p">})</span></code></pre>
+<h4 id="map"><code>map(func)</code></h4>
+
+<p>Operates on the collection returning an array of values by calling the specified method on each instance.</p>
+
+<pre><code><span class="nx">Food</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s2">"name"</span><span class="p">).</span><span class="nx">toUpperCase</span><span class="p">()</span>
+<span class="p">})</span>
+<span class="c1">// =&gt; ["EGG", "HAM", "CHEESE"]</span>
+
+<span class="nx">Food</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s2">"name"</span><span class="p">).</span><span class="nx">indexOf</span><span class="p">(</span><span class="s2">"e"</span><span class="p">)</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span>
+<span class="p">}).</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
+ <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">attr</span><span class="p">(</span><span class="s2">"name"</span><span class="p">).</span><span class="nx">toUpperCase</span><span class="p">()</span>
+<span class="p">})</span>
+<span class="c1">// =&gt; ["EGG", "CHEESE"]</span></code></pre>
+
<h4 id="new"><code>new(attributes)</code></h4>
<p>Instantiates a model, the supplied attributes get assigned directly to the model&rsquo;s <a href="#attributes"><code>attributes</code></a>. Custom initialization behaviour can be added by defining an <a href="#initialize"><code>initialize()</code></a> instance method.</p>
Please sign in to comment.
Something went wrong with that request. Please try again.