UNPKG

can

Version:

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

215 lines (214 loc) 7.67 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#view/scope/scope*/ var can = require('../../util/util.js'); var makeComputeData = require('./compute_data.js'); require('../../construct/construct.js'); require('../../map/map.js'); require('../../list/list.js'); require('../view.js'); require('../../compute/compute.js'); function Scope(context, parent, meta) { this._context = context; this._parent = parent; this._meta = meta || {}; this.__cache = {}; } can.simpleExtend(Scope, { read: can.compute.read, Refs: can.Map.extend({ shortName: 'ReferenceMap' }, {}), refsScope: function () { return new can.view.Scope(new this.Refs()); } }); can.simpleExtend(Scope.prototype, { add: function (context, meta) { if (context !== this._context) { return new this.constructor(context, this, meta); } else { return this; } }, read: function (attr, options) { if (attr === '%root') { return { value: this.getRoot() }; } var isInCurrentContext = attr.substr(0, 2) === './', isInParentContext = attr.substr(0, 3) === '../', isCurrentContext = attr === '.' || attr === 'this', isParentContext = attr === '..', isContextBased = isInCurrentContext || isInParentContext || isCurrentContext || isParentContext; if (isContextBased && this._meta.notContext) { return this._parent.read(attr, options); } var currentScopeOnly; if (isInCurrentContext) { currentScopeOnly = true; attr = attr.substr(2); } else if (isInParentContext || isParentContext) { var parent = this._parent; while (parent._meta.notContext) { parent = parent._parent; } if (isParentContext) { return { value: parent._context }; } return parent.read(attr.substr(3) || '.', options); } else if (isCurrentContext) { return { value: this._context }; } var keyReads = can.compute.read.reads(attr); if (keyReads[0].key.charAt(0) === '*') { return this.getRefs()._read(keyReads, options, true); } else { return this._read(keyReads, options, currentScopeOnly); } }, _read: function (keyReads, options, currentScopeOnly) { var currentScope = this, currentContext, undefinedObserves = [], currentObserve, currentReads, setObserveDepth = -1, currentSetReads, currentSetObserve, readOptions = can.simpleExtend({ foundObservable: function (observe, nameIndex) { currentObserve = observe; currentReads = keyReads.slice(nameIndex); }, earlyExit: function (parentValue, nameIndex) { if (nameIndex > setObserveDepth) { currentSetObserve = currentObserve; currentSetReads = currentReads; setObserveDepth = nameIndex; } } }, options); while (currentScope) { currentContext = currentScope._context; if (currentContext !== null && (typeof currentContext === 'object' || typeof currentContext === 'function')) { var getObserves = can.__trapObserves(); var data = can.compute.read(currentContext, keyReads, readOptions); var observes = getObserves(); if (data.value !== undefined) { can.__observes(observes); return { scope: currentScope, rootObserve: currentObserve, value: data.value, reads: currentReads }; } else { undefinedObserves.push.apply(undefinedObserves, observes); } } if (currentScopeOnly) { currentScope = null; } else { currentScope = currentScope._parent; } } can.__observes(undefinedObserves); return { setRoot: currentSetObserve, reads: currentSetReads, value: undefined }; }, get: can.__notObserve(function (key, options) { options = can.simpleExtend({ isArgument: true }, options); var res = this.read(key, options); return res.value; }), getScope: function (tester) { var scope = this; while (scope) { if (tester(scope)) { return scope; } scope = scope._parent; } }, getContext: function (tester) { var res = this.getScope(tester); return res && res._context; }, getRefs: function () { return this.getScope(function (scope) { return scope._context instanceof Scope.Refs; }); }, getRoot: function () { var cur = this, child = this; while (cur._parent) { child = cur; cur = cur._parent; } if (cur._context instanceof Scope.Refs) { cur = child; } return cur._context; }, set: function (key, value, options) { var dotIndex = key.lastIndexOf('.'), slashIndex = key.lastIndexOf('/'), contextPath, propName; if (slashIndex > dotIndex) { contextPath = key.substring(0, slashIndex); propName = key.substring(slashIndex + 1, key.length); } else { if (dotIndex !== -1) { contextPath = key.substring(0, dotIndex); propName = key.substring(dotIndex + 1, key.length); } else { contextPath = '.'; propName = key; } } if (key.charAt(0) === '*') { can.compute.set(this.getRefs()._context, key, value, options); } else { var context = this.read(contextPath, options).value; can.compute.set(context, propName, value, options); } }, attr: can.__notObserve(function (key, value, options) { options = can.simpleExtend({ isArgument: true }, options); if (arguments.length === 2) { return this.set(key, value, options); } else { return this.get(key, options); } }), computeData: function (key, options) { return makeComputeData(this, key, options); }, compute: function (key, options) { return this.computeData(key, options).compute; }, cloneFromRef: function () { var contexts = []; var scope = this, context, parent; while (scope) { context = scope._context; if (context instanceof Scope.Refs) { parent = scope._parent; break; } contexts.unshift(context); scope = scope._parent; } if (parent) { can.each(contexts, function (context) { parent = parent.add(context); }); return parent; } else { return this; } } }); can.view.Scope = Scope; function Options(data, parent, meta) { if (!data.helpers && !data.partials && !data.tags) { data = { helpers: data }; } Scope.call(this, data, parent, meta); } Options.prototype = new Scope(); Options.prototype.constructor = Options; can.view.Options = Options; module.exports = Scope;