UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

348 lines (347 loc) 12.1 kB
/*! * CanJS - 2.3.34 * http://canjs.com/ * Copyright (c) 2018 Bitovi * Mon, 30 Apr 2018 20:56:51 GMT * Licensed MIT */ /*can@2.3.34#map/map*/ var can = require('../util/util.js'); var bind = require('../util/bind/bind.js'); var bubble = require('./bubble.js'); var mapHelpers = require('./map_helpers.js'); require('../construct/construct.js'); require('../util/batch/batch.js'); require('../compute/get_value_and_bind.js'); var unobservable = { 'constructor': true }; var Map = can.Map = can.Construct.extend({ setup: function (baseMap) { can.Construct.setup.apply(this, arguments); this._computedPropertyNames = []; if (can.Map) { if (!this.defaults) { this.defaults = {}; } for (var prop in this.prototype) { if (prop !== 'define' && prop !== 'constructor' && (typeof this.prototype[prop] !== 'function' || this.prototype[prop].prototype instanceof can.Construct)) { this.defaults[prop] = this.prototype[prop]; } else if (this.prototype[prop].isComputed) { this._computedPropertyNames.push(prop); } } if (mapHelpers.define) { mapHelpers.define(this, baseMap.prototype.define); } } if (can.List && !(this.prototype instanceof can.List)) { this.List = Map.List.extend({ Map: this }, {}); } }, shortName: 'Map', _bubbleRule: function (eventName) { return eventName === 'change' || eventName.indexOf('.') >= 0 ? ['change'] : []; }, bind: can.bindAndSetup, unbind: can.unbindAndTeardown, id: 'id', keys: function (map) { var keys = []; can.__observe(map, '__keys'); for (var keyName in map._data) { keys.push(keyName); } return keys; } }, { setup: function (obj) { if (obj instanceof can.Map) { obj = obj.serialize(); } this._data = {}; can.cid(this, '.map'); this._setupComputedProperties(); var teardownMapping = obj && mapHelpers.addToMap(obj, this); var defaultValues = this._setupDefaults(obj); var data = can.extend(can.extend(true, {}, defaultValues), obj); this.attr(data); if (teardownMapping) { teardownMapping(); } }, _setupComputedProperties: function () { this._computedAttrs = {}; var computes = this.constructor._computedPropertyNames; for (var i = 0, len = computes.length; i < len; i++) { var attrName = computes[i]; mapHelpers.addComputedAttr(this, attrName, this[attrName].clone(this)); } }, _setupDefaults: function () { return this.constructor.defaults || {}; }, attr: function (attr, val) { var type = typeof attr; if (attr === undefined) { return this._getAttrs(); } else if (type !== 'string' && type !== 'number') { return this._setAttrs(attr, val); } else if (arguments.length === 1) { return this._get(attr + ''); } else { this._set(attr + '', val); return this; } }, _get: function (attr) { var dotIndex = attr.indexOf('.'); if (dotIndex >= 0) { var value = this.___get(attr); if (value !== undefined) { can.__observe(this, attr); return value; } var first = attr.substr(0, dotIndex), second = attr.substr(dotIndex + 1); var current = this.__get(first); return current && current._get ? current._get(second) : undefined; } else { return this.__get(attr); } }, __get: function (attr) { if (!unobservable[attr] && !this._computedAttrs[attr]) { can.__observe(this, attr); } return this.___get(attr); }, ___get: function (attr) { if (attr !== undefined) { var computedAttr = this._computedAttrs[attr]; if (computedAttr && computedAttr.compute) { return computedAttr.compute(); } else { return this._data.hasOwnProperty(attr) ? this._data[attr] : undefined; } } else { return this._data; } }, _set: function (attr, value, keepKey) { var dotIndex = attr.indexOf('.'), current; if (dotIndex >= 0 && !keepKey) { var first = attr.substr(0, dotIndex), second = attr.substr(dotIndex + 1); current = this.__inSetup ? undefined : this.___get(first); if (can.isMapLike(current)) { current._set(second, value); } else { throw new Error('can.Map: Object does not exist'); } } else { current = this.__inSetup ? undefined : this.___get(attr); if (this.__convert) { value = this.__convert(attr, value); } this.__set(attr, this.__type(value, attr), current); } }, __type: function (value, prop) { if (typeof value === 'object' && !(value instanceof can.Map) && mapHelpers.canMakeObserve(value)) { var cached = mapHelpers.getMapFromObject(value); if (cached) { return cached; } if (can.isArray(value)) { var List = can.List; return new List(value); } else { var Map = this.constructor.Map || can.Map; return new Map(value); } } return value; }, __set: function (prop, value, current) { if (value !== current) { var computedAttr = this._computedAttrs[prop]; var changeType = computedAttr || current !== undefined || this.___get().hasOwnProperty(prop) ? 'set' : 'add'; this.___set(prop, typeof value === 'object' ? bubble.set(this, prop, value, current) : value); if (!computedAttr || !computedAttr.count) { this._triggerChange(prop, changeType, value, current); } if (typeof current === 'object') { bubble.teardownFromParent(this, current); } } }, ___set: function (prop, val) { var computedAttr = this._computedAttrs[prop]; if (computedAttr && computedAttr.compute) { computedAttr.compute(val); } else { this._data[prop] = val; } if (typeof this.constructor.prototype[prop] !== 'function' && !computedAttr) { this[prop] = val; } }, removeAttr: function (attr) { return this._remove(attr); }, _remove: function (attr) { var parts = mapHelpers.attrParts(attr), prop = parts.shift(), current = this.___get(prop); if (parts.length && current) { return current.removeAttr(parts); } else { if (typeof attr === 'string' && !!~attr.indexOf('.')) { prop = attr; } this.__remove(prop, current); return current; } }, __remove: function (prop, current) { if (prop in this._data) { this.___remove(prop); this._triggerChange(prop, 'remove', undefined, current); } }, ___remove: function (prop) { delete this._data[prop]; if (!(prop in this.constructor.prototype)) { delete this[prop]; } }, ___serialize: function (name, val) { return mapHelpers.getValue(this, name, val, 'serialize'); }, _getAttrs: function () { return mapHelpers.serialize(this, 'attr', {}); }, _setAttrs: function (props, remove) { props = can.simpleExtend({}, props); var prop, self = this, newVal; can.batch.start(); this._each(function (curVal, prop) { if (prop === '_cid') { return; } newVal = props[prop]; if (newVal === undefined) { if (remove) { self.removeAttr(prop); } return; } if (self.__convert) { newVal = self.__convert(prop, newVal); } if (can.isMapLike(curVal) && mapHelpers.canMakeObserve(newVal)) { curVal.attr(newVal, remove); } else if (curVal !== newVal) { self.__set(prop, self.__type(newVal, prop), curVal); } delete props[prop]; }); for (prop in props) { if (prop !== '_cid') { newVal = props[prop]; this._set(prop, newVal, true); } } can.batch.stop(); return this; }, serialize: function () { return mapHelpers.serialize(this, 'serialize', {}); }, _triggerChange: function (attr, how, newVal, oldVal, batchNum) { if (bubble.isBubbling(this, 'change')) { can.batch.trigger(this, { type: 'change', target: this, batchNum: batchNum }, [ attr, how, newVal, oldVal ]); } can.batch.trigger(this, { type: attr, target: this, batchNum: batchNum }, [ newVal, oldVal ]); if (how === 'remove' || how === 'add') { can.batch.trigger(this, { type: '__keys', target: this, batchNum: batchNum }); } }, _bindsetup: function () { }, _bindteardown: function () { }, one: can.one, bind: function (eventName, handler) { var computedBinding = this._computedAttrs && this._computedAttrs[eventName]; if (computedBinding && computedBinding.compute) { if (!computedBinding.count) { computedBinding.count = 1; computedBinding.compute.bind('change', computedBinding.handler); } else { computedBinding.count++; } } bubble.bind(this, eventName); return can.bindAndSetup.apply(this, arguments); }, unbind: function (eventName, handler) { var computedBinding = this._computedAttrs && this._computedAttrs[eventName]; if (computedBinding) { if (computedBinding.count === 1) { computedBinding.count = 0; computedBinding.compute.unbind('change', computedBinding.handler); } else { computedBinding.count--; } } bubble.unbind(this, eventName); return can.unbindAndTeardown.apply(this, arguments); }, compute: function (prop) { if (can.isFunction(this.constructor.prototype[prop])) { return can.compute(this[prop], this); } else { var reads = can.compute.read.reads(prop), last = reads.length - 1; return can.compute(function (newVal) { if (arguments.length) { can.compute.read(this, reads.slice(0, last)).value.attr(reads[last].key, newVal); } else { return can.compute.read(this, reads, { args: [] }).value; } }, this); } }, each: function () { return can.each.apply(undefined, [this].concat(can.makeArray(arguments))); }, _each: function (callback) { var data = this.___get(); for (var prop in data) { if (data.hasOwnProperty(prop)) { callback(data[prop], prop); } } }, dispatch: can.dispatch }); Map.prototype.on = Map.prototype.bind; Map.prototype.off = Map.prototype.unbind; Map.on = Map.bind; Map.off = Map.unbind; module.exports = Map;