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

Unified Diff: template_binding/lib/src/template_iterator.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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 | « template_binding/lib/src/template.dart ('k') | template_binding/lib/template_binding.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: template_binding/lib/src/template_iterator.dart
diff --git a/template_binding/lib/src/template_iterator.dart b/template_binding/lib/src/template_iterator.dart
deleted file mode 100644
index 71004867b9f16fbf768fb95412d475dfe0a52348..0000000000000000000000000000000000000000
--- a/template_binding/lib/src/template_iterator.dart
+++ /dev/null
@@ -1,556 +0,0 @@
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-part of template_binding;
-
-// This code is a port of what was formerly known as Model-Driven-Views, now
-// located at:
-// https://github.com/polymer/TemplateBinding
-// https://github.com/polymer/NodeBind
-
-// TODO(jmesserly): not sure what kind of boolean conversion rules to
-// apply for template data-binding. HTML attributes are true if they're
-// present. However Dart only treats "true" as true. Since this is HTML we'll
-// use something closer to the HTML rules: null (missing) and false are false,
-// everything else is true.
-// See: https://github.com/polymer/TemplateBinding/issues/59
-bool _toBoolean(value) => null != value && false != value;
-
-// Dart note: this was added to decouple the MustacheTokens.parse function from
-// the rest of template_binding.
-_getDelegateFactory(name, node, delegate) {
- if (delegate == null) return null;
- return (pathString) => delegate.prepareBinding(pathString, name, node);
-}
-
-_InstanceBindingMap _getBindings(Node node, BindingDelegate delegate) {
- if (node is Element) {
- return _parseAttributeBindings(node, delegate);
- }
-
- if (node is Text) {
- var tokens = MustacheTokens.parse(node.text,
- _getDelegateFactory('text', node, delegate));
- if (tokens != null) return new _InstanceBindingMap(['text', tokens]);
- }
-
- return null;
-}
-
-void _addBindings(Node node, model, [BindingDelegate delegate]) {
- final bindings = _getBindings(node, delegate);
- if (bindings != null) {
- _processBindings(node, bindings, model);
- }
-
- for (var c = node.firstChild; c != null; c = c.nextNode) {
- _addBindings(c, model, delegate);
- }
-}
-
-MustacheTokens _parseWithDefault(Element element, String name,
- BindingDelegate delegate) {
-
- var v = element.attributes[name];
- if (v == '') v = '{{}}';
- return MustacheTokens.parse(v, _getDelegateFactory(name, element, delegate));
-}
-
-_InstanceBindingMap _parseAttributeBindings(Element element,
- BindingDelegate delegate) {
-
- var bindings = null;
- var ifFound = false;
- var bindFound = false;
- var isTemplateNode = isSemanticTemplate(element);
-
- element.attributes.forEach((name, value) {
- // Allow bindings expressed in attributes to be prefixed with underbars.
- // We do this to allow correct semantics for browsers that don't implement
- // <template> where certain attributes might trigger side-effects -- and
- // for IE which sanitizes certain attributes, disallowing mustache
- // replacements in their text.
- while (name[0] == '_') {
- name = name.substring(1);
- }
-
- if (isTemplateNode &&
- (name == 'bind' || name == 'if' || name == 'repeat')) {
- return;
- }
-
- var tokens = MustacheTokens.parse(value,
- _getDelegateFactory(name, element, delegate));
- if (tokens != null) {
- if (bindings == null) bindings = [];
- bindings..add(name)..add(tokens);
- }
- });
-
- if (isTemplateNode) {
- if (bindings == null) bindings = [];
- var result = new _TemplateBindingMap(bindings)
- .._if = _parseWithDefault(element, 'if', delegate)
- .._bind = _parseWithDefault(element, 'bind', delegate)
- .._repeat = _parseWithDefault(element, 'repeat', delegate);
-
- // Treat <template if> as <template bind if>
- if (result._if != null && result._bind == null && result._repeat == null) {
- result._bind = MustacheTokens.parse('{{}}',
- _getDelegateFactory('bind', element, delegate));
- }
-
- return result;
- }
-
- return bindings == null ? null : new _InstanceBindingMap(bindings);
-}
-
-_processOneTimeBinding(String name, MustacheTokens tokens, Node node, model) {
-
- if (tokens.hasOnePath) {
- var delegateFn = tokens.getPrepareBinding(0);
- var value = delegateFn != null ? delegateFn(model, node, true) :
- tokens.getPath(0).getValueFrom(model);
- return tokens.isSimplePath ? value : tokens.combinator(value);
- }
-
- // Tokens uses a striding scheme to essentially store a sequence of structs in
- // the list. See _MustacheTokens for more information.
- var values = new List(tokens.length);
- for (int i = 0; i < tokens.length; i++) {
- Function delegateFn = tokens.getPrepareBinding(i);
- values[i] = delegateFn != null ?
- delegateFn(model, node, false) :
- tokens.getPath(i).getValueFrom(model);
- }
- return tokens.combinator(values);
-}
-
-_processSinglePathBinding(String name, MustacheTokens tokens, Node node,
- model) {
- Function delegateFn = tokens.getPrepareBinding(0);
- var observer = delegateFn != null ?
- delegateFn(model, node, false) :
- new PathObserver(model, tokens.getPath(0));
-
- return tokens.isSimplePath ? observer :
- new ObserverTransform(observer, tokens.combinator);
-}
-
-_processBinding(String name, MustacheTokens tokens, Node node, model) {
- if (tokens.onlyOneTime) {
- return _processOneTimeBinding(name, tokens, node, model);
- }
- if (tokens.hasOnePath) {
- return _processSinglePathBinding(name, tokens, node, model);
- }
-
- var observer = new CompoundObserver();
-
- for (int i = 0; i < tokens.length; i++) {
- bool oneTime = tokens.getOneTime(i);
- Function delegateFn = tokens.getPrepareBinding(i);
-
- if (delegateFn != null) {
- var value = delegateFn(model, node, oneTime);
- if (oneTime) {
- observer.addPath(value);
- } else {
- observer.addObserver(value);
- }
- continue;
- }
-
- PropertyPath path = tokens.getPath(i);
- if (oneTime) {
- observer.addPath(path.getValueFrom(model));
- } else {
- observer.addPath(model, path);
- }
- }
-
- return new ObserverTransform(observer, tokens.combinator);
-}
-
-void _processBindings(Node node, _InstanceBindingMap map, model,
- [List<Bindable> instanceBindings]) {
-
- final bindings = map.bindings;
- final nodeExt = nodeBind(node);
- 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 = nodeExt.bind(name, value, oneTime: tokens.onlyOneTime);
- if (binding != null && instanceBindings != null) {
- instanceBindings.add(binding);
- }
- }
-
- nodeExt.bindFinished();
- if (map is! _TemplateBindingMap) return;
-
- final templateExt = nodeBindFallback(node);
- templateExt._model = model;
-
- var iter = templateExt._processBindingDirectives(map);
- if (iter != null && instanceBindings != null) {
- instanceBindings.add(iter);
- }
-}
-
-
-// Note: this doesn't really implement most of Bindable. See:
-// https://github.com/Polymer/TemplateBinding/issues/147
-class _TemplateIterator extends Bindable {
- final TemplateBindExtension _templateExt;
-
- final List<DocumentFragment> _instances = [];
-
- /** A copy of the last rendered [_presentValue] list state. */
- final List _iteratedValue = [];
-
- List _presentValue;
-
- bool _closed = false;
-
- // Dart note: instead of storing these in a Map like JS, or using a separate
- // object (extra memory overhead) we just inline the fields.
- var _ifValue, _value;
-
- // TODO(jmesserly): lots of booleans in this object. Bitmask?
- bool _hasIf, _hasRepeat;
- bool _ifOneTime, _oneTime;
-
- StreamSubscription _listSub;
-
- bool _initPrepareFunctions = false;
- PrepareInstanceModelFunction _instanceModelFn;
- PrepareInstancePositionChangedFunction _instancePositionChangedFn;
-
- _TemplateIterator(this._templateExt);
-
- open(callback) => throw new StateError('binding already opened');
- get value => _value;
-
- Element get _templateElement => _templateExt._node;
-
- void _closeDependencies() {
- if (_ifValue is Bindable) {
- _ifValue.close();
- _ifValue = null;
- }
- if (_value is Bindable) {
- _value.close();
- _value = null;
- }
- }
-
- void _updateDependencies(_TemplateBindingMap directives, model) {
- _closeDependencies();
-
- final template = _templateElement;
-
- _hasIf = directives._if != null;
- _hasRepeat = directives._repeat != null;
-
- var ifValue = true;
- if (_hasIf) {
- _ifOneTime = directives._if.onlyOneTime;
- _ifValue = _processBinding('if', directives._if, template, model);
- ifValue = _ifValue;
-
- // oneTime if & predicate is false. nothing else to do.
- if (_ifOneTime && !_toBoolean(ifValue)) {
- _valueChanged(null);
- return;
- }
-
- if (!_ifOneTime) {
- ifValue = (ifValue as Bindable).open(_updateIfValue);
- }
- }
-
- if (_hasRepeat) {
- _oneTime = directives._repeat.onlyOneTime;
- _value = _processBinding('repeat', directives._repeat, template, model);
- } else {
- _oneTime = directives._bind.onlyOneTime;
- _value = _processBinding('bind', directives._bind, template, model);
- }
-
- var value = _value;
- if (!_oneTime) {
- value = _value.open(_updateIteratedValue);
- }
-
- if (!_toBoolean(ifValue)) {
- _valueChanged(null);
- return;
- }
-
- _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).
- Object _getUpdatedValue() {
- var value = _value;
- // Dart note: x.discardChanges() is x.value in Dart.
- if (!_toBoolean(_oneTime)) value = value.value;
- return value;
- }
-
- void _updateIfValue(ifValue) {
- if (!_toBoolean(ifValue)) {
- _valueChanged(null);
- return;
- }
- _updateValue(_getUpdatedValue());
- }
-
- void _updateIteratedValue(value) {
- if (_hasIf) {
- var ifValue = _ifValue;
- if (!_ifOneTime) ifValue = (ifValue as Bindable).value;
- if (!_toBoolean(ifValue)) {
- _valueChanged([]);
- return;
- }
- }
-
- _updateValue(value);
- }
-
- void _updateValue(Object value) {
- if (!_hasRepeat) value = [value];
- _valueChanged(value);
- }
-
- void _valueChanged(Object value) {
- if (value is! List) {
- if (value is Iterable) {
- // Dart note: we support Iterable by calling toList.
- // But we need to be careful to observe the original iterator if it
- // supports that.
- value = (value as Iterable).toList();
- } else {
- value = [];
- }
- }
-
- if (identical(value, _iteratedValue)) return;
-
- _unobserve();
- _presentValue = value;
-
- if (value is ObservableList && _hasRepeat && !_oneTime) {
- // Make sure any pending changes aren't delivered, since we're getting
- // a snapshot at this point in time.
- value.discardListChages();
- _listSub = value.listChanges.listen(_handleSplices);
- }
-
- _handleSplices(ObservableList.calculateChangeRecords(
- _iteratedValue != null ? _iteratedValue : [],
- _presentValue != null ? _presentValue : []));
- }
-
- Node _getLastInstanceNode(int index) {
- if (index == -1) return _templateElement;
- // TODO(jmesserly): we could avoid this expando lookup by caching the
- // instance extension instead of the instance.
- var instance = _instanceExtension[_instances[index]];
- var terminator = instance._terminator;
- if (terminator == null) return _getLastInstanceNode(index - 1);
-
- if (!isSemanticTemplate(terminator) ||
- identical(terminator, _templateElement)) {
- return terminator;
- }
-
- var subtemplateIterator = templateBindFallback(terminator)._iterator;
- if (subtemplateIterator == null) return terminator;
-
- return subtemplateIterator._getLastTemplateNode();
- }
-
- Node _getLastTemplateNode() => _getLastInstanceNode(_instances.length - 1);
-
- void _insertInstanceAt(int index, DocumentFragment fragment) {
- var previousInstanceLast = _getLastInstanceNode(index - 1);
- var parent = _templateElement.parentNode;
-
- _instances.insert(index, fragment);
- parent.insertBefore(fragment, previousInstanceLast.nextNode);
- }
-
- DocumentFragment _extractInstanceAt(int index) {
- var previousInstanceLast = _getLastInstanceNode(index - 1);
- var lastNode = _getLastInstanceNode(index);
- var parent = _templateElement.parentNode;
- var instance = _instances.removeAt(index);
-
- while (lastNode != previousInstanceLast) {
- var node = previousInstanceLast.nextNode;
- if (node == lastNode) lastNode = previousInstanceLast;
-
- instance.append(node..remove());
- }
-
- return instance;
- }
-
- void _handleSplices(List<ListChangeRecord> splices) {
- if (_closed || splices.isEmpty) return;
-
- final template = _templateElement;
-
- if (template.parentNode == null) {
- close();
- return;
- }
-
- ObservableList.applyChangeRecords(_iteratedValue, _presentValue, splices);
-
- final delegate = _templateExt.bindingDelegate;
-
- // Dart note: the JavaScript code relies on the distinction between null
- // and undefined to track whether the functions are prepared. We use a bool.
- if (!_initPrepareFunctions) {
- _initPrepareFunctions = true;
- final delegate = _templateExt._self.bindingDelegate;
- if (delegate != null) {
- _instanceModelFn = delegate.prepareInstanceModel(template);
- _instancePositionChangedFn =
- delegate.prepareInstancePositionChanged(template);
- }
- }
-
- // Instance Removals.
- var instanceCache = new HashMap(equals: identical);
- var removeDelta = 0;
- for (var splice in splices) {
- for (var model in splice.removed) {
- var instance = _extractInstanceAt(splice.index + removeDelta);
- if (instance != _emptyInstance) {
- instanceCache[model] = instance;
- }
- }
-
- removeDelta -= splice.addedCount;
- }
-
- for (var splice in splices) {
- for (var addIndex = splice.index;
- addIndex < splice.index + splice.addedCount;
- addIndex++) {
-
- var model = _iteratedValue[addIndex];
- DocumentFragment instance = instanceCache.remove(model);
- if (instance == null) {
- try {
- if (_instanceModelFn != null) {
- model = _instanceModelFn(model);
- }
- if (model == null) {
- instance = _emptyInstance;
- } else {
- instance = _templateExt.createInstance(model, delegate);
- }
- } catch (e, s) {
- // Dart note: we propagate errors asynchronously here to avoid
- // disrupting the rendering flow. This is different than in the JS
- // implementation but it should probably be fixed there too. Dart
- // hits this case more because non-existing properties in
- // [PropertyPath] are treated as errors, while JS treats them as
- // null/undefined.
- // TODO(sigmund): this should be a synchronous throw when this is
- // called from createInstance, but that requires enough refactoring
- // that it should be done upstream first. See dartbug.com/17789.
- new Completer().completeError(e, s);
- instance = _emptyInstance;
- }
- }
-
- _insertInstanceAt(addIndex, instance);
- }
- }
-
- for (var instance in instanceCache.values) {
- _closeInstanceBindings(instance);
- }
-
- if (_instancePositionChangedFn != null) _reportInstancesMoved(splices);
- }
-
- void _reportInstanceMoved(int index) {
- var instance = _instances[index];
- if (instance == _emptyInstance) return;
-
- _instancePositionChangedFn(nodeBind(instance).templateInstance, index);
- }
-
- void _reportInstancesMoved(List<ListChangeRecord> splices) {
- var index = 0;
- var offset = 0;
- for (var splice in splices) {
- if (offset != 0) {
- while (index < splice.index) {
- _reportInstanceMoved(index);
- index++;
- }
- } else {
- index = splice.index;
- }
-
- while (index < splice.index + splice.addedCount) {
- _reportInstanceMoved(index);
- index++;
- }
-
- offset += splice.addedCount - splice.removed.length;
- }
-
- if (offset == 0) return;
-
- var length = _instances.length;
- while (index < length) {
- _reportInstanceMoved(index);
- index++;
- }
- }
-
- void _closeInstanceBindings(DocumentFragment instance) {
- var bindings = _instanceExtension[instance]._bindings;
- for (var binding in bindings) binding.close();
- }
-
- void _unobserve() {
- if (_listSub == null) return;
- _listSub.cancel();
- _listSub = null;
- }
-
- void close() {
- if (_closed) return;
-
- _unobserve();
- _instances.forEach(_closeInstanceBindings);
- _instances.clear();
- _closeDependencies();
- _templateExt._iterator = null;
- _closed = true;
- }
-}
-
-// Dart note: the JavaScript version just puts an expando on the array.
-class _BoundNodes {
- final List<Node> nodes;
- final List<Bindable> instanceBindings;
- _BoundNodes(this.nodes, this.instanceBindings);
-}
« no previous file with comments | « template_binding/lib/src/template.dart ('k') | template_binding/lib/template_binding.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698