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