| 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="../../../shadycss/apply-shim.html"> |
| 12 <link rel="import" href="../mixins/element-mixin.html"> |
| 13 <link rel="import" href="../mixins/gesture-event-listeners.html"> |
| 14 <link rel="import" href="../utils/mixin.html"> |
| 15 <link rel="import" href="../utils/import-href.html"> |
| 16 <link rel="import" href="../utils/render-status.html"> |
| 17 <link rel="import" href="../utils/unresolved.html"> |
| 18 <link rel="import" href="polymer.dom.html"> |
| 19 |
| 20 <script> |
| 21 (function() { |
| 22 |
| 23 'use strict'; |
| 24 |
| 25 let styleInterface = window.ShadyCSS; |
| 26 |
| 27 /** |
| 28 * Element class mixin that provides Polymer's "legacy" API intended to be |
| 29 * backward-compatible to the greatest extent possible with the API |
| 30 * found on the Polymer 1.x `Polymer.Base` prototype applied to all elements |
| 31 * defined using the `Polymer({...})` function. |
| 32 * |
| 33 * @polymerMixin |
| 34 * @mixes Polymer.ElementMixin |
| 35 * @mixes Polymer.GestureEventListeners |
| 36 * @property isAttached {boolean} Set to `true` in this element's |
| 37 * `connectedCallback` and `false` in `disconnectedCallback` |
| 38 * @memberof Polymer |
| 39 * @summary Element class mixin that provides Polymer's "legacy" API |
| 40 */ |
| 41 Polymer.LegacyElementMixin = Polymer.dedupingMixin(base => { |
| 42 |
| 43 /** |
| 44 * @constructor |
| 45 * @extends {base} |
| 46 * @implements {Polymer_ElementMixin} |
| 47 * @implements {Polymer_GestureEventListeners} |
| 48 */ |
| 49 const legacyElementBase = Polymer.GestureEventListeners(Polymer.ElementMixin
(base)); |
| 50 |
| 51 /** |
| 52 * Map of simple names to touch action names |
| 53 * @dict |
| 54 */ |
| 55 const DIRECTION_MAP = { |
| 56 'x': 'pan-x', |
| 57 'y': 'pan-y', |
| 58 'none': 'none', |
| 59 'all': 'auto' |
| 60 }; |
| 61 |
| 62 /** |
| 63 * @polymerMixinClass |
| 64 * @implements {Polymer_LegacyElement} |
| 65 */ |
| 66 class LegacyElement extends legacyElementBase { |
| 67 |
| 68 constructor() { |
| 69 super(); |
| 70 this.root = this; |
| 71 this.created(); |
| 72 } |
| 73 |
| 74 /** |
| 75 * Legacy callback called during the `constructor`, for overriding |
| 76 * by the user. |
| 77 */ |
| 78 created() {} |
| 79 |
| 80 connectedCallback() { |
| 81 super.connectedCallback(); |
| 82 this.isAttached = true; |
| 83 this.attached(); |
| 84 } |
| 85 |
| 86 /** |
| 87 * Legacy callback called during `connectedCallback`, for overriding |
| 88 * by the user. |
| 89 */ |
| 90 attached() {} |
| 91 |
| 92 disconnectedCallback() { |
| 93 super.disconnectedCallback(); |
| 94 this.isAttached = false; |
| 95 this.detached(); |
| 96 } |
| 97 |
| 98 /** |
| 99 * Legacy callback called during `disconnectedCallback`, for overriding |
| 100 * by the user. |
| 101 */ |
| 102 detached() {} |
| 103 |
| 104 attributeChangedCallback(name, old, value) { |
| 105 if (old !== value) { |
| 106 super.attributeChangedCallback(name, old, value); |
| 107 this.attributeChanged(name, old, value); |
| 108 } |
| 109 } |
| 110 |
| 111 /** |
| 112 * Legacy callback called during `attributeChangedChallback`, for overridi
ng |
| 113 * by the user. |
| 114 */ |
| 115 attributeChanged() {} |
| 116 |
| 117 /** |
| 118 * Overrides the default `Polymer.PropertyEffects` implementation to |
| 119 * add support for class initialization via the `_registered` callback. |
| 120 * This is called only when the first instance of the element is created. |
| 121 * |
| 122 * @override |
| 123 */ |
| 124 _initializeProperties() { |
| 125 let proto = Object.getPrototypeOf(this); |
| 126 if (!proto.hasOwnProperty('__hasRegisterFinished')) { |
| 127 proto.__hasRegisterFinished = true; |
| 128 this._registered(); |
| 129 } |
| 130 super._initializeProperties(); |
| 131 } |
| 132 |
| 133 /** |
| 134 * Called automatically when an element is initializing. |
| 135 * Users may override this method to perform class registration time |
| 136 * work. The implementation should ensure the work is performed |
| 137 * only once for the class. |
| 138 * @protected |
| 139 */ |
| 140 _registered() {} |
| 141 |
| 142 /** |
| 143 * Overrides the default `Polymer.PropertyEffects` implementation to |
| 144 * add support for installing `hostAttributes` and `listeners`. |
| 145 * |
| 146 * @override |
| 147 */ |
| 148 ready() { |
| 149 this._ensureAttributes(); |
| 150 this._applyListeners(); |
| 151 super.ready(); |
| 152 } |
| 153 |
| 154 /** |
| 155 * Ensures an element has required attributes. Called when the element |
| 156 * is being readied via `ready`. Users should override to set the |
| 157 * element's required attributes. The implementation should be sure |
| 158 * to check and not override existing attributes added by |
| 159 * the user of the element. Typically, setting attributes should be left |
| 160 * to the element user and not done here; reasonable exceptions include |
| 161 * setting aria roles and focusability. |
| 162 * @protected |
| 163 */ |
| 164 _ensureAttributes() {} |
| 165 |
| 166 /** |
| 167 * Adds element event listeners. Called when the element |
| 168 * is being readied via `ready`. Users should override to |
| 169 * add any required element event listeners. |
| 170 * In performance critical elements, the work done here should be kept |
| 171 * to a minimum since it is done before the element is rendered. In |
| 172 * these elements, consider adding listeners asychronously so as not to |
| 173 * block render. |
| 174 * @protected |
| 175 */ |
| 176 _applyListeners() {} |
| 177 |
| 178 /** |
| 179 * Converts a typed JavaScript value to a string. |
| 180 * |
| 181 * Note this method is provided as backward-compatible legacy API |
| 182 * only. It is not directly called by any Polymer features. To customize |
| 183 * how properties are serialized to attributes for attribute bindings and |
| 184 * `reflectToAttribute: true` properties as well as this method, override |
| 185 * the `_serializeValue` method provided by `Polymer.PropertyAccessors`. |
| 186 * |
| 187 * @param {*} value Value to deserialize |
| 188 * @return {string} Serialized value |
| 189 */ |
| 190 serialize(value) { |
| 191 return this._serializeValue(value); |
| 192 } |
| 193 |
| 194 /** |
| 195 * Converts a string to a typed JavaScript value. |
| 196 * |
| 197 * Note this method is provided as backward-compatible legacy API |
| 198 * only. It is not directly called by any Polymer features. To customize |
| 199 * how attributes are deserialized to properties for in |
| 200 * `attributeChangedCallback`, override `_deserializeValue` method |
| 201 * provided by `Polymer.PropertyAccessors`. |
| 202 * |
| 203 * @param {string} value String to deserialize |
| 204 * @param {*} type Type to deserialize the string to |
| 205 * @return {*} Returns the deserialized value in the `type` given. |
| 206 */ |
| 207 deserialize(value, type) { |
| 208 return this._deserializeValue(value, type); |
| 209 } |
| 210 |
| 211 /** |
| 212 * Serializes a property to its associated attribute. |
| 213 * |
| 214 * Note this method is provided as backward-compatible legacy API |
| 215 * only. It is not directly called by any Polymer features. |
| 216 * |
| 217 * @param {string} property Property name to reflect. |
| 218 * @param {string=} attribute Attribute name to reflect. |
| 219 * @param {*=} value Property value to refect. |
| 220 */ |
| 221 reflectPropertyToAttribute(property, attribute, value) { |
| 222 this._propertyToAttribute(property, attribute, value); |
| 223 } |
| 224 |
| 225 /** |
| 226 * Sets a typed value to an HTML attribute on a node. |
| 227 * |
| 228 * Note this method is provided as backward-compatible legacy API |
| 229 * only. It is not directly called by any Polymer features. |
| 230 * |
| 231 * @param {*} value Value to serialize. |
| 232 * @param {string} attribute Attribute name to serialize to. |
| 233 * @param {Element} node Element to set attribute to. |
| 234 */ |
| 235 serializeValueToAttribute(value, attribute, node) { |
| 236 this._valueToNodeAttribute(node || this, value, attribute); |
| 237 } |
| 238 |
| 239 /** |
| 240 * Copies own properties (including accessor descriptors) from a source |
| 241 * object to a target object. |
| 242 * |
| 243 * @param {Object} prototype Target object to copy properties to. |
| 244 * @param {Object} api Source object to copy properties from. |
| 245 * @return {Object} prototype object that was passed as first argument. |
| 246 */ |
| 247 extend(prototype, api) { |
| 248 if (!(prototype && api)) { |
| 249 return prototype || api; |
| 250 } |
| 251 let n$ = Object.getOwnPropertyNames(api); |
| 252 for (let i=0, n; (i<n$.length) && (n=n$[i]); i++) { |
| 253 let pd = Object.getOwnPropertyDescriptor(api, n); |
| 254 if (pd) { |
| 255 Object.defineProperty(prototype, n, pd); |
| 256 } |
| 257 } |
| 258 return prototype; |
| 259 } |
| 260 |
| 261 /** |
| 262 * Copies props from a source object to a target object. |
| 263 * |
| 264 * Note, this method uses a simple `for...in` strategy for enumerating |
| 265 * properties. To ensure only `ownProperties` are copied from source |
| 266 * to target and that accessor implementations are copied, use `extend`. |
| 267 * |
| 268 * @param {Object} target Target object to copy properties to. |
| 269 * @param {Object} source Source object to copy properties from. |
| 270 * @return {Object} Target object that was passed as first argument. |
| 271 */ |
| 272 mixin(target, source) { |
| 273 for (let i in source) { |
| 274 target[i] = source[i]; |
| 275 } |
| 276 return target; |
| 277 } |
| 278 |
| 279 /** |
| 280 * Sets the prototype of an object. |
| 281 * |
| 282 * Note this method is provided as backward-compatible legacy API |
| 283 * only. It is not directly called by any Polymer features. |
| 284 * @param {Object} object The object on which to set the prototype. |
| 285 * @param {Object} prototype The prototype that will be set on the given |
| 286 * `object`. |
| 287 * @return {Object} Returns the given `object` with its prototype set |
| 288 * to the given `prototype` object. |
| 289 */ |
| 290 chainObject(object, prototype) { |
| 291 if (object && prototype && object !== prototype) { |
| 292 object.__proto__ = prototype; |
| 293 } |
| 294 return object; |
| 295 } |
| 296 |
| 297 /* **** Begin Template **** */ |
| 298 |
| 299 /** |
| 300 * Calls `importNode` on the `content` of the `template` specified and |
| 301 * returns a document fragment containing the imported content. |
| 302 * |
| 303 * @param {HTMLTemplateElement} template HTML template element to instance
. |
| 304 * @return {DocumentFragment} Document fragment containing the imported |
| 305 * template content. |
| 306 */ |
| 307 instanceTemplate(template) { |
| 308 let content = this.constructor._contentForTemplate(template); |
| 309 let dom = /** @type {DocumentFragment} */ |
| 310 (document.importNode(content, true)); |
| 311 return dom; |
| 312 } |
| 313 |
| 314 /* **** Begin Events **** */ |
| 315 |
| 316 /** |
| 317 * Dispatches a custom event with an optional detail value. |
| 318 * |
| 319 * @param {string} type Name of event type. |
| 320 * @param {*=} detail Detail value containing event-specific |
| 321 * payload. |
| 322 * @param {Object=} options Object specifying options. These may include: |
| 323 * `bubbles` (boolean, defaults to `true`), |
| 324 * `cancelable` (boolean, defaults to false), and |
| 325 * `node` on which to fire the event (HTMLElement, defaults to `this`). |
| 326 * @return {Event} The new event that was fired. |
| 327 */ |
| 328 fire(type, detail, options) { |
| 329 options = options || {}; |
| 330 detail = (detail === null || detail === undefined) ? {} : detail; |
| 331 let event = new Event(type, { |
| 332 bubbles: options.bubbles === undefined ? true : options.bubbles, |
| 333 cancelable: Boolean(options.cancelable), |
| 334 composed: options.composed === undefined ? true: options.composed |
| 335 }); |
| 336 event.detail = detail; |
| 337 let node = options.node || this; |
| 338 node.dispatchEvent(event) |
| 339 return event; |
| 340 } |
| 341 |
| 342 /** |
| 343 * Convenience method to add an event listener on a given element, |
| 344 * late bound to a named method on this element. |
| 345 * |
| 346 * @param {Element} node Element to add event listener to. |
| 347 * @param {string} eventName Name of event to listen for. |
| 348 * @param {string} methodName Name of handler method on `this` to call. |
| 349 */ |
| 350 listen(node, eventName, methodName) { |
| 351 node = node || this; |
| 352 let hbl = this.__boundListeners || |
| 353 (this.__boundListeners = new WeakMap()); |
| 354 let bl = hbl.get(node); |
| 355 if (!bl) { |
| 356 bl = {}; |
| 357 hbl.set(node, bl); |
| 358 } |
| 359 let key = eventName + methodName; |
| 360 if (!bl[key]) { |
| 361 bl[key] = this._addMethodEventListenerToNode( |
| 362 node, eventName, methodName, this); |
| 363 } |
| 364 } |
| 365 |
| 366 /** |
| 367 * Convenience method to remove an event listener from a given element, |
| 368 * late bound to a named method on this element. |
| 369 * |
| 370 * @param {Element} node Element to remove event listener from. |
| 371 * @param {string} eventName Name of event to stop listening to. |
| 372 * @param {string} methodName Name of handler method on `this` to not call |
| 373 anymore. |
| 374 */ |
| 375 unlisten(node, eventName, methodName) { |
| 376 node = node || this; |
| 377 let bl = this.__boundListeners && this.__boundListeners.get(node); |
| 378 let key = eventName + methodName; |
| 379 let handler = bl && bl[key]; |
| 380 if (handler) { |
| 381 this._removeEventListenerFromNode(node, eventName, handler); |
| 382 bl[key] = null; |
| 383 } |
| 384 } |
| 385 |
| 386 /** |
| 387 * Override scrolling behavior to all direction, one direction, or none. |
| 388 * |
| 389 * Valid scroll directions: |
| 390 * - 'all': scroll in any direction |
| 391 * - 'x': scroll only in the 'x' direction |
| 392 * - 'y': scroll only in the 'y' direction |
| 393 * - 'none': disable scrolling for this node |
| 394 * |
| 395 * @param {string=} direction Direction to allow scrolling |
| 396 * Defaults to `all`. |
| 397 * @param {HTMLElement=} node Element to apply scroll direction setting. |
| 398 * Defaults to `this`. |
| 399 */ |
| 400 setScrollDirection(direction, node) { |
| 401 Polymer.Gestures.setTouchAction(node || this, DIRECTION_MAP[direction] |
| 'auto'); |
| 402 } |
| 403 /* **** End Events **** */ |
| 404 |
| 405 /** |
| 406 * Convenience method to run `querySelector` on this local DOM scope. |
| 407 * |
| 408 * This function calls `Polymer.dom(this.root).querySelector(slctr)`. |
| 409 * |
| 410 * @param {string} slctr Selector to run on this local DOM scope |
| 411 * @return {Element} Element found by the selector, or null if not found. |
| 412 */ |
| 413 $$(slctr) { |
| 414 return this.root.querySelector(slctr); |
| 415 } |
| 416 |
| 417 /** |
| 418 * Return the element whose local dom within which this element |
| 419 * is contained. This is a shorthand for |
| 420 * `this.getRootNode().host`. |
| 421 */ |
| 422 get domHost() { |
| 423 let root = this.getRootNode(); |
| 424 return (root instanceof DocumentFragment) ? root.host : root; |
| 425 } |
| 426 |
| 427 /** |
| 428 * Force this element to distribute its children to its local dom. |
| 429 * This is necessary only when ShadyDOM is used and only in cases that |
| 430 * are not automatically handled. For example, |
| 431 * a user should call `distributeContent` if distribution has been |
| 432 * invalidated due to an element being added or removed from the shadowRoo
t |
| 433 * that contains an insertion point (`<slot>`) inside its subtree. |
| 434 */ |
| 435 distributeContent() { |
| 436 if (window.ShadyDOM && this.shadowRoot) { |
| 437 this.shadowRoot.forceRender(); |
| 438 } |
| 439 } |
| 440 |
| 441 /** |
| 442 * Returns a list of nodes that are the effective childNodes. The effectiv
e |
| 443 * childNodes list is the same as the element's childNodes except that |
| 444 * any `<content>` elements are replaced with the list of nodes distribute
d |
| 445 * to the `<content>`, the result of its `getDistributedNodes` method. |
| 446 * |
| 447 * @return {Array<Node>} List of effctive child nodes. |
| 448 */ |
| 449 getEffectiveChildNodes() { |
| 450 return Polymer.dom(this).getEffectiveChildNodes(); |
| 451 } |
| 452 |
| 453 /** |
| 454 * Returns a list of nodes distributed within this element that match |
| 455 * `selector`. These can be dom children or elements distributed to |
| 456 * children that are insertion points. |
| 457 * @param {string} selector Selector to run. |
| 458 * @return {Array<Node>} List of distributed elements that match selector. |
| 459 */ |
| 460 queryDistributedElements(selector) { |
| 461 return Polymer.dom(this).queryDistributedElements(selector); |
| 462 } |
| 463 |
| 464 /** |
| 465 * Returns a list of elements that are the effective children. The effecti
ve |
| 466 * children list is the same as the element's children except that |
| 467 * any `<content>` elements are replaced with the list of elements |
| 468 * distributed to the `<content>`. |
| 469 * |
| 470 * @return {Array<Node>} List of effctive children. |
| 471 */ |
| 472 getEffectiveChildren() { |
| 473 let list = this.getEffectiveChildNodes(); |
| 474 return list.filter(function(n) { |
| 475 return (n.nodeType === Node.ELEMENT_NODE); |
| 476 }); |
| 477 } |
| 478 |
| 479 /** |
| 480 * Returns a string of text content that is the concatenation of the |
| 481 * text content's of the element's effective childNodes (the elements |
| 482 * returned by <a href="#getEffectiveChildNodes>getEffectiveChildNodes</a>
. |
| 483 * |
| 484 * @return {string} List of effctive children. |
| 485 */ |
| 486 getEffectiveTextContent() { |
| 487 let cn = this.getEffectiveChildNodes(); |
| 488 let tc = []; |
| 489 for (let i=0, c; (c = cn[i]); i++) { |
| 490 if (c.nodeType !== Node.COMMENT_NODE) { |
| 491 tc.push(c.textContent); |
| 492 } |
| 493 } |
| 494 return tc.join(''); |
| 495 } |
| 496 |
| 497 /** |
| 498 * Returns the first effective childNode within this element that |
| 499 * match `selector`. These can be dom child nodes or elements distributed |
| 500 * to children that are insertion points. |
| 501 * @param {string} selector Selector to run. |
| 502 * @return {Object<Node>} First effective child node that matches selector
. |
| 503 */ |
| 504 queryEffectiveChildren(selector) { |
| 505 let e$ = this.queryDistributedElements(selector); |
| 506 return e$ && e$[0]; |
| 507 } |
| 508 |
| 509 /** |
| 510 * Returns a list of effective childNodes within this element that |
| 511 * match `selector`. These can be dom child nodes or elements distributed |
| 512 * to children that are insertion points. |
| 513 * @param {string} selector Selector to run. |
| 514 * @return {Array<Node>} List of effective child nodes that match selector
. |
| 515 */ |
| 516 queryAllEffectiveChildren(selector) { |
| 517 return this.queryDistributedElements(selector); |
| 518 } |
| 519 |
| 520 /** |
| 521 * Returns a list of nodes distributed to this element's `<slot>`. |
| 522 * |
| 523 * If this element contains more than one `<slot>` in its local DOM, |
| 524 * an optional selector may be passed to choose the desired content. |
| 525 * |
| 526 * @param {string=} slctr CSS selector to choose the desired |
| 527 * `<slot>`. Defaults to `content`. |
| 528 * @return {Array<Node>} List of distributed nodes for the `<slot>`. |
| 529 */ |
| 530 getContentChildNodes(slctr) { |
| 531 let content = this.root.querySelector(slctr || 'slot'); |
| 532 return content ? Polymer.dom(content).getDistributedNodes() : []; |
| 533 } |
| 534 |
| 535 /** |
| 536 * Returns a list of element children distributed to this element's |
| 537 * `<slot>`. |
| 538 * |
| 539 * If this element contains more than one `<slot>` in its |
| 540 * local DOM, an optional selector may be passed to choose the desired |
| 541 * content. This method differs from `getContentChildNodes` in that only |
| 542 * elements are returned. |
| 543 * |
| 544 * @param {string=} slctr CSS selector to choose the desired |
| 545 * `<content>`. Defaults to `content`. |
| 546 * @return {Array<HTMLElement>} List of distributed nodes for the |
| 547 * `<slot>`. |
| 548 */ |
| 549 getContentChildren(slctr) { |
| 550 return this.getContentChildNodes(slctr).filter(function(n) { |
| 551 return (n.nodeType === Node.ELEMENT_NODE); |
| 552 }); |
| 553 } |
| 554 |
| 555 /** |
| 556 * Checks whether an element is in this element's light DOM tree. |
| 557 * |
| 558 * @param {?Node} node The element to be checked. |
| 559 * @return {boolean} true if node is in this element's light DOM tree. |
| 560 */ |
| 561 isLightDescendant(node) { |
| 562 return this !== node && this.contains(node) && |
| 563 this.getRootNode() === node.getRootNode(); |
| 564 } |
| 565 |
| 566 /** |
| 567 * Checks whether an element is in this element's local DOM tree. |
| 568 * |
| 569 * @param {HTMLElement=} node The element to be checked. |
| 570 * @return {boolean} true if node is in this element's local DOM tree. |
| 571 */ |
| 572 isLocalDescendant(node) { |
| 573 return this.root === node.getRootNode(); |
| 574 } |
| 575 |
| 576 // NOTE: should now be handled by ShadyCss library. |
| 577 scopeSubtree(container, shouldObserve) { // eslint-disable-line no-unused-
vars |
| 578 } |
| 579 |
| 580 /** |
| 581 * Returns the computed style value for the given property. |
| 582 * @param {string} property The css property name. |
| 583 * @return {string} Returns the computed css property value for the given |
| 584 * `property`. |
| 585 */ |
| 586 getComputedStyleValue(property) { |
| 587 return styleInterface.getComputedStyleValue(this, property); |
| 588 } |
| 589 |
| 590 // debounce |
| 591 |
| 592 /** |
| 593 * Call `debounce` to collapse multiple requests for a named task into |
| 594 * one invocation which is made after the wait time has elapsed with |
| 595 * no new request. If no wait time is given, the callback will be called |
| 596 * at microtask timing (guaranteed before paint). |
| 597 * |
| 598 * debouncedClickAction(e) { |
| 599 * // will not call `processClick` more than once per 100ms |
| 600 * this.debounce('click', function() { |
| 601 * this.processClick(); |
| 602 * } 100); |
| 603 * } |
| 604 * |
| 605 * @param {string} jobName String to indentify the debounce job. |
| 606 * @param {function()} callback Function that is called (with `this` |
| 607 * context) when the wait time elapses. |
| 608 * @param {number} wait Optional wait time in milliseconds (ms) after the |
| 609 * last signal that must elapse before invoking `callback` |
| 610 * @return {Object} Returns a debouncer object on which exists the |
| 611 * following methods: `isActive()` returns true if the debouncer is |
| 612 * active; `cancel()` cancels the debouncer if it is active; |
| 613 * `flush()` immediately invokes the debounced callback if the debouncer |
| 614 * is active. |
| 615 */ |
| 616 debounce(jobName, callback, wait) { |
| 617 this._debouncers = this._debouncers || {}; |
| 618 return this._debouncers[jobName] = Polymer.Debouncer.debounce( |
| 619 this._debouncers[jobName] |
| 620 , wait > 0 ? Polymer.Async.timeOut.after(wait) : Polymer.Async.micro
Task |
| 621 , callback.bind(this)); |
| 622 } |
| 623 |
| 624 /** |
| 625 * Returns whether a named debouncer is active. |
| 626 * |
| 627 * @param {string} jobName The name of the debouncer started with `debounc
e` |
| 628 * @return {boolean} Whether the debouncer is active (has not yet fired). |
| 629 */ |
| 630 isDebouncerActive(jobName) { |
| 631 this._debouncers = this._debouncers || {}; |
| 632 let debouncer = this._debouncers[jobName]; |
| 633 return !!(debouncer && debouncer.isActive()); |
| 634 } |
| 635 |
| 636 /** |
| 637 * Immediately calls the debouncer `callback` and inactivates it. |
| 638 * |
| 639 * @param {string} jobName The name of the debouncer started with `debounc
e` |
| 640 */ |
| 641 flushDebouncer(jobName) { |
| 642 this._debouncers = this._debouncers || {}; |
| 643 let debouncer = this._debouncers[jobName]; |
| 644 if (debouncer) { |
| 645 debouncer.flush(); |
| 646 } |
| 647 } |
| 648 |
| 649 /** |
| 650 * Cancels an active debouncer. The `callback` will not be called. |
| 651 * |
| 652 * @param {string} jobName The name of the debouncer started with `debounc
e` |
| 653 */ |
| 654 cancelDebouncer(jobName) { |
| 655 this._debouncers = this._debouncers || {} |
| 656 let debouncer = this._debouncers[jobName]; |
| 657 if (debouncer) { |
| 658 debouncer.cancel(); |
| 659 } |
| 660 } |
| 661 |
| 662 /** |
| 663 * Runs a callback function asyncronously. |
| 664 * |
| 665 * By default (if no waitTime is specified), async callbacks are run at |
| 666 * microtask timing, which will occur before paint. |
| 667 * |
| 668 * @param {Function} callback The callback function to run, bound to `this
`. |
| 669 * @param {number=} waitTime Time to wait before calling the |
| 670 * `callback`. If unspecified or 0, the callback will be run at microta
sk |
| 671 * timing (before paint). |
| 672 * @return {number} Handle that may be used to cancel the async job. |
| 673 */ |
| 674 async(callback, waitTime) { |
| 675 return waitTime > 0 ? Polymer.Async.timeOut.run(callback.bind(this), wai
tTime) : |
| 676 ~Polymer.Async.microTask.run(callback.bind(this)); |
| 677 } |
| 678 |
| 679 /** |
| 680 * Cancels an async operation started with `async`. |
| 681 * |
| 682 * @param {number} handle Handle returned from original `async` call to |
| 683 * cancel. |
| 684 */ |
| 685 cancelAsync(handle) { |
| 686 handle < 0 ? Polymer.Async.microTask.cancel(~handle) : |
| 687 Polymer.Async.timeOut.cancel(handle); |
| 688 } |
| 689 |
| 690 // other |
| 691 |
| 692 /** |
| 693 * Convenience method for creating an element and configuring it. |
| 694 * |
| 695 * @param {string} tag HTML element tag to create. |
| 696 * @param {Object} props Object of properties to configure on the |
| 697 * instance. |
| 698 * @return {Element} Newly created and configured element. |
| 699 */ |
| 700 create(tag, props) { |
| 701 let elt = document.createElement(tag); |
| 702 if (props) { |
| 703 if (elt.setProperties) { |
| 704 elt.setProperties(props); |
| 705 } else { |
| 706 for (let n in props) { |
| 707 elt[n] = props[n]; |
| 708 } |
| 709 } |
| 710 } |
| 711 return elt; |
| 712 } |
| 713 |
| 714 /** |
| 715 * Convenience method for importing an HTML document imperatively. |
| 716 * |
| 717 * This method creates a new `<link rel="import">` element with |
| 718 * the provided URL and appends it to the document to start loading. |
| 719 * In the `onload` callback, the `import` property of the `link` |
| 720 * element will contain the imported document contents. |
| 721 * |
| 722 * @param {string} href URL to document to load. |
| 723 * @param {Function} onload Callback to notify when an import successfully |
| 724 * loaded. |
| 725 * @param {Function} onerror Callback to notify when an import |
| 726 * unsuccessfully loaded. |
| 727 * @param {boolean} optAsync True if the import should be loaded `async`. |
| 728 * Defaults to `false`. |
| 729 * @return {HTMLLinkElement} The link element for the URL to be loaded. |
| 730 */ |
| 731 importHref(href, onload, onerror, optAsync) { // eslint-disable-line no-un
used-vars |
| 732 let loadFn = onload ? onload.bind(this) : null; |
| 733 let errorFn = onerror ? onerror.bind(this) : null; |
| 734 return Polymer.importHref(href, loadFn, errorFn, optAsync); |
| 735 } |
| 736 |
| 737 /** |
| 738 * Polyfill for Element.prototype.matches, which is sometimes still |
| 739 * prefixed. |
| 740 * |
| 741 * @param {string} selector Selector to test. |
| 742 * @param {Element=} node Element to test the selector against. |
| 743 * @return {boolean} Whether the element matches the selector. |
| 744 */ |
| 745 elementMatches(selector, node) { |
| 746 return Polymer.dom.matchesSelector(node || this, selector); |
| 747 } |
| 748 |
| 749 /** |
| 750 * Toggles an HTML attribute on or off. |
| 751 * |
| 752 * @param {string} name HTML attribute name |
| 753 * @param {boolean=} bool Boolean to force the attribute on or off. |
| 754 * When unspecified, the state of the attribute will be reversed. |
| 755 * @param {HTMLElement=} node Node to target. Defaults to `this`. |
| 756 */ |
| 757 toggleAttribute(name, bool, node) { |
| 758 node = node || this; |
| 759 if (arguments.length == 1) { |
| 760 bool = !node.hasAttribute(name); |
| 761 } |
| 762 if (bool) { |
| 763 node.setAttribute(name, ''); |
| 764 } else { |
| 765 node.removeAttribute(name); |
| 766 } |
| 767 } |
| 768 |
| 769 |
| 770 /** |
| 771 * Toggles a CSS class on or off. |
| 772 * |
| 773 * @param {string} name CSS class name |
| 774 * @param {boolean=} bool Boolean to force the class on or off. |
| 775 * When unspecified, the state of the class will be reversed. |
| 776 * @param {HTMLElement=} node Node to target. Defaults to `this`. |
| 777 */ |
| 778 toggleClass(name, bool, node) { |
| 779 node = node || this; |
| 780 if (arguments.length == 1) { |
| 781 bool = !node.classList.contains(name); |
| 782 } |
| 783 if (bool) { |
| 784 node.classList.add(name); |
| 785 } else { |
| 786 node.classList.remove(name); |
| 787 } |
| 788 } |
| 789 |
| 790 /** |
| 791 * Cross-platform helper for setting an element's CSS `transform` property
. |
| 792 * |
| 793 * @param {string} transformText Transform setting. |
| 794 * @param {HTMLElement=} node Element to apply the transform to. |
| 795 * Defaults to `this` |
| 796 */ |
| 797 transform(transformText, node) { |
| 798 node = node || this; |
| 799 node.style.webkitTransform = transformText; |
| 800 node.style.transform = transformText; |
| 801 } |
| 802 |
| 803 /** |
| 804 * Cross-platform helper for setting an element's CSS `translate3d` |
| 805 * property. |
| 806 * |
| 807 * @param {number} x X offset. |
| 808 * @param {number} y Y offset. |
| 809 * @param {number} z Z offset. |
| 810 * @param {HTMLElement=} node Element to apply the transform to. |
| 811 * Defaults to `this`. |
| 812 */ |
| 813 translate3d(x, y, z, node) { |
| 814 node = node || this; |
| 815 this.transform('translate3d(' + x + ',' + y + ',' + z + ')', node); |
| 816 } |
| 817 |
| 818 /** |
| 819 * Removes an item from an array, if it exists. |
| 820 * |
| 821 * If the array is specified by path, a change notification is |
| 822 * generated, so that observers, data bindings and computed |
| 823 * properties watching that path can update. |
| 824 * |
| 825 * If the array is passed directly, **no change |
| 826 * notification is generated**. |
| 827 * |
| 828 * @param {string | !Array<number|string>} arrayOrPath Path to array from
which to remove the item |
| 829 * (or the array itself). |
| 830 * @param {*} item Item to remove. |
| 831 * @return {Array} Array containing item removed. |
| 832 */ |
| 833 arrayDelete(arrayOrPath, item) { |
| 834 let index; |
| 835 if (Array.isArray(arrayOrPath)) { |
| 836 index = arrayOrPath.indexOf(item); |
| 837 if (index >= 0) { |
| 838 return arrayOrPath.splice(index, 1); |
| 839 } |
| 840 } else { |
| 841 let arr = Polymer.Path.get(this, arrayOrPath); |
| 842 index = arr.indexOf(item); |
| 843 if (index >= 0) { |
| 844 return this.splice(arrayOrPath, index, 1); |
| 845 } |
| 846 } |
| 847 return null; |
| 848 } |
| 849 |
| 850 // logging |
| 851 |
| 852 /** |
| 853 * Facades `console.log`/`warn`/`error` as override point. |
| 854 * |
| 855 * @param {string} level One of 'log', 'warn', 'error' |
| 856 * @param {Array} args Array of strings or objects to log |
| 857 */ |
| 858 _logger(level, args) { |
| 859 // accept ['foo', 'bar'] and [['foo', 'bar']] |
| 860 if (Array.isArray(args) && args.length === 1) { |
| 861 args = args[0]; |
| 862 } |
| 863 switch(level) { |
| 864 case 'log': |
| 865 case 'warn': |
| 866 case 'error': |
| 867 console[level](...args); |
| 868 } |
| 869 } |
| 870 |
| 871 /** |
| 872 * Facades `console.log` as an override point. |
| 873 * |
| 874 * @param {...*} var_args Array of strings or objects to log |
| 875 */ |
| 876 _log(...args) { |
| 877 this._logger('log', args); |
| 878 } |
| 879 |
| 880 /** |
| 881 * Facades `console.warn` as an override point. |
| 882 * |
| 883 * @param {...*} var_args Array of strings or objects to log |
| 884 */ |
| 885 _warn(...args) { |
| 886 this._logger('warn', args); |
| 887 } |
| 888 |
| 889 /** |
| 890 * Facades `console.error` as an override point. |
| 891 * |
| 892 * @param {...*} var_args Array of strings or objects to log |
| 893 */ |
| 894 _error(...args) { |
| 895 this._logger('error', args) |
| 896 } |
| 897 |
| 898 /** |
| 899 * Formats a message using the element type an a method name. |
| 900 * |
| 901 * @param {string} methodName Method name to associate with message |
| 902 * @param {...*} var_args Array of strings or objects to log |
| 903 * @return {string} String with formatting information for `console` |
| 904 * logging. |
| 905 */ |
| 906 _logf(...args) { |
| 907 return ['[%s::%s]', this.is, ...args]; |
| 908 } |
| 909 |
| 910 } |
| 911 |
| 912 return LegacyElement; |
| 913 |
| 914 }); |
| 915 |
| 916 })(); |
| 917 </script> |
| OLD | NEW |