You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4146 lines
123 KiB

1 year ago
  1. /******/ (function() { // webpackBootstrap
  2. /******/ "use strict";
  3. /******/ // The require scope
  4. /******/ var __webpack_require__ = {};
  5. /******/
  6. /************************************************************************/
  7. /******/ /* webpack/runtime/compat get default export */
  8. /******/ !function() {
  9. /******/ // getDefaultExport function for compatibility with non-harmony modules
  10. /******/ __webpack_require__.n = function(module) {
  11. /******/ var getter = module && module.__esModule ?
  12. /******/ function() { return module['default']; } :
  13. /******/ function() { return module; };
  14. /******/ __webpack_require__.d(getter, { a: getter });
  15. /******/ return getter;
  16. /******/ };
  17. /******/ }();
  18. /******/
  19. /******/ /* webpack/runtime/define property getters */
  20. /******/ !function() {
  21. /******/ // define getter functions for harmony exports
  22. /******/ __webpack_require__.d = function(exports, definition) {
  23. /******/ for(var key in definition) {
  24. /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
  25. /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
  26. /******/ }
  27. /******/ }
  28. /******/ };
  29. /******/ }();
  30. /******/
  31. /******/ /* webpack/runtime/hasOwnProperty shorthand */
  32. /******/ !function() {
  33. /******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
  34. /******/ }();
  35. /******/
  36. /******/ /* webpack/runtime/make namespace object */
  37. /******/ !function() {
  38. /******/ // define __esModule on exports
  39. /******/ __webpack_require__.r = function(exports) {
  40. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  41. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  42. /******/ }
  43. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  44. /******/ };
  45. /******/ }();
  46. /******/
  47. /************************************************************************/
  48. var __webpack_exports__ = {};
  49. // ESM COMPAT FLAG
  50. __webpack_require__.r(__webpack_exports__);
  51. // EXPORTS
  52. __webpack_require__.d(__webpack_exports__, {
  53. __experimentalRichText: function() { return /* reexport */ __experimentalRichText; },
  54. __unstableCreateElement: function() { return /* reexport */ createElement; },
  55. __unstableFormatEdit: function() { return /* reexport */ FormatEdit; },
  56. __unstableToDom: function() { return /* reexport */ toDom; },
  57. __unstableUseRichText: function() { return /* reexport */ useRichText; },
  58. applyFormat: function() { return /* reexport */ applyFormat; },
  59. concat: function() { return /* reexport */ concat; },
  60. create: function() { return /* reexport */ create; },
  61. getActiveFormat: function() { return /* reexport */ getActiveFormat; },
  62. getActiveFormats: function() { return /* reexport */ getActiveFormats; },
  63. getActiveObject: function() { return /* reexport */ getActiveObject; },
  64. getTextContent: function() { return /* reexport */ getTextContent; },
  65. insert: function() { return /* reexport */ insert; },
  66. insertObject: function() { return /* reexport */ insertObject; },
  67. isCollapsed: function() { return /* reexport */ isCollapsed; },
  68. isEmpty: function() { return /* reexport */ isEmpty; },
  69. join: function() { return /* reexport */ join; },
  70. registerFormatType: function() { return /* reexport */ registerFormatType; },
  71. remove: function() { return /* reexport */ remove; },
  72. removeFormat: function() { return /* reexport */ removeFormat; },
  73. replace: function() { return /* reexport */ replace_replace; },
  74. slice: function() { return /* reexport */ slice; },
  75. split: function() { return /* reexport */ split; },
  76. store: function() { return /* reexport */ store; },
  77. toHTMLString: function() { return /* reexport */ toHTMLString; },
  78. toggleFormat: function() { return /* reexport */ toggleFormat; },
  79. unregisterFormatType: function() { return /* reexport */ unregisterFormatType; },
  80. useAnchor: function() { return /* reexport */ useAnchor; },
  81. useAnchorRef: function() { return /* reexport */ useAnchorRef; }
  82. });
  83. // NAMESPACE OBJECT: ./node_modules/@wordpress/rich-text/build-module/store/selectors.js
  84. var selectors_namespaceObject = {};
  85. __webpack_require__.r(selectors_namespaceObject);
  86. __webpack_require__.d(selectors_namespaceObject, {
  87. getFormatType: function() { return getFormatType; },
  88. getFormatTypeForBareElement: function() { return getFormatTypeForBareElement; },
  89. getFormatTypeForClassName: function() { return getFormatTypeForClassName; },
  90. getFormatTypes: function() { return getFormatTypes; }
  91. });
  92. // NAMESPACE OBJECT: ./node_modules/@wordpress/rich-text/build-module/store/actions.js
  93. var actions_namespaceObject = {};
  94. __webpack_require__.r(actions_namespaceObject);
  95. __webpack_require__.d(actions_namespaceObject, {
  96. addFormatTypes: function() { return addFormatTypes; },
  97. removeFormatTypes: function() { return removeFormatTypes; }
  98. });
  99. ;// CONCATENATED MODULE: external ["wp","data"]
  100. var external_wp_data_namespaceObject = window["wp"]["data"];
  101. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/store/reducer.js
  102. /**
  103. * WordPress dependencies
  104. */
  105. /**
  106. * Reducer managing the format types
  107. *
  108. * @param {Object} state Current state.
  109. * @param {Object} action Dispatched action.
  110. *
  111. * @return {Object} Updated state.
  112. */
  113. function formatTypes(state = {}, action) {
  114. switch (action.type) {
  115. case 'ADD_FORMAT_TYPES':
  116. return {
  117. ...state,
  118. // Key format types by their name.
  119. ...action.formatTypes.reduce((newFormatTypes, type) => ({
  120. ...newFormatTypes,
  121. [type.name]: type
  122. }), {})
  123. };
  124. case 'REMOVE_FORMAT_TYPES':
  125. return Object.fromEntries(Object.entries(state).filter(([key]) => !action.names.includes(key)));
  126. }
  127. return state;
  128. }
  129. /* harmony default export */ var reducer = ((0,external_wp_data_namespaceObject.combineReducers)({
  130. formatTypes
  131. }));
  132. ;// CONCATENATED MODULE: ./node_modules/rememo/rememo.js
  133. /** @typedef {(...args: any[]) => *[]} GetDependants */
  134. /** @typedef {() => void} Clear */
  135. /**
  136. * @typedef {{
  137. * getDependants: GetDependants,
  138. * clear: Clear
  139. * }} EnhancedSelector
  140. */
  141. /**
  142. * Internal cache entry.
  143. *
  144. * @typedef CacheNode
  145. *
  146. * @property {?CacheNode|undefined} [prev] Previous node.
  147. * @property {?CacheNode|undefined} [next] Next node.
  148. * @property {*[]} args Function arguments for cache entry.
  149. * @property {*} val Function result.
  150. */
  151. /**
  152. * @typedef Cache
  153. *
  154. * @property {Clear} clear Function to clear cache.
  155. * @property {boolean} [isUniqueByDependants] Whether dependants are valid in
  156. * considering cache uniqueness. A cache is unique if dependents are all arrays
  157. * or objects.
  158. * @property {CacheNode?} [head] Cache head.
  159. * @property {*[]} [lastDependants] Dependants from previous invocation.
  160. */
  161. /**
  162. * Arbitrary value used as key for referencing cache object in WeakMap tree.
  163. *
  164. * @type {{}}
  165. */
  166. var LEAF_KEY = {};
  167. /**
  168. * Returns the first argument as the sole entry in an array.
  169. *
  170. * @template T
  171. *
  172. * @param {T} value Value to return.
  173. *
  174. * @return {[T]} Value returned as entry in array.
  175. */
  176. function arrayOf(value) {
  177. return [value];
  178. }
  179. /**
  180. * Returns true if the value passed is object-like, or false otherwise. A value
  181. * is object-like if it can support property assignment, e.g. object or array.
  182. *
  183. * @param {*} value Value to test.
  184. *
  185. * @return {boolean} Whether value is object-like.
  186. */
  187. function isObjectLike(value) {
  188. return !!value && 'object' === typeof value;
  189. }
  190. /**
  191. * Creates and returns a new cache object.
  192. *
  193. * @return {Cache} Cache object.
  194. */
  195. function createCache() {
  196. /** @type {Cache} */
  197. var cache = {
  198. clear: function () {
  199. cache.head = null;
  200. },
  201. };
  202. return cache;
  203. }
  204. /**
  205. * Returns true if entries within the two arrays are strictly equal by
  206. * reference from a starting index.
  207. *
  208. * @param {*[]} a First array.
  209. * @param {*[]} b Second array.
  210. * @param {number} fromIndex Index from which to start comparison.
  211. *
  212. * @return {boolean} Whether arrays are shallowly equal.
  213. */
  214. function isShallowEqual(a, b, fromIndex) {
  215. var i;
  216. if (a.length !== b.length) {
  217. return false;
  218. }
  219. for (i = fromIndex; i < a.length; i++) {
  220. if (a[i] !== b[i]) {
  221. return false;
  222. }
  223. }
  224. return true;
  225. }
  226. /**
  227. * Returns a memoized selector function. The getDependants function argument is
  228. * called before the memoized selector and is expected to return an immutable
  229. * reference or array of references on which the selector depends for computing
  230. * its own return value. The memoize cache is preserved only as long as those
  231. * dependant references remain the same. If getDependants returns a different
  232. * reference(s), the cache is cleared and the selector value regenerated.
  233. *
  234. * @template {(...args: *[]) => *} S
  235. *
  236. * @param {S} selector Selector function.
  237. * @param {GetDependants=} getDependants Dependant getter returning an array of
  238. * references used in cache bust consideration.
  239. */
  240. /* harmony default export */ function rememo(selector, getDependants) {
  241. /** @type {WeakMap<*,*>} */
  242. var rootCache;
  243. /** @type {GetDependants} */
  244. var normalizedGetDependants = getDependants ? getDependants : arrayOf;
  245. /**
  246. * Returns the cache for a given dependants array. When possible, a WeakMap
  247. * will be used to create a unique cache for each set of dependants. This
  248. * is feasible due to the nature of WeakMap in allowing garbage collection
  249. * to occur on entries where the key object is no longer referenced. Since
  250. * WeakMap requires the key to be an object, this is only possible when the
  251. * dependant is object-like. The root cache is created as a hierarchy where
  252. * each top-level key is the first entry in a dependants set, the value a
  253. * WeakMap where each key is the next dependant, and so on. This continues
  254. * so long as the dependants are object-like. If no dependants are object-
  255. * like, then the cache is shared across all invocations.
  256. *
  257. * @see isObjectLike
  258. *
  259. * @param {*[]} dependants Selector dependants.
  260. *
  261. * @return {Cache} Cache object.
  262. */
  263. function getCache(dependants) {
  264. var caches = rootCache,
  265. isUniqueByDependants = true,
  266. i,
  267. dependant,
  268. map,
  269. cache;
  270. for (i = 0; i < dependants.length; i++) {
  271. dependant = dependants[i];
  272. // Can only compose WeakMap from object-like key.
  273. if (!isObjectLike(dependant)) {
  274. isUniqueByDependants = false;
  275. break;
  276. }
  277. // Does current segment of cache already have a WeakMap?
  278. if (caches.has(dependant)) {
  279. // Traverse into nested WeakMap.
  280. caches = caches.get(dependant);
  281. } else {
  282. // Create, set, and traverse into a new one.
  283. map = new WeakMap();
  284. caches.set(dependant, map);
  285. caches = map;
  286. }
  287. }
  288. // We use an arbitrary (but consistent) object as key for the last item
  289. // in the WeakMap to serve as our running cache.
  290. if (!caches.has(LEAF_KEY)) {
  291. cache = createCache();
  292. cache.isUniqueByDependants = isUniqueByDependants;
  293. caches.set(LEAF_KEY, cache);
  294. }
  295. return caches.get(LEAF_KEY);
  296. }
  297. /**
  298. * Resets root memoization cache.
  299. */
  300. function clear() {
  301. rootCache = new WeakMap();
  302. }
  303. /* eslint-disable jsdoc/check-param-names */
  304. /**
  305. * The augmented selector call, considering first whether dependants have
  306. * changed before passing it to underlying memoize function.
  307. *
  308. * @param {*} source Source object for derivation.
  309. * @param {...*} extraArgs Additional arguments to pass to selector.
  310. *
  311. * @return {*} Selector result.
  312. */
  313. /* eslint-enable jsdoc/check-param-names */
  314. function callSelector(/* source, ...extraArgs */) {
  315. var len = arguments.length,
  316. cache,
  317. node,
  318. i,
  319. args,
  320. dependants;
  321. // Create copy of arguments (avoid leaking deoptimization).
  322. args = new Array(len);
  323. for (i = 0; i < len; i++) {
  324. args[i] = arguments[i];
  325. }
  326. dependants = normalizedGetDependants.apply(null, args);
  327. cache = getCache(dependants);
  328. // If not guaranteed uniqueness by dependants (primitive type), shallow
  329. // compare against last dependants and, if references have changed,
  330. // destroy cache to recalculate result.
  331. if (!cache.isUniqueByDependants) {
  332. if (
  333. cache.lastDependants &&
  334. !isShallowEqual(dependants, cache.lastDependants, 0)
  335. ) {
  336. cache.clear();
  337. }
  338. cache.lastDependants = dependants;
  339. }
  340. node = cache.head;
  341. while (node) {
  342. // Check whether node arguments match arguments
  343. if (!isShallowEqual(node.args, args, 1)) {
  344. node = node.next;
  345. continue;
  346. }
  347. // At this point we can assume we've found a match
  348. // Surface matched node to head if not already
  349. if (node !== cache.head) {
  350. // Adjust siblings to point to each other.
  351. /** @type {CacheNode} */ (node.prev).next = node.next;
  352. if (node.next) {
  353. node.next.prev = node.prev;
  354. }
  355. node.next = cache.head;
  356. node.prev = null;
  357. /** @type {CacheNode} */ (cache.head).prev = node;
  358. cache.head = node;
  359. }
  360. // Return immediately
  361. return node.val;
  362. }
  363. // No cached value found. Continue to insertion phase:
  364. node = /** @type {CacheNode} */ ({
  365. // Generate the result from original function
  366. val: selector.apply(null, args),
  367. });
  368. // Avoid including the source object in the cache.
  369. args[0] = null;
  370. node.args = args;
  371. // Don't need to check whether node is already head, since it would
  372. // have been returned above already if it was
  373. // Shift existing head down list
  374. if (cache.head) {
  375. cache.head.prev = node;
  376. node.next = cache.head;
  377. }
  378. cache.head = node;
  379. return node.val;
  380. }
  381. callSelector.getDependants = normalizedGetDependants;
  382. callSelector.clear = clear;
  383. clear();
  384. return /** @type {S & EnhancedSelector} */ (callSelector);
  385. }
  386. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/store/selectors.js
  387. /**
  388. * External dependencies
  389. */
  390. /**
  391. * Returns all the available format types.
  392. *
  393. * @param {Object} state Data state.
  394. *
  395. * @example
  396. * ```js
  397. * import { __, sprintf } from '@wordpress/i18n';
  398. * import { store as richTextStore } from '@wordpress/rich-text';
  399. * import { useSelect } from '@wordpress/data';
  400. *
  401. * const ExampleComponent = () => {
  402. * const { getFormatTypes } = useSelect(
  403. * ( select ) => select( richTextStore ),
  404. * []
  405. * );
  406. *
  407. * const availableFormats = getFormatTypes();
  408. *
  409. * return availableFormats ? (
  410. * <ul>
  411. * { availableFormats?.map( ( format ) => (
  412. * <li>{ format.name }</li>
  413. * ) ) }
  414. * </ul>
  415. * ) : (
  416. * __( 'No Formats available' )
  417. * );
  418. * };
  419. * ```
  420. *
  421. * @return {Array} Format types.
  422. */
  423. const getFormatTypes = rememo(state => Object.values(state.formatTypes), state => [state.formatTypes]);
  424. /**
  425. * Returns a format type by name.
  426. *
  427. * @param {Object} state Data state.
  428. * @param {string} name Format type name.
  429. *
  430. * @example
  431. * ```js
  432. * import { __, sprintf } from '@wordpress/i18n';
  433. * import { store as richTextStore } from '@wordpress/rich-text';
  434. * import { useSelect } from '@wordpress/data';
  435. *
  436. * const ExampleComponent = () => {
  437. * const { getFormatType } = useSelect(
  438. * ( select ) => select( richTextStore ),
  439. * []
  440. * );
  441. *
  442. * const boldFormat = getFormatType( 'core/bold' );
  443. *
  444. * return boldFormat ? (
  445. * <ul>
  446. * { Object.entries( boldFormat )?.map( ( [ key, value ] ) => (
  447. * <li>
  448. * { key } : { value }
  449. * </li>
  450. * ) ) }
  451. * </ul>
  452. * ) : (
  453. * __( 'Not Found' )
  454. * ;
  455. * };
  456. * ```
  457. *
  458. * @return {Object?} Format type.
  459. */
  460. function getFormatType(state, name) {
  461. return state.formatTypes[name];
  462. }
  463. /**
  464. * Gets the format type, if any, that can handle a bare element (without a
  465. * data-format-type attribute), given the tag name of this element.
  466. *
  467. * @param {Object} state Data state.
  468. * @param {string} bareElementTagName The tag name of the element to find a
  469. * format type for.
  470. *
  471. * @example
  472. * ```js
  473. * import { __, sprintf } from '@wordpress/i18n';
  474. * import { store as richTextStore } from '@wordpress/rich-text';
  475. * import { useSelect } from '@wordpress/data';
  476. *
  477. * const ExampleComponent = () => {
  478. * const { getFormatTypeForBareElement } = useSelect(
  479. * ( select ) => select( richTextStore ),
  480. * []
  481. * );
  482. *
  483. * const format = getFormatTypeForBareElement( 'strong' );
  484. *
  485. * return format && <p>{ sprintf( __( 'Format name: %s' ), format.name ) }</p>;
  486. * }
  487. * ```
  488. *
  489. * @return {?Object} Format type.
  490. */
  491. function getFormatTypeForBareElement(state, bareElementTagName) {
  492. const formatTypes = getFormatTypes(state);
  493. return formatTypes.find(({
  494. className,
  495. tagName
  496. }) => {
  497. return className === null && bareElementTagName === tagName;
  498. }) || formatTypes.find(({
  499. className,
  500. tagName
  501. }) => {
  502. return className === null && '*' === tagName;
  503. });
  504. }
  505. /**
  506. * Gets the format type, if any, that can handle an element, given its classes.
  507. *
  508. * @param {Object} state Data state.
  509. * @param {string} elementClassName The classes of the element to find a format
  510. * type for.
  511. *
  512. * @example
  513. * ```js
  514. * import { __, sprintf } from '@wordpress/i18n';
  515. * import { store as richTextStore } from '@wordpress/rich-text';
  516. * import { useSelect } from '@wordpress/data';
  517. *
  518. * const ExampleComponent = () => {
  519. * const { getFormatTypeForClassName } = useSelect(
  520. * ( select ) => select( richTextStore ),
  521. * []
  522. * );
  523. *
  524. * const format = getFormatTypeForClassName( 'has-inline-color' );
  525. *
  526. * return format && <p>{ sprintf( __( 'Format name: %s' ), format.name ) }</p>;
  527. * };
  528. * ```
  529. *
  530. * @return {?Object} Format type.
  531. */
  532. function getFormatTypeForClassName(state, elementClassName) {
  533. return getFormatTypes(state).find(({
  534. className
  535. }) => {
  536. if (className === null) {
  537. return false;
  538. }
  539. return ` ${elementClassName} `.indexOf(` ${className} `) >= 0;
  540. });
  541. }
  542. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/store/actions.js
  543. /**
  544. * Returns an action object used in signalling that format types have been
  545. * added.
  546. * Ignored from documentation as registerFormatType should be used instead from @wordpress/rich-text
  547. *
  548. * @ignore
  549. *
  550. * @param {Array|Object} formatTypes Format types received.
  551. *
  552. * @return {Object} Action object.
  553. */
  554. function addFormatTypes(formatTypes) {
  555. return {
  556. type: 'ADD_FORMAT_TYPES',
  557. formatTypes: Array.isArray(formatTypes) ? formatTypes : [formatTypes]
  558. };
  559. }
  560. /**
  561. * Returns an action object used to remove a registered format type.
  562. *
  563. * Ignored from documentation as unregisterFormatType should be used instead from @wordpress/rich-text
  564. *
  565. * @ignore
  566. *
  567. * @param {string|Array} names Format name.
  568. *
  569. * @return {Object} Action object.
  570. */
  571. function removeFormatTypes(names) {
  572. return {
  573. type: 'REMOVE_FORMAT_TYPES',
  574. names: Array.isArray(names) ? names : [names]
  575. };
  576. }
  577. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/store/index.js
  578. /**
  579. * WordPress dependencies
  580. */
  581. /**
  582. * Internal dependencies
  583. */
  584. const STORE_NAME = 'core/rich-text';
  585. /**
  586. * Store definition for the rich-text namespace.
  587. *
  588. * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore
  589. *
  590. * @type {Object}
  591. */
  592. const store = (0,external_wp_data_namespaceObject.createReduxStore)(STORE_NAME, {
  593. reducer: reducer,
  594. selectors: selectors_namespaceObject,
  595. actions: actions_namespaceObject
  596. });
  597. (0,external_wp_data_namespaceObject.register)(store);
  598. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/is-format-equal.js
  599. /** @typedef {import('./types').RichTextFormat} RichTextFormat */
  600. /**
  601. * Optimised equality check for format objects.
  602. *
  603. * @param {?RichTextFormat} format1 Format to compare.
  604. * @param {?RichTextFormat} format2 Format to compare.
  605. *
  606. * @return {boolean} True if formats are equal, false if not.
  607. */
  608. function isFormatEqual(format1, format2) {
  609. // Both not defined.
  610. if (format1 === format2) {
  611. return true;
  612. }
  613. // Either not defined.
  614. if (!format1 || !format2) {
  615. return false;
  616. }
  617. if (format1.type !== format2.type) {
  618. return false;
  619. }
  620. const attributes1 = format1.attributes;
  621. const attributes2 = format2.attributes;
  622. // Both not defined.
  623. if (attributes1 === attributes2) {
  624. return true;
  625. }
  626. // Either not defined.
  627. if (!attributes1 || !attributes2) {
  628. return false;
  629. }
  630. const keys1 = Object.keys(attributes1);
  631. const keys2 = Object.keys(attributes2);
  632. if (keys1.length !== keys2.length) {
  633. return false;
  634. }
  635. const length = keys1.length;
  636. // Optimise for speed.
  637. for (let i = 0; i < length; i++) {
  638. const name = keys1[i];
  639. if (attributes1[name] !== attributes2[name]) {
  640. return false;
  641. }
  642. }
  643. return true;
  644. }
  645. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/normalise-formats.js
  646. /**
  647. * Internal dependencies
  648. */
  649. /** @typedef {import('./types').RichTextValue} RichTextValue */
  650. /**
  651. * Normalises formats: ensures subsequent adjacent equal formats have the same
  652. * reference.
  653. *
  654. * @param {RichTextValue} value Value to normalise formats of.
  655. *
  656. * @return {RichTextValue} New value with normalised formats.
  657. */
  658. function normaliseFormats(value) {
  659. const newFormats = value.formats.slice();
  660. newFormats.forEach((formatsAtIndex, index) => {
  661. const formatsAtPreviousIndex = newFormats[index - 1];
  662. if (formatsAtPreviousIndex) {
  663. const newFormatsAtIndex = formatsAtIndex.slice();
  664. newFormatsAtIndex.forEach((format, formatIndex) => {
  665. const previousFormat = formatsAtPreviousIndex[formatIndex];
  666. if (isFormatEqual(format, previousFormat)) {
  667. newFormatsAtIndex[formatIndex] = previousFormat;
  668. }
  669. });
  670. newFormats[index] = newFormatsAtIndex;
  671. }
  672. });
  673. return {
  674. ...value,
  675. formats: newFormats
  676. };
  677. }
  678. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/apply-format.js
  679. /**
  680. * Internal dependencies
  681. */
  682. /** @typedef {import('./types').RichTextValue} RichTextValue */
  683. /** @typedef {import('./types').RichTextFormat} RichTextFormat */
  684. function replace(array, index, value) {
  685. array = array.slice();
  686. array[index] = value;
  687. return array;
  688. }
  689. /**
  690. * Apply a format object to a Rich Text value from the given `startIndex` to the
  691. * given `endIndex`. Indices are retrieved from the selection if none are
  692. * provided.
  693. *
  694. * @param {RichTextValue} value Value to modify.
  695. * @param {RichTextFormat} format Format to apply.
  696. * @param {number} [startIndex] Start index.
  697. * @param {number} [endIndex] End index.
  698. *
  699. * @return {RichTextValue} A new value with the format applied.
  700. */
  701. function applyFormat(value, format, startIndex = value.start, endIndex = value.end) {
  702. const {
  703. formats,
  704. activeFormats
  705. } = value;
  706. const newFormats = formats.slice();
  707. // The selection is collapsed.
  708. if (startIndex === endIndex) {
  709. const startFormat = newFormats[startIndex]?.find(({
  710. type
  711. }) => type === format.type);
  712. // If the caret is at a format of the same type, expand start and end to
  713. // the edges of the format. This is useful to apply new attributes.
  714. if (startFormat) {
  715. const index = newFormats[startIndex].indexOf(startFormat);
  716. while (newFormats[startIndex] && newFormats[startIndex][index] === startFormat) {
  717. newFormats[startIndex] = replace(newFormats[startIndex], index, format);
  718. startIndex--;
  719. }
  720. endIndex++;
  721. while (newFormats[endIndex] && newFormats[endIndex][index] === startFormat) {
  722. newFormats[endIndex] = replace(newFormats[endIndex], index, format);
  723. endIndex++;
  724. }
  725. }
  726. } else {
  727. // Determine the highest position the new format can be inserted at.
  728. let position = +Infinity;
  729. for (let index = startIndex; index < endIndex; index++) {
  730. if (newFormats[index]) {
  731. newFormats[index] = newFormats[index].filter(({
  732. type
  733. }) => type !== format.type);
  734. const length = newFormats[index].length;
  735. if (length < position) {
  736. position = length;
  737. }
  738. } else {
  739. newFormats[index] = [];
  740. position = 0;
  741. }
  742. }
  743. for (let index = startIndex; index < endIndex; index++) {
  744. newFormats[index].splice(position, 0, format);
  745. }
  746. }
  747. return normaliseFormats({
  748. ...value,
  749. formats: newFormats,
  750. // Always revise active formats. This serves as a placeholder for new
  751. // inputs with the format so new input appears with the format applied,
  752. // and ensures a format of the same type uses the latest values.
  753. activeFormats: [...(activeFormats?.filter(({
  754. type
  755. }) => type !== format.type) || []), format]
  756. });
  757. }
  758. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/create-element.js
  759. /**
  760. * Parse the given HTML into a body element.
  761. *
  762. * Note: The current implementation will return a shared reference, reset on
  763. * each call to `createElement`. Therefore, you should not hold a reference to
  764. * the value to operate upon asynchronously, as it may have unexpected results.
  765. *
  766. * @param {HTMLDocument} document The HTML document to use to parse.
  767. * @param {string} html The HTML to parse.
  768. *
  769. * @return {HTMLBodyElement} Body element with parsed HTML.
  770. */
  771. function createElement({
  772. implementation
  773. }, html) {
  774. // Because `createHTMLDocument` is an expensive operation, and with this
  775. // function being internal to `rich-text` (full control in avoiding a risk
  776. // of asynchronous operations on the shared reference), a single document
  777. // is reused and reset for each call to the function.
  778. if (!createElement.body) {
  779. createElement.body = implementation.createHTMLDocument('').body;
  780. }
  781. createElement.body.innerHTML = html;
  782. return createElement.body;
  783. }
  784. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/special-characters.js
  785. /**
  786. * Object replacement character, used as a placeholder for objects.
  787. */
  788. const OBJECT_REPLACEMENT_CHARACTER = '\ufffc';
  789. /**
  790. * Zero width non-breaking space, used as padding in the editable DOM tree when
  791. * it is empty otherwise.
  792. */
  793. const ZWNBSP = '\ufeff';
  794. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/create.js
  795. /**
  796. * WordPress dependencies
  797. */
  798. /**
  799. * Internal dependencies
  800. */
  801. /** @typedef {import('./types').RichTextValue} RichTextValue */
  802. function createEmptyValue() {
  803. return {
  804. formats: [],
  805. replacements: [],
  806. text: ''
  807. };
  808. }
  809. function toFormat({
  810. tagName,
  811. attributes
  812. }) {
  813. let formatType;
  814. if (attributes && attributes.class) {
  815. formatType = (0,external_wp_data_namespaceObject.select)(store).getFormatTypeForClassName(attributes.class);
  816. if (formatType) {
  817. // Preserve any additional classes.
  818. attributes.class = ` ${attributes.class} `.replace(` ${formatType.className} `, ' ').trim();
  819. if (!attributes.class) {
  820. delete attributes.class;
  821. }
  822. }
  823. }
  824. if (!formatType) {
  825. formatType = (0,external_wp_data_namespaceObject.select)(store).getFormatTypeForBareElement(tagName);
  826. }
  827. if (!formatType) {
  828. return attributes ? {
  829. type: tagName,
  830. attributes
  831. } : {
  832. type: tagName
  833. };
  834. }
  835. if (formatType.__experimentalCreatePrepareEditableTree && !formatType.__experimentalCreateOnChangeEditableValue) {
  836. return null;
  837. }
  838. if (!attributes) {
  839. return {
  840. formatType,
  841. type: formatType.name,
  842. tagName
  843. };
  844. }
  845. const registeredAttributes = {};
  846. const unregisteredAttributes = {};
  847. const _attributes = {
  848. ...attributes
  849. };
  850. for (const key in formatType.attributes) {
  851. const name = formatType.attributes[key];
  852. registeredAttributes[key] = _attributes[name];
  853. if (formatType.__unstableFilterAttributeValue) {
  854. registeredAttributes[key] = formatType.__unstableFilterAttributeValue(key, registeredAttributes[key]);
  855. }
  856. // delete the attribute and what's left is considered
  857. // to be unregistered.
  858. delete _attributes[name];
  859. if (typeof registeredAttributes[key] === 'undefined') {
  860. delete registeredAttributes[key];
  861. }
  862. }
  863. for (const name in _attributes) {
  864. unregisteredAttributes[name] = attributes[name];
  865. }
  866. if (formatType.contentEditable === false) {
  867. delete unregisteredAttributes.contenteditable;
  868. }
  869. return {
  870. formatType,
  871. type: formatType.name,
  872. tagName,
  873. attributes: registeredAttributes,
  874. unregisteredAttributes
  875. };
  876. }
  877. /**
  878. * Create a RichText value from an `Element` tree (DOM), an HTML string or a
  879. * plain text string, with optionally a `Range` object to set the selection. If
  880. * called without any input, an empty value will be created. The optional
  881. * functions can be used to filter out content.
  882. *
  883. * A value will have the following shape, which you are strongly encouraged not
  884. * to modify without the use of helper functions:
  885. *
  886. * ```js
  887. * {
  888. * text: string,
  889. * formats: Array,
  890. * replacements: Array,
  891. * ?start: number,
  892. * ?end: number,
  893. * }
  894. * ```
  895. *
  896. * As you can see, text and formatting are separated. `text` holds the text,
  897. * including any replacement characters for objects and lines. `formats`,
  898. * `objects` and `lines` are all sparse arrays of the same length as `text`. It
  899. * holds information about the formatting at the relevant text indices. Finally
  900. * `start` and `end` state which text indices are selected. They are only
  901. * provided if a `Range` was given.
  902. *
  903. * @param {Object} [$1] Optional named arguments.
  904. * @param {Element} [$1.element] Element to create value from.
  905. * @param {string} [$1.text] Text to create value from.
  906. * @param {string} [$1.html] HTML to create value from.
  907. * @param {Range} [$1.range] Range to create value from.
  908. * @param {boolean} [$1.preserveWhiteSpace] Whether or not to collapse
  909. * white space characters.
  910. * @param {boolean} [$1.__unstableIsEditableTree]
  911. *
  912. * @return {RichTextValue} A rich text value.
  913. */
  914. function create({
  915. element,
  916. text,
  917. html,
  918. range,
  919. __unstableIsEditableTree: isEditableTree,
  920. preserveWhiteSpace
  921. } = {}) {
  922. if (typeof text === 'string' && text.length > 0) {
  923. return {
  924. formats: Array(text.length),
  925. replacements: Array(text.length),
  926. text
  927. };
  928. }
  929. if (typeof html === 'string' && html.length > 0) {
  930. // It does not matter which document this is, we're just using it to
  931. // parse.
  932. element = createElement(document, html);
  933. }
  934. if (typeof element !== 'object') {
  935. return createEmptyValue();
  936. }
  937. return createFromElement({
  938. element,
  939. range,
  940. isEditableTree,
  941. preserveWhiteSpace
  942. });
  943. }
  944. /**
  945. * Helper to accumulate the value's selection start and end from the current
  946. * node and range.
  947. *
  948. * @param {Object} accumulator Object to accumulate into.
  949. * @param {Node} node Node to create value with.
  950. * @param {Range} range Range to create value with.
  951. * @param {Object} value Value that is being accumulated.
  952. */
  953. function accumulateSelection(accumulator, node, range, value) {
  954. if (!range) {
  955. return;
  956. }
  957. const {
  958. parentNode
  959. } = node;
  960. const {
  961. startContainer,
  962. startOffset,
  963. endContainer,
  964. endOffset
  965. } = range;
  966. const currentLength = accumulator.text.length;
  967. // Selection can be extracted from value.
  968. if (value.start !== undefined) {
  969. accumulator.start = currentLength + value.start;
  970. // Range indicates that the current node has selection.
  971. } else if (node === startContainer && node.nodeType === node.TEXT_NODE) {
  972. accumulator.start = currentLength + startOffset;
  973. // Range indicates that the current node is selected.
  974. } else if (parentNode === startContainer && node === startContainer.childNodes[startOffset]) {
  975. accumulator.start = currentLength;
  976. // Range indicates that the selection is after the current node.
  977. } else if (parentNode === startContainer && node === startContainer.childNodes[startOffset - 1]) {
  978. accumulator.start = currentLength + value.text.length;
  979. // Fallback if no child inside handled the selection.
  980. } else if (node === startContainer) {
  981. accumulator.start = currentLength;
  982. }
  983. // Selection can be extracted from value.
  984. if (value.end !== undefined) {
  985. accumulator.end = currentLength + value.end;
  986. // Range indicates that the current node has selection.
  987. } else if (node === endContainer && node.nodeType === node.TEXT_NODE) {
  988. accumulator.end = currentLength + endOffset;
  989. // Range indicates that the current node is selected.
  990. } else if (parentNode === endContainer && node === endContainer.childNodes[endOffset - 1]) {
  991. accumulator.end = currentLength + value.text.length;
  992. // Range indicates that the selection is before the current node.
  993. } else if (parentNode === endContainer && node === endContainer.childNodes[endOffset]) {
  994. accumulator.end = currentLength;
  995. // Fallback if no child inside handled the selection.
  996. } else if (node === endContainer) {
  997. accumulator.end = currentLength + endOffset;
  998. }
  999. }
  1000. /**
  1001. * Adjusts the start and end offsets from a range based on a text filter.
  1002. *
  1003. * @param {Node} node Node of which the text should be filtered.
  1004. * @param {Range} range The range to filter.
  1005. * @param {Function} filter Function to use to filter the text.
  1006. *
  1007. * @return {Object|void} Object containing range properties.
  1008. */
  1009. function filterRange(node, range, filter) {
  1010. if (!range) {
  1011. return;
  1012. }
  1013. const {
  1014. startContainer,
  1015. endContainer
  1016. } = range;
  1017. let {
  1018. startOffset,
  1019. endOffset
  1020. } = range;
  1021. if (node === startContainer) {
  1022. startOffset = filter(node.nodeValue.slice(0, startOffset)).length;
  1023. }
  1024. if (node === endContainer) {
  1025. endOffset = filter(node.nodeValue.slice(0, endOffset)).length;
  1026. }
  1027. return {
  1028. startContainer,
  1029. startOffset,
  1030. endContainer,
  1031. endOffset
  1032. };
  1033. }
  1034. /**
  1035. * Collapse any whitespace used for HTML formatting to one space character,
  1036. * because it will also be displayed as such by the browser.
  1037. *
  1038. * @param {string} string
  1039. */
  1040. function collapseWhiteSpace(string) {
  1041. return string.replace(/[\n\r\t]+/g, ' ');
  1042. }
  1043. /**
  1044. * Removes reserved characters used by rich-text (zero width non breaking spaces added by `toTree` and object replacement characters).
  1045. *
  1046. * @param {string} string
  1047. */
  1048. function removeReservedCharacters(string) {
  1049. // with the global flag, note that we should create a new regex each time OR reset lastIndex state.
  1050. return string.replace(new RegExp(`[${ZWNBSP}${OBJECT_REPLACEMENT_CHARACTER}]`, 'gu'), '');
  1051. }
  1052. /**
  1053. * Creates a Rich Text value from a DOM element and range.
  1054. *
  1055. * @param {Object} $1 Named argements.
  1056. * @param {Element} [$1.element] Element to create value from.
  1057. * @param {Range} [$1.range] Range to create value from.
  1058. * @param {boolean} [$1.preserveWhiteSpace] Whether or not to collapse white
  1059. * space characters.
  1060. * @param {boolean} [$1.isEditableTree]
  1061. *
  1062. * @return {RichTextValue} A rich text value.
  1063. */
  1064. function createFromElement({
  1065. element,
  1066. range,
  1067. isEditableTree,
  1068. preserveWhiteSpace
  1069. }) {
  1070. const accumulator = createEmptyValue();
  1071. if (!element) {
  1072. return accumulator;
  1073. }
  1074. if (!element.hasChildNodes()) {
  1075. accumulateSelection(accumulator, element, range, createEmptyValue());
  1076. return accumulator;
  1077. }
  1078. const length = element.childNodes.length;
  1079. // Optimise for speed.
  1080. for (let index = 0; index < length; index++) {
  1081. const node = element.childNodes[index];
  1082. const tagName = node.nodeName.toLowerCase();
  1083. if (node.nodeType === node.TEXT_NODE) {
  1084. let filter = removeReservedCharacters;
  1085. if (!preserveWhiteSpace) {
  1086. filter = string => removeReservedCharacters(collapseWhiteSpace(string));
  1087. }
  1088. const text = filter(node.nodeValue);
  1089. range = filterRange(node, range, filter);
  1090. accumulateSelection(accumulator, node, range, {
  1091. text
  1092. });
  1093. // Create a sparse array of the same length as `text`, in which
  1094. // formats can be added.
  1095. accumulator.formats.length += text.length;
  1096. accumulator.replacements.length += text.length;
  1097. accumulator.text += text;
  1098. continue;
  1099. }
  1100. if (node.nodeType !== node.ELEMENT_NODE) {
  1101. continue;
  1102. }
  1103. if (isEditableTree && (
  1104. // Ignore any placeholders.
  1105. node.getAttribute('data-rich-text-placeholder') ||
  1106. // Ignore any line breaks that are not inserted by us.
  1107. tagName === 'br' && !node.getAttribute('data-rich-text-line-break'))) {
  1108. accumulateSelection(accumulator, node, range, createEmptyValue());
  1109. continue;
  1110. }
  1111. if (tagName === 'script') {
  1112. const value = {
  1113. formats: [,],
  1114. replacements: [{
  1115. type: tagName,
  1116. attributes: {
  1117. 'data-rich-text-script': node.getAttribute('data-rich-text-script') || encodeURIComponent(node.innerHTML)
  1118. }
  1119. }],
  1120. text: OBJECT_REPLACEMENT_CHARACTER
  1121. };
  1122. accumulateSelection(accumulator, node, range, value);
  1123. mergePair(accumulator, value);
  1124. continue;
  1125. }
  1126. if (tagName === 'br') {
  1127. accumulateSelection(accumulator, node, range, createEmptyValue());
  1128. mergePair(accumulator, create({
  1129. text: '\n'
  1130. }));
  1131. continue;
  1132. }
  1133. const format = toFormat({
  1134. tagName,
  1135. attributes: getAttributes({
  1136. element: node
  1137. })
  1138. });
  1139. // When a format type is declared as not editable, replace it with an
  1140. // object replacement character and preserve the inner HTML.
  1141. if (format?.formatType?.contentEditable === false) {
  1142. delete format.formatType;
  1143. accumulateSelection(accumulator, node, range, createEmptyValue());
  1144. mergePair(accumulator, {
  1145. formats: [,],
  1146. replacements: [{
  1147. ...format,
  1148. innerHTML: node.innerHTML
  1149. }],
  1150. text: OBJECT_REPLACEMENT_CHARACTER
  1151. });
  1152. continue;
  1153. }
  1154. if (format) delete format.formatType;
  1155. const value = createFromElement({
  1156. element: node,
  1157. range,
  1158. isEditableTree,
  1159. preserveWhiteSpace
  1160. });
  1161. accumulateSelection(accumulator, node, range, value);
  1162. if (!format) {
  1163. mergePair(accumulator, value);
  1164. } else if (value.text.length === 0) {
  1165. if (format.attributes) {
  1166. mergePair(accumulator, {
  1167. formats: [,],
  1168. replacements: [format],
  1169. text: OBJECT_REPLACEMENT_CHARACTER
  1170. });
  1171. }
  1172. } else {
  1173. // Indices should share a reference to the same formats array.
  1174. // Only create a new reference if `formats` changes.
  1175. function mergeFormats(formats) {
  1176. if (mergeFormats.formats === formats) {
  1177. return mergeFormats.newFormats;
  1178. }
  1179. const newFormats = formats ? [format, ...formats] : [format];
  1180. mergeFormats.formats = formats;
  1181. mergeFormats.newFormats = newFormats;
  1182. return newFormats;
  1183. }
  1184. // Since the formats parameter can be `undefined`, preset
  1185. // `mergeFormats` with a new reference.
  1186. mergeFormats.newFormats = [format];
  1187. mergePair(accumulator, {
  1188. ...value,
  1189. formats: Array.from(value.formats, mergeFormats)
  1190. });
  1191. }
  1192. }
  1193. return accumulator;
  1194. }
  1195. /**
  1196. * Gets the attributes of an element in object shape.
  1197. *
  1198. * @param {Object} $1 Named argements.
  1199. * @param {Element} $1.element Element to get attributes from.
  1200. *
  1201. * @return {Object|void} Attribute object or `undefined` if the element has no
  1202. * attributes.
  1203. */
  1204. function getAttributes({
  1205. element
  1206. }) {
  1207. if (!element.hasAttributes()) {
  1208. return;
  1209. }
  1210. const length = element.attributes.length;
  1211. let accumulator;
  1212. // Optimise for speed.
  1213. for (let i = 0; i < length; i++) {
  1214. const {
  1215. name,
  1216. value
  1217. } = element.attributes[i];
  1218. if (name.indexOf('data-rich-text-') === 0) {
  1219. continue;
  1220. }
  1221. const safeName = /^on/i.test(name) ? 'data-disable-rich-text-' + name : name;
  1222. accumulator = accumulator || {};
  1223. accumulator[safeName] = value;
  1224. }
  1225. return accumulator;
  1226. }
  1227. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/concat.js
  1228. /**
  1229. * Internal dependencies
  1230. */
  1231. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1232. /**
  1233. * Concats a pair of rich text values. Not that this mutates `a` and does NOT
  1234. * normalise formats!
  1235. *
  1236. * @param {Object} a Value to mutate.
  1237. * @param {Object} b Value to add read from.
  1238. *
  1239. * @return {Object} `a`, mutated.
  1240. */
  1241. function mergePair(a, b) {
  1242. a.formats = a.formats.concat(b.formats);
  1243. a.replacements = a.replacements.concat(b.replacements);
  1244. a.text += b.text;
  1245. return a;
  1246. }
  1247. /**
  1248. * Combine all Rich Text values into one. This is similar to
  1249. * `String.prototype.concat`.
  1250. *
  1251. * @param {...RichTextValue} values Objects to combine.
  1252. *
  1253. * @return {RichTextValue} A new value combining all given records.
  1254. */
  1255. function concat(...values) {
  1256. return normaliseFormats(values.reduce(mergePair, create()));
  1257. }
  1258. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/get-active-formats.js
  1259. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1260. /** @typedef {import('./types').RichTextFormatList} RichTextFormatList */
  1261. /**
  1262. * Internal dependencies
  1263. */
  1264. /**
  1265. * Gets the all format objects at the start of the selection.
  1266. *
  1267. * @param {RichTextValue} value Value to inspect.
  1268. * @param {Array} EMPTY_ACTIVE_FORMATS Array to return if there are no
  1269. * active formats.
  1270. *
  1271. * @return {RichTextFormatList} Active format objects.
  1272. */
  1273. function getActiveFormats(value, EMPTY_ACTIVE_FORMATS = []) {
  1274. const {
  1275. formats,
  1276. start,
  1277. end,
  1278. activeFormats
  1279. } = value;
  1280. if (start === undefined) {
  1281. return EMPTY_ACTIVE_FORMATS;
  1282. }
  1283. if (start === end) {
  1284. // For a collapsed caret, it is possible to override the active formats.
  1285. if (activeFormats) {
  1286. return activeFormats;
  1287. }
  1288. const formatsBefore = formats[start - 1] || EMPTY_ACTIVE_FORMATS;
  1289. const formatsAfter = formats[start] || EMPTY_ACTIVE_FORMATS;
  1290. // By default, select the lowest amount of formats possible (which means
  1291. // the caret is positioned outside the format boundary). The user can
  1292. // then use arrow keys to define `activeFormats`.
  1293. if (formatsBefore.length < formatsAfter.length) {
  1294. return formatsBefore;
  1295. }
  1296. return formatsAfter;
  1297. }
  1298. // If there's no formats at the start index, there are not active formats.
  1299. if (!formats[start]) {
  1300. return EMPTY_ACTIVE_FORMATS;
  1301. }
  1302. const selectedFormats = formats.slice(start, end);
  1303. // Clone the formats so we're not mutating the live value.
  1304. const _activeFormats = [...selectedFormats[0]];
  1305. let i = selectedFormats.length;
  1306. // For performance reasons, start from the end where it's much quicker to
  1307. // realise that there are no active formats.
  1308. while (i--) {
  1309. const formatsAtIndex = selectedFormats[i];
  1310. // If we run into any index without formats, we're sure that there's no
  1311. // active formats.
  1312. if (!formatsAtIndex) {
  1313. return EMPTY_ACTIVE_FORMATS;
  1314. }
  1315. let ii = _activeFormats.length;
  1316. // Loop over the active formats and remove any that are not present at
  1317. // the current index.
  1318. while (ii--) {
  1319. const format = _activeFormats[ii];
  1320. if (!formatsAtIndex.find(_format => isFormatEqual(format, _format))) {
  1321. _activeFormats.splice(ii, 1);
  1322. }
  1323. }
  1324. // If there are no active formats, we can stop.
  1325. if (_activeFormats.length === 0) {
  1326. return EMPTY_ACTIVE_FORMATS;
  1327. }
  1328. }
  1329. return _activeFormats || EMPTY_ACTIVE_FORMATS;
  1330. }
  1331. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/get-active-format.js
  1332. /**
  1333. * Internal dependencies
  1334. */
  1335. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1336. /** @typedef {import('./types').RichTextFormat} RichTextFormat */
  1337. /**
  1338. * Gets the format object by type at the start of the selection. This can be
  1339. * used to get e.g. the URL of a link format at the current selection, but also
  1340. * to check if a format is active at the selection. Returns undefined if there
  1341. * is no format at the selection.
  1342. *
  1343. * @param {RichTextValue} value Value to inspect.
  1344. * @param {string} formatType Format type to look for.
  1345. *
  1346. * @return {RichTextFormat|undefined} Active format object of the specified
  1347. * type, or undefined.
  1348. */
  1349. function getActiveFormat(value, formatType) {
  1350. return getActiveFormats(value).find(({
  1351. type
  1352. }) => type === formatType);
  1353. }
  1354. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/get-active-object.js
  1355. /**
  1356. * Internal dependencies
  1357. */
  1358. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1359. /** @typedef {import('./types').RichTextFormat} RichTextFormat */
  1360. /**
  1361. * Gets the active object, if there is any.
  1362. *
  1363. * @param {RichTextValue} value Value to inspect.
  1364. *
  1365. * @return {RichTextFormat|void} Active object, or undefined.
  1366. */
  1367. function getActiveObject({
  1368. start,
  1369. end,
  1370. replacements,
  1371. text
  1372. }) {
  1373. if (start + 1 !== end || text[start] !== OBJECT_REPLACEMENT_CHARACTER) {
  1374. return;
  1375. }
  1376. return replacements[start];
  1377. }
  1378. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/get-text-content.js
  1379. /**
  1380. * Internal dependencies
  1381. */
  1382. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1383. /**
  1384. * Get the textual content of a Rich Text value. This is similar to
  1385. * `Element.textContent`.
  1386. *
  1387. * @param {RichTextValue} value Value to use.
  1388. *
  1389. * @return {string} The text content.
  1390. */
  1391. function getTextContent({
  1392. text
  1393. }) {
  1394. return text.replace(OBJECT_REPLACEMENT_CHARACTER, '');
  1395. }
  1396. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/is-collapsed.js
  1397. /**
  1398. * Internal dependencies
  1399. */
  1400. /**
  1401. * Check if the selection of a Rich Text value is collapsed or not. Collapsed
  1402. * means that no characters are selected, but there is a caret present. If there
  1403. * is no selection, `undefined` will be returned. This is similar to
  1404. * `window.getSelection().isCollapsed()`.
  1405. *
  1406. * @param props The rich text value to check.
  1407. * @param props.start
  1408. * @param props.end
  1409. * @return True if the selection is collapsed, false if not, undefined if there is no selection.
  1410. */
  1411. function isCollapsed({
  1412. start,
  1413. end
  1414. }) {
  1415. if (start === undefined || end === undefined) {
  1416. return;
  1417. }
  1418. return start === end;
  1419. }
  1420. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/is-empty.js
  1421. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1422. /**
  1423. * Check if a Rich Text value is Empty, meaning it contains no text or any
  1424. * objects (such as images).
  1425. *
  1426. * @param {RichTextValue} value Value to use.
  1427. *
  1428. * @return {boolean} True if the value is empty, false if not.
  1429. */
  1430. function isEmpty({
  1431. text
  1432. }) {
  1433. return text.length === 0;
  1434. }
  1435. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/join.js
  1436. /**
  1437. * Internal dependencies
  1438. */
  1439. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1440. /**
  1441. * Combine an array of Rich Text values into one, optionally separated by
  1442. * `separator`, which can be a Rich Text value, HTML string, or plain text
  1443. * string. This is similar to `Array.prototype.join`.
  1444. *
  1445. * @param {Array<RichTextValue>} values An array of values to join.
  1446. * @param {string|RichTextValue} [separator] Separator string or value.
  1447. *
  1448. * @return {RichTextValue} A new combined value.
  1449. */
  1450. function join(values, separator = '') {
  1451. if (typeof separator === 'string') {
  1452. separator = create({
  1453. text: separator
  1454. });
  1455. }
  1456. return normaliseFormats(values.reduce((accumlator, {
  1457. formats,
  1458. replacements,
  1459. text
  1460. }) => ({
  1461. formats: accumlator.formats.concat(separator.formats, formats),
  1462. replacements: accumlator.replacements.concat(separator.replacements, replacements),
  1463. text: accumlator.text + separator.text + text
  1464. })));
  1465. }
  1466. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/register-format-type.js
  1467. /**
  1468. * WordPress dependencies
  1469. */
  1470. /**
  1471. * Internal dependencies
  1472. */
  1473. /**
  1474. * @typedef {Object} WPFormat
  1475. *
  1476. * @property {string} name A string identifying the format. Must be
  1477. * unique across all registered formats.
  1478. * @property {string} tagName The HTML tag this format will wrap the
  1479. * selection with.
  1480. * @property {boolean} interactive Whether format makes content interactive or not.
  1481. * @property {string | null} [className] A class to match the format.
  1482. * @property {string} title Name of the format.
  1483. * @property {Function} edit Should return a component for the user to
  1484. * interact with the new registered format.
  1485. */
  1486. /**
  1487. * Registers a new format provided a unique name and an object defining its
  1488. * behavior.
  1489. *
  1490. * @param {string} name Format name.
  1491. * @param {WPFormat} settings Format settings.
  1492. *
  1493. * @return {WPFormat|undefined} The format, if it has been successfully
  1494. * registered; otherwise `undefined`.
  1495. */
  1496. function registerFormatType(name, settings) {
  1497. settings = {
  1498. name,
  1499. ...settings
  1500. };
  1501. if (typeof settings.name !== 'string') {
  1502. window.console.error('Format names must be strings.');
  1503. return;
  1504. }
  1505. if (!/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test(settings.name)) {
  1506. window.console.error('Format names must contain a namespace prefix, include only lowercase alphanumeric characters or dashes, and start with a letter. Example: my-plugin/my-custom-format');
  1507. return;
  1508. }
  1509. if ((0,external_wp_data_namespaceObject.select)(store).getFormatType(settings.name)) {
  1510. window.console.error('Format "' + settings.name + '" is already registered.');
  1511. return;
  1512. }
  1513. if (typeof settings.tagName !== 'string' || settings.tagName === '') {
  1514. window.console.error('Format tag names must be a string.');
  1515. return;
  1516. }
  1517. if ((typeof settings.className !== 'string' || settings.className === '') && settings.className !== null) {
  1518. window.console.error('Format class names must be a string, or null to handle bare elements.');
  1519. return;
  1520. }
  1521. if (!/^[_a-zA-Z]+[a-zA-Z0-9-]*$/.test(settings.className)) {
  1522. window.console.error('A class name must begin with a letter, followed by any number of hyphens, letters, or numbers.');
  1523. return;
  1524. }
  1525. if (settings.className === null) {
  1526. const formatTypeForBareElement = (0,external_wp_data_namespaceObject.select)(store).getFormatTypeForBareElement(settings.tagName);
  1527. if (formatTypeForBareElement && formatTypeForBareElement.name !== 'core/unknown') {
  1528. window.console.error(`Format "${formatTypeForBareElement.name}" is already registered to handle bare tag name "${settings.tagName}".`);
  1529. return;
  1530. }
  1531. } else {
  1532. const formatTypeForClassName = (0,external_wp_data_namespaceObject.select)(store).getFormatTypeForClassName(settings.className);
  1533. if (formatTypeForClassName) {
  1534. window.console.error(`Format "${formatTypeForClassName.name}" is already registered to handle class name "${settings.className}".`);
  1535. return;
  1536. }
  1537. }
  1538. if (!('title' in settings) || settings.title === '') {
  1539. window.console.error('The format "' + settings.name + '" must have a title.');
  1540. return;
  1541. }
  1542. if ('keywords' in settings && settings.keywords.length > 3) {
  1543. window.console.error('The format "' + settings.name + '" can have a maximum of 3 keywords.');
  1544. return;
  1545. }
  1546. if (typeof settings.title !== 'string') {
  1547. window.console.error('Format titles must be strings.');
  1548. return;
  1549. }
  1550. (0,external_wp_data_namespaceObject.dispatch)(store).addFormatTypes(settings);
  1551. return settings;
  1552. }
  1553. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/remove-format.js
  1554. /**
  1555. * Internal dependencies
  1556. */
  1557. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1558. /**
  1559. * Remove any format object from a Rich Text value by type from the given
  1560. * `startIndex` to the given `endIndex`. Indices are retrieved from the
  1561. * selection if none are provided.
  1562. *
  1563. * @param {RichTextValue} value Value to modify.
  1564. * @param {string} formatType Format type to remove.
  1565. * @param {number} [startIndex] Start index.
  1566. * @param {number} [endIndex] End index.
  1567. *
  1568. * @return {RichTextValue} A new value with the format applied.
  1569. */
  1570. function removeFormat(value, formatType, startIndex = value.start, endIndex = value.end) {
  1571. const {
  1572. formats,
  1573. activeFormats
  1574. } = value;
  1575. const newFormats = formats.slice();
  1576. // If the selection is collapsed, expand start and end to the edges of the
  1577. // format.
  1578. if (startIndex === endIndex) {
  1579. const format = newFormats[startIndex]?.find(({
  1580. type
  1581. }) => type === formatType);
  1582. if (format) {
  1583. while (newFormats[startIndex]?.find(newFormat => newFormat === format)) {
  1584. filterFormats(newFormats, startIndex, formatType);
  1585. startIndex--;
  1586. }
  1587. endIndex++;
  1588. while (newFormats[endIndex]?.find(newFormat => newFormat === format)) {
  1589. filterFormats(newFormats, endIndex, formatType);
  1590. endIndex++;
  1591. }
  1592. }
  1593. } else {
  1594. for (let i = startIndex; i < endIndex; i++) {
  1595. if (newFormats[i]) {
  1596. filterFormats(newFormats, i, formatType);
  1597. }
  1598. }
  1599. }
  1600. return normaliseFormats({
  1601. ...value,
  1602. formats: newFormats,
  1603. activeFormats: activeFormats?.filter(({
  1604. type
  1605. }) => type !== formatType) || []
  1606. });
  1607. }
  1608. function filterFormats(formats, index, formatType) {
  1609. const newFormats = formats[index].filter(({
  1610. type
  1611. }) => type !== formatType);
  1612. if (newFormats.length) {
  1613. formats[index] = newFormats;
  1614. } else {
  1615. delete formats[index];
  1616. }
  1617. }
  1618. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/insert.js
  1619. /**
  1620. * Internal dependencies
  1621. */
  1622. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1623. /**
  1624. * Insert a Rich Text value, an HTML string, or a plain text string, into a
  1625. * Rich Text value at the given `startIndex`. Any content between `startIndex`
  1626. * and `endIndex` will be removed. Indices are retrieved from the selection if
  1627. * none are provided.
  1628. *
  1629. * @param {RichTextValue} value Value to modify.
  1630. * @param {RichTextValue|string} valueToInsert Value to insert.
  1631. * @param {number} [startIndex] Start index.
  1632. * @param {number} [endIndex] End index.
  1633. *
  1634. * @return {RichTextValue} A new value with the value inserted.
  1635. */
  1636. function insert(value, valueToInsert, startIndex = value.start, endIndex = value.end) {
  1637. const {
  1638. formats,
  1639. replacements,
  1640. text
  1641. } = value;
  1642. if (typeof valueToInsert === 'string') {
  1643. valueToInsert = create({
  1644. text: valueToInsert
  1645. });
  1646. }
  1647. const index = startIndex + valueToInsert.text.length;
  1648. return normaliseFormats({
  1649. formats: formats.slice(0, startIndex).concat(valueToInsert.formats, formats.slice(endIndex)),
  1650. replacements: replacements.slice(0, startIndex).concat(valueToInsert.replacements, replacements.slice(endIndex)),
  1651. text: text.slice(0, startIndex) + valueToInsert.text + text.slice(endIndex),
  1652. start: index,
  1653. end: index
  1654. });
  1655. }
  1656. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/remove.js
  1657. /**
  1658. * Internal dependencies
  1659. */
  1660. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1661. /**
  1662. * Remove content from a Rich Text value between the given `startIndex` and
  1663. * `endIndex`. Indices are retrieved from the selection if none are provided.
  1664. *
  1665. * @param {RichTextValue} value Value to modify.
  1666. * @param {number} [startIndex] Start index.
  1667. * @param {number} [endIndex] End index.
  1668. *
  1669. * @return {RichTextValue} A new value with the content removed.
  1670. */
  1671. function remove(value, startIndex, endIndex) {
  1672. return insert(value, create(), startIndex, endIndex);
  1673. }
  1674. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/replace.js
  1675. /**
  1676. * Internal dependencies
  1677. */
  1678. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1679. /**
  1680. * Search a Rich Text value and replace the match(es) with `replacement`. This
  1681. * is similar to `String.prototype.replace`.
  1682. *
  1683. * @param {RichTextValue} value The value to modify.
  1684. * @param {RegExp|string} pattern A RegExp object or literal. Can also be
  1685. * a string. It is treated as a verbatim
  1686. * string and is not interpreted as a
  1687. * regular expression. Only the first
  1688. * occurrence will be replaced.
  1689. * @param {Function|string} replacement The match or matches are replaced with
  1690. * the specified or the value returned by
  1691. * the specified function.
  1692. *
  1693. * @return {RichTextValue} A new value with replacements applied.
  1694. */
  1695. function replace_replace({
  1696. formats,
  1697. replacements,
  1698. text,
  1699. start,
  1700. end
  1701. }, pattern, replacement) {
  1702. text = text.replace(pattern, (match, ...rest) => {
  1703. const offset = rest[rest.length - 2];
  1704. let newText = replacement;
  1705. let newFormats;
  1706. let newReplacements;
  1707. if (typeof newText === 'function') {
  1708. newText = replacement(match, ...rest);
  1709. }
  1710. if (typeof newText === 'object') {
  1711. newFormats = newText.formats;
  1712. newReplacements = newText.replacements;
  1713. newText = newText.text;
  1714. } else {
  1715. newFormats = Array(newText.length);
  1716. newReplacements = Array(newText.length);
  1717. if (formats[offset]) {
  1718. newFormats = newFormats.fill(formats[offset]);
  1719. }
  1720. }
  1721. formats = formats.slice(0, offset).concat(newFormats, formats.slice(offset + match.length));
  1722. replacements = replacements.slice(0, offset).concat(newReplacements, replacements.slice(offset + match.length));
  1723. if (start) {
  1724. start = end = offset + newText.length;
  1725. }
  1726. return newText;
  1727. });
  1728. return normaliseFormats({
  1729. formats,
  1730. replacements,
  1731. text,
  1732. start,
  1733. end
  1734. });
  1735. }
  1736. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/insert-object.js
  1737. /**
  1738. * Internal dependencies
  1739. */
  1740. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1741. /** @typedef {import('./types').RichTextFormat} RichTextFormat */
  1742. /**
  1743. * Insert a format as an object into a Rich Text value at the given
  1744. * `startIndex`. Any content between `startIndex` and `endIndex` will be
  1745. * removed. Indices are retrieved from the selection if none are provided.
  1746. *
  1747. * @param {RichTextValue} value Value to modify.
  1748. * @param {RichTextFormat} formatToInsert Format to insert as object.
  1749. * @param {number} [startIndex] Start index.
  1750. * @param {number} [endIndex] End index.
  1751. *
  1752. * @return {RichTextValue} A new value with the object inserted.
  1753. */
  1754. function insertObject(value, formatToInsert, startIndex, endIndex) {
  1755. const valueToInsert = {
  1756. formats: [,],
  1757. replacements: [formatToInsert],
  1758. text: OBJECT_REPLACEMENT_CHARACTER
  1759. };
  1760. return insert(value, valueToInsert, startIndex, endIndex);
  1761. }
  1762. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/slice.js
  1763. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1764. /**
  1765. * Slice a Rich Text value from `startIndex` to `endIndex`. Indices are
  1766. * retrieved from the selection if none are provided. This is similar to
  1767. * `String.prototype.slice`.
  1768. *
  1769. * @param {RichTextValue} value Value to modify.
  1770. * @param {number} [startIndex] Start index.
  1771. * @param {number} [endIndex] End index.
  1772. *
  1773. * @return {RichTextValue} A new extracted value.
  1774. */
  1775. function slice(value, startIndex = value.start, endIndex = value.end) {
  1776. const {
  1777. formats,
  1778. replacements,
  1779. text
  1780. } = value;
  1781. if (startIndex === undefined || endIndex === undefined) {
  1782. return {
  1783. ...value
  1784. };
  1785. }
  1786. return {
  1787. formats: formats.slice(startIndex, endIndex),
  1788. replacements: replacements.slice(startIndex, endIndex),
  1789. text: text.slice(startIndex, endIndex)
  1790. };
  1791. }
  1792. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/split.js
  1793. /**
  1794. * Internal dependencies
  1795. */
  1796. /** @typedef {import('./types').RichTextValue} RichTextValue */
  1797. /**
  1798. * Split a Rich Text value in two at the given `startIndex` and `endIndex`, or
  1799. * split at the given separator. This is similar to `String.prototype.split`.
  1800. * Indices are retrieved from the selection if none are provided.
  1801. *
  1802. * @param {RichTextValue} value
  1803. * @param {number|string} [string] Start index, or string at which to split.
  1804. *
  1805. * @return {Array<RichTextValue>|undefined} An array of new values.
  1806. */
  1807. function split({
  1808. formats,
  1809. replacements,
  1810. text,
  1811. start,
  1812. end
  1813. }, string) {
  1814. if (typeof string !== 'string') {
  1815. return splitAtSelection(...arguments);
  1816. }
  1817. let nextStart = 0;
  1818. return text.split(string).map(substring => {
  1819. const startIndex = nextStart;
  1820. const value = {
  1821. formats: formats.slice(startIndex, startIndex + substring.length),
  1822. replacements: replacements.slice(startIndex, startIndex + substring.length),
  1823. text: substring
  1824. };
  1825. nextStart += string.length + substring.length;
  1826. if (start !== undefined && end !== undefined) {
  1827. if (start >= startIndex && start < nextStart) {
  1828. value.start = start - startIndex;
  1829. } else if (start < startIndex && end > startIndex) {
  1830. value.start = 0;
  1831. }
  1832. if (end >= startIndex && end < nextStart) {
  1833. value.end = end - startIndex;
  1834. } else if (start < nextStart && end > nextStart) {
  1835. value.end = substring.length;
  1836. }
  1837. }
  1838. return value;
  1839. });
  1840. }
  1841. function splitAtSelection({
  1842. formats,
  1843. replacements,
  1844. text,
  1845. start,
  1846. end
  1847. }, startIndex = start, endIndex = end) {
  1848. if (start === undefined || end === undefined) {
  1849. return;
  1850. }
  1851. const before = {
  1852. formats: formats.slice(0, startIndex),
  1853. replacements: replacements.slice(0, startIndex),
  1854. text: text.slice(0, startIndex)
  1855. };
  1856. const after = {
  1857. formats: formats.slice(endIndex),
  1858. replacements: replacements.slice(endIndex),
  1859. text: text.slice(endIndex),
  1860. start: 0,
  1861. end: 0
  1862. };
  1863. return [before, after];
  1864. }
  1865. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/get-format-type.js
  1866. /**
  1867. * WordPress dependencies
  1868. */
  1869. /**
  1870. * Internal dependencies
  1871. */
  1872. /** @typedef {import('./register-format-type').RichTextFormatType} RichTextFormatType */
  1873. /**
  1874. * Returns a registered format type.
  1875. *
  1876. * @param {string} name Format name.
  1877. *
  1878. * @return {RichTextFormatType|undefined} Format type.
  1879. */
  1880. function get_format_type_getFormatType(name) {
  1881. return (0,external_wp_data_namespaceObject.select)(store).getFormatType(name);
  1882. }
  1883. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/to-tree.js
  1884. /**
  1885. * Internal dependencies
  1886. */
  1887. function restoreOnAttributes(attributes, isEditableTree) {
  1888. if (isEditableTree) {
  1889. return attributes;
  1890. }
  1891. const newAttributes = {};
  1892. for (const key in attributes) {
  1893. let newKey = key;
  1894. if (key.startsWith('data-disable-rich-text-')) {
  1895. newKey = key.slice('data-disable-rich-text-'.length);
  1896. }
  1897. newAttributes[newKey] = attributes[key];
  1898. }
  1899. return newAttributes;
  1900. }
  1901. /**
  1902. * Converts a format object to information that can be used to create an element
  1903. * from (type, attributes and object).
  1904. *
  1905. * @param {Object} $1 Named parameters.
  1906. * @param {string} $1.type The format type.
  1907. * @param {string} $1.tagName The tag name.
  1908. * @param {Object} $1.attributes The format attributes.
  1909. * @param {Object} $1.unregisteredAttributes The unregistered format
  1910. * attributes.
  1911. * @param {boolean} $1.object Whether or not it is an object
  1912. * format.
  1913. * @param {boolean} $1.boundaryClass Whether or not to apply a boundary
  1914. * class.
  1915. * @param {boolean} $1.isEditableTree
  1916. *
  1917. * @return {Object} Information to be used for element creation.
  1918. */
  1919. function fromFormat({
  1920. type,
  1921. tagName,
  1922. attributes,
  1923. unregisteredAttributes,
  1924. object,
  1925. boundaryClass,
  1926. isEditableTree
  1927. }) {
  1928. const formatType = get_format_type_getFormatType(type);
  1929. let elementAttributes = {};
  1930. if (boundaryClass && isEditableTree) {
  1931. elementAttributes['data-rich-text-format-boundary'] = 'true';
  1932. }
  1933. if (!formatType) {
  1934. if (attributes) {
  1935. elementAttributes = {
  1936. ...attributes,
  1937. ...elementAttributes
  1938. };
  1939. }
  1940. return {
  1941. type,
  1942. attributes: restoreOnAttributes(elementAttributes, isEditableTree),
  1943. object
  1944. };
  1945. }
  1946. elementAttributes = {
  1947. ...unregisteredAttributes,
  1948. ...elementAttributes
  1949. };
  1950. for (const name in attributes) {
  1951. const key = formatType.attributes ? formatType.attributes[name] : false;
  1952. if (key) {
  1953. elementAttributes[key] = attributes[name];
  1954. } else {
  1955. elementAttributes[name] = attributes[name];
  1956. }
  1957. }
  1958. if (formatType.className) {
  1959. if (elementAttributes.class) {
  1960. elementAttributes.class = `${formatType.className} ${elementAttributes.class}`;
  1961. } else {
  1962. elementAttributes.class = formatType.className;
  1963. }
  1964. }
  1965. // When a format is declared as non editable, make it non editable in the
  1966. // editor.
  1967. if (isEditableTree && formatType.contentEditable === false) {
  1968. elementAttributes.contenteditable = 'false';
  1969. }
  1970. return {
  1971. type: tagName || formatType.tagName,
  1972. object: formatType.object,
  1973. attributes: restoreOnAttributes(elementAttributes, isEditableTree)
  1974. };
  1975. }
  1976. /**
  1977. * Checks if both arrays of formats up until a certain index are equal.
  1978. *
  1979. * @param {Array} a Array of formats to compare.
  1980. * @param {Array} b Array of formats to compare.
  1981. * @param {number} index Index to check until.
  1982. */
  1983. function isEqualUntil(a, b, index) {
  1984. do {
  1985. if (a[index] !== b[index]) {
  1986. return false;
  1987. }
  1988. } while (index--);
  1989. return true;
  1990. }
  1991. function toTree({
  1992. value,
  1993. preserveWhiteSpace,
  1994. createEmpty,
  1995. append,
  1996. getLastChild,
  1997. getParent,
  1998. isText,
  1999. getText,
  2000. remove,
  2001. appendText,
  2002. onStartIndex,
  2003. onEndIndex,
  2004. isEditableTree,
  2005. placeholder
  2006. }) {
  2007. const {
  2008. formats,
  2009. replacements,
  2010. text,
  2011. start,
  2012. end
  2013. } = value;
  2014. const formatsLength = formats.length + 1;
  2015. const tree = createEmpty();
  2016. const activeFormats = getActiveFormats(value);
  2017. const deepestActiveFormat = activeFormats[activeFormats.length - 1];
  2018. let lastCharacterFormats;
  2019. let lastCharacter;
  2020. append(tree, '');
  2021. for (let i = 0; i < formatsLength; i++) {
  2022. const character = text.charAt(i);
  2023. const shouldInsertPadding = isEditableTree && (
  2024. // Pad the line if the line is empty.
  2025. !lastCharacter ||
  2026. // Pad the line if the previous character is a line break, otherwise
  2027. // the line break won't be visible.
  2028. lastCharacter === '\n');
  2029. const characterFormats = formats[i];
  2030. let pointer = getLastChild(tree);
  2031. if (characterFormats) {
  2032. characterFormats.forEach((format, formatIndex) => {
  2033. if (pointer && lastCharacterFormats &&
  2034. // Reuse the last element if all formats remain the same.
  2035. isEqualUntil(characterFormats, lastCharacterFormats, formatIndex)) {
  2036. pointer = getLastChild(pointer);
  2037. return;
  2038. }
  2039. const {
  2040. type,
  2041. tagName,
  2042. attributes,
  2043. unregisteredAttributes
  2044. } = format;
  2045. const boundaryClass = isEditableTree && format === deepestActiveFormat;
  2046. const parent = getParent(pointer);
  2047. const newNode = append(parent, fromFormat({
  2048. type,
  2049. tagName,
  2050. attributes,
  2051. unregisteredAttributes,
  2052. boundaryClass,
  2053. isEditableTree
  2054. }));
  2055. if (isText(pointer) && getText(pointer).length === 0) {
  2056. remove(pointer);
  2057. }
  2058. pointer = append(newNode, '');
  2059. });
  2060. }
  2061. // If there is selection at 0, handle it before characters are inserted.
  2062. if (i === 0) {
  2063. if (onStartIndex && start === 0) {
  2064. onStartIndex(tree, pointer);
  2065. }
  2066. if (onEndIndex && end === 0) {
  2067. onEndIndex(tree, pointer);
  2068. }
  2069. }
  2070. if (character === OBJECT_REPLACEMENT_CHARACTER) {
  2071. const replacement = replacements[i];
  2072. if (!replacement) continue;
  2073. const {
  2074. type,
  2075. attributes,
  2076. innerHTML
  2077. } = replacement;
  2078. const formatType = get_format_type_getFormatType(type);
  2079. if (!isEditableTree && type === 'script') {
  2080. pointer = append(getParent(pointer), fromFormat({
  2081. type: 'script',
  2082. isEditableTree
  2083. }));
  2084. append(pointer, {
  2085. html: decodeURIComponent(attributes['data-rich-text-script'])
  2086. });
  2087. } else if (formatType?.contentEditable === false) {
  2088. // For non editable formats, render the stored inner HTML.
  2089. pointer = append(getParent(pointer), fromFormat({
  2090. ...replacement,
  2091. isEditableTree,
  2092. boundaryClass: start === i && end === i + 1
  2093. }));
  2094. if (innerHTML) {
  2095. append(pointer, {
  2096. html: innerHTML
  2097. });
  2098. }
  2099. } else {
  2100. pointer = append(getParent(pointer), fromFormat({
  2101. ...replacement,
  2102. object: true,
  2103. isEditableTree
  2104. }));
  2105. }
  2106. // Ensure pointer is text node.
  2107. pointer = append(getParent(pointer), '');
  2108. } else if (!preserveWhiteSpace && character === '\n') {
  2109. pointer = append(getParent(pointer), {
  2110. type: 'br',
  2111. attributes: isEditableTree ? {
  2112. 'data-rich-text-line-break': 'true'
  2113. } : undefined,
  2114. object: true
  2115. });
  2116. // Ensure pointer is text node.
  2117. pointer = append(getParent(pointer), '');
  2118. } else if (!isText(pointer)) {
  2119. pointer = append(getParent(pointer), character);
  2120. } else {
  2121. appendText(pointer, character);
  2122. }
  2123. if (onStartIndex && start === i + 1) {
  2124. onStartIndex(tree, pointer);
  2125. }
  2126. if (onEndIndex && end === i + 1) {
  2127. onEndIndex(tree, pointer);
  2128. }
  2129. if (shouldInsertPadding && i === text.length) {
  2130. append(getParent(pointer), ZWNBSP);
  2131. if (placeholder && text.length === 0) {
  2132. append(getParent(pointer), {
  2133. type: 'span',
  2134. attributes: {
  2135. 'data-rich-text-placeholder': placeholder,
  2136. // Necessary to prevent the placeholder from catching
  2137. // selection and being editable.
  2138. style: 'pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;'
  2139. }
  2140. });
  2141. }
  2142. }
  2143. lastCharacterFormats = characterFormats;
  2144. lastCharacter = character;
  2145. }
  2146. return tree;
  2147. }
  2148. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/is-range-equal.js
  2149. /**
  2150. * Returns true if two ranges are equal, or false otherwise. Ranges are
  2151. * considered equal if their start and end occur in the same container and
  2152. * offset.
  2153. *
  2154. * @param {Range|null} a First range object to test.
  2155. * @param {Range|null} b First range object to test.
  2156. *
  2157. * @return {boolean} Whether the two ranges are equal.
  2158. */
  2159. function isRangeEqual(a, b) {
  2160. return a === b || a && b && a.startContainer === b.startContainer && a.startOffset === b.startOffset && a.endContainer === b.endContainer && a.endOffset === b.endOffset;
  2161. }
  2162. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/to-dom.js
  2163. /**
  2164. * Internal dependencies
  2165. */
  2166. /** @typedef {import('./types').RichTextValue} RichTextValue */
  2167. /**
  2168. * Creates a path as an array of indices from the given root node to the given
  2169. * node.
  2170. *
  2171. * @param {Node} node Node to find the path of.
  2172. * @param {HTMLElement} rootNode Root node to find the path from.
  2173. * @param {Array} path Initial path to build on.
  2174. *
  2175. * @return {Array} The path from the root node to the node.
  2176. */
  2177. function createPathToNode(node, rootNode, path) {
  2178. const parentNode = node.parentNode;
  2179. let i = 0;
  2180. while (node = node.previousSibling) {
  2181. i++;
  2182. }
  2183. path = [i, ...path];
  2184. if (parentNode !== rootNode) {
  2185. path = createPathToNode(parentNode, rootNode, path);
  2186. }
  2187. return path;
  2188. }
  2189. /**
  2190. * Gets a node given a path (array of indices) from the given node.
  2191. *
  2192. * @param {HTMLElement} node Root node to find the wanted node in.
  2193. * @param {Array} path Path (indices) to the wanted node.
  2194. *
  2195. * @return {Object} Object with the found node and the remaining offset (if any).
  2196. */
  2197. function getNodeByPath(node, path) {
  2198. path = [...path];
  2199. while (node && path.length > 1) {
  2200. node = node.childNodes[path.shift()];
  2201. }
  2202. return {
  2203. node,
  2204. offset: path[0]
  2205. };
  2206. }
  2207. function append(element, child) {
  2208. if (child.html !== undefined) {
  2209. return element.innerHTML += child.html;
  2210. }
  2211. if (typeof child === 'string') {
  2212. child = element.ownerDocument.createTextNode(child);
  2213. }
  2214. const {
  2215. type,
  2216. attributes
  2217. } = child;
  2218. if (type) {
  2219. child = element.ownerDocument.createElement(type);
  2220. for (const key in attributes) {
  2221. child.setAttribute(key, attributes[key]);
  2222. }
  2223. }
  2224. return element.appendChild(child);
  2225. }
  2226. function appendText(node, text) {
  2227. node.appendData(text);
  2228. }
  2229. function getLastChild({
  2230. lastChild
  2231. }) {
  2232. return lastChild;
  2233. }
  2234. function getParent({
  2235. parentNode
  2236. }) {
  2237. return parentNode;
  2238. }
  2239. function isText(node) {
  2240. return node.nodeType === node.TEXT_NODE;
  2241. }
  2242. function getText({
  2243. nodeValue
  2244. }) {
  2245. return nodeValue;
  2246. }
  2247. function to_dom_remove(node) {
  2248. return node.parentNode.removeChild(node);
  2249. }
  2250. function toDom({
  2251. value,
  2252. prepareEditableTree,
  2253. isEditableTree = true,
  2254. placeholder,
  2255. doc = document
  2256. }) {
  2257. let startPath = [];
  2258. let endPath = [];
  2259. if (prepareEditableTree) {
  2260. value = {
  2261. ...value,
  2262. formats: prepareEditableTree(value)
  2263. };
  2264. }
  2265. /**
  2266. * Returns a new instance of a DOM tree upon which RichText operations can be
  2267. * applied.
  2268. *
  2269. * Note: The current implementation will return a shared reference, reset on
  2270. * each call to `createEmpty`. Therefore, you should not hold a reference to
  2271. * the value to operate upon asynchronously, as it may have unexpected results.
  2272. *
  2273. * @return {Object} RichText tree.
  2274. */
  2275. const createEmpty = () => createElement(doc, '');
  2276. const tree = toTree({
  2277. value,
  2278. createEmpty,
  2279. append,
  2280. getLastChild,
  2281. getParent,
  2282. isText,
  2283. getText,
  2284. remove: to_dom_remove,
  2285. appendText,
  2286. onStartIndex(body, pointer) {
  2287. startPath = createPathToNode(pointer, body, [pointer.nodeValue.length]);
  2288. },
  2289. onEndIndex(body, pointer) {
  2290. endPath = createPathToNode(pointer, body, [pointer.nodeValue.length]);
  2291. },
  2292. isEditableTree,
  2293. placeholder
  2294. });
  2295. return {
  2296. body: tree,
  2297. selection: {
  2298. startPath,
  2299. endPath
  2300. }
  2301. };
  2302. }
  2303. /**
  2304. * Create an `Element` tree from a Rich Text value and applies the difference to
  2305. * the `Element` tree contained by `current`.
  2306. *
  2307. * @param {Object} $1 Named arguments.
  2308. * @param {RichTextValue} $1.value Value to apply.
  2309. * @param {HTMLElement} $1.current The live root node to apply the element tree to.
  2310. * @param {Function} [$1.prepareEditableTree] Function to filter editorable formats.
  2311. * @param {boolean} [$1.__unstableDomOnly] Only apply elements, no selection.
  2312. * @param {string} [$1.placeholder] Placeholder text.
  2313. */
  2314. function apply({
  2315. value,
  2316. current,
  2317. prepareEditableTree,
  2318. __unstableDomOnly,
  2319. placeholder
  2320. }) {
  2321. // Construct a new element tree in memory.
  2322. const {
  2323. body,
  2324. selection
  2325. } = toDom({
  2326. value,
  2327. prepareEditableTree,
  2328. placeholder,
  2329. doc: current.ownerDocument
  2330. });
  2331. applyValue(body, current);
  2332. if (value.start !== undefined && !__unstableDomOnly) {
  2333. applySelection(selection, current);
  2334. }
  2335. }
  2336. function applyValue(future, current) {
  2337. let i = 0;
  2338. let futureChild;
  2339. while (futureChild = future.firstChild) {
  2340. const currentChild = current.childNodes[i];
  2341. if (!currentChild) {
  2342. current.appendChild(futureChild);
  2343. } else if (!currentChild.isEqualNode(futureChild)) {
  2344. if (currentChild.nodeName !== futureChild.nodeName || currentChild.nodeType === currentChild.TEXT_NODE && currentChild.data !== futureChild.data) {
  2345. current.replaceChild(futureChild, currentChild);
  2346. } else {
  2347. const currentAttributes = currentChild.attributes;
  2348. const futureAttributes = futureChild.attributes;
  2349. if (currentAttributes) {
  2350. let ii = currentAttributes.length;
  2351. // Reverse loop because `removeAttribute` on `currentChild`
  2352. // changes `currentAttributes`.
  2353. while (ii--) {
  2354. const {
  2355. name
  2356. } = currentAttributes[ii];
  2357. if (!futureChild.getAttribute(name)) {
  2358. currentChild.removeAttribute(name);
  2359. }
  2360. }
  2361. }
  2362. if (futureAttributes) {
  2363. for (let ii = 0; ii < futureAttributes.length; ii++) {
  2364. const {
  2365. name,
  2366. value
  2367. } = futureAttributes[ii];
  2368. if (currentChild.getAttribute(name) !== value) {
  2369. currentChild.setAttribute(name, value);
  2370. }
  2371. }
  2372. }
  2373. applyValue(futureChild, currentChild);
  2374. future.removeChild(futureChild);
  2375. }
  2376. } else {
  2377. future.removeChild(futureChild);
  2378. }
  2379. i++;
  2380. }
  2381. while (current.childNodes[i]) {
  2382. current.removeChild(current.childNodes[i]);
  2383. }
  2384. }
  2385. function applySelection({
  2386. startPath,
  2387. endPath
  2388. }, current) {
  2389. const {
  2390. node: startContainer,
  2391. offset: startOffset
  2392. } = getNodeByPath(current, startPath);
  2393. const {
  2394. node: endContainer,
  2395. offset: endOffset
  2396. } = getNodeByPath(current, endPath);
  2397. const {
  2398. ownerDocument
  2399. } = current;
  2400. const {
  2401. defaultView
  2402. } = ownerDocument;
  2403. const selection = defaultView.getSelection();
  2404. const range = ownerDocument.createRange();
  2405. range.setStart(startContainer, startOffset);
  2406. range.setEnd(endContainer, endOffset);
  2407. const {
  2408. activeElement
  2409. } = ownerDocument;
  2410. if (selection.rangeCount > 0) {
  2411. // If the to be added range and the live range are the same, there's no
  2412. // need to remove the live range and add the equivalent range.
  2413. if (isRangeEqual(range, selection.getRangeAt(0))) {
  2414. return;
  2415. }
  2416. selection.removeAllRanges();
  2417. }
  2418. selection.addRange(range);
  2419. // This function is not intended to cause a shift in focus. Since the above
  2420. // selection manipulations may shift focus, ensure that focus is restored to
  2421. // its previous state.
  2422. if (activeElement !== ownerDocument.activeElement) {
  2423. // The `instanceof` checks protect against edge cases where the focused
  2424. // element is not of the interface HTMLElement (does not have a `focus`
  2425. // or `blur` property).
  2426. //
  2427. // See: https://github.com/Microsoft/TypeScript/issues/5901#issuecomment-431649653
  2428. if (activeElement instanceof defaultView.HTMLElement) {
  2429. activeElement.focus();
  2430. }
  2431. }
  2432. }
  2433. ;// CONCATENATED MODULE: external ["wp","escapeHtml"]
  2434. var external_wp_escapeHtml_namespaceObject = window["wp"]["escapeHtml"];
  2435. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/to-html-string.js
  2436. /**
  2437. * WordPress dependencies
  2438. */
  2439. /**
  2440. * Internal dependencies
  2441. */
  2442. /** @typedef {import('./types').RichTextValue} RichTextValue */
  2443. /**
  2444. * Create an HTML string from a Rich Text value.
  2445. *
  2446. * @param {Object} $1 Named argements.
  2447. * @param {RichTextValue} $1.value Rich text value.
  2448. * @param {boolean} [$1.preserveWhiteSpace] Whether or not to use newline
  2449. * characters for line breaks.
  2450. *
  2451. * @return {string} HTML string.
  2452. */
  2453. function toHTMLString({
  2454. value,
  2455. preserveWhiteSpace
  2456. }) {
  2457. const tree = toTree({
  2458. value,
  2459. preserveWhiteSpace,
  2460. createEmpty,
  2461. append: to_html_string_append,
  2462. getLastChild: to_html_string_getLastChild,
  2463. getParent: to_html_string_getParent,
  2464. isText: to_html_string_isText,
  2465. getText: to_html_string_getText,
  2466. remove: to_html_string_remove,
  2467. appendText: to_html_string_appendText
  2468. });
  2469. return createChildrenHTML(tree.children);
  2470. }
  2471. function createEmpty() {
  2472. return {};
  2473. }
  2474. function to_html_string_getLastChild({
  2475. children
  2476. }) {
  2477. return children && children[children.length - 1];
  2478. }
  2479. function to_html_string_append(parent, object) {
  2480. if (typeof object === 'string') {
  2481. object = {
  2482. text: object
  2483. };
  2484. }
  2485. object.parent = parent;
  2486. parent.children = parent.children || [];
  2487. parent.children.push(object);
  2488. return object;
  2489. }
  2490. function to_html_string_appendText(object, text) {
  2491. object.text += text;
  2492. }
  2493. function to_html_string_getParent({
  2494. parent
  2495. }) {
  2496. return parent;
  2497. }
  2498. function to_html_string_isText({
  2499. text
  2500. }) {
  2501. return typeof text === 'string';
  2502. }
  2503. function to_html_string_getText({
  2504. text
  2505. }) {
  2506. return text;
  2507. }
  2508. function to_html_string_remove(object) {
  2509. const index = object.parent.children.indexOf(object);
  2510. if (index !== -1) {
  2511. object.parent.children.splice(index, 1);
  2512. }
  2513. return object;
  2514. }
  2515. function createElementHTML({
  2516. type,
  2517. attributes,
  2518. object,
  2519. children
  2520. }) {
  2521. let attributeString = '';
  2522. for (const key in attributes) {
  2523. if (!(0,external_wp_escapeHtml_namespaceObject.isValidAttributeName)(key)) {
  2524. continue;
  2525. }
  2526. attributeString += ` ${key}="${(0,external_wp_escapeHtml_namespaceObject.escapeAttribute)(attributes[key])}"`;
  2527. }
  2528. if (object) {
  2529. return `<${type}${attributeString}>`;
  2530. }
  2531. return `<${type}${attributeString}>${createChildrenHTML(children)}</${type}>`;
  2532. }
  2533. function createChildrenHTML(children = []) {
  2534. return children.map(child => {
  2535. if (child.html !== undefined) {
  2536. return child.html;
  2537. }
  2538. return child.text === undefined ? createElementHTML(child) : (0,external_wp_escapeHtml_namespaceObject.escapeEditableHTML)(child.text);
  2539. }).join('');
  2540. }
  2541. ;// CONCATENATED MODULE: external ["wp","a11y"]
  2542. var external_wp_a11y_namespaceObject = window["wp"]["a11y"];
  2543. ;// CONCATENATED MODULE: external ["wp","i18n"]
  2544. var external_wp_i18n_namespaceObject = window["wp"]["i18n"];
  2545. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/toggle-format.js
  2546. /**
  2547. * WordPress dependencies
  2548. */
  2549. /**
  2550. * Internal dependencies
  2551. */
  2552. /** @typedef {import('./types').RichTextValue} RichTextValue */
  2553. /** @typedef {import('./types').RichTextFormat} RichTextFormat */
  2554. /**
  2555. * Toggles a format object to a Rich Text value at the current selection.
  2556. *
  2557. * @param {RichTextValue} value Value to modify.
  2558. * @param {RichTextFormat} format Format to apply or remove.
  2559. *
  2560. * @return {RichTextValue} A new value with the format applied or removed.
  2561. */
  2562. function toggleFormat(value, format) {
  2563. if (getActiveFormat(value, format.type)) {
  2564. // For screen readers, will announce if formatting control is disabled.
  2565. if (format.title) {
  2566. // translators: %s: title of the formatting control
  2567. (0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.sprintf)((0,external_wp_i18n_namespaceObject.__)('%s removed.'), format.title), 'assertive');
  2568. }
  2569. return removeFormat(value, format.type);
  2570. }
  2571. // For screen readers, will announce if formatting control is enabled.
  2572. if (format.title) {
  2573. // translators: %s: title of the formatting control
  2574. (0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.sprintf)((0,external_wp_i18n_namespaceObject.__)('%s applied.'), format.title), 'assertive');
  2575. }
  2576. return applyFormat(value, format);
  2577. }
  2578. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/unregister-format-type.js
  2579. /**
  2580. * WordPress dependencies
  2581. */
  2582. /**
  2583. * Internal dependencies
  2584. */
  2585. /** @typedef {import('./register-format-type').WPFormat} WPFormat */
  2586. /**
  2587. * Unregisters a format.
  2588. *
  2589. * @param {string} name Format name.
  2590. *
  2591. * @return {WPFormat|undefined} The previous format value, if it has
  2592. * been successfully unregistered;
  2593. * otherwise `undefined`.
  2594. */
  2595. function unregisterFormatType(name) {
  2596. const oldFormat = (0,external_wp_data_namespaceObject.select)(store).getFormatType(name);
  2597. if (!oldFormat) {
  2598. window.console.error(`Format ${name} is not registered.`);
  2599. return;
  2600. }
  2601. (0,external_wp_data_namespaceObject.dispatch)(store).removeFormatTypes(name);
  2602. return oldFormat;
  2603. }
  2604. ;// CONCATENATED MODULE: external ["wp","element"]
  2605. var external_wp_element_namespaceObject = window["wp"]["element"];
  2606. ;// CONCATENATED MODULE: external ["wp","deprecated"]
  2607. var external_wp_deprecated_namespaceObject = window["wp"]["deprecated"];
  2608. var external_wp_deprecated_default = /*#__PURE__*/__webpack_require__.n(external_wp_deprecated_namespaceObject);
  2609. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-anchor-ref.js
  2610. /**
  2611. * WordPress dependencies
  2612. */
  2613. /**
  2614. * Internal dependencies
  2615. */
  2616. /**
  2617. * @template T
  2618. * @typedef {import('@wordpress/element').RefObject<T>} RefObject<T>
  2619. */
  2620. /** @typedef {import('../register-format-type').WPFormat} WPFormat */
  2621. /** @typedef {import('../types').RichTextValue} RichTextValue */
  2622. /**
  2623. * This hook, to be used in a format type's Edit component, returns the active
  2624. * element that is formatted, or the selection range if no format is active.
  2625. * The returned value is meant to be used for positioning UI, e.g. by passing it
  2626. * to the `Popover` component.
  2627. *
  2628. * @param {Object} $1 Named parameters.
  2629. * @param {RefObject<HTMLElement>} $1.ref React ref of the element
  2630. * containing the editable content.
  2631. * @param {RichTextValue} $1.value Value to check for selection.
  2632. * @param {WPFormat} $1.settings The format type's settings.
  2633. *
  2634. * @return {Element|Range} The active element or selection range.
  2635. */
  2636. function useAnchorRef({
  2637. ref,
  2638. value,
  2639. settings = {}
  2640. }) {
  2641. external_wp_deprecated_default()('`useAnchorRef` hook', {
  2642. since: '6.1',
  2643. alternative: '`useAnchor` hook'
  2644. });
  2645. const {
  2646. tagName,
  2647. className,
  2648. name
  2649. } = settings;
  2650. const activeFormat = name ? getActiveFormat(value, name) : undefined;
  2651. return (0,external_wp_element_namespaceObject.useMemo)(() => {
  2652. if (!ref.current) return;
  2653. const {
  2654. ownerDocument: {
  2655. defaultView
  2656. }
  2657. } = ref.current;
  2658. const selection = defaultView.getSelection();
  2659. if (!selection.rangeCount) {
  2660. return;
  2661. }
  2662. const range = selection.getRangeAt(0);
  2663. if (!activeFormat) {
  2664. return range;
  2665. }
  2666. let element = range.startContainer;
  2667. // If the caret is right before the element, select the next element.
  2668. element = element.nextElementSibling || element;
  2669. while (element.nodeType !== element.ELEMENT_NODE) {
  2670. element = element.parentNode;
  2671. }
  2672. return element.closest(tagName + (className ? '.' + className : ''));
  2673. }, [activeFormat, value.start, value.end, tagName, className]);
  2674. }
  2675. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-anchor.js
  2676. /**
  2677. * WordPress dependencies
  2678. */
  2679. /** @typedef {import('../register-format-type').WPFormat} WPFormat */
  2680. /** @typedef {import('../types').RichTextValue} RichTextValue */
  2681. /**
  2682. * Given a range and a format tag name and class name, returns the closest
  2683. * format element.
  2684. *
  2685. * @param {Range} range The Range to check.
  2686. * @param {HTMLElement} editableContentElement The editable wrapper.
  2687. * @param {string} tagName The tag name of the format element.
  2688. * @param {string} className The class name of the format element.
  2689. *
  2690. * @return {HTMLElement|undefined} The format element, if found.
  2691. */
  2692. function getFormatElement(range, editableContentElement, tagName, className) {
  2693. let element = range.startContainer;
  2694. // Even if the active format is defined, the actualy DOM range's start
  2695. // container may be outside of the format's DOM element:
  2696. // `a‸<strong>b</strong>` (DOM) while visually it's `a<strong>‸b</strong>`.
  2697. // So at a given selection index, start with the deepest format DOM element.
  2698. if (element.nodeType === element.TEXT_NODE && range.startOffset === element.length && element.nextSibling) {
  2699. element = element.nextSibling;
  2700. while (element.firstChild) {
  2701. element = element.firstChild;
  2702. }
  2703. }
  2704. if (element.nodeType !== element.ELEMENT_NODE) {
  2705. element = element.parentElement;
  2706. }
  2707. if (!element) return;
  2708. if (element === editableContentElement) return;
  2709. if (!editableContentElement.contains(element)) return;
  2710. const selector = tagName + (className ? '.' + className : '');
  2711. // .closest( selector ), but with a boundary. Check if the element matches
  2712. // the selector. If it doesn't match, try the parent element if it's not the
  2713. // editable wrapper. We don't want to try to match ancestors of the editable
  2714. // wrapper, which is what .closest( selector ) would do. When the element is
  2715. // the editable wrapper (which is most likely the case because most text is
  2716. // unformatted), this never runs.
  2717. while (element !== editableContentElement) {
  2718. if (element.matches(selector)) {
  2719. return element;
  2720. }
  2721. element = element.parentElement;
  2722. }
  2723. }
  2724. /**
  2725. * @typedef {Object} VirtualAnchorElement
  2726. * @property {() => DOMRect} getBoundingClientRect A function returning a DOMRect
  2727. * @property {HTMLElement} contextElement The actual DOM element
  2728. */
  2729. /**
  2730. * Creates a virtual anchor element for a range.
  2731. *
  2732. * @param {Range} range The range to create a virtual anchor element for.
  2733. * @param {HTMLElement} editableContentElement The editable wrapper.
  2734. *
  2735. * @return {VirtualAnchorElement} The virtual anchor element.
  2736. */
  2737. function createVirtualAnchorElement(range, editableContentElement) {
  2738. return {
  2739. contextElement: editableContentElement,
  2740. getBoundingClientRect() {
  2741. return editableContentElement.contains(range.startContainer) ? range.getBoundingClientRect() : editableContentElement.getBoundingClientRect();
  2742. }
  2743. };
  2744. }
  2745. /**
  2746. * Get the anchor: a format element if there is a matching one based on the
  2747. * tagName and className or a range otherwise.
  2748. *
  2749. * @param {HTMLElement} editableContentElement The editable wrapper.
  2750. * @param {string} tagName The tag name of the format
  2751. * element.
  2752. * @param {string} className The class name of the format
  2753. * element.
  2754. *
  2755. * @return {HTMLElement|VirtualAnchorElement|undefined} The anchor.
  2756. */
  2757. function getAnchor(editableContentElement, tagName, className) {
  2758. if (!editableContentElement) return;
  2759. const {
  2760. ownerDocument
  2761. } = editableContentElement;
  2762. const {
  2763. defaultView
  2764. } = ownerDocument;
  2765. const selection = defaultView.getSelection();
  2766. if (!selection) return;
  2767. if (!selection.rangeCount) return;
  2768. const range = selection.getRangeAt(0);
  2769. if (!range || !range.startContainer) return;
  2770. const formatElement = getFormatElement(range, editableContentElement, tagName, className);
  2771. if (formatElement) return formatElement;
  2772. return createVirtualAnchorElement(range, editableContentElement);
  2773. }
  2774. /**
  2775. * This hook, to be used in a format type's Edit component, returns the active
  2776. * element that is formatted, or a virtual element for the selection range if
  2777. * no format is active. The returned value is meant to be used for positioning
  2778. * UI, e.g. by passing it to the `Popover` component via the `anchor` prop.
  2779. *
  2780. * @param {Object} $1 Named parameters.
  2781. * @param {HTMLElement|null} $1.editableContentElement The element containing
  2782. * the editable content.
  2783. * @param {WPFormat=} $1.settings The format type's settings.
  2784. * @return {Element|VirtualAnchorElement|undefined|null} The active element or selection range.
  2785. */
  2786. function useAnchor({
  2787. editableContentElement,
  2788. settings = {}
  2789. }) {
  2790. const {
  2791. tagName,
  2792. className
  2793. } = settings;
  2794. const [anchor, setAnchor] = (0,external_wp_element_namespaceObject.useState)(() => getAnchor(editableContentElement, tagName, className));
  2795. (0,external_wp_element_namespaceObject.useLayoutEffect)(() => {
  2796. if (!editableContentElement) return;
  2797. const {
  2798. ownerDocument
  2799. } = editableContentElement;
  2800. function callback() {
  2801. setAnchor(getAnchor(editableContentElement, tagName, className));
  2802. }
  2803. function attach() {
  2804. ownerDocument.addEventListener('selectionchange', callback);
  2805. }
  2806. function detach() {
  2807. ownerDocument.removeEventListener('selectionchange', callback);
  2808. }
  2809. if (editableContentElement === ownerDocument.activeElement) {
  2810. attach();
  2811. }
  2812. editableContentElement.addEventListener('focusin', attach);
  2813. editableContentElement.addEventListener('focusout', detach);
  2814. return detach;
  2815. }, [editableContentElement, tagName, className]);
  2816. return anchor;
  2817. }
  2818. ;// CONCATENATED MODULE: external ["wp","compose"]
  2819. var external_wp_compose_namespaceObject = window["wp"]["compose"];
  2820. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-default-style.js
  2821. /**
  2822. * WordPress dependencies
  2823. */
  2824. /**
  2825. * In HTML, leading and trailing spaces are not visible, and multiple spaces
  2826. * elsewhere are visually reduced to one space. This rule prevents spaces from
  2827. * collapsing so all space is visible in the editor and can be removed. It also
  2828. * prevents some browsers from inserting non-breaking spaces at the end of a
  2829. * line to prevent the space from visually disappearing. Sometimes these non
  2830. * breaking spaces can linger in the editor causing unwanted non breaking spaces
  2831. * in between words. If also prevent Firefox from inserting a trailing `br` node
  2832. * to visualise any trailing space, causing the element to be saved.
  2833. *
  2834. * > Authors are encouraged to set the 'white-space' property on editing hosts
  2835. * > and on markup that was originally created through these editing mechanisms
  2836. * > to the value 'pre-wrap'. Default HTML whitespace handling is not well
  2837. * > suited to WYSIWYG editing, and line wrapping will not work correctly in
  2838. * > some corner cases if 'white-space' is left at its default value.
  2839. *
  2840. * https://html.spec.whatwg.org/multipage/interaction.html#best-practices-for-in-page-editors
  2841. *
  2842. * @type {string}
  2843. */
  2844. const whiteSpace = 'pre-wrap';
  2845. /**
  2846. * A minimum width of 1px will prevent the rich text container from collapsing
  2847. * to 0 width and hiding the caret. This is useful for inline containers.
  2848. */
  2849. const minWidth = '1px';
  2850. function useDefaultStyle() {
  2851. return (0,external_wp_element_namespaceObject.useCallback)(element => {
  2852. if (!element) return;
  2853. element.style.whiteSpace = whiteSpace;
  2854. element.style.minWidth = minWidth;
  2855. }, []);
  2856. }
  2857. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-boundary-style.js
  2858. /**
  2859. * WordPress dependencies
  2860. */
  2861. /*
  2862. * Calculates and renders the format boundary style when the active formats
  2863. * change.
  2864. */
  2865. function useBoundaryStyle({
  2866. record
  2867. }) {
  2868. const ref = (0,external_wp_element_namespaceObject.useRef)();
  2869. const {
  2870. activeFormats = [],
  2871. replacements,
  2872. start
  2873. } = record.current;
  2874. const activeReplacement = replacements[start];
  2875. (0,external_wp_element_namespaceObject.useEffect)(() => {
  2876. // There's no need to recalculate the boundary styles if no formats are
  2877. // active, because no boundary styles will be visible.
  2878. if ((!activeFormats || !activeFormats.length) && !activeReplacement) {
  2879. return;
  2880. }
  2881. const boundarySelector = '*[data-rich-text-format-boundary]';
  2882. const element = ref.current.querySelector(boundarySelector);
  2883. if (!element) {
  2884. return;
  2885. }
  2886. const {
  2887. ownerDocument
  2888. } = element;
  2889. const {
  2890. defaultView
  2891. } = ownerDocument;
  2892. const computedStyle = defaultView.getComputedStyle(element);
  2893. const newColor = computedStyle.color.replace(')', ', 0.2)').replace('rgb', 'rgba');
  2894. const selector = `.rich-text:focus ${boundarySelector}`;
  2895. const rule = `background-color: ${newColor}`;
  2896. const style = `${selector} {${rule}}`;
  2897. const globalStyleId = 'rich-text-boundary-style';
  2898. let globalStyle = ownerDocument.getElementById(globalStyleId);
  2899. if (!globalStyle) {
  2900. globalStyle = ownerDocument.createElement('style');
  2901. globalStyle.id = globalStyleId;
  2902. ownerDocument.head.appendChild(globalStyle);
  2903. }
  2904. if (globalStyle.innerHTML !== style) {
  2905. globalStyle.innerHTML = style;
  2906. }
  2907. }, [activeFormats, activeReplacement]);
  2908. return ref;
  2909. }
  2910. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-copy-handler.js
  2911. /**
  2912. * WordPress dependencies
  2913. */
  2914. /**
  2915. * Internal dependencies
  2916. */
  2917. function useCopyHandler(props) {
  2918. const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
  2919. propsRef.current = props;
  2920. return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
  2921. function onCopy(event) {
  2922. const {
  2923. record,
  2924. preserveWhiteSpace
  2925. } = propsRef.current;
  2926. const {
  2927. ownerDocument
  2928. } = element;
  2929. if (isCollapsed(record.current) || !element.contains(ownerDocument.activeElement)) {
  2930. return;
  2931. }
  2932. const selectedRecord = slice(record.current);
  2933. const plainText = getTextContent(selectedRecord);
  2934. const html = toHTMLString({
  2935. value: selectedRecord,
  2936. preserveWhiteSpace
  2937. });
  2938. event.clipboardData.setData('text/plain', plainText);
  2939. event.clipboardData.setData('text/html', html);
  2940. event.clipboardData.setData('rich-text', 'true');
  2941. event.preventDefault();
  2942. if (event.type === 'cut') {
  2943. ownerDocument.execCommand('delete');
  2944. }
  2945. }
  2946. element.addEventListener('copy', onCopy);
  2947. element.addEventListener('cut', onCopy);
  2948. return () => {
  2949. element.removeEventListener('copy', onCopy);
  2950. element.removeEventListener('cut', onCopy);
  2951. };
  2952. }, []);
  2953. }
  2954. ;// CONCATENATED MODULE: external ["wp","keycodes"]
  2955. var external_wp_keycodes_namespaceObject = window["wp"]["keycodes"];
  2956. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-format-boundaries.js
  2957. /**
  2958. * WordPress dependencies
  2959. */
  2960. /**
  2961. * Internal dependencies
  2962. */
  2963. const EMPTY_ACTIVE_FORMATS = [];
  2964. function useFormatBoundaries(props) {
  2965. const [, forceRender] = (0,external_wp_element_namespaceObject.useReducer)(() => ({}));
  2966. const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
  2967. propsRef.current = props;
  2968. return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
  2969. function onKeyDown(event) {
  2970. const {
  2971. keyCode,
  2972. shiftKey,
  2973. altKey,
  2974. metaKey,
  2975. ctrlKey
  2976. } = event;
  2977. if (
  2978. // Only override left and right keys without modifiers pressed.
  2979. shiftKey || altKey || metaKey || ctrlKey || keyCode !== external_wp_keycodes_namespaceObject.LEFT && keyCode !== external_wp_keycodes_namespaceObject.RIGHT) {
  2980. return;
  2981. }
  2982. const {
  2983. record,
  2984. applyRecord
  2985. } = propsRef.current;
  2986. const {
  2987. text,
  2988. formats,
  2989. start,
  2990. end,
  2991. activeFormats: currentActiveFormats = []
  2992. } = record.current;
  2993. const collapsed = isCollapsed(record.current);
  2994. const {
  2995. ownerDocument
  2996. } = element;
  2997. const {
  2998. defaultView
  2999. } = ownerDocument;
  3000. // To do: ideally, we should look at visual position instead.
  3001. const {
  3002. direction
  3003. } = defaultView.getComputedStyle(element);
  3004. const reverseKey = direction === 'rtl' ? external_wp_keycodes_namespaceObject.RIGHT : external_wp_keycodes_namespaceObject.LEFT;
  3005. const isReverse = event.keyCode === reverseKey;
  3006. // If the selection is collapsed and at the very start, do nothing if
  3007. // navigating backward.
  3008. // If the selection is collapsed and at the very end, do nothing if
  3009. // navigating forward.
  3010. if (collapsed && currentActiveFormats.length === 0) {
  3011. if (start === 0 && isReverse) {
  3012. return;
  3013. }
  3014. if (end === text.length && !isReverse) {
  3015. return;
  3016. }
  3017. }
  3018. // If the selection is not collapsed, let the browser handle collapsing
  3019. // the selection for now. Later we could expand this logic to set
  3020. // boundary positions if needed.
  3021. if (!collapsed) {
  3022. return;
  3023. }
  3024. const formatsBefore = formats[start - 1] || EMPTY_ACTIVE_FORMATS;
  3025. const formatsAfter = formats[start] || EMPTY_ACTIVE_FORMATS;
  3026. const destination = isReverse ? formatsBefore : formatsAfter;
  3027. const isIncreasing = currentActiveFormats.every((format, index) => format === destination[index]);
  3028. let newActiveFormatsLength = currentActiveFormats.length;
  3029. if (!isIncreasing) {
  3030. newActiveFormatsLength--;
  3031. } else if (newActiveFormatsLength < destination.length) {
  3032. newActiveFormatsLength++;
  3033. }
  3034. if (newActiveFormatsLength === currentActiveFormats.length) {
  3035. record.current._newActiveFormats = destination;
  3036. return;
  3037. }
  3038. event.preventDefault();
  3039. const origin = isReverse ? formatsAfter : formatsBefore;
  3040. const source = isIncreasing ? destination : origin;
  3041. const newActiveFormats = source.slice(0, newActiveFormatsLength);
  3042. const newValue = {
  3043. ...record.current,
  3044. activeFormats: newActiveFormats
  3045. };
  3046. record.current = newValue;
  3047. applyRecord(newValue);
  3048. forceRender();
  3049. }
  3050. element.addEventListener('keydown', onKeyDown);
  3051. return () => {
  3052. element.removeEventListener('keydown', onKeyDown);
  3053. };
  3054. }, []);
  3055. }
  3056. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-select-object.js
  3057. /**
  3058. * WordPress dependencies
  3059. */
  3060. function useSelectObject() {
  3061. return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
  3062. function onClick(event) {
  3063. const {
  3064. target
  3065. } = event;
  3066. // If the child element has no text content, it must be an object.
  3067. if (target === element || target.textContent && target.isContentEditable) {
  3068. return;
  3069. }
  3070. const {
  3071. ownerDocument
  3072. } = target;
  3073. const {
  3074. defaultView
  3075. } = ownerDocument;
  3076. const selection = defaultView.getSelection();
  3077. // If it's already selected, do nothing and let default behavior
  3078. // happen. This means it's "click-through".
  3079. if (selection.containsNode(target)) return;
  3080. const range = ownerDocument.createRange();
  3081. // If the target is within a non editable element, select the non
  3082. // editable element.
  3083. const nodeToSelect = target.isContentEditable ? target : target.closest('[contenteditable]');
  3084. range.selectNode(nodeToSelect);
  3085. selection.removeAllRanges();
  3086. selection.addRange(range);
  3087. event.preventDefault();
  3088. }
  3089. function onFocusIn(event) {
  3090. // When there is incoming focus from a link, select the object.
  3091. if (event.relatedTarget && !element.contains(event.relatedTarget) && event.relatedTarget.tagName === 'A') {
  3092. onClick(event);
  3093. }
  3094. }
  3095. element.addEventListener('click', onClick);
  3096. element.addEventListener('focusin', onFocusIn);
  3097. return () => {
  3098. element.removeEventListener('click', onClick);
  3099. element.removeEventListener('focusin', onFocusIn);
  3100. };
  3101. }, []);
  3102. }
  3103. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/update-formats.js
  3104. /**
  3105. * Internal dependencies
  3106. */
  3107. /** @typedef {import('./types').RichTextValue} RichTextValue */
  3108. /**
  3109. * Efficiently updates all the formats from `start` (including) until `end`
  3110. * (excluding) with the active formats. Mutates `value`.
  3111. *
  3112. * @param {Object} $1 Named paramentes.
  3113. * @param {RichTextValue} $1.value Value te update.
  3114. * @param {number} $1.start Index to update from.
  3115. * @param {number} $1.end Index to update until.
  3116. * @param {Array} $1.formats Replacement formats.
  3117. *
  3118. * @return {RichTextValue} Mutated value.
  3119. */
  3120. function updateFormats({
  3121. value,
  3122. start,
  3123. end,
  3124. formats
  3125. }) {
  3126. // Start and end may be switched in case of delete.
  3127. const min = Math.min(start, end);
  3128. const max = Math.max(start, end);
  3129. const formatsBefore = value.formats[min - 1] || [];
  3130. const formatsAfter = value.formats[max] || [];
  3131. // First, fix the references. If any format right before or after are
  3132. // equal, the replacement format should use the same reference.
  3133. value.activeFormats = formats.map((format, index) => {
  3134. if (formatsBefore[index]) {
  3135. if (isFormatEqual(format, formatsBefore[index])) {
  3136. return formatsBefore[index];
  3137. }
  3138. } else if (formatsAfter[index]) {
  3139. if (isFormatEqual(format, formatsAfter[index])) {
  3140. return formatsAfter[index];
  3141. }
  3142. }
  3143. return format;
  3144. });
  3145. while (--end >= start) {
  3146. if (value.activeFormats.length > 0) {
  3147. value.formats[end] = value.activeFormats;
  3148. } else {
  3149. delete value.formats[end];
  3150. }
  3151. }
  3152. return value;
  3153. }
  3154. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-input-and-selection.js
  3155. /**
  3156. * WordPress dependencies
  3157. */
  3158. /**
  3159. * Internal dependencies
  3160. */
  3161. /**
  3162. * All inserting input types that would insert HTML into the DOM.
  3163. *
  3164. * @see https://www.w3.org/TR/input-events-2/#interface-InputEvent-Attributes
  3165. *
  3166. * @type {Set}
  3167. */
  3168. const INSERTION_INPUT_TYPES_TO_IGNORE = new Set(['insertParagraph', 'insertOrderedList', 'insertUnorderedList', 'insertHorizontalRule', 'insertLink']);
  3169. const use_input_and_selection_EMPTY_ACTIVE_FORMATS = [];
  3170. const PLACEHOLDER_ATTR_NAME = 'data-rich-text-placeholder';
  3171. /**
  3172. * If the selection is set on the placeholder element, collapse the selection to
  3173. * the start (before the placeholder).
  3174. *
  3175. * @param {Window} defaultView
  3176. */
  3177. function fixPlaceholderSelection(defaultView) {
  3178. const selection = defaultView.getSelection();
  3179. const {
  3180. anchorNode,
  3181. anchorOffset
  3182. } = selection;
  3183. if (anchorNode.nodeType !== anchorNode.ELEMENT_NODE) {
  3184. return;
  3185. }
  3186. const targetNode = anchorNode.childNodes[anchorOffset];
  3187. if (!targetNode || targetNode.nodeType !== targetNode.ELEMENT_NODE || !targetNode.hasAttribute(PLACEHOLDER_ATTR_NAME)) {
  3188. return;
  3189. }
  3190. selection.collapseToStart();
  3191. }
  3192. function useInputAndSelection(props) {
  3193. const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
  3194. propsRef.current = props;
  3195. return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
  3196. const {
  3197. ownerDocument
  3198. } = element;
  3199. const {
  3200. defaultView
  3201. } = ownerDocument;
  3202. let isComposing = false;
  3203. function onInput(event) {
  3204. // Do not trigger a change if characters are being composed.
  3205. // Browsers will usually emit a final `input` event when the
  3206. // characters are composed.
  3207. // As of December 2019, Safari doesn't support
  3208. // nativeEvent.isComposing.
  3209. if (isComposing) {
  3210. return;
  3211. }
  3212. let inputType;
  3213. if (event) {
  3214. inputType = event.inputType;
  3215. }
  3216. const {
  3217. record,
  3218. applyRecord,
  3219. createRecord,
  3220. handleChange
  3221. } = propsRef.current;
  3222. // The browser formatted something or tried to insert HTML.
  3223. // Overwrite it. It will be handled later by the format library if
  3224. // needed.
  3225. if (inputType && (inputType.indexOf('format') === 0 || INSERTION_INPUT_TYPES_TO_IGNORE.has(inputType))) {
  3226. applyRecord(record.current);
  3227. return;
  3228. }
  3229. const currentValue = createRecord();
  3230. const {
  3231. start,
  3232. activeFormats: oldActiveFormats = []
  3233. } = record.current;
  3234. // Update the formats between the last and new caret position.
  3235. const change = updateFormats({
  3236. value: currentValue,
  3237. start,
  3238. end: currentValue.start,
  3239. formats: oldActiveFormats
  3240. });
  3241. handleChange(change);
  3242. }
  3243. /**
  3244. * Syncs the selection to local state. A callback for the
  3245. * `selectionchange` event.
  3246. */
  3247. function handleSelectionChange() {
  3248. const {
  3249. record,
  3250. applyRecord,
  3251. createRecord,
  3252. onSelectionChange
  3253. } = propsRef.current;
  3254. // Check if the implementor disabled editing. `contentEditable`
  3255. // does disable input, but not text selection, so we must ignore
  3256. // selection changes.
  3257. if (element.contentEditable !== 'true') {
  3258. return;
  3259. }
  3260. // If the selection changes where the active element is a parent of
  3261. // the rich text instance (writing flow), call `onSelectionChange`
  3262. // for the rich text instance that contains the start or end of the
  3263. // selection.
  3264. if (ownerDocument.activeElement !== element) {
  3265. // Only process if the active elment is contentEditable, either
  3266. // this rich text instance or the writing flow parent. Fixes a
  3267. // bug in Firefox where it strangely selects the closest
  3268. // contentEditable element, even though the click was outside
  3269. // any contentEditable element.
  3270. if (ownerDocument.activeElement.contentEditable !== 'true') {
  3271. return;
  3272. }
  3273. if (!ownerDocument.activeElement.contains(element)) {
  3274. return;
  3275. }
  3276. const selection = defaultView.getSelection();
  3277. const {
  3278. anchorNode,
  3279. focusNode
  3280. } = selection;
  3281. if (element.contains(anchorNode) && element !== anchorNode && element.contains(focusNode) && element !== focusNode) {
  3282. const {
  3283. start,
  3284. end
  3285. } = createRecord();
  3286. record.current.activeFormats = use_input_and_selection_EMPTY_ACTIVE_FORMATS;
  3287. onSelectionChange(start, end);
  3288. } else if (element.contains(anchorNode) && element !== anchorNode) {
  3289. const {
  3290. start,
  3291. end: offset = start
  3292. } = createRecord();
  3293. record.current.activeFormats = use_input_and_selection_EMPTY_ACTIVE_FORMATS;
  3294. onSelectionChange(offset);
  3295. } else if (element.contains(focusNode)) {
  3296. const {
  3297. start,
  3298. end: offset = start
  3299. } = createRecord();
  3300. record.current.activeFormats = use_input_and_selection_EMPTY_ACTIVE_FORMATS;
  3301. onSelectionChange(undefined, offset);
  3302. }
  3303. return;
  3304. }
  3305. // In case of a keyboard event, ignore selection changes during
  3306. // composition.
  3307. if (isComposing) {
  3308. return;
  3309. }
  3310. const {
  3311. start,
  3312. end,
  3313. text
  3314. } = createRecord();
  3315. const oldRecord = record.current;
  3316. // Fallback mechanism for IE11, which doesn't support the input event.
  3317. // Any input results in a selection change.
  3318. if (text !== oldRecord.text) {
  3319. onInput();
  3320. return;
  3321. }
  3322. if (start === oldRecord.start && end === oldRecord.end) {
  3323. // Sometimes the browser may set the selection on the placeholder
  3324. // element, in which case the caret is not visible. We need to set
  3325. // the caret before the placeholder if that's the case.
  3326. if (oldRecord.text.length === 0 && start === 0) {
  3327. fixPlaceholderSelection(defaultView);
  3328. }
  3329. return;
  3330. }
  3331. const newValue = {
  3332. ...oldRecord,
  3333. start,
  3334. end,
  3335. // _newActiveFormats may be set on arrow key navigation to control
  3336. // the right boundary position. If undefined, getActiveFormats will
  3337. // give the active formats according to the browser.
  3338. activeFormats: oldRecord._newActiveFormats,
  3339. _newActiveFormats: undefined
  3340. };
  3341. const newActiveFormats = getActiveFormats(newValue, use_input_and_selection_EMPTY_ACTIVE_FORMATS);
  3342. // Update the value with the new active formats.
  3343. newValue.activeFormats = newActiveFormats;
  3344. // It is important that the internal value is updated first,
  3345. // otherwise the value will be wrong on render!
  3346. record.current = newValue;
  3347. applyRecord(newValue, {
  3348. domOnly: true
  3349. });
  3350. onSelectionChange(start, end);
  3351. }
  3352. function onCompositionStart() {
  3353. isComposing = true;
  3354. // Do not update the selection when characters are being composed as
  3355. // this rerenders the component and might destroy internal browser
  3356. // editing state.
  3357. ownerDocument.removeEventListener('selectionchange', handleSelectionChange);
  3358. // Remove the placeholder. Since the rich text value doesn't update
  3359. // during composition, the placeholder doesn't get removed. There's
  3360. // no need to re-add it, when the value is updated on compositionend
  3361. // it will be re-added when the value is empty.
  3362. element.querySelector(`[${PLACEHOLDER_ATTR_NAME}]`)?.remove();
  3363. }
  3364. function onCompositionEnd() {
  3365. isComposing = false;
  3366. // Ensure the value is up-to-date for browsers that don't emit a final
  3367. // input event after composition.
  3368. onInput({
  3369. inputType: 'insertText'
  3370. });
  3371. // Tracking selection changes can be resumed.
  3372. ownerDocument.addEventListener('selectionchange', handleSelectionChange);
  3373. }
  3374. function onFocus() {
  3375. const {
  3376. record,
  3377. isSelected,
  3378. onSelectionChange,
  3379. applyRecord
  3380. } = propsRef.current;
  3381. // When the whole editor is editable, let writing flow handle
  3382. // selection.
  3383. if (element.parentElement.closest('[contenteditable="true"]')) {
  3384. return;
  3385. }
  3386. if (!isSelected) {
  3387. // We know for certain that on focus, the old selection is invalid.
  3388. // It will be recalculated on the next mouseup, keyup, or touchend
  3389. // event.
  3390. const index = undefined;
  3391. record.current = {
  3392. ...record.current,
  3393. start: index,
  3394. end: index,
  3395. activeFormats: use_input_and_selection_EMPTY_ACTIVE_FORMATS
  3396. };
  3397. } else {
  3398. applyRecord(record.current);
  3399. onSelectionChange(record.current.start, record.current.end);
  3400. }
  3401. }
  3402. element.addEventListener('input', onInput);
  3403. element.addEventListener('compositionstart', onCompositionStart);
  3404. element.addEventListener('compositionend', onCompositionEnd);
  3405. element.addEventListener('focus', onFocus);
  3406. ownerDocument.addEventListener('selectionchange', handleSelectionChange);
  3407. return () => {
  3408. element.removeEventListener('input', onInput);
  3409. element.removeEventListener('compositionstart', onCompositionStart);
  3410. element.removeEventListener('compositionend', onCompositionEnd);
  3411. element.removeEventListener('focus', onFocus);
  3412. ownerDocument.removeEventListener('selectionchange', handleSelectionChange);
  3413. };
  3414. }, []);
  3415. }
  3416. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-selection-change-compat.js
  3417. /**
  3418. * WordPress dependencies
  3419. */
  3420. /**
  3421. * Internal dependencies
  3422. */
  3423. /**
  3424. * Sometimes some browsers are not firing a `selectionchange` event when
  3425. * changing the selection by mouse or keyboard. This hook makes sure that, if we
  3426. * detect no `selectionchange` or `input` event between the up and down events,
  3427. * we fire a `selectionchange` event.
  3428. *
  3429. * @return {import('@wordpress/compose').RefEffect} A ref effect attaching the
  3430. * listeners.
  3431. */
  3432. function useSelectionChangeCompat() {
  3433. return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
  3434. const {
  3435. ownerDocument
  3436. } = element;
  3437. const {
  3438. defaultView
  3439. } = ownerDocument;
  3440. const selection = defaultView?.getSelection();
  3441. let range;
  3442. function getRange() {
  3443. return selection.rangeCount ? selection.getRangeAt(0) : null;
  3444. }
  3445. function onDown(event) {
  3446. const type = event.type === 'keydown' ? 'keyup' : 'pointerup';
  3447. function onCancel() {
  3448. ownerDocument.removeEventListener(type, onUp);
  3449. ownerDocument.removeEventListener('selectionchange', onCancel);
  3450. ownerDocument.removeEventListener('input', onCancel);
  3451. }
  3452. function onUp() {
  3453. onCancel();
  3454. if (isRangeEqual(range, getRange())) return;
  3455. ownerDocument.dispatchEvent(new Event('selectionchange'));
  3456. }
  3457. ownerDocument.addEventListener(type, onUp);
  3458. ownerDocument.addEventListener('selectionchange', onCancel);
  3459. ownerDocument.addEventListener('input', onCancel);
  3460. range = getRange();
  3461. }
  3462. element.addEventListener('pointerdown', onDown);
  3463. element.addEventListener('keydown', onDown);
  3464. return () => {
  3465. element.removeEventListener('pointerdown', onDown);
  3466. element.removeEventListener('keydown', onDown);
  3467. };
  3468. }, []);
  3469. }
  3470. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-delete.js
  3471. /**
  3472. * WordPress dependencies
  3473. */
  3474. /**
  3475. * Internal dependencies
  3476. */
  3477. function useDelete(props) {
  3478. const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
  3479. propsRef.current = props;
  3480. return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
  3481. function onKeyDown(event) {
  3482. const {
  3483. keyCode
  3484. } = event;
  3485. const {
  3486. createRecord,
  3487. handleChange
  3488. } = propsRef.current;
  3489. if (event.defaultPrevented) {
  3490. return;
  3491. }
  3492. if (keyCode !== external_wp_keycodes_namespaceObject.DELETE && keyCode !== external_wp_keycodes_namespaceObject.BACKSPACE) {
  3493. return;
  3494. }
  3495. const currentValue = createRecord();
  3496. const {
  3497. start,
  3498. end,
  3499. text
  3500. } = currentValue;
  3501. // Always handle full content deletion ourselves.
  3502. if (start === 0 && end !== 0 && end === text.length) {
  3503. handleChange(remove(currentValue));
  3504. event.preventDefault();
  3505. }
  3506. }
  3507. element.addEventListener('keydown', onKeyDown);
  3508. return () => {
  3509. element.removeEventListener('keydown', onKeyDown);
  3510. };
  3511. }, []);
  3512. }
  3513. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/index.js
  3514. /**
  3515. * WordPress dependencies
  3516. */
  3517. /**
  3518. * Internal dependencies
  3519. */
  3520. function useRichText({
  3521. value = '',
  3522. selectionStart,
  3523. selectionEnd,
  3524. placeholder,
  3525. preserveWhiteSpace,
  3526. onSelectionChange,
  3527. onChange,
  3528. __unstableDisableFormats: disableFormats,
  3529. __unstableIsSelected: isSelected,
  3530. __unstableDependencies = [],
  3531. __unstableAfterParse,
  3532. __unstableBeforeSerialize,
  3533. __unstableAddInvisibleFormats
  3534. }) {
  3535. const registry = (0,external_wp_data_namespaceObject.useRegistry)();
  3536. const [, forceRender] = (0,external_wp_element_namespaceObject.useReducer)(() => ({}));
  3537. const ref = (0,external_wp_element_namespaceObject.useRef)();
  3538. function createRecord() {
  3539. const {
  3540. ownerDocument: {
  3541. defaultView
  3542. }
  3543. } = ref.current;
  3544. const selection = defaultView.getSelection();
  3545. const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
  3546. return create({
  3547. element: ref.current,
  3548. range,
  3549. __unstableIsEditableTree: true,
  3550. preserveWhiteSpace
  3551. });
  3552. }
  3553. function applyRecord(newRecord, {
  3554. domOnly
  3555. } = {}) {
  3556. apply({
  3557. value: newRecord,
  3558. current: ref.current,
  3559. prepareEditableTree: __unstableAddInvisibleFormats,
  3560. __unstableDomOnly: domOnly,
  3561. placeholder
  3562. });
  3563. }
  3564. // Internal values are updated synchronously, unlike props and state.
  3565. const _value = (0,external_wp_element_namespaceObject.useRef)(value);
  3566. const record = (0,external_wp_element_namespaceObject.useRef)();
  3567. function setRecordFromProps() {
  3568. _value.current = value;
  3569. record.current = create({
  3570. html: value,
  3571. preserveWhiteSpace
  3572. });
  3573. if (disableFormats) {
  3574. record.current.formats = Array(value.length);
  3575. record.current.replacements = Array(value.length);
  3576. }
  3577. if (__unstableAfterParse) {
  3578. record.current.formats = __unstableAfterParse(record.current);
  3579. }
  3580. record.current.start = selectionStart;
  3581. record.current.end = selectionEnd;
  3582. }
  3583. const hadSelectionUpdate = (0,external_wp_element_namespaceObject.useRef)(false);
  3584. if (!record.current) {
  3585. hadSelectionUpdate.current = isSelected;
  3586. setRecordFromProps();
  3587. // Sometimes formats are added programmatically and we need to make
  3588. // sure it's persisted to the block store / markup. If these formats
  3589. // are not applied, they could cause inconsistencies between the data
  3590. // in the visual editor and the frontend. Right now, it's only relevant
  3591. // to the `core/text-color` format, which is applied at runtime in
  3592. // certain circunstances. See the `__unstableFilterAttributeValue`
  3593. // function in `packages/format-library/src/text-color/index.js`.
  3594. // @todo find a less-hacky way of solving this.
  3595. const hasRelevantInitFormat = record.current?.formats[0]?.[0]?.type === 'core/text-color';
  3596. if (hasRelevantInitFormat) {
  3597. handleChangesUponInit(record.current);
  3598. }
  3599. } else if (selectionStart !== record.current.start || selectionEnd !== record.current.end) {
  3600. hadSelectionUpdate.current = isSelected;
  3601. record.current = {
  3602. ...record.current,
  3603. start: selectionStart,
  3604. end: selectionEnd,
  3605. activeFormats: undefined
  3606. };
  3607. }
  3608. /**
  3609. * Sync the value to global state. The node tree and selection will also be
  3610. * updated if differences are found.
  3611. *
  3612. * @param {Object} newRecord The record to sync and apply.
  3613. */
  3614. function handleChange(newRecord) {
  3615. record.current = newRecord;
  3616. applyRecord(newRecord);
  3617. if (disableFormats) {
  3618. _value.current = newRecord.text;
  3619. } else {
  3620. _value.current = toHTMLString({
  3621. value: __unstableBeforeSerialize ? {
  3622. ...newRecord,
  3623. formats: __unstableBeforeSerialize(newRecord)
  3624. } : newRecord,
  3625. preserveWhiteSpace
  3626. });
  3627. }
  3628. const {
  3629. start,
  3630. end,
  3631. formats,
  3632. text
  3633. } = newRecord;
  3634. // Selection must be updated first, so it is recorded in history when
  3635. // the content change happens.
  3636. // We batch both calls to only attempt to rerender once.
  3637. registry.batch(() => {
  3638. onSelectionChange(start, end);
  3639. onChange(_value.current, {
  3640. __unstableFormats: formats,
  3641. __unstableText: text
  3642. });
  3643. });
  3644. forceRender();
  3645. }
  3646. function handleChangesUponInit(newRecord) {
  3647. record.current = newRecord;
  3648. _value.current = toHTMLString({
  3649. value: __unstableBeforeSerialize ? {
  3650. ...newRecord,
  3651. formats: __unstableBeforeSerialize(newRecord)
  3652. } : newRecord,
  3653. preserveWhiteSpace
  3654. });
  3655. const {
  3656. formats,
  3657. text
  3658. } = newRecord;
  3659. registry.batch(() => {
  3660. onChange(_value.current, {
  3661. __unstableFormats: formats,
  3662. __unstableText: text
  3663. });
  3664. });
  3665. forceRender();
  3666. }
  3667. function applyFromProps() {
  3668. setRecordFromProps();
  3669. applyRecord(record.current);
  3670. }
  3671. const didMount = (0,external_wp_element_namespaceObject.useRef)(false);
  3672. // Value updates must happen synchonously to avoid overwriting newer values.
  3673. (0,external_wp_element_namespaceObject.useLayoutEffect)(() => {
  3674. if (didMount.current && value !== _value.current) {
  3675. applyFromProps();
  3676. forceRender();
  3677. }
  3678. }, [value]);
  3679. // Value updates must happen synchonously to avoid overwriting newer values.
  3680. (0,external_wp_element_namespaceObject.useLayoutEffect)(() => {
  3681. if (!hadSelectionUpdate.current) {
  3682. return;
  3683. }
  3684. if (ref.current.ownerDocument.activeElement !== ref.current) {
  3685. ref.current.focus();
  3686. }
  3687. applyRecord(record.current);
  3688. hadSelectionUpdate.current = false;
  3689. }, [hadSelectionUpdate.current]);
  3690. const mergedRefs = (0,external_wp_compose_namespaceObject.useMergeRefs)([ref, useDefaultStyle(), useBoundaryStyle({
  3691. record
  3692. }), useCopyHandler({
  3693. record,
  3694. preserveWhiteSpace
  3695. }), useSelectObject(), useFormatBoundaries({
  3696. record,
  3697. applyRecord
  3698. }), useDelete({
  3699. createRecord,
  3700. handleChange
  3701. }), useInputAndSelection({
  3702. record,
  3703. applyRecord,
  3704. createRecord,
  3705. handleChange,
  3706. isSelected,
  3707. onSelectionChange
  3708. }), useSelectionChangeCompat(), (0,external_wp_compose_namespaceObject.useRefEffect)(() => {
  3709. applyFromProps();
  3710. didMount.current = true;
  3711. }, [placeholder, ...__unstableDependencies])]);
  3712. return {
  3713. value: record.current,
  3714. // A function to get the most recent value so event handlers in
  3715. // useRichText implementations have access to it. For example when
  3716. // listening to input events, we internally update the state, but this
  3717. // state is not yet available to the input event handler because React
  3718. // may re-render asynchronously.
  3719. getValue: () => record.current,
  3720. onChange: handleChange,
  3721. ref: mergedRefs
  3722. };
  3723. }
  3724. function __experimentalRichText() {}
  3725. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/format-edit.js
  3726. /**
  3727. * Internal dependencies
  3728. */
  3729. function FormatEdit({
  3730. formatTypes,
  3731. onChange,
  3732. onFocus,
  3733. value,
  3734. forwardedRef
  3735. }) {
  3736. return formatTypes.map(settings => {
  3737. const {
  3738. name,
  3739. edit: Edit
  3740. } = settings;
  3741. if (!Edit) {
  3742. return null;
  3743. }
  3744. const activeFormat = getActiveFormat(value, name);
  3745. const isActive = activeFormat !== undefined;
  3746. const activeObject = getActiveObject(value);
  3747. const isObjectActive = activeObject !== undefined && activeObject.type === name;
  3748. return (0,external_wp_element_namespaceObject.createElement)(Edit, {
  3749. key: name,
  3750. isActive: isActive,
  3751. activeAttributes: isActive ? activeFormat.attributes || {} : {},
  3752. isObjectActive: isObjectActive,
  3753. activeObjectAttributes: isObjectActive ? activeObject.attributes || {} : {},
  3754. value: value,
  3755. onChange: onChange,
  3756. onFocus: onFocus,
  3757. contentRef: forwardedRef
  3758. });
  3759. });
  3760. }
  3761. ;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/index.js
  3762. /**
  3763. * An object which represents a formatted string. See main `@wordpress/rich-text`
  3764. * documentation for more information.
  3765. */
  3766. (window.wp = window.wp || {}).richText = __webpack_exports__;
  3767. /******/ })()
  3768. ;