Chromium Code Reviews| OLD | NEW |
|---|---|
| 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(); | 8 var templates = new Map(); |
| 9 | 9 |
| 10 var attributeDescriptors = { | |
| 11 boolean: { | |
| 12 type: 'boolean', | |
| 13 convert: function(value) { | |
| 14 if (typeof value == 'string') | |
| 15 return value == 'true'; | |
| 16 return !!value; | |
| 17 }, | |
| 18 }, | |
| 19 number: { | |
| 20 type: 'number', | |
| 21 convert: function(value) { | |
| 22 return Number(value); | |
| 23 }, | |
| 24 }, | |
| 25 string: { | |
| 26 type: 'string', | |
| 27 convert: function(value) { | |
| 28 if (value == null) | |
|
abarth-chromium
2015/01/06 04:02:38
Why handle |null| special but not |undefined|?
esprehn
2015/01/06 04:10:24
undefined is itself a string, if I special case th
| |
| 29 return ""; | |
| 30 return String(value); | |
| 31 }, | |
| 32 }, | |
| 33 }; | |
| 34 | |
| 35 function defineReflectedAttribute(prototype, descriptor, name) { | |
|
abarth-chromium
2015/01/06 04:02:38
Why not just pass in the convert function instead
esprehn
2015/01/06 04:10:24
Done, originally I had a more complex idea for how
| |
| 36 Object.defineProperty(prototype, name, { | |
| 37 get: function() { | |
| 38 return descriptor.convert(this.getAttribute(name)); | |
| 39 }, | |
| 40 set: function(newValue) { | |
| 41 this.setAttribute(name, descriptor.convert(newValue)); | |
| 42 }, | |
| 43 enumerable: true, | |
| 44 configurable: true, | |
| 45 }); | |
| 46 | |
| 47 prototype[name + 'AttributeChanged'] = function(oldValue, newValue) { | |
| 48 this.notifyPropertyChanged(name, descriptor.convert(oldValue), | |
| 49 descriptor.convert(newValue)); | |
| 50 }; | |
| 51 } | |
| 52 | |
| 53 function defineReflectedAttributes(elementClass, list) { | |
| 54 var attributeNames = (list || '').split(','); | |
| 55 var prototype = elementClass.prototype; | |
| 56 | |
| 57 for (var i = 0; i < attributeNames.length; ++i) { | |
| 58 var parts = attributeNames[i].split(':'); | |
| 59 var name = parts[0].trim(); | |
| 60 var type = (parts[1] || "").trim(); | |
|
abarth-chromium
2015/01/06 04:02:38
s/""/''/ for consistency
esprehn
2015/01/06 04:10:24
done.
| |
| 61 var descriptor = attributeDescriptors[type] || attributeDescriptors.string; | |
| 62 | |
| 63 defineReflectedAttribute(prototype, descriptor, name); | |
| 64 } | |
| 65 } | |
| 66 | |
| 10 class SkyElement extends HTMLElement { | 67 class SkyElement extends HTMLElement { |
| 11 | 68 |
| 12 static register() { | 69 static register() { |
| 13 var wrapper = document.currentScript.parentNode; | 70 var wrapper = document.currentScript.parentNode; |
| 14 | 71 |
| 15 if (wrapper.localName !== 'sky-element') | 72 if (wrapper.localName !== 'sky-element') |
| 16 throw new Error('No <sky-element>.'); | 73 throw new Error('No <sky-element>.'); |
| 17 | 74 |
| 18 var tagName = wrapper.getAttribute("name"); | 75 var tagName = wrapper.getAttribute("name"); |
| 19 if (!tagName) | 76 if (!tagName) |
| 20 throw new Error('<sky-element> must have a name.'); | 77 throw new Error('<sky-element> must have a name.'); |
| 21 | 78 |
| 22 var template = wrapper.querySelector('template'); | 79 var template = wrapper.querySelector('template'); |
| 23 if (template) | 80 if (template) |
| 24 templates.set(tagName, template); | 81 templates.set(tagName, template); |
| 25 | 82 |
| 83 defineReflectedAttributes(this, wrapper.getAttribute("attributes")); | |
| 84 | |
| 26 return document.registerElement(tagName, { | 85 return document.registerElement(tagName, { |
| 27 prototype: this.prototype, | 86 prototype: this.prototype, |
| 28 }); | 87 }); |
| 29 } | 88 } |
| 30 | 89 |
| 31 created() { | 90 created() { |
| 32 // override | 91 // override |
| 33 } | 92 } |
| 34 | 93 |
| 35 attached() { | 94 attached() { |
| 36 // override | 95 // override |
| 37 } | 96 } |
| 38 | 97 |
| 39 detached() { | 98 detached() { |
| 40 // override | 99 // override |
| 41 } | 100 } |
| 42 | 101 |
| 43 attributeChanged(attrName, oldValue, newValue) { | 102 attributeChanged(attrName, oldValue, newValue) { |
| 44 // override | 103 // override |
| 45 } | 104 } |
| 46 | 105 |
| 47 shadowRootReady() { | 106 shadowRootReady() { |
| 48 // override | 107 // override |
| 49 } | 108 } |
| 50 | 109 |
| 51 createdCallback() { | 110 createdCallback() { |
| 52 this.isAttached = false; | 111 this.isAttached = false; |
| 53 this.created(); | 112 this.created(); |
| 113 | |
| 114 // Invoke attributeChanged callback when element is first created too. | |
| 115 var attributes = this.getAttributes(); | |
| 116 for (var i = 0; i < attributes.length; ++i) { | |
| 117 var attribute = attributes[i]; | |
| 118 this.attributeChangedCallback(attribute.name, null, attribute.value); | |
| 119 } | |
| 54 } | 120 } |
| 55 | 121 |
| 56 attachedCallback() { | 122 attachedCallback() { |
| 57 if (!this.shadowRoot) { | 123 if (!this.shadowRoot) { |
| 58 var template = templates.get(this.localName); | 124 var template = templates.get(this.localName); |
| 59 if (template) { | 125 if (template) { |
| 60 var shadow = this.ensureShadowRoot(); | 126 var shadow = this.ensureShadowRoot(); |
| 61 var instance = binder.createInstance(template, this); | 127 var instance = binder.createInstance(template, this); |
| 62 shadow.appendChild(instance.fragment); | 128 shadow.appendChild(instance.fragment); |
| 63 this.shadowRootReady(); | 129 this.shadowRootReady(); |
| 64 } | 130 } |
| 65 } | 131 } |
| 66 this.attached(); | 132 this.attached(); |
| 67 this.isAttached = true; | 133 this.isAttached = true; |
| 68 } | 134 } |
| 69 | 135 |
| 70 detachedCallback() { | 136 detachedCallback() { |
| 71 this.detached(); | 137 this.detached(); |
| 72 this.isAttached = false; | 138 this.isAttached = false; |
| 73 } | 139 } |
| 74 | 140 |
| 75 attributeChangedCallback(attrName, oldValue, newValue) { | 141 attributeChangedCallback(name, oldValue, newValue) { |
| 76 // reserved for canonical behavior | 142 this.attributeChanged(name, oldValue, newValue); |
| 77 this.attributeChanged(attrName, oldValue, newValue); | 143 var handler = this[name + 'AttributeChanged']; |
| 144 if (typeof handler == 'function') | |
| 145 handler.call(this, oldValue, newValue); | |
| 146 } | |
| 147 | |
| 148 notifyPropertyChanged(name, oldValue, newValue) { | |
| 149 var notifier = Object.getNotifier(this); | |
| 150 notifier.notify({ | |
| 151 type: 'update', | |
| 152 name: name, | |
| 153 oldValue: oldValue, | |
| 154 }); | |
| 155 var handler = this[name + 'Changed']; | |
| 156 if (typeof handler == 'function') | |
| 157 handler.call(this, oldValue, newValue); | |
| 78 } | 158 } |
| 79 }; | 159 }; |
| 80 | 160 |
| 81 module.exports = SkyElement; | 161 module.exports = SkyElement; |
| 82 </script> | 162 </script> |
| OLD | NEW |