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

Side by Side Diff: appengine/config_service/ui/bower_components/polymer/lib/mixins/property-accessors.html

Issue 2923973003: Added base template for config ui. (Closed)
Patch Set: Created 3 years, 6 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
OLDNEW
(Empty)
1 <!--
2 @license
3 Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 -->
10
11 <link rel="import" href="../utils/boot.html">
12 <link rel="import" href="../utils/mixin.html">
13 <link rel="import" href="../utils/case-map.html">
14 <link rel="import" href="../utils/async.html">
15
16 <script>
17 (function() {
18
19 'use strict';
20
21 let caseMap = Polymer.CaseMap;
22
23 let microtask = Polymer.Async.microTask;
24
25 // Save map of native properties; this forms a blacklist or properties
26 // that won't have their values "saved" by `saveAccessorValue`, since
27 // reading from an HTMLElement accessor from the context of a prototype throws
28 const nativeProperties = {};
29 let proto = HTMLElement.prototype;
30 while (proto) {
31 let props = Object.getOwnPropertyNames(proto);
32 for (let i=0; i<props.length; i++) {
33 nativeProperties[props[i]] = true;
34 }
35 proto = Object.getPrototypeOf(proto);
36 }
37
38 /**
39 * Used to save the value of a property that will be overridden with
40 * an accessor. If the `model` is a prototype, the values will be saved
41 * in `__dataProto`, and it's up to the user (or downstream mixin) to
42 * decide how/when to set these values back into the accessors.
43 * If `model` is already an instance (it has a `__data` property), then
44 * the value will be set as a pending property, meaning the user should
45 * call `_invalidateProperties` or `_flushProperties` to take effect
46 *
47 * @param {Object} model Prototype or instance
48 * @param {string} property Name of property
49 * @private
50 */
51 function saveAccessorValue(model, property) {
52 // Don't read/store value for any native properties since they could throw
53 if (!nativeProperties[property]) {
54 let value = model[property];
55 if (value !== undefined) {
56 if (model.__data) {
57 // Adding accessor to instance; update the property
58 // It is the user's responsibility to call _flushProperties
59 model._setPendingProperty(property, value);
60 } else {
61 // Adding accessor to proto; save proto's value for instance-time use
62 if (!model.__dataProto) {
63 model.__dataProto = {};
64 } else if (!model.hasOwnProperty(JSCompiler_renameProperty('__dataProt o', model))) {
65 model.__dataProto = Object.create(model.__dataProto);
66 }
67 model.__dataProto[property] = value;
68 }
69 }
70 }
71 }
72
73 /**
74 * Element class mixin that provides basic meta-programming for creating one
75 * or more property accessors (getter/setter pair) that enqueue an async
76 * (batched) `_propertiesChanged` callback.
77 *
78 * For basic usage of this mixin, simply declare attributes to observe via
79 * the standard `static get observedAttributes()`, implement `_propertiesChang ed`
80 * on the class, and then call `MyClass.createPropertiesForAttributes()` once
81 * on the class to generate property accessors for each observed attribute
82 * prior to instancing. Last, call `this._flushProperties()` once to enable
83 * the accessors.
84 *
85 * Any `observedAttributes` will automatically be
86 * deserialized via `attributeChangedCallback` and set to the associated
87 * property using `dash-case`-to-`camelCase` convention.
88 *
89 * @polymerMixin
90 * @memberof Polymer
91 * @summary Element class mixin for reacting to property changes from
92 * generated property accessors.
93 */
94 Polymer.PropertyAccessors = Polymer.dedupingMixin(superClass => {
95
96 /**
97 * @polymerMixinClass
98 * @implements {Polymer_PropertyAccessors}
99 * @unrestricted
100 */
101 class PropertyAccessors extends superClass {
102
103 /**
104 * Generates property accessors for all attributes in the standard
105 * static `observedAttributes` array.
106 *
107 * Attribute names are mapped to property names using the `dash-case` to
108 * `camelCase` convention
109 *
110 */
111 static createPropertiesForAttributes() {
112 let a$ = this.observedAttributes;
113 for (let i=0; i < a$.length; i++) {
114 this.prototype._createPropertyAccessor(caseMap.dashToCamelCase(a$[i])) ;
115 }
116 }
117
118 constructor() {
119 super();
120 this._initializeProperties();
121 }
122
123 attributeChangedCallback(name, old, value) {
124 if (old !== value) {
125 this._attributeToProperty(name, value);
126 }
127 }
128
129 /**
130 * Initializes the local storage for property accessors.
131 *
132 * Provided as an override point for performing any setup work prior
133 * to initializing the property accessor system.
134 *
135 * @protected
136 */
137 _initializeProperties() {
138 this.__serializing = false;
139 this.__dataCounter = 0;
140 this.__dataEnabled = false;
141 this.__dataReady = false;
142 this.__dataInvalid = false;
143 // initialize data with prototype values saved when creating accessors
144 this.__data = {};
145 this.__dataPending = null;
146 this.__dataOld = null;
147 if (this.__dataProto) {
148 this._initializeProtoProperties(this.__dataProto);
149 this.__dataProto = null;
150 }
151 // Capture instance properties; these will be set into accessors
152 // during first flush. Don't set them here, since we want
153 // these to overwrite defaults/constructor assignments
154 for (let p in this.__dataHasAccessor) {
155 if (this.hasOwnProperty(p)) {
156 this.__dataInstanceProps = this.__dataInstanceProps || {};
157 this.__dataInstanceProps[p] = this[p];
158 delete this[p];
159 }
160 }
161 }
162
163 /**
164 * Called at instance time with bag of properties that were overwritten
165 * by accessors on the prototype when accessors were created.
166 *
167 * The default implementation sets these properties back into the
168 * setter at instance time. This method is provided as an override
169 * point for customizing or providing more efficient initialization.
170 *
171 * @param {Object} props Bag of property values that were overwritten
172 * when creating property accessors.
173 * @protected
174 */
175 _initializeProtoProperties(props) {
176 for (let p in props) {
177 this._setProperty(p, props[p]);
178 }
179 }
180
181 /**
182 * Called at ready time with bag of instance properties that overwrote
183 * accessors when the element upgraded.
184 *
185 * The default implementation sets these properties back into the
186 * setter at ready time. This method is provided as an override
187 * point for customizing or providing more efficient initialization.
188 *
189 * @param {Object} props Bag of property values that were overwritten
190 * when creating property accessors.
191 * @protected
192 */
193 _initializeInstanceProperties(props) {
194 Object.assign(this, props);
195 }
196
197 /**
198 * Ensures the element has the given attribute. If it does not,
199 * assigns the given value to the attribute.
200 *
201 *
202 * @param {string} attribute Name of attribute to ensure is set.
203 * @param {string} value of the attribute.
204 */
205 _ensureAttribute(attribute, value) {
206 if (!this.hasAttribute(attribute)) {
207 this._valueToNodeAttribute(this, value, attribute);
208 }
209 }
210
211 /**
212 * Deserializes an attribute to its associated property.
213 *
214 * This method calls the `_deserializeValue` method to convert the string to
215 * a typed value.
216 *
217 * @param {string} attribute Name of attribute to deserialize.
218 * @param {string} value of the attribute.
219 * @param {*} type type to deserialize to.
220 */
221 _attributeToProperty(attribute, value, type) {
222 // Don't deserialize back to property if currently reflecting
223 if (!this.__serializing) {
224 let property = caseMap.dashToCamelCase(attribute);
225 this[property] = this._deserializeValue(value, type);
226 }
227 }
228
229 /**
230 * Serializes a property to its associated attribute.
231 *
232 * @param {string} property Property name to reflect.
233 * @param {string=} attribute Attribute name to reflect.
234 * @param {*=} value Property value to refect.
235 */
236 _propertyToAttribute(property, attribute, value) {
237 this.__serializing = true;
238 value = (arguments.length < 3) ? this[property] : value;
239 this._valueToNodeAttribute(this, value,
240 attribute || caseMap.camelToDashCase(property));
241 this.__serializing = false;
242 }
243
244 /**
245 * Sets a typed value to an HTML attribute on a node.
246 *
247 * This method calls the `_serializeValue` method to convert the typed
248 * value to a string. If the `_serializeValue` method returns `undefined` ,
249 * the attribute will be removed (this is the default for boolean
250 * type `false`).
251 *
252 * @param {Element} node Element to set attribute to.
253 * @param {*} value Value to serialize.
254 * @param {string} attribute Attribute name to serialize to.
255 */
256 _valueToNodeAttribute(node, value, attribute) {
257 let str = this._serializeValue(value);
258 if (str === undefined) {
259 node.removeAttribute(attribute);
260 } else {
261 node.setAttribute(attribute, str);
262 }
263 }
264
265 /**
266 * Converts a typed JavaScript value to a string.
267 *
268 * This method is called by Polymer when setting JS property values to
269 * HTML attributes. Users may override this method on Polymer element
270 * prototypes to provide serialization for custom types.
271 *
272 * @param {*} value Property value to serialize.
273 * @return {string | undefined} String serialized from the provided proper ty value.
274 */
275 _serializeValue(value) {
276 /* eslint-disable no-fallthrough */
277 switch (typeof value) {
278 case 'boolean':
279 return value ? '' : undefined;
280
281 case 'object':
282 if (value instanceof Date) {
283 return value.toString();
284 } else if (value) {
285 try {
286 return JSON.stringify(value);
287 } catch(x) {
288 return '';
289 }
290 }
291
292 default:
293 return value != null ? value.toString() : undefined;
294 }
295 }
296
297 /**
298 * Converts a string to a typed JavaScript value.
299 *
300 * This method is called by Polymer when reading HTML attribute values to
301 * JS properties. Users may override this method on Polymer element
302 * prototypes to provide deserialization for custom `type`s. Note,
303 * the `type` argument is the value of the `type` field provided in the
304 * `properties` configuration object for a given property, and is
305 * by convention the constructor for the type to deserialize.
306 *
307 * Note: The return value of `undefined` is used as a sentinel value to
308 * indicate the attribute should be removed.
309 *
310 * @param {string} value Attribute value to deserialize.
311 * @param {*} type Type to deserialize the string to.
312 * @return {*} Typed value deserialized from the provided string.
313 */
314 _deserializeValue(value, type) {
315 /**
316 * @type {*}
317 */
318 let outValue;
319 switch (type) {
320 case Number:
321 outValue = Number(value);
322 break;
323
324 case Boolean:
325 outValue = (value !== null);
326 break;
327
328 case Object:
329 try {
330 outValue = JSON.parse(value);
331 } catch(x) {
332 // allow non-JSON literals like Strings and Numbers
333 }
334 break;
335
336 case Array:
337 try {
338 outValue = JSON.parse(value);
339 } catch(x) {
340 outValue = null;
341 console.warn(`Polymer::Attributes: couldn't decode Array as JSON: ${value}`);
342 }
343 break;
344
345 case Date:
346 outValue = new Date(value);
347 break;
348
349 case String:
350 default:
351 outValue = value;
352 break;
353 }
354
355 return outValue;
356 }
357 /* eslint-enable no-fallthrough */
358
359 /**
360 * Creates a setter/getter pair for the named property with its own
361 * local storage. The getter returns the value in the local storage,
362 * and the setter calls `_setProperty`, which updates the local storage
363 * for the property and enqueues a `_propertiesChanged` callback.
364 *
365 * This method may be called on a prototype or an instance. Calling
366 * this method may overwrite a property value that already exists on
367 * the prototype/instance by creating the accessor. When calling on
368 * a prototype, any overwritten values are saved in `__dataProto`,
369 * and it is up to the subclasser to decide how/when to set those
370 * properties back into the accessor. When calling on an instance,
371 * the overwritten value is set via `_setPendingProperty`, and the
372 * user should call `_invalidateProperties` or `_flushProperties`
373 * for the values to take effect.
374 *
375 * @param {string} property Name of the property
376 * @param {boolean=} readOnly When true, no setter is created; the
377 * protected `_setProperty` function must be used to set the property
378 * @protected
379 */
380 _createPropertyAccessor(property, readOnly) {
381 if (!this.hasOwnProperty('__dataHasAccessor')) {
382 this.__dataHasAccessor = Object.assign({}, this.__dataHasAccessor);
383 }
384 if (!this.__dataHasAccessor[property]) {
385 this.__dataHasAccessor[property] = true;
386 saveAccessorValue(this, property);
387 Object.defineProperty(this, property, {
388 get: function() {
389 return this.__data[property];
390 },
391 set: readOnly ? function() { } : function(value) {
392 this._setProperty(property, value);
393 }
394 });
395 }
396 }
397
398 /**
399 * Returns true if this library created an accessor for the given property .
400 *
401 * @param {string} property Property name
402 * @return {boolean} True if an accessor was created
403 */
404 _hasAccessor(property) {
405 return this.__dataHasAccessor && this.__dataHasAccessor[property];
406 }
407
408 /**
409 * Updates the local storage for a property (via `_setPendingProperty`)
410 * and enqueues a `_proeprtiesChanged` callback.
411 *
412 * @param {string} property Name of the property
413 * @param {*} value Value to set
414 * @protected
415 */
416 _setProperty(property, value) {
417 if (this._setPendingProperty(property, value)) {
418 this._invalidateProperties();
419 }
420 }
421
422 /**
423 * Updates the local storage for a property, records the previous value,
424 * and adds it to the set of "pending changes" that will be passed to the
425 * `_propertiesChanged` callback. This method does not enqueue the
426 * `_propertiesChanged` callback.
427 *
428 * @param {string} property Name of the property
429 * @param {*} value Value to set
430 * @return {boolean} Returns true if the property changed
431 * @protected
432 */
433 _setPendingProperty(property, value) {
434 let old = this.__data[property];
435 if (this._shouldPropertyChange(property, value, old)) {
436 if (!this.__dataPending) {
437 this.__dataPending = {};
438 this.__dataOld = {};
439 }
440 // Ensure old is captured from the last turn
441 if (!(property in this.__dataOld)) {
442 this.__dataOld[property] = old;
443 }
444 this.__data[property] = value;
445 this.__dataPending[property] = value;
446 return true;
447 }
448 }
449
450 /**
451 * Returns true if the specified property has a pending change.
452 *
453 * @param {string} prop Property name
454 * @return {boolean} True if property has a pending change
455 * @protected
456 */
457 _isPropertyPending(prop) {
458 return this.__dataPending && (prop in this.__dataPending);
459 }
460
461 /**
462 * Marks the properties as invalid, and enqueues an async
463 * `_propertiesChanged` callback.
464 *
465 * @protected
466 */
467 _invalidateProperties() {
468 if (!this.__dataInvalid && this.__dataReady) {
469 this.__dataInvalid = true;
470 microtask.run(() => {
471 if (this.__dataInvalid) {
472 this.__dataInvalid = false;
473 this._flushProperties();
474 }
475 });
476 }
477 }
478
479 /**
480 * Call to enable property accessor processing. Before this method is
481 * called accessor values will be set but side effects are
482 * queued. When called, any pending side effects occur immediately.
483 * For elements, generally `connectedCallback` is a normal spot to do so.
484 * It is safe to call this method multiple times as it only turns on
485 * property accessors once.
486 */
487 _enableProperties() {
488 if (!this.__dataEnabled) {
489 this.__dataEnabled = true;
490 if (this.__dataInstanceProps) {
491 this._initializeInstanceProperties(this.__dataInstanceProps);
492 this.__dataInstanceProps = null;
493 }
494 this.ready()
495 }
496 }
497
498 /**
499 * Calls the `_propertiesChanged` callback with the current set of
500 * pending changes (and old values recorded when pending changes were
501 * set), and resets the pending set of changes. Generally, this method
502 * should not be called in user code.
503 *
504 *
505 * @protected
506 */
507 _flushProperties() {
508 if (this.__dataPending) {
509 let changedProps = this.__dataPending;
510 this.__dataPending = null;
511 this.__dataCounter++;
512 this._propertiesChanged(this.__data, changedProps, this.__dataOld);
513 this.__dataCounter--;
514 }
515 }
516
517 /**
518 * Lifecycle callback called the first time properties are being flushed.
519 * Prior to `ready`, all property sets through accessors are queued and
520 * their effects are flushed after this method returns.
521 *
522 * Users may override this function to implement behavior that is
523 * dependent on the element having its properties initialized, e.g.
524 * from defaults (initialized from `constructor`, `_initializeProperties`) ,
525 * `attributeChangedCallback`, or values propagated from host e.g. via
526 * bindings. `super.ready()` must be called to ensure the data system
527 * becomes enabled.
528 *
529 * @public
530 */
531 ready() {
532 this.__dataReady = true;
533 // Run normal flush
534 this._flushProperties();
535 }
536
537 /**
538 * Callback called when any properties with accessors created via
539 * `_createPropertyAccessor` have been set.
540 *
541 * @param {Object} currentProps Bag of all current accessor values
542 * @param {Object} changedProps Bag of properties changed since the last
543 * call to `_propertiesChanged`
544 * @param {Object} oldProps Bag of previous values for each property
545 * in `changedProps`
546 * @protected
547 */
548 _propertiesChanged(currentProps, changedProps, oldProps) { // eslint-disab le-line no-unused-vars
549 }
550
551 /**
552 * Method called to determine whether a property value should be
553 * considered as a change and cause the `_propertiesChanged` callback
554 * to be enqueued.
555 *
556 * The default implementation returns `true` for primitive types if a
557 * strict equality check fails, and returns `true` for all Object/Arrays.
558 * The method always returns false for `NaN`.
559 *
560 * Override this method to e.g. provide stricter checking for
561 * Objects/Arrays when using immutable patterns.
562 *
563 * @param {string} property Property name
564 * @param {*} value New property value
565 * @param {*} old Previous property value
566 * @return {boolean} Whether the property should be considered a change
567 * and enqueue a `_proeprtiesChanged` callback
568 * @protected
569 */
570 _shouldPropertyChange(property, value, old) {
571 return (
572 // Strict equality check
573 (old !== value &&
574 // This ensures (old==NaN, value==NaN) always returns false
575 (old === old || value === value))
576 );
577 }
578
579 }
580
581 return PropertyAccessors;
582
583 });
584
585 })();
586 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698