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

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

Issue 17552019: Reorganize mdv and observe packages (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: merged Created 7 years, 6 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 | « sdk/lib/_internal/libraries.dart ('k') | 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 /// 2 ///
3 /// For examples, see 3 /// For examples, see
4 /// [Dart HTML5 Samples](https://github.com/dart-lang/dart-html5-samples) 4 /// [Dart HTML5 Samples](https://github.com/dart-lang/dart-html5-samples)
5 /// on Github. 5 /// on Github.
6 library dart.dom.html; 6 library dart.dom.html;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:collection'; 9 import 'dart:collection';
10 import 'dart:_collection-dev' hide Symbol; 10 import 'dart:_collection-dev' hide Symbol;
11 import 'dart:html_common'; 11 import 'dart:html_common';
12 import 'dart:indexed_db'; 12 import 'dart:indexed_db';
13 import 'dart:isolate'; 13 import 'dart:isolate';
14 import 'dart:json' as json; 14 import 'dart:json' as json;
15 import 'dart:math'; 15 import 'dart:math';
16 import 'dart:mdv_observe_impl';
17 import 'dart:typed_data'; 16 import 'dart:typed_data';
18 import 'dart:svg' as svg; 17 import 'dart:svg' as svg;
19 import 'dart:web_audio' as web_audio; 18 import 'dart:web_audio' as web_audio;
20 import 'dart:web_gl' as gl; 19 import 'dart:web_gl' as gl;
21 import 'dart:web_sql'; 20 import 'dart:web_sql';
22 import 'dart:_js_helper' show convertDartClosureToJS, Creates, JavaScriptIndexin gBehavior, JSName, Null, Returns; 21 import 'dart:_js_helper' show convertDartClosureToJS, Creates, JavaScriptIndexin gBehavior, JSName, Null, Returns;
23 import 'dart:_interceptors' show Interceptor, JSExtendableArray; 22 import 'dart:_interceptors' show Interceptor, JSExtendableArray;
24 import 'dart:_isolate_helper' show IsolateNatives; 23 import 'dart:_isolate_helper' show IsolateNatives;
25 import 'dart:_foreign_helper' show JS; 24 import 'dart:_foreign_helper' show JS;
26 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 25 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
(...skipping 8246 matching lines...) Expand 10 before | Expand all | Expand 10 after
8273 } else if (JS('bool', '!!#.webkitMatchesSelector', this)) { 8272 } else if (JS('bool', '!!#.webkitMatchesSelector', this)) {
8274 return JS('bool', '#.webkitMatchesSelector(#)', this, selectors); 8273 return JS('bool', '#.webkitMatchesSelector(#)', this, selectors);
8275 } else if (JS('bool', '!!#.mozMatchesSelector', this)) { 8274 } else if (JS('bool', '!!#.mozMatchesSelector', this)) {
8276 return JS('bool', '#.mozMatchesSelector(#)', this, selectors); 8275 return JS('bool', '#.mozMatchesSelector(#)', this, selectors);
8277 } else if (JS('bool', '!!#.msMatchesSelector', this)) { 8276 } else if (JS('bool', '!!#.msMatchesSelector', this)) {
8278 return JS('bool', '#.msMatchesSelector(#)', this, selectors); 8277 return JS('bool', '#.msMatchesSelector(#)', this, selectors);
8279 } 8278 }
8280 throw new UnsupportedError("Not supported on this platform"); 8279 throw new UnsupportedError("Not supported on this platform");
8281 } 8280 }
8282 8281
8283 @Creates('Null') // Set from Dart code; does not instantiate a native type.
8284 Map<String, StreamSubscription> _attributeBindings;
8285
8286 // TODO(jmesserly): I'm concerned about adding these to every element.
8287 // Conceptually all of these belong on TemplateElement. They are here to
8288 // support browsers that don't have <template> yet.
8289 // However even in the polyfill they're restricted to certain tags
8290 // (see [isTemplate]). So we can probably convert it to a (public) mixin, and
8291 // only mix it in to the elements that need it.
8292 @Creates('Null') // Set from Dart code; does not instantiate a native type.
8293 var _model;
8294
8295 @Creates('Null') // Set from Dart code; does not instantiate a native type.
8296 _TemplateIterator _templateIterator;
8297
8298 @Creates('Null') // Set from Dart code; does not instantiate a native type. 8282 @Creates('Null') // Set from Dart code; does not instantiate a native type.
8299 Element _templateInstanceRef; 8283 Element _templateInstanceRef;
8300 8284
8301 // Note: only used if `this is! TemplateElement` 8285 // Note: only used if `this is! TemplateElement`
8302 @Creates('Null') // Set from Dart code; does not instantiate a native type. 8286 @Creates('Null') // Set from Dart code; does not instantiate a native type.
8303 DocumentFragment _templateContent; 8287 DocumentFragment _templateContent;
8304 8288
8305 bool _templateIsDecorated; 8289 bool _templateIsDecorated;
8306 8290
8307 // TODO(jmesserly): should path be optional, and default to empty path?
8308 // It is used that way in at least one path in JS TemplateElement tests
8309 // (see "BindImperative" test in original JS code).
8310 @Experimental
8311 void bind(String name, model, String path) {
8312 _bindElement(this, name, model, path);
8313 }
8314
8315 // TODO(jmesserly): this is static to work around http://dartbug.com/10166
8316 // Similar issue for unbind/unbindAll below.
8317 static void _bindElement(Element self, String name, model, String path) {
8318 if (self._bindTemplate(name, model, path)) return;
8319
8320 if (self._attributeBindings == null) {
8321 self._attributeBindings = new Map<String, StreamSubscription>();
8322 }
8323
8324 self.xtag.attributes.remove(name);
8325
8326 var changed;
8327 if (name.endsWith('?')) {
8328 name = name.substring(0, name.length - 1);
8329
8330 changed = (value) {
8331 if (_Bindings._toBoolean(value)) {
8332 self.xtag.attributes[name] = '';
8333 } else {
8334 self.xtag.attributes.remove(name);
8335 }
8336 };
8337 } else {
8338 changed = (value) {
8339 // TODO(jmesserly): escape value if needed to protect against XSS.
8340 // See https://github.com/polymer-project/mdv/issues/58
8341 self.xtag.attributes[name] = value == null ? '' : '$value';
8342 };
8343 }
8344
8345 self.unbind(name);
8346
8347 self._attributeBindings[name] =
8348 new PathObserver(model, path).bindSync(changed);
8349 }
8350
8351 @Experimental
8352 void unbind(String name) {
8353 _unbindElement(this, name);
8354 }
8355
8356 static _unbindElement(Element self, String name) {
8357 if (self._unbindTemplate(name)) return;
8358 if (self._attributeBindings != null) {
8359 var binding = self._attributeBindings.remove(name);
8360 if (binding != null) binding.cancel();
8361 }
8362 }
8363
8364 @Experimental
8365 void unbindAll() {
8366 _unbindAllElement(this);
8367 }
8368
8369 static void _unbindAllElement(Element self) {
8370 self._unbindAllTemplate();
8371
8372 if (self._attributeBindings != null) {
8373 for (var binding in self._attributeBindings.values) {
8374 binding.cancel();
8375 }
8376 self._attributeBindings = null;
8377 }
8378 }
8379
8380 // TODO(jmesserly): unlike the JS polyfill, we can't mixin
8381 // HTMLTemplateElement at runtime into things that are semantically template
8382 // elements. So instead we implement it here with a runtime check.
8383 // If the bind succeeds, we return true, otherwise we return false and let
8384 // the normal Element.bind logic kick in.
8385 bool _bindTemplate(String name, model, String path) {
8386 if (isTemplate) {
8387 switch (name) {
8388 case 'bind':
8389 case 'repeat':
8390 case 'if':
8391 _ensureTemplate();
8392 if (_templateIterator == null) {
8393 _templateIterator = new _TemplateIterator(this);
8394 }
8395 _templateIterator.inputs.bind(name, model, path);
8396 return true;
8397 }
8398 }
8399 return false;
8400 }
8401
8402 bool _unbindTemplate(String name) {
8403 if (isTemplate) {
8404 switch (name) {
8405 case 'bind':
8406 case 'repeat':
8407 case 'if':
8408 _ensureTemplate();
8409 if (_templateIterator != null) {
8410 _templateIterator.inputs.unbind(name);
8411 }
8412 return true;
8413 }
8414 }
8415 return false;
8416 }
8417
8418 void _unbindAllTemplate() {
8419 if (isTemplate) {
8420 unbind('bind');
8421 unbind('repeat');
8422 unbind('if');
8423 }
8424 }
8425
8426 /** 8291 /**
8427 * Gets the template this node refers to. 8292 * Gets the template this node refers to.
8428 * This is only supported if [isTemplate] is true. 8293 * This is only supported if [isTemplate] is true.
8429 */ 8294 */
8430 @Experimental 8295 @Experimental
8431 Element get ref { 8296 Element get ref {
8432 _ensureTemplate(); 8297 _ensureTemplate();
8433 8298
8434 Element ref = null; 8299 Element ref = null;
8435 var refId = attributes['ref']; 8300 var refId = attributes['ref'];
(...skipping 14 matching lines...) Expand all
8450 return _templateContent; 8315 return _templateContent;
8451 } 8316 }
8452 8317
8453 /** 8318 /**
8454 * Creates an instance of the template. 8319 * Creates an instance of the template.
8455 * This is only supported if [isTemplate] is true. 8320 * This is only supported if [isTemplate] is true.
8456 */ 8321 */
8457 @Experimental 8322 @Experimental
8458 DocumentFragment createInstance() { 8323 DocumentFragment createInstance() {
8459 _ensureTemplate(); 8324 _ensureTemplate();
8460 8325 return TemplateElement.mdvPackage(this).createInstance();
8461 var template = ref;
8462 if (template == null) template = this;
8463
8464 var instance = _Bindings._createDeepCloneAndDecorateTemplates(
8465 template.content, attributes['syntax']);
8466
8467 if (TemplateElement._instanceCreated != null) {
8468 TemplateElement._instanceCreated.add(instance);
8469 }
8470 return instance;
8471 } 8326 }
8472 8327
8473 /** 8328 /**
8474 * The data model which is inherited through the tree. 8329 * The data model which is inherited through the tree.
8475 * This is only supported if [isTemplate] is true. 8330 * This is only supported if [isTemplate] is true.
8476 *
8477 * Setting this will destructive propagate the value to all descendant nodes,
8478 * and reinstantiate all of the nodes expanded by this template.
8479 *
8480 * Currently this does not support propagation through Shadow DOMs.
8481 */ 8331 */
8482 @Experimental 8332 @Experimental
8483 get model => _model; 8333 get model => TemplateElement.mdvPackage(this).model;
8484 8334
8485 @Experimental 8335 @Experimental
8486 void set model(value) { 8336 void set model(value) {
8487 _ensureTemplate(); 8337 _ensureTemplate();
8488 8338 TemplateElement.mdvPackage(this).model = value;
8489 var syntax = TemplateElement.syntax[attributes['syntax']];
8490 _model = value;
8491 _Bindings._addBindings(this, model, syntax);
8492 } 8339 }
8493 8340
8494 // TODO(jmesserly): const set would be better 8341 // TODO(jmesserly): const set would be better
8495 static const _TABLE_TAGS = const { 8342 static const _TABLE_TAGS = const {
8496 'caption': null, 8343 'caption': null,
8497 'col': null, 8344 'col': null,
8498 'colgroup': null, 8345 'colgroup': null,
8499 'tbody': null, 8346 'tbody': null,
8500 'td': null, 8347 'td': null,
8501 'tfoot': null, 8348 'tfoot': null,
(...skipping 4015 matching lines...) Expand 10 before | Expand all | Expand 10 after
12517 var e = document.$dom_createElement("input"); 12364 var e = document.$dom_createElement("input");
12518 if (type != null) { 12365 if (type != null) {
12519 try { 12366 try {
12520 // IE throws an exception for unknown types. 12367 // IE throws an exception for unknown types.
12521 e.type = type; 12368 e.type = type;
12522 } catch(_) {} 12369 } catch(_) {}
12523 } 12370 }
12524 return e; 12371 return e;
12525 } 12372 }
12526 12373
12527 @Creates('Null') // Set from Dart code; does not instantiate a native type.
12528 _ValueBinding _valueBinding;
12529
12530 @Creates('Null') // Set from Dart code; does not instantiate a native type.
12531 _CheckedBinding _checkedBinding;
12532
12533 @Experimental
12534 void bind(String name, model, String path) {
12535 switch (name) {
12536 case 'value':
12537 unbind('value');
12538 attributes.remove('value');
12539 _valueBinding = new _ValueBinding(this, model, path);
12540 break;
12541 case 'checked':
12542 unbind('checked');
12543 attributes.remove('checked');
12544 _checkedBinding = new _CheckedBinding(this, model, path);
12545 break;
12546 default:
12547 // TODO(jmesserly): this should be "super" (http://dartbug.com/10166).
12548 // Similar issue for unbind/unbindAll below.
12549 Element._bindElement(this, name, model, path);
12550 break;
12551 }
12552 }
12553
12554 @Experimental
12555 void unbind(String name) {
12556 switch (name) {
12557 case 'value':
12558 if (_valueBinding != null) {
12559 _valueBinding.unbind();
12560 _valueBinding = null;
12561 }
12562 break;
12563 case 'checked':
12564 if (_checkedBinding != null) {
12565 _checkedBinding.unbind();
12566 _checkedBinding = null;
12567 }
12568 break;
12569 default:
12570 Element._unbindElement(this, name);
12571 break;
12572 }
12573 }
12574
12575 @Experimental
12576 void unbindAll() {
12577 unbind('value');
12578 unbind('checked');
12579 Element._unbindAllElement(this);
12580 }
12581
12582 12374
12583 @DomName('HTMLInputElement.webkitSpeechChangeEvent') 12375 @DomName('HTMLInputElement.webkitSpeechChangeEvent')
12584 @DocsEditable 12376 @DocsEditable
12585 @SupportedBrowser(SupportedBrowser.CHROME) 12377 @SupportedBrowser(SupportedBrowser.CHROME)
12586 @SupportedBrowser(SupportedBrowser.SAFARI) 12378 @SupportedBrowser(SupportedBrowser.SAFARI)
12587 @Experimental 12379 @Experimental
12588 // http://lists.w3.org/Archives/Public/public-xg-htmlspeech/2011Feb/att-0020/a pi-draft.html#extending_html_elements 12380 // http://lists.w3.org/Archives/Public/public-xg-htmlspeech/2011Feb/att-0020/a pi-draft.html#extending_html_elements
12589 static const EventStreamProvider<Event> speechChangeEvent = const EventStreamP rovider<Event>('webkitSpeechChange'); 12381 static const EventStreamProvider<Event> speechChangeEvent = const EventStreamP rovider<Event>('webkitSpeechChange');
12590 12382
12591 @DomName('HTMLInputElement.accept') 12383 @DomName('HTMLInputElement.accept')
(...skipping 3898 matching lines...) Expand 10 before | Expand all | Expand 10 after
16490 int get length => _this.$dom_childNodes.length; 16282 int get length => _this.$dom_childNodes.length;
16491 16283
16492 void set length(int value) { 16284 void set length(int value) {
16493 throw new UnsupportedError( 16285 throw new UnsupportedError(
16494 "Cannot set length on immutable List."); 16286 "Cannot set length on immutable List.");
16495 } 16287 }
16496 16288
16497 Node operator[](int index) => _this.$dom_childNodes[index]; 16289 Node operator[](int index) => _this.$dom_childNodes[index];
16498 } 16290 }
16499 16291
16292 /** Information about the instantiated template. */
16293 class TemplateInstance {
16294 // TODO(rafaelw): firstNode & lastNode should be read-synchronous
16295 // in cases where script has modified the template instance boundary.
16296
16297 /** The first node of this template instantiation. */
16298 final Node firstNode;
16299
16300 /**
16301 * The last node of this template instantiation.
16302 * This could be identical to [firstNode] if the template only expanded to a
16303 * single node.
16304 */
16305 final Node lastNode;
16306
16307 /** The model used to instantiate the template. */
16308 final model;
16309
16310 TemplateInstance(this.firstNode, this.lastNode, this.model);
16311 }
16312
16500 @DomName('Node') 16313 @DomName('Node')
16501 class Node extends EventTarget native "Node" { 16314 class Node extends EventTarget native "Node" {
16502 List<Node> get nodes { 16315 List<Node> get nodes {
16503 return new _ChildNodeListLazy(this); 16316 return new _ChildNodeListLazy(this);
16504 } 16317 }
16505 16318
16506 void set nodes(Iterable<Node> value) { 16319 void set nodes(Iterable<Node> value) {
16507 // Copy list first since we don't want liveness during iteration. 16320 // Copy list first since we don't want liveness during iteration.
16508 // TODO(jacobr): there is a better way to do this. 16321 // TODO(jacobr): there is a better way to do this.
16509 List copy = new List.from(value); 16322 List copy = new List.from(value);
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
16569 * Print out a String representation of this Node. 16382 * Print out a String representation of this Node.
16570 */ 16383 */
16571 String toString() => nodeValue == null ? super.toString() : nodeValue; 16384 String toString() => nodeValue == null ? super.toString() : nodeValue;
16572 16385
16573 /** 16386 /**
16574 * Binds the attribute [name] to the [path] of the [model]. 16387 * Binds the attribute [name] to the [path] of the [model].
16575 * Path is a String of accessors such as `foo.bar.baz`. 16388 * Path is a String of accessors such as `foo.bar.baz`.
16576 */ 16389 */
16577 @Experimental 16390 @Experimental
16578 void bind(String name, model, String path) { 16391 void bind(String name, model, String path) {
16579 // TODO(jmesserly): should we throw instead? 16392 TemplateElement.mdvPackage(this).bind(name, model, path);
16580 window.console.error('Unhandled binding to Node: '
16581 '$this $name $model $path');
16582 } 16393 }
16583 16394
16584 /** Unbinds the attribute [name]. */ 16395 /** Unbinds the attribute [name]. */
16585 @Experimental 16396 @Experimental
16586 void unbind(String name) {} 16397 void unbind(String name) {
16398 TemplateElement.mdvPackage(this).unbind(name);
16399 }
16587 16400
16588 /** Unbinds all bound attributes. */ 16401 /** Unbinds all bound attributes. */
16589 @Experimental 16402 @Experimental
16590 void unbindAll() {} 16403 void unbindAll() {
16591 16404 TemplateElement.mdvPackage(this).unbindAll();
16592 TemplateInstance _templateInstance; 16405 }
16593 16406
16594 /** Gets the template instance that instantiated this node, if any. */ 16407 /** Gets the template instance that instantiated this node, if any. */
16595 @Experimental 16408 @Experimental
16596 TemplateInstance get templateInstance => 16409 TemplateInstance get templateInstance => mdvPackage(this).templateInstance;
16597 _templateInstance != null ? _templateInstance :
16598 (parent != null ? parent.templateInstance : null);
16599 16410
16600 16411
16601 @DomName('Node.ATTRIBUTE_NODE') 16412 @DomName('Node.ATTRIBUTE_NODE')
16602 @DocsEditable 16413 @DocsEditable
16603 static const int ATTRIBUTE_NODE = 2; 16414 static const int ATTRIBUTE_NODE = 2;
16604 16415
16605 @DomName('Node.CDATA_SECTION_NODE') 16416 @DomName('Node.CDATA_SECTION_NODE')
16606 @DocsEditable 16417 @DocsEditable
16607 static const int CDATA_SECTION_NODE = 4; 16418 static const int CDATA_SECTION_NODE = 4;
16608 16419
(...skipping 4417 matching lines...) Expand 10 before | Expand all | Expand 10 after
21026 @DocsEditable 20837 @DocsEditable
21027 Element $dom_insertRow(int index) native; 20838 Element $dom_insertRow(int index) native;
21028 } 20839 }
21029 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 20840 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
21030 // for details. All rights reserved. Use of this source code is governed by a 20841 // for details. All rights reserved. Use of this source code is governed by a
21031 // BSD-style license that can be found in the LICENSE file. 20842 // BSD-style license that can be found in the LICENSE file.
21032 20843
21033 // WARNING: Do not edit - generated code. 20844 // WARNING: Do not edit - generated code.
21034 20845
21035 20846
20847
20848 /**
20849 * Model-Driven Views (MDV)'s native features enables a wide-range of use cases,
20850 * but (by design) don't attempt to implement a wide array of specialized
20851 * behaviors.
20852 *
20853 * Enabling these features in MDV is a matter of implementing and registering an
20854 * MDV Custom Syntax. A Custom Syntax is an object which contains one or more
20855 * delegation functions which implement specialized behavior. This object is
20856 * registered with MDV via [TemplateElement.syntax]:
20857 *
20858 *
20859 * HTML:
20860 * <template bind syntax="MySyntax">
20861 * {{ What!Ever('crazy')->thing^^^I+Want(data) }}
20862 * </template>
20863 *
20864 * Dart:
20865 * class MySyntax extends CustomBindingSyntax {
20866 * getBinding(model, path, name, node) {
20867 * // The magic happens here!
20868 * }
20869 * }
20870 *
20871 * ...
20872 *
20873 * TemplateElement.syntax['MySyntax'] = new MySyntax();
20874 *
20875 * See <https://github.com/polymer-project/mdv/blob/master/docs/syntax.md> for
20876 * more information about Custom Syntax.
20877 */
20878 // TODO(jmesserly): if this is just one method, a function type would make it
20879 // more Dart-friendly.
20880 @Experimental
20881 abstract class CustomBindingSyntax {
20882 /**
20883 * This syntax method allows for a custom interpretation of the contents of
20884 * mustaches (`{{` ... `}}`).
20885 *
20886 * When a template is inserting an instance, it will invoke this method for
20887 * each mustache which is encountered. The function is invoked with four
20888 * arguments:
20889 *
20890 * - [model]: The data context for which this instance is being created.
20891 * - [path]: The text contents (trimmed of outer whitespace) of the mustache.
20892 * - [name]: The context in which the mustache occurs. Within element
20893 * attributes, this will be the name of the attribute. Within text,
20894 * this will be 'text'.
20895 * - [node]: A reference to the node to which this binding will be created.
20896 *
20897 * If the method wishes to handle binding, it is required to return an object
20898 * which has at least a `value` property that can be observed. If it does,
20899 * then MDV will call [Node.bind on the node:
20900 *
20901 * node.bind(name, retval, 'value');
20902 *
20903 * If the 'getBinding' does not wish to override the binding, it should return
20904 * null.
20905 */
20906 // TODO(jmesserly): I had to remove type annotations from "name" and "node"
20907 // Normally they are String and Node respectively. But sometimes it will pass
20908 // (int name, CompoundBinding node). That seems very confusing; we may want
20909 // to change this API.
20910 getBinding(model, String path, name, node) => null;
20911
20912 /**
20913 * This syntax method allows a syntax to provide an alterate model than the
20914 * one the template would otherwise use when producing an instance.
20915 *
20916 * When a template is about to create an instance, it will invoke this method
20917 * The function is invoked with two arguments:
20918 *
20919 * - [template]: The template element which is about to create and insert an
20920 * instance.
20921 * - [model]: The data context for which this instance is being created.
20922 *
20923 * The template element will always use the return value of `getInstanceModel`
20924 * as the model for the new instance. If the syntax does not wish to override
20925 * the value, it should simply return the `model` value it was passed.
20926 */
20927 getInstanceModel(Element template, model) => model;
20928
20929 /**
20930 * This syntax method allows a syntax to provide an alterate expansion of
20931 * the [template] contents. When the template wants to create an instance,
20932 * it will call this method with the template element.
20933 *
20934 * By default this will call `template.createInstance()`.
20935 */
20936 getInstanceFragment(Element template) => template.createInstance();
20937 }
20938
20939
21036 @Experimental 20940 @Experimental
21037 @DomName('HTMLTemplateElement') 20941 @DomName('HTMLTemplateElement')
21038 @SupportedBrowser(SupportedBrowser.CHROME) 20942 @SupportedBrowser(SupportedBrowser.CHROME)
21039 @Experimental 20943 @Experimental
21040 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#t emplate-element 20944 // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#t emplate-element
21041 class TemplateElement extends _HTMLElement native "HTMLTemplateElement" { 20945 class TemplateElement extends _HTMLElement native "HTMLTemplateElement" {
21042 20946
21043 @DomName('HTMLTemplateElement.HTMLTemplateElement') 20947 @DomName('HTMLTemplateElement.HTMLTemplateElement')
21044 @DocsEditable 20948 @DocsEditable
21045 factory TemplateElement() => document.$dom_createElement("template"); 20949 factory TemplateElement() => document.$dom_createElement("template");
21046 20950
21047 /// Checks if this type is supported on the current platform. 20951 /// Checks if this type is supported on the current platform.
21048 static bool get supported => Element.isTagSupported('template'); 20952 static bool get supported => Element.isTagSupported('template');
21049 20953
21050 @JSName('content') 20954 @JSName('content')
21051 @DomName('HTMLTemplateElement.content') 20955 @DomName('HTMLTemplateElement.content')
21052 @DocsEditable 20956 @DocsEditable
21053 final DocumentFragment $dom_content; 20957 final DocumentFragment $dom_content;
21054 20958
21055 20959
21056 // For real TemplateElement use the actual DOM .content field instead of 20960 // For real TemplateElement use the actual DOM .content field instead of
21057 // our polyfilled expando. 20961 // our polyfilled expando.
21058 @Experimental 20962 @Experimental
21059 DocumentFragment get content => $dom_content; 20963 DocumentFragment get content => $dom_content;
21060 20964
21061 static StreamController<DocumentFragment> _instanceCreated;
21062 20965
21063 /** 20966 /**
21064 * *Warning*: This is an implementation helper for Model-Driven Views and 20967 * The MDV package, if available.
21065 * should not be used in your code.
21066 * 20968 *
21067 * This event is fired whenever a template is instantiated via 20969 * This can be used to initialize MDV support via:
21068 * [createInstance]. 20970 *
20971 * import 'dart:html';
20972 * import 'package:mdv/mdv.dart' as mdv;
20973 * main() {
20974 * mdv.initialize();
20975 * }
21069 */ 20976 */
21070 // TODO(rafaelw): This is a hack, and is neccesary for the polyfill 20977 static Function mdvPackage = (node) {
21071 // because custom elements are not upgraded during clone() 20978 throw new UnsupportedError("The MDV package is not available. "
21072 @Experimental 20979 "You can enable it with `import 'package:mdv/mdv.dart' as mdv;` and "
21073 static Stream<DocumentFragment> get instanceCreated { 20980 "`mdv.initialize()`");
21074 if (_instanceCreated == null) { 20981 };
21075 _instanceCreated = new StreamController<DocumentFragment>(sync: true);
21076 }
21077 return _instanceCreated.stream;
21078 }
21079 20982
21080 /** 20983 /**
21081 * Ensures proper API and content model for template elements. 20984 * Ensures proper API and content model for template elements.
21082 * 20985 *
21083 * [instanceRef] can be used to set the [Element.ref] property of [template], 20986 * [instanceRef] can be used to set the [Element.ref] property of [template],
21084 * and use the ref's content will be used as source when createInstance() is 20987 * and use the ref's content will be used as source when createInstance() is
21085 * invoked. 20988 * invoked.
21086 * 20989 *
21087 * Returns true if this template was just decorated, or false if it was 20990 * Returns true if this template was just decorated, or false if it was
21088 * already decorated. 20991 * already decorated.
21089 */ 20992 */
21090 @Experimental 20993 @Experimental
21091 static bool decorate(Element template, [Element instanceRef]) { 20994 static bool decorate(Element template, [Element instanceRef]) {
21092 // == true check because it starts as a null field. 20995 // == true check because it starts as a null field.
21093 if (template._templateIsDecorated == true) return false; 20996 if (template._templateIsDecorated == true) return false;
21094 20997
21095 template._templateIsDecorated = true; 20998 template._templateIsDecorated = true;
21096 20999
21097 _injectStylesheet(); 21000 _injectStylesheet();
21098 21001
21099 // Create content 21002 // Create content
21100 if (template is! TemplateElement) { 21003 if (template is! TemplateElement) {
21101 var doc = _Bindings._getTemplateContentsOwner(template.document); 21004 var doc = _getTemplateContentsOwner(template.document);
21102 template._templateContent = doc.createDocumentFragment(); 21005 template._templateContent = doc.createDocumentFragment();
21103 } 21006 }
21104 21007
21105 if (instanceRef != null) { 21008 if (instanceRef != null) {
21106 template._templateInstanceRef = instanceRef; 21009 template._templateInstanceRef = instanceRef;
21107 return true; // content is empty. 21010 return true; // content is empty.
21108 } 21011 }
21109 21012
21110 if (template is TemplateElement) { 21013 if (template is TemplateElement) {
21111 bootstrap(template.content); 21014 bootstrap(template.content);
21112 } else { 21015 } else {
21113 _Bindings._liftNonNativeChildrenIntoContent(template); 21016 _liftNonNativeChildrenIntoContent(template);
21114 } 21017 }
21115 21018
21116 return true; 21019 return true;
21117 } 21020 }
21118 21021
21022 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
21023 static Document _getTemplateContentsOwner(HtmlDocument doc) {
21024 if (doc.window == null) {
21025 return doc;
21026 }
21027 var d = doc._templateContentsOwner;
21028 if (d == null) {
21029 // TODO(arv): This should either be a Document or HTMLDocument depending
21030 // on doc.
21031 d = doc.implementation.createHtmlDocument('');
21032 while (d.lastChild != null) {
21033 d.lastChild.remove();
21034 }
21035 doc._templateContentsOwner = d;
21036 }
21037 return d;
21038 }
21039
21040 static Element _cloneAndSeperateAttributeTemplate(Element templateElement) {
21041 var clone = templateElement.clone(false);
21042 var attributes = templateElement.attributes;
21043 for (var name in attributes.keys.toList()) {
21044 switch (name) {
21045 case 'template':
21046 case 'repeat':
21047 case 'bind':
21048 case 'ref':
21049 clone.attributes.remove(name);
21050 break;
21051 default:
21052 attributes.remove(name);
21053 break;
21054 }
21055 }
21056
21057 return clone;
21058 }
21059
21060 static void _liftNonNativeChildrenIntoContent(Element templateElement) {
21061 var content = templateElement.content;
21062
21063 if (!templateElement._isAttributeTemplate) {
21064 var child;
21065 while ((child = templateElement.firstChild) != null) {
21066 content.append(child);
21067 }
21068 return;
21069 }
21070
21071 // For attribute templates we copy the whole thing into the content and
21072 // we move the non template attributes into the content.
21073 //
21074 // <tr foo template>
21075 //
21076 // becomes
21077 //
21078 // <tr template>
21079 // + #document-fragment
21080 // + <tr foo>
21081 //
21082 var newRoot = _cloneAndSeperateAttributeTemplate(templateElement);
21083 var child;
21084 while ((child = templateElement.firstChild) != null) {
21085 newRoot.append(child);
21086 }
21087 content.append(newRoot);
21088 }
21089
21119 /** 21090 /**
21120 * This used to decorate recursively all templates from a given node. 21091 * This used to decorate recursively all templates from a given node.
21121 * 21092 *
21122 * By default [decorate] will be called on templates lazily when certain 21093 * By default [decorate] will be called on templates lazily when certain
21123 * properties such as [model] are accessed, but it can be run eagerly to 21094 * properties such as [model] are accessed, but it can be run eagerly to
21124 * decorate an entire tree recursively. 21095 * decorate an entire tree recursively.
21125 */ 21096 */
21126 // TODO(rafaelw): Review whether this is the right public API. 21097 // TODO(rafaelw): Review whether this is the right public API.
21127 @Experimental 21098 @Experimental
21128 static void bootstrap(Node content) { 21099 static void bootstrap(Node content) {
21129 _Bindings._bootstrapTemplatesRecursivelyFrom(content); 21100 void _bootstrap(template) {
21101 if (!TemplateElement.decorate(template)) {
21102 bootstrap(template.content);
21103 }
21104 }
21105
21106 // Need to do this first as the contents may get lifted if |node| is
21107 // template.
21108 // TODO(jmesserly): content is DocumentFragment or Element
21109 var descendents = (content as dynamic).queryAll(_allTemplatesSelectors);
21110 if (content is Element && (content as Element).isTemplate) {
21111 _bootstrap(content);
21112 }
21113
21114 descendents.forEach(_bootstrap);
21130 } 21115 }
21131 21116
21132 /** 21117 static final String _allTemplatesSelectors = 'template, option[template], ' +
21133 * Binds all mustaches recursively starting from the [root] node. 21118 Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", ");
21134 *
21135 * Note: this is not an official Model-Driven-Views API; it is intended to
21136 * support binding the [ShadowRoot]'s content to a model.
21137 */
21138 // TODO(jmesserly): this is needed to avoid two <template> nodes when using
21139 // bindings in a custom element's template. See also:
21140 // https://github.com/polymer-project/polymer/blob/master/src/bindMDV.js#L68
21141 // Called from:
21142 // https://github.com/polymer-project/polymer/blob/master/src/register.js#L99
21143 @Experimental
21144 static void bindModel(Node root, model, [CustomBindingSyntax syntax]) {
21145 _Bindings._addBindings(root, model, syntax);
21146 }
21147 21119
21148 static bool _initStyles; 21120 static bool _initStyles;
21149 21121
21150 static void _injectStylesheet() { 21122 static void _injectStylesheet() {
21151 if (_initStyles == true) return; 21123 if (_initStyles == true) return;
21152 _initStyles = true; 21124 _initStyles = true;
21153 21125
21154 var style = new StyleElement(); 21126 var style = new StyleElement();
21155 style.text = r''' 21127 style.text = r'''
21156 template, 21128 template,
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
21194 @DomName('Text.replaceWholeText') 21166 @DomName('Text.replaceWholeText')
21195 @DocsEditable 21167 @DocsEditable
21196 // http://dom.spec.whatwg.org/#dom-text-replacewholetext 21168 // http://dom.spec.whatwg.org/#dom-text-replacewholetext
21197 @deprecated // deprecated 21169 @deprecated // deprecated
21198 Text replaceWholeText(String content) native; 21170 Text replaceWholeText(String content) native;
21199 21171
21200 @DomName('Text.splitText') 21172 @DomName('Text.splitText')
21201 @DocsEditable 21173 @DocsEditable
21202 Text splitText(int offset) native; 21174 Text splitText(int offset) native;
21203 21175
21204
21205 @Creates('Null') // Set from Dart code; does not instantiate a native type.
21206 StreamSubscription _textBinding;
21207
21208 @Experimental
21209 void bind(String name, model, String path) {
21210 if (name != 'text') {
21211 super.bind(name, model, path);
21212 return;
21213 }
21214
21215 unbind('text');
21216
21217 _textBinding = new PathObserver(model, path).bindSync((value) {
21218 text = value == null ? '' : '$value';
21219 });
21220 }
21221
21222 @Experimental
21223 void unbind(String name) {
21224 if (name != 'text') {
21225 super.unbind(name);
21226 return;
21227 }
21228
21229 if (_textBinding == null) return;
21230
21231 _textBinding.cancel();
21232 _textBinding = null;
21233 }
21234
21235 @Experimental
21236 void unbindAll() {
21237 unbind('text');
21238 super.unbindAll();
21239 }
21240 } 21176 }
21241 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 21177 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
21242 // for details. All rights reserved. Use of this source code is governed by a 21178 // for details. All rights reserved. Use of this source code is governed by a
21243 // BSD-style license that can be found in the LICENSE file. 21179 // BSD-style license that can be found in the LICENSE file.
21244 21180
21245 21181
21246 @DocsEditable 21182 @DocsEditable
21247 @DomName('HTMLTextAreaElement') 21183 @DomName('HTMLTextAreaElement')
21248 class TextAreaElement extends _HTMLElement native "HTMLTextAreaElement" { 21184 class TextAreaElement extends _HTMLElement native "HTMLTextAreaElement" {
21249 21185
(...skipping 6445 matching lines...) Expand 10 before | Expand all | Expand 10 after
27695 * Truncates coordinates to integers and returns the result as a new 27631 * Truncates coordinates to integers and returns the result as a new
27696 * rectangle. 27632 * rectangle.
27697 */ 27633 */
27698 Rect toInt() => new Rect(left.toInt(), top.toInt(), width.toInt(), 27634 Rect toInt() => new Rect(left.toInt(), top.toInt(), width.toInt(),
27699 height.toInt()); 27635 height.toInt());
27700 27636
27701 Point get topLeft => new Point(this.left, this.top); 27637 Point get topLeft => new Point(this.left, this.top);
27702 Point get bottomRight => new Point(this.left + this.width, 27638 Point get bottomRight => new Point(this.left + this.width,
27703 this.top + this.height); 27639 this.top + this.height);
27704 } 27640 }
27705 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
27706 // for details. All rights reserved. Use of this source code is governed by a
27707 // BSD-style license that can be found in the LICENSE file.
27708
27709
27710 // This code is a port of Model-Driven-Views:
27711 // https://github.com/polymer-project/mdv
27712 // The code mostly comes from src/template_element.js
27713
27714 typedef void _ChangeHandler(value);
27715
27716 /**
27717 * Model-Driven Views (MDV)'s native features enables a wide-range of use cases,
27718 * but (by design) don't attempt to implement a wide array of specialized
27719 * behaviors.
27720 *
27721 * Enabling these features in MDV is a matter of implementing and registering an
27722 * MDV Custom Syntax. A Custom Syntax is an object which contains one or more
27723 * delegation functions which implement specialized behavior. This object is
27724 * registered with MDV via [TemplateElement.syntax]:
27725 *
27726 *
27727 * HTML:
27728 * <template bind syntax="MySyntax">
27729 * {{ What!Ever('crazy')->thing^^^I+Want(data) }}
27730 * </template>
27731 *
27732 * Dart:
27733 * class MySyntax extends CustomBindingSyntax {
27734 * getBinding(model, path, name, node) {
27735 * // The magic happens here!
27736 * }
27737 * }
27738 *
27739 * ...
27740 *
27741 * TemplateElement.syntax['MySyntax'] = new MySyntax();
27742 *
27743 * See <https://github.com/polymer-project/mdv/blob/master/docs/syntax.md> for m ore
27744 * information about Custom Syntax.
27745 */
27746 // TODO(jmesserly): if this is just one method, a function type would make it
27747 // more Dart-friendly.
27748 @Experimental
27749 abstract class CustomBindingSyntax {
27750 /**
27751 * This syntax method allows for a custom interpretation of the contents of
27752 * mustaches (`{{` ... `}}`).
27753 *
27754 * When a template is inserting an instance, it will invoke this method for
27755 * each mustache which is encountered. The function is invoked with four
27756 * arguments:
27757 *
27758 * - [model]: The data context for which this instance is being created.
27759 * - [path]: The text contents (trimmed of outer whitespace) of the mustache.
27760 * - [name]: The context in which the mustache occurs. Within element
27761 * attributes, this will be the name of the attribute. Within text,
27762 * this will be 'text'.
27763 * - [node]: A reference to the node to which this binding will be created.
27764 *
27765 * If the method wishes to handle binding, it is required to return an object
27766 * which has at least a `value` property that can be observed. If it does,
27767 * then MDV will call [Node.bind on the node:
27768 *
27769 * node.bind(name, retval, 'value');
27770 *
27771 * If the 'getBinding' does not wish to override the binding, it should return
27772 * null.
27773 */
27774 // TODO(jmesserly): I had to remove type annotations from "name" and "node"
27775 // Normally they are String and Node respectively. But sometimes it will pass
27776 // (int name, CompoundBinding node). That seems very confusing; we may want
27777 // to change this API.
27778 getBinding(model, String path, name, node) => null;
27779
27780 /**
27781 * This syntax method allows a syntax to provide an alterate model than the
27782 * one the template would otherwise use when producing an instance.
27783 *
27784 * When a template is about to create an instance, it will invoke this method
27785 * The function is invoked with two arguments:
27786 *
27787 * - [template]: The template element which is about to create and insert an
27788 * instance.
27789 * - [model]: The data context for which this instance is being created.
27790 *
27791 * The template element will always use the return value of `getInstanceModel`
27792 * as the model for the new instance. If the syntax does not wish to override
27793 * the value, it should simply return the `model` value it was passed.
27794 */
27795 getInstanceModel(Element template, model) => model;
27796
27797 /**
27798 * This syntax method allows a syntax to provide an alterate expansion of
27799 * the [template] contents. When the template wants to create an instance,
27800 * it will call this method with the template element.
27801 *
27802 * By default this will call `template.createInstance()`.
27803 */
27804 getInstanceFragment(Element template) => template.createInstance();
27805 }
27806
27807 /** The callback used in the [CompoundBinding.combinator] field. */
27808 @Experimental
27809 typedef Object CompoundBindingCombinator(Map objects);
27810
27811 /** Information about the instantiated template. */
27812 @Experimental
27813 class TemplateInstance {
27814 // TODO(rafaelw): firstNode & lastNode should be read-synchronous
27815 // in cases where script has modified the template instance boundary.
27816
27817 /** The first node of this template instantiation. */
27818 final Node firstNode;
27819
27820 /**
27821 * The last node of this template instantiation.
27822 * This could be identical to [firstNode] if the template only expanded to a
27823 * single node.
27824 */
27825 final Node lastNode;
27826
27827 /** The model used to instantiate the template. */
27828 final model;
27829
27830 TemplateInstance(this.firstNode, this.lastNode, this.model);
27831 }
27832
27833 /**
27834 * Model-Driven Views contains a helper object which is useful for the
27835 * implementation of a Custom Syntax.
27836 *
27837 * var binding = new CompoundBinding((values) {
27838 * var combinedValue;
27839 * // compute combinedValue based on the current values which are provided
27840 * return combinedValue;
27841 * });
27842 * binding.bind('name1', obj1, path1);
27843 * binding.bind('name2', obj2, path2);
27844 * //...
27845 * binding.bind('nameN', objN, pathN);
27846 *
27847 * CompoundBinding is an object which knows how to listen to multiple path
27848 * values (registered via [bind]) and invoke its [combinator] when one or more
27849 * of the values have changed and set its [value] property to the return value
27850 * of the function. When any value has changed, all current values are provided
27851 * to the [combinator] in the single `values` argument.
27852 *
27853 * See [CustomBindingSyntax] for more information.
27854 */
27855 // TODO(jmesserly): what is the public API surface here? I just guessed;
27856 // most of it seemed non-public.
27857 @Experimental
27858 class CompoundBinding extends ObservableBase {
27859 CompoundBindingCombinator _combinator;
27860
27861 // TODO(jmesserly): ideally these would be String keys, but sometimes we
27862 // use integers.
27863 Map<dynamic, StreamSubscription> _bindings = new Map();
27864 Map _values = new Map();
27865 bool _scheduled = false;
27866 bool _disposed = false;
27867 Object _value;
27868
27869 CompoundBinding([CompoundBindingCombinator combinator]) {
27870 // TODO(jmesserly): this is a tweak to the original code, it seemed to me
27871 // that passing the combinator to the constructor should be equivalent to
27872 // setting it via the property.
27873 // I also added a null check to the combinator setter.
27874 this.combinator = combinator;
27875 }
27876
27877 CompoundBindingCombinator get combinator => _combinator;
27878
27879 set combinator(CompoundBindingCombinator combinator) {
27880 _combinator = combinator;
27881 if (combinator != null) _scheduleResolve();
27882 }
27883
27884 static const _VALUE = const Symbol('value');
27885
27886 get value => _value;
27887
27888 void set value(newValue) {
27889 _value = notifyPropertyChange(_VALUE, _value, newValue);
27890 }
27891
27892 // TODO(jmesserly): remove these workarounds when dart2js supports mirrors!
27893 getValueWorkaround(key) {
27894 if (key == _VALUE) return value;
27895 return null;
27896 }
27897 setValueWorkaround(key, val) {
27898 if (key == _VALUE) value = val;
27899 }
27900
27901 void bind(name, model, String path) {
27902 unbind(name);
27903
27904 _bindings[name] = new PathObserver(model, path).bindSync((value) {
27905 _values[name] = value;
27906 _scheduleResolve();
27907 });
27908 }
27909
27910 void unbind(name, {bool suppressResolve: false}) {
27911 var binding = _bindings.remove(name);
27912 if (binding == null) return;
27913
27914 binding.cancel();
27915 _values.remove(name);
27916 if (!suppressResolve) _scheduleResolve();
27917 }
27918
27919 // TODO(rafaelw): Is this the right processing model?
27920 // TODO(rafaelw): Consider having a seperate ChangeSummary for
27921 // CompoundBindings so to excess dirtyChecks.
27922 void _scheduleResolve() {
27923 if (_scheduled) return;
27924 _scheduled = true;
27925 queueChangeRecords(resolve);
27926 }
27927
27928 void resolve() {
27929 if (_disposed) return;
27930 _scheduled = false;
27931
27932 if (_combinator == null) {
27933 throw new StateError(
27934 'CompoundBinding attempted to resolve without a combinator');
27935 }
27936
27937 value = _combinator(_values);
27938 }
27939
27940 void dispose() {
27941 for (var binding in _bindings.values) {
27942 binding.cancel();
27943 }
27944 _bindings.clear();
27945 _values.clear();
27946
27947 _disposed = true;
27948 value = null;
27949 }
27950 }
27951
27952 abstract class _InputBinding {
27953 final InputElement element;
27954 PathObserver binding;
27955 StreamSubscription _pathSub;
27956 StreamSubscription _eventSub;
27957
27958 _InputBinding(this.element, model, String path) {
27959 binding = new PathObserver(model, path);
27960 _pathSub = binding.bindSync(valueChanged);
27961 _eventSub = _getStreamForInputType(element).listen(updateBinding);
27962 }
27963
27964 void valueChanged(newValue);
27965
27966 void updateBinding(e);
27967
27968 void unbind() {
27969 binding = null;
27970 _pathSub.cancel();
27971 _eventSub.cancel();
27972 }
27973
27974
27975 static Stream<Event> _getStreamForInputType(InputElement element) {
27976 switch (element.type) {
27977 case 'checkbox':
27978 return element.onClick;
27979 case 'radio':
27980 case 'select-multiple':
27981 case 'select-one':
27982 return element.onChange;
27983 default:
27984 return element.onInput;
27985 }
27986 }
27987 }
27988
27989 class _ValueBinding extends _InputBinding {
27990 _ValueBinding(element, model, path) : super(element, model, path);
27991
27992 void valueChanged(value) {
27993 element.value = value == null ? '' : '$value';
27994 }
27995
27996 void updateBinding(e) {
27997 binding.value = element.value;
27998 }
27999 }
28000
28001 class _CheckedBinding extends _InputBinding {
28002 _CheckedBinding(element, model, path) : super(element, model, path);
28003
28004 void valueChanged(value) {
28005 element.checked = _Bindings._toBoolean(value);
28006 }
28007
28008 void updateBinding(e) {
28009 binding.value = element.checked;
28010
28011 // Only the radio button that is getting checked gets an event. We
28012 // therefore find all the associated radio buttons and update their
28013 // CheckedBinding manually.
28014 if (element is InputElement && element.type == 'radio') {
28015 for (var r in _getAssociatedRadioButtons(element)) {
28016 var checkedBinding = r._checkedBinding;
28017 if (checkedBinding != null) {
28018 // Set the value directly to avoid an infinite call stack.
28019 checkedBinding.binding.value = false;
28020 }
28021 }
28022 }
28023 }
28024
28025 // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
28026 // Returns an array containing all radio buttons other than |element| that
28027 // have the same |name|, either in the form that |element| belongs to or,
28028 // if no form, in the document tree to which |element| belongs.
28029 //
28030 // This implementation is based upon the HTML spec definition of a
28031 // "radio button group":
28032 // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state. html#radio-button-group
28033 //
28034 static Iterable _getAssociatedRadioButtons(element) {
28035 if (!_isNodeInDocument(element)) return [];
28036 if (element.form != null) {
28037 return element.form.nodes.where((el) {
28038 return el != element &&
28039 el is InputElement &&
28040 el.type == 'radio' &&
28041 el.name == element.name;
28042 });
28043 } else {
28044 var radios = element.document.queryAll(
28045 'input[type="radio"][name="${element.name}"]');
28046 return radios.where((el) => el != element && el.form == null);
28047 }
28048 }
28049
28050 // TODO(jmesserly): polyfill document.contains API instead of doing it here
28051 static bool _isNodeInDocument(Node node) {
28052 // On non-IE this works:
28053 // return node.document.contains(node);
28054 var document = node.document;
28055 if (node == document || node.parentNode == document) return true;
28056 return document.documentElement.contains(node);
28057 }
28058 }
28059
28060 class _Bindings {
28061 // TODO(jmesserly): not sure what kind of boolean conversion rules to
28062 // apply for template data-binding. HTML attributes are true if they're
28063 // present. However Dart only treats "true" as true. Since this is HTML we'll
28064 // use something closer to the HTML rules: null (missing) and false are false,
28065 // everything else is true. See: https://github.com/polymer-project/mdv/issues /59
28066 static bool _toBoolean(value) => null != value && false != value;
28067
28068 static Node _createDeepCloneAndDecorateTemplates(Node node, String syntax) {
28069 var clone = node.clone(false); // Shallow clone.
28070 if (clone is Element && clone.isTemplate) {
28071 TemplateElement.decorate(clone, node);
28072 if (syntax != null) {
28073 clone.attributes.putIfAbsent('syntax', () => syntax);
28074 }
28075 }
28076
28077 for (var c = node.firstChild; c != null; c = c.nextNode) {
28078 clone.append(_createDeepCloneAndDecorateTemplates(c, syntax));
28079 }
28080 return clone;
28081 }
28082
28083 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
28084 static Document _getTemplateContentsOwner(HtmlDocument doc) {
28085 if (doc.window == null) {
28086 return doc;
28087 }
28088 var d = doc._templateContentsOwner;
28089 if (d == null) {
28090 // TODO(arv): This should either be a Document or HTMLDocument depending
28091 // on doc.
28092 d = doc.implementation.createHtmlDocument('');
28093 while (d.lastChild != null) {
28094 d.lastChild.remove();
28095 }
28096 doc._templateContentsOwner = d;
28097 }
28098 return d;
28099 }
28100
28101 static Element _cloneAndSeperateAttributeTemplate(Element templateElement) {
28102 var clone = templateElement.clone(false);
28103 var attributes = templateElement.attributes;
28104 for (var name in attributes.keys.toList()) {
28105 switch (name) {
28106 case 'template':
28107 case 'repeat':
28108 case 'bind':
28109 case 'ref':
28110 clone.attributes.remove(name);
28111 break;
28112 default:
28113 attributes.remove(name);
28114 break;
28115 }
28116 }
28117
28118 return clone;
28119 }
28120
28121 static void _liftNonNativeChildrenIntoContent(Element templateElement) {
28122 var content = templateElement.content;
28123
28124 if (!templateElement._isAttributeTemplate) {
28125 var child;
28126 while ((child = templateElement.firstChild) != null) {
28127 content.append(child);
28128 }
28129 return;
28130 }
28131
28132 // For attribute templates we copy the whole thing into the content and
28133 // we move the non template attributes into the content.
28134 //
28135 // <tr foo template>
28136 //
28137 // becomes
28138 //
28139 // <tr template>
28140 // + #document-fragment
28141 // + <tr foo>
28142 //
28143 var newRoot = _cloneAndSeperateAttributeTemplate(templateElement);
28144 var child;
28145 while ((child = templateElement.firstChild) != null) {
28146 newRoot.append(child);
28147 }
28148 content.append(newRoot);
28149 }
28150
28151 static void _bootstrapTemplatesRecursivelyFrom(Node node) {
28152 void bootstrap(template) {
28153 if (!TemplateElement.decorate(template)) {
28154 _bootstrapTemplatesRecursivelyFrom(template.content);
28155 }
28156 }
28157
28158 // Need to do this first as the contents may get lifted if |node| is
28159 // template.
28160 // TODO(jmesserly): node is DocumentFragment or Element
28161 var descendents = (node as dynamic).queryAll(_allTemplatesSelectors);
28162 if (node is Element && (node as Element).isTemplate) bootstrap(node);
28163
28164 descendents.forEach(bootstrap);
28165 }
28166
28167 static final String _allTemplatesSelectors = 'template, option[template], ' +
28168 Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", ");
28169
28170 static void _addBindings(Node node, model, [CustomBindingSyntax syntax]) {
28171 if (node is Element) {
28172 _addAttributeBindings(node, model, syntax);
28173 } else if (node is Text) {
28174 _parseAndBind(node, 'text', node.text, model, syntax);
28175 }
28176
28177 for (var c = node.firstChild; c != null; c = c.nextNode) {
28178 _addBindings(c, model, syntax);
28179 }
28180 }
28181
28182 static void _addAttributeBindings(Element element, model, syntax) {
28183 element.attributes.forEach((name, value) {
28184 if (value == '' && (name == 'bind' || name == 'repeat')) {
28185 value = '{{}}';
28186 }
28187 _parseAndBind(element, name, value, model, syntax);
28188 });
28189 }
28190
28191 static void _parseAndBind(Node node, String name, String text, model,
28192 CustomBindingSyntax syntax) {
28193
28194 var tokens = _parseMustacheTokens(text);
28195 if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) {
28196 return;
28197 }
28198
28199 // If this is a custom element, give the .xtag a change to bind.
28200 node = _nodeOrCustom(node);
28201
28202 if (tokens.length == 1 && tokens[0].isBinding) {
28203 _bindOrDelegate(node, name, model, tokens[0].value, syntax);
28204 return;
28205 }
28206
28207 var replacementBinding = new CompoundBinding();
28208 for (var i = 0; i < tokens.length; i++) {
28209 var token = tokens[i];
28210 if (token.isBinding) {
28211 _bindOrDelegate(replacementBinding, i, model, token.value, syntax);
28212 }
28213 }
28214
28215 replacementBinding.combinator = (values) {
28216 var newValue = new StringBuffer();
28217
28218 for (var i = 0; i < tokens.length; i++) {
28219 var token = tokens[i];
28220 if (token.isText) {
28221 newValue.write(token.value);
28222 } else {
28223 var value = values[i];
28224 if (value != null) {
28225 newValue.write(value);
28226 }
28227 }
28228 }
28229
28230 return newValue.toString();
28231 };
28232
28233 node.bind(name, replacementBinding, 'value');
28234 }
28235
28236 static void _bindOrDelegate(node, name, model, String path,
28237 CustomBindingSyntax syntax) {
28238
28239 if (syntax != null) {
28240 var delegateBinding = syntax.getBinding(model, path, name, node);
28241 if (delegateBinding != null) {
28242 model = delegateBinding;
28243 path = 'value';
28244 }
28245 }
28246
28247 node.bind(name, model, path);
28248 }
28249
28250 /**
28251 * Gets the [node]'s custom [Element.xtag] if present, otherwise returns
28252 * the node. This is used so nodes can override [Node.bind], [Node.unbind],
28253 * and [Node.unbindAll] like InputElement does.
28254 */
28255 // TODO(jmesserly): remove this when we can extend Element for real.
28256 static _nodeOrCustom(node) => node is Element ? node.xtag : node;
28257
28258 static List<_BindingToken> _parseMustacheTokens(String s) {
28259 var result = [];
28260 var length = s.length;
28261 var index = 0, lastIndex = 0;
28262 while (lastIndex < length) {
28263 index = s.indexOf('{{', lastIndex);
28264 if (index < 0) {
28265 result.add(new _BindingToken(s.substring(lastIndex)));
28266 break;
28267 } else {
28268 // There is a non-empty text run before the next path token.
28269 if (index > 0 && lastIndex < index) {
28270 result.add(new _BindingToken(s.substring(lastIndex, index)));
28271 }
28272 lastIndex = index + 2;
28273 index = s.indexOf('}}', lastIndex);
28274 if (index < 0) {
28275 var text = s.substring(lastIndex - 2);
28276 if (result.length > 0 && result.last.isText) {
28277 result.last.value += text;
28278 } else {
28279 result.add(new _BindingToken(text));
28280 }
28281 break;
28282 }
28283
28284 var value = s.substring(lastIndex, index).trim();
28285 result.add(new _BindingToken(value, isBinding: true));
28286 lastIndex = index + 2;
28287 }
28288 }
28289 return result;
28290 }
28291
28292 static void _addTemplateInstanceRecord(fragment, model) {
28293 if (fragment.firstChild == null) {
28294 return;
28295 }
28296
28297 var instanceRecord = new TemplateInstance(
28298 fragment.firstChild, fragment.lastChild, model);
28299
28300 var node = instanceRecord.firstNode;
28301 while (node != null) {
28302 node._templateInstance = instanceRecord;
28303 node = node.nextNode;
28304 }
28305 }
28306
28307 static void _removeAllBindingsRecursively(Node node) {
28308 _nodeOrCustom(node).unbindAll();
28309 for (var c = node.firstChild; c != null; c = c.nextNode) {
28310 _removeAllBindingsRecursively(c);
28311 }
28312 }
28313
28314 static void _removeChild(Node parent, Node child) {
28315 child._templateInstance = null;
28316 if (child is Element && (child as Element).isTemplate) {
28317 Element childElement = child;
28318 // Make sure we stop observing when we remove an element.
28319 var templateIterator = childElement._templateIterator;
28320 if (templateIterator != null) {
28321 templateIterator.abandon();
28322 childElement._templateIterator = null;
28323 }
28324 }
28325 child.remove();
28326 _removeAllBindingsRecursively(child);
28327 }
28328 }
28329
28330 class _BindingToken {
28331 final String value;
28332 final bool isBinding;
28333
28334 _BindingToken(this.value, {this.isBinding: false});
28335
28336 bool get isText => !isBinding;
28337 }
28338
28339 class _TemplateIterator {
28340 final Element _templateElement;
28341 final List<Node> terminators = [];
28342 final CompoundBinding inputs;
28343 List iteratedValue;
28344
28345 StreamSubscription _sub;
28346 StreamSubscription _valueBinding;
28347
28348 _TemplateIterator(this._templateElement)
28349 : inputs = new CompoundBinding(resolveInputs) {
28350
28351 _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged);
28352 }
28353
28354 static Object resolveInputs(Map values) {
28355 if (values.containsKey('if') && !_Bindings._toBoolean(values['if'])) {
28356 return null;
28357 }
28358
28359 if (values.containsKey('repeat')) {
28360 return values['repeat'];
28361 }
28362
28363 if (values.containsKey('bind')) {
28364 return [values['bind']];
28365 }
28366
28367 return null;
28368 }
28369
28370 void valueChanged(value) {
28371 clear();
28372 if (value is! List) return;
28373
28374 iteratedValue = value;
28375
28376 if (value is Observable) {
28377 _sub = value.changes.listen(_handleChanges);
28378 }
28379
28380 int len = iteratedValue.length;
28381 if (len > 0) {
28382 _handleChanges([new ListChangeRecord(0, addedCount: len)]);
28383 }
28384 }
28385
28386 Node getTerminatorAt(int index) {
28387 if (index == -1) return _templateElement;
28388 var terminator = terminators[index];
28389 if (terminator is! Element) return terminator;
28390
28391 var subIterator = terminator._templateIterator;
28392 if (subIterator == null) return terminator;
28393
28394 return subIterator.getTerminatorAt(subIterator.terminators.length - 1);
28395 }
28396
28397 void insertInstanceAt(int index, Node fragment) {
28398 var previousTerminator = getTerminatorAt(index - 1);
28399 var terminator = fragment.lastChild;
28400 if (terminator == null) terminator = previousTerminator;
28401
28402 terminators.insert(index, terminator);
28403 var parent = _templateElement.parentNode;
28404 parent.insertBefore(fragment, previousTerminator.nextNode);
28405 }
28406
28407 void removeInstanceAt(int index) {
28408 var previousTerminator = getTerminatorAt(index - 1);
28409 var terminator = getTerminatorAt(index);
28410 terminators.removeAt(index);
28411
28412 var parent = _templateElement.parentNode;
28413 while (terminator != previousTerminator) {
28414 var node = terminator;
28415 terminator = node.previousNode;
28416 _Bindings._removeChild(parent, node);
28417 }
28418 }
28419
28420 void removeAllInstances() {
28421 if (terminators.length == 0) return;
28422
28423 var previousTerminator = _templateElement;
28424 var terminator = getTerminatorAt(terminators.length - 1);
28425 terminators.length = 0;
28426
28427 var parent = _templateElement.parentNode;
28428 while (terminator != previousTerminator) {
28429 var node = terminator;
28430 terminator = node.previousNode;
28431 _Bindings._removeChild(parent, node);
28432 }
28433 }
28434
28435 void clear() {
28436 unobserve();
28437 removeAllInstances();
28438 iteratedValue = null;
28439 }
28440
28441 getInstanceModel(model, syntax) {
28442 if (syntax != null) {
28443 return syntax.getInstanceModel(_templateElement, model);
28444 }
28445 return model;
28446 }
28447
28448 getInstanceFragment(syntax) {
28449 if (syntax != null) {
28450 return syntax.getInstanceFragment(_templateElement);
28451 }
28452 return _templateElement.createInstance();
28453 }
28454
28455 void _handleChanges(List<ListChangeRecord> splices) {
28456 var syntax = TemplateElement.syntax[_templateElement.attributes['syntax']];
28457
28458 for (var splice in splices) {
28459 if (splice is! ListChangeRecord) continue;
28460
28461 for (int i = 0; i < splice.removedCount; i++) {
28462 removeInstanceAt(splice.index);
28463 }
28464
28465 for (var addIndex = splice.index;
28466 addIndex < splice.index + splice.addedCount;
28467 addIndex++) {
28468
28469 var model = getInstanceModel(iteratedValue[addIndex], syntax);
28470
28471 var fragment = getInstanceFragment(syntax);
28472
28473 _Bindings._addBindings(fragment, model, syntax);
28474 _Bindings._addTemplateInstanceRecord(fragment, model);
28475
28476 insertInstanceAt(addIndex, fragment);
28477 }
28478 }
28479 }
28480
28481 void unobserve() {
28482 if (_sub == null) return;
28483 _sub.cancel();
28484 _sub = null;
28485 }
28486
28487 void abandon() {
28488 unobserve();
28489 _valueBinding.cancel();
28490 inputs.dispose();
28491 }
28492 }
28493 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 27641 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
28494 // for details. All rights reserved. Use of this source code is governed by a 27642 // for details. All rights reserved. Use of this source code is governed by a
28495 // BSD-style license that can be found in the LICENSE file. 27643 // BSD-style license that can be found in the LICENSE file.
28496 27644
28497 27645
28498 class _HttpRequestUtils { 27646 class _HttpRequestUtils {
28499 27647
28500 // Helper for factory HttpRequest.get 27648 // Helper for factory HttpRequest.get
28501 static HttpRequest get(String url, 27649 static HttpRequest get(String url,
28502 onComplete(HttpRequest request), 27650 onComplete(HttpRequest request),
(...skipping 1111 matching lines...) Expand 10 before | Expand all | Expand 10 after
29614 _position = nextPosition; 28762 _position = nextPosition;
29615 return true; 28763 return true;
29616 } 28764 }
29617 _current = null; 28765 _current = null;
29618 _position = _array.length; 28766 _position = _array.length;
29619 return false; 28767 return false;
29620 } 28768 }
29621 28769
29622 T get current => _current; 28770 T get current => _current;
29623 } 28771 }
OLDNEW
« no previous file with comments | « sdk/lib/_internal/libraries.dart ('k') | sdk/lib/html/dartium/html_dartium.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698