steal('can/util', 'can/util/object/isplain', function(can){
var mapHelpers = {
Helper functions that are primarily used to serialize
a map, or track the maps created from plain JavaScript objects.
can.Map
handles cycles in objects nicely!
steal('can/util', 'can/util/object/isplain', function(can){
var mapHelpers = {
attrParts: function (attr, keepKey) {
Keep key intact
if (keepKey ) {
return [attr];
}
Split key on ‘.’
return typeof attr === "object" ? attr : ("" + attr)
.split(".");
},
canMakeObserve: function (obj) {
return obj && !can.isPromise(obj) && (can.isArray(obj) || can.isPlainObject(obj) );
},
Serializes a Map or Map.List by recursively calling the how
method on any child objects. This is able to handle
cycles.
map
- the map or list to serialize.
how
- the method to call recursively.
where
- the target Object or Array that becomes the serialized result.
serialize: (function(){
A temporary mapping of map cids to the serialized result.
var serializeMap = null;
return function (map, how, where) {
var cid = can.cid(map),
firstSerialize = false;
If there isn’t an existing serializeMap, this means
this is the initial non-recursive call to this function.
We mark this as the first call, and then setup the serializeMap.
The serialize map is further devided into how
because
.serialize
might call .attr
.
if(!serializeMap) {
firstSerialize = true;
serializeMap = {
attr: {},
serialize: {}
};
}
serializeMap[how][cid] = where;
Go through each property.
map.each(function (val, name) {
If the value is an object
, and has an attr
or serialize
function.
var result,
isObservable = can.isMapLike(val),
serialized = isObservable && serializeMap[how][can.cid(val)];
if( serialized ) {
result = serialized;
} else {
special attr or serializer
if(map["___"+how]) {
result = map["___"+how](name, val);
} else {
result = mapHelpers.getValue(map, name, val, how);
}
}
this is probably removable
if(result !== undefined){
where[name] = result;
}
});
if(firstSerialize) {
serializeMap = null;
}
return where;
};
})(),
getValue: function(map, name, val, how){
if( can.isMapLike(val) ) {
return val[how]();
} else {
return val;
}
},
define: null,
Adds a compute so it will control the behavior of an attribute. Each computedAttrs object has:
compute
- the compute that will be read and updated.count
- the number of bindings to this individual property.
This is used to know when to bind handler
to the compute.handler
- a function that when bound to compute
forwards all
events to map
. addComputedAttr: function(map, attrName, compute){
map._computedAttrs[attrName] = {
compute: compute,
count: 0,
handler: function (ev, newVal, oldVal) {
map._triggerChange(attrName, "set", newVal, oldVal, ev.batchNum);
}
};
},
Tracks map instances created from JS objects.
This should be called whenever an instance is created for a particular object.
This may return a teardown
function that should be called after all instances
might be created.
While creating map instances from plain ole JS objects (POJOs), it’s possible that the same JS object exists as two different properties and we want only one map instance created for one JS object.
var obj = {name: "I am everywhere"}
var map = new can.Map({obj1: obj, obj2: obj});
ok( map.attr("obj1") === map.attr("obj2") )
This works by temporarily adding a cid
to any found POJO object
and storing it in a temporary Object that maps those cid
s to
the POJO and the instance created for it.
The teardown
function removes those temporary cid
s and
clears the map for memory safety.
addToMap: function addToMap(obj, instance) {
var teardown;
Setup a fresh mapping if madeMap
is missing.
if (!madeMap) {
teardown = teardownMap;
madeMap = {};
}
Record if Object has a _cid
before adding one.
var hasCid = obj._cid;
var cid = can.cid(obj);
Only update if there already isn’t one already.
if (!madeMap[cid]) {
madeMap[cid] = {
obj: obj,
instance: instance,
added: !hasCid
};
}
return teardown;
},
Returns the map instance already created for this object obj
or
undefined
if nothing has been already created.
getMapFromObject: function (obj) {
return madeMap && madeMap[obj._cid] && madeMap[obj._cid].instance;
},
twoLevelDeepExtend: function (destination, source) {
for (var prop in source) {
destination[prop] = destination[prop] || {};
can.simpleExtend(destination[prop], source[prop]);
}
}
};
A temporary map of Maps that have been made from plain JS objects.
{POJO_CID: {obj: POJO, instance: MAP, added: Boolean}}
var madeMap = null;
var teardownMap = function () {
for (var cid in madeMap) {
if (madeMap[cid].added) {
delete madeMap[cid].obj._cid;
}
}
madeMap = null;
};
return mapHelpers;
});