canjs-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +36 -0
- data/Rakefile +2 -0
- data/canjs-rails.gemspec +23 -0
- data/lib/canjs-rails.rb +1 -0
- data/lib/canjs/rails.rb +7 -0
- data/lib/canjs/rails/engine.rb +6 -0
- data/lib/canjs/rails/version.rb +6 -0
- data/vendor/assets/javascripts/can.construct.proxy.js +60 -0
- data/vendor/assets/javascripts/can.construct.super.js +44 -0
- data/vendor/assets/javascripts/can.control.plugin.js +245 -0
- data/vendor/assets/javascripts/can.control.view.js +88 -0
- data/vendor/assets/javascripts/can.fixture.js +1020 -0
- data/vendor/assets/javascripts/can.jquery.js +2995 -0
- data/vendor/assets/javascripts/can.jquery.min.js +52 -0
- data/vendor/assets/javascripts/can.observe.attributes.js +293 -0
- data/vendor/assets/javascripts/can.observe.backup.js +368 -0
- data/vendor/assets/javascripts/can.observe.delegate.js +359 -0
- data/vendor/assets/javascripts/can.observe.setter.js +58 -0
- data/vendor/assets/javascripts/can.observe.validations.js +374 -0
- data/vendor/assets/javascripts/can.view.modifiers.js +292 -0
- data/vendor/assets/javascripts/download_canjs.sh +15 -0
- metadata +108 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
(function(c,n,l){$.extend(c,jQuery,{trigger:function(a,b,d){a.trigger?a.trigger(b,d):$.event.trigger(b,d,a,!0)},addEvent:function(a,b){$([this]).bind(a,b);return this},removeEvent:function(a,b){$([this]).unbind(a,b);return this},buildFragment:function(a,b){var d=$.buildFragment([a],[b]);return d.cacheable?$.clone(d.fragment):d.fragment},$:jQuery});$.each(["bind","unbind","undelegate","delegate"],function(a,b){c[b]=function(){var a=this[b]?this:$([this]);a[b].apply(a,arguments);return this}});$.each("append filter addClass remove data get".split(" "),
|
2
|
+
function(a,b){c[b]=function(a){return a[b].apply(a,c.makeArray(arguments).slice(1))}});var pa=$.cleanData;$.cleanData=function(a){$.each(a,function(a,d){c.trigger(d,"destroyed",[],!1)});pa(a)};c.each=function(a,b,d){var c=0,e;if(a)if("number"==typeof a.length&&a.pop){a.attr&&a.attr("length");for(e=a.length;c<e&&!1!==b.call(d||a[c],a[c],c,a);c++);}else for(e in a)if(!1===b.call(d||a[c],a[e],e,a))break;return a};var qa=/==/,ra=/([A-Z]+)([A-Z][a-z])/g,sa=/([a-z\d])([A-Z])/g,ta=/([a-z\d])([A-Z])/g,P=
|
3
|
+
/\{([^\}]+)\}/g,s=/"/g,ua=/'/g;c.extend(c,{esc:function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(s,""").replace(ua,"'")},getObject:function(a,b,d){var a=a?a.split("."):[],f=a.length,e,g=0,h,i,b=c.isArray(b)?b:[b||n];if(!f)return b[0];for(;e=b[g++];){for(i=0;i<f-1&&/^f|^o/.test(typeof e);i++)e=a[i]in e?e[a[i]]:d&&(e[a[i]]={});if(/^f|^o/.test(typeof e)&&(h=a[i]in e?e[a[i]]:d&&(e[a[i]]={}),h!==l))return!1===d&&delete e[a[i]],h}},capitalize:function(a){return a.charAt(0).toUpperCase()+
|
4
|
+
a.slice(1)},underscore:function(a){return a.replace(qa,"/").replace(ra,"$1_$2").replace(sa,"$1_$2").replace(ta,"_").toLowerCase()},sub:function(a,b,d){var f=[];f.push(a.replace(P,function(a,g){var h=c.getObject(g,b,d===l?d:!d);return/^f|^o/.test(typeof h)?(f.push(h),""):""+h}));return 1>=f.length?f[0]:f},replacer:P,undHash:/_|-/});var F=0;c.Construct=function(){if(arguments.length)return c.Construct.extend.apply(c.Construct,arguments)};c.extend(c.Construct,{newInstance:function(){var a=this.instance(),
|
5
|
+
b;a.setup&&(b=a.setup.apply(a,arguments));a.init&&a.init.apply(a,b||arguments);return a},_inherit:function(a,b,d){c.extend(d||a,a||{})},_overwrite:function(a,b,d,c){a[d]=c},setup:function(a){this.defaults=c.extend(!0,{},a.defaults,this.defaults)},instance:function(){F=1;var a=new this;F=0;return a},extend:function(a,b,d){function f(){if(!F)return this.constructor!==f&&arguments.length?arguments.callee.extend.apply(arguments.callee,arguments):this.constructor.newInstance.apply(this.constructor,arguments)}
|
6
|
+
"string"!=typeof a&&(d=b,b=a,a=null);d||(d=b,b=null);var d=d||{},e=this.prototype,g,h,i,j;j=this.instance();c.Construct._inherit(d,e,j);for(g in this)this.hasOwnProperty(g)&&(f[g]=this[g]);c.Construct._inherit(b,this,f);if(a){i=a.split(".");h=i.pop();i=e=c.getObject(i.join("."),n,!0);var o=c.underscore(a.replace(/\./g,"_")),z=c.underscore(h);e[h]=f}c.extend(f,{constructor:f,prototype:j,namespace:i,shortName:h,_shortName:z,fullName:a,_fullName:o});f.prototype.constructor=f;h=[this].concat(c.makeArray(arguments));
|
7
|
+
j=f.setup.apply(f,h);f.init&&f.init.apply(f,j||h);return f}});var q=function(a){return a&&"object"===typeof a&&!(a instanceof Date)},G=function(a,b){return c.each(a,function(a){a&&a.unbind&&a.unbind("change"+b)})},H=function(a,b,d){a instanceof u?G([a],d._namespace):a=c.isArray(a)?new u.List(a):new u(a);a.bind("change"+d._namespace,function(f,e){var g=c.makeArray(arguments),f=g.shift();g[0]="*"===b?d.indexOf(a)+"."+g[0]:b+"."+g[0];f.triggeredNS=f.triggeredNS||{};f.triggeredNS[d._namespace]||(f.triggeredNS[d._namespace]=
|
8
|
+
!0,c.trigger(d,f,g),c.trigger(d,g[0],g))});return a},Q=0,v=l,R=function(){if(!v)return v=[],!0},p=function(a,b,d){if(!a._init)if(v)v.push([a,{type:b,batchNum:S},d]);else return c.trigger(a,b,d)},S=1,T=function(){var a=v.slice(0);v=l;S++;c.each(a,function(a){c.trigger.apply(c,a)})},C=function(a,b,d){a.each(function(a,e){d[e]=q(a)&&c.isFunction(a[b])?a[b]():a});return d},A=function(a){return function(){return c[a].apply(this,arguments)}},U=A("addEvent"),A=A("removeEvent"),I=function(a){return c.isArray(a)?
|
9
|
+
a:(""+a).split(".")},u=c.Construct("can.Observe",{setup:function(){c.Construct.setup.apply(this,arguments)},bind:U,unbind:A,id:"id"},{setup:function(a){this._data={};this._namespace=".observe"+ ++Q;this._init=1;this.attr(a);delete this._init},attr:function(a,b){if(~"ns".indexOf((typeof a).charAt(0))){if(b===l)return u.__reading&&u.__reading(this,a),this._get(a);this._set(a,b);return this}return this._attrs(a,b)},each:function(){return c.each.apply(l,[this.__get()].concat(c.makeArray(arguments)))},
|
10
|
+
removeAttr:function(a){var a=I(a),b=a.shift(),d=this._data[b];if(a.length)return d.removeAttr(a);delete this._data[b];b in this.constructor.prototype||delete this[b];p(this,"change",[b,"remove",l,d]);p(this,b,[l,d]);return d},_get:function(a){var a=I(a),b=this.__get(a.shift());return a.length?b?b._get(a):l:b},__get:function(a){return a?this._data[a]:this._data},_set:function(a,b){var d=I(a),c=d.shift(),e=this.__get(c);if(q(e)&&d.length)e._set(d,b);else{if(d.length)throw"can.Observe: Object does not exist";
|
11
|
+
this.__convert&&(b=this.__convert(c,b));this.__set(c,b,e)}},__set:function(a,b,c){if(b!==c){var f=this.__get().hasOwnProperty(a)?"set":"add";this.___set(a,q(b)?H(b,a,this):b);p(this,"change",[a,f,b,c]);p(this,a,[b,c]);c&&G([c],this._namespace)}},___set:function(a,b){this._data[a]=b;a in this.constructor.prototype||(this[a]=b)},bind:U,unbind:A,serialize:function(){return C(this,"serialize",{})},_attrs:function(a,b){if(a===l)return C(this,"attr",{});var a=c.extend(!0,{},a),d,f=R(),e=this,g;this.each(function(c,
|
12
|
+
d){g=a[d];g===l?b&&e.removeAttr(d):(q(c)&&q(g)?c.attr(g,b):c!=g&&e._set(d,g),delete a[d])});for(d in a)g=a[d],this._set(d,g);f&&T();return this}}),va=[].splice,J=u("can.Observe.List",{setup:function(a,b){this.length=0;this._namespace=".observe"+ ++Q;this._init=1;this.bind("change",c.proxy(this._changes,this));this.push.apply(this,c.makeArray(a||[]));c.extend(this,b);delete this._init},_changes:function(a,b,c,f,e){~b.indexOf(".")||("add"===c?(p(this,c,[f,+b]),p(this,"length",[this.length])):"remove"===
|
13
|
+
c?(p(this,c,[e,+b]),p(this,"length",[this.length])):p(this,c,[f,+b]))},__get:function(a){return a?this[a]:this},___set:function(a,b){this[a]=b;+a>=this.length&&(this.length=+a+1)},serialize:function(){return C(this,"serialize",[])},splice:function(a,b){var d=c.makeArray(arguments),f;for(f=2;f<d.length;f++){var e=d[f];q(e)&&(d[f]=H(e,"*",this))}b===l&&(b=d[1]=this.length-a);f=va.apply(this,d);0<b&&(p(this,"change",[""+a,"remove",l,f]),G(f,this._namespace));2<d.length&&p(this,"change",[""+a,"add",d.slice(2),
|
14
|
+
f]);return f},_attrs:function(a,b){if(a===l)return C(this,"attr",[]);var a=a.slice(0),c=Math.min(a.length,this.length),f=R(),e;for(e=0;e<c;e++){var g=this[e],h=a[e];q(g)&&q(h)?g.attr(h,b):g!=h&&this._set(e,h)}a.length>this.length?this.push(a.slice(this.length)):a.length<this.length&&b&&this.splice(a.length);f&&T()}});c.each({push:"length",unshift:0},function(a,b){J.prototype[b]=function(){for(var d=arguments[0]&&c.isArray(arguments[0])?arguments[0]:c.makeArray(arguments),f=a?this.length:0,e=0;e<d.length;e++){var g=
|
15
|
+
d[e];q(g)&&(d[e]=H(g,"*",this))}e=[][b].apply(this,d);(!this.comparator||!d.length)&&p(this,"change",[""+f,"add",d,l]);return e}});c.each({pop:"length",shift:0},function(a,b){J.prototype[b]=function(){var d=arguments[0]&&c.isArray(arguments[0])?arguments[0]:c.makeArray(arguments),f=a&&this.length?this.length-1:0,d=[][b].apply(this,d);p(this,"change",[""+f,"remove",l,[d]]);d&&d.unbind&&d.unbind("change"+this._namespace);return d}});J.prototype.indexOf=[].indexOf||function(a){return c.inArray(a,this)};
|
16
|
+
var wa=function(a,b,d){var f=new c.Deferred;a.then(function(){arguments[0]=b[d](arguments[0]);f.resolve.apply(f,arguments)},function(){f.rejectWith.apply(this,arguments)});return f},xa=0,V=/change.observe\d+/,W=function(a,b,c,f,e){var g;g=[a.serialize()];var h=a.constructor,i;"destroy"==b&&g.shift();"create"!==b&&g.unshift(a[a.constructor.id]);i=h[b].apply(h,g);g=i.pipe(function(c){a[e||b+"d"](c,i);return a});i.abort&&(g.abort=function(){i.abort()});return g.then(c,f)},ya={create:{url:"_shortName",
|
17
|
+
type:"post"},update:{data:function(a,b){var b=b||{},d=this.id;b[d]&&b[d]!==a&&(b["new"+c.capitalize(a)]=b[d],delete b[d]);b[d]=a;return b},type:"put"},destroy:{type:"delete",data:function(a){var b={};b[this.id]=a;return b}},findAll:{url:"_shortName"},findOne:{}},X=function(a,b){return function(d){var d=a.data?a.data.apply(this,arguments):d,f=b||this[a.url||"_url"],e=d,g=a.type||"get";if("string"==typeof f){var h=f.split(" "),f={url:h.pop()};h.length&&(f.type=h.pop())}f.data="object"==typeof e&&!c.isArray(e)?
|
18
|
+
c.extend(f.data||{},e):e;f.url=c.sub(f.url,f.data,!0);return c.ajax(c.extend({type:g||"post",dataType:"json",success:void 0,error:void 0},f))}};c.Observe("can.Model",{setup:function(a){c.Observe.apply(this,arguments);if(this!==c.Model){var b=this,d=c.proxy(this._clean,b);c.each(ya,function(f,e){c.isFunction(b[e])||(b[e]=X(f,b[e]));if(b["make"+c.capitalize(e)]){var g=b["make"+c.capitalize(e)](b[e]);c.Construct._overwrite(b,a,e,function(){this._super;this._reqs++;return g.apply(this,arguments).then(d,
|
19
|
+
d)})}});if(!b.fullName||b.fullName==a.fullName)b.fullName=b._shortName="Model"+ ++xa;this.store={};this._reqs=0;this._url=this._shortName+"/{"+this.id+"}"}},_ajax:X,_clean:function(){this._reqs--;if(!this._reqs)for(var a in this.store)this.store[a]._bindings||delete this.store[a]},models:function(a){if(a){if(a instanceof this.List)return a;var b=this,d=new (b.List||Y),f=c.isArray(a),e=a instanceof Y,e=f?a:e?a.serialize():a.data;c.each(e,function(a){d.push(b.model(a))});f||c.each(a,function(a,b){"data"!==
|
20
|
+
b&&(d[b]=a)});return d}},model:function(a){if(a){a instanceof this&&(a=a.serialize());var b=this.store[a[this.id]]?this.store[a[this.id]].attr(a):new this(a);this._reqs&&(this.store[a[this.id]]=b);return b}}},{isNew:function(){var a=this[this.constructor.id];return!(a||0===a)},save:function(a,b){return W(this,this.isNew()?"create":"update",a,b)},destroy:function(a,b){return W(this,"destroy",a,b,"destroyed")},bind:function(a){V.test(a)||(this._bindings||(this.constructor.store[this[this.constructor.id]]=
|
21
|
+
this,this._bindings=0),this._bindings++);return c.Observe.prototype.bind.apply(this,arguments)},unbind:function(a){V.test(a)||(this._bindings--,this._bindings||delete this.constructor.store[this[this.constructor.id]]);return c.Observe.prototype.unbind.apply(this,arguments)},___set:function(a,b){c.Observe.prototype.___set.call(this,a,b);a===this.constructor.id&&this._bindings&&(this.constructor.store[this[this.constructor.id]]=this)}});c.each({makeFindAll:"models",makeFindOne:"model"},function(a,b){c.Model[b]=
|
22
|
+
function(b){return function(c,e,g){return wa(b.call(this,c),this,a).then(e,g)}}});c.each(["created","updated","destroyed"],function(a){c.Model.prototype[a]=function(b){var d=this.constructor;b&&"object"==typeof b&&this.attr(b.attr?b.attr():b);c.trigger(this,a);c.trigger(this,"change",a);c.trigger(d,a,this)}});var Y=c.Observe.List("can.Model.List",{setup:function(){c.Observe.List.prototype.setup.apply(this,arguments);var a=this;this.bind("change",function(b,c){/\w+\.destroyed/.test(c)&&a.splice(a.indexOf(b.target),
|
23
|
+
1)})}}),za=/^\d+$/,Aa=/([^\[\]]+)|(\[\])/g,Ba=/([^?#]*)(#.*)?$/,Z=function(a){return decodeURIComponent(a.replace(/\+/g," "))};c.extend(c,{deparam:function(a){var b={},d;a&&Ba.test(a)&&(a=a.split("&"),c.each(a,function(a){var a=a.split("="),c=Z(a.shift()),g=Z(a.join("="));current=b;for(var a=c.match(Aa),c=0,h=a.length-1;c<h;c++)current[a[c]]||(current[a[c]]=za.test(a[c+1])||"[]"==a[c+1]?[]:{}),current=current[a[c]];d=a.pop();"[]"==d?current.push(g):current[d]=g}));return b}});var aa=/\:([\w\.]+)/g,
|
24
|
+
ba=/^(?:&[^=]+=[^&]*)+/,Ca=function(a){var b=[];c.each(a,function(a,f){b.push(("className"===f?"class":f)+'="'+("href"===f?a:c.esc(a))+'"')});return b.join(" ")},ca=function(a,b){var c=0,f=0,e={},g;for(g in a.defaults)a.defaults[g]===b[g]&&(e[g]=1,c++);for(;f<a.names.length;f++){if(!b.hasOwnProperty(a.names[f]))return-1;e[a.names[f]]||c++}return c},da=!0,K=n.location,w=c.each,t=c.extend;c.route=function(a,b){var b=b||{},d=[],f=a.replace(aa,function(c,f,h){d.push(f);return"([^\\"+(a.substr(h+c.length,
|
25
|
+
1)||"&")+"]"+(b[f]?"*":"+")+")"});c.route.routes[a]={test:RegExp("^"+f+"($|&)"),route:a,names:d,defaults:b,length:a.split("/").length};return c.route};t(c.route,{param:function(a,b){var d,f=0,e,g=a.route,h=0;delete a.route;w(a,function(){h++});w(c.route.routes,function(c){e=ca(c,a);e>f&&(d=c,f=e);if(e>=h)return!1});c.route.routes[g]&&ca(c.route.routes[g],a)===f&&(d=c.route.routes[g]);if(d){var i=t({},a),g=d.route.replace(aa,function(c,b){delete i[b];return a[b]===d.defaults[b]?"":encodeURIComponent(a[b])}),
|
26
|
+
j;w(d.defaults,function(a,b){i[b]===a&&delete i[b]});j=c.param(i);b&&c.route.attr("route",d.route);return g+(j?"&"+j:"")}return c.isEmptyObject(a)?"":"&"+c.param(a)},deparam:function(a){var b={length:-1};w(c.route.routes,function(c){c.test.test(a)&&c.length>b.length&&(b=c)});if(-1<b.length){var d=a.match(b.test),f=d.shift(),e=(f=a.substr(f.length-("&"===d[d.length-1]?1:0)))&&ba.test(f)?c.deparam(f.slice(1)):{},e=t(!0,{},b.defaults,e);w(d,function(a,c){a&&"&"!==a&&(e[b.names[c]]=decodeURIComponent(a))});
|
27
|
+
e.route=b.route;return e}"&"!==a.charAt(0)&&(a="&"+a);return ba.test(a)?c.deparam(a.slice(1)):{}},data:new c.Observe({}),routes:{},ready:function(a){!1===a&&(da=a);(!0===a||!0===da)&&ea();return c.route},url:function(a,b){b&&(a=t({},L,a));return"#!"+c.route.param(a)},link:function(a,b,d,f){return"<a "+Ca(t({href:c.route.url(b,f)},d))+">"+a+"</a>"},current:function(a){return K.hash=="#!"+c.route.param(a)}});w("bind unbind delegate undelegate attr removeAttr".split(" "),function(a){c.route[a]=function(){return c.route.data[a].apply(c.route.data,
|
28
|
+
arguments)}});var fa,L,ea=function(){var a=K.href.split(/#!?/)[1]||"";L=c.route.deparam(a);(!M||a!==ga)&&c.route.attr(L,!0)},ga,M;c.bind.call(n,"hashchange",ea);c.route.bind("change",function(){M=1;clearTimeout(fa);fa=setTimeout(function(){M=0;var a=c.route.data.serialize();K.hash="#!"+(ga=c.route.param(a,!0))},1)});c.bind.call(document,"ready",c.route.ready);(function(){var a=function(a,b,d){c.bind.call(a,b,d);return function(){c.unbind.call(a,b,d)}},b=c.isFunction,d=c.extend,f=c.each,e=[].slice,
|
29
|
+
g=/\{([^\}]+)\}/g,h=c.getObject("$.event.special")||{},i=function(a,b,d,f){c.delegate.call(a,b,d,f);return function(){c.undelegate.call(a,b,d,f)}},j=function(a,d){var f="string"==typeof d?a[d]:d;b(f)||(f=a[f]);return function(){a.called=d;return f.apply(a,[this.nodeName?c.$(this):this].concat(e.call(arguments,0)))}},o;c.Construct("can.Control",{setup:function(){c.Construct.setup.apply(this,arguments);if(this!==c.Control){var a;this.actions={};for(a in this.prototype)this._isAction(a)&&(this.actions[a]=
|
30
|
+
this._action(a))}},_isAction:function(a){var c=this.prototype[a],d=typeof c;return"constructor"!==a&&("function"==d||"string"==d&&b(this.prototype[c]))&&!(!h[a]&&!z[a]&&!/[^\w]/.test(a))},_action:function(a,b){g.lastIndex=0;if(b||!g.test(a)){var d=b?c.sub(a,[b,n]):a,f=c.isArray(d),e=(f?d[1]:d).match(/^(?:(.*?)\s)?([\w\.\:>]+)$/);return{processor:z[e[2]]||o,parts:e,delegate:f?d[0]:l}}},processors:{},defaults:{}},{setup:function(a,b){var f=this.constructor,e=f.pluginName||f._fullName;this.element=c.$(a);
|
31
|
+
e&&"can_control"!==e&&this.element.addClass(e);(e=c.data(this.element,"controls"))||c.data(this.element,"controls",e=[]);e.push(this);this.options=d({},f.defaults,b);this.on();return[this.element,this.options]},on:function(b,d,f,e){if(!b){this.off();var b=this.constructor,d=this._bindings,f=b.actions,e=this.element,g=j(this,"destroy"),h,o;for(h in f)f.hasOwnProperty(h)&&(o=f[h]||b._action(h,this.options),d.push(o.processor(o.delegate||e,o.parts[2],o.parts[1],h,this)));c.bind.call(e,"destroyed",g);
|
32
|
+
d.push(function(a){c.unbind.call(a,"destroyed",g)});return d.length}"string"==typeof b&&(e=f,f=d,d=b,b=this.element);"string"==typeof e&&(e=j(this,e));this._bindings.push(d?i(b,c.trim(d),f,e):a(b,f,e));return this._bindings.length},off:function(){var a=this.element[0];f(this._bindings||[],function(b){b(a)});this._bindings=[]},destroy:function(){var a=this.constructor,a=a.pluginName||a._fullName;this.off();a&&"can_control"!==a&&this.element.removeClass(a);a=c.data(this.element,"controls");a.splice(c.inArray(this,
|
33
|
+
a),1);c.trigger(this,"destroyed");this.element=null}});var z=c.Control.processors;o=function(b,d,f,e,g){e=j(g,e);return f?i(b,c.trim(f),d,e):a(b,d,e)};f("change click contextmenu dblclick keydown keyup keypress mousedown mousemove mouseout mouseover mouseup reset resize scroll select submit focusin focusout mouseenter mouseleave".split(" "),function(a){z[a]=o})})();c.Control.processors.route=function(a,b,d,f,e){c.route(d||"");var g,h=function(a){if(c.route.attr("route")===(d||"")&&(a.batchNum===l||
|
34
|
+
a.batchNum!==g))if(g=a.batchNum,a=c.route.attr(),delete a.route,c.isFunction(e[f]))e[f](a);else e[e[f]](a)};c.route.bind("change",h);return function(){c.route.unbind("change",h)}};var D=c.isFunction,Da=c.makeArray,ha=1,k=c.view=function(a,b,d,f){a=k.render(a,b,d,f);return c.isDeferred(a)?a.pipe(function(a){return k.frag(a)}):k.frag(a)};c.extend(k,{frag:function(a,b){return k.hookup(k.fragment(a),b)},fragment:function(a){a=c.buildFragment(a,document.body);a.childNodes.length||a.appendChild(document.createTextNode(""));
|
35
|
+
return a},toId:function(a){return c.map(a.toString().split(/\/|\./g),function(a){if(a)return a}).join("_")},hookup:function(a,b){var d=[],f,e,g,h=0;for(c.each(a.childNodes?c.makeArray(a.childNodes):a,function(a){1===a.nodeType&&(d.push(a),d.push.apply(d,c.makeArray(a.getElementsByTagName("*"))))});g=d[h++];)if(g.getAttribute&&(f=g.getAttribute("data-view-id"))&&(e=k.hookups[f]))e(g,b,f),delete k.hookups[f],g.removeAttribute("data-view-id");return a},hookups:{},hook:function(a){k.hookups[++ha]=a;return" data-view-id='"+
|
36
|
+
ha+"'"},cached:{},cache:!0,register:function(a){this.types["."+a.suffix]=a},types:{},ext:".ejs",registerScript:function(){},preload:function(){},render:function(a,b,d,f){D(d)&&(f=d,d=l);var e=Ea(b);if(e.length){var g=new c.Deferred;e.push(ia(a,!0));c.when.apply(c,e).then(function(a){var e=Da(arguments),h=e.pop();if(c.isDeferred(b))b=ja(a);else for(var l in b)c.isDeferred(b[l])&&(b[l]=ja(e.shift()));e=h(b,d);g.resolve(e);f&&f(e)});return g}var h,e=D(f),g=ia(a,e);e?(h=g,g.then(function(a){f(a(b,d))})):
|
37
|
+
g.then(function(a){h=a(b,d)});return h}});c.isDeferred=function(a){return a&&D(a.then)&&D(a.pipe)};var ka=function(a,b){if(!a.length)throw"can.view: No template or empty template:"+b;},ia=function(a,b){var d=a.match(/\.[\w\d]+$/),f,e,g,h=function(a){var a=f.renderer(g,a),b=new c.Deferred;b.resolve(a);k.cache&&(k.cached[g]=b);return b};a.match(/^#/)&&(a=a.substr(1));if(e=document.getElementById(a))d="."+e.type.match(/\/(x\-)?(.+)/)[2];!d&&!k.cached[a]&&(a+=d=k.ext);c.isArray(d)&&(d=d[0]);g=c.view.toId(a);
|
38
|
+
if(a.match(/^\/\//))var i=a.substr(2),a=!n.steal?"/"+i:steal.root.mapJoin(i);f=k.types[d];if(k.cached[g])return k.cached[g];if(e)return h(e.innerHTML);var j=new c.Deferred;c.ajax({async:b,url:a,dataType:"text",error:function(b){ka("",a);j.reject(b)},success:function(b){ka(b,a);j.resolve(f.renderer(g,b));k.cache&&(k.cached[g]=j)}});return j},Ea=function(a){var b=[];if(c.isDeferred(a))return[a];for(var d in a)c.isDeferred(a[d])&&b.push(a[d]);return b},ja=function(a){return c.isArray(a)&&"success"===
|
39
|
+
a[1]?a[0]:a};n.steal&&steal.type("view js",function(a,b){var d=c.view.types["."+a.type],f=c.view.toId(a.rootSrc);a.text="steal('"+(d.plugin||"can/view/"+a.type)+"').then(function($){can.view.preload('"+f+"',"+a.text+");\n})";b()});c.extend(c.view,{register:function(a){this.types["."+a.suffix]=a;n.steal&&steal.type(a.suffix+" view js",function(a,d){var f=c.view.types["."+a.type],e=c.view.toId(a.rootSrc+"");a.text=f.script(e,a.text);d()});c.view[a.suffix]=function(b,c){k.preload(b,a.renderer(b,c))}},
|
40
|
+
registerScript:function(a,b,c){return"can.view.preload('"+b+"',"+k.types["."+a].script(b,c)+");"},preload:function(a,b){c.view.cached[a]=(new c.Deferred).resolve(function(a,c){return b.call(a,a,c)})}});var Fa=function(a,b){var d;c.Observe&&(d=c.Observe.__reading,c.Observe.__reading=function(a,b){f.push({obj:a,attr:b})});var f=[],e=a.call(b);c.Observe&&(c.Observe.__reading=d);return{value:e,observed:f}},la=function(a,b,d){var f={},e=!0,g={value:l,teardown:function(){for(var a in f){var b=f[a];b.observe.obj.unbind(b.observe.attr,
|
41
|
+
h);delete f[a]}}},h=function(){var a=g.value,b=i();g.value=b;b!==a&&d(b,a)},i=function(){var d=Fa(a,b),g=d.observed,d=d.value;e=!e;c.each(g,function(a){f[a.obj._namespace+"|"+a.attr]?f[a.obj._namespace+"|"+a.attr].matched=e:(f[a.obj._namespace+"|"+a.attr]={matched:e,observe:a},a.obj.bind(a.attr,h))});for(var i in f)g=f[i],g.matched!==e&&(g.observe.obj.unbind(g.observe.attr,h),delete f[i]);return d};g.value=i();g.isListening=!c.isEmptyObject(f);return g};c.compute=function(a,b){if(a.isComputed)return a;
|
42
|
+
var d,f=0,e,g=!0;"function"===typeof a?e=function(c){return c===l?d?d.value:a.call(b||this):a.apply(b||this,arguments)}:(e=function(b){if(b===l)return a;var d=a;a=b;d!==b&&c.trigger(e,"change",[b,d]);return b},g=!1);e.isComputed=!0;e.bind=function(h,i){c.addEvent.apply(e,arguments);f===0&&g&&(d=la(a,b||this,function(a,b){c.trigger(e,"change",[a,b])}));f++};e.unbind=function(a,b){c.removeEvent.apply(e,arguments);f--;f===0&&g&&d.teardown()};return e};c.compute.binder=la;var Ga=function(a){eval(a)},
|
43
|
+
t=c.extend,ma=/\s*\(([\$\w]+)\)\s*->([^\n]*)/,na=/([^\s]+)=$/,Ha=/(\r|\n)+/g,Ia=/__!!__/g,Ja={"":"span",table:"tr",tr:"td",ol:"li",ul:"li",tbody:"tr",thead:"tr",tfoot:"tr"},B={"class":"className"},oa=c.each(["checked","disabled","readonly","required"],function(a){B[a]=a}),N=function(a,b,d){B[b]?a[B[b]]=-1<c.inArray(b,oa)?!0:d:a.setAttribute(b,d)},Ka=function(a){return"string"==typeof a||"number"==typeof a?c.esc(a):O(a)},O=function(a){if("string"==typeof a)return a;if(!a&&0!=a)return"";var b=a.hookup&&
|
44
|
+
function(b,c){a.hookup.call(a,b,c)}||"function"==typeof a&&a;return b?(x.push(b),""):""+a},m=function(a){if(this.constructor!=m){var b=new m(a);return function(a,c){return b.render(a,c)}}"function"==typeof a?this.template={fn:a}:(t(this,a),this.template=La(this.text,this.name))};c.EJS=m;m.prototype.render=function(a,b){a=a||{};return this.template.fn.call(a,a,new m.Helpers(a,b||{}))};t(m,{txt:function(a,b,d,f,e){var g=c.compute.binder(e,f,function(a,b){o(a,b)});if(!g.isListening)return(a||0!==d?Ka:
|
45
|
+
O)(g.value);var h,i=function(a){c.bind.call(a,"destroyed",g.teardown);h=a},j=function(a){a||(g.teardown(),c.unbind.call(h,"destroyed",g.teardown))},b=Ja[b]||"span",o;if(0==d)return"<"+b+c.view.hook(a?function(a,b){o=function(a){d.nodeValue=""+a;j(d.parentNode)};var c=b&&11===a.parentNode.nodeType?b:a.parentNode,d=document.createTextNode(g.value);c.insertBefore(d,a);c.removeChild(a);i(c)}:function(a,b){o=function(a){e[0].parentNode&&(e=d(a,e));j(e[0].parentNode)};var b=b&&11===a.parentNode.nodeType?
|
46
|
+
b:a.parentNode,d=function(a,d){var e=c.view.frag(a,b),f=c.map(e.childNodes,function(a){return a}),g=d[d.length-1];g.nextSibling?g.parentNode.insertBefore(e,g.nextSibling):g.parentNode.appendChild(e);c.remove(c.$(d));return f},e=d(g.value,[a]);i(b)})+"></"+b+">";if(1===d){var k=g.value.replace(/['"]/g,"").split("=")[0];x.push(function(a){o=function(b){var b=(b||"").replace(/['"]/g,"").split("="),d=b[0];if(d!=k&&k){var e=k;-1<c.inArray(e,oa)?a[e]=!1:a.removeAttribute(e)}d&&(N(a,d,b[1]),k=d)};i(a)});
|
47
|
+
return g.value}x.push(function(a){o=function(){N(a,d,h.render())};var b=c.$(a),e;(e=c.data(b,"hooks"))||c.data(b,"hooks",e={});var f=B[d]?a[B[d]]:a.getAttribute(d),b=f.split("__!!__"),h;e[d]?e[d].bindings.push(g):e[d]={render:function(){var a=0;return f.replace(Ia,function(){return O(h.bindings[a++].value)})},bindings:[g],batchNum:l};h=e[d];b.splice(1,0,g.value);N(a,d,b.join(""));i(a)});return"__!!__"},pending:function(){if(x.length){var a=x.slice(0);x=[];return c.view.hook(function(b){c.each(a,function(a){a(b)})})}return""}});
|
48
|
+
var Ma=RegExp("(<%%|%%>|<%==|<%=|<%#|<%|%>|<|>|\"|')","g"),y=null,E=s=null,x=[],La=function(a,b){var c=[],f=0,a=a.replace(Ha,"\n");a.replace(Ma,function(b,e,g){g>f&&c.push(a.substring(f,g));c.push(e);f=g+e.length});f<a.length&&c.push(a.substr(f));var e="",g=["var ___v1ew = [];"],h=function(a,b){g.push("___v1ew.push(",'"',a.split("\\").join("\\\\").split("\n").join("\\n").split('"').join('\\"').split("\t").join("\\t"),'"'+(b||"")+");")},i=[],j,k=null,p=!1,m="",n=[],q=0,r;for(y=s=E=null;(r=c[q++])!==
|
49
|
+
l;){if(null===k)switch(r){case "<%":case "<%=":case "<%==":p=1;case "<%#":k=r;e.length&&h(e);e="";break;case "<%%":e+="<%";break;case "<":0!==c[q].indexOf("!--")&&(y=1,p=0);e+=r;break;case ">":y=0;p?(h(e,',can.EJS.pending(),">"'),e=""):e+=r;"/"==j.substr(-1)&&(n.pop(),m=n[n.length-1]);break;case "'":case '"':y&&(s&&s===r?s=null:null===s&&(s=r,E=j));default:"<"===j&&(m=r.split(" ")[0],0===m.indexOf("/")&&n.pop()===m.substr(1)?m=n[n.length-1]||m.substr(1):n.push(m)),e+=r}else switch(r){case "%>":switch(k){case "<%":j=
|
50
|
+
--e.split("{").length- --e.split("}").length;1==j?(g.push("___v1ew.push(","can.EJS.txt(0,'"+m+"',"+(s?"'"+E.match(na)[1]+"'":y?1:0)+",this,function(){","var ___v1ew = [];",e),i.push({before:"",after:"return ___v1ew.join('')}));\n"})):(f=i.length&&-1==j?i.pop():{after:";"},f.before&&g.push(f.before),g.push(e,";",f.after));break;case "<%=":case "<%==":(j=--e.split("{").length- --e.split("}").length)&&i.push({before:"return ___v1ew.join('')",after:"}));"}),ma.test(e)&&(e=e.match(ma),e="function(__){var "+
|
51
|
+
e[1]+"=can.$(__);"+e[2]+"}"),g.push("___v1ew.push(","can.EJS.txt("+("<%="===k?1:0)+",'"+m+"',"+(s?"'"+E.match(na)[1]+"'":y?1:0)+",this,function(){ return ",e,j?"var ___v1ew = [];":"}));")}k=null;e="";break;case "<%%":e+="<%";break;default:e+=r}j=r}e.length&&h(e);g.push(";");h={out:"with(_VIEW) { with (_CONTEXT) {"+g.join("")+" return ___v1ew.join('')}}"};Ga.call(h,"this.fn = (function(_CONTEXT,_VIEW){"+h.out+"});\r\n//@ sourceURL="+b+".js");return h};m.Helpers=function(a,b){this._data=a;this._extras=
|
52
|
+
b;t(this,b)};m.Helpers.prototype={list:function(a,b){c.each(a,function(c,f){b(c,f,a)})}};c.view.register({suffix:"ejs",script:function(a,b){return"can.EJS(function(_CONTEXT,_VIEW) { "+(new m({text:b,name:a})).template.out+" })"},renderer:function(a,b){return m({text:b,name:a})}});"function"===typeof define&&define.amd?define("can",[],function(){return c}):n.can=c})(can={},this);
|
@@ -0,0 +1,293 @@
|
|
1
|
+
(function(can, window, undefined){
|
2
|
+
|
3
|
+
can.each([ can.Observe, can.Model ], function(clss){
|
4
|
+
// in some cases model might not be defined quite yet.
|
5
|
+
if(clss === undefined){
|
6
|
+
return;
|
7
|
+
}
|
8
|
+
|
9
|
+
can.extend(clss, {
|
10
|
+
/**
|
11
|
+
* @attribute can.Observe.static.attributes
|
12
|
+
* @parent can.Observe.attributes
|
13
|
+
*
|
14
|
+
* `can.Observe.attributes` is a property that contains key/value pair(s) of an attribute's name and its
|
15
|
+
* respective type for using in [can.Observe.static.convert convert] and [can.Observe.prototype.serialize serialize].
|
16
|
+
*
|
17
|
+
* var Contact = can.Observe({
|
18
|
+
* attributes : {
|
19
|
+
* birthday : 'date',
|
20
|
+
* age: 'number',
|
21
|
+
* name: 'string'
|
22
|
+
* }
|
23
|
+
* });
|
24
|
+
*
|
25
|
+
*/
|
26
|
+
attributes : {},
|
27
|
+
|
28
|
+
/**
|
29
|
+
* @attribute can.Observe.static.convert
|
30
|
+
* @parent can.Observe.attributes
|
31
|
+
*
|
32
|
+
* You often want to convert from what the observe sends you to a form more useful to JavaScript.
|
33
|
+
* For example, contacts might be returned from the server with dates that look like: "1982-10-20".
|
34
|
+
* We can observe to convert it to something closer to `new Date(1982,10,20)`.
|
35
|
+
*
|
36
|
+
* Convert comes with the following types:
|
37
|
+
*
|
38
|
+
* - __date__ Converts to a JS date. Accepts integers or strings that work with Date.parse
|
39
|
+
* - __number__ An integer or number that can be passed to parseFloat
|
40
|
+
* - __boolean__ Converts "false" to false, and puts everything else through Boolean()
|
41
|
+
*
|
42
|
+
* The following sets the birthday attribute to "date" and provides a date conversion function:
|
43
|
+
*
|
44
|
+
* var Contact = can.Observe({
|
45
|
+
* attributes : {
|
46
|
+
* birthday : 'date'
|
47
|
+
* },
|
48
|
+
* convert : {
|
49
|
+
* date : function(raw){
|
50
|
+
* if(typeof raw == 'string'){
|
51
|
+
* //- Extracts dates formated 'YYYY-DD-MM'
|
52
|
+
* var matches = raw.match(/(\d+)-(\d+)-(\d+)/);
|
53
|
+
*
|
54
|
+
* //- Parses to date object and returns
|
55
|
+
* return new Date(matches[1],
|
56
|
+
* (+matches[2])-1,
|
57
|
+
* matches[3]);
|
58
|
+
*
|
59
|
+
* }else if(raw instanceof Date){
|
60
|
+
* return raw;
|
61
|
+
* }
|
62
|
+
* }
|
63
|
+
* }
|
64
|
+
* },{});
|
65
|
+
*
|
66
|
+
* var contact = new Contact();
|
67
|
+
*
|
68
|
+
* //- calls convert on attribute set
|
69
|
+
* contact.attr('birthday', '4-26-2012')
|
70
|
+
*
|
71
|
+
* contact.attr('birthday'); //-> Date
|
72
|
+
*
|
73
|
+
* ## Assocations and Convert
|
74
|
+
*
|
75
|
+
* If you have assocations defined within your model(s), you can use convert to automatically
|
76
|
+
* call seralize on those models.
|
77
|
+
*
|
78
|
+
* can.Model("Contact",{
|
79
|
+
* attributes : {
|
80
|
+
* tasks: "Task.models"
|
81
|
+
* }
|
82
|
+
* }, {});
|
83
|
+
*
|
84
|
+
* can.Model("Task",{
|
85
|
+
* attributes : {
|
86
|
+
* due : 'date'
|
87
|
+
* }
|
88
|
+
* },{});
|
89
|
+
*
|
90
|
+
* var contact = new Contact({
|
91
|
+
* tasks: [ new Task({
|
92
|
+
* due: new Date()
|
93
|
+
* }) ]
|
94
|
+
* });
|
95
|
+
*
|
96
|
+
* contact.seralize();
|
97
|
+
* //-> { tasks: [ { due: 1333219754627 } ] }
|
98
|
+
*
|
99
|
+
*/
|
100
|
+
convert: {
|
101
|
+
"date": function( str ) {
|
102
|
+
var type = typeof str;
|
103
|
+
if ( type === "string" ) {
|
104
|
+
return isNaN(Date.parse(str)) ? null : Date.parse(str)
|
105
|
+
} else if ( type === 'number' ) {
|
106
|
+
return new Date(str)
|
107
|
+
} else {
|
108
|
+
return str
|
109
|
+
}
|
110
|
+
},
|
111
|
+
"number": function( val ) {
|
112
|
+
return parseFloat(val);
|
113
|
+
},
|
114
|
+
"boolean": function( val ) {
|
115
|
+
return Boolean(val === "false" ? 0 : val);
|
116
|
+
},
|
117
|
+
"default": function( val, error, type ) {
|
118
|
+
var construct = can.getObject(type),
|
119
|
+
context = window,
|
120
|
+
realType;
|
121
|
+
// if type has a . we need to look it up
|
122
|
+
if ( type.indexOf(".") >= 0 ) {
|
123
|
+
// get everything before the last .
|
124
|
+
realType = type.substring(0, type.lastIndexOf("."));
|
125
|
+
// get the object before the last .
|
126
|
+
context = can.getObject(realType);
|
127
|
+
}
|
128
|
+
return typeof construct == "function" ? construct.call(context, val) : val;
|
129
|
+
}
|
130
|
+
},
|
131
|
+
/**
|
132
|
+
* @attribute can.Observe.static.serialize
|
133
|
+
* @parent can.Observe.attributes
|
134
|
+
*
|
135
|
+
* `can.Observe.static.seralize` is object of name-function pairs that are used to
|
136
|
+
* serialize attributes.
|
137
|
+
*
|
138
|
+
* Similar to [can.Observe.convert], in that the keys of this object correspond to
|
139
|
+
* the types specified in [can.Observe.attributes].
|
140
|
+
*
|
141
|
+
* By default every attribute will be passed through the 'default' serialization method
|
142
|
+
* that will return the value if the property holds a primitive value (string, number, ...),
|
143
|
+
* or it will call the "serialize" method if the property holds an object with the "serialize" method set.
|
144
|
+
*
|
145
|
+
* For example, to serialize all dates to ISO format:
|
146
|
+
*
|
147
|
+
* var Contact = can.Observe({
|
148
|
+
* attributes : {
|
149
|
+
* birthday : 'date'
|
150
|
+
* },
|
151
|
+
* serialize : {
|
152
|
+
* date : function(val, type){
|
153
|
+
* return new Date(val).toISOString();
|
154
|
+
* }
|
155
|
+
* }
|
156
|
+
* },{});
|
157
|
+
*
|
158
|
+
* var contact = new Contact({
|
159
|
+
* birthday: new Date("Oct 25, 1973")
|
160
|
+
* }).serialize();
|
161
|
+
* //-> { "birthday" : "1973-10-25T05:00:00.000Z" }
|
162
|
+
*
|
163
|
+
*/
|
164
|
+
serialize: {
|
165
|
+
"default": function( val, type ) {
|
166
|
+
return isObject(val) && val.serialize ? val.serialize() : val;
|
167
|
+
},
|
168
|
+
"date": function( val ) {
|
169
|
+
return val && val.getTime()
|
170
|
+
}
|
171
|
+
}
|
172
|
+
});
|
173
|
+
|
174
|
+
// overwrite setup to do this stuff
|
175
|
+
var oldSetup = clss.setup;
|
176
|
+
|
177
|
+
/**
|
178
|
+
* @hide
|
179
|
+
* @attribute can.Observe.static.setup
|
180
|
+
* @parent can.Observe.attributes
|
181
|
+
*
|
182
|
+
* `can.Observe.static.setup` overrides default `can.Observe` setup to provide
|
183
|
+
* functionality for attributes.
|
184
|
+
*
|
185
|
+
*/
|
186
|
+
clss.setup = function(superClass, stat, proto){
|
187
|
+
var self = this;
|
188
|
+
oldSetup.call(self, superClass, stat, proto);
|
189
|
+
|
190
|
+
can.each(["attributes", "validations"], function( name ) {
|
191
|
+
if (!self[name] || superClass[name] === self[name] ) {
|
192
|
+
self[name] = {};
|
193
|
+
}
|
194
|
+
});
|
195
|
+
|
196
|
+
can.each(["convert", "serialize"], function( name ) {
|
197
|
+
if ( superClass[name] != self[name] ) {
|
198
|
+
self[name] = can.extend({}, superClass[name], self[name]);
|
199
|
+
}
|
200
|
+
});
|
201
|
+
};
|
202
|
+
});
|
203
|
+
|
204
|
+
/**
|
205
|
+
* @hide
|
206
|
+
* @function can.Observe.prototype.convert
|
207
|
+
* @parent can.Observe.attributes
|
208
|
+
*/
|
209
|
+
can.Observe.prototype.__convert = function(prop, value){
|
210
|
+
// check if there is a
|
211
|
+
|
212
|
+
var Class = this.constructor,
|
213
|
+
val, type, converter;
|
214
|
+
|
215
|
+
if(Class.attributes){
|
216
|
+
// the type of the attribute
|
217
|
+
type = Class.attributes[prop];
|
218
|
+
converter = Class.convert[type] || Class.convert['default'];
|
219
|
+
}
|
220
|
+
|
221
|
+
return value === null || !type ?
|
222
|
+
// just use the value
|
223
|
+
value :
|
224
|
+
// otherwise, pass to the converter
|
225
|
+
converter.call(Class, value, function() {}, type);
|
226
|
+
};
|
227
|
+
|
228
|
+
/**
|
229
|
+
* @function can.Observe.prototype.serialize
|
230
|
+
* @parent can.Observe.attributes
|
231
|
+
*
|
232
|
+
* `can.Observe.prototype.serialize` serializes an object for the object.
|
233
|
+
* Serialized data is typically used to send back to a server.
|
234
|
+
*
|
235
|
+
* You can set the serialization methods similar to the convert methods:
|
236
|
+
*
|
237
|
+
* var Contact = can.Observe({
|
238
|
+
* attributes : {
|
239
|
+
* birthday : 'date'
|
240
|
+
* },
|
241
|
+
* serialize : {
|
242
|
+
* date : function( val, type ){
|
243
|
+
* return val.getYear() +
|
244
|
+
* "-" + (val.getMonth() + 1) +
|
245
|
+
* "-" + val.getDate();
|
246
|
+
* }
|
247
|
+
* }
|
248
|
+
* },{})
|
249
|
+
*
|
250
|
+
* var contact = new Contact();
|
251
|
+
* contact.attr('birthday', new Date());
|
252
|
+
* contact.serialize()
|
253
|
+
* //-> { birthday: 'YYYY-MM-DD' }
|
254
|
+
*
|
255
|
+
* You can also get and serialize an individual property by passing the attribute
|
256
|
+
* name to the `serialize` function. Building on the above demo, we can serialize
|
257
|
+
* the `birthday` attribute only.
|
258
|
+
*
|
259
|
+
* contact.serialize('birthday') //-> 'YYYY-MM-DD'
|
260
|
+
*
|
261
|
+
* @param {Object} attrName (optional) when passed returns only that attribute name
|
262
|
+
*/
|
263
|
+
can.Observe.prototype.serialize = function(attrName){
|
264
|
+
var where = {},
|
265
|
+
Class = this.constructor,
|
266
|
+
attrs = {};
|
267
|
+
|
268
|
+
if(attrName != undefined){
|
269
|
+
attrs[attrName] = this[attrName];
|
270
|
+
} else {
|
271
|
+
attrs = this.__get();
|
272
|
+
}
|
273
|
+
|
274
|
+
can.each(attrs, function( val, name ) {
|
275
|
+
var type = Class.attributes[name],
|
276
|
+
converter= Class.serialize[type];
|
277
|
+
|
278
|
+
// if the value is an object, and has a attrs or serialize function
|
279
|
+
where[name] = val && typeof val.serialize == 'function' ?
|
280
|
+
// call attrs or serialize to get the original data back
|
281
|
+
val.serialize() :
|
282
|
+
// otherwise if we have a converter
|
283
|
+
converter ?
|
284
|
+
// use the converter
|
285
|
+
converter(val, type) :
|
286
|
+
// or return the val
|
287
|
+
val
|
288
|
+
});
|
289
|
+
|
290
|
+
return attrName != undefined ? where[attrName] : where;
|
291
|
+
};
|
292
|
+
|
293
|
+
})(this.can, this )
|
@@ -0,0 +1,368 @@
|
|
1
|
+
(function(can, window, undefined){
|
2
|
+
|
3
|
+
var isArray = can.isArray,
|
4
|
+
// essentially returns an object that has all the must have comparisons ...
|
5
|
+
// must haves, do not return true when provided undefined
|
6
|
+
cleanSet = function(obj, compares){
|
7
|
+
var copy = can.extend({}, obj);
|
8
|
+
for(var prop in copy) {
|
9
|
+
var compare = compares[prop] === undefined ? compares["*"] : compares[prop];
|
10
|
+
if( same(copy[prop], undefined, compare ) ) {
|
11
|
+
delete copy[prop]
|
12
|
+
}
|
13
|
+
}
|
14
|
+
return copy;
|
15
|
+
},
|
16
|
+
propCount = function(obj){
|
17
|
+
var count = 0;
|
18
|
+
for(var prop in obj) count++;
|
19
|
+
return count;
|
20
|
+
};
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @class can.Object
|
24
|
+
* @parent can.util
|
25
|
+
*
|
26
|
+
* Object contains several helper methods that
|
27
|
+
* help compare objects.
|
28
|
+
*
|
29
|
+
* ## same
|
30
|
+
*
|
31
|
+
* Returns true if two objects are similar.
|
32
|
+
*
|
33
|
+
* can.Object.same({foo: "bar"} , {bar: "foo"}) //-> false
|
34
|
+
*
|
35
|
+
* ## subset
|
36
|
+
*
|
37
|
+
* Returns true if an object is a set of another set.
|
38
|
+
*
|
39
|
+
* can.Object.subset({}, {foo: "bar"} ) //-> true
|
40
|
+
*
|
41
|
+
* ## subsets
|
42
|
+
*
|
43
|
+
* Returns the subsets of an object
|
44
|
+
*
|
45
|
+
* can.Object.subsets({userId: 20},
|
46
|
+
* [
|
47
|
+
* {userId: 20, limit: 30},
|
48
|
+
* {userId: 5},
|
49
|
+
* {}
|
50
|
+
* ])
|
51
|
+
* //-> [{userId: 20, limit: 30}]
|
52
|
+
*/
|
53
|
+
can.Object = {};
|
54
|
+
|
55
|
+
/**
|
56
|
+
* @function same
|
57
|
+
* Returns if two objects are the same. It takes an optional compares object that
|
58
|
+
* can be used to make comparisons.
|
59
|
+
*
|
60
|
+
* This function does not work with objects that create circular references.
|
61
|
+
*
|
62
|
+
* ## Examples
|
63
|
+
*
|
64
|
+
* can.Object.same({name: "Justin"},
|
65
|
+
* {name: "JUSTIN"}) //-> false
|
66
|
+
*
|
67
|
+
* // ignore the name property
|
68
|
+
* can.Object.same({name: "Brian"},
|
69
|
+
* {name: "JUSTIN"},
|
70
|
+
* {name: null}) //-> true
|
71
|
+
*
|
72
|
+
* // ignore case
|
73
|
+
* can.Object.same({name: "Justin"},
|
74
|
+
* {name: "JUSTIN"},
|
75
|
+
* {name: "i"}) //-> true
|
76
|
+
*
|
77
|
+
* // deep rule
|
78
|
+
* can.Object.same({ person : { name: "Justin" } },
|
79
|
+
* { person : { name: "JUSTIN" } },
|
80
|
+
* { person : { name: "i" } }) //-> true
|
81
|
+
*
|
82
|
+
* // supplied compare function
|
83
|
+
* can.Object.same({age: "Thirty"},
|
84
|
+
* {age: 30},
|
85
|
+
* {age: function( a, b ){
|
86
|
+
* if( a == "Thirty" ) {
|
87
|
+
* a = 30
|
88
|
+
* }
|
89
|
+
* if( b == "Thirty" ) {
|
90
|
+
* b = 30
|
91
|
+
* }
|
92
|
+
* return a === b;
|
93
|
+
* }}) //-> true
|
94
|
+
*
|
95
|
+
* @param {Object} a an object to compare
|
96
|
+
* @param {Object} b an object to compare
|
97
|
+
* @param {Object} [compares] an object that indicates how to
|
98
|
+
* compare specific properties.
|
99
|
+
* Typically this is a name / value pair
|
100
|
+
*
|
101
|
+
* can.Object.same({name: "Justin"},{name: "JUSTIN"},{name: "i"})
|
102
|
+
*
|
103
|
+
* There are two compare functions that you can specify with a string:
|
104
|
+
*
|
105
|
+
* - 'i' - ignores case
|
106
|
+
* - null - ignores this property
|
107
|
+
*
|
108
|
+
* @param {Object} [deep] used internally
|
109
|
+
*/
|
110
|
+
var same = can.Object.same = function(a, b, compares, aParent, bParent, deep){
|
111
|
+
var aType = typeof a,
|
112
|
+
aArray = isArray(a),
|
113
|
+
comparesType = typeof compares,
|
114
|
+
compare;
|
115
|
+
|
116
|
+
if(comparesType == 'string' || compares === null ){
|
117
|
+
compares = compareMethods[compares];
|
118
|
+
comparesType = 'function'
|
119
|
+
}
|
120
|
+
if(comparesType == 'function'){
|
121
|
+
return compares(a, b, aParent, bParent)
|
122
|
+
}
|
123
|
+
compares = compares || {};
|
124
|
+
|
125
|
+
if(a instanceof Date){
|
126
|
+
return a === b;
|
127
|
+
}
|
128
|
+
if(deep === -1){
|
129
|
+
return aType === 'object' || a === b;
|
130
|
+
}
|
131
|
+
if(aType !== typeof b || aArray !== isArray(b)){
|
132
|
+
return false;
|
133
|
+
}
|
134
|
+
if(a === b){
|
135
|
+
return true;
|
136
|
+
}
|
137
|
+
if(aArray){
|
138
|
+
if(a.length !== b.length){
|
139
|
+
return false;
|
140
|
+
}
|
141
|
+
for(var i =0; i < a.length; i ++){
|
142
|
+
compare = compares[i] === undefined ? compares["*"] : compares[i]
|
143
|
+
if(!same(a[i],b[i], a, b, compare )){
|
144
|
+
return false;
|
145
|
+
}
|
146
|
+
};
|
147
|
+
return true;
|
148
|
+
} else if(aType === "object" || aType === 'function'){
|
149
|
+
var bCopy = can.extend({}, b);
|
150
|
+
for(var prop in a){
|
151
|
+
compare = compares[prop] === undefined ? compares["*"] : compares[prop];
|
152
|
+
if(! same( a[prop], b[prop], compare , a, b, deep === false ? -1 : undefined )){
|
153
|
+
return false;
|
154
|
+
}
|
155
|
+
delete bCopy[prop];
|
156
|
+
}
|
157
|
+
// go through bCopy props ... if there is no compare .. return false
|
158
|
+
for(prop in bCopy){
|
159
|
+
if( compares[prop] === undefined ||
|
160
|
+
! same( undefined, b[prop], compares[prop] , a, b, deep === false ? -1 : undefined )){
|
161
|
+
return false;
|
162
|
+
}
|
163
|
+
}
|
164
|
+
return true;
|
165
|
+
}
|
166
|
+
return false;
|
167
|
+
};
|
168
|
+
|
169
|
+
/**
|
170
|
+
* @function subsets
|
171
|
+
* Returns the sets in 'sets' that are a subset of checkSet
|
172
|
+
* @param {Object} checkSet
|
173
|
+
* @param {Object} sets
|
174
|
+
*/
|
175
|
+
can.Object.subsets = function(checkSet, sets, compares){
|
176
|
+
var len = sets.length,
|
177
|
+
subsets = [],
|
178
|
+
checkPropCount = propCount(checkSet),
|
179
|
+
setLength;
|
180
|
+
|
181
|
+
for(var i =0; i < len; i++){
|
182
|
+
//check this subset
|
183
|
+
var set = sets[i];
|
184
|
+
if( can.Object.subset(checkSet, set, compares) ){
|
185
|
+
subsets.push(set)
|
186
|
+
}
|
187
|
+
}
|
188
|
+
return subsets;
|
189
|
+
};
|
190
|
+
/**
|
191
|
+
* @function subset
|
192
|
+
* Compares if checkSet is a subset of set
|
193
|
+
* @param {Object} checkSet
|
194
|
+
* @param {Object} set
|
195
|
+
* @param {Object} [compares]
|
196
|
+
* @param {Object} [checkPropCount]
|
197
|
+
*/
|
198
|
+
can.Object.subset = function(subset, set, compares){
|
199
|
+
// go through set {type: 'folder'} and make sure every property
|
200
|
+
// is in subset {type: 'folder', parentId :5}
|
201
|
+
// then make sure that set has fewer properties
|
202
|
+
// make sure we are only checking 'important' properties
|
203
|
+
// in subset (ones that have to have a value)
|
204
|
+
|
205
|
+
var setPropCount =0,
|
206
|
+
compares = compares || {};
|
207
|
+
|
208
|
+
for(var prop in set){
|
209
|
+
|
210
|
+
if(! same(subset[prop], set[prop], compares[prop], subset, set ) ){
|
211
|
+
return false;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
return true;
|
215
|
+
}
|
216
|
+
|
217
|
+
|
218
|
+
var compareMethods = {
|
219
|
+
"null" : function(){
|
220
|
+
return true;
|
221
|
+
},
|
222
|
+
i : function(a, b){
|
223
|
+
return (""+a).toLowerCase() == (""+b).toLowerCase()
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
|
228
|
+
;
|
229
|
+
|
230
|
+
var flatProps = function(a){
|
231
|
+
var obj = {};
|
232
|
+
for(var prop in a){
|
233
|
+
if(typeof a[prop] !== 'object' || a[prop] === null || a[prop] instanceof Date){
|
234
|
+
obj[prop] = a[prop]
|
235
|
+
}
|
236
|
+
}
|
237
|
+
return obj;
|
238
|
+
};
|
239
|
+
|
240
|
+
can.extend(can.Observe.prototype, {
|
241
|
+
|
242
|
+
/**
|
243
|
+
* @function can.Observe.prototype.backup
|
244
|
+
* @plugin can/observe/backup
|
245
|
+
* @parent can.Observe.backup
|
246
|
+
*
|
247
|
+
* `observe.backup()` backs up a [can.Observe] instance, so it can be restored later
|
248
|
+
* by calling [can.Observe.prototype.restore] or checked if it
|
249
|
+
* has changed with [can.Observe.prototype.isDirty]:
|
250
|
+
*
|
251
|
+
* var recipe = new can.Observe({
|
252
|
+
* name : 'Pancakes',
|
253
|
+
* ingredients : [{
|
254
|
+
* name : "eggs",
|
255
|
+
* amount : '1'
|
256
|
+
* }, {
|
257
|
+
* name : "flour",
|
258
|
+
* amount : '1 cup'
|
259
|
+
* }, {
|
260
|
+
* name : "milk",
|
261
|
+
* amount : '1 1/4 cup'
|
262
|
+
* }]
|
263
|
+
* });
|
264
|
+
*
|
265
|
+
* recipe.backup();
|
266
|
+
*
|
267
|
+
* @return {can.Observe} The observe instance
|
268
|
+
*/
|
269
|
+
backup: function() {
|
270
|
+
this._backupStore = this._attrs();
|
271
|
+
return this;
|
272
|
+
},
|
273
|
+
|
274
|
+
/**
|
275
|
+
* @function can.Observe.prototype.isDirty
|
276
|
+
* @plugin can/observe/backup
|
277
|
+
* @parent can.Observe.backup
|
278
|
+
*
|
279
|
+
* `observe.isDirty([checkAssociations])` returns if the observe has changed since the last
|
280
|
+
* [can.Observe.prototype.backup] call. If there is no backup it will return false. If you pass
|
281
|
+
* true, _isDirty_ also checks if any child properties or [can.Model] associations have changed.
|
282
|
+
*
|
283
|
+
* var recipe = new can.Observe({
|
284
|
+
* name : 'Pancakes',
|
285
|
+
* ingredients : [{
|
286
|
+
* name : "eggs",
|
287
|
+
* amount : '1'
|
288
|
+
* }, {
|
289
|
+
* name : "flour",
|
290
|
+
* amount : '1 cup'
|
291
|
+
* }, {
|
292
|
+
* name : "milk",
|
293
|
+
* amount : '1 1/4 cup'
|
294
|
+
* }]
|
295
|
+
* });
|
296
|
+
*
|
297
|
+
* recipe.backup();
|
298
|
+
* // Change the attribute of a nested property
|
299
|
+
* recipe.attr('ingredients.0.amount', '2');
|
300
|
+
* recipe.isDirty() // -> false
|
301
|
+
* recipe.isDirty(true) // -> true
|
302
|
+
* recipe.attr('name', 'Eggcakes');
|
303
|
+
* recipe.isDirty() // -> true
|
304
|
+
*
|
305
|
+
* @param {Boolean} [checkAssociations] Whether nested objects should be checked or
|
306
|
+
* not. Defaults to false.
|
307
|
+
* @return {Boolean} true if there are changes,
|
308
|
+
* false if not or there is no backup
|
309
|
+
*/
|
310
|
+
isDirty: function(checkAssociations) {
|
311
|
+
return this._backupStore &&
|
312
|
+
!can.Object.same(this._attrs(),
|
313
|
+
this._backupStore,
|
314
|
+
undefined,
|
315
|
+
undefined,
|
316
|
+
undefined,
|
317
|
+
!!checkAssociations);
|
318
|
+
},
|
319
|
+
|
320
|
+
/**
|
321
|
+
* @function can.Observe.prototype.restore
|
322
|
+
* @parent can.Observe.backup
|
323
|
+
*
|
324
|
+
* `observe.restore([restoreAssociations])` restores the observe to the state of the last time
|
325
|
+
* [can.Observe.prototype.backup] was called if [can.Observe.prototype.isDirty]
|
326
|
+
* returns true. If you pass true, _restore_ will also check and restore all nested properties
|
327
|
+
* and [can.Model] associations.
|
328
|
+
*
|
329
|
+
* var recipe = new can.Observe({
|
330
|
+
* name : 'Pancakes',
|
331
|
+
* ingredients : [{
|
332
|
+
* name : "eggs",
|
333
|
+
* amount : '1'
|
334
|
+
* }, {
|
335
|
+
* name : "flour",
|
336
|
+
* amount : '1 cup'
|
337
|
+
* }, {
|
338
|
+
* name : "milk",
|
339
|
+
* amount : '1 1/4 cup'
|
340
|
+
* }]});
|
341
|
+
*
|
342
|
+
* recipe.backup();
|
343
|
+
*
|
344
|
+
* // Change the attribute of a nested observe
|
345
|
+
* recipe.attr('ingredients.0.amount', '2');
|
346
|
+
* recipe.attr('name', 'Eggcakes');
|
347
|
+
* recipe.attr('name') // -> Eggcakes
|
348
|
+
* recipe.attr('ingredients.0.amount') // -> 2
|
349
|
+
* recipe.restore(true);
|
350
|
+
* recipe.attr('name') // -> Pancakes
|
351
|
+
* recipe.attr('ingredients.0.amount') // -> 1
|
352
|
+
*
|
353
|
+
* @param {Boolean} [restoreAssociations] Whether nested objects should also
|
354
|
+
* be restored or not. Defaults to false.
|
355
|
+
* @return {can.Observe} The observe instance
|
356
|
+
*/
|
357
|
+
restore: function(restoreAssociations) {
|
358
|
+
var props = restoreAssociations ? this._backupStore : flatProps(this._backupStore)
|
359
|
+
|
360
|
+
if(this.isDirty(restoreAssociations)){
|
361
|
+
this._attrs(props);
|
362
|
+
}
|
363
|
+
|
364
|
+
return this;
|
365
|
+
}
|
366
|
+
|
367
|
+
})
|
368
|
+
})(this.can, this )
|