UNPKG

can

Version:

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

250 lines (249 loc) 8.85 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#compute/get_value_and_bind*/ define(['can/util/library'], function (can) { function ObservedInfo(func, context, compute) { this.newObserved = {}; this.oldObserved = null; this.func = func; this.context = context; this.compute = compute; this.onDependencyChange = can.proxy(this.onDependencyChange, this); this.depth = null; this.childDepths = {}; this.ignore = 0; this.inBatch = false; this.ready = false; compute.observedInfo = this; this.setReady = can.proxy(this._setReady, this); } can.simpleExtend(ObservedInfo.prototype, { getPrimaryDepth: function () { return this.compute._primaryDepth; }, _setReady: function () { this.ready = true; }, getDepth: function () { if (this.depth !== null) { return this.depth; } else { return this.depth = this._getDepth(); } }, _getDepth: function () { var max = 0, childDepths = this.childDepths; for (var cid in childDepths) { if (childDepths[cid] > max) { max = childDepths[cid]; } } return max + 1; }, addEdge: function (objEv) { objEv.obj.bind(objEv.event, this.onDependencyChange); if (objEv.obj.observedInfo) { this.childDepths[objEv.obj._cid] = objEv.obj.observedInfo.getDepth(); this.depth = null; } }, removeEdge: function (objEv) { objEv.obj.unbind(objEv.event, this.onDependencyChange); if (objEv.obj.observedInfo) { delete this.childDepths[objEv.obj._cid]; this.depth = null; } }, dependencyChange: function (ev) { if (this.bound && this.ready) { if (ev.batchNum !== undefined) { if (ev.batchNum !== this.batchNum) { ObservedInfo.registerUpdate(this); this.batchNum = ev.batchNum; } } else { this.updateCompute(ev.batchNum); } } }, onDependencyChange: function (ev, newVal, oldVal) { this.dependencyChange(ev, newVal, oldVal); }, updateCompute: function (batchNum) { if (this.bound) { var oldValue = this.value; this.getValueAndBind(); this.compute.updater(this.value, oldValue, batchNum); } }, getValueAndBind: function () { this.bound = true; this.oldObserved = this.newObserved || {}; this.ignore = 0; this.newObserved = {}; this.ready = false; observedInfoStack.push(this); this.value = this.func.call(this.context); observedInfoStack.pop(); this.updateBindings(); can.batch.afterPreviousEvents(this.setReady); }, updateBindings: function () { var newObserved = this.newObserved, oldObserved = this.oldObserved, name, obEv; for (name in newObserved) { obEv = newObserved[name]; if (!oldObserved[name]) { this.addEdge(obEv); } else { oldObserved[name] = null; } } for (name in oldObserved) { obEv = oldObserved[name]; if (obEv) { this.removeEdge(obEv); } } }, teardown: function () { this.bound = false; for (var name in this.newObserved) { var ob = this.newObserved[name]; this.removeEdge(ob); } this.newObserved = {}; } }); var updateOrder = [], curPrimaryDepth = Infinity, maxPrimaryDepth = 0, currentBatchNum; ObservedInfo.registerUpdate = function (observeInfo, batchNum) { var depth = observeInfo.getDepth() - 1; var primaryDepth = observeInfo.getPrimaryDepth(); curPrimaryDepth = Math.min(primaryDepth, curPrimaryDepth); maxPrimaryDepth = Math.max(primaryDepth, maxPrimaryDepth); var primary = updateOrder[primaryDepth] || (updateOrder[primaryDepth] = { observeInfos: [], current: Infinity, max: 0 }); var objs = primary.observeInfos[depth] || (primary.observeInfos[depth] = []); objs.push(observeInfo); primary.current = Math.min(depth, primary.current); primary.max = Math.max(depth, primary.max); }; ObservedInfo.updateUntil = function (primaryDepth, depth) { var cur; while (true) { if (curPrimaryDepth <= maxPrimaryDepth && curPrimaryDepth <= primaryDepth) { var primary = updateOrder[curPrimaryDepth]; if (primary && primary.current <= primary.max) { if (primary.current > depth) { return; } var last = primary.observeInfos[primary.current]; if (last && (cur = last.pop())) { cur.updateCompute(currentBatchNum); } else { primary.current++; } } else { curPrimaryDepth++; } } else { return; } } }; ObservedInfo.batchEnd = function (batchNum) { var cur; currentBatchNum = batchNum; while (true) { if (curPrimaryDepth <= maxPrimaryDepth) { var primary = updateOrder[curPrimaryDepth]; if (primary && primary.current <= primary.max) { var last = primary.observeInfos[primary.current]; if (last && (cur = last.pop())) { cur.updateCompute(batchNum); } else { primary.current++; } } else { curPrimaryDepth++; } } else { updateOrder = []; curPrimaryDepth = Infinity; maxPrimaryDepth = 0; return; } } }; var observedInfoStack = []; can.__observe = function (obj, event) { var top = observedInfoStack[observedInfoStack.length - 1]; if (top && !top.ignore) { var evStr = event + '', name = obj._cid + '|' + evStr; if (top.traps) { top.traps.push({ obj: obj, event: evStr, name: name }); } else if (!top.newObserved[name]) { top.newObserved[name] = { obj: obj, event: evStr }; } } }; can.__reading = can.__observe; can.__trapObserves = function () { if (observedInfoStack.length) { var top = observedInfoStack[observedInfoStack.length - 1]; var traps = top.traps = []; return function () { top.traps = null; return traps; }; } else { return function () { return []; }; } }; can.__observes = function (observes) { var top = observedInfoStack[observedInfoStack.length - 1]; if (top) { for (var i = 0, len = observes.length; i < len; i++) { var trap = observes[i], name = trap.name; if (!top.newObserved[name]) { top.newObserved[name] = trap; } } } }; can.__isRecordingObserves = function () { var len = observedInfoStack.length, last = observedInfoStack[len - 1]; return len && last.ignore === 0 && last; }; can.__notObserve = function (fn) { return function () { if (observedInfoStack.length) { var top = observedInfoStack[observedInfoStack.length - 1]; top.ignore++; var res = fn.apply(this, arguments); top.ignore--; return res; } else { return fn.apply(this, arguments); } }; }; can.batch._onDispatchedEvents = ObservedInfo.batchEnd; return ObservedInfo; });