| OLD | NEW |
| (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/style-gather.html"> |
| 15 <link rel="import" href="../utils/resolve-url.html"> |
| 16 <link rel="import" href="../elements/dom-module.html"> |
| 17 <link rel="import" href="property-effects.html"> |
| 18 |
| 19 <script> |
| 20 (function() { |
| 21 'use strict'; |
| 22 /** |
| 23 * @typedef Object<string, { |
| 24 * value: *, |
| 25 * type: (Function | undefined), |
| 26 * readOnly: (boolean | undefined), |
| 27 * computed: (string | undefined), |
| 28 * reflectToAttribute: (boolean | undefined), |
| 29 * notify: (boolean | undefined), |
| 30 * observer: (string | undefined) |
| 31 * }>) |
| 32 */ |
| 33 let PolymerElementProperties; // eslint-disable-line no-unused-vars |
| 34 |
| 35 /** @record */ |
| 36 let PolymerElementConstructor = function(){}; // eslint-disable-line no-unused
-vars |
| 37 /** @type {(string | undefined)} */ |
| 38 PolymerElementConstructor.is; |
| 39 /** @type {(string | undefined)} */ |
| 40 PolymerElementConstructor.extends; |
| 41 /** @type {(!PolymerElementProperties | undefined)} */ |
| 42 PolymerElementConstructor.properties; |
| 43 /** @type {(!Array<string> | undefined)} */ |
| 44 PolymerElementConstructor.observers; |
| 45 /** @type {(!HTMLTemplateElement | string | undefined)} */ |
| 46 PolymerElementConstructor.template; |
| 47 |
| 48 /** |
| 49 * Element class mixin that provides the core API for Polymer's meta-programmi
ng |
| 50 * features including template stamping, data-binding, attribute deserializati
on, |
| 51 * and property change observation. |
| 52 * |
| 53 * Subclassers may provide the following static getters to return metadata |
| 54 * used to configure Polymer's features for the class: |
| 55 * |
| 56 * - `static get is()`: When the template is provided via a `dom-module`, |
| 57 * users should return the `dom-module` id from a static `is` getter. If |
| 58 * no template is needed or the template is provided directly via the |
| 59 * `template` getter, there is no need to define `is` for the element. |
| 60 * |
| 61 * - `static get template()`: Users may provide the template directly (as |
| 62 * opposed to via `dom-module`) by implementing a static `template` getter. |
| 63 * The getter may return an `HTMLTemplateElement` or a string, which will |
| 64 * automatically be parsed into a template. |
| 65 * |
| 66 * - `static get properties()`: Should return an object describing |
| 67 * property-related metadata used by Polymer features (key: property name |
| 68 * value: object containing property metadata). Valid keys in per-property |
| 69 * metadata include: |
| 70 * - `type` (String|Number|Object|Array|...): Used by |
| 71 * `attributeChangedCallback` to determine how string-based attributes |
| 72 * are deserialized to JavaScript property values. |
| 73 * - `notify` (boolean): Causes a change in the property to fire a |
| 74 * non-bubbling event called `<property>-changed`. Elements that have |
| 75 * enabled two-way binding to the property use this event to observe chang
es. |
| 76 * - `readOnly` (boolean): Creates a getter for the property, but no setter. |
| 77 * To set a read-only property, use the private setter method |
| 78 * `_setProperty(property, value)`. |
| 79 * - `observer` (string): Observer method name that will be called when |
| 80 * the property changes. The arguments of the method are |
| 81 * `(value, previousValue)`. |
| 82 * - `computed` (string): String describing method and dependent properties |
| 83 * for computing the value of this property (e.g. `'computeFoo(bar, zot)'`
). |
| 84 * Computed properties are read-only by default and can only be changed |
| 85 * via the return value of the computing method. |
| 86 * |
| 87 * - `static get observers()`: Array of strings describing multi-property |
| 88 * observer methods and their dependent properties (e.g. |
| 89 * `'observeABC(a, b, c)'`). |
| 90 * |
| 91 * The base class provides default implementations for the following standard |
| 92 * custom element lifecycle callbacks; users may override these, but should |
| 93 * call the super method to ensure |
| 94 * - `constructor`: Run when the element is created or upgraded |
| 95 * - `connectedCallback`: Run each time the element is connected to the |
| 96 * document |
| 97 * - `disconnectedCallback`: Run each time the element is disconnected from |
| 98 * the document |
| 99 * - `attributeChangedCallback`: Run each time an attribute in |
| 100 * `observedAttributes` is set or removed (note: this element's default |
| 101 * `observedAttributes` implementation will automatically return an array |
| 102 * of dash-cased attributes based on `properties`) |
| 103 * |
| 104 * @polymerMixin |
| 105 * @mixes Polymer.PropertyEffects |
| 106 * @memberof Polymer |
| 107 * @property rootPath {string} Set to the value of `Polymer.rootPath`, |
| 108 * which defaults to the main document path |
| 109 * @property importPath {string} Set to the value of the class's static |
| 110 * `importPath` property, which defaults to the path of this element's |
| 111 * `dom-module` (when `is` is used), but can be overridden for other |
| 112 * import strategies. |
| 113 * @summary Element class mixin that provides the core API for Polymer's |
| 114 * meta-programming features. |
| 115 */ |
| 116 Polymer.ElementMixin = Polymer.dedupingMixin(base => { |
| 117 |
| 118 /** |
| 119 * @constructor |
| 120 * @extends {base} |
| 121 * @implements {Polymer_PropertyEffects} |
| 122 */ |
| 123 const polymerElementBase = Polymer.PropertyEffects(base); |
| 124 |
| 125 let caseMap = Polymer.CaseMap; |
| 126 |
| 127 /** |
| 128 * Returns the `properties` object specifically on `klass`. Use for: |
| 129 * (1) super chain mixes togther to make `propertiesForClass` which is |
| 130 * then used to make `observedAttributes`. |
| 131 * (2) properties effects and observers are created from it at `finalize` ti
me. |
| 132 * |
| 133 * @param {HTMLElement} klass Element class |
| 134 * @return {Object} Object containing own properties for this class |
| 135 * @private |
| 136 */ |
| 137 function ownPropertiesForClass(klass) { |
| 138 if (!klass.hasOwnProperty( |
| 139 JSCompiler_renameProperty('__ownProperties', klass))) { |
| 140 klass.__ownProperties = |
| 141 klass.hasOwnProperty(JSCompiler_renameProperty('properties', klass)) ? |
| 142 klass.properties : {}; |
| 143 } |
| 144 return klass.__ownProperties; |
| 145 } |
| 146 |
| 147 /** |
| 148 * Returns the `observers` array specifically on `klass`. Use for |
| 149 * setting up observers. |
| 150 * |
| 151 * @param {HTMLElement} klass Element class |
| 152 * @return {Array} Array containing own observers for this class |
| 153 * @private |
| 154 */ |
| 155 function ownObserversForClass(klass) { |
| 156 if (!klass.hasOwnProperty( |
| 157 JSCompiler_renameProperty('__ownObservers', klass))) { |
| 158 klass.__ownObservers = |
| 159 klass.hasOwnProperty(JSCompiler_renameProperty('observers', klass)) ? |
| 160 klass.observers : []; |
| 161 } |
| 162 return klass.__ownObservers; |
| 163 } |
| 164 |
| 165 /** |
| 166 * Mixes `props` into `flattenedProps` but upgrades shorthand type |
| 167 * syntax to { type: Type}. |
| 168 * |
| 169 * @param {Object} flattenedProps Bag to collect flattened properties into |
| 170 * @param {Object} props Bag of properties to add to `flattenedProps` |
| 171 * @return {Objecg} The input `flattenedProps` bag |
| 172 * @private |
| 173 */ |
| 174 function flattenProperties(flattenedProps, props) { |
| 175 for (let p in props) { |
| 176 let o = props[p]; |
| 177 if (typeof o == 'function') { |
| 178 o = { type: o }; |
| 179 } |
| 180 flattenedProps[p] = o; |
| 181 } |
| 182 return flattenedProps; |
| 183 } |
| 184 |
| 185 /** |
| 186 * Returns a flattened list of properties mixed together from the chain of a
ll |
| 187 * constructor's `config.properties`. This list is used to create |
| 188 * (1) observedAttributes, |
| 189 * (2) class property default values |
| 190 * |
| 191 * @param {HTMLElement} klass Element class |
| 192 * @return {PolymerElementProperties} Flattened properties for this class |
| 193 * @private |
| 194 */ |
| 195 function propertiesForClass(klass) { |
| 196 if (!klass.hasOwnProperty( |
| 197 JSCompiler_renameProperty('__classProperties', klass))) { |
| 198 klass.__classProperties = |
| 199 flattenProperties({}, ownPropertiesForClass(klass)); |
| 200 let superCtor = Object.getPrototypeOf(klass.prototype).constructor; |
| 201 if (superCtor.prototype instanceof PolymerElement) { |
| 202 klass.__classProperties = Object.assign( |
| 203 Object.create(propertiesForClass(superCtor)), |
| 204 klass.__classProperties); |
| 205 } |
| 206 } |
| 207 return klass.__classProperties; |
| 208 } |
| 209 |
| 210 /** |
| 211 * Returns a list of properties with default values. |
| 212 * This list is created as an optimization since it is a subset of |
| 213 * the list returned from `propertiesForClass`. |
| 214 * This list is used in `_initializeProperties` to set property defaults. |
| 215 * |
| 216 * @param {HTMLElement} klass Element class |
| 217 * @return {PolymerElementProperties} Flattened properties for this class |
| 218 * that have default values |
| 219 * @private |
| 220 */ |
| 221 function propertyDefaultsForClass(klass) { |
| 222 if (!klass.hasOwnProperty( |
| 223 JSCompiler_renameProperty('__classPropertyDefaults', klass))) { |
| 224 klass.__classPropertyDefaults = null; |
| 225 let props = propertiesForClass(klass); |
| 226 for (let p in props) { |
| 227 let info = props[p]; |
| 228 if ('value' in info) { |
| 229 klass.__classPropertyDefaults = klass.__classPropertyDefaults || {}; |
| 230 klass.__classPropertyDefaults[p] = info; |
| 231 } |
| 232 } |
| 233 } |
| 234 return klass.__classPropertyDefaults; |
| 235 } |
| 236 |
| 237 /** |
| 238 * Returns true if a `klass` has finalized. Called in `ElementClass.finalize
()` |
| 239 * @param {HTMLElement} klass Element class |
| 240 * @return {boolean} True if all metaprogramming for this class has been |
| 241 * completed |
| 242 * @private |
| 243 */ |
| 244 function hasClassFinalized(klass) { |
| 245 return klass.hasOwnProperty(JSCompiler_renameProperty('__finalized', klass
)); |
| 246 } |
| 247 |
| 248 /** |
| 249 * Called by `ElementClass.finalize()`. Ensures this `klass` and |
| 250 * *all superclasses* are finalized by traversing the prototype chain |
| 251 * and calling `klass.finalize()`. |
| 252 * |
| 253 * @param {HTMLElement} klass Element class |
| 254 * @private |
| 255 */ |
| 256 function finalizeClassAndSuper(klass) { |
| 257 let proto = klass.prototype; |
| 258 let superCtor = Object.getPrototypeOf(proto).constructor; |
| 259 if (superCtor.prototype instanceof PolymerElement) { |
| 260 superCtor.finalize(); |
| 261 } |
| 262 finalizeClass(klass); |
| 263 } |
| 264 |
| 265 /** |
| 266 * Configures a `klass` based on a staic `klass.config` object and |
| 267 * a `template`. This includes creating accessors and effects |
| 268 * for properties in `config` and the `template` as well as preparing the |
| 269 * `template` for stamping. |
| 270 * |
| 271 * @param {HTMLElement} klass Element class |
| 272 * @private |
| 273 */ |
| 274 function finalizeClass(klass) { |
| 275 klass.__finalized = true; |
| 276 let proto = klass.prototype; |
| 277 if (klass.hasOwnProperty( |
| 278 JSCompiler_renameProperty('is', klass)) && klass.is) { |
| 279 Polymer.telemetry.register(proto); |
| 280 } |
| 281 let props = ownPropertiesForClass(klass); |
| 282 if (props) { |
| 283 finalizeProperties(proto, props); |
| 284 } |
| 285 let observers = ownObserversForClass(klass); |
| 286 if (observers) { |
| 287 finalizeObservers(proto, observers, props); |
| 288 } |
| 289 // note: create "working" template that is finalized at instance time |
| 290 let template = klass.template; |
| 291 if (template) { |
| 292 if (typeof template === 'string') { |
| 293 let t = document.createElement('template'); |
| 294 t.innerHTML = template; |
| 295 template = t; |
| 296 } else { |
| 297 template = template.cloneNode(true); |
| 298 } |
| 299 proto._template = template; |
| 300 } |
| 301 } |
| 302 |
| 303 /** |
| 304 * Configures a `proto` based on a `properties` object. |
| 305 * Leverages `PropertyEffects` to create property accessors and effects |
| 306 * supporting, observers, reflecting to attributes, change notification, |
| 307 * computed properties, and read only properties. |
| 308 * @param {HTMLElement} proto Element class prototype to add accessors |
| 309 * and effects to |
| 310 * @param {Object} properties Flattened bag of property descriptors for |
| 311 * this class |
| 312 * @private |
| 313 */ |
| 314 function finalizeProperties(proto, properties) { |
| 315 for (let p in properties) { |
| 316 createPropertyFromConfig(proto, p, properties[p], properties); |
| 317 } |
| 318 } |
| 319 |
| 320 /** |
| 321 * Configures a `proto` based on a `observers` array. |
| 322 * Leverages `PropertyEffects` to create observers. |
| 323 * @param {HTMLElement} proto Element class prototype to add accessors |
| 324 * and effects to |
| 325 * @param {Object} observers Flattened array of observer descriptors for |
| 326 * this class |
| 327 * @param {Object} dynamicFns Object containing keys for any properties |
| 328 * that are functions and should trigger the effect when the function |
| 329 * reference is changed |
| 330 * @private |
| 331 */ |
| 332 function finalizeObservers(proto, observers, dynamicFns) { |
| 333 for (let i=0; i < observers.length; i++) { |
| 334 proto._createMethodObserver(observers[i], dynamicFns); |
| 335 } |
| 336 } |
| 337 |
| 338 /** |
| 339 * Creates effects for a property. |
| 340 * |
| 341 * Note, once a property has been set to |
| 342 * `readOnly`, `computed`, `reflectToAttribute`, or `notify` |
| 343 * these values may not be changed. For example, a subclass cannot |
| 344 * alter these settings. However, additional `observers` may be added |
| 345 * by subclasses. |
| 346 * |
| 347 * The info object should may contain property metadata as follows: |
| 348 * |
| 349 * * `type`: {function} type to which an attribute matching the property |
| 350 * is deserialized. Note the property is camel-cased from a dash-cased |
| 351 * attribute. For example, 'foo-bar' attribute is dersialized to a |
| 352 * property named 'fooBar'. |
| 353 * |
| 354 * * `readOnly`: {boolean} creates a readOnly property and |
| 355 * makes a private setter for the private of the form '_setFoo' for a |
| 356 * property 'foo', |
| 357 * |
| 358 * * `computed`: {string} creates a computed property. A computed property |
| 359 * also automatically is set to `readOnly: true`. The value is calculated |
| 360 * by running a method and arguments parsed from the given string. For |
| 361 * example 'compute(foo)' will compute a given property when the |
| 362 * 'foo' property changes by executing the 'compute' method. This method |
| 363 * must return the computed value. |
| 364 * |
| 365 * * `reflectToAttriute`: {boolean} If true, the property value is reflected |
| 366 * to an attribute of the same name. Note, the attribute is dash-cased |
| 367 * so a property named 'fooBar' is reflected as 'foo-bar'. |
| 368 * |
| 369 * * `notify`: {boolean} sends a non-bubbling notification event when |
| 370 * the property changes. For example, a property named 'foo' sends an |
| 371 * event named 'foo-changed' with `event.detail` set to the value of |
| 372 * the property. |
| 373 * |
| 374 * * observer: {string} name of a method that runs when the property |
| 375 * changes. The arguments of the method are (value, previousValue). |
| 376 * |
| 377 * Note: Users may want control over modifying property |
| 378 * effects via subclassing. For example, a user might want to make a |
| 379 * reflectToAttribute property not do so in a subclass. We've chosen to |
| 380 * disable this because it leads to additional complication. |
| 381 * For example, a readOnly effect generates a special setter. If a subclass |
| 382 * disables the effect, the setter would fail unexpectedly. |
| 383 * Based on feedback, we may want to try to make effects more malleable |
| 384 * and/or provide an advanced api for manipulating them. |
| 385 * Also consider adding warnings when an effect cannot be changed. |
| 386 * |
| 387 * @param {HTMLElement} proto Element class prototype to add accessors |
| 388 * and effects to |
| 389 * @param {string} name Name of the property. |
| 390 * @param {Object} info Info object from which to create property effects. |
| 391 * Supported keys: |
| 392 * @param {Object} allProps Flattened map of all properties defined in this |
| 393 * element (including inherited properties) |
| 394 * @private |
| 395 */ |
| 396 function createPropertyFromConfig(proto, name, info, allProps) { |
| 397 // computed forces readOnly... |
| 398 if (info.computed) { |
| 399 info.readOnly = true; |
| 400 } |
| 401 // Note, since all computed properties are readOnly, this prevents |
| 402 // adding additional computed property effects (which leads to a confusing |
| 403 // setup where multiple triggers for setting a property) |
| 404 // While we do have `hasComputedEffect` this is set on the property's |
| 405 // dependencies rather than itself. |
| 406 if (info.computed && !proto._hasReadOnlyEffect(name)) { |
| 407 proto._createComputedProperty(name, info.computed, allProps); |
| 408 } |
| 409 if (info.readOnly && !proto._hasReadOnlyEffect(name)) { |
| 410 proto._createReadOnlyProperty(name, !info.computed); |
| 411 } |
| 412 if (info.reflectToAttribute && !proto._hasReflectEffect(name)) { |
| 413 proto._createReflectedProperty(name); |
| 414 } |
| 415 if (info.notify && !proto._hasNotifyEffect(name)) { |
| 416 proto._createNotifyingProperty(name); |
| 417 } |
| 418 // always add observer |
| 419 if (info.observer) { |
| 420 proto._createPropertyObserver(name, info.observer, allProps[info.observe
r]); |
| 421 } |
| 422 } |
| 423 |
| 424 /** |
| 425 * Configures an element `proto` to function with a given `template`. |
| 426 * The element name `is` and extends `ext` must be specified for ShadyCSS |
| 427 * style scoping. |
| 428 * |
| 429 * @param {HTMLElement} proto Element class prototype to add accessors |
| 430 * and effects to |
| 431 * @param {HTMLTemplateElement} template Template to process and bind |
| 432 * @param {string} baseURI URL against which to resolve urls in |
| 433 * style element cssText |
| 434 * @param {string} is Tag name (or type extension name) for this element |
| 435 * @param {string=} ext For type extensions, the tag name that was extended |
| 436 * @private |
| 437 */ |
| 438 function finalizeTemplate(proto, template, baseURI, is, ext) { |
| 439 // support `include="module-name"` |
| 440 let cssText = |
| 441 Polymer.StyleGather.cssFromTemplate(template, baseURI) + |
| 442 Polymer.StyleGather.cssFromModuleImports(is); |
| 443 if (cssText) { |
| 444 let style = document.createElement('style'); |
| 445 style.textContent = cssText; |
| 446 template.content.insertBefore(style, template.content.firstChild); |
| 447 } |
| 448 if (window.ShadyCSS) { |
| 449 window.ShadyCSS.prepareTemplate(template, is, ext); |
| 450 } |
| 451 proto._bindTemplate(template); |
| 452 } |
| 453 |
| 454 /** |
| 455 * @polymerMixinClass |
| 456 * @unrestricted |
| 457 * @implements {Polymer_ElementMixin} |
| 458 */ |
| 459 class PolymerElement extends polymerElementBase { |
| 460 |
| 461 /** |
| 462 * Standard Custom Elements V1 API. The default implementation returns |
| 463 * a list of dash-cased attributes based on a flattening of all properties |
| 464 * declared in `static get properties()` for this element and any |
| 465 * superclasses. |
| 466 * |
| 467 * @return {Array} Observed attribute list |
| 468 */ |
| 469 static get observedAttributes() { |
| 470 if (!this.hasOwnProperty(JSCompiler_renameProperty('__observedAttributes
', this))) { |
| 471 let list = []; |
| 472 let properties = propertiesForClass(this); |
| 473 for (let prop in properties) { |
| 474 list.push(Polymer.CaseMap.camelToDashCase(prop)); |
| 475 } |
| 476 this.__observedAttributes = list; |
| 477 } |
| 478 return this.__observedAttributes; |
| 479 } |
| 480 |
| 481 /** |
| 482 * Called automatically when the first element instance is created to |
| 483 * ensure that class finalization work has been completed. |
| 484 * May be called by users to eagerly perform class finalization work |
| 485 * prior to the creation of the first element instance. |
| 486 * |
| 487 * Class finalization work generally includes meta-programming such as |
| 488 * creating property accessors and any property effect metadata needed for |
| 489 * the features used. |
| 490 * |
| 491 * @public |
| 492 */ |
| 493 static finalize() { |
| 494 if (!hasClassFinalized(this)) { |
| 495 finalizeClassAndSuper(this); |
| 496 } |
| 497 } |
| 498 |
| 499 /** |
| 500 * Returns the template that will be stamped into this element's shadow ro
ot. |
| 501 * |
| 502 * If a `static get is()` getter is defined, the default implementation |
| 503 * will return the first `<template>` in a `dom-module` whose `id` |
| 504 * matches this element's `is`. |
| 505 * |
| 506 * Users may override this getter to return an arbitrary template |
| 507 * (in which case the `is` getter is unnecessary). The template returned |
| 508 * may be either an `HTMLTemplateElement` or a string that will be |
| 509 * automatically parsed into a template. |
| 510 * |
| 511 * Note that when subclassing, if the super class overrode the default |
| 512 * implementation and the subclass would like to provide an alternate |
| 513 * template via a `dom-module`, it should override this getter and |
| 514 * return `Polymer.DomModule.import(this.is, 'template')`. |
| 515 * |
| 516 * If a subclass would like to modify the super class template, it should |
| 517 * clone it rather than modify it in place. If the getter does expensive |
| 518 * work such as cloning/modifying a template, it should memoize the |
| 519 * template for maximum performance: |
| 520 * |
| 521 * let memoizedTemplate; |
| 522 * class MySubClass extends MySuperClass { |
| 523 * static get template() { |
| 524 * if (!memoizedTemplate) { |
| 525 * memoizedTemplate = super.template.cloneNode(true); |
| 526 * let subContent = document.createElement('div'); |
| 527 * subContent.textContent = 'This came from MySubClass'; |
| 528 * memoizedTemplate.content.appendChild(subContent); |
| 529 * } |
| 530 * return memoizedTemplate; |
| 531 * } |
| 532 * } |
| 533 * |
| 534 * @return {HTMLTemplateElement|string} Template to be stamped |
| 535 */ |
| 536 static get template() { |
| 537 if (!this.hasOwnProperty(JSCompiler_renameProperty('_template', this)))
{ |
| 538 this._template = Polymer.DomModule.import(this.is, 'template') || |
| 539 // note: implemented so a subclass can retrieve the super |
| 540 // template; call the super impl this way so that `this` points |
| 541 // to the superclass. |
| 542 Object.getPrototypeOf(this.prototype).constructor.template; |
| 543 } |
| 544 return this._template; |
| 545 } |
| 546 |
| 547 /** |
| 548 * Path matching the url from which the element was imported. |
| 549 * This path is used to resolve url's in template style cssText. |
| 550 * The `importPath` property is also set on element instances and can be |
| 551 * used to create bindings relative to the import path. |
| 552 * Defaults to the path matching the url containing a `dom-module` element |
| 553 * matching this element's static `is` property. |
| 554 * Note, this path should contain a trailing `/`. |
| 555 * |
| 556 * @return {string} The import path for this element class |
| 557 */ |
| 558 static get importPath() { |
| 559 if (!this.hasOwnProperty(JSCompiler_renameProperty('_importPath', this))
) { |
| 560 const module = Polymer.DomModule.import(this.is); |
| 561 this._importPath = module ? module.assetpath : '' || |
| 562 Object.getPrototypeOf(this.prototype).constructor.importPath; |
| 563 } |
| 564 return this._importPath; |
| 565 } |
| 566 |
| 567 /** |
| 568 * Overrides the default `Polymer.PropertyAccessors` to ensure class |
| 569 * metaprogramming related to property accessors and effects has |
| 570 * completed (calls `finalize`). |
| 571 * |
| 572 * It also initializes any property defaults provided via `value` in |
| 573 * `properties` metadata. |
| 574 * |
| 575 * @override |
| 576 */ |
| 577 _initializeProperties() { |
| 578 Polymer.telemetry.instanceCount++; |
| 579 this.constructor.finalize(); |
| 580 const importPath = this.constructor.importPath; |
| 581 // note: finalize template when we have access to `localName` to |
| 582 // avoid dependence on `is` for polyfilling styling. |
| 583 if (this._template && !this._template.__polymerFinalized) { |
| 584 this._template.__polymerFinalized = true; |
| 585 const baseURI = |
| 586 importPath ? Polymer.ResolveUrl.resolveUrl(importPath) : ''; |
| 587 finalizeTemplate(this.__proto__, this._template, baseURI, |
| 588 this.localName); |
| 589 } |
| 590 super._initializeProperties(); |
| 591 // set path defaults |
| 592 this.rootPath = Polymer.rootPath; |
| 593 this.importPath = importPath; |
| 594 // apply property defaults... |
| 595 let p$ = propertyDefaultsForClass(this.constructor); |
| 596 if (!p$) { |
| 597 return; |
| 598 } |
| 599 for (let p in p$) { |
| 600 let info = p$[p]; |
| 601 // Don't set default value if there is already an own property, which |
| 602 // happens when a `properties` property with default but no effects ha
d |
| 603 // a property set (e.g. bound) by its host before upgrade |
| 604 if (!this.hasOwnProperty(p)) { |
| 605 let value = typeof info.value == 'function' ? |
| 606 info.value.call(this) : |
| 607 info.value; |
| 608 // Set via `_setProperty` if there is an accessor, to enable |
| 609 // initializing readOnly property defaults |
| 610 if (this._hasAccessor(p)) { |
| 611 this._setPendingProperty(p, value, true); |
| 612 } else { |
| 613 this[p] = value; |
| 614 } |
| 615 } |
| 616 } |
| 617 } |
| 618 |
| 619 /** |
| 620 * Provides a default implementation of the standard Custom Elements |
| 621 * `connectedCallback`. |
| 622 * |
| 623 * The default implementation enables the property effects system and |
| 624 * flushes any pending properties, and updates shimmed CSS properties |
| 625 * when using the ShadyCSS scoping/custom properties polyfill. |
| 626 * |
| 627 * @override |
| 628 */ |
| 629 connectedCallback() { |
| 630 if (window.ShadyCSS && this._template) { |
| 631 window.ShadyCSS.styleElement(this); |
| 632 } |
| 633 this._enableProperties(); |
| 634 } |
| 635 |
| 636 /** |
| 637 * Provides a default implementation of the standard Custom Elements |
| 638 * `disconnectedCallback`. |
| 639 * |
| 640 * @override |
| 641 */ |
| 642 disconnectedCallback() {} |
| 643 |
| 644 /** |
| 645 * Stamps the element template. |
| 646 * |
| 647 * @override |
| 648 */ |
| 649 ready() { |
| 650 if (this._template) { |
| 651 this.root = this._stampTemplate(this._template); |
| 652 this.$ = this.root.$; |
| 653 } |
| 654 super.ready(); |
| 655 } |
| 656 |
| 657 /** |
| 658 * Implements `PropertyEffects`'s `_readyClients` call. Attaches |
| 659 * element dom by calling `_attachDom` with the dom stamped from the |
| 660 * element's template via `_stampTemplate`. Note that this allows |
| 661 * client dom to be attached to the element prior to any observers |
| 662 * running. |
| 663 * |
| 664 * @override |
| 665 */ |
| 666 _readyClients() { |
| 667 if (this._template) { |
| 668 this.root = this._attachDom(this.root); |
| 669 } |
| 670 // The super._readyClients here sets the clients initialized flag. |
| 671 // We must wait to do this until after client dom is created/attached |
| 672 // so that this flag can be checked to prevent notifications fired |
| 673 // during this process from being handled before clients are ready. |
| 674 super._readyClients(); |
| 675 } |
| 676 |
| 677 |
| 678 /** |
| 679 * Attaches an element's stamped dom to itself. By default, |
| 680 * this method creates a `shadowRoot` and adds the dom to it. |
| 681 * However, this method may be overridden to allow an element |
| 682 * to put its dom in another location. |
| 683 * |
| 684 * @throws {Error} |
| 685 * @suppress {missingReturn} |
| 686 * @param {NodeList} dom to attach to the element. |
| 687 * @return {Node} node to which the dom has been attached. |
| 688 */ |
| 689 _attachDom(dom) { |
| 690 if (this.attachShadow) { |
| 691 if (dom) { |
| 692 if (!this.shadowRoot) { |
| 693 this.attachShadow({mode: 'open'}); |
| 694 } |
| 695 this.shadowRoot.appendChild(dom); |
| 696 return this.shadowRoot; |
| 697 } |
| 698 } else { |
| 699 throw new Error('ShadowDOM not available. ' + |
| 700 // TODO(sorvell): move to compile-time conditional when supported |
| 701 'Polymer.Element can create dom as children instead of in ' + |
| 702 'ShadowDOM by setting `this.root = this;\` before \`ready\`.'); |
| 703 } |
| 704 } |
| 705 |
| 706 /** |
| 707 * Provides a default implementation of the standard Custom Elements |
| 708 * `attributeChangedCallback`. |
| 709 * |
| 710 * By default, attributes declared in `properties` metadata are |
| 711 * deserialized using their `type` information to properties of the |
| 712 * same name. "Dash-cased" attributes are deserialzed to "camelCase" |
| 713 * properties. |
| 714 * |
| 715 * @override |
| 716 */ |
| 717 attributeChangedCallback(name, old, value) { |
| 718 if (old !== value) { |
| 719 let property = caseMap.dashToCamelCase(name); |
| 720 let type = propertiesForClass(this.constructor)[property].type; |
| 721 if (!this._hasReadOnlyEffect(property)) { |
| 722 this._attributeToProperty(name, value, type); |
| 723 } |
| 724 } |
| 725 } |
| 726 |
| 727 /** |
| 728 * When using the ShadyCSS scoping and custom property shim, causes all |
| 729 * shimmed styles in this element (and its subtree) to be updated |
| 730 * based on current custom property values. |
| 731 * |
| 732 * The optional parameter overrides inline custom property styles with an |
| 733 * object of properties where the keys are CSS properties, and the values |
| 734 * are strings. |
| 735 * |
| 736 * Example: `this.updateStyles({'--color': 'blue'})` |
| 737 * |
| 738 * These properties are retained unless a value of `null` is set. |
| 739 * |
| 740 * @param {Object=} properties Bag of custom property key/values to |
| 741 * apply to this element. |
| 742 */ |
| 743 updateStyles(properties) { |
| 744 if (window.ShadyCSS) { |
| 745 window.ShadyCSS.styleSubtree(this, properties); |
| 746 } |
| 747 } |
| 748 |
| 749 /** |
| 750 * Rewrites a given URL relative to a base URL. The base URL defaults to |
| 751 * the original location of the document containing the `dom-module` for |
| 752 * this element. This method will return the same URL before and after |
| 753 * bundling. |
| 754 * |
| 755 * @param {string} url URL to resolve. |
| 756 * @param {string=} base Optional base URL to resolve against, defaults |
| 757 * to the element's `importPath` |
| 758 * @return {string} Rewritten URL relative to base |
| 759 */ |
| 760 resolveUrl(url, base) { |
| 761 if (!base && this.importPath) { |
| 762 base = Polymer.ResolveUrl.resolveUrl(this.importPath); |
| 763 } |
| 764 return Polymer.ResolveUrl.resolveUrl(url, base); |
| 765 } |
| 766 |
| 767 /** |
| 768 * Overrides `PropertyAccessors` to add map of dynamic functions on |
| 769 * template info, for consumption by `PropertyEffects` template binding |
| 770 * code. This map determines which method templates should have accessors |
| 771 * created for them. |
| 772 * |
| 773 * @override |
| 774 */ |
| 775 static _parseTemplateContent(template, templateInfo, nodeInfo) { |
| 776 templateInfo.dynamicFns = templateInfo.dynamicFns || propertiesForClass(
this); |
| 777 return super._parseTemplateContent(template, templateInfo, nodeInfo); |
| 778 } |
| 779 |
| 780 } |
| 781 |
| 782 return PolymerElement; |
| 783 }); |
| 784 |
| 785 /** |
| 786 * Provides basic tracking of element definitions (registrations) and |
| 787 * instance counts. |
| 788 * |
| 789 * @namespace |
| 790 * @summary Provides basic tracking of element definitions (registrations) and |
| 791 * instance counts. |
| 792 */ |
| 793 Polymer.telemetry = { |
| 794 /** |
| 795 * Total number of Polymer element instances created. |
| 796 * @type {number} |
| 797 */ |
| 798 instanceCount: 0, |
| 799 /** |
| 800 * Array of Polymer element classes that have been finalized. |
| 801 * @type {Array<Polymer.Element>} |
| 802 */ |
| 803 registrations: [], |
| 804 /** |
| 805 * @param {HTMLElement} prototype Element prototype to log |
| 806 * @private |
| 807 */ |
| 808 _regLog: function(prototype) { |
| 809 console.log('[' + prototype.is + ']: registered') |
| 810 }, |
| 811 /** |
| 812 * Registers a class prototype for telemetry purposes. |
| 813 * @param {HTMLElement} prototype Element prototype to register |
| 814 * @protected |
| 815 */ |
| 816 register: function(prototype) { |
| 817 this.registrations.push(prototype); |
| 818 Polymer.log && this._regLog(prototype); |
| 819 }, |
| 820 /** |
| 821 * Logs all elements registered with an `is` to the console. |
| 822 * @public |
| 823 */ |
| 824 dumpRegistrations: function() { |
| 825 this.registrations.forEach(this._regLog); |
| 826 } |
| 827 }; |
| 828 |
| 829 /** |
| 830 * When using the ShadyCSS scoping and custom property shim, causes all |
| 831 * shimmed `styles` (via `custom-style`) in the document (and its subtree) |
| 832 * to be updated based on current custom property values. |
| 833 * |
| 834 * The optional parameter overrides inline custom property styles with an |
| 835 * object of properties where the keys are CSS properties, and the values |
| 836 * are strings. |
| 837 * |
| 838 * Example: `Polymer.updateStyles({'--color': 'blue'})` |
| 839 * |
| 840 * These properties are retained unless a value of `null` is set. |
| 841 * |
| 842 * @param {Object=} props Bag of custom property key/values to |
| 843 * apply to the document. |
| 844 */ |
| 845 Polymer.updateStyles = function(props) { |
| 846 if (window.ShadyCSS) { |
| 847 window.ShadyCSS.styleDocument(props); |
| 848 } |
| 849 }; |
| 850 |
| 851 /** |
| 852 * Globally settable property that is automatically assigned to |
| 853 * `Polymer.ElementMixin` instances, useful for binding in templates to |
| 854 * make URL's relative to an application's root. Defaults to the main |
| 855 * document URL, but can be overridden by users. It may be useful to set |
| 856 * `Polymer.rootPath` to provide a stable application mount path when |
| 857 * using client side routing. |
| 858 * |
| 859 * @memberof Polymer |
| 860 */ |
| 861 Polymer.rootPath = Polymer.rootPath || |
| 862 Polymer.ResolveUrl.pathFromUrl(document.baseURI || window.location.href); |
| 863 |
| 864 })(); |
| 865 </script> |
| OLD | NEW |