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

Unified 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: Add back attr validation. 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sky/framework/sky-element/sky-element.sky
diff --git a/sky/framework/sky-element/sky-element.sky b/sky/framework/sky-element/sky-element.sky
index ea4ef8bdb6fec06616d908b63c21dd75d4168f3c..8d141cba3326a64196e132e09f20b95acf44dac9 100644
--- a/sky/framework/sky-element/sky-element.sky
+++ b/sky/framework/sky-element/sky-element.sky
@@ -5,8 +5,6 @@
-->
<import src="sky-binder.sky" as="binder" />
<script>
-var templates = new Map();
-
var attributeConverters = {
boolean: function(value) {
if (typeof value == 'string')
@@ -23,27 +21,9 @@ var attributeConverters = {
},
};
-function defineReflectedAttribute(prototype, converter, name) {
- Object.defineProperty(prototype, name, {
- get: function() {
- return converter(this.getAttribute(name));
- },
- set: function(newValue) {
- this.setAttribute(name, converter(newValue));
- },
- enumerable: true,
- configurable: true,
- });
-
- prototype[name + 'AttributeChanged'] = function(oldValue, newValue) {
- this.notifyPropertyChanged(name, converter(oldValue), converter(newValue));
- };
-}
-
-function defineReflectedAttributes(elementClass, tagName, list) {
- var attributeTokens = (list || '').split(',');
- var attributeNames = [];
- var prototype = elementClass.prototype;
+function parseAttributeSpec(spec) {
+ var attributes = new Map();
+ var attributeTokens = (spec || '').split(',');
for (var i = 0; i < attributeTokens.length; ++i) {
var parts = attributeTokens[i].split(':');
@@ -51,35 +31,106 @@ function defineReflectedAttributes(elementClass, tagName, list) {
var type = (parts[1] || '').trim();
var converter = attributeConverters[type] || attributeConverters.string;
- attributeNames.push(name);
- defineReflectedAttribute(prototype, converter, name);
+ attributes.set(name, converter);
}
- binder.registerElement(tagName, {
- attributeNames: attributeNames,
- });
+ return attributes;
}
+function collectEventHandlers(definition) {
+ var eventHandlers = [];
+ var attributes = definition.getAttributes();
+
+ for (var i = 0; i < attributes.length; i++) {
+ var attr = attributes[i];
+ var name = attr.name;
+ var value = attr.value;
+
+ if (name.startsWith('on-')) {
+ eventHandlers.push(name.substring(3));
+ }
+ }
+
+ return eventHandlers;
+}
+
+function eventHandlerCallback(event) {
+ var element = event.currentTarget;
+ var registration = registrations.get(element.localName);
+ var method = registration.getEventHandler(event.type);
+ var handler = element[method];
+ if (handler instanceof Function)
+ return handler.call(element, event);
+}
+
+class ElementRegistration {
+ constructor(definition) {
+ this.definition = definition;
+ this.tagName = definition.getAttribute('name');
+ this.attributes = parseAttributeSpec(definition.getAttribute('attributes'));
+ this.eventHandlers = collectEventHandlers(definition);
+ this.template = definition.querySelector('template');
+ Object.preventExtensions(this);
+ }
+ getEventHandler(eventName) {
ojan 2015/01/13 01:02:46 Nit: I'd put an extra line break after each method
+ return this.definition.getAttribute('on-' + eventName);
+ }
+ getAttributeNames() {
+ // TODO(esprehn): We can replace this method with
+ // Array.from(registration.attributes) once we turn that on.
+ var names = []
+ this.attributes.forEach(function(converter, name) {
+ names.push(name);
+ });
+ return names;
+ }
+ synthesizeAttributes(prototype) {
+ this.attributes.forEach(function(converter, name) {
+ Object.defineProperty(prototype, name, {
+ get: function() {
+ return converter(this.getAttribute(name));
+ },
+ set: function(newValue) {
+ this.setAttribute(name, converter(newValue));
+ },
+ enumerable: true,
+ configurable: true,
+ });
+ });
+ }
+}
+
+var registrations = new Map();
+
class SkyElement extends HTMLElement {
static register() {
- var wrapper = document.currentScript.parentNode;
+ var definition = document.currentScript.parentNode;
+
+ if (definition.localName !== 'sky-element') {
+ throw new Error('register() calls must be inside a <sky-element>.');
+ }
- if (wrapper.localName !== 'sky-element')
- throw new Error('No <sky-element>.');
+ var registration = new ElementRegistration(definition);
- var tagName = wrapper.getAttribute('name');
- if (!tagName)
+ if (!registration.tagName) {
throw new Error('<sky-element> must have a name.');
+ }
- var template = wrapper.querySelector('template');
- if (template)
- templates.set(tagName, template);
+ if (registrations.has(registration.tagName)) {
+ throw new Error('Duplicate registration for tag name: ' +
+ registration.tagName);
+ }
+
+ registration.synthesizeAttributes(this.prototype);
- defineReflectedAttributes(this, tagName,
- wrapper.getAttribute('attributes'));
+ // TODO(esprehn): Combine the two element registries here and in sky binder.
+ binder.registerElement(registration.tagName, {
+ attributeNames: registration.getAttributeNames(),
+ });
- return document.registerElement(tagName, {
+ registrations.set(registration.tagName, registration);
+ return document.registerElement(registration.tagName, {
prototype: this.prototype,
});
}
@@ -116,14 +167,20 @@ class SkyElement extends HTMLElement {
var attribute = attributes[i];
this.attributeChangedCallback(attribute.name, null, attribute.value);
}
+
+ var registration = registrations.get(this.localName);
+ for (var i = 0; i < registration.eventHandlers.length; ++i) {
+ var eventName = registration.eventHandlers[i];
+ this.addEventListener(eventName, eventHandlerCallback);
+ }
}
attachedCallback() {
if (!this.shadowRoot) {
- var template = templates.get(this.localName);
- if (template) {
+ var registration = registrations.get(this.localName);
+ if (registration.template) {
var shadow = this.ensureShadowRoot();
- var instance = binder.createInstance(template, this);
+ var instance = binder.createInstance(registration.template, this);
shadow.appendChild(instance.fragment);
this.shadowRootReady();
}
@@ -139,9 +196,12 @@ class SkyElement extends HTMLElement {
attributeChangedCallback(name, oldValue, newValue) {
this.attributeChanged(name, oldValue, newValue);
- var handler = this[name + 'AttributeChanged'];
- if (typeof handler == 'function')
- handler.call(this, oldValue, newValue);
+ var registration = registrations.get(this.localName);
+ var converter = registration.attributes.get(name);
+ if (converter) {
+ this.notifyPropertyChanged(name, converter(oldValue),
+ converter(newValue));
+ }
}
notifyPropertyChanged(name, oldValue, newValue) {
« 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