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

Side by Side Diff: sdk/lib/html/dart2js/html_dart2js.dart

Issue 14634011: move top-level internal methods into a class (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | sdk/lib/html/dartium/html_dartium.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /// The Dart HTML library. 1 /// The Dart HTML library.
2 library dart.dom.html; 2 library dart.dom.html;
3 3
4 import 'dart:async'; 4 import 'dart:async';
5 import 'dart:collection'; 5 import 'dart:collection';
6 import 'dart:_collection-dev' hide Symbol; 6 import 'dart:_collection-dev' hide Symbol;
7 import 'dart:html_common'; 7 import 'dart:html_common';
8 import 'dart:indexed_db'; 8 import 'dart:indexed_db';
9 import 'dart:isolate'; 9 import 'dart:isolate';
10 import 'dart:json' as json; 10 import 'dart:json' as json;
(...skipping 7619 matching lines...) Expand 10 before | Expand all | Expand 10 after
7630 self._attributeBindings = new Map<String, StreamSubscription>(); 7630 self._attributeBindings = new Map<String, StreamSubscription>();
7631 } 7631 }
7632 7632
7633 self.xtag.attributes.remove(name); 7633 self.xtag.attributes.remove(name);
7634 7634
7635 var changed; 7635 var changed;
7636 if (name.endsWith('?')) { 7636 if (name.endsWith('?')) {
7637 name = name.substring(0, name.length - 1); 7637 name = name.substring(0, name.length - 1);
7638 7638
7639 changed = (value) { 7639 changed = (value) {
7640 if (_templateBooleanConversion(value)) { 7640 if (_Bindings._toBoolean(value)) {
7641 self.xtag.attributes[name] = ''; 7641 self.xtag.attributes[name] = '';
7642 } else { 7642 } else {
7643 self.xtag.attributes.remove(name); 7643 self.xtag.attributes.remove(name);
7644 } 7644 }
7645 }; 7645 };
7646 } else { 7646 } else {
7647 changed = (value) { 7647 changed = (value) {
7648 // TODO(jmesserly): escape value if needed to protect against XSS. 7648 // TODO(jmesserly): escape value if needed to protect against XSS.
7649 // See https://github.com/toolkitchen/mdv/issues/58 7649 // See https://github.com/toolkitchen/mdv/issues/58
7650 self.xtag.attributes[name] = value == null ? '' : '$value'; 7650 self.xtag.attributes[name] = value == null ? '' : '$value';
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
7763 * Creates an instance of the template. 7763 * Creates an instance of the template.
7764 * This is only supported if [isTemplate] is true. 7764 * This is only supported if [isTemplate] is true.
7765 */ 7765 */
7766 @Experimental 7766 @Experimental
7767 DocumentFragment createInstance() { 7767 DocumentFragment createInstance() {
7768 _ensureTemplate(); 7768 _ensureTemplate();
7769 7769
7770 var template = ref; 7770 var template = ref;
7771 if (template == null) template = this; 7771 if (template == null) template = this;
7772 7772
7773 var instance = _createDeepCloneAndDecorateTemplates(template.content, 7773 var instance = _Bindings._createDeepCloneAndDecorateTemplates(
7774 attributes['syntax']); 7774 template.content, attributes['syntax']);
7775 7775
7776 if (TemplateElement._instanceCreated != null) { 7776 if (TemplateElement._instanceCreated != null) {
7777 TemplateElement._instanceCreated.add(instance); 7777 TemplateElement._instanceCreated.add(instance);
7778 } 7778 }
7779 return instance; 7779 return instance;
7780 } 7780 }
7781 7781
7782 /** 7782 /**
7783 * The data model which is inherited through the tree. 7783 * The data model which is inherited through the tree.
7784 * This is only supported if [isTemplate] is true. 7784 * This is only supported if [isTemplate] is true.
7785 * 7785 *
7786 * Setting this will destructive propagate the value to all descendant nodes, 7786 * Setting this will destructive propagate the value to all descendant nodes,
7787 * and reinstantiate all of the nodes expanded by this template. 7787 * and reinstantiate all of the nodes expanded by this template.
7788 * 7788 *
7789 * Currently this does not support propagation through Shadow DOMs. 7789 * Currently this does not support propagation through Shadow DOMs.
7790 */ 7790 */
7791 @Experimental 7791 @Experimental
7792 get model => _model; 7792 get model => _model;
7793 7793
7794 @Experimental 7794 @Experimental
7795 void set model(value) { 7795 void set model(value) {
7796 _ensureTemplate(); 7796 _ensureTemplate();
7797 7797
7798 var syntax = TemplateElement.syntax[attributes['syntax']]; 7798 var syntax = TemplateElement.syntax[attributes['syntax']];
7799 _model = value; 7799 _model = value;
7800 _addBindings(this, model, syntax); 7800 _Bindings._addBindings(this, model, syntax);
7801 } 7801 }
7802 7802
7803 // TODO(jmesserly): const set would be better 7803 // TODO(jmesserly): const set would be better
7804 static const _TABLE_TAGS = const { 7804 static const _TABLE_TAGS = const {
7805 'caption': null, 7805 'caption': null,
7806 'col': null, 7806 'col': null,
7807 'colgroup': null, 7807 'colgroup': null,
7808 'tbody': null, 7808 'tbody': null,
7809 'td': null, 7809 'td': null,
7810 'tfoot': null, 7810 'tfoot': null,
(...skipping 11513 matching lines...) Expand 10 before | Expand all | Expand 10 after
19324 static bool decorate(Element template, [Element instanceRef]) { 19324 static bool decorate(Element template, [Element instanceRef]) {
19325 // == true check because it starts as a null field. 19325 // == true check because it starts as a null field.
19326 if (template._templateIsDecorated == true) return false; 19326 if (template._templateIsDecorated == true) return false;
19327 19327
19328 template._templateIsDecorated = true; 19328 template._templateIsDecorated = true;
19329 19329
19330 _injectStylesheet(); 19330 _injectStylesheet();
19331 19331
19332 // Create content 19332 // Create content
19333 if (template is! TemplateElement) { 19333 if (template is! TemplateElement) {
19334 var doc = _getTemplateContentsOwner(template.document); 19334 var doc = _Bindings._getTemplateContentsOwner(template.document);
19335 template._templateContent = doc.createDocumentFragment(); 19335 template._templateContent = doc.createDocumentFragment();
19336 } 19336 }
19337 19337
19338 if (instanceRef != null) { 19338 if (instanceRef != null) {
19339 template._templateInstanceRef = instanceRef; 19339 template._templateInstanceRef = instanceRef;
19340 return true; // content is empty. 19340 return true; // content is empty.
19341 } 19341 }
19342 19342
19343 if (template is TemplateElement) { 19343 if (template is TemplateElement) {
19344 _bootstrapTemplatesRecursivelyFrom(template.content); 19344 bootstrap(template.content);
19345 } else { 19345 } else {
19346 _liftNonNativeTemplateChildrenIntoContent(template); 19346 _Bindings._liftNonNativeChildrenIntoContent(template);
19347 } 19347 }
19348 19348
19349 return true; 19349 return true;
19350 } 19350 }
19351 19351
19352 /** 19352 /**
19353 * This used to decorate recursively all templates from a given node. 19353 * This used to decorate recursively all templates from a given node.
19354 * 19354 *
19355 * By default [decorate] will be called on templates lazily when certain 19355 * By default [decorate] will be called on templates lazily when certain
19356 * properties such as [model] are accessed, but it can be run eagerly to 19356 * properties such as [model] are accessed, but it can be run eagerly to
19357 * decorate an entire tree recursively. 19357 * decorate an entire tree recursively.
19358 */ 19358 */
19359 // TODO(rafaelw): Review whether this is the right public API. 19359 // TODO(rafaelw): Review whether this is the right public API.
19360 @Experimental 19360 @Experimental
19361 static void bootstrap(Node content) { 19361 static void bootstrap(Node content) {
19362 _bootstrapTemplatesRecursivelyFrom(content); 19362 _Bindings._bootstrapTemplatesRecursivelyFrom(content);
19363 } 19363 }
19364 19364
19365 static bool _initStyles; 19365 static bool _initStyles;
19366 19366
19367 static void _injectStylesheet() { 19367 static void _injectStylesheet() {
19368 if (_initStyles == true) return; 19368 if (_initStyles == true) return;
19369 _initStyles = true; 19369 _initStyles = true;
19370 19370
19371 var style = new StyleElement(); 19371 var style = new StyleElement();
19372 style.text = r''' 19372 style.text = r'''
(...skipping 6831 matching lines...) Expand 10 before | Expand all | Expand 10 after
26204 binding.cancel(); 26204 binding.cancel();
26205 } 26205 }
26206 _bindings.clear(); 26206 _bindings.clear();
26207 _values.clear(); 26207 _values.clear();
26208 26208
26209 _disposed = true; 26209 _disposed = true;
26210 value = null; 26210 value = null;
26211 } 26211 }
26212 } 26212 }
26213 26213
26214 Stream<Event> _getStreamForInputType(InputElement element) {
26215 switch (element.type) {
26216 case 'checkbox':
26217 return element.onClick;
26218 case 'radio':
26219 case 'select-multiple':
26220 case 'select-one':
26221 return element.onChange;
26222 default:
26223 return element.onInput;
26224 }
26225 }
26226
26227 abstract class _InputBinding { 26214 abstract class _InputBinding {
26228 final InputElement element; 26215 final InputElement element;
26229 PathObserver binding; 26216 PathObserver binding;
26230 StreamSubscription _pathSub; 26217 StreamSubscription _pathSub;
26231 StreamSubscription _eventSub; 26218 StreamSubscription _eventSub;
26232 26219
26233 _InputBinding(this.element, model, String path) { 26220 _InputBinding(this.element, model, String path) {
26234 binding = new PathObserver(model, path); 26221 binding = new PathObserver(model, path);
26235 _pathSub = binding.bindSync(valueChanged); 26222 _pathSub = binding.bindSync(valueChanged);
26236 _eventSub = _getStreamForInputType(element).listen(updateBinding); 26223 _eventSub = _getStreamForInputType(element).listen(updateBinding);
26237 } 26224 }
26238 26225
26239 void valueChanged(newValue); 26226 void valueChanged(newValue);
26240 26227
26241 void updateBinding(e); 26228 void updateBinding(e);
26242 26229
26243 void unbind() { 26230 void unbind() {
26244 binding = null; 26231 binding = null;
26245 _pathSub.cancel(); 26232 _pathSub.cancel();
26246 _eventSub.cancel(); 26233 _eventSub.cancel();
26247 } 26234 }
26235
26236
26237 static Stream<Event> _getStreamForInputType(InputElement element) {
26238 switch (element.type) {
26239 case 'checkbox':
26240 return element.onClick;
26241 case 'radio':
26242 case 'select-multiple':
26243 case 'select-one':
26244 return element.onChange;
26245 default:
26246 return element.onInput;
26247 }
26248 }
26248 } 26249 }
26249 26250
26250 class _ValueBinding extends _InputBinding { 26251 class _ValueBinding extends _InputBinding {
26251 _ValueBinding(element, model, path) : super(element, model, path); 26252 _ValueBinding(element, model, path) : super(element, model, path);
26252 26253
26253 void valueChanged(value) { 26254 void valueChanged(value) {
26254 element.value = value == null ? '' : '$value'; 26255 element.value = value == null ? '' : '$value';
26255 } 26256 }
26256 26257
26257 void updateBinding(e) { 26258 void updateBinding(e) {
26258 binding.value = element.value; 26259 binding.value = element.value;
26259 } 26260 }
26260 } 26261 }
26261 26262
26262 // TODO(jmesserly): not sure what kind of boolean conversion rules to
26263 // apply for template data-binding. HTML attributes are true if they're present.
26264 // However Dart only treats "true" as true. Since this is HTML we'll use
26265 // something closer to the HTML rules: null (missing) and false are false,
26266 // everything else is true. See: https://github.com/toolkitchen/mdv/issues/59
26267 bool _templateBooleanConversion(value) => null != value && false != value;
26268
26269 class _CheckedBinding extends _InputBinding { 26263 class _CheckedBinding extends _InputBinding {
26270 _CheckedBinding(element, model, path) : super(element, model, path); 26264 _CheckedBinding(element, model, path) : super(element, model, path);
26271 26265
26272 void valueChanged(value) { 26266 void valueChanged(value) {
26273 element.checked = _templateBooleanConversion(value); 26267 element.checked = _Bindings._toBoolean(value);
26274 } 26268 }
26275 26269
26276 void updateBinding(e) { 26270 void updateBinding(e) {
26277 binding.value = element.checked; 26271 binding.value = element.checked;
26278 26272
26279 // Only the radio button that is getting checked gets an event. We 26273 // Only the radio button that is getting checked gets an event. We
26280 // therefore find all the associated radio buttons and update their 26274 // therefore find all the associated radio buttons and update their
26281 // CheckedBinding manually. 26275 // CheckedBinding manually.
26282 if (element is InputElement && element.type == 'radio') { 26276 if (element is InputElement && element.type == 'radio') {
26283 for (var r in _getAssociatedRadioButtons(element)) { 26277 for (var r in _getAssociatedRadioButtons(element)) {
26284 var checkedBinding = r._checkedBinding; 26278 var checkedBinding = r._checkedBinding;
26285 if (checkedBinding != null) { 26279 if (checkedBinding != null) {
26286 // Set the value directly to avoid an infinite call stack. 26280 // Set the value directly to avoid an infinite call stack.
26287 checkedBinding.binding.value = false; 26281 checkedBinding.binding.value = false;
26288 } 26282 }
26289 } 26283 }
26290 } 26284 }
26291 } 26285 }
26286
26287 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
26288 // Returns an array containing all radio buttons other than |element| that
26289 // have the same |name|, either in the form that |element| belongs to or,
26290 // if no form, in the document tree to which |element| belongs.
26291 //
26292 // This implementation is based upon the HTML spec definition of a
26293 // "radio button group":
26294 // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state. html#radio-button-group
26295 //
26296 static Iterable _getAssociatedRadioButtons(element) {
26297 if (!_isNodeInDocument(element)) return [];
26298 if (element.form != null) {
26299 return element.form.nodes.where((el) {
26300 return el != element &&
26301 el is InputElement &&
26302 el.type == 'radio' &&
26303 el.name == element.name;
26304 });
26305 } else {
26306 var radios = element.document.queryAll(
26307 'input[type="radio"][name="${element.name}"]');
26308 return radios.where((el) => el != element && el.form == null);
26309 }
26310 }
26311
26312 // TODO(jmesserly): polyfill document.contains API instead of doing it here
26313 static bool _isNodeInDocument(Node node) {
26314 // On non-IE this works:
26315 // return node.document.contains(node);
26316 var document = node.document;
26317 if (node == document || node.parentNode == document) return true;
26318 return document.documentElement.contains(node);
26319 }
26292 } 26320 }
26293 26321
26294 // TODO(jmesserly): polyfill document.contains API instead of doing it here 26322 class _Bindings {
26295 bool _isNodeInDocument(Node node) { 26323 // TODO(jmesserly): not sure what kind of boolean conversion rules to
26296 // On non-IE this works: 26324 // apply for template data-binding. HTML attributes are true if they're
26297 // return node.document.contains(node); 26325 // present. However Dart only treats "true" as true. Since this is HTML we'll
26298 var document = node.document; 26326 // use something closer to the HTML rules: null (missing) and false are false,
26299 if (node == document || node.parentNode == document) return true; 26327 // everything else is true. See: https://github.com/toolkitchen/mdv/issues/59
26300 return document.documentElement.contains(node); 26328 static bool _toBoolean(value) => null != value && false != value;
26301 } 26329
26302 26330 static Node _createDeepCloneAndDecorateTemplates(Node node, String syntax) {
26303 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'. 26331 var clone = node.clone(false); // Shallow clone.
26304 // Returns an array containing all radio buttons other than |element| that 26332 if (clone is Element && clone.isTemplate) {
26305 // have the same |name|, either in the form that |element| belongs to or, 26333 TemplateElement.decorate(clone, node);
26306 // if no form, in the document tree to which |element| belongs. 26334 if (syntax != null) {
26307 // 26335 clone.attributes.putIfAbsent('syntax', () => syntax);
26308 // This implementation is based upon the HTML spec definition of a 26336 }
26309 // "radio button group": 26337 }
26310 // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.ht ml#radio-button-group 26338
26311 // 26339 for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
26312 Iterable _getAssociatedRadioButtons(element) { 26340 clone.append(_createDeepCloneAndDecorateTemplates(c, syntax));
26313 if (!_isNodeInDocument(element)) return []; 26341 }
26314 if (element.form != null) { 26342 return clone;
26315 return element.form.nodes.where((el) { 26343 }
26316 return el != element && 26344
26317 el is InputElement && 26345 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
26318 el.type == 'radio' && 26346 static Document _getTemplateContentsOwner(Document doc) {
26319 el.name == element.name; 26347 if (doc.window == null) {
26320 }); 26348 return doc;
26321 } else { 26349 }
26322 var radios = element.document.queryAll( 26350 var d = doc._templateContentsOwner;
26323 'input[type="radio"][name="${element.name}"]'); 26351 if (d == null) {
26324 return radios.where((el) => el != element && el.form == null); 26352 // TODO(arv): This should either be a Document or HTMLDocument depending
26325 } 26353 // on doc.
26326 } 26354 d = doc.implementation.createHtmlDocument('');
26327 26355 while (d.$dom_lastChild != null) {
26328 Node _createDeepCloneAndDecorateTemplates(Node node, String syntax) { 26356 d.$dom_lastChild.remove();
26329 var clone = node.clone(false); // Shallow clone. 26357 }
26330 if (clone is Element && clone.isTemplate) { 26358 doc._templateContentsOwner = d;
26331 TemplateElement.decorate(clone, node); 26359 }
26332 if (syntax != null) { 26360 return d;
26333 clone.attributes.putIfAbsent('syntax', () => syntax); 26361 }
26334 } 26362
26335 } 26363 static Element _cloneAndSeperateAttributeTemplate(Element templateElement) {
26336 26364 var clone = templateElement.clone(false);
26337 for (var c = node.$dom_firstChild; c != null; c = c.nextNode) { 26365 var attributes = templateElement.attributes;
26338 clone.append(_createDeepCloneAndDecorateTemplates(c, syntax)); 26366 for (var name in attributes.keys.toList()) {
26339 } 26367 switch (name) {
26340 return clone; 26368 case 'template':
26341 } 26369 case 'repeat':
26342 26370 case 'bind':
26343 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#df n-template-contents-owner 26371 case 'ref':
26344 Document _getTemplateContentsOwner(Document doc) { 26372 clone.attributes.remove(name);
26345 if (doc.window == null) { 26373 break;
26346 return doc; 26374 default:
26347 } 26375 attributes.remove(name);
26348 var d = doc._templateContentsOwner; 26376 break;
26349 if (d == null) { 26377 }
26350 // TODO(arv): This should either be a Document or HTMLDocument depending 26378 }
26351 // on doc. 26379
26352 d = doc.implementation.createHtmlDocument(''); 26380 return clone;
26353 while (d.$dom_lastChild != null) { 26381 }
26354 d.$dom_lastChild.remove(); 26382
26355 } 26383 static void _liftNonNativeChildrenIntoContent(Element templateElement) {
26356 doc._templateContentsOwner = d; 26384 var content = templateElement.content;
26357 } 26385
26358 return d; 26386 if (!templateElement._isAttributeTemplate) {
26359 } 26387 var child;
26360 26388 while ((child = templateElement.$dom_firstChild) != null) {
26361 Element _cloneAndSeperateAttributeTemplate(Element templateElement) { 26389 content.append(child);
26362 var clone = templateElement.clone(false); 26390 }
26363 var attributes = templateElement.attributes; 26391 return;
26364 for (var name in attributes.keys.toList()) { 26392 }
26365 switch (name) { 26393
26366 case 'template': 26394 // For attribute templates we copy the whole thing into the content and
26367 case 'repeat': 26395 // we move the non template attributes into the content.
26368 case 'bind': 26396 //
26369 case 'ref': 26397 // <tr foo template>
26370 clone.attributes.remove(name); 26398 //
26371 break; 26399 // becomes
26372 default: 26400 //
26373 attributes.remove(name); 26401 // <tr template>
26374 break; 26402 // + #document-fragment
26375 } 26403 // + <tr foo>
26376 } 26404 //
26377 26405 var newRoot = _cloneAndSeperateAttributeTemplate(templateElement);
26378 return clone;
26379 }
26380
26381 void _liftNonNativeTemplateChildrenIntoContent(Element templateElement) {
26382 var content = templateElement.content;
26383
26384 if (!templateElement._isAttributeTemplate) {
26385 var child; 26406 var child;
26386 while ((child = templateElement.$dom_firstChild) != null) { 26407 while ((child = templateElement.$dom_firstChild) != null) {
26387 content.append(child); 26408 newRoot.append(child);
26388 } 26409 }
26389 return; 26410 content.append(newRoot);
26390 } 26411 }
26391 26412
26392 // For attribute templates we copy the whole thing into the content and 26413 static void _bootstrapTemplatesRecursivelyFrom(Node node) {
26393 // we move the non template attributes into the content. 26414 void bootstrap(template) {
26394 // 26415 if (!TemplateElement.decorate(template)) {
26395 // <tr foo template> 26416 _bootstrapTemplatesRecursivelyFrom(template.content);
26396 // 26417 }
26397 // becomes 26418 }
26398 // 26419
26399 // <tr template> 26420 // Need to do this first as the contents may get lifted if |node| is
26400 // + #document-fragment 26421 // template.
26401 // + <tr foo> 26422 // TODO(jmesserly): node is DocumentFragment or Element
26402 // 26423 var descendents = (node as dynamic).queryAll(_allTemplatesSelectors);
26403 var newRoot = _cloneAndSeperateAttributeTemplate(templateElement); 26424 if (node is Element && node.isTemplate) bootstrap(node);
26404 var child; 26425
26405 while ((child = templateElement.$dom_firstChild) != null) { 26426 descendents.forEach(bootstrap);
26406 newRoot.append(child); 26427 }
26407 } 26428
26408 content.append(newRoot); 26429 static final String _allTemplatesSelectors = 'template, option[template], ' +
26409 } 26430 Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", ");
26410 26431
26411 void _bootstrapTemplatesRecursivelyFrom(Node node) { 26432 static void _addBindings(Node node, model, [CustomBindingSyntax syntax]) {
26412 void bootstrap(template) { 26433 if (node is Element) {
26413 if (!TemplateElement.decorate(template)) { 26434 _addAttributeBindings(node, model, syntax);
26414 _bootstrapTemplatesRecursivelyFrom(template.content); 26435 } else if (node is Text) {
26415 } 26436 _parseAndBind(node, 'text', node.text, model, syntax);
26416 } 26437 }
26417 26438
26418 // Need to do this first as the contents may get lifted if |node| is 26439 for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
26419 // template. 26440 _addBindings(c, model, syntax);
26420 // TODO(jmesserly): node is DocumentFragment or Element 26441 }
26421 var templateDescendents = (node as dynamic).queryAll(_allTemplatesSelectors); 26442 }
26422 if (node is Element && node.isTemplate) bootstrap(node); 26443
26423 26444 static void _addAttributeBindings(Element element, model, syntax) {
26424 templateDescendents.forEach(bootstrap); 26445 element.attributes.forEach((name, value) {
26425 } 26446 if (value == '' && (name == 'bind' || name == 'repeat')) {
26426 26447 value = '{{}}';
26427 final String _allTemplatesSelectors = 'template, option[template], ' + 26448 }
26428 Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", "); 26449 _parseAndBind(element, name, value, model, syntax);
26429 26450 });
26430 void _addBindings(Node node, model, [CustomBindingSyntax syntax]) { 26451 }
26431 if (node is Element) { 26452
26432 _addAttributeBindings(node, model, syntax); 26453 static void _parseAndBind(Node node, String name, String text, model,
26433 } else if (node is Text) { 26454 CustomBindingSyntax syntax) {
26434 _parseAndBind(node, 'text', node.text, model, syntax); 26455
26435 } 26456 var tokens = _parseMustacheTokens(text);
26436 26457 if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) {
26437 for (var c = node.$dom_firstChild; c != null; c = c.nextNode) { 26458 return;
26438 _addBindings(c, model, syntax); 26459 }
26439 } 26460
26440 } 26461 if (tokens.length == 1 && tokens[0].isBinding) {
26441 26462 _bindOrDelegate(node, name, model, tokens[0].value, syntax);
26442 26463 return;
26443 void _addAttributeBindings(Element element, model, syntax) { 26464 }
26444 element.attributes.forEach((name, value) { 26465
26445 if (value == '' && (name == 'bind' || name == 'repeat')) { 26466 var replacementBinding = new CompoundBinding();
26446 value = '{{}}';
26447 }
26448 _parseAndBind(element, name, value, model, syntax);
26449 });
26450 }
26451
26452 void _parseAndBind(Node node, String name, String text, model,
26453 CustomBindingSyntax syntax) {
26454
26455 var tokens = _parseMustacheTokens(text);
26456 if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) {
26457 return;
26458 }
26459
26460 if (tokens.length == 1 && tokens[0].isBinding) {
26461 _bindOrDelegate(node, name, model, tokens[0].value, syntax);
26462 return;
26463 }
26464
26465 var replacementBinding = new CompoundBinding();
26466 for (var i = 0; i < tokens.length; i++) {
26467 var token = tokens[i];
26468 if (token.isBinding) {
26469 _bindOrDelegate(replacementBinding, i, model, token.value, syntax);
26470 }
26471 }
26472
26473 replacementBinding.combinator = (values) {
26474 var newValue = new StringBuffer();
26475
26476 for (var i = 0; i < tokens.length; i++) { 26467 for (var i = 0; i < tokens.length; i++) {
26477 var token = tokens[i]; 26468 var token = tokens[i];
26478 if (token.isText) { 26469 if (token.isBinding) {
26479 newValue.write(token.value); 26470 _bindOrDelegate(replacementBinding, i, model, token.value, syntax);
26471 }
26472 }
26473
26474 replacementBinding.combinator = (values) {
26475 var newValue = new StringBuffer();
26476
26477 for (var i = 0; i < tokens.length; i++) {
26478 var token = tokens[i];
26479 if (token.isText) {
26480 newValue.write(token.value);
26481 } else {
26482 var value = values[i];
26483 if (value != null) {
26484 newValue.write(value);
26485 }
26486 }
26487 }
26488
26489 return newValue.toString();
26490 };
26491
26492 _nodeOrCustom(node).bind(name, replacementBinding, 'value');
26493 }
26494
26495 static void _bindOrDelegate(node, name, model, String path,
26496 CustomBindingSyntax syntax) {
26497
26498 if (syntax != null) {
26499 var delegateBinding = syntax.getBinding(model, path, name, node);
26500 if (delegateBinding != null) {
26501 model = delegateBinding;
26502 path = 'value';
26503 }
26504 }
26505
26506 _nodeOrCustom(node).bind(name, model, path);
26507 }
26508
26509 /**
26510 * Gets the [node]'s custom [Element.xtag] if present, otherwise returns
26511 * the node. This is used so nodes can override [Node.bind], [Node.unbind],
26512 * and [Node.unbindAll] like InputElement does.
26513 */
26514 // TODO(jmesserly): remove this when we can extend Element for real.
26515 static _nodeOrCustom(node) => node is Element ? node.xtag : node;
26516
26517 static List<_BindingToken> _parseMustacheTokens(String s) {
26518 var result = [];
26519 var length = s.length;
26520 var index = 0, lastIndex = 0;
26521 while (lastIndex < length) {
26522 index = s.indexOf('{{', lastIndex);
26523 if (index < 0) {
26524 result.add(new _BindingToken(s.substring(lastIndex)));
26525 break;
26480 } else { 26526 } else {
26481 var value = values[i]; 26527 // There is a non-empty text run before the next path token.
26482 if (value != null) { 26528 if (index > 0 && lastIndex < index) {
26483 newValue.write(value); 26529 result.add(new _BindingToken(s.substring(lastIndex, index)));
26484 } 26530 }
26485 } 26531 lastIndex = index + 2;
26486 } 26532 index = s.indexOf('}}', lastIndex);
26487 26533 if (index < 0) {
26488 return newValue.toString(); 26534 var text = s.substring(lastIndex - 2);
26489 }; 26535 if (result.length > 0 && result.last.isText) {
26490 26536 result.last.value += text;
26491 _nodeOrCustom(node).bind(name, replacementBinding, 'value'); 26537 } else {
26538 result.add(new _BindingToken(text));
26539 }
26540 break;
26541 }
26542
26543 var value = s.substring(lastIndex, index).trim();
26544 result.add(new _BindingToken(value, isBinding: true));
26545 lastIndex = index + 2;
26546 }
26547 }
26548 return result;
26549 }
26550
26551 static void _addTemplateInstanceRecord(fragment, model) {
26552 if (fragment.$dom_firstChild == null) {
26553 return;
26554 }
26555
26556 var instanceRecord = new TemplateInstance(
26557 fragment.$dom_firstChild, fragment.$dom_lastChild, model);
26558
26559 var node = instanceRecord.firstNode;
26560 while (node != null) {
26561 node._templateInstance = instanceRecord;
26562 node = node.nextNode;
26563 }
26564 }
26565
26566 static void _removeAllBindingsRecursively(Node node) {
26567 _nodeOrCustom(node).unbindAll();
26568 for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
26569 _removeAllBindingsRecursively(c);
26570 }
26571 }
26572
26573 static void _removeChild(Node parent, Node child) {
26574 child._templateInstance = null;
26575 if (child is Element && child.isTemplate) {
26576 // Make sure we stop observing when we remove an element.
26577 var templateIterator = child._templateIterator;
26578 if (templateIterator != null) {
26579 templateIterator.abandon();
26580 child._templateIterator = null;
26581 }
26582 }
26583 child.remove();
26584 _removeAllBindingsRecursively(child);
26585 }
26492 } 26586 }
26493 26587
26494 void _bindOrDelegate(node, name, model, String path,
26495 CustomBindingSyntax syntax) {
26496
26497 if (syntax != null) {
26498 var delegateBinding = syntax.getBinding(model, path, name, node);
26499 if (delegateBinding != null) {
26500 model = delegateBinding;
26501 path = 'value';
26502 }
26503 }
26504
26505 _nodeOrCustom(node).bind(name, model, path);
26506 }
26507
26508 /**
26509 * Gets the [node]'s custom [Element.xtag] if present, otherwise returns
26510 * the node. This is used so nodes can override [Node.bind], [Node.unbind],
26511 * and [Node.unbindAll] like InputElement does.
26512 */
26513 // TODO(jmesserly): remove this when we can extend Element for real.
26514 _nodeOrCustom(node) => node is Element ? node.xtag : node;
26515
26516 class _BindingToken { 26588 class _BindingToken {
26517 final String value; 26589 final String value;
26518 final bool isBinding; 26590 final bool isBinding;
26519 26591
26520 _BindingToken(this.value, {this.isBinding: false}); 26592 _BindingToken(this.value, {this.isBinding: false});
26521 26593
26522 bool get isText => !isBinding; 26594 bool get isText => !isBinding;
26523 } 26595 }
26524 26596
26525 List<_BindingToken> _parseMustacheTokens(String s) {
26526 var result = [];
26527 var length = s.length;
26528 var index = 0, lastIndex = 0;
26529 while (lastIndex < length) {
26530 index = s.indexOf('{{', lastIndex);
26531 if (index < 0) {
26532 result.add(new _BindingToken(s.substring(lastIndex)));
26533 break;
26534 } else {
26535 // There is a non-empty text run before the next path token.
26536 if (index > 0 && lastIndex < index) {
26537 result.add(new _BindingToken(s.substring(lastIndex, index)));
26538 }
26539 lastIndex = index + 2;
26540 index = s.indexOf('}}', lastIndex);
26541 if (index < 0) {
26542 var text = s.substring(lastIndex - 2);
26543 if (result.length > 0 && result.last.isText) {
26544 result.last.value += text;
26545 } else {
26546 result.add(new _BindingToken(text));
26547 }
26548 break;
26549 }
26550
26551 var value = s.substring(lastIndex, index).trim();
26552 result.add(new _BindingToken(value, isBinding: true));
26553 lastIndex = index + 2;
26554 }
26555 }
26556 return result;
26557 }
26558
26559 void _addTemplateInstanceRecord(fragment, model) {
26560 if (fragment.$dom_firstChild == null) {
26561 return;
26562 }
26563
26564 var instanceRecord = new TemplateInstance(
26565 fragment.$dom_firstChild, fragment.$dom_lastChild, model);
26566
26567 var node = instanceRecord.firstNode;
26568 while (node != null) {
26569 node._templateInstance = instanceRecord;
26570 node = node.nextNode;
26571 }
26572 }
26573
26574 void _removeAllBindingsRecursively(Node node) {
26575 _nodeOrCustom(node).unbindAll();
26576 for (var c = node.$dom_firstChild; c != null; c = c.nextNode) {
26577 _removeAllBindingsRecursively(c);
26578 }
26579 }
26580
26581 void _removeTemplateChild(Node parent, Node child) {
26582 child._templateInstance = null;
26583 if (child is Element && child.isTemplate) {
26584 // Make sure we stop observing when we remove an element.
26585 var templateIterator = child._templateIterator;
26586 if (templateIterator != null) {
26587 templateIterator.abandon();
26588 child._templateIterator = null;
26589 }
26590 }
26591 child.remove();
26592 _removeAllBindingsRecursively(child);
26593 }
26594
26595
26596 class _TemplateIterator { 26597 class _TemplateIterator {
26597 final Element _templateElement; 26598 final Element _templateElement;
26598 final List<Node> terminators = []; 26599 final List<Node> terminators = [];
26599 final CompoundBinding inputs; 26600 final CompoundBinding inputs;
26600 List iteratedValue; 26601 List iteratedValue;
26601 26602
26602 StreamSubscription _sub; 26603 StreamSubscription _sub;
26603 StreamSubscription _valueBinding; 26604 StreamSubscription _valueBinding;
26604 26605
26605 _TemplateIterator(this._templateElement) 26606 _TemplateIterator(this._templateElement)
26606 : inputs = new CompoundBinding(resolveInputs) { 26607 : inputs = new CompoundBinding(resolveInputs) {
26607 26608
26608 _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged); 26609 _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged);
26609 } 26610 }
26610 26611
26611 static Object resolveInputs(Map values) { 26612 static Object resolveInputs(Map values) {
26612 if (values.containsKey('if') && !_templateBooleanConversion(values['if'])) { 26613 if (values.containsKey('if') && !_Bindings._toBoolean(values['if'])) {
26613 return null; 26614 return null;
26614 } 26615 }
26615 26616
26616 if (values.containsKey('repeat')) { 26617 if (values.containsKey('repeat')) {
26617 return values['repeat']; 26618 return values['repeat'];
26618 } 26619 }
26619 26620
26620 if (values.containsKey('bind')) { 26621 if (values.containsKey('bind')) {
26621 return [values['bind']]; 26622 return [values['bind']];
26622 } 26623 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
26663 26664
26664 void removeInstanceAt(int index) { 26665 void removeInstanceAt(int index) {
26665 var previousTerminator = getTerminatorAt(index - 1); 26666 var previousTerminator = getTerminatorAt(index - 1);
26666 var terminator = getTerminatorAt(index); 26667 var terminator = getTerminatorAt(index);
26667 terminators.removeAt(index); 26668 terminators.removeAt(index);
26668 26669
26669 var parent = _templateElement.parentNode; 26670 var parent = _templateElement.parentNode;
26670 while (terminator != previousTerminator) { 26671 while (terminator != previousTerminator) {
26671 var node = terminator; 26672 var node = terminator;
26672 terminator = node.previousNode; 26673 terminator = node.previousNode;
26673 _removeTemplateChild(parent, node); 26674 _Bindings._removeChild(parent, node);
26674 } 26675 }
26675 } 26676 }
26676 26677
26677 void removeAllInstances() { 26678 void removeAllInstances() {
26678 if (terminators.length == 0) return; 26679 if (terminators.length == 0) return;
26679 26680
26680 var previousTerminator = _templateElement; 26681 var previousTerminator = _templateElement;
26681 var terminator = getTerminatorAt(terminators.length - 1); 26682 var terminator = getTerminatorAt(terminators.length - 1);
26682 terminators.length = 0; 26683 terminators.length = 0;
26683 26684
26684 var parent = _templateElement.parentNode; 26685 var parent = _templateElement.parentNode;
26685 while (terminator != previousTerminator) { 26686 while (terminator != previousTerminator) {
26686 var node = terminator; 26687 var node = terminator;
26687 terminator = node.previousNode; 26688 terminator = node.previousNode;
26688 _removeTemplateChild(parent, node); 26689 _Bindings._removeChild(parent, node);
26689 } 26690 }
26690 } 26691 }
26691 26692
26692 void clear() { 26693 void clear() {
26693 unobserve(); 26694 unobserve();
26694 removeAllInstances(); 26695 removeAllInstances();
26695 iteratedValue = null; 26696 iteratedValue = null;
26696 } 26697 }
26697 26698
26698 getInstanceModel(model, syntax) { 26699 getInstanceModel(model, syntax) {
(...skipping 21 matching lines...) Expand all
26720 } 26721 }
26721 26722
26722 for (var addIndex = splice.index; 26723 for (var addIndex = splice.index;
26723 addIndex < splice.index + splice.addedCount; 26724 addIndex < splice.index + splice.addedCount;
26724 addIndex++) { 26725 addIndex++) {
26725 26726
26726 var model = getInstanceModel(iteratedValue[addIndex], syntax); 26727 var model = getInstanceModel(iteratedValue[addIndex], syntax);
26727 26728
26728 var fragment = getInstanceFragment(syntax); 26729 var fragment = getInstanceFragment(syntax);
26729 26730
26730 _addBindings(fragment, model, syntax); 26731 _Bindings._addBindings(fragment, model, syntax);
26731 _addTemplateInstanceRecord(fragment, model); 26732 _Bindings._addTemplateInstanceRecord(fragment, model);
26732 26733
26733 insertInstanceAt(addIndex, fragment); 26734 insertInstanceAt(addIndex, fragment);
26734 } 26735 }
26735 } 26736 }
26736 } 26737 }
26737 26738
26738 void unobserve() { 26739 void unobserve() {
26739 if (_sub == null) return; 26740 if (_sub == null) return;
26740 _sub.cancel(); 26741 _sub.cancel();
26741 _sub = null; 26742 _sub = null;
(...skipping 1226 matching lines...) Expand 10 before | Expand all | Expand 10 after
27968 _position = nextPosition; 27969 _position = nextPosition;
27969 return true; 27970 return true;
27970 } 27971 }
27971 _current = null; 27972 _current = null;
27972 _position = _array.length; 27973 _position = _array.length;
27973 return false; 27974 return false;
27974 } 27975 }
27975 27976
27976 T get current => _current; 27977 T get current => _current;
27977 } 27978 }
OLDNEW
« no previous file with comments | « no previous file | sdk/lib/html/dartium/html_dartium.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698