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

Side by Side Diff: pkg/polymer/lib/src/declaration.dart

Issue 24149003: Port of github.com/polymer/polymer. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: rebase Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/polymer/lib/src/build/utils.dart ('k') | pkg/polymer/lib/src/instance.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of polymer;
6
7 /**
8 * **Deprecated**: use [Polymer.register] instead.
9 *
10 * Registers a [PolymerElement]. This is similar to [registerCustomElement]
11 * but it is designed to work with the `<element>` element and adds additional
12 * features.
13 */
14 @deprecated
15 void registerPolymerElement(String localName, PolymerElement create()) {
16 Polymer._registerClassMirror(localName, reflect(create()).type);
17 }
18
19 /**
20 * **Warning**: this class is experiental and subject to change.
21 *
22 * The implementation for the `polymer-element` element.
23 *
24 * Normally you do not need to use this class directly, see [PolymerElement].
25 */
26 class PolymerDeclaration extends CustomElement {
27 // Fully ported from revision:
28 // https://github.com/Polymer/polymer/blob/4dc481c11505991a7c43228d3797d28f212 67779
29 //
30 // src/declaration/attributes.js
31 // src/declaration/events.js
32 // src/declaration/polymer-element.js
33 // src/declaration/properties.js
34 // src/declaration/prototype.js (note: most code not needed in Dart)
35 // src/declaration/styles.js
36 //
37 // Not yet ported:
38 // src/declaration/path.js - blocked on HTMLImports.getDocumentUrl
39
40 // TODO(jmesserly): these should be Type not ClassMirror. But we can't get
41 // from ClassMirror to Type yet in dart2js, so we use ClassMirror for now.
42 // See https://code.google.com/p/dart/issues/detail?id=12607
43 ClassMirror _type;
44 ClassMirror get type => _type;
45
46 // TODO(jmesserly): this is a cache, because it's tricky in Dart to get from
47 // ClassMirror -> Supertype.
48 ClassMirror _supertype;
49 ClassMirror get supertype => _supertype;
50
51 // TODO(jmesserly): this is also a cache, since we can't store .element on
52 // each level of the __proto__ like JS does.
53 PolymerDeclaration _super;
54 PolymerDeclaration get superDeclaration => _super;
55
56 String _name;
57 String get name => _name;
58
59 /**
60 * Map of publish properties. Can be a [VariableMirror] or a [MethodMirror]
61 * representing a getter. If it is a getter, there will also be a setter.
62 */
63 Map<String, DeclarationMirror> _publish;
64
65 /** The names of published properties for this polymer-element. */
66 Iterable<String> get publishedProperties =>
67 _publish != null ? _publish.keys : const [];
68
69 /** Same as [_publish] but with lower case names. */
70 Map<String, DeclarationMirror> _publishLC;
71
72 Map<String, Symbol> _observe;
73
74 Map<Symbol, Object> _instanceAttributes;
75
76 List<Element> _sheets;
77 List<Element> get sheets => _sheets;
78
79 List<Element> _styles;
80 List<Element> get styles => _styles;
81
82 DocumentFragment get templateContent {
83 final template = query('template');
84 return template != null ? template.content : null;
85 }
86
87 /** Maps event names and their associated method in the element class. */
88 final Map<String, String> _eventDelegates = {};
89
90 /** Expected events per element node. */
91 // TODO(sigmund): investigate whether we need more than 1 set of local events
92 // per element (why does the js implementation stores 1 per template node?)
93 Expando<Set<String>> _templateDelegates;
94
95 void created() {
96 super.created();
97
98 // fetch the element name
99 _name = attributes['name'];
100 // install element definition, if ready
101 registerWhenReady();
102 }
103
104 void registerWhenReady() {
105 // if we have no prototype, wait
106 if (waitingForType(name)) {
107 return;
108 }
109 // fetch our extendee name
110 var extendee = attributes['extends'];
111 if (waitingForExtendee(extendee)) {
112 //console.warn(name + ': waitingForExtendee:' + extendee);
113 return;
114 }
115 // TODO(sjmiles): HTMLImports polyfill awareness:
116 // elements in the main document are likely to parse
117 // in advance of elements in imports because the
118 // polyfill parser is simulated
119 // therefore, wait for imports loaded before
120 // finalizing elements in the main document
121 // TODO(jmesserly): Polymer.dart waits for HTMLImportsLoaded, so I've
122 // removed "whenImportsLoaded" for now. Restore the workaround if needed.
123 _register(extendee);
124 }
125
126 void _register(extendee) {
127 //console.group('registering', name);
128 register(name, extendee);
129 //console.groupEnd();
130 // subclasses may now register themselves
131 _notifySuper(name);
132 }
133
134 bool waitingForType(String name) {
135 if (_getRegisteredType(name) != null) return false;
136
137 // then wait for a prototype
138 _waitType[name] = this;
139 // if explicitly marked as 'noscript'
140 if (attributes.containsKey('noscript')) {
141 // TODO(sorvell): CustomElements polyfill awareness:
142 // noscript elements should upgrade in logical order
143 // script injection ensures this under native custom elements;
144 // under imports + ce polyfills, scripts run before upgrades.
145 // dependencies should be ready at upgrade time so register
146 // prototype at this time.
147 // TODO(jmesserly): I'm not sure how to port this; since script
148 // injection doesn't work for Dart, we'll just call Polymer.register
149 // here and hope for the best.
150 Polymer.register(name);
151 }
152 return true;
153 }
154
155 bool waitingForExtendee(String extendee) {
156 // if extending a custom element...
157 if (extendee != null && extendee.indexOf('-') >= 0) {
158 // wait for the extendee to be _registered first
159 if (!_isRegistered(extendee)) {
160 _waitSuper.putIfAbsent(extendee, () => []).add(this);
161 return true;
162 }
163 }
164 return false;
165 }
166
167 void register(String name, String extendee) {
168 // build prototype combining extendee, Polymer base, and named api
169 buildType(name, extendee);
170
171 // back reference declaration element
172 // TODO(sjmiles): replace `element` with `elementElement` or `declaration`
173 _declarations[_type] = this;
174
175 // more declarative features
176 desugar();
177
178 // TODO(sorvell): install a helper method this.resolvePath to aid in
179 // setting resource paths. e.g.
180 // this.$.image.src = this.resolvePath('images/foo.png')
181 // Potentially remove when spec bug is addressed.
182 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=21407
183 // TODO(jmesserly): resolvePath not ported, see first comment in this class.
184
185 // under ShadowDOMPolyfill, transforms to approximate missing CSS features
186 _shimShadowDomStyling(templateContent, name);
187
188 // register our custom element
189 registerType(name);
190
191 // NOTE: skip in Dart because we don't have mutable global scope.
192 // reference constructor in a global named by 'constructor' attribute
193 // publishConstructor();
194 }
195
196 /**
197 * Gets the Dart type registered for this name, and sets up declarative
198 * features. Fills in the [type] and [supertype] fields.
199 *
200 * *Note*: unlike the JavaScript version, we do not have to metaprogram the
201 * prototype, which simplifies this method.
202 */
203 void buildType(String name, String extendee) {
204 // get our custom type
205 _type = _getRegisteredType(name);
206
207 // get basal prototype
208 _supertype = _getRegisteredType(extendee);
209 if (supertype != null) _super = _getDeclaration(supertype);
210
211 // transcribe `attributes` declarations onto own prototype's `publish`
212 publishAttributes(type, _super);
213
214 publishProperties(type);
215
216 inferObservers(type);
217
218 // Skip the rest in Dart:
219 // chain various meta-data objects to inherited versions
220 // chain custom api to inherited
221 // build side-chained lists to optimize iterations
222 // inherit publishing meta-data
223 //this.inheritAttributesObjects(prototype);
224 //this.inheritDelegates(prototype);
225 // x-platform fixups
226 }
227
228 /** Implement various declarative features. */
229 void desugar() {
230 // compile list of attributes to copy to instances
231 accumulateInstanceAttributes();
232 // parse on-* delegates declared on `this` element
233 parseHostEvents();
234 // parse on-* delegates declared in templates
235 parseLocalEvents();
236 // install external stylesheets as if they are inline
237 installSheets();
238 // TODO(jmesserly): this feels unnatrual in Dart. Since we have convenient
239 // lazy static initialization, can we get by without it?
240 var registered = type.methods[const Symbol('registerCallback')];
241 if (registered != null && registered.isStatic &&
242 registered.isRegularMethod) {
243 type.invoke(const Symbol('registerCallback'), [this]);
244 }
245
246 }
247
248 void registerType(String name) {
249 // TODO(jmesserly): document.register
250 registerCustomElement(name, () =>
251 type.newInstance(const Symbol(''), const []).reflectee);
252 }
253
254 void publishAttributes(ClassMirror type, PolymerDeclaration superDecl) {
255 // get properties to publish
256 if (superDecl != null && superDecl._publish != null) {
257 _publish = new Map.from(superDecl._publish);
258 }
259 _publish = _getProperties(type, _publish, (x) => x is PublishedProperty);
260
261 // merge names from 'attributes' attribute
262 var attrs = attributes['attributes'];
263 if (attrs != null) {
264 // names='a b c' or names='a,b,c'
265 // record each name for publishing
266 for (var attr in attrs.split(attrs.contains(',') ? ',' : ' ')) {
267 // remove excess ws
268 attr = attr.trim();
269
270 // do not override explicit entries
271 if (_publish != null && _publish.containsKey(attr)) continue;
272
273 var property = new Symbol(attr);
274 var mirror = type.variables[property];
275 if (mirror == null) {
276 mirror = type.getters[property];
277 if (mirror != null && !_hasSetter(type, mirror)) mirror = null;
278 }
279 if (mirror == null) {
280 window.console.warn('property for attribute $attr of polymer-element '
281 'name=$name not found.');
282 continue;
283 }
284 if (_publish == null) _publish = {};
285 _publish[attr] = mirror;
286 }
287 }
288
289 // NOTE: the following is not possible in Dart; fields must be declared.
290 // install 'attributes' as properties on the prototype,
291 // but don't override
292 }
293
294 void accumulateInstanceAttributes() {
295 // inherit instance attributes
296 _instanceAttributes = new Map<Symbol, Object>();
297 if (_super != null) _instanceAttributes.addAll(_super._instanceAttributes);
298
299 // merge attributes from element
300 attributes.forEach((name, value) {
301 if (isInstanceAttribute(name)) {
302 _instanceAttributes[new Symbol(name)] = value;
303 }
304 });
305 }
306
307 static bool isInstanceAttribute(name) {
308 // do not clone these attributes onto instances
309 final blackList = const {
310 'name': 1, 'extends': 1, 'constructor': 1, 'noscript': 1,
311 'attributes': 1};
312
313 return !blackList.containsKey(name) && !name.startsWith('on-');
314 }
315
316 /** Extracts events from the element tag attributes. */
317 void parseHostEvents() {
318 addAttributeDelegates(_eventDelegates);
319 }
320
321 void addAttributeDelegates(Map<String, String> delegates) {
322 attributes.forEach((name, value) {
323 if (_hasEventPrefix(name)) {
324 delegates[_removeEventPrefix(name)] = value;
325 }
326 });
327 }
328
329 /** Extracts events under the element's <template>. */
330 void parseLocalEvents() {
331 for (var t in queryAll('template')) {
332 final events = new Set<String>();
333 // acquire delegates from entire subtree at t
334 accumulateTemplatedEvents(t, events);
335 if (events.isNotEmpty) {
336 // store delegate information directly on template
337 if (_templateDelegates == null) {
338 _templateDelegates = new Expando<Set<String>>();
339 }
340 _templateDelegates[t] = events;
341 }
342 }
343 }
344
345 void accumulateTemplatedEvents(Element node, Set<String> events) {
346 if (node.localName == 'template' && node.content != null) {
347 accumulateChildEvents(node.content, events);
348 }
349 }
350
351 void accumulateChildEvents(node, Set<String> events) {
352 assert(node is Element || node is DocumentFragment);
353 for (var n in node.children) {
354 accumulateEvents(n, events);
355 }
356 }
357
358 void accumulateEvents(Element node, Set<String> events) {
359 accumulateAttributeEvents(node, events);
360 accumulateChildEvents(node, events);
361 accumulateTemplatedEvents(node, events);
362 }
363
364 void accumulateAttributeEvents(Element node, Set<String> events) {
365 for (var name in node.attributes.keys) {
366 if (_hasEventPrefix(name)) {
367 accumulateEvent(_removeEventPrefix(name), events);
368 }
369 }
370 }
371
372 void accumulateEvent(String name, Set<String> events) {
373 var translated = _eventTranslations[name];
374 events.add(translated != null ? translated : name);
375 }
376
377 String urlToPath(String url) {
378 if (url == null) return '';
379 return (url.split('/')..removeLast()..add('')).join('/');
380 }
381
382 /**
383 * Install external stylesheets loaded in <element> elements into the
384 * element's template.
385 */
386 void installSheets() {
387 cacheSheets();
388 cacheStyles();
389 installLocalSheets();
390 installGlobalStyles();
391 }
392
393 void cacheSheets() {
394 _sheets = findNodes(_SHEET_SELECTOR);
395 for (var s in sheets) s.remove();
396 }
397
398 void cacheStyles() {
399 _styles = findNodes('$_STYLE_SELECTOR[$_SCOPE_ATTR]');
400 for (var s in styles) s.remove();
401 }
402
403 /**
404 * Takes external stylesheets loaded in an `<element>` element and moves
405 * their content into a style element inside the `<element>`'s template.
406 * The sheet is then removed from the `<element>`. This is done only so
407 * that if the element is loaded in the main document, the sheet does
408 * not become active.
409 * Note, ignores sheets with the attribute 'polymer-scope'.
410 */
411 void installLocalSheets() {
412 var sheets = this.sheets.where(
413 (s) => !s.attributes.containsKey(_SCOPE_ATTR));
414 var content = this.templateContent;
415 if (content != null) {
416 var cssText = new StringBuffer();
417 for (var sheet in sheets) {
418 cssText..write(_cssTextFromSheet(sheet))..write('\n');
419 }
420 if (cssText.length > 0) {
421 content.insertBefore(
422 new StyleElement()..text = '$cssText',
423 content.firstChild);
424 }
425 }
426 }
427
428 List<Element> findNodes(String selector, [bool matcher(Element e)]) {
429 var nodes = this.queryAll(selector).toList();
430 var content = this.templateContent;
431 if (content != null) {
432 nodes = nodes..addAll(content.queryAll(selector));
433 }
434 if (matcher != null) return nodes.where(matcher).toList();
435 return nodes;
436 }
437
438 /**
439 * Promotes external stylesheets and style elements with the attribute
440 * polymer-scope='global' into global scope.
441 * This is particularly useful for defining @keyframe rules which
442 * currently do not function in scoped or shadow style elements.
443 * (See wkb.ug/72462)
444 */
445 // TODO(sorvell): remove when wkb.ug/72462 is addressed.
446 void installGlobalStyles() {
447 var style = styleForScope(_STYLE_GLOBAL_SCOPE);
448 _applyStyleToScope(style, document.head);
449 }
450
451 String cssTextForScope(String scopeDescriptor) {
452 var cssText = new StringBuffer();
453 // handle stylesheets
454 var selector = '[$_SCOPE_ATTR=$scopeDescriptor]';
455 matcher(s) => s.matches(selector);
456
457 for (var sheet in sheets.where(matcher)) {
458 cssText..write(_cssTextFromSheet(sheet))..write('\n\n');
459 }
460 // handle cached style elements
461 for (var style in styles.where(matcher)) {
462 cssText..write(style.textContent)..write('\n\n');
463 }
464 return cssText.toString();
465 }
466
467 StyleElement styleForScope(String scopeDescriptor) {
468 var cssText = cssTextForScope(scopeDescriptor);
469 return cssTextToScopeStyle(cssText, scopeDescriptor);
470 }
471
472 StyleElement cssTextToScopeStyle(String cssText, String scopeDescriptor) {
473 if (cssText == '') return null;
474
475 return new StyleElement()
476 ..text = cssText
477 ..attributes[_STYLE_SCOPE_ATTRIBUTE] = '$name-$scopeDescriptor';
478 }
479
480 /**
481 * fetch a list of all observable properties names in our inheritance chain
482 * above Polymer.
483 */
484 // TODO(sjmiles): perf: reflection is slow, relatively speaking
485 // If an element may take 6us to create, getCustomPropertyNames might
486 // cost 1.6us more.
487 void inferObservers(ClassMirror type) {
488 for (var method in type.methods.values) {
489 if (method.isStatic || !method.isRegularMethod) continue;
490
491 String name = MirrorSystem.getName(method.simpleName);
492 if (name.endsWith('Changed')) {
493 if (_observe == null) _observe = {};
494 name = name.substring(0, name.length - 7);
495 _observe[name] = method.simpleName;
496 }
497 }
498 }
499
500 void publishProperties(ClassMirror type) {
501 // Dart note: _publish was already populated by publishAttributes
502 if (_publish != null) _publishLC = _lowerCaseMap(_publish);
503 }
504
505 Map<String, dynamic> _lowerCaseMap(Map<String, dynamic> properties) {
506 final map = new Map<String, dynamic>();
507 properties.forEach((name, value) {
508 map[name.toLowerCase()] = value;
509 });
510 return map;
511 }
512 }
513
514 /// maps tag names to prototypes
515 final Map _typesByName = new Map<String, ClassMirror>();
516
517 ClassMirror _getRegisteredType(String name) => _typesByName[name];
518
519 /// elements waiting for prototype, by name
520 final Map _waitType = new Map<String, PolymerDeclaration>();
521
522 void _notifyType(String name) {
523 var waiting = _waitType.remove(name);
524 if (waiting != null) waiting.registerWhenReady();
525 }
526
527 /// elements waiting for super, by name
528 final Map _waitSuper = new Map<String, List<PolymerDeclaration>>();
529
530 void _notifySuper(String name) {
531 _registered.add(name);
532 var waiting = _waitSuper.remove(name);
533 if (waiting != null) {
534 for (var w in waiting) {
535 w.registerWhenReady();
536 }
537 }
538 }
539
540 /// track document.register'ed tag names
541 final Set _registered = new Set<String>();
542
543 bool _isRegistered(name) => _registered.contains(name);
544
545 final Map _declarations = new Map<ClassMirror, PolymerDeclaration>();
546
547 PolymerDeclaration _getDeclaration(ClassMirror type) => _declarations[type];
548
549 final _objectType = reflectClass(Object);
550
551 Map _getProperties(ClassMirror type, Map props, bool matches(metadata)) {
552 for (var field in type.variables.values) {
553 if (field.isFinal || field.isStatic || field.isPrivate) continue;
554
555 for (var meta in field.metadata) {
556 if (matches(meta.reflectee)) {
557 if (props == null) props = {};
558 props[MirrorSystem.getName(field.simpleName)] = field;
559 break;
560 }
561 }
562 }
563
564 for (var getter in type.getters.values) {
565 if (getter.isStatic || getter.isPrivate) continue;
566
567 for (var meta in getter.metadata) {
568 if (matches(meta.reflectee)) {
569 if (_hasSetter(type, getter)) {
570 if (props == null) props = {};
571 props[MirrorSystem.getName(getter.simpleName)] = getter;
572 }
573 break;
574 }
575 }
576 }
577
578 return props;
579 }
580
581 bool _hasSetter(ClassMirror type, MethodMirror getter) {
582 var setterName = new Symbol('${MirrorSystem.getName(getter.simpleName)}=');
583 return type.setters.containsKey(setterName);
584 }
585
586 bool _inDartHtml(ClassMirror type) =>
587 type.owner.simpleName == const Symbol('dart.dom.html');
588
589
590 /** Attribute prefix used for declarative event handlers. */
591 const _EVENT_PREFIX = 'on-';
592
593 /** Whether an attribute declares an event. */
594 bool _hasEventPrefix(String attr) => attr.startsWith(_EVENT_PREFIX);
595
596 String _removeEventPrefix(String name) => name.substring(_EVENT_PREFIX.length);
597
598 /**
599 * Using Polymer's platform/src/ShadowCSS.js passing the style tag's content.
600 */
601 void _shimShadowDomStyling(DocumentFragment template, String localName) {
602 if (js.context == null || template == null) return;
603
604 var platform = js.context['Platform'];
605 if (platform == null) return;
606
607 var style = template.query('style');
608 if (style == null) return;
609
610 var shadowCss = platform['ShadowCSS'];
611 if (shadowCss == null) return;
612
613 // TODO(terry): Remove calls to shimShadowDOMStyling2 and replace with
614 // shimShadowDOMStyling when we support unwrapping dart:html
615 // Element to a JS DOM node.
616 var shimShadowDOMStyling2 = shadowCss['shimShadowDOMStyling2'];
617 if (shimShadowDOMStyling2 == null) return;
618
619 var scopedCSS = shimShadowDOMStyling2.apply(shadowCss,
620 [style.text, localName]);
621
622 // TODO(terry): Remove when shimShadowDOMStyling is called we don't need to
623 // replace original CSS with scoped CSS shimShadowDOMStyling
624 // does that.
625 style.text = scopedCSS;
626 }
627
628 const _STYLE_SELECTOR = 'style';
629 const _SHEET_SELECTOR = '[rel=stylesheet]';
630 const _STYLE_GLOBAL_SCOPE = 'global';
631 const _SCOPE_ATTR = 'polymer-scope';
632 const _STYLE_SCOPE_ATTRIBUTE = 'element';
633
634 void _applyStyleToScope(StyleElement style, Node scope) {
635 if (style == null) return;
636
637 // TODO(sorvell): necessary for IE
638 // see https://connect.microsoft.com/IE/feedback/details/790212/
639 // cloning-a-style-element-and-adding-to-document-produces
640 // -unexpected-result#details
641 // var clone = style.cloneNode(true);
642 var clone = new StyleElement()..text = style.text;
643
644 var attr = style.attributes[_STYLE_SCOPE_ATTRIBUTE];
645 if (attr != null) {
646 clone.attributes[_STYLE_SCOPE_ATTRIBUTE] = attr;
647 }
648
649 scope.append(clone);
650 }
651
652 String _cssTextFromSheet(Element sheet) {
653 if (sheet == null || js.context == null) return '';
654
655 // TODO(jmesserly): this is a hacky way to communcate with HTMLImports...
656
657 // Remove rel=stylesheet, href to keep this inert.
658 var href = sheet.attributes.remove('href');
659 var rel = sheet.attributes.remove('rel');
660 document.body.append(sheet);
661 String resource;
662 try {
663 resource = js.context['document']['body']['lastChild']['__resource'];
664 } finally {
665 sheet.remove();
666 if (href != null) sheet.attributes['href'] = href;
667 if (rel != null) sheet.attributes['rel'] = rel;
668 }
669 return resource != null ? resource : '';
670 }
671
672 const _OBSERVE_SUFFIX = 'Changed';
673
674 // TODO(jmesserly): is this list complete?
675 final _eventTranslations = const {
676 // TODO(jmesserly): these three Polymer.js translations won't work in Dart,
677 // because we strip the webkit prefix (below). Reconcile.
678 'webkitanimationstart': 'webkitAnimationStart',
679 'webkitanimationend': 'webkitAnimationEnd',
680 'webkittransitionend': 'webkitTransitionEnd',
681
682 'domfocusout': 'DOMFocusOut',
683 'domfocusin': 'DOMFocusIn',
684
685 // TODO(jmesserly): Dart specific renames. Reconcile with Polymer.js
686 'animationend': 'webkitAnimationEnd',
687 'animationiteration': 'webkitAnimationIteration',
688 'animationstart': 'webkitAnimationStart',
689 'doubleclick': 'dblclick',
690 'fullscreenchange': 'webkitfullscreenchange',
691 'fullscreenerror': 'webkitfullscreenerror',
692 'keyadded': 'webkitkeyadded',
693 'keyerror': 'webkitkeyerror',
694 'keymessage': 'webkitkeymessage',
695 'needkey': 'webkitneedkey',
696 'speechchange': 'webkitSpeechChange',
697 };
698
699 final _reverseEventTranslations = () {
700 final map = new Map<String, String>();
701 _eventTranslations.forEach((onName, eventType) {
702 map[eventType] = onName;
703 });
704 return map;
705 }();
706
707 // Dart note: we need this function because we have additional renames JS does
708 // not have. The JS renames are simply case differences, whereas we have ones
709 // like doubleclick -> dblclick and stripping the webkit prefix.
710 String _eventNameFromType(String eventType) {
711 final result = _reverseEventTranslations[eventType];
712 return result != null ? result : eventType;
713 }
OLDNEW
« no previous file with comments | « pkg/polymer/lib/src/build/utils.dart ('k') | pkg/polymer/lib/src/instance.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698