PageSourceSearch

https://wpt.fyi/node_modules/@polymer/polymer/lib/legacy/polymer.dom.js

js wpt.fyi collected 2026-09-24 08:46:37 UTC 13,768 bytes, 496 lines download raw bytes

1/**
2@license
3Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
4This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7Code distributed by Google as part of the polymer project is also
8subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9*/
10import '../utils/boot.js';
11import { wrap } from '../utils/wrap.js';
12import '../utils/settings.js';
13import { FlattenedNodesObserver } from '../utils/flattened-nodes-observer.js';
14export { flush, enqueueDebouncer as addDebouncer } from '../utils/flush.js';
15/* eslint-disable no-unused-vars */
16import { Debouncer } from '../utils/debounce.js';  // used in type annotations
17/* eslint-enable no-unused-vars */
18
19const p = Element.prototype;
20/**
21 * @const {function(this:Node, string): boolean}
22 */
23const normalizedMatchesSelector = p.matches || p.matchesSelector ||
24  p.mozMatchesSelector || p.msMatchesSelector ||
25  p.oMatchesSelector || p.webkitMatchesSelector;
26
27/**
28 * Cross-platform `element.matches` shim.
29 *
30 * @function matchesSelector
31 * @param {!Node}
31 node Node to check selector against
32 * @param {string} selector Selector to match
33 * @return {boolean} True if node matched selector
34 */
35export const matchesSelector = function(node, selector) {
36  return normalizedMatchesSelector.call(node, selector);
37};
38
39/**
40 * Node API wrapper class returned from `Polymer.dom.(target)` when
41 * `target` is a `Node`.
42 * @implements {PolymerDomApi}
43 * @unrestricted
44 */
45class DomApiNative {
46
47  /**
48   * @param {!Node} node Node for which to create a Polymer.dom helper object.
49   */
50  constructor(node) {
51    if (window['ShadyDOM'] && window['ShadyDOM']['inUse']) {
52      window['ShadyDOM']['patch'](node);
53    }
54    this.node = node;
55  }
56
57  /**
58   * Returns an instance of `FlattenedNodesObserver` that
59   * listens for node changes on this element.
60   *
61   * @param {function(this:HTMLElement, { target: !HTMLElement, addedNodes: !Array<!Element>, removedNodes: !Array<!Element> }):void} callback Called when direct or distributed children
62   *   of this element changes
63   * @return {!PolymerDomApi.ObserveHandle} Observer instance
64   * @override
65   */
66  observeNodes(callback) {
67    return new FlattenedNodesObserver(
68        /** @type {!HTMLElement} */(this.node), callback);
69  }
70
71  /**
72   * Disconnects an observer previously created via `observeNodes`
73   *
74   * @param {!PolymerDomApi.ObserveHandle} observerHandle Observer instance
75   *   to disconnect.
76   * @return {void}
77   * @override
78   */
79  unobserveNodes(observerHandle) {
80    observerHandle.disconnect();
81  }
82
83  /**
84   * Provided as a backwards-compatible API only.  This method does nothing.
85   * @return {void}
86   */
87  notifyObserver() {}
88
89  /**
90   * Returns true if the provided node is contained with this element's
91   * light-DOM children or shadow root, including any nested shadow roots
92   * of children therein.
93   *
94   * @param {Node} node Node to test
95   * @return {boolean} Returns true if the given `node` is contained within
96   *   this element's light or shadow DOM.
97   * @override
98   */
99  deepContains(node) {
100    if (wrap(this.node).contains(node)) {
101      return true;
102    }
103    let n = node;
104    let doc = node.ownerDocument;
105    // walk from node to `this` or `document`
106    while (n && n !== doc && n !== this.node) {
107      // use logical parentnode, or native ShadowRoot host
108      n = wrap(n).parentNode || wrap(n).host;
109    }
110    return n === this.node;
111  }
112
113  /**
114   * Returns the root node of this node.  Equivalent to `getRootNode()`.
115   *
116   * @return {!Node} Top most element in the dom tree in which the node
117   * exists. If the node is connected to a document this is either a
118   * shadowRoot or the document; otherwise, it may be the node
119   * itself or a node or document fragment containing it.
120   * @override
121   */
122  getOwnerRoot() {
123    return wrap(this.node).getRootNode();
124  }
125
126  /**
127   * For slot elements, returns the nodes assigned to the slot; otherwise
128   * an empty array. It is equivalent to `<slot>.addignedNodes({flatten:true})`.
129   *
130   * @return {!Array<!Node>} Array of assigned nodes
131   * @override
132   */
133  getDistributedNodes() {
134    return (this.node.localName === 'slot') ?
135      wrap(this.node).assignedNodes({flatten: true}) :
136      [];
137  }
138
139  /**
140   * Returns an array of all slots this element was distributed to.
141   *
142   * @return {!Array<!HTMLSlotElement>} Description
143   * @override
144   */
145  getDestinationInsertionPoints() {
146    let ip$ = [];
147    let n = wrap(this.node).assignedSlot;
148    while (n) {
149      ip$.push(n);
150      n = wrap(n).assignedSlot;
151    }
152    return ip$;
153  }
154
155  /**
156   * Calls `importNode` on the `ownerDocument` for this node.
157   *
158   * @param {!Node} node Node to import
159   * @param {boolean} deep True if the node should be cloned deeply during
160   *   import
161   * @return {Node} Clone of given node imported to this owner document
162   */
163  importNode(node, deep) {
164    let doc = this.node instanceof Document ? this.node :
165      this.node.ownerDocument;
166    return wrap(doc).importNode(node, deep);
167  }
168
169  /**
170   * @return {!Array<!Node>} Returns a flattened list of all child nodes and
171   * nodes assigned to child slots.
172   * @override
173   */
174  getEffectiveChildNodes() {
175    return FlattenedNodesObserver.getFlattenedNodes(
176        /** @type {!HTMLElement} */ (this.node));
177  }
178
179  /**
180   * Returns a filtered list of flattened child elements for this element based
181   * on the given selector.
182   *
183   * @param {string} selector Selector to filter nodes against
184   * @return {!Array<!HTMLElement>} List of flattened child elements
185   * @override
186   */
187  queryDistributedElements(selector) {
188    let c$ = this.getEffectiveChildNodes();
189    let list = [];
190    for (let i=0, l=c$.length, c; (i<l) && (c=c$[i]); i++) {
191      if ((c.nodeType === Node.ELEMENT_NODE) &&
192          matchesSelector(c, selector)) {
193        list.push(c);
194      }
195    }
196    return list;
197  }
198
199  /**
200   * For shadow roots, returns the currently focused element within this
201   * shadow root.
202   *
203   * return {Node|undefined} Currently focused element
204   * @override
205   */
206  get activeElement() {
207    let node = this.node;
208    return node._activeElement !== undefined ? node._activeElement : node.activeElement;
209  }
210}
211
212function forwardMethods(proto, methods) {
213  for (let i=0; i < methods.length; i++) {
214    let method = methods[i];
215    /* eslint-disable valid-jsdoc */
216    proto[method] = /** @this {DomApiNative} */ function() {
217      return this.node[method].apply(this.node, arguments);
218    };
219    /* eslint-enable */
220  }
221}
222
223function forwardReadOnlyProperties(proto, properties) {
224  for (let i=0; i < properties.length; i++) {
225    let name = properties[i];
226    Object.defineProperty(proto, name, {
227      get: function() {
228        const domApi = /** @type {DomApiNative} */(this);
229        return domApi.node[name];
230      },
231      configurable: true
232    });
233  }
234}
235
236function forwardProperties(proto, properties) {
237  for (let i=0; i < properties.length; i++) {
238    let name = properties[i];
239    Object.defineProperty(proto, name, {
240      /**
241       * @this {DomApiNative}
242       * @return {*} .
243       */
244      get: function() {
245        return this.node[name];
246      },
247      /**
248       * @this {DomApiNative}
249       * @param {*} value .
250       */
251      set: function(value) {
252        this.node[name] = value;
253      },
254      configurable: true
255    });
256  }
257}
258
259
260/**
261 * Event API wrapper class returned from `dom.(target)` when
262 * `target` is an `Event`.
263 */
264export class EventApi {
265  constructor(event) {
266    this.event = event;
267  }
268
269  /**
270   * Returns the first node on the `composedPath` of this event.
271   *
272   * @return {!EventTarget} The node this event was dispatched to
273   */
274  get rootTarget() {
275    return this.path[0];
276  }
277
278  /**
279   * Returns the local (re-targeted) target for this event.
280   *
281   * @return {!EventTarget} The local (re-targeted) target for this event.
282   */
283  get localTarget() {
284    return this.event.target;
285  }
286
287  /**
288   * Returns the `composedPath` for this event.
289   * @return {!Array<!EventTarget>} The nodes this event propagated through
290   */
291  get path() {
292    return this.event.composedPath();
293  }
294}
295
296/**
297 * @function
298 * @param {boolean=} deep
299 * @return {!Node}
300 */
301DomApiNative.prototype.cloneNode;
302/**
303 * @function
304 * @param {!Node} node
305 * @return {!Node}
306 */
307DomApiNative.prototype.appendChild;
308/**
309 * @function
310 * @param {!Node} newChild
311 * @param {Node} refChild
312 * @return {!Node}
313 */
314DomApiNative.prototype.insertBefore;
315/**
316 * @function
317 * @param {!Node} node
318 * @return {!Node}
319 */
320DomApiNative.prototype.removeChild;
321/**
322 * @function
323 * @param {!Node} oldChild
324 * @param {!Node} newChild
325 * @return {!Node}
326 */
327DomApiNative.prototype.replaceChild;
328/**
329 * @function
330 * @param {string} name
331 * @param {string} value
332 * @return {void}
333 */
334DomApiNative.prototype.setAttribute;
335/**
336 * @function
337 * @param {string} name
338 * @return {void}
339 */
340DomApiNative.prototype.removeAttribute;
341/**
342 * @function
343 * @param {string} selector
344 * @return {?Element}
345 */
346DomApiNative.prototype.querySelector;
347/**
348 * @function
349 * @param {string} selector
350 * @return {!NodeList<!Element>}
351 */
352DomApiNative.prototype.querySelectorAll;
353
354/** @type {?Node} */
355DomApiNative.prototype.parentNode;
356/** @type {?Node} */
357DomApiNative.prototype.firstChild;
358/** @type {?Node} */
359DomApiNative.prototype.lastChild;
360/** @type {?Node} */
361DomApiNative.prototype.nextSibling;
362/** @type {?Node} */
363DomApiNative.prototype.previousSibling;
364/** @type {?HTMLElement} */
365DomApiNative.prototype.firstElementChild;
366/** @type {?HTMLElement} */
367DomApiNative.prototype.lastElementChild;
368/** @type {?HTMLElement} */
369DomApiNative.prototype.nextElementSibling;
370/** @type {?HTMLElement} */
371DomApiNative.prototype.previousElementSibling;
372/** @type {!Array<!Node>} */
373DomApiNative.prototype.childNodes;
374/** @type {!Array<!HTMLElement>} */
375DomApiNative.prototype.children;
376/** @type {?DOMTokenList} */
377DomApiNative.prototype.classList;
378
379/** @type {string} */
380DomApiNative.prototype.textContent;
381/** @type {string} */
382DomApiNative.prototype.innerHTML;
383
384let DomApiImpl = DomApiNative;
385
386if (window['ShadyDOM'] && window['ShadyDOM']['inUse'] && window['ShadyDOM']['noPatch'] && window['ShadyDOM']['Wrapper']) {
387
388  /**
389   * @private
390   * @extends {HTMLElement}
391   */
392  class Wrapper extends window['ShadyDOM']['Wrapper'] {}
393
394  // copy bespoke API onto wrapper
395  Object.getOwnPropertyNames(DomApiNative.prototype).forEach((prop) => {
396    if (prop != 'activeElement') {
397      Wrapper.prototype[prop] = DomApiNative.prototype[prop];
398    }
399  });
400
401  // Note, `classList` is here only for legacy compatibility since it does not
402  // trigger distribution in v1 Shadow DOM.
403  forwardReadOnlyProperties(Wrapper.prototype, [
404    'classList'
405  ]);
406
407  DomApiImpl = Wrapper;
408
409  Object.defineProperties(EventApi.prototype, {
410
411    // Returns the "lowest" node in the same root as the event's currentTarget.
412    // When in `noPatch` mode, this must be calculated by walking the event's
413    // path.
414    localTarget: {
415      get() {
416        const current = this.event.currentTarget;
417        const currentRoot = current && dom(current).getOwnerRoot();
418        const p$ = this.path;
419        for (let i = 0; i < p$.length; i++) {
420          const e = p$[i];
421          if (dom(e).getOwnerRoot() === currentRoot) {
422            return e;
423          }
424        }
425      },
426      configurable: true
427    },
428
429    path: {
430      get() {
431        return window['ShadyDOM']['composedPath'](this.event);
432      },
433      configurable: true
434    }
435  });
436
437} else {
438
439  // Methods that can provoke distribution or must return the logical, not
440  // composed tree.
441  forwardMethods(DomApiNative.prototype, [
442    'cloneNode', 'appendChild', 'insertBefore', 'removeChild',
443    'replaceChild', 'setAttribute', 'removeAttribute',
444    'querySelector', 'querySelectorAll', 'attachShadow'
445  ]);
446
447  // Properties that should return the logical, not composed tree. Note, `classList`
448  // is here only for legacy compatibility since it does not trigger distribution
449  // in v1 Shadow DOM.
450  forwardReadOnlyProperties(DomApiNative.prototype, [
451    'parentNode', 'firstChild', 'lastChild',
452    'nextSibling', 'previousSibling', 'firstElementChild',
453    'lastElementChild', 'nextElementSibling', 'previousElementSibling',
454    'childNodes', 'children', 'classList', 'shadowRoot'
455  ]);
456
457  forwardProperties(DomApiNative.prototype, [
458    'textContent', 'innerHTML', 'className'
459  ]);
460}
461
462export const DomApi = DomApiImpl;
463
464/**
465 * Legacy DOM and Event manipulation API wrapper factory used to abstract
466 * differences between native Shadow DOM and "Shady DOM" when polyfilling on
467 * older browsers.
468 *
469 * Note that in Polymer 2.x use of `Polymer.dom` is no longer required and
470 * in the majority of cases simply facades directly to the standard native
471 * API.
472 *
473 * @summary Legacy DOM and Event manipulation API wrapper factory used to
474 * abstract differences between native Shadow DOM and "Shady DOM."
475 * @param {(Node|Event|DomApiNative|EventApi)=} obj Node or event to operate on
476 * @return {!DomApiNative|!EventApi} Wrapper providing either node API or event API
477 */
478export const dom = function(obj) {
479  obj = obj || document;
480  if (obj instanceof DomApiImpl) {
481    return /** @type {!DomApi} */(obj);
482  }
483  if (obj instanceof EventApi) {
484    return /** @type {!EventApi} */(obj);
485  }
486  let helper = obj['__domApi'];
487  if (!helper) {
488    if (obj instanceof Event) {
489      helper = new EventApi(obj);
490    } else {
491      helper = new DomApiImpl(/** @type {Node} */(obj));
492    }
493    obj['__domApi'] = helper;
494  }
495  return helper;
496};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.