| Index: sky/framework/sky-element/TemplateBinding.sky
|
| diff --git a/sky/framework/sky-element/TemplateBinding.sky b/sky/framework/sky-element/TemplateBinding.sky
|
| deleted file mode 100644
|
| index 1000094526302b2a835bbffbed705aa243337670..0000000000000000000000000000000000000000
|
| --- a/sky/framework/sky-element/TemplateBinding.sky
|
| +++ /dev/null
|
| @@ -1,1036 +0,0 @@
|
| -<!--
|
| -// Copyright 2014 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| --->
|
| -<import src="observe.sky" as="observe" />
|
| -
|
| -<script>
|
| -Node.prototype.bind = function(name, observable, oneTime) {
|
| - var self = this;
|
| -
|
| - if (oneTime) {
|
| - this[name] = observable;
|
| - return;
|
| - }
|
| -
|
| - this[name] = observable.open(function(value) {
|
| - self[name] = value;
|
| - });
|
| -
|
| - return observable;
|
| -};
|
| -
|
| -function sanitizeValue(value) {
|
| - return value == null ? '' : value;
|
| -}
|
| -
|
| -function updateText(node, value) {
|
| - node.data = sanitizeValue(value);
|
| -}
|
| -
|
| -function textBinding(node) {
|
| - return function(value) {
|
| - return updateText(node, value);
|
| - };
|
| -}
|
| -
|
| -Text.prototype.bind = function(name, value, oneTime) {
|
| - if (name !== 'textContent')
|
| - return Node.prototype.bind.call(this, name, value, oneTime);
|
| -
|
| - if (oneTime)
|
| - return updateText(this, value);
|
| -
|
| - var observable = value;
|
| - updateText(this, observable.open(textBinding(this)));
|
| - return observable;
|
| -}
|
| -
|
| -function updateAttribute(el, name, value) {
|
| - el.setAttribute(name, sanitizeValue(value));
|
| -}
|
| -
|
| -function attributeBinding(el, name) {
|
| - return function(value) {
|
| - updateAttribute(el, name, value);
|
| - };
|
| -}
|
| -
|
| -function bindAsAttribute(el, name) {
|
| - if (name == 'style' || name == 'class')
|
| - return true;
|
| - if (el.tagName == 'a' && name == 'href')
|
| - return true;
|
| -}
|
| -
|
| -Element.prototype.bind = function(name, value, oneTime) {
|
| - if (!bindAsAttribute(this, name))
|
| - return Node.prototype.bind.call(this, name, value, oneTime);
|
| -
|
| - if (oneTime)
|
| - return updateAttribute(this, name, value);
|
| -
|
| - var observable = value;
|
| - updateAttribute(this, name, observable.open(attributeBinding(this, name)));
|
| - return observable;
|
| -}
|
| -
|
| -function getFragmentRoot(node) {
|
| - var p;
|
| - while (p = node.parentNode) {
|
| - node = p;
|
| - }
|
| -
|
| - return node;
|
| -}
|
| -
|
| -function searchRefId(node, id) {
|
| - if (!id)
|
| - return;
|
| -
|
| - var ref;
|
| - var selector = '#' + id;
|
| - while (!ref) {
|
| - node = getFragmentRoot(node);
|
| -
|
| - if (node.protoContent_)
|
| - ref = node.protoContent_.querySelector(selector);
|
| - else if (node.getElementById)
|
| - ref = node.getElementById(id);
|
| -
|
| - if (ref || !node.templateCreator_)
|
| - break
|
| -
|
| - node = node.templateCreator_;
|
| - }
|
| -
|
| - return ref;
|
| -}
|
| -
|
| -function getInstanceRoot(node) {
|
| - while (node.parentNode) {
|
| - node = node.parentNode;
|
| - }
|
| - return node.templateCreator_ ? node : null;
|
| -}
|
| -
|
| -var BIND = 'bind';
|
| -var REPEAT = 'repeat';
|
| -var IF = 'if';
|
| -
|
| -var templateAttributeDirectives = {
|
| - 'template': true,
|
| - 'repeat': true,
|
| - 'bind': true,
|
| - 'ref': true
|
| -};
|
| -
|
| -function isTemplate(el) {
|
| - if (el.isTemplate_ === undefined)
|
| - el.isTemplate_ = el.tagName == 'template';
|
| -
|
| - return el.isTemplate_;
|
| -}
|
| -
|
| -function mixin(to, from) {
|
| - Object.getOwnPropertyNames(from).forEach(function(name) {
|
| - Object.defineProperty(to, name,
|
| - Object.getOwnPropertyDescriptor(from, name));
|
| - });
|
| -}
|
| -
|
| -function getTemplateStagingDocument(template) {
|
| - if (!template.stagingDocument_) {
|
| - var owner = template.ownerDocument;
|
| - if (!owner.stagingDocument_) {
|
| - // FIXME(sky): Does this need to create a Document without a registration
|
| - // context?
|
| - owner.stagingDocument_ = new Document();
|
| - owner.stagingDocument_.isStagingDocument = true;
|
| - owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_;
|
| - }
|
| -
|
| - template.stagingDocument_ = owner.stagingDocument_;
|
| - }
|
| -
|
| - return template.stagingDocument_;
|
| -}
|
| -
|
| -var templateObserver;
|
| -if (typeof MutationObserver == 'function') {
|
| - templateObserver = new MutationObserver(function(records) {
|
| - for (var i = 0; i < records.length; i++) {
|
| - records[i].target.refChanged_();
|
| - }
|
| - });
|
| -}
|
| -
|
| -var contentDescriptor = {
|
| - get: function() {
|
| - return this.content_;
|
| - },
|
| - enumerable: true,
|
| - configurable: true
|
| -};
|
| -
|
| -function ensureSetModelScheduled(template) {
|
| - if (!template.setModelFn_) {
|
| - template.setModelFn_ = function() {
|
| - template.setModelFnScheduled_ = false;
|
| - var map = getBindings(template,
|
| - template.delegate_ && template.delegate_.prepareBinding);
|
| - processBindings(template, map, template.model_);
|
| - };
|
| - }
|
| -
|
| - if (!template.setModelFnScheduled_) {
|
| - template.setModelFnScheduled_ = true;
|
| - Observer.runEOM_(template.setModelFn_);
|
| - }
|
| -}
|
| -
|
| -mixin(HTMLTemplateElement.prototype, {
|
| - bind: function(name, value, oneTime) {
|
| - if (name != 'ref')
|
| - return Element.prototype.bind.call(this, name, value, oneTime);
|
| -
|
| - var self = this;
|
| - var ref = oneTime ? value : value.open(function(ref) {
|
| - self.setAttribute('ref', ref);
|
| - self.refChanged_();
|
| - });
|
| -
|
| - this.setAttribute('ref', ref);
|
| - this.refChanged_();
|
| - if (oneTime)
|
| - return;
|
| -
|
| - if (!this.bindings_) {
|
| - this.bindings_ = { ref: value };
|
| - } else {
|
| - this.bindings_.ref = value;
|
| - }
|
| -
|
| - return value;
|
| - },
|
| -
|
| - processBindingDirectives_: function(directives) {
|
| - if (this.iterator_)
|
| - this.iterator_.closeDeps();
|
| -
|
| - if (!directives.if && !directives.bind && !directives.repeat) {
|
| - if (this.iterator_) {
|
| - this.iterator_.close();
|
| - this.iterator_ = undefined;
|
| - }
|
| -
|
| - return;
|
| - }
|
| -
|
| - if (!this.iterator_) {
|
| - this.iterator_ = new TemplateIterator(this);
|
| - }
|
| -
|
| - this.iterator_.updateDependencies(directives, this.model_);
|
| -
|
| - if (templateObserver) {
|
| - templateObserver.observe(this, { attributes: true,
|
| - attributeFilter: ['ref'] });
|
| - }
|
| -
|
| - return this.iterator_;
|
| - },
|
| -
|
| - createInstance: function(model, bindingDelegate, delegate_) {
|
| - if (bindingDelegate)
|
| - delegate_ = this.newDelegate_(bindingDelegate);
|
| - else if (!delegate_)
|
| - delegate_ = this.delegate_;
|
| -
|
| - if (!this.refContent_)
|
| - this.refContent_ = this.ref_.content;
|
| - var content = this.refContent_;
|
| - if (content.firstChild === null)
|
| - return emptyInstance;
|
| -
|
| - var map = getInstanceBindingMap(content, delegate_);
|
| - var stagingDocument = getTemplateStagingDocument(this);
|
| - var instance = stagingDocument.createDocumentFragment();
|
| - instance.templateCreator_ = this;
|
| - instance.protoContent_ = content;
|
| - instance.bindings_ = [];
|
| - instance.terminator_ = null;
|
| - var instanceRecord = instance.templateInstance_ = {
|
| - firstNode: null,
|
| - lastNode: null,
|
| - model: model
|
| - };
|
| -
|
| - var i = 0;
|
| - var collectTerminator = false;
|
| - for (var child = content.firstChild; child; child = child.nextSibling) {
|
| - // The terminator of the instance is the clone of the last child of the
|
| - // content. If the last child is an active template, it may produce
|
| - // instances as a result of production, so simply collecting the last
|
| - // child of the instance after it has finished producing may be wrong.
|
| - if (child.nextSibling === null)
|
| - collectTerminator = true;
|
| -
|
| - var clone = cloneAndBindInstance(child, instance, stagingDocument,
|
| - map.children[i++],
|
| - model,
|
| - delegate_,
|
| - instance.bindings_);
|
| - clone.templateInstance_ = instanceRecord;
|
| - if (collectTerminator)
|
| - instance.terminator_ = clone;
|
| - }
|
| -
|
| - instanceRecord.firstNode = instance.firstChild;
|
| - instanceRecord.lastNode = instance.lastChild;
|
| - instance.templateCreator_ = undefined;
|
| - instance.protoContent_ = undefined;
|
| - return instance;
|
| - },
|
| -
|
| - get model() {
|
| - return this.model_;
|
| - },
|
| -
|
| - set model(model) {
|
| - this.model_ = model;
|
| - ensureSetModelScheduled(this);
|
| - },
|
| -
|
| - get bindingDelegate() {
|
| - return this.delegate_ && this.delegate_.raw;
|
| - },
|
| -
|
| - refChanged_: function() {
|
| - if (!this.iterator_ || this.refContent_ === this.ref_.content)
|
| - return;
|
| -
|
| - this.refContent_ = undefined;
|
| - this.iterator_.valueChanged();
|
| - this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue());
|
| - },
|
| -
|
| - clear: function() {
|
| - this.model_ = undefined;
|
| - this.delegate_ = undefined;
|
| - if (this.bindings_ && this.bindings_.ref)
|
| - this.bindings_.ref.close()
|
| - this.refContent_ = undefined;
|
| - if (!this.iterator_)
|
| - return;
|
| - this.iterator_.valueChanged();
|
| - this.iterator_.close()
|
| - this.iterator_ = undefined;
|
| - },
|
| -
|
| - setDelegate_: function(delegate) {
|
| - this.delegate_ = delegate;
|
| - this.bindingMap_ = undefined;
|
| - if (this.iterator_) {
|
| - this.iterator_.instancePositionChangedFn_ = undefined;
|
| - this.iterator_.instanceModelFn_ = undefined;
|
| - }
|
| - },
|
| -
|
| - newDelegate_: function(bindingDelegate) {
|
| - if (!bindingDelegate)
|
| - return;
|
| -
|
| - function delegateFn(name) {
|
| - var fn = bindingDelegate && bindingDelegate[name];
|
| - if (typeof fn != 'function')
|
| - return;
|
| -
|
| - return function() {
|
| - return fn.apply(bindingDelegate, arguments);
|
| - };
|
| - }
|
| -
|
| - return {
|
| - bindingMaps: {},
|
| - raw: bindingDelegate,
|
| - prepareBinding: delegateFn('prepareBinding'),
|
| - prepareInstanceModel: delegateFn('prepareInstanceModel'),
|
| - prepareInstancePositionChanged:
|
| - delegateFn('prepareInstancePositionChanged')
|
| - };
|
| - },
|
| -
|
| - set bindingDelegate(bindingDelegate) {
|
| - if (this.delegate_) {
|
| - throw Error('Template must be cleared before a new bindingDelegate ' +
|
| - 'can be assigned');
|
| - }
|
| -
|
| - this.setDelegate_(this.newDelegate_(bindingDelegate));
|
| - },
|
| -
|
| - get ref_() {
|
| - var ref = searchRefId(this, this.getAttribute('ref'));
|
| - if (!ref)
|
| - ref = this.instanceRef_;
|
| -
|
| - if (!ref)
|
| - return this;
|
| -
|
| - var nextRef = ref.ref_;
|
| - return nextRef ? nextRef : ref;
|
| - }
|
| -});
|
| -
|
| -// Returns
|
| -// a) undefined if there are no mustaches.
|
| -// b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least
|
| -// one mustache.
|
| -function parseMustaches(s, name, node, prepareBindingFn) {
|
| - if (!s || !s.length)
|
| - return;
|
| -
|
| - var tokens;
|
| - var length = s.length;
|
| - var startIndex = 0, lastIndex = 0, endIndex = 0;
|
| - var onlyOneTime = true;
|
| - while (lastIndex < length) {
|
| - var startIndex = s.indexOf('{{', lastIndex);
|
| - var oneTimeStart = s.indexOf('[[', lastIndex);
|
| - var oneTime = false;
|
| - var terminator = '}}';
|
| -
|
| - if (oneTimeStart >= 0 &&
|
| - (startIndex < 0 || oneTimeStart < startIndex)) {
|
| - startIndex = oneTimeStart;
|
| - oneTime = true;
|
| - terminator = ']]';
|
| - }
|
| -
|
| - endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2);
|
| -
|
| - if (endIndex < 0) {
|
| - if (!tokens)
|
| - return;
|
| -
|
| - tokens.push(s.slice(lastIndex)); // TEXT
|
| - break;
|
| - }
|
| -
|
| - tokens = tokens || [];
|
| - tokens.push(s.slice(lastIndex, startIndex)); // TEXT
|
| - var pathString = s.slice(startIndex + 2, endIndex).trim();
|
| - tokens.push(oneTime); // ONE_TIME?
|
| - onlyOneTime = onlyOneTime && oneTime;
|
| - var delegateFn = prepareBindingFn &&
|
| - prepareBindingFn(pathString, name, node);
|
| - // Don't try to parse the expression if there's a prepareBinding function
|
| - if (delegateFn == null) {
|
| - tokens.push(observe.Path.get(pathString)); // PATH
|
| - } else {
|
| - tokens.push(null);
|
| - }
|
| - tokens.push(delegateFn); // DELEGATE_FN
|
| - lastIndex = endIndex + 2;
|
| - }
|
| -
|
| - if (lastIndex === length)
|
| - tokens.push(''); // TEXT
|
| -
|
| - tokens.hasOnePath = tokens.length === 5;
|
| - tokens.isSimplePath = tokens.hasOnePath &&
|
| - tokens[0] == '' &&
|
| - tokens[4] == '';
|
| - tokens.onlyOneTime = onlyOneTime;
|
| -
|
| - tokens.combinator = function(values) {
|
| - var newValue = tokens[0];
|
| -
|
| - for (var i = 1; i < tokens.length; i += 4) {
|
| - var value = tokens.hasOnePath ? values : values[(i - 1) / 4];
|
| - if (value !== undefined)
|
| - newValue += value;
|
| - newValue += tokens[i + 3];
|
| - }
|
| -
|
| - return newValue;
|
| - }
|
| -
|
| - return tokens;
|
| -};
|
| -
|
| -function processOneTimeBinding(name, tokens, node, model) {
|
| - if (tokens.hasOnePath) {
|
| - var delegateFn = tokens[3];
|
| - var value = delegateFn ? delegateFn(model, node, true) :
|
| - tokens[2].getValueFrom(model);
|
| - return tokens.isSimplePath ? value : tokens.combinator(value);
|
| - }
|
| -
|
| - var values = [];
|
| - for (var i = 1; i < tokens.length; i += 4) {
|
| - var delegateFn = tokens[i + 2];
|
| - values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) :
|
| - tokens[i + 1].getValueFrom(model);
|
| - }
|
| -
|
| - return tokens.combinator(values);
|
| -}
|
| -
|
| -function processSinglePathBinding(name, tokens, node, model) {
|
| - var delegateFn = tokens[3];
|
| - var observer = delegateFn ? delegateFn(model, node, false) :
|
| - new observe.PathObserver(model, tokens[2]);
|
| -
|
| - return tokens.isSimplePath ? observer :
|
| - new observe.ObserverTransform(observer, tokens.combinator);
|
| -}
|
| -
|
| -function processBinding(name, tokens, node, model) {
|
| - if (tokens.onlyOneTime)
|
| - return processOneTimeBinding(name, tokens, node, model);
|
| -
|
| - if (tokens.hasOnePath)
|
| - return processSinglePathBinding(name, tokens, node, model);
|
| -
|
| - var observer = new observe.CompoundObserver();
|
| -
|
| - for (var i = 1; i < tokens.length; i += 4) {
|
| - var oneTime = tokens[i];
|
| - var delegateFn = tokens[i + 2];
|
| -
|
| - if (delegateFn) {
|
| - var value = delegateFn(model, node, oneTime);
|
| - if (oneTime)
|
| - observer.addPath(value)
|
| - else
|
| - observer.addObserver(value);
|
| - continue;
|
| - }
|
| -
|
| - var path = tokens[i + 1];
|
| - if (oneTime)
|
| - observer.addPath(path.getValueFrom(model))
|
| - else
|
| - observer.addPath(model, path);
|
| - }
|
| -
|
| - return new observe.ObserverTransform(observer, tokens.combinator);
|
| -}
|
| -
|
| -function processBindings(node, bindings, model, instanceBindings) {
|
| - for (var i = 0; i < bindings.length; i += 2) {
|
| - var name = bindings[i]
|
| - var tokens = bindings[i + 1];
|
| - var value = processBinding(name, tokens, node, model);
|
| - var binding = node.bind(name, value, tokens.onlyOneTime);
|
| - if (binding && instanceBindings)
|
| - instanceBindings.push(binding);
|
| - }
|
| -
|
| - if (!bindings.isTemplate)
|
| - return;
|
| -
|
| - node.model_ = model;
|
| - var iter = node.processBindingDirectives_(bindings);
|
| - if (instanceBindings && iter)
|
| - instanceBindings.push(iter);
|
| -}
|
| -
|
| -function parseWithDefault(el, name, prepareBindingFn) {
|
| - var v = el.getAttribute(name);
|
| - return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn);
|
| -}
|
| -
|
| -function addEventHandler(element, name, method) {
|
| - element.addEventListener(name, function(event) {
|
| - var scope = element.ownerScope;
|
| - var host = scope.host;
|
| - var handler = host && host[method];
|
| - if (handler instanceof Function)
|
| - return handler.call(host, event);
|
| - });
|
| -}
|
| -
|
| -function parseAttributeBindings(element, prepareBindingFn) {
|
| - var bindings = [];
|
| - var ifFound = false;
|
| - var bindFound = false;
|
| - var attributes = element.getAttributes();
|
| -
|
| - for (var i = 0; i < attributes.length; i++) {
|
| - var attr = attributes[i];
|
| - var name = attr.name;
|
| - var value = attr.value;
|
| -
|
| - if (isTemplate(element) &&
|
| - (name === IF || name === BIND || name === REPEAT)) {
|
| - continue;
|
| - }
|
| -
|
| - if (name.startsWith('on-')) {
|
| - if (!bindings.eventHandlers)
|
| - bindings.eventHandlers = new Map();
|
| - bindings.eventHandlers.set(name.substring(3), value);
|
| - continue;
|
| - }
|
| -
|
| - var tokens = parseMustaches(value, name, element,
|
| - prepareBindingFn);
|
| - if (!tokens)
|
| - continue;
|
| -
|
| - bindings.push(name, tokens);
|
| - }
|
| -
|
| - if (isTemplate(element)) {
|
| - bindings.isTemplate = true;
|
| - bindings.if = parseWithDefault(element, IF, prepareBindingFn);
|
| - bindings.bind = parseWithDefault(element, BIND, prepareBindingFn);
|
| - bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn);
|
| -
|
| - if (bindings.if && !bindings.bind && !bindings.repeat)
|
| - bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn);
|
| - }
|
| -
|
| - return bindings;
|
| -}
|
| -
|
| -function getBindings(node, prepareBindingFn) {
|
| - if (node instanceof Element) {
|
| - return parseAttributeBindings(node, prepareBindingFn);
|
| - }
|
| -
|
| - if (node instanceof Text) {
|
| - var tokens = parseMustaches(node.data, 'textContent', node,
|
| - prepareBindingFn);
|
| - if (tokens)
|
| - return ['textContent', tokens];
|
| - }
|
| -
|
| - return [];
|
| -}
|
| -
|
| -function cloneAndBindInstance(node, parent, stagingDocument, bindings, model,
|
| - delegate,
|
| - instanceBindings,
|
| - instanceRecord) {
|
| - var clone = parent.appendChild(stagingDocument.importNode(node, false));
|
| -
|
| - var i = 0;
|
| - for (var child = node.firstChild; child; child = child.nextSibling) {
|
| - cloneAndBindInstance(child, clone, stagingDocument,
|
| - bindings.children[i++],
|
| - model,
|
| - delegate,
|
| - instanceBindings);
|
| - }
|
| -
|
| - if (bindings.isTemplate) {
|
| - clone.instanceRef_ = node;
|
| -
|
| - if (delegate)
|
| - clone.setDelegate_(delegate);
|
| - }
|
| -
|
| - if (bindings.eventHandlers) {
|
| - bindings.eventHandlers.forEach(function(handler, eventName) {
|
| - addEventHandler(clone, eventName, handler);
|
| - });
|
| - }
|
| -
|
| - processBindings(clone, bindings, model, instanceBindings);
|
| - return clone;
|
| -}
|
| -
|
| -function createInstanceBindingMap(node, prepareBindingFn) {
|
| - var map = getBindings(node, prepareBindingFn);
|
| - map.children = {};
|
| - var index = 0;
|
| - for (var child = node.firstChild; child; child = child.nextSibling) {
|
| - map.children[index++] = createInstanceBindingMap(child, prepareBindingFn);
|
| - }
|
| -
|
| - return map;
|
| -}
|
| -
|
| -var contentUidCounter = 1;
|
| -
|
| -// TODO(rafaelw): Setup a MutationObserver on content which clears the id
|
| -// so that bindingMaps regenerate when the template.content changes.
|
| -function getContentUid(content) {
|
| - var id = content.id_;
|
| - if (!id)
|
| - id = content.id_ = contentUidCounter++;
|
| - return id;
|
| -}
|
| -
|
| -// Each delegate is associated with a set of bindingMaps, one for each
|
| -// content which may be used by a template. The intent is that each binding
|
| -// delegate gets the opportunity to prepare the instance (via the prepare*
|
| -// delegate calls) once across all uses.
|
| -// TODO(rafaelw): Separate out the parse map from the binding map. In the
|
| -// current implementation, if two delegates need a binding map for the same
|
| -// content, the second will have to reparse.
|
| -function getInstanceBindingMap(content, delegate_) {
|
| - var contentId = getContentUid(content);
|
| - if (delegate_) {
|
| - var map = delegate_.bindingMaps[contentId];
|
| - if (!map) {
|
| - map = delegate_.bindingMaps[contentId] =
|
| - createInstanceBindingMap(content, delegate_.prepareBinding) || [];
|
| - }
|
| - return map;
|
| - }
|
| -
|
| - var map = content.bindingMap_;
|
| - if (!map) {
|
| - map = content.bindingMap_ =
|
| - createInstanceBindingMap(content, undefined) || [];
|
| - }
|
| - return map;
|
| -}
|
| -
|
| -Object.defineProperty(Node.prototype, 'templateInstance', {
|
| - get: function() {
|
| - var instance = this.templateInstance_;
|
| - return instance ? instance :
|
| - (this.parentNode ? this.parentNode.templateInstance : undefined);
|
| - }
|
| -});
|
| -
|
| -var emptyInstance = document.createDocumentFragment();
|
| -emptyInstance.bindings_ = [];
|
| -emptyInstance.terminator_ = null;
|
| -
|
| -function TemplateIterator(templateElement) {
|
| - this.closed = false;
|
| - this.templateElement_ = templateElement;
|
| - this.instances = [];
|
| - this.deps = undefined;
|
| - this.iteratedValue = [];
|
| - this.presentValue = undefined;
|
| - this.arrayObserver = undefined;
|
| -}
|
| -
|
| -TemplateIterator.prototype = {
|
| - closeDeps: function() {
|
| - var deps = this.deps;
|
| - if (deps) {
|
| - if (deps.ifOneTime === false)
|
| - deps.ifValue.close();
|
| - if (deps.oneTime === false)
|
| - deps.value.close();
|
| - }
|
| - },
|
| -
|
| - updateDependencies: function(directives, model) {
|
| - this.closeDeps();
|
| -
|
| - var deps = this.deps = {};
|
| - var template = this.templateElement_;
|
| -
|
| - var ifValue = true;
|
| - if (directives.if) {
|
| - deps.hasIf = true;
|
| - deps.ifOneTime = directives.if.onlyOneTime;
|
| - deps.ifValue = processBinding(IF, directives.if, template, model);
|
| -
|
| - ifValue = deps.ifValue;
|
| -
|
| - // oneTime if & predicate is false. nothing else to do.
|
| - if (deps.ifOneTime && !ifValue) {
|
| - this.valueChanged();
|
| - return;
|
| - }
|
| -
|
| - if (!deps.ifOneTime)
|
| - ifValue = ifValue.open(this.updateIfValue, this);
|
| - }
|
| -
|
| - if (directives.repeat) {
|
| - deps.repeat = true;
|
| - deps.oneTime = directives.repeat.onlyOneTime;
|
| - deps.value = processBinding(REPEAT, directives.repeat, template, model);
|
| - } else {
|
| - deps.repeat = false;
|
| - deps.oneTime = directives.bind.onlyOneTime;
|
| - deps.value = processBinding(BIND, directives.bind, template, model);
|
| - }
|
| -
|
| - var value = deps.value;
|
| - if (!deps.oneTime)
|
| - value = value.open(this.updateIteratedValue, this);
|
| -
|
| - if (!ifValue) {
|
| - this.valueChanged();
|
| - return;
|
| - }
|
| -
|
| - this.updateValue(value);
|
| - },
|
| -
|
| - /**
|
| - * Gets the updated value of the bind/repeat. This can potentially call
|
| - * user code (if a bindingDelegate is set up) so we try to avoid it if we
|
| - * already have the value in hand (from Observer.open).
|
| - */
|
| - getUpdatedValue: function() {
|
| - var value = this.deps.value;
|
| - if (!this.deps.oneTime)
|
| - value = value.discardChanges();
|
| - return value;
|
| - },
|
| -
|
| - updateIfValue: function(ifValue) {
|
| - if (!ifValue) {
|
| - this.valueChanged();
|
| - return;
|
| - }
|
| -
|
| - this.updateValue(this.getUpdatedValue());
|
| - },
|
| -
|
| - updateIteratedValue: function(value) {
|
| - if (this.deps.hasIf) {
|
| - var ifValue = this.deps.ifValue;
|
| - if (!this.deps.ifOneTime)
|
| - ifValue = ifValue.discardChanges();
|
| - if (!ifValue) {
|
| - this.valueChanged();
|
| - return;
|
| - }
|
| - }
|
| -
|
| - this.updateValue(value);
|
| - },
|
| -
|
| - updateValue: function(value) {
|
| - if (!this.deps.repeat)
|
| - value = [value];
|
| - var observe = this.deps.repeat &&
|
| - !this.deps.oneTime &&
|
| - Array.isArray(value);
|
| - this.valueChanged(value, observe);
|
| - },
|
| -
|
| - valueChanged: function(value, observeValue) {
|
| - if (!Array.isArray(value))
|
| - value = [];
|
| -
|
| - if (value === this.iteratedValue)
|
| - return;
|
| -
|
| - this.unobserve();
|
| - this.presentValue = value;
|
| - if (observeValue) {
|
| - this.arrayObserver = new observe.ArrayObserver(this.presentValue);
|
| - this.arrayObserver.open(this.handleSplices, this);
|
| - }
|
| -
|
| - this.handleSplices(observe.ArrayObserver.calculateSplices(this.presentValue,
|
| - this.iteratedValue));
|
| - },
|
| -
|
| - getLastInstanceNode: function(index) {
|
| - if (index == -1)
|
| - return this.templateElement_;
|
| - var instance = this.instances[index];
|
| - var terminator = instance.terminator_;
|
| - if (!terminator)
|
| - return this.getLastInstanceNode(index - 1);
|
| -
|
| - if (terminator.nodeType !== Node.ELEMENT_NODE ||
|
| - this.templateElement_ === terminator) {
|
| - return terminator;
|
| - }
|
| -
|
| - var subtemplateIterator = terminator.iterator_;
|
| - if (!subtemplateIterator)
|
| - return terminator;
|
| -
|
| - return subtemplateIterator.getLastTemplateNode();
|
| - },
|
| -
|
| - getLastTemplateNode: function() {
|
| - return this.getLastInstanceNode(this.instances.length - 1);
|
| - },
|
| -
|
| - insertInstanceAt: function(index, fragment) {
|
| - var previousInstanceLast = this.getLastInstanceNode(index - 1);
|
| - var parent = this.templateElement_.parentNode;
|
| - this.instances.splice(index, 0, fragment);
|
| -
|
| - parent.insertBefore(fragment, previousInstanceLast.nextSibling);
|
| - },
|
| -
|
| - extractInstanceAt: function(index) {
|
| - var previousInstanceLast = this.getLastInstanceNode(index - 1);
|
| - var lastNode = this.getLastInstanceNode(index);
|
| - var parent = this.templateElement_.parentNode;
|
| - var instance = this.instances.splice(index, 1)[0];
|
| -
|
| - while (lastNode !== previousInstanceLast) {
|
| - var node = previousInstanceLast.nextSibling;
|
| - if (node == lastNode)
|
| - lastNode = previousInstanceLast;
|
| -
|
| - instance.appendChild(parent.removeChild(node));
|
| - }
|
| -
|
| - return instance;
|
| - },
|
| -
|
| - getDelegateFn: function(fn) {
|
| - fn = fn && fn(this.templateElement_);
|
| - return typeof fn === 'function' ? fn : null;
|
| - },
|
| -
|
| - handleSplices: function(splices) {
|
| - if (this.closed || !splices.length)
|
| - return;
|
| -
|
| - var template = this.templateElement_;
|
| -
|
| - if (!template.parentNode) {
|
| - this.close();
|
| - return;
|
| - }
|
| -
|
| - observe.ArrayObserver.applySplices(this.iteratedValue, this.presentValue,
|
| - splices);
|
| -
|
| - var delegate = template.delegate_;
|
| - if (this.instanceModelFn_ === undefined) {
|
| - this.instanceModelFn_ =
|
| - this.getDelegateFn(delegate && delegate.prepareInstanceModel);
|
| - }
|
| -
|
| - if (this.instancePositionChangedFn_ === undefined) {
|
| - this.instancePositionChangedFn_ =
|
| - this.getDelegateFn(delegate &&
|
| - delegate.prepareInstancePositionChanged);
|
| - }
|
| -
|
| - // Instance Removals
|
| - var instanceCache = new Map;
|
| - var removeDelta = 0;
|
| - for (var i = 0; i < splices.length; i++) {
|
| - var splice = splices[i];
|
| - var removed = splice.removed;
|
| - for (var j = 0; j < removed.length; j++) {
|
| - var model = removed[j];
|
| - var instance = this.extractInstanceAt(splice.index + removeDelta);
|
| - if (instance !== emptyInstance) {
|
| - instanceCache.set(model, instance);
|
| - }
|
| - }
|
| -
|
| - removeDelta -= splice.addedCount;
|
| - }
|
| -
|
| - // Instance Insertions
|
| - for (var i = 0; i < splices.length; i++) {
|
| - var splice = splices[i];
|
| - var addIndex = splice.index;
|
| - for (; addIndex < splice.index + splice.addedCount; addIndex++) {
|
| - var model = this.iteratedValue[addIndex];
|
| - var instance = instanceCache.get(model);
|
| - if (instance) {
|
| - instanceCache.delete(model);
|
| - } else {
|
| - if (this.instanceModelFn_) {
|
| - model = this.instanceModelFn_(model);
|
| - }
|
| -
|
| - if (model === undefined) {
|
| - instance = emptyInstance;
|
| - } else {
|
| - instance = template.createInstance(model, undefined, delegate);
|
| - }
|
| - }
|
| -
|
| - this.insertInstanceAt(addIndex, instance);
|
| - }
|
| - }
|
| -
|
| - instanceCache.forEach(function(instance) {
|
| - this.closeInstanceBindings(instance);
|
| - }, this);
|
| -
|
| - if (this.instancePositionChangedFn_)
|
| - this.reportInstancesMoved(splices);
|
| - },
|
| -
|
| - reportInstanceMoved: function(index) {
|
| - var instance = this.instances[index];
|
| - if (instance === emptyInstance)
|
| - return;
|
| -
|
| - this.instancePositionChangedFn_(instance.templateInstance_, index);
|
| - },
|
| -
|
| - reportInstancesMoved: function(splices) {
|
| - var index = 0;
|
| - var offset = 0;
|
| - for (var i = 0; i < splices.length; i++) {
|
| - var splice = splices[i];
|
| - if (offset != 0) {
|
| - while (index < splice.index) {
|
| - this.reportInstanceMoved(index);
|
| - index++;
|
| - }
|
| - } else {
|
| - index = splice.index;
|
| - }
|
| -
|
| - while (index < splice.index + splice.addedCount) {
|
| - this.reportInstanceMoved(index);
|
| - index++;
|
| - }
|
| -
|
| - offset += splice.addedCount - splice.removed.length;
|
| - }
|
| -
|
| - if (offset == 0)
|
| - return;
|
| -
|
| - var length = this.instances.length;
|
| - while (index < length) {
|
| - this.reportInstanceMoved(index);
|
| - index++;
|
| - }
|
| - },
|
| -
|
| - closeInstanceBindings: function(instance) {
|
| - var bindings = instance.bindings_;
|
| - for (var i = 0; i < bindings.length; i++) {
|
| - bindings[i].close();
|
| - }
|
| - },
|
| -
|
| - unobserve: function() {
|
| - if (!this.arrayObserver)
|
| - return;
|
| -
|
| - this.arrayObserver.close();
|
| - this.arrayObserver = undefined;
|
| - },
|
| -
|
| - close: function() {
|
| - if (this.closed)
|
| - return;
|
| - this.unobserve();
|
| - for (var i = 0; i < this.instances.length; i++) {
|
| - this.closeInstanceBindings(this.instances[i]);
|
| - }
|
| -
|
| - this.instances.length = 0;
|
| - this.closeDeps();
|
| - this.templateElement_.iterator_ = undefined;
|
| - this.closed = true;
|
| - }
|
| -};
|
| -</script>
|
|
|