From: dsc Date: Wed, 14 Mar 2012 08:39:36 +0000 (-0700) Subject: Adds backbone-nested X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=c080d5ecb859b2aba383c3aa9fff88d3cbc871e7;p=limn-bak.git Adds backbone-nested --- 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 diff --git a/static/vendor/require.min.js b/static/vendor/require.min.js index 3a47453..7d397bd 100644 --- a/static/vendor/require.min.js +++ b/static/vendor/require.min.js @@ -1 +1 @@ -require=function(){function d(){}function c(d){if(b.hasOwnProperty(d))return b[d];var e=arguments.callee.caller;if(!a.hasOwnProperty(d))throw new Error('No such module "'+d+'"'+(e?" (requested by "+(e.name||e.displayName||e)+")":"")+"!");var f=a[d],g=f.exports=b[d]={};f.setup.call(g,c,g,f),g=b[d]=f.exports;return g}var a=c.modules={},b=c.cache={};c.install=function(b,c,e){if(!b)throw new Error("No module ID specified: id="+b+"!");if(a.hasOwnProperty(b))throw new Error('Module "'+b+'" already exists!');c instanceof Function&&(e=c,c={id:b}),c.id=b,c.setup=e||d,a[b]=c;return c};return c}() \ No newline at end of file +require=function(){var a=this,b=a.require,c=a.require||{};b=function(a,b){var c=require.resolve?require.resolve(a,b||"/"):a,d=require.modules[c];if(!d)throw new Error("Failed to resolve module "+a+", tried "+c);d.exports||d();var e=(d.module||{}).exports||d._cached||d.exports;return e};for(var d in c)b[d]=c[d];return b.modules=c.modules||{},b.cache=c.cache||{},b}(),require.install=function(a,b,c){if(!a)throw new Error("No module ID specified: id="+a+"!");a=""+a;if(require.modules.hasOwnProperty(a))throw new Error('Module "'+a+'" already exists!');typeof b=="function"&&(c=b),b={id:a};var d=b.exports={},e;return e=function(){return e.exports=d,c&&c.call(d,require,d,b),e.exports=e._cached=require.cache[a]=b.exports,b.exports},e.id=a,e.module=b,e.setup=c,require.modules[a]=e,b}; \ No newline at end of file