can-observable-array
Version:
Observable arrays
142 lines (123 loc) • 4.59 kB
JavaScript
var canReflect = require("can-reflect");
var _require = require("can-observable-mixin"),
mixins = _require.mixins;
var ObservationRecorder = require("can-observation-recorder");
var mapBindings = require("can-event-queue/map/map");
var metaSymbol = Symbol.for("can.meta");
var helpers = {
assignNonEnumerable: function assignNonEnumerable(obj, key, value) {
return Object.defineProperty(obj, key, {
enumerable: false,
writable: true,
configurable: true,
value: value
});
},
shouldRecordObservationOnAllKeysExceptFunctionsOnProto: function shouldRecordObservationOnAllKeysExceptFunctionsOnProto(keyInfo, meta) {
return meta.preventSideEffects === 0 && !keyInfo.isAccessor && ( // it's on our proto, but not a function
keyInfo.targetHasOwnKey || !keyInfo.protoHasKey && !Object.isSealed(meta.target) || keyInfo.protoHasKey && typeof targetValue !== "function");
},
/*
* dispatch an event when an index changes
*/
dispatchIndexEvent: function dispatchIndexEvent(attr, how, newVal, oldVal) {
var index = +attr; // Make sure this is not nested and not an expando
if (!isNaN(index)) {
var itemsDefinition = this._define.definitions["#"];
if (how === 'set') {
this.dispatch({
type: index,
action: how,
key: index,
value: newVal,
oldValue: oldVal
}, [newVal, oldVal]); // if event is being set through an ObservableArray.prototype method,
// do not dispatch length or patch events.
// This will be handled by ObservableArray.prototype method.
var meta = this[metaSymbol];
if (!("preventSideEffects" in meta) || meta.preventSideEffects === 0) {
var patches = [{
index: index,
deleteCount: 1,
insert: [newVal],
type: "splice"
}];
helpers.dispatchLengthPatch.call(this, how, patches, this.length, this.length);
}
} else if (how === 'add') {
if (itemsDefinition && typeof itemsDefinition.added === 'function') {
ObservationRecorder.ignore(itemsDefinition.added).call(this, newVal, index);
}
this.dispatch({
type: index,
action: how,
key: index,
value: newVal,
oldValue: oldVal
}, [newVal, oldVal]); // if event is being set through an ObservableArray.prototype method,
// do not dispatch length or patch events.
// This will be handled by ObservableArray.prototype method.
var _meta = this[metaSymbol];
if (!("preventSideEffects" in _meta) || _meta.preventSideEffects === 0) {
var _patches = [{
index: index,
deleteCount: 0,
insert: [newVal],
type: "splice"
}];
helpers.dispatchLengthPatch.call(this, how, _patches, this.length, this.length - 1);
}
} else if (how === 'remove') {
if (itemsDefinition && typeof itemsDefinition.removed === 'function') {
ObservationRecorder.ignore(itemsDefinition.removed).call(this, oldVal, index);
}
}
} else {
var key = "" + attr;
this.dispatch({
type: key,
key: key,
action: how,
value: newVal,
oldValue: oldVal,
target: this
}, [newVal, oldVal]);
}
},
/*
* Dispatch a `type: "splice"` patch and a `length` event
*/
dispatchLengthPatch: function dispatchLengthPatch(how, patches, newLength, oldLength) {
var dispatchArgs = {
type: "length",
key: "length",
action: how,
value: newLength,
oldValue: oldLength,
patches: patches
}; //!steal-remove-start
if (process.env.NODE_ENV !== "production") {
dispatchArgs.reasonLog = [canReflect.getName(this) + "." + how + " called with", arguments];
} //!steal-remove-end
mapBindings.dispatch.call(this, dispatchArgs, [newLength, oldLength]);
},
convertItem: function convertItem(Constructor, item) {
if (Constructor.items) {
var definition = mixins.normalizeTypeDefinition(Constructor.items.type || Constructor.items);
return canReflect.convert(item, definition);
}
return item;
},
convertItems: function convertItems(Constructor, items) {
if (items.length) {
if (Constructor.items) {
for (var i = 0, len = items.length; i < len; i++) {
items[i] = helpers.convertItem(Constructor, items[i]);
}
}
}
return items;
}
};
module.exports = helpers;
;