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

Side by Side Diff: sky/framework/sky-element/sky-element.sky

Issue 845283003: Allow on-* event handlers on <sky-element>. (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: s p a c i n g Created 5 years, 11 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
« no previous file with comments | « sky/framework/sky-checkbox/sky-checkbox.sky ('k') | sky/framework/sky-radio/sky-radio.sky » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 <!-- 1 <!--
2 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Copyright 2014 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be 3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file. 4 // found in the LICENSE file.
5 --> 5 -->
6 <import src="sky-binder.sky" as="binder" /> 6 <import src="sky-binder.sky" as="binder" />
7 <script> 7 <script>
8 var templates = new Map();
9
10 var attributeConverters = { 8 var attributeConverters = {
11 boolean: function(value) { 9 boolean: function(value) {
12 if (typeof value == 'string') 10 if (typeof value == 'string')
13 return value == 'true'; 11 return value == 'true';
14 return !!value; 12 return !!value;
15 }, 13 },
16 number: function(value) { 14 number: function(value) {
17 return Number(value); 15 return Number(value);
18 }, 16 },
19 string: function(value) { 17 string: function(value) {
20 if (value === null) 18 if (value === null)
21 return ''; 19 return '';
22 return String(value); 20 return String(value);
23 }, 21 },
24 }; 22 };
25 23
26 function defineReflectedAttribute(prototype, converter, name) { 24 function parseAttributeSpec(spec) {
27 Object.defineProperty(prototype, name, { 25 var attributes = new Map();
28 get: function() { 26 var attributeTokens = (spec || '').split(',');
29 return converter(this.getAttribute(name));
30 },
31 set: function(newValue) {
32 this.setAttribute(name, converter(newValue));
33 },
34 enumerable: true,
35 configurable: true,
36 });
37
38 prototype[name + 'AttributeChanged'] = function(oldValue, newValue) {
39 this.notifyPropertyChanged(name, converter(oldValue), converter(newValue));
40 };
41 }
42
43 function defineReflectedAttributes(elementClass, tagName, list) {
44 var attributeTokens = (list || '').split(',');
45 var attributeNames = [];
46 var prototype = elementClass.prototype;
47 27
48 for (var i = 0; i < attributeTokens.length; ++i) { 28 for (var i = 0; i < attributeTokens.length; ++i) {
49 var parts = attributeTokens[i].split(':'); 29 var parts = attributeTokens[i].split(':');
50 var name = parts[0].trim(); 30 var name = parts[0].trim();
51 var type = (parts[1] || '').trim(); 31 var type = (parts[1] || '').trim();
52 var converter = attributeConverters[type] || attributeConverters.string; 32 var converter = attributeConverters[type] || attributeConverters.string;
53 33
54 attributeNames.push(name); 34 attributes.set(name, converter);
55 defineReflectedAttribute(prototype, converter, name);
56 } 35 }
57 36
58 binder.registerElement(tagName, { 37 return attributes;
59 attributeNames: attributeNames,
60 });
61 } 38 }
62 39
40 function collectEventHandlers(definition) {
41 var eventHandlers = [];
42 var attributes = definition.getAttributes();
43
44 for (var i = 0; i < attributes.length; i++) {
45 var attr = attributes[i];
46 var name = attr.name;
47 var value = attr.value;
48
49 if (name.startsWith('on-')) {
50 eventHandlers.push(name.substring(3));
51 }
52 }
53
54 return eventHandlers;
55 }
56
57 function eventHandlerCallback(event) {
58 var element = event.currentTarget;
59 var registration = registrations.get(element.localName);
60 var method = registration.getEventHandler(event.type);
61 var handler = element[method];
62 if (handler instanceof Function)
63 return handler.call(element, event);
64 }
65
66 class ElementRegistration {
67 constructor(definition) {
68 this.definition = definition;
69 this.tagName = definition.getAttribute('name');
70 this.attributes = parseAttributeSpec(definition.getAttribute('attributes'));
71 this.eventHandlers = collectEventHandlers(definition);
72 this.template = definition.querySelector('template');
73 Object.preventExtensions(this);
74 }
75
76 getEventHandler(eventName) {
77 return this.definition.getAttribute('on-' + eventName);
78 }
79
80 getAttributeNames() {
81 // TODO(esprehn): We can replace this method with
82 // Array.from(registration.attributes) once we turn that on.
83 var names = []
84 this.attributes.forEach(function(converter, name) {
85 names.push(name);
86 });
87 return names;
88 }
89
90 synthesizeAttributes(prototype) {
91 this.attributes.forEach(function(converter, name) {
92 Object.defineProperty(prototype, name, {
93 get: function() {
94 return converter(this.getAttribute(name));
95 },
96 set: function(newValue) {
97 this.setAttribute(name, converter(newValue));
98 },
99 enumerable: true,
100 configurable: true,
101 });
102 });
103 }
104 }
105
106 var registrations = new Map();
107
63 class SkyElement extends HTMLElement { 108 class SkyElement extends HTMLElement {
64 109
65 static register() { 110 static register() {
66 var wrapper = document.currentScript.parentNode; 111 var definition = document.currentScript.parentNode;
67 112
68 if (wrapper.localName !== 'sky-element') 113 if (definition.localName !== 'sky-element') {
69 throw new Error('No <sky-element>.'); 114 throw new Error('register() calls must be inside a <sky-element>.');
115 }
70 116
71 var tagName = wrapper.getAttribute('name'); 117 var registration = new ElementRegistration(definition);
72 if (!tagName) 118
119 if (!registration.tagName) {
73 throw new Error('<sky-element> must have a name.'); 120 throw new Error('<sky-element> must have a name.');
121 }
74 122
75 var template = wrapper.querySelector('template'); 123 if (registrations.has(registration.tagName)) {
76 if (template) 124 throw new Error('Duplicate registration for tag name: ' +
77 templates.set(tagName, template); 125 registration.tagName);
126 }
78 127
79 defineReflectedAttributes(this, tagName, 128 registration.synthesizeAttributes(this.prototype);
80 wrapper.getAttribute('attributes'));
81 129
82 return document.registerElement(tagName, { 130 // TODO(esprehn): Combine the two element registries here and in sky binder.
131 binder.registerElement(registration.tagName, {
132 attributeNames: registration.getAttributeNames(),
133 });
134
135 registrations.set(registration.tagName, registration);
136 return document.registerElement(registration.tagName, {
83 prototype: this.prototype, 137 prototype: this.prototype,
84 }); 138 });
85 } 139 }
86 140
87 created() { 141 created() {
88 // override 142 // override
89 } 143 }
90 144
91 attached() { 145 attached() {
92 // override 146 // override
(...skipping 16 matching lines...) Expand all
109 this.created(); 163 this.created();
110 164
111 Object.preventExtensions(this); 165 Object.preventExtensions(this);
112 166
113 // Invoke attributeChanged callback when element is first created too. 167 // Invoke attributeChanged callback when element is first created too.
114 var attributes = this.getAttributes(); 168 var attributes = this.getAttributes();
115 for (var i = 0; i < attributes.length; ++i) { 169 for (var i = 0; i < attributes.length; ++i) {
116 var attribute = attributes[i]; 170 var attribute = attributes[i];
117 this.attributeChangedCallback(attribute.name, null, attribute.value); 171 this.attributeChangedCallback(attribute.name, null, attribute.value);
118 } 172 }
173
174 var registration = registrations.get(this.localName);
175 for (var i = 0; i < registration.eventHandlers.length; ++i) {
176 var eventName = registration.eventHandlers[i];
177 this.addEventListener(eventName, eventHandlerCallback);
178 }
119 } 179 }
120 180
121 attachedCallback() { 181 attachedCallback() {
122 if (!this.shadowRoot) { 182 if (!this.shadowRoot) {
123 var template = templates.get(this.localName); 183 var registration = registrations.get(this.localName);
124 if (template) { 184 if (registration.template) {
125 var shadow = this.ensureShadowRoot(); 185 var shadow = this.ensureShadowRoot();
126 var instance = binder.createInstance(template, this); 186 var instance = binder.createInstance(registration.template, this);
127 shadow.appendChild(instance.fragment); 187 shadow.appendChild(instance.fragment);
128 this.shadowRootReady(); 188 this.shadowRootReady();
129 } 189 }
130 } 190 }
131 this.attached(); 191 this.attached();
132 this.isAttached = true; 192 this.isAttached = true;
133 } 193 }
134 194
135 detachedCallback() { 195 detachedCallback() {
136 this.detached(); 196 this.detached();
137 this.isAttached = false; 197 this.isAttached = false;
138 } 198 }
139 199
140 attributeChangedCallback(name, oldValue, newValue) { 200 attributeChangedCallback(name, oldValue, newValue) {
141 this.attributeChanged(name, oldValue, newValue); 201 this.attributeChanged(name, oldValue, newValue);
142 var handler = this[name + 'AttributeChanged']; 202 var registration = registrations.get(this.localName);
143 if (typeof handler == 'function') 203 var converter = registration.attributes.get(name);
144 handler.call(this, oldValue, newValue); 204 if (converter) {
205 this.notifyPropertyChanged(name, converter(oldValue),
206 converter(newValue));
207 }
145 } 208 }
146 209
147 notifyPropertyChanged(name, oldValue, newValue) { 210 notifyPropertyChanged(name, oldValue, newValue) {
148 var notifier = Object.getNotifier(this); 211 var notifier = Object.getNotifier(this);
149 notifier.notify({ 212 notifier.notify({
150 type: 'update', 213 type: 'update',
151 name: name, 214 name: name,
152 oldValue: oldValue, 215 oldValue: oldValue,
153 }); 216 });
154 var handler = this[name + 'Changed']; 217 var handler = this[name + 'Changed'];
155 if (typeof handler == 'function') 218 if (typeof handler == 'function')
156 handler.call(this, oldValue, newValue); 219 handler.call(this, oldValue, newValue);
157 } 220 }
158 }; 221 };
159 222
160 module.exports = SkyElement; 223 module.exports = SkyElement;
161 </script> 224 </script>
OLDNEW
« no previous file with comments | « sky/framework/sky-checkbox/sky-checkbox.sky ('k') | sky/framework/sky-radio/sky-radio.sky » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698