Skip to content

Instantly share code, notes, and snippets.

@daffl
Created March 26, 2013 14:34
Show Gist options
  • Save daffl/5245799 to your computer and use it in GitHub Desktop.
Save daffl/5245799 to your computer and use it in GitHub Desktop.
Attributes plugin with recursive call fix
/*!
* CanJS - 1.1.5 (2013-03-26)
* http://canjs.us/
* Copyright (c) 2013 Bitovi
* Licensed MIT
*/
(function (can, window, undefined) {
// ## can/observe/attributes/attributes.js
can.each([can.Observe, can.Model], function (clss) {
// in some cases model might not be defined quite yet.
if (clss === undefined) {
return;
}
var isObject = function (obj) {
return typeof obj === 'object' && obj !== null && obj;
};
can.extend(clss, {
attributes: {},
convert: {
"date": function (str) {
var type = typeof str;
if (type === "string") {
return isNaN(Date.parse(str)) ? null : Date.parse(str)
} else if (type === 'number') {
return new Date(str)
} else {
return str
}
},
"number": function (val) {
return parseFloat(val);
},
"boolean": function (val) {
if (val === 'false' || val === '0' || !val) {
return false;
}
return true;
},
"default": function (val, oldVal, error, type) {
var construct = can.getObject(type),
context = window,
realType;
// if type has a . we need to look it up
if (type.indexOf(".") >= 0) {
// get everything before the last .
realType = type.substring(0, type.lastIndexOf("."));
// get the object before the last .
context = can.getObject(realType);
}
return typeof construct == "function" ? construct.call(context, val, oldVal) : val;
}
},
serialize: {
"default": function (val, type) {
return isObject(val) && val.serialize ? val.serialize() : val;
},
"date": function (val) {
return val && val.getTime()
}
}
});
// overwrite setup to do this stuff
var oldSetup = clss.setup;
clss.setup = function (superClass, stat, proto) {
var self = this;
oldSetup.call(self, superClass, stat, proto);
can.each(["attributes"], function (name) {
if (!self[name] || superClass[name] === self[name]) {
self[name] = {};
}
});
can.each(["convert", "serialize"], function (name) {
if (superClass[name] != self[name]) {
self[name] = can.extend({}, superClass[name], self[name]);
}
});
};
});
var oldSetup = can.Observe.prototype.setup;
can.Observe.prototype.setup = function (obj) {
var diff = {};
oldSetup.call(this, obj);
can.each(this.constructor.defaults, function (value, key) {
if (!this.hasOwnProperty(key)) {
diff[key] = value;
}
}, this);
this._init = 1;
this.attr(diff);
delete this._init;
};
can.Observe.prototype.__convert = function (prop, value) {
// check if there is a
var Class = this.constructor,
oldVal = this.attr(prop),
type, converter;
if (Class.attributes) {
// the type of the attribute
type = Class.attributes[prop];
converter = Class.convert[type] || Class.convert['default'];
}
return value === null || !type ?
// just use the value
value :
// otherwise, pass to the converter
converter.call(Class, value, oldVal, function () {}, type);
};
can.Observe.prototype.serialize = function (attrName, stack) {
var where = {},
Class = this.constructor,
attrs = {};
stack = can.isArray(stack) ? stack : [];
stack.push(this._cid);
if (attrName !== undefined) {
attrs[attrName] = this[attrName];
} else {
attrs = this.__get();
}
can.each(attrs, function (val, name) {
var type, converter;
if (val instanceof can.Observe) {
where[name] = stack.indexOf(val._cid) !== -1 ? val.serialize(undefined, stack) : undefined;
} else {
type = Class.attributes ? Class.attributes[name] : 0;
converter = Class.serialize ? Class.serialize[type] : 0;
// if the value is an object, and has a attrs or serialize function
where[name] = val && typeof val.serialize == 'function' ?
// call attrs or serialize to get the original data back
val.serialize() :
// otherwise if we have a converter
converter ?
// use the converter
converter(val, type) :
// or return the val
val;
}
});
return attrName != undefined ? where[attrName] : where;
};
})(can, this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment