Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / src / base / properties.js
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 base.require('base.events');
8
9 base.exportTo('base', function() {
10   /**
11    * Fires a property change event on the target.
12    * @param {EventTarget} target The target to dispatch the event on.
13    * @param {string} propertyName The name of the property that changed.
14    * @param {*} newValue The new value for the property.
15    * @param {*} oldValue The old value for the property.
16    */
17   function dispatchPropertyChange(target, propertyName, newValue, oldValue,
18                                   opt_bubbles, opt_cancelable) {
19     var e = new base.Event(propertyName + 'Change',
20                            opt_bubbles, opt_cancelable);
21     e.propertyName = propertyName;
22     e.newValue = newValue;
23     e.oldValue = oldValue;
24
25     var error;
26     e.throwError = function(err) {  // workaround CR 239648
27       error = err;
28     };
29
30     target.dispatchEvent(e);
31     if (error)
32       throw error;
33   }
34
35   function setPropertyAndDispatchChange(obj, propertyName, newValue) {
36     var privateName = propertyName + '_';
37     var oldValue = obj[propertyName];
38     obj[privateName] = newValue;
39     if (oldValue !== newValue)
40       base.dispatchPropertyChange(obj, propertyName,
41           newValue, oldValue, true, false);
42   }
43
44   /**
45    * Converts a camelCase javascript property name to a hyphenated-lower-case
46    * attribute name.
47    * @param {string} jsName The javascript camelCase property name.
48    * @return {string} The equivalent hyphenated-lower-case attribute name.
49    */
50   function getAttributeName(jsName) {
51     return jsName.replace(/([A-Z])/g, '-$1').toLowerCase();
52   }
53
54   /* Creates a private name unlikely to collide with object properties names
55    * @param {string} name The defineProperty name
56    * @return {string} an obfuscated name
57    */
58   function getPrivateName(name) {
59     return name + '_base_';
60   }
61
62   /**
63    * The kind of property to define in {@code defineProperty}.
64    * @enum {number}
65    * @const
66    */
67   var PropertyKind = {
68     /**
69      * Plain old JS property where the backing data is stored as a 'private'
70      * field on the object.
71      */
72     JS: 'js',
73
74     /**
75      * The property backing data is stored as an attribute on an element.
76      */
77     ATTR: 'attr',
78
79     /**
80      * The property backing data is stored as an attribute on an element. If the
81      * element has the attribute then the value is true.
82      */
83     BOOL_ATTR: 'boolAttr'
84   };
85
86   /**
87    * Helper function for defineProperty that returns the getter to use for the
88    * property.
89    * @param {string} name The name of the property.
90    * @param {base.PropertyKind} kind The kind of the property.
91    * @return {function():*} The getter for the property.
92    */
93   function getGetter(name, kind) {
94     switch (kind) {
95       case PropertyKind.JS:
96         var privateName = getPrivateName(name);
97         return function() {
98           return this[privateName];
99         };
100       case PropertyKind.ATTR:
101         var attributeName = getAttributeName(name);
102         return function() {
103           return this.getAttribute(attributeName);
104         };
105       case PropertyKind.BOOL_ATTR:
106         var attributeName = getAttributeName(name);
107         return function() {
108           return this.hasAttribute(attributeName);
109         };
110     }
111   }
112
113   /**
114    * Helper function for defineProperty that returns the setter of the right
115    * kind.
116    * @param {string} name The name of the property we are defining the setter
117    *     for.
118    * @param {base.PropertyKind} kind The kind of property we are getting the
119    *     setter for.
120    * @param {function(*):void=} opt_setHook A function to run after the property
121    *     is set, but before the propertyChange event is fired.
122    * @param {boolean=} opt_bubbles Whether the event bubbles or not.
123    * @param {boolean=} opt_cancelable Whether the default action of the event
124    *     can be prevented.
125    * @return {function(*):void} The function to use as a setter.
126    */
127   function getSetter(name, kind, opt_setHook, opt_bubbles, opt_cancelable) {
128     switch (kind) {
129       case PropertyKind.JS:
130         var privateName = getPrivateName(name);
131         return function(value) {
132           var oldValue = this[privateName];
133           if (value !== oldValue) {
134             this[privateName] = value;
135             if (opt_setHook)
136               opt_setHook.call(this, value, oldValue);
137             dispatchPropertyChange(this, name, value, oldValue,
138                 opt_bubbles, opt_cancelable);
139           }
140         };
141
142       case PropertyKind.ATTR:
143         var attributeName = getAttributeName(name);
144         return function(value) {
145           var oldValue = this.getAttribute(attributeName);
146           if (value !== oldValue) {
147             if (value == undefined)
148               this.removeAttribute(attributeName);
149             else
150               this.setAttribute(attributeName, value);
151             if (opt_setHook)
152               opt_setHook.call(this, value, oldValue);
153             dispatchPropertyChange(this, name, value, oldValue,
154                 opt_bubbles, opt_cancelable);
155           }
156         };
157
158       case PropertyKind.BOOL_ATTR:
159         var attributeName = getAttributeName(name);
160         return function(value) {
161           var oldValue = (this.getAttribute(attributeName) === name);
162           if (value !== oldValue) {
163             if (value)
164               this.setAttribute(attributeName, name);
165             else
166               this.removeAttribute(attributeName);
167             if (opt_setHook)
168               opt_setHook.call(this, value, oldValue);
169             dispatchPropertyChange(this, name, value, oldValue,
170                 opt_bubbles, opt_cancelable);
171           }
172         };
173     }
174   }
175
176   /**
177    * Defines a property on an object. When the setter changes the value a
178    * property change event with the type {@code name + 'Change'} is fired.
179    * @param {!Object} obj The object to define the property for.
180    * @param {string} name The name of the property.
181    * @param {base.PropertyKind=} opt_kind What kind of underlying storage to
182    * use.
183    * @param {function(*):void=} opt_setHook A function to run after the
184    *     property is set, but before the propertyChange event is fired.
185    * @param {boolean=} opt_bubbles Whether the event bubbles or not.
186    * @param {boolean=} opt_cancelable Whether the default action of the event
187    *     can be prevented.
188    */
189   function defineProperty(obj, name, opt_kind, opt_setHook,
190                           opt_bubbles, opt_cancelable) {
191     console.error("Don't use base.defineProperty");
192     if (typeof obj == 'function')
193       obj = obj.prototype;
194
195     var kind = opt_kind || PropertyKind.JS;
196
197     if (!obj.__lookupGetter__(name))
198       obj.__defineGetter__(name, getGetter(name, kind));
199
200     if (!obj.__lookupSetter__(name))
201       obj.__defineSetter__(name, getSetter(name, kind, opt_setHook,
202           opt_bubbles, opt_cancelable));
203   }
204
205   return {
206     PropertyKind: PropertyKind,
207     defineProperty: defineProperty,
208     dispatchPropertyChange: dispatchPropertyChange,
209     setPropertyAndDispatchChange: setPropertyAndDispatchChange
210   };
211 });