PluginProbe
Gutenberg / 12.1.0
Gutenberg v12.1.0
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / build / dom / index.js

index.js in Gutenberg 12.1.0, at build/dom/index.js

2,030 lines 60.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /******/ (function() { // webpackBootstrap
2 /******/ "use strict";
3 /******/ // The require scope
4 /******/ var __webpack_require__ = {};
5 /******/
6 /************************************************************************/
7 /******/ /* webpack/runtime/define property getters */
8 /******/ !function() {
9 /******/ // define getter functions for harmony exports
10 /******/ __webpack_require__.d = function(exports, definition) {
11 /******/ for(var key in definition) {
12 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
13 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
14 /******/ }
15 /******/ }
16 /******/ };
17 /******/ }();
18 /******/
19 /******/ /* webpack/runtime/hasOwnProperty shorthand */
20 /******/ !function() {
21 /******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
22 /******/ }();
23 /******/
24 /******/ /* webpack/runtime/make namespace object */
25 /******/ !function() {
26 /******/ // define __esModule on exports
27 /******/ __webpack_require__.r = function(exports) {
28 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
29 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
30 /******/ }
31 /******/ Object.defineProperty(exports, '__esModule', { value: true });
32 /******/ };
33 /******/ }();
34 /******/
35 /************************************************************************/
36 var __webpack_exports__ = {};
37 // ESM COMPAT FLAG
38 __webpack_require__.r(__webpack_exports__);
39
40 // EXPORTS
41 __webpack_require__.d(__webpack_exports__, {
42 "__unstableStripHTML": function() { return /* reexport */ stripHTML; },
43 "computeCaretRect": function() { return /* reexport */ computeCaretRect; },
44 "documentHasSelection": function() { return /* reexport */ documentHasSelection; },
45 "documentHasTextSelection": function() { return /* reexport */ documentHasTextSelection; },
46 "documentHasUncollapsedSelection": function() { return /* reexport */ documentHasUncollapsedSelection; },
47 "focus": function() { return /* binding */ build_module_focus; },
48 "getFilesFromDataTransfer": function() { return /* reexport */ getFilesFromDataTransfer; },
49 "getOffsetParent": function() { return /* reexport */ getOffsetParent; },
50 "getPhrasingContentSchema": function() { return /* reexport */ getPhrasingContentSchema; },
51 "getRectangleFromRange": function() { return /* reexport */ getRectangleFromRange; },
52 "getScrollContainer": function() { return /* reexport */ getScrollContainer; },
53 "insertAfter": function() { return /* reexport */ insertAfter; },
54 "isEmpty": function() { return /* reexport */ isEmpty; },
55 "isEntirelySelected": function() { return /* reexport */ isEntirelySelected; },
56 "isHorizontalEdge": function() { return /* reexport */ isHorizontalEdge; },
57 "isNumberInput": function() { return /* reexport */ isNumberInput; },
58 "isPhrasingContent": function() { return /* reexport */ isPhrasingContent; },
59 "isRTL": function() { return /* reexport */ isRTL; },
60 "isTextContent": function() { return /* reexport */ isTextContent; },
61 "isTextField": function() { return /* reexport */ isTextField; },
62 "isVerticalEdge": function() { return /* reexport */ isVerticalEdge; },
63 "placeCaretAtHorizontalEdge": function() { return /* reexport */ placeCaretAtHorizontalEdge; },
64 "placeCaretAtVerticalEdge": function() { return /* reexport */ placeCaretAtVerticalEdge; },
65 "remove": function() { return /* reexport */ remove; },
66 "removeInvalidHTML": function() { return /* reexport */ removeInvalidHTML; },
67 "replace": function() { return /* reexport */ replace; },
68 "replaceTag": function() { return /* reexport */ replaceTag; },
69 "safeHTML": function() { return /* reexport */ safeHTML; },
70 "unwrap": function() { return /* reexport */ unwrap; },
71 "wrap": function() { return /* reexport */ wrap; }
72 });
73
74 // NAMESPACE OBJECT: ./packages/dom/build-module/focusable.js
75 var focusable_namespaceObject = {};
76 __webpack_require__.r(focusable_namespaceObject);
77 __webpack_require__.d(focusable_namespaceObject, {
78 "find": function() { return find; }
79 });
80
81 // NAMESPACE OBJECT: ./packages/dom/build-module/tabbable.js
82 var tabbable_namespaceObject = {};
83 __webpack_require__.r(tabbable_namespaceObject);
84 __webpack_require__.d(tabbable_namespaceObject, {
85 "find": function() { return tabbable_find; },
86 "findNext": function() { return findNext; },
87 "findPrevious": function() { return findPrevious; },
88 "isTabbableIndex": function() { return isTabbableIndex; }
89 });
90
91 ;// CONCATENATED MODULE: ./packages/dom/build-module/focusable.js
92 /**
93 * References:
94 *
95 * Focusable:
96 * - https://www.w3.org/TR/html5/editing.html#focus-management
97 *
98 * Sequential focus navigation:
99 * - https://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute
100 *
101 * Disabled elements:
102 * - https://www.w3.org/TR/html5/disabled-elements.html#disabled-elements
103 *
104 * getClientRects algorithm (requiring layout box):
105 * - https://www.w3.org/TR/cssom-view-1/#extension-to-the-element-interface
106 *
107 * AREA elements associated with an IMG:
108 * - https://w3c.github.io/html/editing.html#data-model
109 */
110
111 /**
112 * Returns a CSS selector used to query for focusable elements.
113 *
114 * @param {boolean} sequential If set, only query elements that are sequentially
115 * focusable. Non-interactive elements with a
116 * negative `tabindex` are focusable but not
117 * sequentially focusable.
118 * https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute
119 *
120 * @return {string} CSS selector.
121 */
122 function buildSelector(sequential) {
123 return [sequential ? '[tabindex]:not([tabindex^="-"])' : '[tabindex]', 'a[href]', 'button:not([disabled])', 'input:not([type="hidden"]):not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'iframe:not([tabindex^="-"])', 'object', 'embed', 'area[href]', '[contenteditable]:not([contenteditable=false])'].join(',');
124 }
125 /**
126 * Returns true if the specified element is visible (i.e. neither display: none
127 * nor visibility: hidden).
128 *
129 * @param {HTMLElement} element DOM element to test.
130 *
131 * @return {boolean} Whether element is visible.
132 */
133
134
135 function isVisible(element) {
136 return element.offsetWidth > 0 || element.offsetHeight > 0 || element.getClientRects().length > 0;
137 }
138 /**
139 * Returns true if the specified area element is a valid focusable element, or
140 * false otherwise. Area is only focusable if within a map where a named map
141 * referenced by an image somewhere in the document.
142 *
143 * @param {HTMLAreaElement} element DOM area element to test.
144 *
145 * @return {boolean} Whether area element is valid for focus.
146 */
147
148
149 function isValidFocusableArea(element) {
150 /** @type {HTMLMapElement | null} */
151 const map = element.closest('map[name]');
152
153 if (!map) {
154 return false;
155 }
156 /** @type {HTMLImageElement | null} */
157
158
159 const img = element.ownerDocument.querySelector('img[usemap="#' + map.name + '"]');
160 return !!img && isVisible(img);
161 }
162 /**
163 * Returns all focusable elements within a given context.
164 *
165 * @param {Element} context Element in which to search.
166 * @param {Object} [options]
167 * @param {boolean} [options.sequential] If set, only return elements that are
168 * sequentially focusable.
169 * Non-interactive elements with a
170 * negative `tabindex` are focusable but
171 * not sequentially focusable.
172 * https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute
173 *
174 * @return {Element[]} Focusable elements.
175 */
176
177
178 function find(context) {
179 let {
180 sequential = false
181 } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
182
183 /* eslint-disable jsdoc/no-undefined-types */
184
185 /** @type {NodeListOf<HTMLElement>} */
186
187 /* eslint-enable jsdoc/no-undefined-types */
188 const elements = context.querySelectorAll(buildSelector(sequential));
189 return Array.from(elements).filter(element => {
190 if (!isVisible(element)) {
191 return false;
192 }
193
194 const {
195 nodeName
196 } = element;
197
198 if ('AREA' === nodeName) {
199 return isValidFocusableArea(
200 /** @type {HTMLAreaElement} */
201 element);
202 }
203
204 return true;
205 });
206 }
207 //# sourceMappingURL=focusable.js.map
208 ;// CONCATENATED MODULE: external "lodash"
209 var external_lodash_namespaceObject = window["lodash"];
210 ;// CONCATENATED MODULE: ./packages/dom/build-module/tabbable.js
211 /**
212 * External dependencies
213 */
214
215 /**
216 * Internal dependencies
217 */
218
219
220 /**
221 * Returns the tab index of the given element. In contrast with the tabIndex
222 * property, this normalizes the default (0) to avoid browser inconsistencies,
223 * operating under the assumption that this function is only ever called with a
224 * focusable node.
225 *
226 * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1190261
227 *
228 * @param {Element} element Element from which to retrieve.
229 *
230 * @return {number} Tab index of element (default 0).
231 */
232
233 function getTabIndex(element) {
234 const tabIndex = element.getAttribute('tabindex');
235 return tabIndex === null ? 0 : parseInt(tabIndex, 10);
236 }
237 /**
238 * Returns true if the specified element is tabbable, or false otherwise.
239 *
240 * @param {Element} element Element to test.
241 *
242 * @return {boolean} Whether element is tabbable.
243 */
244
245
246 function isTabbableIndex(element) {
247 return getTabIndex(element) !== -1;
248 }
249 /** @typedef {Element & { type?: string, checked?: boolean, name?: string }} MaybeHTMLInputElement */
250
251 /**
252 * Returns a stateful reducer function which constructs a filtered array of
253 * tabbable elements, where at most one radio input is selected for a given
254 * name, giving priority to checked input, falling back to the first
255 * encountered.
256 *
257 * @return {(acc: MaybeHTMLInputElement[], el: MaybeHTMLInputElement) => MaybeHTMLInputElement[]} Radio group collapse reducer.
258 */
259
260 function createStatefulCollapseRadioGroup() {
261 /** @type {Record<string, MaybeHTMLInputElement>} */
262 const CHOSEN_RADIO_BY_NAME = {};
263 return function collapseRadioGroup(
264 /** @type {MaybeHTMLInputElement[]} */
265 result,
266 /** @type {MaybeHTMLInputElement} */
267 element) {
268 const {
269 nodeName,
270 type,
271 checked,
272 name
273 } = element; // For all non-radio tabbables, construct to array by concatenating.
274
275 if (nodeName !== 'INPUT' || type !== 'radio' || !name) {
276 return result.concat(element);
277 }
278
279 const hasChosen = CHOSEN_RADIO_BY_NAME.hasOwnProperty(name); // Omit by skipping concatenation if the radio element is not chosen.
280
281 const isChosen = checked || !hasChosen;
282
283 if (!isChosen) {
284 return result;
285 } // At this point, if there had been a chosen element, the current
286 // element is checked and should take priority. Retroactively remove
287 // the element which had previously been considered the chosen one.
288
289
290 if (hasChosen) {
291 const hadChosenElement = CHOSEN_RADIO_BY_NAME[name];
292 result = (0,external_lodash_namespaceObject.without)(result, hadChosenElement);
293 }
294
295 CHOSEN_RADIO_BY_NAME[name] = element;
296 return result.concat(element);
297 };
298 }
299 /**
300 * An array map callback, returning an object with the element value and its
301 * array index location as properties. This is used to emulate a proper stable
302 * sort where equal tabIndex should be left in order of their occurrence in the
303 * document.
304 *
305 * @param {Element} element Element.
306 * @param {number} index Array index of element.
307 *
308 * @return {{ element: Element, index: number }} Mapped object with element, index.
309 */
310
311
312 function mapElementToObjectTabbable(element, index) {
313 return {
314 element,
315 index
316 };
317 }
318 /**
319 * An array map callback, returning an element of the given mapped object's
320 * element value.
321 *
322 * @param {{ element: Element }} object Mapped object with element.
323 *
324 * @return {Element} Mapped object element.
325 */
326
327
328 function mapObjectTabbableToElement(object) {
329 return object.element;
330 }
331 /**
332 * A sort comparator function used in comparing two objects of mapped elements.
333 *
334 * @see mapElementToObjectTabbable
335 *
336 * @param {{ element: Element, index: number }} a First object to compare.
337 * @param {{ element: Element, index: number }} b Second object to compare.
338 *
339 * @return {number} Comparator result.
340 */
341
342
343 function compareObjectTabbables(a, b) {
344 const aTabIndex = getTabIndex(a.element);
345 const bTabIndex = getTabIndex(b.element);
346
347 if (aTabIndex === bTabIndex) {
348 return a.index - b.index;
349 }
350
351 return aTabIndex - bTabIndex;
352 }
353 /**
354 * Givin focusable elements, filters out tabbable element.
355 *
356 * @param {Element[]} focusables Focusable elements to filter.
357 *
358 * @return {Element[]} Tabbable elements.
359 */
360
361
362 function filterTabbable(focusables) {
363 return focusables.filter(isTabbableIndex).map(mapElementToObjectTabbable).sort(compareObjectTabbables).map(mapObjectTabbableToElement).reduce(createStatefulCollapseRadioGroup(), []);
364 }
365 /**
366 * @param {Element} context
367 * @return {Element[]} Tabbable elements within the context.
368 */
369
370
371 function tabbable_find(context) {
372 return filterTabbable(find(context));
373 }
374 /**
375 * Given a focusable element, find the preceding tabbable element.
376 *
377 * @param {Element} element The focusable element before which to look. Defaults
378 * to the active element.
379 */
380
381 function findPrevious(element) {
382 const focusables = find(element.ownerDocument.body);
383 const index = focusables.indexOf(element); // Remove all focusables after and including `element`.
384
385 focusables.length = index;
386 return (0,external_lodash_namespaceObject.last)(filterTabbable(focusables));
387 }
388 /**
389 * Given a focusable element, find the next tabbable element.
390 *
391 * @param {Element} element The focusable element after which to look. Defaults
392 * to the active element.
393 */
394
395 function findNext(element) {
396 const focusables = find(element.ownerDocument.body);
397 const index = focusables.indexOf(element); // Remove all focusables before and including `element`.
398
399 const remaining = focusables.slice(index + 1);
400 return (0,external_lodash_namespaceObject.first)(filterTabbable(remaining));
401 }
402 //# sourceMappingURL=tabbable.js.map
403 ;// CONCATENATED MODULE: ./packages/dom/build-module/utils/assert-is-defined.js
404 function assertIsDefined(val, name) {
405 if (false) {}
406 }
407 //# sourceMappingURL=assert-is-defined.js.map
408 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/get-rectangle-from-range.js
409 /**
410 * Internal dependencies
411 */
412
413 /**
414 * Get the rectangle of a given Range.
415 *
416 * @param {Range} range The range.
417 *
418 * @return {DOMRect} The rectangle.
419 */
420
421 function getRectangleFromRange(range) {
422 // For uncollapsed ranges, get the rectangle that bounds the contents of the
423 // range; this a rectangle enclosing the union of the bounding rectangles
424 // for all the elements in the range.
425 if (!range.collapsed) {
426 const rects = Array.from(range.getClientRects()); // If there's just a single rect, return it.
427
428 if (rects.length === 1) {
429 return rects[0];
430 } // Ignore tiny selection at the edge of a range.
431
432
433 const filteredRects = rects.filter(_ref => {
434 let {
435 width
436 } = _ref;
437 return width > 1;
438 }); // If it's full of tiny selections, return browser default.
439
440 if (filteredRects.length === 0) {
441 return range.getBoundingClientRect();
442 }
443
444 if (filteredRects.length === 1) {
445 return filteredRects[0];
446 }
447
448 let {
449 top: furthestTop,
450 bottom: furthestBottom,
451 left: furthestLeft,
452 right: furthestRight
453 } = filteredRects[0];
454
455 for (const {
456 top,
457 bottom,
458 left,
459 right
460 } of filteredRects) {
461 if (top < furthestTop) furthestTop = top;
462 if (bottom > furthestBottom) furthestBottom = bottom;
463 if (left < furthestLeft) furthestLeft = left;
464 if (right > furthestRight) furthestRight = right;
465 }
466
467 return new window.DOMRect(furthestLeft, furthestTop, furthestRight - furthestLeft, furthestBottom - furthestTop);
468 }
469
470 const {
471 startContainer
472 } = range;
473 const {
474 ownerDocument
475 } = startContainer; // Correct invalid "BR" ranges. The cannot contain any children.
476
477 if (startContainer.nodeName === 'BR') {
478 const {
479 parentNode
480 } = startContainer;
481 assertIsDefined(parentNode, 'parentNode');
482 const index =
483 /** @type {Node[]} */
484 Array.from(parentNode.childNodes).indexOf(startContainer);
485 assertIsDefined(ownerDocument, 'ownerDocument');
486 range = ownerDocument.createRange();
487 range.setStart(parentNode, index);
488 range.setEnd(parentNode, index);
489 }
490
491 let rect = range.getClientRects()[0]; // If the collapsed range starts (and therefore ends) at an element node,
492 // `getClientRects` can be empty in some browsers. This can be resolved
493 // by adding a temporary text node with zero-width space to the range.
494 //
495 // See: https://stackoverflow.com/a/6847328/995445
496
497 if (!rect) {
498 assertIsDefined(ownerDocument, 'ownerDocument');
499 const padNode = ownerDocument.createTextNode('\u200b'); // Do not modify the live range.
500
501 range = range.cloneRange();
502 range.insertNode(padNode);
503 rect = range.getClientRects()[0];
504 assertIsDefined(padNode.parentNode, 'padNode.parentNode');
505 padNode.parentNode.removeChild(padNode);
506 }
507
508 return rect;
509 }
510 //# sourceMappingURL=get-rectangle-from-range.js.map
511 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/compute-caret-rect.js
512 /**
513 * Internal dependencies
514 */
515
516
517 /**
518 * Get the rectangle for the selection in a container.
519 *
520 * @param {Window} win The window of the selection.
521 *
522 * @return {DOMRect | null} The rectangle.
523 */
524
525 function computeCaretRect(win) {
526 const selection = win.getSelection();
527 assertIsDefined(selection, 'selection');
528 const range = selection.rangeCount ? selection.getRangeAt(0) : null;
529
530 if (!range) {
531 return null;
532 }
533
534 return getRectangleFromRange(range);
535 }
536 //# sourceMappingURL=compute-caret-rect.js.map
537 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/document-has-text-selection.js
538 /**
539 * Internal dependencies
540 */
541
542 /**
543 * Check whether the current document has selected text. This applies to ranges
544 * of text in the document, and not selection inside `<input>` and `<textarea>`
545 * elements.
546 *
547 * See: https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection#Related_objects.
548 *
549 * @param {Document} doc The document to check.
550 *
551 * @return {boolean} True if there is selection, false if not.
552 */
553
554 function documentHasTextSelection(doc) {
555 assertIsDefined(doc.defaultView, 'doc.defaultView');
556 const selection = doc.defaultView.getSelection();
557 assertIsDefined(selection, 'selection');
558 const range = selection.rangeCount ? selection.getRangeAt(0) : null;
559 return !!range && !range.collapsed;
560 }
561 //# sourceMappingURL=document-has-text-selection.js.map
562 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-html-input-element.js
563 /* eslint-disable jsdoc/valid-types */
564
565 /**
566 * @param {Node} node
567 * @return {node is HTMLInputElement} Whether the node is an HTMLInputElement.
568 */
569 function isHTMLInputElement(node) {
570 /* eslint-enable jsdoc/valid-types */
571 return !!node && node.nodeName === 'INPUT';
572 }
573 //# sourceMappingURL=is-html-input-element.js.map
574 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-text-field.js
575 /**
576 * Internal dependencies
577 */
578
579 /* eslint-disable jsdoc/valid-types */
580
581 /**
582 * Check whether the given element is a text field, where text field is defined
583 * by the ability to select within the input, or that it is contenteditable.
584 *
585 * See: https://html.spec.whatwg.org/#textFieldSelection
586 *
587 * @param {Node} node The HTML element.
588 * @return {node is HTMLElement} True if the element is an text field, false if not.
589 */
590
591 function isTextField(node) {
592 /* eslint-enable jsdoc/valid-types */
593 const nonTextInputs = ['button', 'checkbox', 'hidden', 'file', 'radio', 'image', 'range', 'reset', 'submit', 'number'];
594 return isHTMLInputElement(node) && node.type && !nonTextInputs.includes(node.type) || node.nodeName === 'TEXTAREA' ||
595 /** @type {HTMLElement} */
596 node.contentEditable === 'true';
597 }
598 //# sourceMappingURL=is-text-field.js.map
599 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-number-input.js
600 /**
601 * Internal dependencies
602 */
603
604 /* eslint-disable jsdoc/valid-types */
605
606 /**
607 * Check whether the given element is an input field of type number
608 * and has a valueAsNumber
609 *
610 * @param {Node} node The HTML node.
611 *
612 * @return {node is HTMLInputElement} True if the node is input and holds a number.
613 */
614
615 function isNumberInput(node) {
616 /* eslint-enable jsdoc/valid-types */
617 return isHTMLInputElement(node) && node.type === 'number' && !!node.valueAsNumber;
618 }
619 //# sourceMappingURL=is-number-input.js.map
620 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/input-field-has-uncollapsed-selection.js
621 /**
622 * Internal dependencies
623 */
624
625
626 /**
627 * Check whether the given element, assumed an input field or textarea,
628 * contains a (uncollapsed) selection of text.
629 *
630 * Note: this is perhaps an abuse of the term "selection", since these elements
631 * manage selection differently and aren't covered by Selection#collapsed.
632 *
633 * See: https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection#Related_objects.
634 *
635 * @param {Element} element The HTML element.
636 *
637 * @return {boolean} Whether the input/textareaa element has some "selection".
638 */
639
640 function inputFieldHasUncollapsedSelection(element) {
641 if (!isTextField(element) && !isNumberInput(element)) {
642 return false;
643 }
644
645 try {
646 const {
647 selectionStart,
648 selectionEnd
649 } =
650 /** @type {HTMLInputElement | HTMLTextAreaElement} */
651 element;
652 return selectionStart !== null && selectionStart !== selectionEnd;
653 } catch (error) {
654 // Safari throws an exception when trying to get `selectionStart`
655 // on non-text <input> elements (which, understandably, don't
656 // have the text selection API). We catch this via a try/catch
657 // block, as opposed to a more explicit check of the element's
658 // input types, because of Safari's non-standard behavior. This
659 // also means we don't have to worry about the list of input
660 // types that support `selectionStart` changing as the HTML spec
661 // evolves over time.
662 return false;
663 }
664 }
665 //# sourceMappingURL=input-field-has-uncollapsed-selection.js.map
666 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/document-has-uncollapsed-selection.js
667 /**
668 * Internal dependencies
669 */
670
671
672 /**
673 * Check whether the current document has any sort of selection. This includes
674 * ranges of text across elements and any selection inside `<input>` and
675 * `<textarea>` elements.
676 *
677 * @param {Document} doc The document to check.
678 *
679 * @return {boolean} Whether there is any sort of "selection" in the document.
680 */
681
682 function documentHasUncollapsedSelection(doc) {
683 return documentHasTextSelection(doc) || !!doc.activeElement && inputFieldHasUncollapsedSelection(doc.activeElement);
684 }
685 //# sourceMappingURL=document-has-uncollapsed-selection.js.map
686 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/document-has-selection.js
687 /**
688 * Internal dependencies
689 */
690
691
692
693 /**
694 * Check whether the current document has a selection. This checks for both
695 * focus in an input field and general text selection.
696 *
697 * @param {Document} doc The document to check.
698 *
699 * @return {boolean} True if there is selection, false if not.
700 */
701
702 function documentHasSelection(doc) {
703 return !!doc.activeElement && (isTextField(doc.activeElement) || isNumberInput(doc.activeElement) || documentHasTextSelection(doc));
704 }
705 //# sourceMappingURL=document-has-selection.js.map
706 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/get-computed-style.js
707 /**
708 * Internal dependencies
709 */
710
711 /* eslint-disable jsdoc/valid-types */
712
713 /**
714 * @param {Element} element
715 * @return {ReturnType<Window['getComputedStyle']>} The computed style for the element.
716 */
717
718 function getComputedStyle(element) {
719 /* eslint-enable jsdoc/valid-types */
720 assertIsDefined(element.ownerDocument.defaultView, 'element.ownerDocument.defaultView');
721 return element.ownerDocument.defaultView.getComputedStyle(element);
722 }
723 //# sourceMappingURL=get-computed-style.js.map
724 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/get-scroll-container.js
725 /**
726 * Internal dependencies
727 */
728
729 /**
730 * Given a DOM node, finds the closest scrollable container node.
731 *
732 * @param {Element | null} node Node from which to start.
733 *
734 * @return {Element | undefined} Scrollable container node, if found.
735 */
736
737 function getScrollContainer(node) {
738 if (!node) {
739 return undefined;
740 } // Scrollable if scrollable height exceeds displayed...
741
742
743 if (node.scrollHeight > node.clientHeight) {
744 // ...except when overflow is defined to be hidden or visible
745 const {
746 overflowY
747 } = getComputedStyle(node);
748
749 if (/(auto|scroll)/.test(overflowY)) {
750 return node;
751 }
752 } // Continue traversing
753
754
755 return getScrollContainer(
756 /** @type {Element} */
757 node.parentNode);
758 }
759 //# sourceMappingURL=get-scroll-container.js.map
760 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/get-offset-parent.js
761 /**
762 * Internal dependencies
763 */
764
765 /**
766 * Returns the closest positioned element, or null under any of the conditions
767 * of the offsetParent specification. Unlike offsetParent, this function is not
768 * limited to HTMLElement and accepts any Node (e.g. Node.TEXT_NODE).
769 *
770 * @see https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent
771 *
772 * @param {Node} node Node from which to find offset parent.
773 *
774 * @return {Node | null} Offset parent.
775 */
776
777 function getOffsetParent(node) {
778 // Cannot retrieve computed style or offset parent only anything other than
779 // an element node, so find the closest element node.
780 let closestElement;
781
782 while (closestElement =
783 /** @type {Node} */
784 node.parentNode) {
785 if (closestElement.nodeType === closestElement.ELEMENT_NODE) {
786 break;
787 }
788 }
789
790 if (!closestElement) {
791 return null;
792 } // If the closest element is already positioned, return it, as offsetParent
793 // does not otherwise consider the node itself.
794
795
796 if (getComputedStyle(
797 /** @type {Element} */
798 closestElement).position !== 'static') {
799 return closestElement;
800 } // offsetParent is undocumented/draft
801
802
803 return (
804 /** @type {Node & { offsetParent: Node }} */
805 closestElement.offsetParent
806 );
807 }
808 //# sourceMappingURL=get-offset-parent.js.map
809 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-input-or-text-area.js
810 /* eslint-disable jsdoc/valid-types */
811
812 /**
813 * @param {Element} element
814 * @return {element is HTMLInputElement | HTMLTextAreaElement} Whether the element is an input or textarea
815 */
816 function isInputOrTextArea(element) {
817 /* eslint-enable jsdoc/valid-types */
818 return element.tagName === 'INPUT' || element.tagName === 'TEXTAREA';
819 }
820 //# sourceMappingURL=is-input-or-text-area.js.map
821 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-entirely-selected.js
822 /**
823 * Internal dependencies
824 */
825
826
827 /**
828 * Check whether the contents of the element have been entirely selected.
829 * Returns true if there is no possibility of selection.
830 *
831 * @param {HTMLElement} element The element to check.
832 *
833 * @return {boolean} True if entirely selected, false if not.
834 */
835
836 function isEntirelySelected(element) {
837 if (isInputOrTextArea(element)) {
838 return element.selectionStart === 0 && element.value.length === element.selectionEnd;
839 }
840
841 if (!element.isContentEditable) {
842 return true;
843 }
844
845 const {
846 ownerDocument
847 } = element;
848 const {
849 defaultView
850 } = ownerDocument;
851 assertIsDefined(defaultView, 'defaultView');
852 const selection = defaultView.getSelection();
853 assertIsDefined(selection, 'selection');
854 const range = selection.rangeCount ? selection.getRangeAt(0) : null;
855
856 if (!range) {
857 return true;
858 }
859
860 const {
861 startContainer,
862 endContainer,
863 startOffset,
864 endOffset
865 } = range;
866
867 if (startContainer === element && endContainer === element && startOffset === 0 && endOffset === element.childNodes.length) {
868 return true;
869 }
870
871 const lastChild = element.lastChild;
872 assertIsDefined(lastChild, 'lastChild');
873 const endContainerContentLength = endContainer.nodeType === endContainer.TEXT_NODE ?
874 /** @type {Text} */
875 endContainer.data.length : endContainer.childNodes.length;
876 return isDeepChild(startContainer, element, 'firstChild') && isDeepChild(endContainer, element, 'lastChild') && startOffset === 0 && endOffset === endContainerContentLength;
877 }
878 /**
879 * Check whether the contents of the element have been entirely selected.
880 * Returns true if there is no possibility of selection.
881 *
882 * @param {HTMLElement|Node} query The element to check.
883 * @param {HTMLElement} container The container that we suspect "query" may be a first or last child of.
884 * @param {"firstChild"|"lastChild"} propName "firstChild" or "lastChild"
885 *
886 * @return {boolean} True if query is a deep first/last child of container, false otherwise.
887 */
888
889 function isDeepChild(query, container, propName) {
890 /** @type {HTMLElement | ChildNode | null} */
891 let candidate = container;
892
893 do {
894 if (query === candidate) {
895 return true;
896 }
897
898 candidate = candidate[propName];
899 } while (candidate);
900
901 return false;
902 }
903 //# sourceMappingURL=is-entirely-selected.js.map
904 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-rtl.js
905 /**
906 * Internal dependencies
907 */
908
909 /**
910 * Whether the element's text direction is right-to-left.
911 *
912 * @param {Element} element The element to check.
913 *
914 * @return {boolean} True if rtl, false if ltr.
915 */
916
917 function isRTL(element) {
918 return getComputedStyle(element).direction === 'rtl';
919 }
920 //# sourceMappingURL=is-rtl.js.map
921 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/get-range-height.js
922 /**
923 * Gets the height of the range without ignoring zero width rectangles, which
924 * some browsers ignore when creating a union.
925 *
926 * @param {Range} range The range to check.
927 * @return {number | undefined} Height of the range or undefined if the range has no client rectangles.
928 */
929 function getRangeHeight(range) {
930 const rects = Array.from(range.getClientRects());
931
932 if (!rects.length) {
933 return;
934 }
935
936 const highestTop = Math.min(...rects.map(_ref => {
937 let {
938 top
939 } = _ref;
940 return top;
941 }));
942 const lowestBottom = Math.max(...rects.map(_ref2 => {
943 let {
944 bottom
945 } = _ref2;
946 return bottom;
947 }));
948 return lowestBottom - highestTop;
949 }
950 //# sourceMappingURL=get-range-height.js.map
951 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-selection-forward.js
952 /**
953 * Internal dependencies
954 */
955
956 /**
957 * Returns true if the given selection object is in the forward direction, or
958 * false otherwise.
959 *
960 * @see https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
961 *
962 * @param {Selection} selection Selection object to check.
963 *
964 * @return {boolean} Whether the selection is forward.
965 */
966
967 function isSelectionForward(selection) {
968 const {
969 anchorNode,
970 focusNode,
971 anchorOffset,
972 focusOffset
973 } = selection;
974 assertIsDefined(anchorNode, 'anchorNode');
975 assertIsDefined(focusNode, 'focusNode');
976 const position = anchorNode.compareDocumentPosition(focusNode); // Disable reason: `Node#compareDocumentPosition` returns a bitmask value,
977 // so bitwise operators are intended.
978
979 /* eslint-disable no-bitwise */
980 // Compare whether anchor node precedes focus node. If focus node (where
981 // end of selection occurs) is after the anchor node, it is forward.
982
983 if (position & anchorNode.DOCUMENT_POSITION_PRECEDING) {
984 return false;
985 }
986
987 if (position & anchorNode.DOCUMENT_POSITION_FOLLOWING) {
988 return true;
989 }
990 /* eslint-enable no-bitwise */
991 // `compareDocumentPosition` returns 0 when passed the same node, in which
992 // case compare offsets.
993
994
995 if (position === 0) {
996 return anchorOffset <= focusOffset;
997 } // This should never be reached, but return true as default case.
998
999
1000 return true;
1001 }
1002 //# sourceMappingURL=is-selection-forward.js.map
1003 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/caret-range-from-point.js
1004 /**
1005 * Polyfill.
1006 * Get a collapsed range for a given point.
1007 *
1008 * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/caretRangeFromPoint
1009 *
1010 * @param {DocumentMaybeWithCaretPositionFromPoint} doc The document of the range.
1011 * @param {number} x Horizontal position within the current viewport.
1012 * @param {number} y Vertical position within the current viewport.
1013 *
1014 * @return {Range | null} The best range for the given point.
1015 */
1016 function caretRangeFromPoint(doc, x, y) {
1017 if (doc.caretRangeFromPoint) {
1018 return doc.caretRangeFromPoint(x, y);
1019 }
1020
1021 if (!doc.caretPositionFromPoint) {
1022 return null;
1023 }
1024
1025 const point = doc.caretPositionFromPoint(x, y); // If x or y are negative, outside viewport, or there is no text entry node.
1026 // https://developer.mozilla.org/en-US/docs/Web/API/Document/caretRangeFromPoint
1027
1028 if (!point) {
1029 return null;
1030 }
1031
1032 const range = doc.createRange();
1033 range.setStart(point.offsetNode, point.offset);
1034 range.collapse(true);
1035 return range;
1036 }
1037 /**
1038 * @typedef {{caretPositionFromPoint?: (x: number, y: number)=> CaretPosition | null} & Document } DocumentMaybeWithCaretPositionFromPoint
1039 * @typedef {{ readonly offset: number; readonly offsetNode: Node; getClientRect(): DOMRect | null; }} CaretPosition
1040 */
1041 //# sourceMappingURL=caret-range-from-point.js.map
1042 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/hidden-caret-range-from-point.js
1043 /**
1044 * Internal dependencies
1045 */
1046
1047
1048 /**
1049 * Get a collapsed range for a given point.
1050 * Gives the container a temporary high z-index (above any UI).
1051 * This is preferred over getting the UI nodes and set styles there.
1052 *
1053 * @param {Document} doc The document of the range.
1054 * @param {number} x Horizontal position within the current viewport.
1055 * @param {number} y Vertical position within the current viewport.
1056 * @param {HTMLElement} container Container in which the range is expected to be found.
1057 *
1058 * @return {?Range} The best range for the given point.
1059 */
1060
1061 function hiddenCaretRangeFromPoint(doc, x, y, container) {
1062 const originalZIndex = container.style.zIndex;
1063 const originalPosition = container.style.position;
1064 const {
1065 position = 'static'
1066 } = getComputedStyle(container); // A z-index only works if the element position is not static.
1067
1068 if (position === 'static') {
1069 container.style.position = 'relative';
1070 }
1071
1072 container.style.zIndex = '10000';
1073 const range = caretRangeFromPoint(doc, x, y);
1074 container.style.zIndex = originalZIndex;
1075 container.style.position = originalPosition;
1076 return range;
1077 }
1078 //# sourceMappingURL=hidden-caret-range-from-point.js.map
1079 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-edge.js
1080 /**
1081 * Internal dependencies
1082 */
1083
1084
1085
1086
1087
1088
1089
1090 /**
1091 * Check whether the selection is at the edge of the container. Checks for
1092 * horizontal position by default. Set `onlyVertical` to true to check only
1093 * vertically.
1094 *
1095 * @param {Element} container Focusable element.
1096 * @param {boolean} isReverse Set to true to check left, false to check right.
1097 * @param {boolean} [onlyVertical=false] Set to true to check only vertical position.
1098 *
1099 * @return {boolean} True if at the edge, false if not.
1100 */
1101
1102 function isEdge(container, isReverse) {
1103 let onlyVertical = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
1104
1105 if (isInputOrTextArea(container) && typeof container.selectionStart === 'number') {
1106 if (container.selectionStart !== container.selectionEnd) {
1107 return false;
1108 }
1109
1110 if (isReverse) {
1111 return container.selectionStart === 0;
1112 }
1113
1114 return container.value.length === container.selectionStart;
1115 }
1116
1117 if (!
1118 /** @type {HTMLElement} */
1119 container.isContentEditable) {
1120 return true;
1121 }
1122
1123 const {
1124 ownerDocument
1125 } = container;
1126 const {
1127 defaultView
1128 } = ownerDocument;
1129 assertIsDefined(defaultView, 'defaultView');
1130 const selection = defaultView.getSelection();
1131
1132 if (!selection || !selection.rangeCount) {
1133 return false;
1134 }
1135
1136 const range = selection.getRangeAt(0);
1137 const collapsedRange = range.cloneRange();
1138 const isForward = isSelectionForward(selection);
1139 const isCollapsed = selection.isCollapsed; // Collapse in direction of selection.
1140
1141 if (!isCollapsed) {
1142 collapsedRange.collapse(!isForward);
1143 }
1144
1145 const collapsedRangeRect = getRectangleFromRange(collapsedRange);
1146 const rangeRect = getRectangleFromRange(range);
1147
1148 if (!collapsedRangeRect || !rangeRect) {
1149 return false;
1150 } // Only consider the multiline selection at the edge if the direction is
1151 // towards the edge. The selection is multiline if it is taller than the
1152 // collapsed selection.
1153
1154
1155 const rangeHeight = getRangeHeight(range);
1156
1157 if (!isCollapsed && rangeHeight && rangeHeight > collapsedRangeRect.height && isForward === isReverse) {
1158 return false;
1159 } // In the case of RTL scripts, the horizontal edge is at the opposite side.
1160
1161
1162 const isReverseDir = isRTL(container) ? !isReverse : isReverse;
1163 const containerRect = container.getBoundingClientRect(); // To check if a selection is at the edge, we insert a test selection at the
1164 // edge of the container and check if the selections have the same vertical
1165 // or horizontal position. If they do, the selection is at the edge.
1166 // This method proves to be better than a DOM-based calculation for the
1167 // horizontal edge, since it ignores empty textnodes and a trailing line
1168 // break element. In other words, we need to check visual positioning, not
1169 // DOM positioning.
1170 // It also proves better than using the computed style for the vertical
1171 // edge, because we cannot know the padding and line height reliably in
1172 // pixels. `getComputedStyle` may return a value with different units.
1173
1174 const x = isReverseDir ? containerRect.left + 1 : containerRect.right - 1;
1175 const y = isReverse ? containerRect.top + 1 : containerRect.bottom - 1;
1176 const testRange = hiddenCaretRangeFromPoint(ownerDocument, x, y,
1177 /** @type {HTMLElement} */
1178 container);
1179
1180 if (!testRange) {
1181 return false;
1182 }
1183
1184 const testRect = getRectangleFromRange(testRange);
1185
1186 if (!testRect) {
1187 return false;
1188 }
1189
1190 const verticalSide = isReverse ? 'top' : 'bottom';
1191 const horizontalSide = isReverseDir ? 'left' : 'right';
1192 const verticalDiff = testRect[verticalSide] - rangeRect[verticalSide];
1193 const horizontalDiff = testRect[horizontalSide] - collapsedRangeRect[horizontalSide]; // Allow the position to be 1px off.
1194
1195 const hasVerticalDiff = Math.abs(verticalDiff) <= 1;
1196 const hasHorizontalDiff = Math.abs(horizontalDiff) <= 1;
1197 return onlyVertical ? hasVerticalDiff : hasVerticalDiff && hasHorizontalDiff;
1198 }
1199 //# sourceMappingURL=is-edge.js.map
1200 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-horizontal-edge.js
1201 /**
1202 * Internal dependencies
1203 */
1204
1205 /**
1206 * Check whether the selection is horizontally at the edge of the container.
1207 *
1208 * @param {Element} container Focusable element.
1209 * @param {boolean} isReverse Set to true to check left, false for right.
1210 *
1211 * @return {boolean} True if at the horizontal edge, false if not.
1212 */
1213
1214 function isHorizontalEdge(container, isReverse) {
1215 return isEdge(container, isReverse);
1216 }
1217 //# sourceMappingURL=is-horizontal-edge.js.map
1218 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-vertical-edge.js
1219 /**
1220 * Internal dependencies
1221 */
1222
1223 /**
1224 * Check whether the selection is vertically at the edge of the container.
1225 *
1226 * @param {Element} container Focusable element.
1227 * @param {boolean} isReverse Set to true to check top, false for bottom.
1228 *
1229 * @return {boolean} True if at the vertical edge, false if not.
1230 */
1231
1232 function isVerticalEdge(container, isReverse) {
1233 return isEdge(container, isReverse, true);
1234 }
1235 //# sourceMappingURL=is-vertical-edge.js.map
1236 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/place-caret-at-edge.js
1237 /**
1238 * Internal dependencies
1239 */
1240
1241
1242
1243
1244 /**
1245 * Gets the range to place.
1246 *
1247 * @param {HTMLElement} container Focusable element.
1248 * @param {boolean} isReverse True for end, false for start.
1249 * @param {number|undefined} x X coordinate to vertically position.
1250 *
1251 * @return {Range|null} The range to place.
1252 */
1253
1254 function getRange(container, isReverse, x) {
1255 const {
1256 ownerDocument
1257 } = container; // In the case of RTL scripts, the horizontal edge is at the opposite side.
1258
1259 const isReverseDir = isRTL(container) ? !isReverse : isReverse;
1260 const containerRect = container.getBoundingClientRect(); // When placing at the end (isReverse), find the closest range to the bottom
1261 // right corner. When placing at the start, to the top left corner.
1262
1263 if (x === undefined) {
1264 x = isReverse ? containerRect.right - 1 : containerRect.left + 1;
1265 }
1266
1267 const y = isReverseDir ? containerRect.bottom - 1 : containerRect.top + 1;
1268 return hiddenCaretRangeFromPoint(ownerDocument, x, y, container);
1269 }
1270 /**
1271 * Places the caret at start or end of a given element.
1272 *
1273 * @param {HTMLElement} container Focusable element.
1274 * @param {boolean} isReverse True for end, false for start.
1275 * @param {number|undefined} x X coordinate to vertically position.
1276 */
1277
1278
1279 function placeCaretAtEdge(container, isReverse, x) {
1280 if (!container) {
1281 return;
1282 }
1283
1284 container.focus();
1285
1286 if (isInputOrTextArea(container)) {
1287 // The element may not support selection setting.
1288 if (typeof container.selectionStart !== 'number') {
1289 return;
1290 }
1291
1292 if (isReverse) {
1293 container.selectionStart = container.value.length;
1294 container.selectionEnd = container.value.length;
1295 } else {
1296 container.selectionStart = 0;
1297 container.selectionEnd = 0;
1298 }
1299
1300 return;
1301 }
1302
1303 if (!container.isContentEditable) {
1304 return;
1305 }
1306
1307 let range = getRange(container, isReverse, x); // If no range range can be created or it is outside the container, the
1308 // element may be out of view.
1309
1310 if (!range || !range.startContainer || !container.contains(range.startContainer)) {
1311 container.scrollIntoView(isReverse);
1312 range = range = getRange(container, isReverse, x);
1313
1314 if (!range || !range.startContainer || !container.contains(range.startContainer)) {
1315 return;
1316 }
1317 }
1318
1319 const {
1320 ownerDocument
1321 } = container;
1322 const {
1323 defaultView
1324 } = ownerDocument;
1325 assertIsDefined(defaultView, 'defaultView');
1326 const selection = defaultView.getSelection();
1327 assertIsDefined(selection, 'selection');
1328 selection.removeAllRanges();
1329 selection.addRange(range);
1330 }
1331 //# sourceMappingURL=place-caret-at-edge.js.map
1332 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/place-caret-at-horizontal-edge.js
1333 /**
1334 * Internal dependencies
1335 */
1336
1337 /**
1338 * Places the caret at start or end of a given element.
1339 *
1340 * @param {HTMLElement} container Focusable element.
1341 * @param {boolean} isReverse True for end, false for start.
1342 */
1343
1344 function placeCaretAtHorizontalEdge(container, isReverse) {
1345 return placeCaretAtEdge(container, isReverse, undefined);
1346 }
1347 //# sourceMappingURL=place-caret-at-horizontal-edge.js.map
1348 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/place-caret-at-vertical-edge.js
1349 /**
1350 * Internal dependencies
1351 */
1352
1353 /**
1354 * Places the caret at the top or bottom of a given element.
1355 *
1356 * @param {HTMLElement} container Focusable element.
1357 * @param {boolean} isReverse True for bottom, false for top.
1358 * @param {DOMRect} [rect] The rectangle to position the caret with.
1359 */
1360
1361 function placeCaretAtVerticalEdge(container, isReverse, rect) {
1362 return placeCaretAtEdge(container, isReverse, rect === null || rect === void 0 ? void 0 : rect.left);
1363 }
1364 //# sourceMappingURL=place-caret-at-vertical-edge.js.map
1365 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/insert-after.js
1366 /**
1367 * Internal dependencies
1368 */
1369
1370 /**
1371 * Given two DOM nodes, inserts the former in the DOM as the next sibling of
1372 * the latter.
1373 *
1374 * @param {Node} newNode Node to be inserted.
1375 * @param {Node} referenceNode Node after which to perform the insertion.
1376 * @return {void}
1377 */
1378
1379 function insertAfter(newNode, referenceNode) {
1380 assertIsDefined(referenceNode.parentNode, 'referenceNode.parentNode');
1381 referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
1382 }
1383 //# sourceMappingURL=insert-after.js.map
1384 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/remove.js
1385 /**
1386 * Internal dependencies
1387 */
1388
1389 /**
1390 * Given a DOM node, removes it from the DOM.
1391 *
1392 * @param {Node} node Node to be removed.
1393 * @return {void}
1394 */
1395
1396 function remove(node) {
1397 assertIsDefined(node.parentNode, 'node.parentNode');
1398 node.parentNode.removeChild(node);
1399 }
1400 //# sourceMappingURL=remove.js.map
1401 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/replace.js
1402 /**
1403 * Internal dependencies
1404 */
1405
1406
1407
1408 /**
1409 * Given two DOM nodes, replaces the former with the latter in the DOM.
1410 *
1411 * @param {Element} processedNode Node to be removed.
1412 * @param {Element} newNode Node to be inserted in its place.
1413 * @return {void}
1414 */
1415
1416 function replace(processedNode, newNode) {
1417 assertIsDefined(processedNode.parentNode, 'processedNode.parentNode');
1418 insertAfter(newNode, processedNode.parentNode);
1419 remove(processedNode);
1420 }
1421 //# sourceMappingURL=replace.js.map
1422 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/unwrap.js
1423 /**
1424 * Internal dependencies
1425 */
1426
1427 /**
1428 * Unwrap the given node. This means any child nodes are moved to the parent.
1429 *
1430 * @param {Node} node The node to unwrap.
1431 *
1432 * @return {void}
1433 */
1434
1435 function unwrap(node) {
1436 const parent = node.parentNode;
1437 assertIsDefined(parent, 'node.parentNode');
1438
1439 while (node.firstChild) {
1440 parent.insertBefore(node.firstChild, node);
1441 }
1442
1443 parent.removeChild(node);
1444 }
1445 //# sourceMappingURL=unwrap.js.map
1446 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/replace-tag.js
1447 /**
1448 * Internal dependencies
1449 */
1450
1451 /**
1452 * Replaces the given node with a new node with the given tag name.
1453 *
1454 * @param {Element} node The node to replace
1455 * @param {string} tagName The new tag name.
1456 *
1457 * @return {Element} The new node.
1458 */
1459
1460 function replaceTag(node, tagName) {
1461 const newNode = node.ownerDocument.createElement(tagName);
1462
1463 while (node.firstChild) {
1464 newNode.appendChild(node.firstChild);
1465 }
1466
1467 assertIsDefined(node.parentNode, 'node.parentNode');
1468 node.parentNode.replaceChild(newNode, node);
1469 return newNode;
1470 }
1471 //# sourceMappingURL=replace-tag.js.map
1472 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/wrap.js
1473 /**
1474 * Internal dependencies
1475 */
1476
1477 /**
1478 * Wraps the given node with a new node with the given tag name.
1479 *
1480 * @param {Element} newNode The node to insert.
1481 * @param {Element} referenceNode The node to wrap.
1482 */
1483
1484 function wrap(newNode, referenceNode) {
1485 assertIsDefined(referenceNode.parentNode, 'referenceNode.parentNode');
1486 referenceNode.parentNode.insertBefore(newNode, referenceNode);
1487 newNode.appendChild(referenceNode);
1488 }
1489 //# sourceMappingURL=wrap.js.map
1490 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/safe-html.js
1491 /**
1492 * Internal dependencies
1493 */
1494
1495 /**
1496 * Strips scripts and on* attributes from HTML.
1497 *
1498 * @param {string} html HTML to sanitize.
1499 *
1500 * @return {string} The sanitized HTML.
1501 */
1502
1503 function safeHTML(html) {
1504 const {
1505 body
1506 } = document.implementation.createHTMLDocument('');
1507 body.innerHTML = html;
1508 const elements = body.getElementsByTagName('*');
1509 let elementIndex = elements.length;
1510
1511 while (elementIndex--) {
1512 const element = elements[elementIndex];
1513
1514 if (element.tagName === 'SCRIPT') {
1515 remove(element);
1516 } else {
1517 let attributeIndex = element.attributes.length;
1518
1519 while (attributeIndex--) {
1520 const {
1521 name: key
1522 } = element.attributes[attributeIndex];
1523
1524 if (key.startsWith('on')) {
1525 element.removeAttribute(key);
1526 }
1527 }
1528 }
1529 }
1530
1531 return body.innerHTML;
1532 }
1533 //# sourceMappingURL=safe-html.js.map
1534 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/strip-html.js
1535 /**
1536 * Internal dependencies
1537 */
1538
1539 /**
1540 * Removes any HTML tags from the provided string.
1541 *
1542 * @param {string} html The string containing html.
1543 *
1544 * @return {string} The text content with any html removed.
1545 */
1546
1547 function stripHTML(html) {
1548 // Remove any script tags or on* attributes otherwise their *contents* will be left
1549 // in place following removal of HTML tags.
1550 html = safeHTML(html);
1551 const doc = document.implementation.createHTMLDocument('');
1552 doc.body.innerHTML = html;
1553 return doc.body.textContent || '';
1554 }
1555 //# sourceMappingURL=strip-html.js.map
1556 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-empty.js
1557 /**
1558 * Recursively checks if an element is empty. An element is not empty if it
1559 * contains text or contains elements with attributes such as images.
1560 *
1561 * @param {Element} element The element to check.
1562 *
1563 * @return {boolean} Whether or not the element is empty.
1564 */
1565 function isEmpty(element) {
1566 switch (element.nodeType) {
1567 case element.TEXT_NODE:
1568 // We cannot use \s since it includes special spaces which we want
1569 // to preserve.
1570 return /^[ \f\n\r\t\v\u00a0]*$/.test(element.nodeValue || '');
1571
1572 case element.ELEMENT_NODE:
1573 if (element.hasAttributes()) {
1574 return false;
1575 } else if (!element.hasChildNodes()) {
1576 return true;
1577 }
1578
1579 return (
1580 /** @type {Element[]} */
1581 Array.from(element.childNodes).every(isEmpty)
1582 );
1583
1584 default:
1585 return true;
1586 }
1587 }
1588 //# sourceMappingURL=is-empty.js.map
1589 ;// CONCATENATED MODULE: ./packages/dom/build-module/phrasing-content.js
1590 /**
1591 * External dependencies
1592 */
1593
1594 /**
1595 * All phrasing content elements.
1596 *
1597 * @see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content-0
1598 */
1599
1600 /**
1601 * @typedef {Record<string,SemanticElementDefinition>} ContentSchema
1602 */
1603
1604 /**
1605 * @typedef SemanticElementDefinition
1606 * @property {string[]} [attributes] Content attributes
1607 * @property {ContentSchema} [children] Content attributes
1608 */
1609
1610 /**
1611 * All text-level semantic elements.
1612 *
1613 * @see https://html.spec.whatwg.org/multipage/text-level-semantics.html
1614 *
1615 * @type {ContentSchema}
1616 */
1617
1618 const textContentSchema = {
1619 strong: {},
1620 em: {},
1621 s: {},
1622 del: {},
1623 ins: {},
1624 a: {
1625 attributes: ['href', 'target', 'rel', 'id']
1626 },
1627 code: {},
1628 abbr: {
1629 attributes: ['title']
1630 },
1631 sub: {},
1632 sup: {},
1633 br: {},
1634 small: {},
1635 // To do: fix blockquote.
1636 // cite: {},
1637 q: {
1638 attributes: ['cite']
1639 },
1640 dfn: {
1641 attributes: ['title']
1642 },
1643 data: {
1644 attributes: ['value']
1645 },
1646 time: {
1647 attributes: ['datetime']
1648 },
1649 var: {},
1650 samp: {},
1651 kbd: {},
1652 i: {},
1653 b: {},
1654 u: {},
1655 mark: {},
1656 ruby: {},
1657 rt: {},
1658 rp: {},
1659 bdi: {
1660 attributes: ['dir']
1661 },
1662 bdo: {
1663 attributes: ['dir']
1664 },
1665 wbr: {},
1666 '#text': {}
1667 }; // Recursion is needed.
1668 // Possible: strong > em > strong.
1669 // Impossible: strong > strong.
1670
1671 (0,external_lodash_namespaceObject.without)(Object.keys(textContentSchema), '#text', 'br').forEach(tag => {
1672 textContentSchema[tag].children = (0,external_lodash_namespaceObject.omit)(textContentSchema, tag);
1673 });
1674 /**
1675 * Embedded content elements.
1676 *
1677 * @see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#embedded-content-0
1678 *
1679 * @type {ContentSchema}
1680 */
1681
1682 const embeddedContentSchema = {
1683 audio: {
1684 attributes: ['src', 'preload', 'autoplay', 'mediagroup', 'loop', 'muted']
1685 },
1686 canvas: {
1687 attributes: ['width', 'height']
1688 },
1689 embed: {
1690 attributes: ['src', 'type', 'width', 'height']
1691 },
1692 img: {
1693 attributes: ['alt', 'src', 'srcset', 'usemap', 'ismap', 'width', 'height']
1694 },
1695 object: {
1696 attributes: ['data', 'type', 'name', 'usemap', 'form', 'width', 'height']
1697 },
1698 video: {
1699 attributes: ['src', 'poster', 'preload', 'autoplay', 'mediagroup', 'loop', 'muted', 'controls', 'width', 'height']
1700 }
1701 };
1702 /**
1703 * Phrasing content elements.
1704 *
1705 * @see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content-0
1706 */
1707
1708 const phrasingContentSchema = { ...textContentSchema,
1709 ...embeddedContentSchema
1710 };
1711 /**
1712 * Get schema of possible paths for phrasing content.
1713 *
1714 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content
1715 *
1716 * @param {string} [context] Set to "paste" to exclude invisible elements and
1717 * sensitive data.
1718 *
1719 * @return {Partial<ContentSchema>} Schema.
1720 */
1721
1722 function getPhrasingContentSchema(context) {
1723 if (context !== 'paste') {
1724 return phrasingContentSchema;
1725 }
1726
1727 return (0,external_lodash_namespaceObject.omit)({ ...phrasingContentSchema,
1728 // We shouldn't paste potentially sensitive information which is not
1729 // visible to the user when pasted, so strip the attributes.
1730 ins: {
1731 children: phrasingContentSchema.ins.children
1732 },
1733 del: {
1734 children: phrasingContentSchema.del.children
1735 }
1736 }, ['u', // Used to mark misspelling. Shouldn't be pasted.
1737 'abbr', // Invisible.
1738 'data', // Invisible.
1739 'time', // Invisible.
1740 'wbr', // Invisible.
1741 'bdi', // Invisible.
1742 'bdo' // Invisible.
1743 ]);
1744 }
1745 /**
1746 * Find out whether or not the given node is phrasing content.
1747 *
1748 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content
1749 *
1750 * @param {Node} node The node to test.
1751 *
1752 * @return {boolean} True if phrasing content, false if not.
1753 */
1754
1755 function isPhrasingContent(node) {
1756 const tag = node.nodeName.toLowerCase();
1757 return getPhrasingContentSchema().hasOwnProperty(tag) || tag === 'span';
1758 }
1759 /**
1760 * @param {Node} node
1761 * @return {boolean} Node is text content
1762 */
1763
1764 function isTextContent(node) {
1765 const tag = node.nodeName.toLowerCase();
1766 return textContentSchema.hasOwnProperty(tag) || tag === 'span';
1767 }
1768 //# sourceMappingURL=phrasing-content.js.map
1769 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/is-element.js
1770 /* eslint-disable jsdoc/valid-types */
1771
1772 /**
1773 * @param {Node | null | undefined} node
1774 * @return {node is Element} True if node is an Element node
1775 */
1776 function isElement(node) {
1777 /* eslint-enable jsdoc/valid-types */
1778 return !!node && node.nodeType === node.ELEMENT_NODE;
1779 }
1780 //# sourceMappingURL=is-element.js.map
1781 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/clean-node-list.js
1782 /**
1783 * External dependencies
1784 */
1785
1786 /**
1787 * Internal dependencies
1788 */
1789
1790
1791
1792
1793
1794
1795
1796 /* eslint-disable jsdoc/valid-types */
1797
1798 /**
1799 * @typedef SchemaItem
1800 * @property {string[]} [attributes] Attributes.
1801 * @property {(string | RegExp)[]} [classes] Classnames or RegExp to test against.
1802 * @property {'*' | { [tag: string]: SchemaItem }} [children] Child schemas.
1803 * @property {string[]} [require] Selectors to test required children against. Leave empty or undefined if there are no requirements.
1804 * @property {boolean} allowEmpty Whether to allow nodes without children.
1805 * @property {(node: Node) => boolean} [isMatch] Function to test whether a node is a match. If left undefined any node will be assumed to match.
1806 */
1807
1808 /** @typedef {{ [tag: string]: SchemaItem }} Schema */
1809
1810 /* eslint-enable jsdoc/valid-types */
1811
1812 /**
1813 * Given a schema, unwraps or removes nodes, attributes and classes on a node
1814 * list.
1815 *
1816 * @param {NodeList} nodeList The nodeList to filter.
1817 * @param {Document} doc The document of the nodeList.
1818 * @param {Schema} schema An array of functions that can mutate with the provided node.
1819 * @param {boolean} inline Whether to clean for inline mode.
1820 */
1821
1822 function cleanNodeList(nodeList, doc, schema, inline) {
1823 Array.from(nodeList).forEach((
1824 /** @type {Node & { nextElementSibling?: unknown }} */
1825 node) => {
1826 var _schema$tag$isMatch, _schema$tag;
1827
1828 const tag = node.nodeName.toLowerCase(); // It's a valid child, if the tag exists in the schema without an isMatch
1829 // function, or with an isMatch function that matches the node.
1830
1831 if (schema.hasOwnProperty(tag) && (!schema[tag].isMatch || (_schema$tag$isMatch = (_schema$tag = schema[tag]).isMatch) !== null && _schema$tag$isMatch !== void 0 && _schema$tag$isMatch.call(_schema$tag, node))) {
1832 if (isElement(node)) {
1833 const {
1834 attributes = [],
1835 classes = [],
1836 children,
1837 require = [],
1838 allowEmpty
1839 } = schema[tag]; // If the node is empty and it's supposed to have children,
1840 // remove the node.
1841
1842 if (children && !allowEmpty && isEmpty(node)) {
1843 remove(node);
1844 return;
1845 }
1846
1847 if (node.hasAttributes()) {
1848 // Strip invalid attributes.
1849 Array.from(node.attributes).forEach(_ref => {
1850 let {
1851 name
1852 } = _ref;
1853
1854 if (name !== 'class' && !(0,external_lodash_namespaceObject.includes)(attributes, name)) {
1855 node.removeAttribute(name);
1856 }
1857 }); // Strip invalid classes.
1858 // In jsdom-jscore, 'node.classList' can be undefined.
1859 // TODO: Explore patching this in jsdom-jscore.
1860
1861 if (node.classList && node.classList.length) {
1862 const mattchers = classes.map(item => {
1863 if (typeof item === 'string') {
1864 return (
1865 /** @type {string} */
1866 className) => className === item;
1867 } else if (item instanceof RegExp) {
1868 return (
1869 /** @type {string} */
1870 className) => item.test(className);
1871 }
1872
1873 return external_lodash_namespaceObject.noop;
1874 });
1875 Array.from(node.classList).forEach(name => {
1876 if (!mattchers.some(isMatch => isMatch(name))) {
1877 node.classList.remove(name);
1878 }
1879 });
1880
1881 if (!node.classList.length) {
1882 node.removeAttribute('class');
1883 }
1884 }
1885 }
1886
1887 if (node.hasChildNodes()) {
1888 // Do not filter any content.
1889 if (children === '*') {
1890 return;
1891 } // Continue if the node is supposed to have children.
1892
1893
1894 if (children) {
1895 // If a parent requires certain children, but it does
1896 // not have them, drop the parent and continue.
1897 if (require.length && !node.querySelector(require.join(','))) {
1898 cleanNodeList(node.childNodes, doc, schema, inline);
1899 unwrap(node); // If the node is at the top, phrasing content, and
1900 // contains children that are block content, unwrap
1901 // the node because it is invalid.
1902 } else if (node.parentNode && node.parentNode.nodeName === 'BODY' && isPhrasingContent(node)) {
1903 cleanNodeList(node.childNodes, doc, schema, inline);
1904
1905 if (Array.from(node.childNodes).some(child => !isPhrasingContent(child))) {
1906 unwrap(node);
1907 }
1908 } else {
1909 cleanNodeList(node.childNodes, doc, children, inline);
1910 } // Remove children if the node is not supposed to have any.
1911
1912 } else {
1913 while (node.firstChild) {
1914 remove(node.firstChild);
1915 }
1916 }
1917 }
1918 } // Invalid child. Continue with schema at the same place and unwrap.
1919
1920 } else {
1921 cleanNodeList(node.childNodes, doc, schema, inline); // For inline mode, insert a line break when unwrapping nodes that
1922 // are not phrasing content.
1923
1924 if (inline && !isPhrasingContent(node) && node.nextElementSibling) {
1925 insertAfter(doc.createElement('br'), node);
1926 }
1927
1928 unwrap(node);
1929 }
1930 });
1931 }
1932 //# sourceMappingURL=clean-node-list.js.map
1933 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/remove-invalid-html.js
1934 /**
1935 * Internal dependencies
1936 */
1937
1938 /**
1939 * Given a schema, unwraps or removes nodes, attributes and classes on HTML.
1940 *
1941 * @param {string} HTML The HTML to clean up.
1942 * @param {import('./clean-node-list').Schema} schema Schema for the HTML.
1943 * @param {boolean} inline Whether to clean for inline mode.
1944 *
1945 * @return {string} The cleaned up HTML.
1946 */
1947
1948 function removeInvalidHTML(HTML, schema, inline) {
1949 const doc = document.implementation.createHTMLDocument('');
1950 doc.body.innerHTML = HTML;
1951 cleanNodeList(doc.body.childNodes, doc, schema, inline);
1952 return doc.body.innerHTML;
1953 }
1954 //# sourceMappingURL=remove-invalid-html.js.map
1955 ;// CONCATENATED MODULE: ./packages/dom/build-module/dom/index.js
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981 //# sourceMappingURL=index.js.map
1982 ;// CONCATENATED MODULE: ./packages/dom/build-module/data-transfer.js
1983 /**
1984 * Gets all files from a DataTransfer object.
1985 *
1986 * @param {DataTransfer} dataTransfer DataTransfer object to inspect.
1987 *
1988 * @return {File[]} An array containing all files.
1989 */
1990 function getFilesFromDataTransfer(dataTransfer) {
1991 const files = Array.from(dataTransfer.files);
1992 Array.from(dataTransfer.items).forEach(item => {
1993 const file = item.getAsFile();
1994
1995 if (file && !files.find(_ref => {
1996 let {
1997 name,
1998 type,
1999 size
2000 } = _ref;
2001 return name === file.name && type === file.type && size === file.size;
2002 })) {
2003 files.push(file);
2004 }
2005 });
2006 return files;
2007 }
2008 //# sourceMappingURL=data-transfer.js.map
2009 ;// CONCATENATED MODULE: ./packages/dom/build-module/index.js
2010 /**
2011 * Internal dependencies
2012 */
2013
2014
2015 /**
2016 * Object grouping `focusable` and `tabbable` utils
2017 * under the keys with the same name.
2018 */
2019
2020 const build_module_focus = {
2021 focusable: focusable_namespaceObject,
2022 tabbable: tabbable_namespaceObject
2023 };
2024
2025
2026
2027 //# sourceMappingURL=index.js.map
2028 (window.wp = window.wp || {}).dom = __webpack_exports__;
2029 /******/ })()
2030 ;