3 Copyright (c) 2014 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
7 <link rel="import" href="/tvcm/events.html">
11 tvcm.exportTo('tvcm', function() {
13 * Fires a property change event on the target.
14 * @param {EventTarget} target The target to dispatch the event on.
15 * @param {string} propertyName The name of the property that changed.
16 * @param {*} newValue The new value for the property.
17 * @param {*} oldValue The old value for the property.
19 function dispatchPropertyChange(target, propertyName, newValue, oldValue,
20 opt_bubbles, opt_cancelable) {
21 var e = new tvcm.Event(propertyName + 'Change',
22 opt_bubbles, opt_cancelable);
23 e.propertyName = propertyName;
24 e.newValue = newValue;
25 e.oldValue = oldValue;
28 e.throwError = function(err) { // workaround CR 239648
32 target.dispatchEvent(e);
37 function setPropertyAndDispatchChange(obj, propertyName, newValue) {
38 var privateName = propertyName + '_';
39 var oldValue = obj[propertyName];
40 obj[privateName] = newValue;
41 if (oldValue !== newValue)
42 tvcm.dispatchPropertyChange(obj, propertyName,
43 newValue, oldValue, true, false);
47 * Converts a camelCase javascript property name to a hyphenated-lower-case
49 * @param {string} jsName The javascript camelCase property name.
50 * @return {string} The equivalent hyphenated-lower-case attribute name.
52 function getAttributeName(jsName) {
53 return jsName.replace(/([A-Z])/g, '-$1').toLowerCase();
56 /* Creates a private name unlikely to collide with object properties names
57 * @param {string} name The defineProperty name
58 * @return {string} an obfuscated name
60 function getPrivateName(name) {
61 return name + '_tvcm_';
65 * The kind of property to define in {@code defineProperty}.
71 * Plain old JS property where the backing data is stored as a 'private'
72 * field on the object.
77 * The property backing data is stored as an attribute on an element.
82 * The property backing data is stored as an attribute on an element. If the
83 * element has the attribute then the value is true.
89 * Helper function for defineProperty that returns the getter to use for the
91 * @param {string} name The name of the property.
92 * @param {tvcm.PropertyKind} kind The kind of the property.
93 * @return {function():*} The getter for the property.
95 function getGetter(name, kind) {
98 var privateName = getPrivateName(name);
100 return this[privateName];
102 case PropertyKind.ATTR:
103 var attributeName = getAttributeName(name);
105 return this.getAttribute(attributeName);
107 case PropertyKind.BOOL_ATTR:
108 var attributeName = getAttributeName(name);
110 return this.hasAttribute(attributeName);
116 * Helper function for defineProperty that returns the setter of the right
118 * @param {string} name The name of the property we are defining the setter
120 * @param {tvcm.PropertyKind} kind The kind of property we are getting the
122 * @param {function(*):void=} opt_setHook A function to run after the property
123 * is set, but before the propertyChange event is fired.
124 * @param {boolean=} opt_bubbles Whether the event bubbles or not.
125 * @param {boolean=} opt_cancelable Whether the default action of the event
127 * @return {function(*):void} The function to use as a setter.
129 function getSetter(name, kind, opt_setHook, opt_bubbles, opt_cancelable) {
131 case PropertyKind.JS:
132 var privateName = getPrivateName(name);
133 return function(value) {
134 var oldValue = this[privateName];
135 if (value !== oldValue) {
136 this[privateName] = value;
138 opt_setHook.call(this, value, oldValue);
139 dispatchPropertyChange(this, name, value, oldValue,
140 opt_bubbles, opt_cancelable);
144 case PropertyKind.ATTR:
145 var attributeName = getAttributeName(name);
146 return function(value) {
147 var oldValue = this.getAttribute(attributeName);
148 if (value !== oldValue) {
149 if (value == undefined)
150 this.removeAttribute(attributeName);
152 this.setAttribute(attributeName, value);
154 opt_setHook.call(this, value, oldValue);
155 dispatchPropertyChange(this, name, value, oldValue,
156 opt_bubbles, opt_cancelable);
160 case PropertyKind.BOOL_ATTR:
161 var attributeName = getAttributeName(name);
162 return function(value) {
163 var oldValue = (this.getAttribute(attributeName) === name);
164 if (value !== oldValue) {
166 this.setAttribute(attributeName, name);
168 this.removeAttribute(attributeName);
170 opt_setHook.call(this, value, oldValue);
171 dispatchPropertyChange(this, name, value, oldValue,
172 opt_bubbles, opt_cancelable);
179 * Defines a property on an object. When the setter changes the value a
180 * property change event with the type {@code name + 'Change'} is fired.
181 * @param {!Object} obj The object to define the property for.
182 * @param {string} name The name of the property.
183 * @param {tvcm.PropertyKind=} opt_kind What kind of underlying storage to
185 * @param {function(*):void=} opt_setHook A function to run after the
186 * property is set, but before the propertyChange event is fired.
187 * @param {boolean=} opt_bubbles Whether the event bubbles or not.
188 * @param {boolean=} opt_cancelable Whether the default action of the event
191 function defineProperty(obj, name, opt_kind, opt_setHook,
192 opt_bubbles, opt_cancelable) {
193 console.error("Don't use tvcm.defineProperty");
194 if (typeof obj == 'function')
197 var kind = opt_kind || PropertyKind.JS;
199 if (!obj.__lookupGetter__(name))
200 obj.__defineGetter__(name, getGetter(name, kind));
202 if (!obj.__lookupSetter__(name))
203 obj.__defineSetter__(name, getSetter(name, kind, opt_setHook,
204 opt_bubbles, opt_cancelable));
208 PropertyKind: PropertyKind,
209 defineProperty: defineProperty,
210 dispatchPropertyChange: dispatchPropertyChange,
211 setPropertyAndDispatchChange: setPropertyAndDispatchChange