Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(657)

Unified Diff: sky/framework/sky-element/TemplateBinding.sky

Issue 821353003: Switch to SkyBinder (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | sky/framework/sky-element/sky-binder.sky » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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>
« no previous file with comments | « no previous file | sky/framework/sky-element/sky-binder.sky » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698