From 83d72c27fe1118d145826ada7015c31052f0f087 Mon Sep 17 00:00:00 2001 From: dsc Date: Wed, 14 Mar 2012 01:39:36 -0700 Subject: [PATCH] Adds backbone-nested --- static/vendor/backbone.nested-1.1.0.js | 228 ++++++++++++++++++++++++++++ static/vendor/backbone.nested-1.1.0.min.js | 8 + static/vendor/backbone.nested.js | 1 + static/vendor/backbone.nested.min.js | 1 + 4 files changed, 238 insertions(+), 0 deletions(-) create mode 100644 static/vendor/backbone.nested-1.1.0.js create mode 100644 static/vendor/backbone.nested-1.1.0.min.js create mode 120000 static/vendor/backbone.nested.js create mode 120000 static/vendor/backbone.nested.min.js diff --git a/static/vendor/backbone.nested-1.1.0.js b/static/vendor/backbone.nested-1.1.0.js new file mode 100644 index 0000000..3002834 --- /dev/null +++ b/static/vendor/backbone.nested-1.1.0.js @@ -0,0 +1,228 @@ +/** + * Backbone-Nested 1.1.0 - An extension of Backbone.js that keeps track of nested attributes + * + * http://afeld.github.com/backbone-nested/ + * + * Copyright (c) 2011-2012 Aidan Feldman + * MIT Licensed (LICENSE) + */ +/*global Backbone, _, $ */ +(function(){ + 'use strict'; + + Backbone.NestedModel = Backbone.Model.extend({ + + get: function(attrStrOrPath, opts){ + opts = opts || {}; + + var attrPath = Backbone.NestedModel.attrPath(attrStrOrPath), + childAttr = attrPath[0], + result = Backbone.NestedModel.__super__.get.call(this, childAttr); + + // walk through the child attributes + for (var i = 1; i < attrPath.length; i++){ + if (!result){ + // value not present + break; + } + childAttr = attrPath[i]; + result = result[childAttr]; + } + + // check if the result is an Object, Array, etc. + if (!opts.silent && _.isObject(result) && window.console){ + window.console.log("Backbone-Nested syntax is preferred for accesing values of attribute '" + attrStrOrPath + "'."); + } + // else it's a leaf + + return result; + }, + + has: function(attr){ + // for some reason this is not how Backbone.Model is implemented - it accesses the attributes object directly + var result = this.get(attr, {silent: true}); + return !(result === null || _.isUndefined(result)); + }, + + set: function(key, value, opts){ + var attrs; + if (_.isObject(key) || key == null) { + attrs = key; + opts = value; + } else { + attrs = {}; + attrs[key] = value; + } + opts = opts || {}; + + var newAttrs = Backbone.NestedModel.deepClone(this.attributes), + attrVal, attrPath, attrObj; + + for (var attrStr in attrs){ + attrPath = Backbone.NestedModel.attrPath(attrStr); + attrObj = Backbone.NestedModel.createAttrObj(attrPath, attrs[attrStr]); + + this._mergeAttrs(newAttrs, attrObj, opts); + } + + return Backbone.NestedModel.__super__.set.call(this, newAttrs, opts); + }, + + unset: function(attrStr, opts){ + opts = _.extend({}, opts, {unset: true}); + this.set(attrStr, null, opts); + + return this; + }, + + add: function(attrStr, value, opts){ + var current = this.get(attrStr, {silent: true}); + this.set(attrStr + '[' + current.length + ']', value, opts); + }, + + remove: function(attrStr, opts){ + opts = opts || {}; + + var attrPath = Backbone.NestedModel.attrPath(attrStr), + aryPath = _.initial(attrPath), + val = this.get(aryPath, {silent: true}), + i = _.last(attrPath); + + if (!_.isArray(val)){ + throw new Error("remove() must be called on a nested array"); + } + + // only trigger if an element is actually being removed + var trigger = !opts.silent && (val.length > i + 1), + oldEl = val[i]; + + // remove the element from the array + val.splice(i, 1); + this.set(attrStr, val, opts); + + if (trigger){ + this.trigger('remove:' + Backbone.NestedModel.createAttrStr(aryPath), this, oldEl); + } + + return this; + }, + + toJSON: function(){ + var json = Backbone.NestedModel.__super__.toJSON.apply(this); + return Backbone.NestedModel.deepClone(json); + }, + + + // private + + _mergeAttrs: function(dest, source, opts, stack){ + stack = stack || []; + + _.each(source, function(sourceVal, prop){ + if (prop === '-1'){ + prop = dest.length; + } + + var destVal = dest[prop], + newStack = stack.concat([prop]), + attrStr; + + var isChildAry = _.isObject(sourceVal) && _.any(sourceVal, function(val, attr){ + return attr === '-1' || _.isNumber(attr); + }); + + if (isChildAry && !_.isArray(destVal)){ + destVal = dest[prop] = []; + } + + if (prop in dest && _.isObject(sourceVal) && _.isObject(destVal)){ + destVal = dest[prop] = this._mergeAttrs(destVal, sourceVal, opts, newStack); + } else { + var oldVal = destVal; + + destVal = dest[prop] = sourceVal; + + if (_.isArray(dest) && !opts.silent){ + attrStr = Backbone.NestedModel.createAttrStr(stack); + + if (!oldVal && destVal){ + this.trigger('add:' + attrStr, this, destVal); + } else if (oldVal && !destVal){ + this.trigger('remove:' + attrStr, this, oldVal); + } + } + } + + // let the superclass handle change events for top-level attributes + if (!opts.silent && newStack.length > 1){ + attrStr = Backbone.NestedModel.createAttrStr(newStack); + this.trigger('change:' + attrStr, this, destVal); + } + }, this); + + return dest; + } + + }, { + // class methods + + attrPath: function(attrStrOrPath){ + var path; + + if (_.isString(attrStrOrPath)){ + // change all appends to '-1' + attrStrOrPath = attrStrOrPath.replace(/\[\]/g, '[-1]'); + // TODO this parsing can probably be more efficient + path = (attrStrOrPath === '') ? [''] : attrStrOrPath.match(/[^\.\[\]]+/g); + path = _.map(path, function(val){ + // convert array accessors to numbers + return val.match(/^\d+$/) ? parseInt(val, 10) : val; + }); + } else { + path = attrStrOrPath; + } + + return path; + }, + + createAttrObj: function(attrStrOrPath, val){ + var attrPath = this.attrPath(attrStrOrPath), + newVal; + + switch (attrPath.length){ + case 0: + throw "no valid attributes: '" + attrStrOrPath + "'"; + + case 1: // leaf + newVal = val; + break; + + default: // nested attributes + var otherAttrs = _.rest(attrPath); + newVal = this.createAttrObj(otherAttrs, val); + break; + } + + var childAttr = attrPath[0], + result = _.isNumber(childAttr) ? [] : {}; + + result[childAttr] = newVal; + return result; + }, + + createAttrStr: function(attrPath){ + var attrStr = attrPath[0]; + _.each(_.rest(attrPath), function(attr){ + attrStr += _.isNumber(attr) ? ('[' + attr + ']') : ('.' + attr); + }); + + return attrStr; + }, + + deepClone: function(obj){ + return $.extend(true, {}, obj); + } + + }); + +})(); diff --git a/static/vendor/backbone.nested-1.1.0.min.js b/static/vendor/backbone.nested-1.1.0.min.js new file mode 100644 index 0000000..c1f8a53 --- /dev/null +++ b/static/vendor/backbone.nested-1.1.0.min.js @@ -0,0 +1,8 @@ +/** + * Backbone-Nested 1.1.0 - An extension of Backbone.js that keeps track of nested attributes + * + * http://afeld.github.com/backbone-nested/ + * + * Copyright (c) 2011-2012 Aidan Feldman + * MIT Licensed (LICENSE) + *//*global Backbone, _, $ */(function(){"use strict",Backbone.NestedModel=Backbone.Model.extend({get:function(a,b){b=b||{};var c=Backbone.NestedModel.attrPath(a),d=c[0],e=Backbone.NestedModel.__super__.get.call(this,d);for(var f=1;ff+1,h=e[f];return e.splice(f,1),this.set(a,e,b),g&&this.trigger("remove:"+Backbone.NestedModel.createAttrStr(d),this,h),this},toJSON:function(){var a=Backbone.NestedModel.__super__.toJSON.apply(this);return Backbone.NestedModel.deepClone(a)},_mergeAttrs:function(a,b,c,d){return d=d||[],_.each(b,function(b,e){e==="-1"&&(e=a.length);var f=a[e],g=d.concat([e]),h,i=_.isObject(b)&&_.any(b,function(a,b){return b==="-1"||_.isNumber(b)});i&&!_.isArray(f)&&(f=a[e]=[]);if(e in a&&_.isObject(b)&&_.isObject(f))f=a[e]=this._mergeAttrs(f,b,c,g);else{var j=f;f=a[e]=b,_.isArray(a)&&!c.silent&&(h=Backbone.NestedModel.createAttrStr(d),!j&&f?this.trigger("add:"+h,this,f):j&&!f&&this.trigger("remove:"+h,this,j))}!c.silent&&g.length>1&&(h=Backbone.NestedModel.createAttrStr(g),this.trigger("change:"+h,this,f))},this),a}},{attrPath:function(a){var b;return _.isString(a)?(a=a.replace(/\[\]/g,"[-1]"),b=a===""?[""]:a.match(/[^\.\[\]]+/g),b=_.map(b,function(a){return a.match(/^\d+$/)?parseInt(a,10):a})):b=a,b},createAttrObj:function(a,b){var c=this.attrPath(a),d;switch(c.length){case 0:throw"no valid attributes: '"+a+"'";case 1:d=b;break;default:var e=_.rest(c);d=this.createAttrObj(e,b)}var f=c[0],g=_.isNumber(f)?[]:{};return g[f]=d,g},createAttrStr:function(a){var b=a[0];return _.each(_.rest(a),function(a){b+=_.isNumber(a)?"["+a+"]":"."+a}),b},deepClone:function(a){return $.extend(!0,{},a)}})})(); \ No newline at end of file diff --git a/static/vendor/backbone.nested.js b/static/vendor/backbone.nested.js new file mode 120000 index 0000000..84461d0 --- /dev/null +++ b/static/vendor/backbone.nested.js @@ -0,0 +1 @@ +backbone.nested-1.1.0.js \ No newline at end of file diff --git a/static/vendor/backbone.nested.min.js b/static/vendor/backbone.nested.min.js new file mode 120000 index 0000000..0437046 --- /dev/null +++ b/static/vendor/backbone.nested.min.js @@ -0,0 +1 @@ +backbone.nested-1.1.0.min.js \ No newline at end of file -- 1.7.0.4