dom.js 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. this["wp"] = this["wp"] || {}; this["wp"]["dom"] =
  2. /******/ (function(modules) { // webpackBootstrap
  3. /******/ // The module cache
  4. /******/ var installedModules = {};
  5. /******/
  6. /******/ // The require function
  7. /******/ function __webpack_require__(moduleId) {
  8. /******/
  9. /******/ // Check if module is in cache
  10. /******/ if(installedModules[moduleId]) {
  11. /******/ return installedModules[moduleId].exports;
  12. /******/ }
  13. /******/ // Create a new module (and put it into the cache)
  14. /******/ var module = installedModules[moduleId] = {
  15. /******/ i: moduleId,
  16. /******/ l: false,
  17. /******/ exports: {}
  18. /******/ };
  19. /******/
  20. /******/ // Execute the module function
  21. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  22. /******/
  23. /******/ // Flag the module as loaded
  24. /******/ module.l = true;
  25. /******/
  26. /******/ // Return the exports of the module
  27. /******/ return module.exports;
  28. /******/ }
  29. /******/
  30. /******/
  31. /******/ // expose the modules object (__webpack_modules__)
  32. /******/ __webpack_require__.m = modules;
  33. /******/
  34. /******/ // expose the module cache
  35. /******/ __webpack_require__.c = installedModules;
  36. /******/
  37. /******/ // define getter function for harmony exports
  38. /******/ __webpack_require__.d = function(exports, name, getter) {
  39. /******/ if(!__webpack_require__.o(exports, name)) {
  40. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  41. /******/ }
  42. /******/ };
  43. /******/
  44. /******/ // define __esModule on exports
  45. /******/ __webpack_require__.r = function(exports) {
  46. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  47. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  48. /******/ }
  49. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  50. /******/ };
  51. /******/
  52. /******/ // create a fake namespace object
  53. /******/ // mode & 1: value is a module id, require it
  54. /******/ // mode & 2: merge all properties of value into the ns
  55. /******/ // mode & 4: return value when already ns object
  56. /******/ // mode & 8|1: behave like require
  57. /******/ __webpack_require__.t = function(value, mode) {
  58. /******/ if(mode & 1) value = __webpack_require__(value);
  59. /******/ if(mode & 8) return value;
  60. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  61. /******/ var ns = Object.create(null);
  62. /******/ __webpack_require__.r(ns);
  63. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  64. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  65. /******/ return ns;
  66. /******/ };
  67. /******/
  68. /******/ // getDefaultExport function for compatibility with non-harmony modules
  69. /******/ __webpack_require__.n = function(module) {
  70. /******/ var getter = module && module.__esModule ?
  71. /******/ function getDefault() { return module['default']; } :
  72. /******/ function getModuleExports() { return module; };
  73. /******/ __webpack_require__.d(getter, 'a', getter);
  74. /******/ return getter;
  75. /******/ };
  76. /******/
  77. /******/ // Object.prototype.hasOwnProperty.call
  78. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  79. /******/
  80. /******/ // __webpack_public_path__
  81. /******/ __webpack_require__.p = "";
  82. /******/
  83. /******/
  84. /******/ // Load entry module and return exports
  85. /******/ return __webpack_require__(__webpack_require__.s = 364);
  86. /******/ })
  87. /************************************************************************/
  88. /******/ ({
  89. /***/ 17:
  90. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  91. "use strict";
  92. // CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/arrayWithoutHoles.js
  93. function _arrayWithoutHoles(arr) {
  94. if (Array.isArray(arr)) {
  95. for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
  96. arr2[i] = arr[i];
  97. }
  98. return arr2;
  99. }
  100. }
  101. // EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/iterableToArray.js
  102. var iterableToArray = __webpack_require__(30);
  103. // CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/nonIterableSpread.js
  104. function _nonIterableSpread() {
  105. throw new TypeError("Invalid attempt to spread non-iterable instance");
  106. }
  107. // CONCATENATED MODULE: ./node_modules/@babel/runtime/helpers/esm/toConsumableArray.js
  108. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _toConsumableArray; });
  109. function _toConsumableArray(arr) {
  110. return _arrayWithoutHoles(arr) || Object(iterableToArray["a" /* default */])(arr) || _nonIterableSpread();
  111. }
  112. /***/ }),
  113. /***/ 2:
  114. /***/ (function(module, exports) {
  115. (function() { module.exports = this["lodash"]; }());
  116. /***/ }),
  117. /***/ 30:
  118. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  119. "use strict";
  120. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _iterableToArray; });
  121. function _iterableToArray(iter) {
  122. if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
  123. }
  124. /***/ }),
  125. /***/ 364:
  126. /***/ (function(module, __webpack_exports__, __webpack_require__) {
  127. "use strict";
  128. __webpack_require__.r(__webpack_exports__);
  129. var focusable_namespaceObject = {};
  130. __webpack_require__.r(focusable_namespaceObject);
  131. __webpack_require__.d(focusable_namespaceObject, "find", function() { return find; });
  132. var tabbable_namespaceObject = {};
  133. __webpack_require__.r(tabbable_namespaceObject);
  134. __webpack_require__.d(tabbable_namespaceObject, "isTabbableIndex", function() { return isTabbableIndex; });
  135. __webpack_require__.d(tabbable_namespaceObject, "find", function() { return tabbable_find; });
  136. // EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/toConsumableArray.js + 2 modules
  137. var toConsumableArray = __webpack_require__(17);
  138. // CONCATENATED MODULE: ./node_modules/@wordpress/dom/build-module/focusable.js
  139. /**
  140. * References:
  141. *
  142. * Focusable:
  143. * - https://www.w3.org/TR/html5/editing.html#focus-management
  144. *
  145. * Sequential focus navigation:
  146. * - https://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute
  147. *
  148. * Disabled elements:
  149. * - https://www.w3.org/TR/html5/disabled-elements.html#disabled-elements
  150. *
  151. * getClientRects algorithm (requiring layout box):
  152. * - https://www.w3.org/TR/cssom-view-1/#extension-to-the-element-interface
  153. *
  154. * AREA elements associated with an IMG:
  155. * - https://w3c.github.io/html/editing.html#data-model
  156. */
  157. var SELECTOR = ['[tabindex]', 'a[href]', 'button:not([disabled])', 'input:not([type="hidden"]):not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', 'iframe', 'object', 'embed', 'area[href]', '[contenteditable]:not([contenteditable=false])'].join(',');
  158. /**
  159. * Returns true if the specified element is visible (i.e. neither display: none
  160. * nor visibility: hidden).
  161. *
  162. * @param {Element} element DOM element to test.
  163. *
  164. * @return {boolean} Whether element is visible.
  165. */
  166. function isVisible(element) {
  167. return element.offsetWidth > 0 || element.offsetHeight > 0 || element.getClientRects().length > 0;
  168. }
  169. /**
  170. * Returns true if the specified area element is a valid focusable element, or
  171. * false otherwise. Area is only focusable if within a map where a named map
  172. * referenced by an image somewhere in the document.
  173. *
  174. * @param {Element} element DOM area element to test.
  175. *
  176. * @return {boolean} Whether area element is valid for focus.
  177. */
  178. function isValidFocusableArea(element) {
  179. var map = element.closest('map[name]');
  180. if (!map) {
  181. return false;
  182. }
  183. var img = document.querySelector('img[usemap="#' + map.name + '"]');
  184. return !!img && isVisible(img);
  185. }
  186. /**
  187. * Returns all focusable elements within a given context.
  188. *
  189. * @param {Element} context Element in which to search.
  190. *
  191. * @return {Element[]} Focusable elements.
  192. */
  193. function find(context) {
  194. var elements = context.querySelectorAll(SELECTOR);
  195. return Object(toConsumableArray["a" /* default */])(elements).filter(function (element) {
  196. if (!isVisible(element)) {
  197. return false;
  198. }
  199. var nodeName = element.nodeName;
  200. if ('AREA' === nodeName) {
  201. return isValidFocusableArea(element);
  202. }
  203. return true;
  204. });
  205. }
  206. // EXTERNAL MODULE: external "lodash"
  207. var external_lodash_ = __webpack_require__(2);
  208. // CONCATENATED MODULE: ./node_modules/@wordpress/dom/build-module/tabbable.js
  209. /**
  210. * External dependencies
  211. */
  212. /**
  213. * Internal dependencies
  214. */
  215. /**
  216. * Returns the tab index of the given element. In contrast with the tabIndex
  217. * property, this normalizes the default (0) to avoid browser inconsistencies,
  218. * operating under the assumption that this function is only ever called with a
  219. * focusable node.
  220. *
  221. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=1190261
  222. *
  223. * @param {Element} element Element from which to retrieve.
  224. *
  225. * @return {?number} Tab index of element (default 0).
  226. */
  227. function getTabIndex(element) {
  228. var tabIndex = element.getAttribute('tabindex');
  229. return tabIndex === null ? 0 : parseInt(tabIndex, 10);
  230. }
  231. /**
  232. * Returns true if the specified element is tabbable, or false otherwise.
  233. *
  234. * @param {Element} element Element to test.
  235. *
  236. * @return {boolean} Whether element is tabbable.
  237. */
  238. function isTabbableIndex(element) {
  239. return getTabIndex(element) !== -1;
  240. }
  241. /**
  242. * Returns a stateful reducer function which constructs a filtered array of
  243. * tabbable elements, where at most one radio input is selected for a given
  244. * name, giving priority to checked input, falling back to the first
  245. * encountered.
  246. *
  247. * @return {Function} Radio group collapse reducer.
  248. */
  249. function createStatefulCollapseRadioGroup() {
  250. var CHOSEN_RADIO_BY_NAME = {};
  251. return function collapseRadioGroup(result, element) {
  252. var nodeName = element.nodeName,
  253. type = element.type,
  254. checked = element.checked,
  255. name = element.name; // For all non-radio tabbables, construct to array by concatenating.
  256. if (nodeName !== 'INPUT' || type !== 'radio' || !name) {
  257. return result.concat(element);
  258. }
  259. var hasChosen = CHOSEN_RADIO_BY_NAME.hasOwnProperty(name); // Omit by skipping concatenation if the radio element is not chosen.
  260. var isChosen = checked || !hasChosen;
  261. if (!isChosen) {
  262. return result;
  263. } // At this point, if there had been a chosen element, the current
  264. // element is checked and should take priority. Retroactively remove
  265. // the element which had previously been considered the chosen one.
  266. if (hasChosen) {
  267. var hadChosenElement = CHOSEN_RADIO_BY_NAME[name];
  268. result = Object(external_lodash_["without"])(result, hadChosenElement);
  269. }
  270. CHOSEN_RADIO_BY_NAME[name] = element;
  271. return result.concat(element);
  272. };
  273. }
  274. /**
  275. * An array map callback, returning an object with the element value and its
  276. * array index location as properties. This is used to emulate a proper stable
  277. * sort where equal tabIndex should be left in order of their occurrence in the
  278. * document.
  279. *
  280. * @param {Element} element Element.
  281. * @param {number} index Array index of element.
  282. *
  283. * @return {Object} Mapped object with element, index.
  284. */
  285. function mapElementToObjectTabbable(element, index) {
  286. return {
  287. element: element,
  288. index: index
  289. };
  290. }
  291. /**
  292. * An array map callback, returning an element of the given mapped object's
  293. * element value.
  294. *
  295. * @param {Object} object Mapped object with index.
  296. *
  297. * @return {Element} Mapped object element.
  298. */
  299. function mapObjectTabbableToElement(object) {
  300. return object.element;
  301. }
  302. /**
  303. * A sort comparator function used in comparing two objects of mapped elements.
  304. *
  305. * @see mapElementToObjectTabbable
  306. *
  307. * @param {Object} a First object to compare.
  308. * @param {Object} b Second object to compare.
  309. *
  310. * @return {number} Comparator result.
  311. */
  312. function compareObjectTabbables(a, b) {
  313. var aTabIndex = getTabIndex(a.element);
  314. var bTabIndex = getTabIndex(b.element);
  315. if (aTabIndex === bTabIndex) {
  316. return a.index - b.index;
  317. }
  318. return aTabIndex - bTabIndex;
  319. }
  320. function tabbable_find(context) {
  321. return find(context).filter(isTabbableIndex).map(mapElementToObjectTabbable).sort(compareObjectTabbables).map(mapObjectTabbableToElement).reduce(createStatefulCollapseRadioGroup(), []);
  322. }
  323. // CONCATENATED MODULE: ./node_modules/@wordpress/dom/build-module/dom.js
  324. /**
  325. * External dependencies
  326. */
  327. /**
  328. * Browser dependencies
  329. */
  330. var _window = window,
  331. getComputedStyle = _window.getComputedStyle;
  332. var _window$Node = window.Node,
  333. TEXT_NODE = _window$Node.TEXT_NODE,
  334. ELEMENT_NODE = _window$Node.ELEMENT_NODE,
  335. DOCUMENT_POSITION_PRECEDING = _window$Node.DOCUMENT_POSITION_PRECEDING,
  336. DOCUMENT_POSITION_FOLLOWING = _window$Node.DOCUMENT_POSITION_FOLLOWING;
  337. /**
  338. * Returns true if the given selection object is in the forward direction, or
  339. * false otherwise.
  340. *
  341. * @see https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition
  342. *
  343. * @param {Selection} selection Selection object to check.
  344. *
  345. * @return {boolean} Whether the selection is forward.
  346. */
  347. function isSelectionForward(selection) {
  348. var anchorNode = selection.anchorNode,
  349. focusNode = selection.focusNode,
  350. anchorOffset = selection.anchorOffset,
  351. focusOffset = selection.focusOffset;
  352. var position = anchorNode.compareDocumentPosition(focusNode); // Disable reason: `Node#compareDocumentPosition` returns a bitmask value,
  353. // so bitwise operators are intended.
  354. /* eslint-disable no-bitwise */
  355. // Compare whether anchor node precedes focus node. If focus node (where
  356. // end of selection occurs) is after the anchor node, it is forward.
  357. if (position & DOCUMENT_POSITION_PRECEDING) {
  358. return false;
  359. }
  360. if (position & DOCUMENT_POSITION_FOLLOWING) {
  361. return true;
  362. }
  363. /* eslint-enable no-bitwise */
  364. // `compareDocumentPosition` returns 0 when passed the same node, in which
  365. // case compare offsets.
  366. if (position === 0) {
  367. return anchorOffset <= focusOffset;
  368. } // This should never be reached, but return true as default case.
  369. return true;
  370. }
  371. /**
  372. * Check whether the selection is at the edge of the container. Checks for
  373. * horizontal position by default. Set `onlyVertical` to true to check only
  374. * vertically.
  375. *
  376. * @param {Element} container Focusable element.
  377. * @param {boolean} isReverse Set to true to check left, false to check right.
  378. * @param {boolean} onlyVertical Set to true to check only vertical position.
  379. *
  380. * @return {boolean} True if at the edge, false if not.
  381. */
  382. function isEdge(container, isReverse, onlyVertical) {
  383. if (Object(external_lodash_["includes"])(['INPUT', 'TEXTAREA'], container.tagName)) {
  384. if (container.selectionStart !== container.selectionEnd) {
  385. return false;
  386. }
  387. if (isReverse) {
  388. return container.selectionStart === 0;
  389. }
  390. return container.value.length === container.selectionStart;
  391. }
  392. if (!container.isContentEditable) {
  393. return true;
  394. }
  395. var selection = window.getSelection();
  396. if (!selection.rangeCount) {
  397. return false;
  398. }
  399. var range = selection.getRangeAt(0).cloneRange();
  400. var isForward = isSelectionForward(selection);
  401. var isCollapsed = selection.isCollapsed; // Collapse in direction of selection.
  402. if (!isCollapsed) {
  403. range.collapse(!isForward);
  404. }
  405. var rangeRect = getRectangleFromRange(range);
  406. if (!rangeRect) {
  407. return false;
  408. }
  409. var computedStyle = window.getComputedStyle(container);
  410. var lineHeight = parseInt(computedStyle.lineHeight, 10) || 0; // Only consider the multiline selection at the edge if the direction is
  411. // towards the edge.
  412. if (!isCollapsed && rangeRect.height > lineHeight && isForward === isReverse) {
  413. return false;
  414. }
  415. var padding = parseInt(computedStyle["padding".concat(isReverse ? 'Top' : 'Bottom')], 10) || 0; // Calculate a buffer that is half the line height. In some browsers, the
  416. // selection rectangle may not fill the entire height of the line, so we add
  417. // 3/4 the line height to the selection rectangle to ensure that it is well
  418. // over its line boundary.
  419. var buffer = 3 * parseInt(lineHeight, 10) / 4;
  420. var containerRect = container.getBoundingClientRect();
  421. var verticalEdge = isReverse ? containerRect.top + padding > rangeRect.top - buffer : containerRect.bottom - padding < rangeRect.bottom + buffer;
  422. if (!verticalEdge) {
  423. return false;
  424. }
  425. if (onlyVertical) {
  426. return true;
  427. } // In the case of RTL scripts, the horizontal edge is at the opposite side.
  428. var direction = computedStyle.direction;
  429. var isReverseDir = direction === 'rtl' ? !isReverse : isReverse; // To calculate the horizontal position, we insert a test range and see if
  430. // this test range has the same horizontal position. This method proves to
  431. // be better than a DOM-based calculation, because it ignores empty text
  432. // nodes and a trailing line break element. In other words, we need to check
  433. // visual positioning, not DOM positioning.
  434. var x = isReverseDir ? containerRect.left + 1 : containerRect.right - 1;
  435. var y = isReverse ? containerRect.top + buffer : containerRect.bottom - buffer;
  436. var testRange = hiddenCaretRangeFromPoint(document, x, y, container);
  437. if (!testRange) {
  438. return false;
  439. }
  440. var side = isReverseDir ? 'left' : 'right';
  441. var testRect = getRectangleFromRange(testRange); // Allow the position to be 1px off.
  442. return Math.abs(testRect[side] - rangeRect[side]) <= 1;
  443. }
  444. /**
  445. * Check whether the selection is horizontally at the edge of the container.
  446. *
  447. * @param {Element} container Focusable element.
  448. * @param {boolean} isReverse Set to true to check left, false for right.
  449. *
  450. * @return {boolean} True if at the horizontal edge, false if not.
  451. */
  452. function isHorizontalEdge(container, isReverse) {
  453. return isEdge(container, isReverse);
  454. }
  455. /**
  456. * Check whether the selection is vertically at the edge of the container.
  457. *
  458. * @param {Element} container Focusable element.
  459. * @param {boolean} isReverse Set to true to check top, false for bottom.
  460. *
  461. * @return {boolean} True if at the vertical edge, false if not.
  462. */
  463. function isVerticalEdge(container, isReverse) {
  464. return isEdge(container, isReverse, true);
  465. }
  466. /**
  467. * Get the rectangle of a given Range.
  468. *
  469. * @param {Range} range The range.
  470. *
  471. * @return {DOMRect} The rectangle.
  472. */
  473. function getRectangleFromRange(range) {
  474. // For uncollapsed ranges, get the rectangle that bounds the contents of the
  475. // range; this a rectangle enclosing the union of the bounding rectangles
  476. // for all the elements in the range.
  477. if (!range.collapsed) {
  478. return range.getBoundingClientRect();
  479. }
  480. var _range = range,
  481. startContainer = _range.startContainer; // Correct invalid "BR" ranges. The cannot contain any children.
  482. if (startContainer.nodeName === 'BR') {
  483. var parentNode = startContainer.parentNode;
  484. var index = Array.from(parentNode.childNodes).indexOf(startContainer);
  485. range = document.createRange();
  486. range.setStart(parentNode, index);
  487. range.setEnd(parentNode, index);
  488. }
  489. var rect = range.getClientRects()[0]; // If the collapsed range starts (and therefore ends) at an element node,
  490. // `getClientRects` can be empty in some browsers. This can be resolved
  491. // by adding a temporary text node with zero-width space to the range.
  492. //
  493. // See: https://stackoverflow.com/a/6847328/995445
  494. if (!rect) {
  495. var padNode = document.createTextNode("\u200B"); // Do not modify the live range.
  496. range = range.cloneRange();
  497. range.insertNode(padNode);
  498. rect = range.getClientRects()[0];
  499. padNode.parentNode.removeChild(padNode);
  500. }
  501. return rect;
  502. }
  503. /**
  504. * Get the rectangle for the selection in a container.
  505. *
  506. * @return {?DOMRect} The rectangle.
  507. */
  508. function computeCaretRect() {
  509. var selection = window.getSelection();
  510. var range = selection.rangeCount ? selection.getRangeAt(0) : null;
  511. if (!range) {
  512. return;
  513. }
  514. return getRectangleFromRange(range);
  515. }
  516. /**
  517. * Places the caret at start or end of a given element.
  518. *
  519. * @param {Element} container Focusable element.
  520. * @param {boolean} isReverse True for end, false for start.
  521. */
  522. function placeCaretAtHorizontalEdge(container, isReverse) {
  523. if (!container) {
  524. return;
  525. }
  526. if (Object(external_lodash_["includes"])(['INPUT', 'TEXTAREA'], container.tagName)) {
  527. container.focus();
  528. if (isReverse) {
  529. container.selectionStart = container.value.length;
  530. container.selectionEnd = container.value.length;
  531. } else {
  532. container.selectionStart = 0;
  533. container.selectionEnd = 0;
  534. }
  535. return;
  536. }
  537. container.focus();
  538. if (!container.isContentEditable) {
  539. return;
  540. } // Select on extent child of the container, not the container itself. This
  541. // avoids the selection always being `endOffset` of 1 when placed at end,
  542. // where `startContainer`, `endContainer` would always be container itself.
  543. var rangeTarget = container[isReverse ? 'lastChild' : 'firstChild']; // If no range target, it implies that the container is empty. Focusing is
  544. // sufficient for caret to be placed correctly.
  545. if (!rangeTarget) {
  546. return;
  547. }
  548. var selection = window.getSelection();
  549. var range = document.createRange();
  550. range.selectNodeContents(rangeTarget);
  551. range.collapse(!isReverse);
  552. selection.removeAllRanges();
  553. selection.addRange(range);
  554. }
  555. /**
  556. * Polyfill.
  557. * Get a collapsed range for a given point.
  558. *
  559. * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/caretRangeFromPoint
  560. *
  561. * @param {Document} doc The document of the range.
  562. * @param {number} x Horizontal position within the current viewport.
  563. * @param {number} y Vertical position within the current viewport.
  564. *
  565. * @return {?Range} The best range for the given point.
  566. */
  567. function caretRangeFromPoint(doc, x, y) {
  568. if (doc.caretRangeFromPoint) {
  569. return doc.caretRangeFromPoint(x, y);
  570. }
  571. if (!doc.caretPositionFromPoint) {
  572. return null;
  573. }
  574. var point = doc.caretPositionFromPoint(x, y); // If x or y are negative, outside viewport, or there is no text entry node.
  575. // https://developer.mozilla.org/en-US/docs/Web/API/Document/caretRangeFromPoint
  576. if (!point) {
  577. return null;
  578. }
  579. var range = doc.createRange();
  580. range.setStart(point.offsetNode, point.offset);
  581. range.collapse(true);
  582. return range;
  583. }
  584. /**
  585. * Get a collapsed range for a given point.
  586. * Gives the container a temporary high z-index (above any UI).
  587. * This is preferred over getting the UI nodes and set styles there.
  588. *
  589. * @param {Document} doc The document of the range.
  590. * @param {number} x Horizontal position within the current viewport.
  591. * @param {number} y Vertical position within the current viewport.
  592. * @param {Element} container Container in which the range is expected to be found.
  593. *
  594. * @return {?Range} The best range for the given point.
  595. */
  596. function hiddenCaretRangeFromPoint(doc, x, y, container) {
  597. var originalZIndex = container.style.zIndex;
  598. var originalPosition = container.style.position; // A z-index only works if the element position is not static.
  599. container.style.zIndex = '10000';
  600. container.style.position = 'relative';
  601. var range = caretRangeFromPoint(doc, x, y);
  602. container.style.zIndex = originalZIndex;
  603. container.style.position = originalPosition;
  604. return range;
  605. }
  606. /**
  607. * Places the caret at the top or bottom of a given element.
  608. *
  609. * @param {Element} container Focusable element.
  610. * @param {boolean} isReverse True for bottom, false for top.
  611. * @param {DOMRect} [rect] The rectangle to position the caret with.
  612. * @param {boolean} [mayUseScroll=true] True to allow scrolling, false to disallow.
  613. */
  614. function placeCaretAtVerticalEdge(container, isReverse, rect) {
  615. var mayUseScroll = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
  616. if (!container) {
  617. return;
  618. }
  619. if (!rect || !container.isContentEditable) {
  620. placeCaretAtHorizontalEdge(container, isReverse);
  621. return;
  622. } // Offset by a buffer half the height of the caret rect. This is needed
  623. // because caretRangeFromPoint may default to the end of the selection if
  624. // offset is too close to the edge. It's unclear how to precisely calculate
  625. // this threshold; it may be the padded area of some combination of line
  626. // height, caret height, and font size. The buffer offset is effectively
  627. // equivalent to a point at half the height of a line of text.
  628. var buffer = rect.height / 2;
  629. var editableRect = container.getBoundingClientRect();
  630. var x = rect.left;
  631. var y = isReverse ? editableRect.bottom - buffer : editableRect.top + buffer;
  632. var range = hiddenCaretRangeFromPoint(document, x, y, container);
  633. if (!range || !container.contains(range.startContainer)) {
  634. if (mayUseScroll && (!range || !range.startContainer || !range.startContainer.contains(container))) {
  635. // Might be out of view.
  636. // Easier than attempting to calculate manually.
  637. container.scrollIntoView(isReverse);
  638. placeCaretAtVerticalEdge(container, isReverse, rect, false);
  639. return;
  640. }
  641. placeCaretAtHorizontalEdge(container, isReverse);
  642. return;
  643. }
  644. var selection = window.getSelection();
  645. selection.removeAllRanges();
  646. selection.addRange(range);
  647. container.focus(); // Editable was already focussed, it goes back to old range...
  648. // This fixes it.
  649. selection.removeAllRanges();
  650. selection.addRange(range);
  651. }
  652. /**
  653. * Check whether the given element is a text field, where text field is defined
  654. * by the ability to select within the input, or that it is contenteditable.
  655. *
  656. * See: https://html.spec.whatwg.org/#textFieldSelection
  657. *
  658. * @param {HTMLElement} element The HTML element.
  659. *
  660. * @return {boolean} True if the element is an text field, false if not.
  661. */
  662. function isTextField(element) {
  663. try {
  664. var nodeName = element.nodeName,
  665. selectionStart = element.selectionStart,
  666. contentEditable = element.contentEditable;
  667. return nodeName === 'INPUT' && selectionStart !== null || nodeName === 'TEXTAREA' || contentEditable === 'true';
  668. } catch (error) {
  669. // Safari throws an exception when trying to get `selectionStart`
  670. // on non-text <input> elements (which, understandably, don't
  671. // have the text selection API). We catch this via a try/catch
  672. // block, as opposed to a more explicit check of the element's
  673. // input types, because of Safari's non-standard behavior. This
  674. // also means we don't have to worry about the list of input
  675. // types that support `selectionStart` changing as the HTML spec
  676. // evolves over time.
  677. return false;
  678. }
  679. }
  680. /**
  681. * Check wether the current document has a selection.
  682. * This checks both for focus in an input field and general text selection.
  683. *
  684. * @return {boolean} True if there is selection, false if not.
  685. */
  686. function documentHasSelection() {
  687. if (isTextField(document.activeElement)) {
  688. return true;
  689. }
  690. var selection = window.getSelection();
  691. var range = selection.rangeCount ? selection.getRangeAt(0) : null;
  692. return range && !range.collapsed;
  693. }
  694. /**
  695. * Check whether the contents of the element have been entirely selected.
  696. * Returns true if there is no possibility of selection.
  697. *
  698. * @param {Element} element The element to check.
  699. *
  700. * @return {boolean} True if entirely selected, false if not.
  701. */
  702. function isEntirelySelected(element) {
  703. if (Object(external_lodash_["includes"])(['INPUT', 'TEXTAREA'], element.nodeName)) {
  704. return element.selectionStart === 0 && element.value.length === element.selectionEnd;
  705. }
  706. if (!element.isContentEditable) {
  707. return true;
  708. }
  709. var selection = window.getSelection();
  710. var range = selection.rangeCount ? selection.getRangeAt(0) : null;
  711. if (!range) {
  712. return true;
  713. }
  714. var startContainer = range.startContainer,
  715. endContainer = range.endContainer,
  716. startOffset = range.startOffset,
  717. endOffset = range.endOffset;
  718. if (startContainer === element && endContainer === element && startOffset === 0 && endOffset === element.childNodes.length) {
  719. return true;
  720. }
  721. var lastChild = element.lastChild;
  722. var lastChildContentLength = lastChild.nodeType === TEXT_NODE ? lastChild.data.length : lastChild.childNodes.length;
  723. return startContainer === element.firstChild && endContainer === element.lastChild && startOffset === 0 && endOffset === lastChildContentLength;
  724. }
  725. /**
  726. * Given a DOM node, finds the closest scrollable container node.
  727. *
  728. * @param {Element} node Node from which to start.
  729. *
  730. * @return {?Element} Scrollable container node, if found.
  731. */
  732. function getScrollContainer(node) {
  733. if (!node) {
  734. return;
  735. } // Scrollable if scrollable height exceeds displayed...
  736. if (node.scrollHeight > node.clientHeight) {
  737. // ...except when overflow is defined to be hidden or visible
  738. var _window$getComputedSt = window.getComputedStyle(node),
  739. overflowY = _window$getComputedSt.overflowY;
  740. if (/(auto|scroll)/.test(overflowY)) {
  741. return node;
  742. }
  743. } // Continue traversing
  744. return getScrollContainer(node.parentNode);
  745. }
  746. /**
  747. * Returns the closest positioned element, or null under any of the conditions
  748. * of the offsetParent specification. Unlike offsetParent, this function is not
  749. * limited to HTMLElement and accepts any Node (e.g. Node.TEXT_NODE).
  750. *
  751. * @see https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent
  752. *
  753. * @param {Node} node Node from which to find offset parent.
  754. *
  755. * @return {?Node} Offset parent.
  756. */
  757. function getOffsetParent(node) {
  758. // Cannot retrieve computed style or offset parent only anything other than
  759. // an element node, so find the closest element node.
  760. var closestElement;
  761. while (closestElement = node.parentNode) {
  762. if (closestElement.nodeType === ELEMENT_NODE) {
  763. break;
  764. }
  765. }
  766. if (!closestElement) {
  767. return null;
  768. } // If the closest element is already positioned, return it, as offsetParent
  769. // does not otherwise consider the node itself.
  770. if (getComputedStyle(closestElement).position !== 'static') {
  771. return closestElement;
  772. }
  773. return closestElement.offsetParent;
  774. }
  775. /**
  776. * Given two DOM nodes, replaces the former with the latter in the DOM.
  777. *
  778. * @param {Element} processedNode Node to be removed.
  779. * @param {Element} newNode Node to be inserted in its place.
  780. * @return {void}
  781. */
  782. function replace(processedNode, newNode) {
  783. insertAfter(newNode, processedNode.parentNode);
  784. remove(processedNode);
  785. }
  786. /**
  787. * Given a DOM node, removes it from the DOM.
  788. *
  789. * @param {Element} node Node to be removed.
  790. * @return {void}
  791. */
  792. function remove(node) {
  793. node.parentNode.removeChild(node);
  794. }
  795. /**
  796. * Given two DOM nodes, inserts the former in the DOM as the next sibling of
  797. * the latter.
  798. *
  799. * @param {Element} newNode Node to be inserted.
  800. * @param {Element} referenceNode Node after which to perform the insertion.
  801. * @return {void}
  802. */
  803. function insertAfter(newNode, referenceNode) {
  804. referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  805. }
  806. /**
  807. * Unwrap the given node. This means any child nodes are moved to the parent.
  808. *
  809. * @param {Node} node The node to unwrap.
  810. *
  811. * @return {void}
  812. */
  813. function unwrap(node) {
  814. var parent = node.parentNode;
  815. while (node.firstChild) {
  816. parent.insertBefore(node.firstChild, node);
  817. }
  818. parent.removeChild(node);
  819. }
  820. /**
  821. * Replaces the given node with a new node with the given tag name.
  822. *
  823. * @param {Element} node The node to replace
  824. * @param {string} tagName The new tag name.
  825. *
  826. * @return {Element} The new node.
  827. */
  828. function replaceTag(node, tagName) {
  829. var newNode = node.ownerDocument.createElement(tagName);
  830. while (node.firstChild) {
  831. newNode.appendChild(node.firstChild);
  832. }
  833. node.parentNode.replaceChild(newNode, node);
  834. return newNode;
  835. }
  836. /**
  837. * Wraps the given node with a new node with the given tag name.
  838. *
  839. * @param {Element} newNode The node to insert.
  840. * @param {Element} referenceNode The node to wrap.
  841. */
  842. function wrap(newNode, referenceNode) {
  843. referenceNode.parentNode.insertBefore(newNode, referenceNode);
  844. newNode.appendChild(referenceNode);
  845. }
  846. // CONCATENATED MODULE: ./node_modules/@wordpress/dom/build-module/index.js
  847. /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "focus", function() { return build_module_focus; });
  848. /* concated harmony reexport isHorizontalEdge */__webpack_require__.d(__webpack_exports__, "isHorizontalEdge", function() { return isHorizontalEdge; });
  849. /* concated harmony reexport isVerticalEdge */__webpack_require__.d(__webpack_exports__, "isVerticalEdge", function() { return isVerticalEdge; });
  850. /* concated harmony reexport getRectangleFromRange */__webpack_require__.d(__webpack_exports__, "getRectangleFromRange", function() { return getRectangleFromRange; });
  851. /* concated harmony reexport computeCaretRect */__webpack_require__.d(__webpack_exports__, "computeCaretRect", function() { return computeCaretRect; });
  852. /* concated harmony reexport placeCaretAtHorizontalEdge */__webpack_require__.d(__webpack_exports__, "placeCaretAtHorizontalEdge", function() { return placeCaretAtHorizontalEdge; });
  853. /* concated harmony reexport placeCaretAtVerticalEdge */__webpack_require__.d(__webpack_exports__, "placeCaretAtVerticalEdge", function() { return placeCaretAtVerticalEdge; });
  854. /* concated harmony reexport isTextField */__webpack_require__.d(__webpack_exports__, "isTextField", function() { return isTextField; });
  855. /* concated harmony reexport documentHasSelection */__webpack_require__.d(__webpack_exports__, "documentHasSelection", function() { return documentHasSelection; });
  856. /* concated harmony reexport isEntirelySelected */__webpack_require__.d(__webpack_exports__, "isEntirelySelected", function() { return isEntirelySelected; });
  857. /* concated harmony reexport getScrollContainer */__webpack_require__.d(__webpack_exports__, "getScrollContainer", function() { return getScrollContainer; });
  858. /* concated harmony reexport getOffsetParent */__webpack_require__.d(__webpack_exports__, "getOffsetParent", function() { return getOffsetParent; });
  859. /* concated harmony reexport replace */__webpack_require__.d(__webpack_exports__, "replace", function() { return replace; });
  860. /* concated harmony reexport remove */__webpack_require__.d(__webpack_exports__, "remove", function() { return remove; });
  861. /* concated harmony reexport insertAfter */__webpack_require__.d(__webpack_exports__, "insertAfter", function() { return insertAfter; });
  862. /* concated harmony reexport unwrap */__webpack_require__.d(__webpack_exports__, "unwrap", function() { return unwrap; });
  863. /* concated harmony reexport replaceTag */__webpack_require__.d(__webpack_exports__, "replaceTag", function() { return replaceTag; });
  864. /* concated harmony reexport wrap */__webpack_require__.d(__webpack_exports__, "wrap", function() { return wrap; });
  865. /**
  866. * Internal dependencies
  867. */
  868. /**
  869. * Object grouping `focusable` and `tabbable` utils
  870. * under the keys with the same name.
  871. */
  872. var build_module_focus = {
  873. focusable: focusable_namespaceObject,
  874. tabbable: tabbable_namespaceObject
  875. };
  876. /***/ })
  877. /******/ });