PluginProbe
Gutenberg / 13.9.0
Gutenberg v13.9.0
24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 All 403 releases
gutenberg / build / rich-text / index.js

index.js in Gutenberg 13.9.0, at build/rich-text/index.js

4,952 lines 137.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /******/ (() => { // webpackBootstrap
2 /******/ "use strict";
3 /******/ // The require scope
4 /******/ var __webpack_require__ = {};
5 /******/
6 /************************************************************************/
7 /******/ /* webpack/runtime/define property getters */
8 /******/ (() => {
9 /******/ // define getter functions for harmony exports
10 /******/ __webpack_require__.d = (exports, definition) => {
11 /******/ for(var key in definition) {
12 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
13 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
14 /******/ }
15 /******/ }
16 /******/ };
17 /******/ })();
18 /******/
19 /******/ /* webpack/runtime/hasOwnProperty shorthand */
20 /******/ (() => {
21 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
22 /******/ })();
23 /******/
24 /******/ /* webpack/runtime/make namespace object */
25 /******/ (() => {
26 /******/ // define __esModule on exports
27 /******/ __webpack_require__.r = (exports) => {
28 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
29 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
30 /******/ }
31 /******/ Object.defineProperty(exports, '__esModule', { value: true });
32 /******/ };
33 /******/ })();
34 /******/
35 /************************************************************************/
36 var __webpack_exports__ = {};
37 // ESM COMPAT FLAG
38 __webpack_require__.r(__webpack_exports__);
39
40 // EXPORTS
41 __webpack_require__.d(__webpack_exports__, {
42 "__UNSTABLE_LINE_SEPARATOR": () => (/* reexport */ LINE_SEPARATOR),
43 "__experimentalRichText": () => (/* reexport */ __experimentalRichText),
44 "__unstableCanIndentListItems": () => (/* reexport */ canIndentListItems),
45 "__unstableCanOutdentListItems": () => (/* reexport */ canOutdentListItems),
46 "__unstableChangeListType": () => (/* reexport */ changeListType),
47 "__unstableCreateElement": () => (/* reexport */ createElement),
48 "__unstableFormatEdit": () => (/* reexport */ FormatEdit),
49 "__unstableIndentListItems": () => (/* reexport */ indentListItems),
50 "__unstableInsertLineSeparator": () => (/* reexport */ insertLineSeparator),
51 "__unstableIsActiveListType": () => (/* reexport */ isActiveListType),
52 "__unstableIsEmptyLine": () => (/* reexport */ isEmptyLine),
53 "__unstableIsListRootSelected": () => (/* reexport */ isListRootSelected),
54 "__unstableOutdentListItems": () => (/* reexport */ outdentListItems),
55 "__unstableRemoveLineSeparator": () => (/* reexport */ removeLineSeparator),
56 "__unstableToDom": () => (/* reexport */ toDom),
57 "__unstableUseRichText": () => (/* reexport */ useRichText),
58 "applyFormat": () => (/* reexport */ applyFormat),
59 "concat": () => (/* reexport */ concat),
60 "create": () => (/* reexport */ create),
61 "getActiveFormat": () => (/* reexport */ getActiveFormat),
62 "getActiveFormats": () => (/* reexport */ getActiveFormats),
63 "getActiveObject": () => (/* reexport */ getActiveObject),
64 "getTextContent": () => (/* reexport */ getTextContent),
65 "insert": () => (/* reexport */ insert),
66 "insertObject": () => (/* reexport */ insertObject),
67 "isCollapsed": () => (/* reexport */ isCollapsed),
68 "isEmpty": () => (/* reexport */ isEmpty),
69 "join": () => (/* reexport */ join),
70 "registerFormatType": () => (/* reexport */ registerFormatType),
71 "remove": () => (/* reexport */ remove),
72 "removeFormat": () => (/* reexport */ removeFormat),
73 "replace": () => (/* reexport */ replace_replace),
74 "slice": () => (/* reexport */ slice),
75 "split": () => (/* reexport */ split),
76 "store": () => (/* reexport */ store),
77 "toHTMLString": () => (/* reexport */ toHTMLString),
78 "toggleFormat": () => (/* reexport */ toggleFormat),
79 "unregisterFormatType": () => (/* reexport */ unregisterFormatType),
80 "useAnchorRef": () => (/* reexport */ useAnchorRef)
81 });
82
83 // NAMESPACE OBJECT: ./packages/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": () => (getFormatType),
88 "getFormatTypeForBareElement": () => (getFormatTypeForBareElement),
89 "getFormatTypeForClassName": () => (getFormatTypeForClassName),
90 "getFormatTypes": () => (getFormatTypes)
91 });
92
93 // NAMESPACE OBJECT: ./packages/rich-text/build-module/store/actions.js
94 var actions_namespaceObject = {};
95 __webpack_require__.r(actions_namespaceObject);
96 __webpack_require__.d(actions_namespaceObject, {
97 "addFormatTypes": () => (addFormatTypes),
98 "removeFormatTypes": () => (removeFormatTypes)
99 });
100
101 ;// CONCATENATED MODULE: external ["wp","data"]
102 const external_wp_data_namespaceObject = window["wp"]["data"];
103 ;// CONCATENATED MODULE: external "lodash"
104 const external_lodash_namespaceObject = window["lodash"];
105 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/store/reducer.js
106 /**
107 * External dependencies
108 */
109
110 /**
111 * WordPress dependencies
112 */
113
114
115 /**
116 * Reducer managing the format types
117 *
118 * @param {Object} state Current state.
119 * @param {Object} action Dispatched action.
120 *
121 * @return {Object} Updated state.
122 */
123
124 function formatTypes() {
125 let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
126 let action = arguments.length > 1 ? arguments[1] : undefined;
127
128 switch (action.type) {
129 case 'ADD_FORMAT_TYPES':
130 return { ...state,
131 // Key format types by their name.
132 ...action.formatTypes.reduce((newFormatTypes, type) => ({ ...newFormatTypes,
133 [type.name]: type
134 }), {})
135 };
136
137 case 'REMOVE_FORMAT_TYPES':
138 return (0,external_lodash_namespaceObject.omit)(state, action.names);
139 }
140
141 return state;
142 }
143 /* harmony default export */ const reducer = ((0,external_wp_data_namespaceObject.combineReducers)({
144 formatTypes
145 }));
146
147 ;// CONCATENATED MODULE: ./node_modules/rememo/es/rememo.js
148
149
150 /** @typedef {(...args: any[]) => *[]} GetDependants */
151
152 /** @typedef {() => void} Clear */
153
154 /**
155 * @typedef {{
156 * getDependants: GetDependants,
157 * clear: Clear
158 * }} EnhancedSelector
159 */
160
161 /**
162 * Internal cache entry.
163 *
164 * @typedef CacheNode
165 *
166 * @property {?CacheNode|undefined} [prev] Previous node.
167 * @property {?CacheNode|undefined} [next] Next node.
168 * @property {*[]} args Function arguments for cache entry.
169 * @property {*} val Function result.
170 */
171
172 /**
173 * @typedef Cache
174 *
175 * @property {Clear} clear Function to clear cache.
176 * @property {boolean} [isUniqueByDependants] Whether dependants are valid in
177 * considering cache uniqueness. A cache is unique if dependents are all arrays
178 * or objects.
179 * @property {CacheNode?} [head] Cache head.
180 * @property {*[]} [lastDependants] Dependants from previous invocation.
181 */
182
183 /**
184 * Arbitrary value used as key for referencing cache object in WeakMap tree.
185 *
186 * @type {{}}
187 */
188 var LEAF_KEY = {};
189
190 /**
191 * Returns the first argument as the sole entry in an array.
192 *
193 * @template T
194 *
195 * @param {T} value Value to return.
196 *
197 * @return {[T]} Value returned as entry in array.
198 */
199 function arrayOf(value) {
200 return [value];
201 }
202
203 /**
204 * Returns true if the value passed is object-like, or false otherwise. A value
205 * is object-like if it can support property assignment, e.g. object or array.
206 *
207 * @param {*} value Value to test.
208 *
209 * @return {boolean} Whether value is object-like.
210 */
211 function isObjectLike(value) {
212 return !!value && 'object' === typeof value;
213 }
214
215 /**
216 * Creates and returns a new cache object.
217 *
218 * @return {Cache} Cache object.
219 */
220 function createCache() {
221 /** @type {Cache} */
222 var cache = {
223 clear: function () {
224 cache.head = null;
225 },
226 };
227
228 return cache;
229 }
230
231 /**
232 * Returns true if entries within the two arrays are strictly equal by
233 * reference from a starting index.
234 *
235 * @param {*[]} a First array.
236 * @param {*[]} b Second array.
237 * @param {number} fromIndex Index from which to start comparison.
238 *
239 * @return {boolean} Whether arrays are shallowly equal.
240 */
241 function isShallowEqual(a, b, fromIndex) {
242 var i;
243
244 if (a.length !== b.length) {
245 return false;
246 }
247
248 for (i = fromIndex; i < a.length; i++) {
249 if (a[i] !== b[i]) {
250 return false;
251 }
252 }
253
254 return true;
255 }
256
257 /**
258 * Returns a memoized selector function. The getDependants function argument is
259 * called before the memoized selector and is expected to return an immutable
260 * reference or array of references on which the selector depends for computing
261 * its own return value. The memoize cache is preserved only as long as those
262 * dependant references remain the same. If getDependants returns a different
263 * reference(s), the cache is cleared and the selector value regenerated.
264 *
265 * @template {(...args: *[]) => *} S
266 *
267 * @param {S} selector Selector function.
268 * @param {GetDependants=} getDependants Dependant getter returning an array of
269 * references used in cache bust consideration.
270 */
271 /* harmony default export */ function rememo(selector, getDependants) {
272 /** @type {WeakMap<*,*>} */
273 var rootCache;
274
275 /** @type {GetDependants} */
276 var normalizedGetDependants = getDependants ? getDependants : arrayOf;
277
278 /**
279 * Returns the cache for a given dependants array. When possible, a WeakMap
280 * will be used to create a unique cache for each set of dependants. This
281 * is feasible due to the nature of WeakMap in allowing garbage collection
282 * to occur on entries where the key object is no longer referenced. Since
283 * WeakMap requires the key to be an object, this is only possible when the
284 * dependant is object-like. The root cache is created as a hierarchy where
285 * each top-level key is the first entry in a dependants set, the value a
286 * WeakMap where each key is the next dependant, and so on. This continues
287 * so long as the dependants are object-like. If no dependants are object-
288 * like, then the cache is shared across all invocations.
289 *
290 * @see isObjectLike
291 *
292 * @param {*[]} dependants Selector dependants.
293 *
294 * @return {Cache} Cache object.
295 */
296 function getCache(dependants) {
297 var caches = rootCache,
298 isUniqueByDependants = true,
299 i,
300 dependant,
301 map,
302 cache;
303
304 for (i = 0; i < dependants.length; i++) {
305 dependant = dependants[i];
306
307 // Can only compose WeakMap from object-like key.
308 if (!isObjectLike(dependant)) {
309 isUniqueByDependants = false;
310 break;
311 }
312
313 // Does current segment of cache already have a WeakMap?
314 if (caches.has(dependant)) {
315 // Traverse into nested WeakMap.
316 caches = caches.get(dependant);
317 } else {
318 // Create, set, and traverse into a new one.
319 map = new WeakMap();
320 caches.set(dependant, map);
321 caches = map;
322 }
323 }
324
325 // We use an arbitrary (but consistent) object as key for the last item
326 // in the WeakMap to serve as our running cache.
327 if (!caches.has(LEAF_KEY)) {
328 cache = createCache();
329 cache.isUniqueByDependants = isUniqueByDependants;
330 caches.set(LEAF_KEY, cache);
331 }
332
333 return caches.get(LEAF_KEY);
334 }
335
336 /**
337 * Resets root memoization cache.
338 */
339 function clear() {
340 rootCache = new WeakMap();
341 }
342
343 /* eslint-disable jsdoc/check-param-names */
344 /**
345 * The augmented selector call, considering first whether dependants have
346 * changed before passing it to underlying memoize function.
347 *
348 * @param {*} source Source object for derivation.
349 * @param {...*} extraArgs Additional arguments to pass to selector.
350 *
351 * @return {*} Selector result.
352 */
353 /* eslint-enable jsdoc/check-param-names */
354 function callSelector(/* source, ...extraArgs */) {
355 var len = arguments.length,
356 cache,
357 node,
358 i,
359 args,
360 dependants;
361
362 // Create copy of arguments (avoid leaking deoptimization).
363 args = new Array(len);
364 for (i = 0; i < len; i++) {
365 args[i] = arguments[i];
366 }
367
368 dependants = normalizedGetDependants.apply(null, args);
369 cache = getCache(dependants);
370
371 // If not guaranteed uniqueness by dependants (primitive type), shallow
372 // compare against last dependants and, if references have changed,
373 // destroy cache to recalculate result.
374 if (!cache.isUniqueByDependants) {
375 if (
376 cache.lastDependants &&
377 !isShallowEqual(dependants, cache.lastDependants, 0)
378 ) {
379 cache.clear();
380 }
381
382 cache.lastDependants = dependants;
383 }
384
385 node = cache.head;
386 while (node) {
387 // Check whether node arguments match arguments
388 if (!isShallowEqual(node.args, args, 1)) {
389 node = node.next;
390 continue;
391 }
392
393 // At this point we can assume we've found a match
394
395 // Surface matched node to head if not already
396 if (node !== cache.head) {
397 // Adjust siblings to point to each other.
398 /** @type {CacheNode} */ (node.prev).next = node.next;
399 if (node.next) {
400 node.next.prev = node.prev;
401 }
402
403 node.next = cache.head;
404 node.prev = null;
405 /** @type {CacheNode} */ (cache.head).prev = node;
406 cache.head = node;
407 }
408
409 // Return immediately
410 return node.val;
411 }
412
413 // No cached value found. Continue to insertion phase:
414
415 node = /** @type {CacheNode} */ ({
416 // Generate the result from original function
417 val: selector.apply(null, args),
418 });
419
420 // Avoid including the source object in the cache.
421 args[0] = null;
422 node.args = args;
423
424 // Don't need to check whether node is already head, since it would
425 // have been returned above already if it was
426
427 // Shift existing head down list
428 if (cache.head) {
429 cache.head.prev = node;
430 node.next = cache.head;
431 }
432
433 cache.head = node;
434
435 return node.val;
436 }
437
438 callSelector.getDependants = normalizedGetDependants;
439 callSelector.clear = clear;
440 clear();
441
442 return /** @type {S & EnhancedSelector} */ (callSelector);
443 }
444
445 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/store/selectors.js
446 /**
447 * External dependencies
448 */
449
450
451 /**
452 * Returns all the available format types.
453 *
454 * @param {Object} state Data state.
455 *
456 * @return {Array} Format types.
457 */
458
459 const getFormatTypes = rememo(state => Object.values(state.formatTypes), state => [state.formatTypes]);
460 /**
461 * Returns a format type by name.
462 *
463 * @param {Object} state Data state.
464 * @param {string} name Format type name.
465 *
466 * @return {Object?} Format type.
467 */
468
469 function getFormatType(state, name) {
470 return state.formatTypes[name];
471 }
472 /**
473 * Gets the format type, if any, that can handle a bare element (without a
474 * data-format-type attribute), given the tag name of this element.
475 *
476 * @param {Object} state Data state.
477 * @param {string} bareElementTagName The tag name of the element to find a
478 * format type for.
479 * @return {?Object} Format type.
480 */
481
482 function getFormatTypeForBareElement(state, bareElementTagName) {
483 return (0,external_lodash_namespaceObject.find)(getFormatTypes(state), _ref => {
484 let {
485 className,
486 tagName
487 } = _ref;
488 return className === null && bareElementTagName === tagName;
489 });
490 }
491 /**
492 * Gets the format type, if any, that can handle an element, given its classes.
493 *
494 * @param {Object} state Data state.
495 * @param {string} elementClassName The classes of the element to find a format
496 * type for.
497 * @return {?Object} Format type.
498 */
499
500 function getFormatTypeForClassName(state, elementClassName) {
501 return (0,external_lodash_namespaceObject.find)(getFormatTypes(state), _ref2 => {
502 let {
503 className
504 } = _ref2;
505
506 if (className === null) {
507 return false;
508 }
509
510 return ` ${elementClassName} `.indexOf(` ${className} `) >= 0;
511 });
512 }
513
514 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/store/actions.js
515 /**
516 * External dependencies
517 */
518
519 /**
520 * Returns an action object used in signalling that format types have been
521 * added.
522 *
523 * @param {Array|Object} formatTypes Format types received.
524 *
525 * @return {Object} Action object.
526 */
527
528 function addFormatTypes(formatTypes) {
529 return {
530 type: 'ADD_FORMAT_TYPES',
531 formatTypes: (0,external_lodash_namespaceObject.castArray)(formatTypes)
532 };
533 }
534 /**
535 * Returns an action object used to remove a registered format type.
536 *
537 * @param {string|Array} names Format name.
538 *
539 * @return {Object} Action object.
540 */
541
542 function removeFormatTypes(names) {
543 return {
544 type: 'REMOVE_FORMAT_TYPES',
545 names: (0,external_lodash_namespaceObject.castArray)(names)
546 };
547 }
548
549 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/store/index.js
550 /**
551 * WordPress dependencies
552 */
553
554 /**
555 * Internal dependencies
556 */
557
558
559
560
561 const STORE_NAME = 'core/rich-text';
562 /**
563 * Store definition for the rich-text namespace.
564 *
565 * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore
566 *
567 * @type {Object}
568 */
569
570 const store = (0,external_wp_data_namespaceObject.createReduxStore)(STORE_NAME, {
571 reducer: reducer,
572 selectors: selectors_namespaceObject,
573 actions: actions_namespaceObject
574 });
575 (0,external_wp_data_namespaceObject.register)(store);
576
577 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/is-format-equal.js
578 /** @typedef {import('./create').RichTextFormat} RichTextFormat */
579
580 /**
581 * Optimised equality check for format objects.
582 *
583 * @param {?RichTextFormat} format1 Format to compare.
584 * @param {?RichTextFormat} format2 Format to compare.
585 *
586 * @return {boolean} True if formats are equal, false if not.
587 */
588 function isFormatEqual(format1, format2) {
589 // Both not defined.
590 if (format1 === format2) {
591 return true;
592 } // Either not defined.
593
594
595 if (!format1 || !format2) {
596 return false;
597 }
598
599 if (format1.type !== format2.type) {
600 return false;
601 }
602
603 const attributes1 = format1.attributes;
604 const attributes2 = format2.attributes; // Both not defined.
605
606 if (attributes1 === attributes2) {
607 return true;
608 } // Either not defined.
609
610
611 if (!attributes1 || !attributes2) {
612 return false;
613 }
614
615 const keys1 = Object.keys(attributes1);
616 const keys2 = Object.keys(attributes2);
617
618 if (keys1.length !== keys2.length) {
619 return false;
620 }
621
622 const length = keys1.length; // Optimise for speed.
623
624 for (let i = 0; i < length; i++) {
625 const name = keys1[i];
626
627 if (attributes1[name] !== attributes2[name]) {
628 return false;
629 }
630 }
631
632 return true;
633 }
634
635 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/normalise-formats.js
636 /**
637 * Internal dependencies
638 */
639
640 /** @typedef {import('./create').RichTextValue} RichTextValue */
641
642 /**
643 * Normalises formats: ensures subsequent adjacent equal formats have the same
644 * reference.
645 *
646 * @param {RichTextValue} value Value to normalise formats of.
647 *
648 * @return {RichTextValue} New value with normalised formats.
649 */
650
651 function normaliseFormats(value) {
652 const newFormats = value.formats.slice();
653 newFormats.forEach((formatsAtIndex, index) => {
654 const formatsAtPreviousIndex = newFormats[index - 1];
655
656 if (formatsAtPreviousIndex) {
657 const newFormatsAtIndex = formatsAtIndex.slice();
658 newFormatsAtIndex.forEach((format, formatIndex) => {
659 const previousFormat = formatsAtPreviousIndex[formatIndex];
660
661 if (isFormatEqual(format, previousFormat)) {
662 newFormatsAtIndex[formatIndex] = previousFormat;
663 }
664 });
665 newFormats[index] = newFormatsAtIndex;
666 }
667 });
668 return { ...value,
669 formats: newFormats
670 };
671 }
672
673 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/apply-format.js
674 /**
675 * Internal dependencies
676 */
677
678 /** @typedef {import('./create').RichTextValue} RichTextValue */
679
680 /** @typedef {import('./create').RichTextFormat} RichTextFormat */
681
682 function replace(array, index, value) {
683 array = array.slice();
684 array[index] = value;
685 return array;
686 }
687 /**
688 * Apply a format object to a Rich Text value from the given `startIndex` to the
689 * given `endIndex`. Indices are retrieved from the selection if none are
690 * provided.
691 *
692 * @param {RichTextValue} value Value to modify.
693 * @param {RichTextFormat} format Format to apply.
694 * @param {number} [startIndex] Start index.
695 * @param {number} [endIndex] End index.
696 *
697 * @return {RichTextValue} A new value with the format applied.
698 */
699
700
701 function applyFormat(value, format) {
702 let startIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : value.start;
703 let endIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : value.end;
704 const {
705 formats,
706 activeFormats
707 } = value;
708 const newFormats = formats.slice(); // The selection is collapsed.
709
710 if (startIndex === endIndex) {
711 var _newFormats$startInde;
712
713 const startFormat = (_newFormats$startInde = newFormats[startIndex]) === null || _newFormats$startInde === void 0 ? void 0 : _newFormats$startInde.find(_ref => {
714 let {
715 type
716 } = _ref;
717 return type === format.type;
718 }); // If the caret is at a format of the same type, expand start and end to
719 // the edges of the format. This is useful to apply new attributes.
720
721 if (startFormat) {
722 const index = newFormats[startIndex].indexOf(startFormat);
723
724 while (newFormats[startIndex] && newFormats[startIndex][index] === startFormat) {
725 newFormats[startIndex] = replace(newFormats[startIndex], index, format);
726 startIndex--;
727 }
728
729 endIndex++;
730
731 while (newFormats[endIndex] && newFormats[endIndex][index] === startFormat) {
732 newFormats[endIndex] = replace(newFormats[endIndex], index, format);
733 endIndex++;
734 }
735 }
736 } else {
737 // Determine the highest position the new format can be inserted at.
738 let position = +Infinity;
739
740 for (let index = startIndex; index < endIndex; index++) {
741 if (newFormats[index]) {
742 newFormats[index] = newFormats[index].filter(_ref2 => {
743 let {
744 type
745 } = _ref2;
746 return type !== format.type;
747 });
748 const length = newFormats[index].length;
749
750 if (length < position) {
751 position = length;
752 }
753 } else {
754 newFormats[index] = [];
755 position = 0;
756 }
757 }
758
759 for (let index = startIndex; index < endIndex; index++) {
760 newFormats[index].splice(position, 0, format);
761 }
762 }
763
764 return normaliseFormats({ ...value,
765 formats: newFormats,
766 // Always revise active formats. This serves as a placeholder for new
767 // inputs with the format so new input appears with the format applied,
768 // and ensures a format of the same type uses the latest values.
769 activeFormats: [...((activeFormats === null || activeFormats === void 0 ? void 0 : activeFormats.filter(_ref3 => {
770 let {
771 type
772 } = _ref3;
773 return type !== format.type;
774 })) || []), format]
775 });
776 }
777
778 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/create-element.js
779 /**
780 * Parse the given HTML into a body element.
781 *
782 * Note: The current implementation will return a shared reference, reset on
783 * each call to `createElement`. Therefore, you should not hold a reference to
784 * the value to operate upon asynchronously, as it may have unexpected results.
785 *
786 * @param {HTMLDocument} document The HTML document to use to parse.
787 * @param {string} html The HTML to parse.
788 *
789 * @return {HTMLBodyElement} Body element with parsed HTML.
790 */
791 function createElement(_ref, html) {
792 let {
793 implementation
794 } = _ref;
795
796 // Because `createHTMLDocument` is an expensive operation, and with this
797 // function being internal to `rich-text` (full control in avoiding a risk
798 // of asynchronous operations on the shared reference), a single document
799 // is reused and reset for each call to the function.
800 if (!createElement.body) {
801 createElement.body = implementation.createHTMLDocument('').body;
802 }
803
804 createElement.body.innerHTML = html;
805 return createElement.body;
806 }
807
808 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/special-characters.js
809 /**
810 * Line separator character, used for multiline text.
811 */
812 const LINE_SEPARATOR = '\u2028';
813 /**
814 * Object replacement character, used as a placeholder for objects.
815 */
816
817 const OBJECT_REPLACEMENT_CHARACTER = '\ufffc';
818 /**
819 * Zero width non-breaking space, used as padding in the editable DOM tree when
820 * it is empty otherwise.
821 */
822
823 const ZWNBSP = '\ufeff';
824
825 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/create.js
826 /**
827 * WordPress dependencies
828 */
829
830 /**
831 * Internal dependencies
832 */
833
834
835
836
837
838 /**
839 * @typedef {Object} RichTextFormat
840 *
841 * @property {string} type Format type.
842 */
843
844 /**
845 * @typedef {Array<RichTextFormat>} RichTextFormatList
846 */
847
848 /**
849 * @typedef {Object} RichTextValue
850 *
851 * @property {string} text Text.
852 * @property {Array<RichTextFormatList>} formats Formats.
853 * @property {Array<RichTextFormat>} replacements Replacements.
854 * @property {number|undefined} start Selection start.
855 * @property {number|undefined} end Selection end.
856 */
857
858 function createEmptyValue() {
859 return {
860 formats: [],
861 replacements: [],
862 text: ''
863 };
864 }
865
866 function toFormat(_ref) {
867 let {
868 type,
869 attributes
870 } = _ref;
871 let formatType;
872
873 if (attributes && attributes.class) {
874 formatType = (0,external_wp_data_namespaceObject.select)(store).getFormatTypeForClassName(attributes.class);
875
876 if (formatType) {
877 // Preserve any additional classes.
878 attributes.class = ` ${attributes.class} `.replace(` ${formatType.className} `, ' ').trim();
879
880 if (!attributes.class) {
881 delete attributes.class;
882 }
883 }
884 }
885
886 if (!formatType) {
887 formatType = (0,external_wp_data_namespaceObject.select)(store).getFormatTypeForBareElement(type);
888 }
889
890 if (!formatType) {
891 return attributes ? {
892 type,
893 attributes
894 } : {
895 type
896 };
897 }
898
899 if (formatType.__experimentalCreatePrepareEditableTree && !formatType.__experimentalCreateOnChangeEditableValue) {
900 return null;
901 }
902
903 if (!attributes) {
904 return {
905 type: formatType.name
906 };
907 }
908
909 const registeredAttributes = {};
910 const unregisteredAttributes = {};
911 const _attributes = { ...attributes
912 };
913
914 for (const key in formatType.attributes) {
915 const name = formatType.attributes[key];
916 registeredAttributes[key] = _attributes[name];
917
918 if (formatType.__unstableFilterAttributeValue) {
919 registeredAttributes[key] = formatType.__unstableFilterAttributeValue(key, registeredAttributes[key]);
920 } // delete the attribute and what's left is considered
921 // to be unregistered.
922
923
924 delete _attributes[name];
925
926 if (typeof registeredAttributes[key] === 'undefined') {
927 delete registeredAttributes[key];
928 }
929 }
930
931 for (const name in _attributes) {
932 unregisteredAttributes[name] = attributes[name];
933 }
934
935 return {
936 type: formatType.name,
937 attributes: registeredAttributes,
938 unregisteredAttributes
939 };
940 }
941 /**
942 * Create a RichText value from an `Element` tree (DOM), an HTML string or a
943 * plain text string, with optionally a `Range` object to set the selection. If
944 * called without any input, an empty value will be created. If
945 * `multilineTag` is provided, any content of direct children whose type matches
946 * `multilineTag` will be separated by two newlines. The optional functions can
947 * be used to filter out content.
948 *
949 * A value will have the following shape, which you are strongly encouraged not
950 * to modify without the use of helper functions:
951 *
952 * ```js
953 * {
954 * text: string,
955 * formats: Array,
956 * replacements: Array,
957 * ?start: number,
958 * ?end: number,
959 * }
960 * ```
961 *
962 * As you can see, text and formatting are separated. `text` holds the text,
963 * including any replacement characters for objects and lines. `formats`,
964 * `objects` and `lines` are all sparse arrays of the same length as `text`. It
965 * holds information about the formatting at the relevant text indices. Finally
966 * `start` and `end` state which text indices are selected. They are only
967 * provided if a `Range` was given.
968 *
969 * @param {Object} [$1] Optional named arguments.
970 * @param {Element} [$1.element] Element to create value from.
971 * @param {string} [$1.text] Text to create value from.
972 * @param {string} [$1.html] HTML to create value from.
973 * @param {Range} [$1.range] Range to create value from.
974 * @param {string} [$1.multilineTag] Multiline tag if the structure is
975 * multiline.
976 * @param {Array} [$1.multilineWrapperTags] Tags where lines can be found if
977 * nesting is possible.
978 * @param {boolean} [$1.preserveWhiteSpace] Whether or not to collapse white
979 * space characters.
980 * @param {boolean} [$1.__unstableIsEditableTree]
981 *
982 * @return {RichTextValue} A rich text value.
983 */
984
985
986 function create() {
987 let {
988 element,
989 text,
990 html,
991 range,
992 multilineTag,
993 multilineWrapperTags,
994 __unstableIsEditableTree: isEditableTree,
995 preserveWhiteSpace
996 } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
997
998 if (typeof text === 'string' && text.length > 0) {
999 return {
1000 formats: Array(text.length),
1001 replacements: Array(text.length),
1002 text
1003 };
1004 }
1005
1006 if (typeof html === 'string' && html.length > 0) {
1007 // It does not matter which document this is, we're just using it to
1008 // parse.
1009 element = createElement(document, html);
1010 }
1011
1012 if (typeof element !== 'object') {
1013 return createEmptyValue();
1014 }
1015
1016 if (!multilineTag) {
1017 return createFromElement({
1018 element,
1019 range,
1020 isEditableTree,
1021 preserveWhiteSpace
1022 });
1023 }
1024
1025 return createFromMultilineElement({
1026 element,
1027 range,
1028 multilineTag,
1029 multilineWrapperTags,
1030 isEditableTree,
1031 preserveWhiteSpace
1032 });
1033 }
1034 /**
1035 * Helper to accumulate the value's selection start and end from the current
1036 * node and range.
1037 *
1038 * @param {Object} accumulator Object to accumulate into.
1039 * @param {Node} node Node to create value with.
1040 * @param {Range} range Range to create value with.
1041 * @param {Object} value Value that is being accumulated.
1042 */
1043
1044 function accumulateSelection(accumulator, node, range, value) {
1045 if (!range) {
1046 return;
1047 }
1048
1049 const {
1050 parentNode
1051 } = node;
1052 const {
1053 startContainer,
1054 startOffset,
1055 endContainer,
1056 endOffset
1057 } = range;
1058 const currentLength = accumulator.text.length; // Selection can be extracted from value.
1059
1060 if (value.start !== undefined) {
1061 accumulator.start = currentLength + value.start; // Range indicates that the current node has selection.
1062 } else if (node === startContainer && node.nodeType === node.TEXT_NODE) {
1063 accumulator.start = currentLength + startOffset; // Range indicates that the current node is selected.
1064 } else if (parentNode === startContainer && node === startContainer.childNodes[startOffset]) {
1065 accumulator.start = currentLength; // Range indicates that the selection is after the current node.
1066 } else if (parentNode === startContainer && node === startContainer.childNodes[startOffset - 1]) {
1067 accumulator.start = currentLength + value.text.length; // Fallback if no child inside handled the selection.
1068 } else if (node === startContainer) {
1069 accumulator.start = currentLength;
1070 } // Selection can be extracted from value.
1071
1072
1073 if (value.end !== undefined) {
1074 accumulator.end = currentLength + value.end; // Range indicates that the current node has selection.
1075 } else if (node === endContainer && node.nodeType === node.TEXT_NODE) {
1076 accumulator.end = currentLength + endOffset; // Range indicates that the current node is selected.
1077 } else if (parentNode === endContainer && node === endContainer.childNodes[endOffset - 1]) {
1078 accumulator.end = currentLength + value.text.length; // Range indicates that the selection is before the current node.
1079 } else if (parentNode === endContainer && node === endContainer.childNodes[endOffset]) {
1080 accumulator.end = currentLength; // Fallback if no child inside handled the selection.
1081 } else if (node === endContainer) {
1082 accumulator.end = currentLength + endOffset;
1083 }
1084 }
1085 /**
1086 * Adjusts the start and end offsets from a range based on a text filter.
1087 *
1088 * @param {Node} node Node of which the text should be filtered.
1089 * @param {Range} range The range to filter.
1090 * @param {Function} filter Function to use to filter the text.
1091 *
1092 * @return {Object|void} Object containing range properties.
1093 */
1094
1095
1096 function filterRange(node, range, filter) {
1097 if (!range) {
1098 return;
1099 }
1100
1101 const {
1102 startContainer,
1103 endContainer
1104 } = range;
1105 let {
1106 startOffset,
1107 endOffset
1108 } = range;
1109
1110 if (node === startContainer) {
1111 startOffset = filter(node.nodeValue.slice(0, startOffset)).length;
1112 }
1113
1114 if (node === endContainer) {
1115 endOffset = filter(node.nodeValue.slice(0, endOffset)).length;
1116 }
1117
1118 return {
1119 startContainer,
1120 startOffset,
1121 endContainer,
1122 endOffset
1123 };
1124 }
1125 /**
1126 * Collapse any whitespace used for HTML formatting to one space character,
1127 * because it will also be displayed as such by the browser.
1128 *
1129 * @param {string} string
1130 */
1131
1132
1133 function collapseWhiteSpace(string) {
1134 return string.replace(/[\n\r\t]+/g, ' ');
1135 }
1136 /**
1137 * Removes reserved characters used by rich-text (zero width non breaking spaces added by `toTree` and object replacement characters).
1138 *
1139 * @param {string} string
1140 */
1141
1142
1143 function removeReservedCharacters(string) {
1144 // with the global flag, note that we should create a new regex each time OR reset lastIndex state.
1145 return string.replace(new RegExp(`[${ZWNBSP}${OBJECT_REPLACEMENT_CHARACTER}]`, 'gu'), '');
1146 }
1147 /**
1148 * Creates a Rich Text value from a DOM element and range.
1149 *
1150 * @param {Object} $1 Named argements.
1151 * @param {Element} [$1.element] Element to create value from.
1152 * @param {Range} [$1.range] Range to create value from.
1153 * @param {string} [$1.multilineTag] Multiline tag if the structure is
1154 * multiline.
1155 * @param {Array} [$1.multilineWrapperTags] Tags where lines can be found if
1156 * nesting is possible.
1157 * @param {boolean} [$1.preserveWhiteSpace] Whether or not to collapse white
1158 * space characters.
1159 * @param {Array} [$1.currentWrapperTags]
1160 * @param {boolean} [$1.isEditableTree]
1161 *
1162 * @return {RichTextValue} A rich text value.
1163 */
1164
1165 function createFromElement(_ref2) {
1166 let {
1167 element,
1168 range,
1169 multilineTag,
1170 multilineWrapperTags,
1171 currentWrapperTags = [],
1172 isEditableTree,
1173 preserveWhiteSpace
1174 } = _ref2;
1175 const accumulator = createEmptyValue();
1176
1177 if (!element) {
1178 return accumulator;
1179 }
1180
1181 if (!element.hasChildNodes()) {
1182 accumulateSelection(accumulator, element, range, createEmptyValue());
1183 return accumulator;
1184 }
1185
1186 const length = element.childNodes.length; // Optimise for speed.
1187
1188 for (let index = 0; index < length; index++) {
1189 const node = element.childNodes[index];
1190 const type = node.nodeName.toLowerCase();
1191
1192 if (node.nodeType === node.TEXT_NODE) {
1193 let filter = removeReservedCharacters;
1194
1195 if (!preserveWhiteSpace) {
1196 filter = string => removeReservedCharacters(collapseWhiteSpace(string));
1197 }
1198
1199 const text = filter(node.nodeValue);
1200 range = filterRange(node, range, filter);
1201 accumulateSelection(accumulator, node, range, {
1202 text
1203 }); // Create a sparse array of the same length as `text`, in which
1204 // formats can be added.
1205
1206 accumulator.formats.length += text.length;
1207 accumulator.replacements.length += text.length;
1208 accumulator.text += text;
1209 continue;
1210 }
1211
1212 if (node.nodeType !== node.ELEMENT_NODE) {
1213 continue;
1214 }
1215
1216 if (isEditableTree && ( // Ignore any placeholders.
1217 node.getAttribute('data-rich-text-placeholder') || // Ignore any line breaks that are not inserted by us.
1218 type === 'br' && !node.getAttribute('data-rich-text-line-break'))) {
1219 accumulateSelection(accumulator, node, range, createEmptyValue());
1220 continue;
1221 }
1222
1223 if (type === 'script') {
1224 const value = {
1225 formats: [,],
1226 replacements: [{
1227 type,
1228 attributes: {
1229 'data-rich-text-script': node.getAttribute('data-rich-text-script') || encodeURIComponent(node.innerHTML)
1230 }
1231 }],
1232 text: OBJECT_REPLACEMENT_CHARACTER
1233 };
1234 accumulateSelection(accumulator, node, range, value);
1235 mergePair(accumulator, value);
1236 continue;
1237 }
1238
1239 if (type === 'br') {
1240 accumulateSelection(accumulator, node, range, createEmptyValue());
1241 mergePair(accumulator, create({
1242 text: '\n'
1243 }));
1244 continue;
1245 }
1246
1247 const format = toFormat({
1248 type,
1249 attributes: getAttributes({
1250 element: node
1251 })
1252 });
1253
1254 if (multilineWrapperTags && multilineWrapperTags.indexOf(type) !== -1) {
1255 const value = createFromMultilineElement({
1256 element: node,
1257 range,
1258 multilineTag,
1259 multilineWrapperTags,
1260 currentWrapperTags: [...currentWrapperTags, format],
1261 isEditableTree,
1262 preserveWhiteSpace
1263 });
1264 accumulateSelection(accumulator, node, range, value);
1265 mergePair(accumulator, value);
1266 continue;
1267 }
1268
1269 const value = createFromElement({
1270 element: node,
1271 range,
1272 multilineTag,
1273 multilineWrapperTags,
1274 isEditableTree,
1275 preserveWhiteSpace
1276 });
1277 accumulateSelection(accumulator, node, range, value);
1278
1279 if (!format) {
1280 mergePair(accumulator, value);
1281 } else if (value.text.length === 0) {
1282 if (format.attributes) {
1283 mergePair(accumulator, {
1284 formats: [,],
1285 replacements: [format],
1286 text: OBJECT_REPLACEMENT_CHARACTER
1287 });
1288 }
1289 } else {
1290 // Indices should share a reference to the same formats array.
1291 // Only create a new reference if `formats` changes.
1292 function mergeFormats(formats) {
1293 if (mergeFormats.formats === formats) {
1294 return mergeFormats.newFormats;
1295 }
1296
1297 const newFormats = formats ? [format, ...formats] : [format];
1298 mergeFormats.formats = formats;
1299 mergeFormats.newFormats = newFormats;
1300 return newFormats;
1301 } // Since the formats parameter can be `undefined`, preset
1302 // `mergeFormats` with a new reference.
1303
1304
1305 mergeFormats.newFormats = [format];
1306 mergePair(accumulator, { ...value,
1307 formats: Array.from(value.formats, mergeFormats)
1308 });
1309 }
1310 }
1311
1312 return accumulator;
1313 }
1314 /**
1315 * Creates a rich text value from a DOM element and range that should be
1316 * multiline.
1317 *
1318 * @param {Object} $1 Named argements.
1319 * @param {Element} [$1.element] Element to create value from.
1320 * @param {Range} [$1.range] Range to create value from.
1321 * @param {string} [$1.multilineTag] Multiline tag if the structure is
1322 * multiline.
1323 * @param {Array} [$1.multilineWrapperTags] Tags where lines can be found if
1324 * nesting is possible.
1325 * @param {Array} [$1.currentWrapperTags] Whether to prepend a line
1326 * separator.
1327 * @param {boolean} [$1.preserveWhiteSpace] Whether or not to collapse white
1328 * space characters.
1329 * @param {boolean} [$1.isEditableTree]
1330 *
1331 * @return {RichTextValue} A rich text value.
1332 */
1333
1334
1335 function createFromMultilineElement(_ref3) {
1336 let {
1337 element,
1338 range,
1339 multilineTag,
1340 multilineWrapperTags,
1341 currentWrapperTags = [],
1342 isEditableTree,
1343 preserveWhiteSpace
1344 } = _ref3;
1345 const accumulator = createEmptyValue();
1346
1347 if (!element || !element.hasChildNodes()) {
1348 return accumulator;
1349 }
1350
1351 const length = element.children.length; // Optimise for speed.
1352
1353 for (let index = 0; index < length; index++) {
1354 const node = element.children[index];
1355
1356 if (node.nodeName.toLowerCase() !== multilineTag) {
1357 continue;
1358 }
1359
1360 const value = createFromElement({
1361 element: node,
1362 range,
1363 multilineTag,
1364 multilineWrapperTags,
1365 currentWrapperTags,
1366 isEditableTree,
1367 preserveWhiteSpace
1368 }); // Multiline value text should be separated by a line separator.
1369
1370 if (index !== 0 || currentWrapperTags.length > 0) {
1371 mergePair(accumulator, {
1372 formats: [,],
1373 replacements: currentWrapperTags.length > 0 ? [currentWrapperTags] : [,],
1374 text: LINE_SEPARATOR
1375 });
1376 }
1377
1378 accumulateSelection(accumulator, node, range, value);
1379 mergePair(accumulator, value);
1380 }
1381
1382 return accumulator;
1383 }
1384 /**
1385 * Gets the attributes of an element in object shape.
1386 *
1387 * @param {Object} $1 Named argements.
1388 * @param {Element} $1.element Element to get attributes from.
1389 *
1390 * @return {Object|void} Attribute object or `undefined` if the element has no
1391 * attributes.
1392 */
1393
1394
1395 function getAttributes(_ref4) {
1396 let {
1397 element
1398 } = _ref4;
1399
1400 if (!element.hasAttributes()) {
1401 return;
1402 }
1403
1404 const length = element.attributes.length;
1405 let accumulator; // Optimise for speed.
1406
1407 for (let i = 0; i < length; i++) {
1408 const {
1409 name,
1410 value
1411 } = element.attributes[i];
1412
1413 if (name.indexOf('data-rich-text-') === 0) {
1414 continue;
1415 }
1416
1417 const safeName = /^on/i.test(name) ? 'data-disable-rich-text-' + name : name;
1418 accumulator = accumulator || {};
1419 accumulator[safeName] = value;
1420 }
1421
1422 return accumulator;
1423 }
1424
1425 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/concat.js
1426 /**
1427 * Internal dependencies
1428 */
1429
1430
1431 /** @typedef {import('./create').RichTextValue} RichTextValue */
1432
1433 /**
1434 * Concats a pair of rich text values. Not that this mutates `a` and does NOT
1435 * normalise formats!
1436 *
1437 * @param {Object} a Value to mutate.
1438 * @param {Object} b Value to add read from.
1439 *
1440 * @return {Object} `a`, mutated.
1441 */
1442
1443 function mergePair(a, b) {
1444 a.formats = a.formats.concat(b.formats);
1445 a.replacements = a.replacements.concat(b.replacements);
1446 a.text += b.text;
1447 return a;
1448 }
1449 /**
1450 * Combine all Rich Text values into one. This is similar to
1451 * `String.prototype.concat`.
1452 *
1453 * @param {...RichTextValue} values Objects to combine.
1454 *
1455 * @return {RichTextValue} A new value combining all given records.
1456 */
1457
1458 function concat() {
1459 for (var _len = arguments.length, values = new Array(_len), _key = 0; _key < _len; _key++) {
1460 values[_key] = arguments[_key];
1461 }
1462
1463 return normaliseFormats(values.reduce(mergePair, create()));
1464 }
1465
1466 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/get-active-formats.js
1467 /** @typedef {import('./create').RichTextValue} RichTextValue */
1468
1469 /** @typedef {import('./create').RichTextFormatList} RichTextFormatList */
1470
1471 /**
1472 * Gets the all format objects at the start of the selection.
1473 *
1474 * @param {RichTextValue} value Value to inspect.
1475 * @param {Array} EMPTY_ACTIVE_FORMATS Array to return if there are no
1476 * active formats.
1477 *
1478 * @return {RichTextFormatList} Active format objects.
1479 */
1480 function getActiveFormats(_ref) {
1481 let {
1482 formats,
1483 start,
1484 end,
1485 activeFormats
1486 } = _ref;
1487 let EMPTY_ACTIVE_FORMATS = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
1488
1489 if (start === undefined) {
1490 return EMPTY_ACTIVE_FORMATS;
1491 }
1492
1493 if (start === end) {
1494 // For a collapsed caret, it is possible to override the active formats.
1495 if (activeFormats) {
1496 return activeFormats;
1497 }
1498
1499 const formatsBefore = formats[start - 1] || EMPTY_ACTIVE_FORMATS;
1500 const formatsAfter = formats[start] || EMPTY_ACTIVE_FORMATS; // By default, select the lowest amount of formats possible (which means
1501 // the caret is positioned outside the format boundary). The user can
1502 // then use arrow keys to define `activeFormats`.
1503
1504 if (formatsBefore.length < formatsAfter.length) {
1505 return formatsBefore;
1506 }
1507
1508 return formatsAfter;
1509 }
1510
1511 return formats[start] || EMPTY_ACTIVE_FORMATS;
1512 }
1513
1514 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/get-active-format.js
1515 /**
1516 * External dependencies
1517 */
1518
1519 /**
1520 * Internal dependencies
1521 */
1522
1523
1524 /** @typedef {import('./create').RichTextValue} RichTextValue */
1525
1526 /** @typedef {import('./create').RichTextFormat} RichTextFormat */
1527
1528 /**
1529 * Gets the format object by type at the start of the selection. This can be
1530 * used to get e.g. the URL of a link format at the current selection, but also
1531 * to check if a format is active at the selection. Returns undefined if there
1532 * is no format at the selection.
1533 *
1534 * @param {RichTextValue} value Value to inspect.
1535 * @param {string} formatType Format type to look for.
1536 *
1537 * @return {RichTextFormat|undefined} Active format object of the specified
1538 * type, or undefined.
1539 */
1540
1541 function getActiveFormat(value, formatType) {
1542 return (0,external_lodash_namespaceObject.find)(getActiveFormats(value), {
1543 type: formatType
1544 });
1545 }
1546
1547 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/get-active-object.js
1548 /**
1549 * Internal dependencies
1550 */
1551
1552 /** @typedef {import('./create').RichTextValue} RichTextValue */
1553
1554 /** @typedef {import('./create').RichTextFormat} RichTextFormat */
1555
1556 /**
1557 * Gets the active object, if there is any.
1558 *
1559 * @param {RichTextValue} value Value to inspect.
1560 *
1561 * @return {RichTextFormat|void} Active object, or undefined.
1562 */
1563
1564 function getActiveObject(_ref) {
1565 let {
1566 start,
1567 end,
1568 replacements,
1569 text
1570 } = _ref;
1571
1572 if (start + 1 !== end || text[start] !== OBJECT_REPLACEMENT_CHARACTER) {
1573 return;
1574 }
1575
1576 return replacements[start];
1577 }
1578
1579 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/get-text-content.js
1580 /**
1581 * Internal dependencies
1582 */
1583
1584 /** @typedef {import('./create').RichTextValue} RichTextValue */
1585
1586 /**
1587 * Get the textual content of a Rich Text value. This is similar to
1588 * `Element.textContent`.
1589 *
1590 * @param {RichTextValue} value Value to use.
1591 *
1592 * @return {string} The text content.
1593 */
1594
1595 function getTextContent(_ref) {
1596 let {
1597 text
1598 } = _ref;
1599 return text.replace(new RegExp(OBJECT_REPLACEMENT_CHARACTER, 'g'), '').replace(new RegExp(LINE_SEPARATOR, 'g'), '\n');
1600 }
1601
1602 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/get-line-index.js
1603 /**
1604 * Internal dependencies
1605 */
1606
1607 /** @typedef {import('./create').RichTextValue} RichTextValue */
1608
1609 /**
1610 * Gets the currently selected line index, or the first line index if the
1611 * selection spans over multiple items.
1612 *
1613 * @param {RichTextValue} value Value to get the line index from.
1614 * @param {boolean} startIndex Optional index that should be contained by
1615 * the line. Defaults to the selection start
1616 * of the value.
1617 *
1618 * @return {number|void} The line index. Undefined if not found.
1619 */
1620
1621 function getLineIndex(_ref) {
1622 let {
1623 start,
1624 text
1625 } = _ref;
1626 let startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : start;
1627 let index = startIndex;
1628
1629 while (index--) {
1630 if (text[index] === LINE_SEPARATOR) {
1631 return index;
1632 }
1633 }
1634 }
1635
1636 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/is-list-root-selected.js
1637 /**
1638 * Internal dependencies
1639 */
1640
1641 /** @typedef {import('./create').RichTextValue} RichTextValue */
1642
1643 /**
1644 * Whether or not the root list is selected.
1645 *
1646 * @param {RichTextValue} value The value to check.
1647 *
1648 * @return {boolean} True if the root list or nothing is selected, false if an
1649 * inner list is selected.
1650 */
1651
1652 function isListRootSelected(value) {
1653 const {
1654 replacements,
1655 start
1656 } = value;
1657 const lineIndex = getLineIndex(value, start);
1658 const replacement = replacements[lineIndex];
1659 return !replacement || replacement.length < 1;
1660 }
1661
1662 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/is-active-list-type.js
1663 /**
1664 * Internal dependencies
1665 */
1666
1667 /** @typedef {import('./create').RichTextValue} RichTextValue */
1668
1669 /**
1670 * Whether or not the selected list has the given tag name.
1671 *
1672 * @param {RichTextValue} value The value to check.
1673 * @param {string} type The tag name the list should have.
1674 * @param {string} rootType The current root tag name, to compare with in
1675 * case nothing is selected.
1676 *
1677 * @return {boolean} True if the current list type matches `type`, false if not.
1678 */
1679
1680 function isActiveListType(value, type, rootType) {
1681 const {
1682 replacements,
1683 start
1684 } = value;
1685 const lineIndex = getLineIndex(value, start);
1686 const replacement = replacements[lineIndex];
1687
1688 if (!replacement || replacement.length === 0) {
1689 return type === rootType;
1690 }
1691
1692 const lastFormat = replacement[replacement.length - 1];
1693 return lastFormat.type === type;
1694 }
1695
1696 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/is-collapsed.js
1697 /** @typedef {import('./create').RichTextValue} RichTextValue */
1698
1699 /**
1700 * Check if the selection of a Rich Text value is collapsed or not. Collapsed
1701 * means that no characters are selected, but there is a caret present. If there
1702 * is no selection, `undefined` will be returned. This is similar to
1703 * `window.getSelection().isCollapsed()`.
1704 *
1705 * @param {RichTextValue} value The rich text value to check.
1706 *
1707 * @return {boolean|undefined} True if the selection is collapsed, false if not,
1708 * undefined if there is no selection.
1709 */
1710 function isCollapsed(_ref) {
1711 let {
1712 start,
1713 end
1714 } = _ref;
1715
1716 if (start === undefined || end === undefined) {
1717 return;
1718 }
1719
1720 return start === end;
1721 }
1722
1723 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/is-empty.js
1724 /**
1725 * Internal dependencies
1726 */
1727
1728 /** @typedef {import('./create').RichTextValue} RichTextValue */
1729
1730 /**
1731 * Check if a Rich Text value is Empty, meaning it contains no text or any
1732 * objects (such as images).
1733 *
1734 * @param {RichTextValue} value Value to use.
1735 *
1736 * @return {boolean} True if the value is empty, false if not.
1737 */
1738
1739 function isEmpty(_ref) {
1740 let {
1741 text
1742 } = _ref;
1743 return text.length === 0;
1744 }
1745 /**
1746 * Check if the current collapsed selection is on an empty line in case of a
1747 * multiline value.
1748 *
1749 * @param {RichTextValue} value Value te check.
1750 *
1751 * @return {boolean} True if the line is empty, false if not.
1752 */
1753
1754 function isEmptyLine(_ref2) {
1755 let {
1756 text,
1757 start,
1758 end
1759 } = _ref2;
1760
1761 if (start !== end) {
1762 return false;
1763 }
1764
1765 if (text.length === 0) {
1766 return true;
1767 }
1768
1769 if (start === 0 && text.slice(0, 1) === LINE_SEPARATOR) {
1770 return true;
1771 }
1772
1773 if (start === text.length && text.slice(-1) === LINE_SEPARATOR) {
1774 return true;
1775 }
1776
1777 return text.slice(start - 1, end + 1) === `${LINE_SEPARATOR}${LINE_SEPARATOR}`;
1778 }
1779
1780 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/join.js
1781 /**
1782 * Internal dependencies
1783 */
1784
1785
1786 /** @typedef {import('./create').RichTextValue} RichTextValue */
1787
1788 /**
1789 * Combine an array of Rich Text values into one, optionally separated by
1790 * `separator`, which can be a Rich Text value, HTML string, or plain text
1791 * string. This is similar to `Array.prototype.join`.
1792 *
1793 * @param {Array<RichTextValue>} values An array of values to join.
1794 * @param {string|RichTextValue} [separator] Separator string or value.
1795 *
1796 * @return {RichTextValue} A new combined value.
1797 */
1798
1799 function join(values) {
1800 let separator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
1801
1802 if (typeof separator === 'string') {
1803 separator = create({
1804 text: separator
1805 });
1806 }
1807
1808 return normaliseFormats(values.reduce((accumlator, _ref) => {
1809 let {
1810 formats,
1811 replacements,
1812 text
1813 } = _ref;
1814 return {
1815 formats: accumlator.formats.concat(separator.formats, formats),
1816 replacements: accumlator.replacements.concat(separator.replacements, replacements),
1817 text: accumlator.text + separator.text + text
1818 };
1819 }));
1820 }
1821
1822 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/register-format-type.js
1823 /**
1824 * WordPress dependencies
1825 */
1826
1827 /**
1828 * Internal dependencies
1829 */
1830
1831
1832 /**
1833 * @typedef {Object} WPFormat
1834 *
1835 * @property {string} name A string identifying the format. Must be
1836 * unique across all registered formats.
1837 * @property {string} tagName The HTML tag this format will wrap the
1838 * selection with.
1839 * @property {string} [className] A class to match the format.
1840 * @property {string} title Name of the format.
1841 * @property {Function} edit Should return a component for the user to
1842 * interact with the new registered format.
1843 */
1844
1845 /**
1846 * Registers a new format provided a unique name and an object defining its
1847 * behavior.
1848 *
1849 * @param {string} name Format name.
1850 * @param {WPFormat} settings Format settings.
1851 *
1852 * @return {WPFormat|undefined} The format, if it has been successfully
1853 * registered; otherwise `undefined`.
1854 */
1855
1856 function registerFormatType(name, settings) {
1857 settings = {
1858 name,
1859 ...settings
1860 };
1861
1862 if (typeof settings.name !== 'string') {
1863 window.console.error('Format names must be strings.');
1864 return;
1865 }
1866
1867 if (!/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test(settings.name)) {
1868 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');
1869 return;
1870 }
1871
1872 if ((0,external_wp_data_namespaceObject.select)(store).getFormatType(settings.name)) {
1873 window.console.error('Format "' + settings.name + '" is already registered.');
1874 return;
1875 }
1876
1877 if (typeof settings.tagName !== 'string' || settings.tagName === '') {
1878 window.console.error('Format tag names must be a string.');
1879 return;
1880 }
1881
1882 if ((typeof settings.className !== 'string' || settings.className === '') && settings.className !== null) {
1883 window.console.error('Format class names must be a string, or null to handle bare elements.');
1884 return;
1885 }
1886
1887 if (!/^[_a-zA-Z]+[a-zA-Z0-9-]*$/.test(settings.className)) {
1888 window.console.error('A class name must begin with a letter, followed by any number of hyphens, letters, or numbers.');
1889 return;
1890 }
1891
1892 if (settings.className === null) {
1893 const formatTypeForBareElement = (0,external_wp_data_namespaceObject.select)(store).getFormatTypeForBareElement(settings.tagName);
1894
1895 if (formatTypeForBareElement) {
1896 window.console.error(`Format "${formatTypeForBareElement.name}" is already registered to handle bare tag name "${settings.tagName}".`);
1897 return;
1898 }
1899 } else {
1900 const formatTypeForClassName = (0,external_wp_data_namespaceObject.select)(store).getFormatTypeForClassName(settings.className);
1901
1902 if (formatTypeForClassName) {
1903 window.console.error(`Format "${formatTypeForClassName.name}" is already registered to handle class name "${settings.className}".`);
1904 return;
1905 }
1906 }
1907
1908 if (!('title' in settings) || settings.title === '') {
1909 window.console.error('The format "' + settings.name + '" must have a title.');
1910 return;
1911 }
1912
1913 if ('keywords' in settings && settings.keywords.length > 3) {
1914 window.console.error('The format "' + settings.name + '" can have a maximum of 3 keywords.');
1915 return;
1916 }
1917
1918 if (typeof settings.title !== 'string') {
1919 window.console.error('Format titles must be strings.');
1920 return;
1921 }
1922
1923 (0,external_wp_data_namespaceObject.dispatch)(store).addFormatTypes(settings);
1924 return settings;
1925 }
1926
1927 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/remove-format.js
1928 /**
1929 * Internal dependencies
1930 */
1931
1932 /** @typedef {import('./create').RichTextValue} RichTextValue */
1933
1934 /**
1935 * Remove any format object from a Rich Text value by type from the given
1936 * `startIndex` to the given `endIndex`. Indices are retrieved from the
1937 * selection if none are provided.
1938 *
1939 * @param {RichTextValue} value Value to modify.
1940 * @param {string} formatType Format type to remove.
1941 * @param {number} [startIndex] Start index.
1942 * @param {number} [endIndex] End index.
1943 *
1944 * @return {RichTextValue} A new value with the format applied.
1945 */
1946
1947 function removeFormat(value, formatType) {
1948 let startIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : value.start;
1949 let endIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : value.end;
1950 const {
1951 formats,
1952 activeFormats
1953 } = value;
1954 const newFormats = formats.slice(); // If the selection is collapsed, expand start and end to the edges of the
1955 // format.
1956
1957 if (startIndex === endIndex) {
1958 var _newFormats$startInde;
1959
1960 const format = (_newFormats$startInde = newFormats[startIndex]) === null || _newFormats$startInde === void 0 ? void 0 : _newFormats$startInde.find(_ref => {
1961 let {
1962 type
1963 } = _ref;
1964 return type === formatType;
1965 });
1966
1967 if (format) {
1968 while ((_newFormats$startInde2 = newFormats[startIndex]) !== null && _newFormats$startInde2 !== void 0 && _newFormats$startInde2.find(newFormat => newFormat === format)) {
1969 var _newFormats$startInde2;
1970
1971 filterFormats(newFormats, startIndex, formatType);
1972 startIndex--;
1973 }
1974
1975 endIndex++;
1976
1977 while ((_newFormats$endIndex = newFormats[endIndex]) !== null && _newFormats$endIndex !== void 0 && _newFormats$endIndex.find(newFormat => newFormat === format)) {
1978 var _newFormats$endIndex;
1979
1980 filterFormats(newFormats, endIndex, formatType);
1981 endIndex++;
1982 }
1983 }
1984 } else {
1985 for (let i = startIndex; i < endIndex; i++) {
1986 if (newFormats[i]) {
1987 filterFormats(newFormats, i, formatType);
1988 }
1989 }
1990 }
1991
1992 return normaliseFormats({ ...value,
1993 formats: newFormats,
1994 activeFormats: (activeFormats === null || activeFormats === void 0 ? void 0 : activeFormats.filter(_ref2 => {
1995 let {
1996 type
1997 } = _ref2;
1998 return type !== formatType;
1999 })) || []
2000 });
2001 }
2002
2003 function filterFormats(formats, index, formatType) {
2004 const newFormats = formats[index].filter(_ref3 => {
2005 let {
2006 type
2007 } = _ref3;
2008 return type !== formatType;
2009 });
2010
2011 if (newFormats.length) {
2012 formats[index] = newFormats;
2013 } else {
2014 delete formats[index];
2015 }
2016 }
2017
2018 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/insert.js
2019 /**
2020 * Internal dependencies
2021 */
2022
2023
2024 /** @typedef {import('./create').RichTextValue} RichTextValue */
2025
2026 /**
2027 * Insert a Rich Text value, an HTML string, or a plain text string, into a
2028 * Rich Text value at the given `startIndex`. Any content between `startIndex`
2029 * and `endIndex` will be removed. Indices are retrieved from the selection if
2030 * none are provided.
2031 *
2032 * @param {RichTextValue} value Value to modify.
2033 * @param {RichTextValue|string} valueToInsert Value to insert.
2034 * @param {number} [startIndex] Start index.
2035 * @param {number} [endIndex] End index.
2036 *
2037 * @return {RichTextValue} A new value with the value inserted.
2038 */
2039
2040 function insert(value, valueToInsert) {
2041 let startIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : value.start;
2042 let endIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : value.end;
2043 const {
2044 formats,
2045 replacements,
2046 text
2047 } = value;
2048
2049 if (typeof valueToInsert === 'string') {
2050 valueToInsert = create({
2051 text: valueToInsert
2052 });
2053 }
2054
2055 const index = startIndex + valueToInsert.text.length;
2056 return normaliseFormats({
2057 formats: formats.slice(0, startIndex).concat(valueToInsert.formats, formats.slice(endIndex)),
2058 replacements: replacements.slice(0, startIndex).concat(valueToInsert.replacements, replacements.slice(endIndex)),
2059 text: text.slice(0, startIndex) + valueToInsert.text + text.slice(endIndex),
2060 start: index,
2061 end: index
2062 });
2063 }
2064
2065 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/remove.js
2066 /**
2067 * Internal dependencies
2068 */
2069
2070
2071 /** @typedef {import('./create').RichTextValue} RichTextValue */
2072
2073 /**
2074 * Remove content from a Rich Text value between the given `startIndex` and
2075 * `endIndex`. Indices are retrieved from the selection if none are provided.
2076 *
2077 * @param {RichTextValue} value Value to modify.
2078 * @param {number} [startIndex] Start index.
2079 * @param {number} [endIndex] End index.
2080 *
2081 * @return {RichTextValue} A new value with the content removed.
2082 */
2083
2084 function remove(value, startIndex, endIndex) {
2085 return insert(value, create(), startIndex, endIndex);
2086 }
2087
2088 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/replace.js
2089 /**
2090 * Internal dependencies
2091 */
2092
2093 /** @typedef {import('./create').RichTextValue} RichTextValue */
2094
2095 /**
2096 * Search a Rich Text value and replace the match(es) with `replacement`. This
2097 * is similar to `String.prototype.replace`.
2098 *
2099 * @param {RichTextValue} value The value to modify.
2100 * @param {RegExp|string} pattern A RegExp object or literal. Can also be
2101 * a string. It is treated as a verbatim
2102 * string and is not interpreted as a
2103 * regular expression. Only the first
2104 * occurrence will be replaced.
2105 * @param {Function|string} replacement The match or matches are replaced with
2106 * the specified or the value returned by
2107 * the specified function.
2108 *
2109 * @return {RichTextValue} A new value with replacements applied.
2110 */
2111
2112 function replace_replace(_ref, pattern, replacement) {
2113 let {
2114 formats,
2115 replacements,
2116 text,
2117 start,
2118 end
2119 } = _ref;
2120 text = text.replace(pattern, function (match) {
2121 for (var _len = arguments.length, rest = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
2122 rest[_key - 1] = arguments[_key];
2123 }
2124
2125 const offset = rest[rest.length - 2];
2126 let newText = replacement;
2127 let newFormats;
2128 let newReplacements;
2129
2130 if (typeof newText === 'function') {
2131 newText = replacement(match, ...rest);
2132 }
2133
2134 if (typeof newText === 'object') {
2135 newFormats = newText.formats;
2136 newReplacements = newText.replacements;
2137 newText = newText.text;
2138 } else {
2139 newFormats = Array(newText.length);
2140 newReplacements = Array(newText.length);
2141
2142 if (formats[offset]) {
2143 newFormats = newFormats.fill(formats[offset]);
2144 }
2145 }
2146
2147 formats = formats.slice(0, offset).concat(newFormats, formats.slice(offset + match.length));
2148 replacements = replacements.slice(0, offset).concat(newReplacements, replacements.slice(offset + match.length));
2149
2150 if (start) {
2151 start = end = offset + newText.length;
2152 }
2153
2154 return newText;
2155 });
2156 return normaliseFormats({
2157 formats,
2158 replacements,
2159 text,
2160 start,
2161 end
2162 });
2163 }
2164
2165 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/insert-line-separator.js
2166 /**
2167 * Internal dependencies
2168 */
2169
2170
2171 /** @typedef {import('./create').RichTextValue} RichTextValue */
2172
2173 /**
2174 * Insert a line break character into a Rich Text value at the given
2175 * `startIndex`. Any content between `startIndex` and `endIndex` will be
2176 * removed. Indices are retrieved from the selection if none are provided.
2177 *
2178 * @param {RichTextValue} value Value to modify.
2179 * @param {number} [startIndex] Start index.
2180 * @param {number} [endIndex] End index.
2181 *
2182 * @return {RichTextValue} A new value with the value inserted.
2183 */
2184
2185 function insertLineSeparator(value) {
2186 let startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : value.start;
2187 let endIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : value.end;
2188 const beforeText = value.text.slice(0, startIndex);
2189 const previousLineSeparatorIndex = beforeText.lastIndexOf(LINE_SEPARATOR);
2190 const previousLineSeparatorFormats = value.replacements[previousLineSeparatorIndex];
2191 let replacements = [,];
2192
2193 if (previousLineSeparatorFormats) {
2194 replacements = [previousLineSeparatorFormats];
2195 }
2196
2197 const valueToInsert = {
2198 formats: [,],
2199 replacements,
2200 text: LINE_SEPARATOR
2201 };
2202 return insert(value, valueToInsert, startIndex, endIndex);
2203 }
2204
2205 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/remove-line-separator.js
2206 /**
2207 * Internal dependencies
2208 */
2209
2210
2211
2212 /** @typedef {import('./create').RichTextValue} RichTextValue */
2213
2214 /**
2215 * Removes a line separator character, if existing, from a Rich Text value at
2216 * the current indices. If no line separator exists on the indices it will
2217 * return undefined.
2218 *
2219 * @param {RichTextValue} value Value to modify.
2220 * @param {boolean} backward Indicates if are removing from the start
2221 * index or the end index.
2222 *
2223 * @return {RichTextValue|undefined} A new value with the line separator
2224 * removed. Or undefined if no line separator
2225 * is found on the position.
2226 */
2227
2228 function removeLineSeparator(value) {
2229 let backward = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
2230 const {
2231 replacements,
2232 text,
2233 start,
2234 end
2235 } = value;
2236 const collapsed = isCollapsed(value);
2237 let index = start - 1;
2238 let removeStart = collapsed ? start - 1 : start;
2239 let removeEnd = end;
2240
2241 if (!backward) {
2242 index = end;
2243 removeStart = start;
2244 removeEnd = collapsed ? end + 1 : end;
2245 }
2246
2247 if (text[index] !== LINE_SEPARATOR) {
2248 return;
2249 }
2250
2251 let newValue; // If the line separator that is about te be removed
2252 // contains wrappers, remove the wrappers first.
2253
2254 if (collapsed && replacements[index] && replacements[index].length) {
2255 const newReplacements = replacements.slice();
2256 newReplacements[index] = replacements[index].slice(0, -1);
2257 newValue = { ...value,
2258 replacements: newReplacements
2259 };
2260 } else {
2261 newValue = remove(value, removeStart, removeEnd);
2262 }
2263
2264 return newValue;
2265 }
2266
2267 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/insert-object.js
2268 /**
2269 * Internal dependencies
2270 */
2271
2272
2273 /** @typedef {import('./create').RichTextValue} RichTextValue */
2274
2275 /** @typedef {import('./create').RichTextFormat} RichTextFormat */
2276
2277 /**
2278 * Insert a format as an object into a Rich Text value at the given
2279 * `startIndex`. Any content between `startIndex` and `endIndex` will be
2280 * removed. Indices are retrieved from the selection if none are provided.
2281 *
2282 * @param {RichTextValue} value Value to modify.
2283 * @param {RichTextFormat} formatToInsert Format to insert as object.
2284 * @param {number} [startIndex] Start index.
2285 * @param {number} [endIndex] End index.
2286 *
2287 * @return {RichTextValue} A new value with the object inserted.
2288 */
2289
2290 function insertObject(value, formatToInsert, startIndex, endIndex) {
2291 const valueToInsert = {
2292 formats: [,],
2293 replacements: [formatToInsert],
2294 text: OBJECT_REPLACEMENT_CHARACTER
2295 };
2296 return insert(value, valueToInsert, startIndex, endIndex);
2297 }
2298
2299 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/slice.js
2300 /** @typedef {import('./create').RichTextValue} RichTextValue */
2301
2302 /**
2303 * Slice a Rich Text value from `startIndex` to `endIndex`. Indices are
2304 * retrieved from the selection if none are provided. This is similar to
2305 * `String.prototype.slice`.
2306 *
2307 * @param {RichTextValue} value Value to modify.
2308 * @param {number} [startIndex] Start index.
2309 * @param {number} [endIndex] End index.
2310 *
2311 * @return {RichTextValue} A new extracted value.
2312 */
2313 function slice(value) {
2314 let startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : value.start;
2315 let endIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : value.end;
2316 const {
2317 formats,
2318 replacements,
2319 text
2320 } = value;
2321
2322 if (startIndex === undefined || endIndex === undefined) {
2323 return { ...value
2324 };
2325 }
2326
2327 return {
2328 formats: formats.slice(startIndex, endIndex),
2329 replacements: replacements.slice(startIndex, endIndex),
2330 text: text.slice(startIndex, endIndex)
2331 };
2332 }
2333
2334 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/split.js
2335 /**
2336 * Internal dependencies
2337 */
2338
2339 /** @typedef {import('./create').RichTextValue} RichTextValue */
2340
2341 /**
2342 * Split a Rich Text value in two at the given `startIndex` and `endIndex`, or
2343 * split at the given separator. This is similar to `String.prototype.split`.
2344 * Indices are retrieved from the selection if none are provided.
2345 *
2346 * @param {RichTextValue} value
2347 * @param {number|string} [string] Start index, or string at which to split.
2348 *
2349 * @return {Array<RichTextValue>|undefined} An array of new values.
2350 */
2351
2352 function split(_ref, string) {
2353 let {
2354 formats,
2355 replacements,
2356 text,
2357 start,
2358 end
2359 } = _ref;
2360
2361 if (typeof string !== 'string') {
2362 return splitAtSelection(...arguments);
2363 }
2364
2365 let nextStart = 0;
2366 return text.split(string).map(substring => {
2367 const startIndex = nextStart;
2368 const value = {
2369 formats: formats.slice(startIndex, startIndex + substring.length),
2370 replacements: replacements.slice(startIndex, startIndex + substring.length),
2371 text: substring
2372 };
2373 nextStart += string.length + substring.length;
2374
2375 if (start !== undefined && end !== undefined) {
2376 if (start >= startIndex && start < nextStart) {
2377 value.start = start - startIndex;
2378 } else if (start < startIndex && end > startIndex) {
2379 value.start = 0;
2380 }
2381
2382 if (end >= startIndex && end < nextStart) {
2383 value.end = end - startIndex;
2384 } else if (start < nextStart && end > nextStart) {
2385 value.end = substring.length;
2386 }
2387 }
2388
2389 return value;
2390 });
2391 }
2392
2393 function splitAtSelection(_ref2) {
2394 let {
2395 formats,
2396 replacements,
2397 text,
2398 start,
2399 end
2400 } = _ref2;
2401 let startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : start;
2402 let endIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : end;
2403
2404 if (start === undefined || end === undefined) {
2405 return;
2406 }
2407
2408 const before = {
2409 formats: formats.slice(0, startIndex),
2410 replacements: replacements.slice(0, startIndex),
2411 text: text.slice(0, startIndex)
2412 };
2413 const after = {
2414 formats: formats.slice(endIndex),
2415 replacements: replacements.slice(endIndex),
2416 text: text.slice(endIndex),
2417 start: 0,
2418 end: 0
2419 };
2420 return [// Ensure newlines are trimmed.
2421 replace_replace(before, /\u2028+$/, ''), replace_replace(after, /^\u2028+/, '')];
2422 }
2423
2424 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/get-format-type.js
2425 /**
2426 * WordPress dependencies
2427 */
2428
2429 /**
2430 * Internal dependencies
2431 */
2432
2433
2434 /** @typedef {import('./register-format-type').RichTextFormatType} RichTextFormatType */
2435
2436 /**
2437 * Returns a registered format type.
2438 *
2439 * @param {string} name Format name.
2440 *
2441 * @return {RichTextFormatType|undefined} Format type.
2442 */
2443
2444 function get_format_type_getFormatType(name) {
2445 return (0,external_wp_data_namespaceObject.select)(store).getFormatType(name);
2446 }
2447
2448 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/to-tree.js
2449 /**
2450 * Internal dependencies
2451 */
2452
2453
2454
2455
2456 function restoreOnAttributes(attributes, isEditableTree) {
2457 if (isEditableTree) {
2458 return attributes;
2459 }
2460
2461 const newAttributes = {};
2462
2463 for (const key in attributes) {
2464 let newKey = key;
2465
2466 if (key.startsWith('data-disable-rich-text-')) {
2467 newKey = key.slice('data-disable-rich-text-'.length);
2468 }
2469
2470 newAttributes[newKey] = attributes[key];
2471 }
2472
2473 return newAttributes;
2474 }
2475 /**
2476 * Converts a format object to information that can be used to create an element
2477 * from (type, attributes and object).
2478 *
2479 * @param {Object} $1 Named parameters.
2480 * @param {string} $1.type The format type.
2481 * @param {Object} $1.attributes The format attributes.
2482 * @param {Object} $1.unregisteredAttributes The unregistered format
2483 * attributes.
2484 * @param {boolean} $1.object Whether or not it is an object
2485 * format.
2486 * @param {boolean} $1.boundaryClass Whether or not to apply a boundary
2487 * class.
2488 * @param {boolean} $1.isEditableTree
2489 *
2490 * @return {Object} Information to be used for element creation.
2491 */
2492
2493
2494 function fromFormat(_ref) {
2495 let {
2496 type,
2497 attributes,
2498 unregisteredAttributes,
2499 object,
2500 boundaryClass,
2501 isEditableTree
2502 } = _ref;
2503 const formatType = get_format_type_getFormatType(type);
2504 let elementAttributes = {};
2505
2506 if (boundaryClass) {
2507 elementAttributes['data-rich-text-format-boundary'] = 'true';
2508 }
2509
2510 if (!formatType) {
2511 if (attributes) {
2512 elementAttributes = { ...attributes,
2513 ...elementAttributes
2514 };
2515 }
2516
2517 return {
2518 type,
2519 attributes: restoreOnAttributes(elementAttributes, isEditableTree),
2520 object
2521 };
2522 }
2523
2524 elementAttributes = { ...unregisteredAttributes,
2525 ...elementAttributes
2526 };
2527
2528 for (const name in attributes) {
2529 const key = formatType.attributes ? formatType.attributes[name] : false;
2530
2531 if (key) {
2532 elementAttributes[key] = attributes[name];
2533 } else {
2534 elementAttributes[name] = attributes[name];
2535 }
2536 }
2537
2538 if (formatType.className) {
2539 if (elementAttributes.class) {
2540 elementAttributes.class = `${formatType.className} ${elementAttributes.class}`;
2541 } else {
2542 elementAttributes.class = formatType.className;
2543 }
2544 }
2545
2546 return {
2547 type: formatType.tagName,
2548 object: formatType.object,
2549 attributes: restoreOnAttributes(elementAttributes, isEditableTree)
2550 };
2551 }
2552 /**
2553 * Checks if both arrays of formats up until a certain index are equal.
2554 *
2555 * @param {Array} a Array of formats to compare.
2556 * @param {Array} b Array of formats to compare.
2557 * @param {number} index Index to check until.
2558 */
2559
2560
2561 function isEqualUntil(a, b, index) {
2562 do {
2563 if (a[index] !== b[index]) {
2564 return false;
2565 }
2566 } while (index--);
2567
2568 return true;
2569 }
2570
2571 function toTree(_ref2) {
2572 let {
2573 value,
2574 multilineTag,
2575 preserveWhiteSpace,
2576 createEmpty,
2577 append,
2578 getLastChild,
2579 getParent,
2580 isText,
2581 getText,
2582 remove,
2583 appendText,
2584 onStartIndex,
2585 onEndIndex,
2586 isEditableTree,
2587 placeholder
2588 } = _ref2;
2589 const {
2590 formats,
2591 replacements,
2592 text,
2593 start,
2594 end
2595 } = value;
2596 const formatsLength = formats.length + 1;
2597 const tree = createEmpty();
2598 const multilineFormat = {
2599 type: multilineTag
2600 };
2601 const activeFormats = getActiveFormats(value);
2602 const deepestActiveFormat = activeFormats[activeFormats.length - 1];
2603 let lastSeparatorFormats;
2604 let lastCharacterFormats;
2605 let lastCharacter; // If we're building a multiline tree, start off with a multiline element.
2606
2607 if (multilineTag) {
2608 append(append(tree, {
2609 type: multilineTag
2610 }), '');
2611 lastCharacterFormats = lastSeparatorFormats = [multilineFormat];
2612 } else {
2613 append(tree, '');
2614 }
2615
2616 for (let i = 0; i < formatsLength; i++) {
2617 const character = text.charAt(i);
2618 const shouldInsertPadding = isEditableTree && ( // Pad the line if the line is empty.
2619 !lastCharacter || lastCharacter === LINE_SEPARATOR || // Pad the line if the previous character is a line break, otherwise
2620 // the line break won't be visible.
2621 lastCharacter === '\n');
2622 let characterFormats = formats[i]; // Set multiline tags in queue for building the tree.
2623
2624 if (multilineTag) {
2625 if (character === LINE_SEPARATOR) {
2626 characterFormats = lastSeparatorFormats = (replacements[i] || []).reduce((accumulator, format) => {
2627 accumulator.push(format, multilineFormat);
2628 return accumulator;
2629 }, [multilineFormat]);
2630 } else {
2631 characterFormats = [...lastSeparatorFormats, ...(characterFormats || [])];
2632 }
2633 }
2634
2635 let pointer = getLastChild(tree);
2636
2637 if (shouldInsertPadding && character === LINE_SEPARATOR) {
2638 let node = pointer;
2639
2640 while (!isText(node)) {
2641 node = getLastChild(node);
2642 }
2643
2644 append(getParent(node), ZWNBSP);
2645 } // Set selection for the start of line.
2646
2647
2648 if (lastCharacter === LINE_SEPARATOR) {
2649 let node = pointer;
2650
2651 while (!isText(node)) {
2652 node = getLastChild(node);
2653 }
2654
2655 if (onStartIndex && start === i) {
2656 onStartIndex(tree, node);
2657 }
2658
2659 if (onEndIndex && end === i) {
2660 onEndIndex(tree, node);
2661 }
2662 }
2663
2664 if (characterFormats) {
2665 characterFormats.forEach((format, formatIndex) => {
2666 if (pointer && lastCharacterFormats && // Reuse the last element if all formats remain the same.
2667 isEqualUntil(characterFormats, lastCharacterFormats, formatIndex) && ( // Do not reuse the last element if the character is a
2668 // line separator.
2669 character !== LINE_SEPARATOR || characterFormats.length - 1 !== formatIndex)) {
2670 pointer = getLastChild(pointer);
2671 return;
2672 }
2673
2674 const {
2675 type,
2676 attributes,
2677 unregisteredAttributes
2678 } = format;
2679 const boundaryClass = isEditableTree && character !== LINE_SEPARATOR && format === deepestActiveFormat;
2680 const parent = getParent(pointer);
2681 const newNode = append(parent, fromFormat({
2682 type,
2683 attributes,
2684 unregisteredAttributes,
2685 boundaryClass,
2686 isEditableTree
2687 }));
2688
2689 if (isText(pointer) && getText(pointer).length === 0) {
2690 remove(pointer);
2691 }
2692
2693 pointer = append(newNode, '');
2694 });
2695 } // No need for further processing if the character is a line separator.
2696
2697
2698 if (character === LINE_SEPARATOR) {
2699 lastCharacterFormats = characterFormats;
2700 lastCharacter = character;
2701 continue;
2702 } // If there is selection at 0, handle it before characters are inserted.
2703
2704
2705 if (i === 0) {
2706 if (onStartIndex && start === 0) {
2707 onStartIndex(tree, pointer);
2708 }
2709
2710 if (onEndIndex && end === 0) {
2711 onEndIndex(tree, pointer);
2712 }
2713 }
2714
2715 if (character === OBJECT_REPLACEMENT_CHARACTER) {
2716 var _replacements$i;
2717
2718 if (!isEditableTree && ((_replacements$i = replacements[i]) === null || _replacements$i === void 0 ? void 0 : _replacements$i.type) === 'script') {
2719 pointer = append(getParent(pointer), fromFormat({
2720 type: 'script',
2721 isEditableTree
2722 }));
2723 append(pointer, {
2724 html: decodeURIComponent(replacements[i].attributes['data-rich-text-script'])
2725 });
2726 } else {
2727 pointer = append(getParent(pointer), fromFormat({ ...replacements[i],
2728 object: true,
2729 isEditableTree
2730 }));
2731 } // Ensure pointer is text node.
2732
2733
2734 pointer = append(getParent(pointer), '');
2735 } else if (!preserveWhiteSpace && character === '\n') {
2736 pointer = append(getParent(pointer), {
2737 type: 'br',
2738 attributes: isEditableTree ? {
2739 'data-rich-text-line-break': 'true'
2740 } : undefined,
2741 object: true
2742 }); // Ensure pointer is text node.
2743
2744 pointer = append(getParent(pointer), '');
2745 } else if (!isText(pointer)) {
2746 pointer = append(getParent(pointer), character);
2747 } else {
2748 appendText(pointer, character);
2749 }
2750
2751 if (onStartIndex && start === i + 1) {
2752 onStartIndex(tree, pointer);
2753 }
2754
2755 if (onEndIndex && end === i + 1) {
2756 onEndIndex(tree, pointer);
2757 }
2758
2759 if (shouldInsertPadding && i === text.length) {
2760 append(getParent(pointer), ZWNBSP);
2761
2762 if (placeholder && text.length === 0) {
2763 append(getParent(pointer), {
2764 type: 'span',
2765 attributes: {
2766 'data-rich-text-placeholder': placeholder,
2767 // Necessary to prevent the placeholder from catching
2768 // selection. The placeholder is also not editable after
2769 // all.
2770 contenteditable: 'false',
2771 style: 'pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;'
2772 }
2773 });
2774 }
2775 }
2776
2777 lastCharacterFormats = characterFormats;
2778 lastCharacter = character;
2779 }
2780
2781 return tree;
2782 }
2783
2784 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/to-dom.js
2785 /**
2786 * Internal dependencies
2787 */
2788
2789
2790 /** @typedef {import('./create').RichTextValue} RichTextValue */
2791
2792 /**
2793 * Creates a path as an array of indices from the given root node to the given
2794 * node.
2795 *
2796 * @param {Node} node Node to find the path of.
2797 * @param {HTMLElement} rootNode Root node to find the path from.
2798 * @param {Array} path Initial path to build on.
2799 *
2800 * @return {Array} The path from the root node to the node.
2801 */
2802
2803 function createPathToNode(node, rootNode, path) {
2804 const parentNode = node.parentNode;
2805 let i = 0;
2806
2807 while (node = node.previousSibling) {
2808 i++;
2809 }
2810
2811 path = [i, ...path];
2812
2813 if (parentNode !== rootNode) {
2814 path = createPathToNode(parentNode, rootNode, path);
2815 }
2816
2817 return path;
2818 }
2819 /**
2820 * Gets a node given a path (array of indices) from the given node.
2821 *
2822 * @param {HTMLElement} node Root node to find the wanted node in.
2823 * @param {Array} path Path (indices) to the wanted node.
2824 *
2825 * @return {Object} Object with the found node and the remaining offset (if any).
2826 */
2827
2828
2829 function getNodeByPath(node, path) {
2830 path = [...path];
2831
2832 while (node && path.length > 1) {
2833 node = node.childNodes[path.shift()];
2834 }
2835
2836 return {
2837 node,
2838 offset: path[0]
2839 };
2840 }
2841
2842 function append(element, child) {
2843 if (typeof child === 'string') {
2844 child = element.ownerDocument.createTextNode(child);
2845 }
2846
2847 const {
2848 type,
2849 attributes
2850 } = child;
2851
2852 if (type) {
2853 child = element.ownerDocument.createElement(type);
2854
2855 for (const key in attributes) {
2856 child.setAttribute(key, attributes[key]);
2857 }
2858 }
2859
2860 return element.appendChild(child);
2861 }
2862
2863 function appendText(node, text) {
2864 node.appendData(text);
2865 }
2866
2867 function getLastChild(_ref) {
2868 let {
2869 lastChild
2870 } = _ref;
2871 return lastChild;
2872 }
2873
2874 function getParent(_ref2) {
2875 let {
2876 parentNode
2877 } = _ref2;
2878 return parentNode;
2879 }
2880
2881 function isText(node) {
2882 return node.nodeType === node.TEXT_NODE;
2883 }
2884
2885 function getText(_ref3) {
2886 let {
2887 nodeValue
2888 } = _ref3;
2889 return nodeValue;
2890 }
2891
2892 function to_dom_remove(node) {
2893 return node.parentNode.removeChild(node);
2894 }
2895
2896 function toDom(_ref4) {
2897 let {
2898 value,
2899 multilineTag,
2900 prepareEditableTree,
2901 isEditableTree = true,
2902 placeholder,
2903 doc = document
2904 } = _ref4;
2905 let startPath = [];
2906 let endPath = [];
2907
2908 if (prepareEditableTree) {
2909 value = { ...value,
2910 formats: prepareEditableTree(value)
2911 };
2912 }
2913 /**
2914 * Returns a new instance of a DOM tree upon which RichText operations can be
2915 * applied.
2916 *
2917 * Note: The current implementation will return a shared reference, reset on
2918 * each call to `createEmpty`. Therefore, you should not hold a reference to
2919 * the value to operate upon asynchronously, as it may have unexpected results.
2920 *
2921 * @return {Object} RichText tree.
2922 */
2923
2924
2925 const createEmpty = () => createElement(doc, '');
2926
2927 const tree = toTree({
2928 value,
2929 multilineTag,
2930 createEmpty,
2931 append,
2932 getLastChild,
2933 getParent,
2934 isText,
2935 getText,
2936 remove: to_dom_remove,
2937 appendText,
2938
2939 onStartIndex(body, pointer) {
2940 startPath = createPathToNode(pointer, body, [pointer.nodeValue.length]);
2941 },
2942
2943 onEndIndex(body, pointer) {
2944 endPath = createPathToNode(pointer, body, [pointer.nodeValue.length]);
2945 },
2946
2947 isEditableTree,
2948 placeholder
2949 });
2950 return {
2951 body: tree,
2952 selection: {
2953 startPath,
2954 endPath
2955 }
2956 };
2957 }
2958 /**
2959 * Create an `Element` tree from a Rich Text value and applies the difference to
2960 * the `Element` tree contained by `current`. If a `multilineTag` is provided,
2961 * text separated by two new lines will be wrapped in an `Element` of that type.
2962 *
2963 * @param {Object} $1 Named arguments.
2964 * @param {RichTextValue} $1.value Value to apply.
2965 * @param {HTMLElement} $1.current The live root node to apply the element tree to.
2966 * @param {string} [$1.multilineTag] Multiline tag.
2967 * @param {Function} [$1.prepareEditableTree] Function to filter editorable formats.
2968 * @param {boolean} [$1.__unstableDomOnly] Only apply elements, no selection.
2969 * @param {string} [$1.placeholder] Placeholder text.
2970 */
2971
2972 function apply(_ref5) {
2973 let {
2974 value,
2975 current,
2976 multilineTag,
2977 prepareEditableTree,
2978 __unstableDomOnly,
2979 placeholder
2980 } = _ref5;
2981 // Construct a new element tree in memory.
2982 const {
2983 body,
2984 selection
2985 } = toDom({
2986 value,
2987 multilineTag,
2988 prepareEditableTree,
2989 placeholder,
2990 doc: current.ownerDocument
2991 });
2992 applyValue(body, current);
2993
2994 if (value.start !== undefined && !__unstableDomOnly) {
2995 applySelection(selection, current);
2996 }
2997 }
2998 function applyValue(future, current) {
2999 let i = 0;
3000 let futureChild;
3001
3002 while (futureChild = future.firstChild) {
3003 const currentChild = current.childNodes[i];
3004
3005 if (!currentChild) {
3006 current.appendChild(futureChild);
3007 } else if (!currentChild.isEqualNode(futureChild)) {
3008 if (currentChild.nodeName !== futureChild.nodeName || currentChild.nodeType === currentChild.TEXT_NODE && currentChild.data !== futureChild.data) {
3009 current.replaceChild(futureChild, currentChild);
3010 } else {
3011 const currentAttributes = currentChild.attributes;
3012 const futureAttributes = futureChild.attributes;
3013
3014 if (currentAttributes) {
3015 let ii = currentAttributes.length; // Reverse loop because `removeAttribute` on `currentChild`
3016 // changes `currentAttributes`.
3017
3018 while (ii--) {
3019 const {
3020 name
3021 } = currentAttributes[ii];
3022
3023 if (!futureChild.getAttribute(name)) {
3024 currentChild.removeAttribute(name);
3025 }
3026 }
3027 }
3028
3029 if (futureAttributes) {
3030 for (let ii = 0; ii < futureAttributes.length; ii++) {
3031 const {
3032 name,
3033 value
3034 } = futureAttributes[ii];
3035
3036 if (currentChild.getAttribute(name) !== value) {
3037 currentChild.setAttribute(name, value);
3038 }
3039 }
3040 }
3041
3042 applyValue(futureChild, currentChild);
3043 future.removeChild(futureChild);
3044 }
3045 } else {
3046 future.removeChild(futureChild);
3047 }
3048
3049 i++;
3050 }
3051
3052 while (current.childNodes[i]) {
3053 current.removeChild(current.childNodes[i]);
3054 }
3055 }
3056 /**
3057 * Returns true if two ranges are equal, or false otherwise. Ranges are
3058 * considered equal if their start and end occur in the same container and
3059 * offset.
3060 *
3061 * @param {Range} a First range object to test.
3062 * @param {Range} b First range object to test.
3063 *
3064 * @return {boolean} Whether the two ranges are equal.
3065 */
3066
3067 function isRangeEqual(a, b) {
3068 return a.startContainer === b.startContainer && a.startOffset === b.startOffset && a.endContainer === b.endContainer && a.endOffset === b.endOffset;
3069 }
3070
3071 function applySelection(_ref6, current) {
3072 let {
3073 startPath,
3074 endPath
3075 } = _ref6;
3076 const {
3077 node: startContainer,
3078 offset: startOffset
3079 } = getNodeByPath(current, startPath);
3080 const {
3081 node: endContainer,
3082 offset: endOffset
3083 } = getNodeByPath(current, endPath);
3084 const {
3085 ownerDocument
3086 } = current;
3087 const {
3088 defaultView
3089 } = ownerDocument;
3090 const selection = defaultView.getSelection();
3091 const range = ownerDocument.createRange();
3092 range.setStart(startContainer, startOffset);
3093 range.setEnd(endContainer, endOffset);
3094 const {
3095 activeElement
3096 } = ownerDocument;
3097
3098 if (selection.rangeCount > 0) {
3099 // If the to be added range and the live range are the same, there's no
3100 // need to remove the live range and add the equivalent range.
3101 if (isRangeEqual(range, selection.getRangeAt(0))) {
3102 return;
3103 }
3104
3105 selection.removeAllRanges();
3106 }
3107
3108 selection.addRange(range); // This function is not intended to cause a shift in focus. Since the above
3109 // selection manipulations may shift focus, ensure that focus is restored to
3110 // its previous state.
3111
3112 if (activeElement !== ownerDocument.activeElement) {
3113 // The `instanceof` checks protect against edge cases where the focused
3114 // element is not of the interface HTMLElement (does not have a `focus`
3115 // or `blur` property).
3116 //
3117 // See: https://github.com/Microsoft/TypeScript/issues/5901#issuecomment-431649653
3118 if (activeElement instanceof defaultView.HTMLElement) {
3119 activeElement.focus();
3120 }
3121 }
3122 }
3123
3124 ;// CONCATENATED MODULE: external ["wp","escapeHtml"]
3125 const external_wp_escapeHtml_namespaceObject = window["wp"]["escapeHtml"];
3126 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/to-html-string.js
3127 /**
3128 * WordPress dependencies
3129 */
3130
3131 /**
3132 * Internal dependencies
3133 */
3134
3135
3136 /** @typedef {import('./create').RichTextValue} RichTextValue */
3137
3138 /**
3139 * Create an HTML string from a Rich Text value. If a `multilineTag` is
3140 * provided, text separated by a line separator will be wrapped in it.
3141 *
3142 * @param {Object} $1 Named argements.
3143 * @param {RichTextValue} $1.value Rich text value.
3144 * @param {string} [$1.multilineTag] Multiline tag.
3145 * @param {boolean} [$1.preserveWhiteSpace] Whether or not to use newline
3146 * characters for line breaks.
3147 *
3148 * @return {string} HTML string.
3149 */
3150
3151 function toHTMLString(_ref) {
3152 let {
3153 value,
3154 multilineTag,
3155 preserveWhiteSpace
3156 } = _ref;
3157 const tree = toTree({
3158 value,
3159 multilineTag,
3160 preserveWhiteSpace,
3161 createEmpty,
3162 append: to_html_string_append,
3163 getLastChild: to_html_string_getLastChild,
3164 getParent: to_html_string_getParent,
3165 isText: to_html_string_isText,
3166 getText: to_html_string_getText,
3167 remove: to_html_string_remove,
3168 appendText: to_html_string_appendText
3169 });
3170 return createChildrenHTML(tree.children);
3171 }
3172
3173 function createEmpty() {
3174 return {};
3175 }
3176
3177 function to_html_string_getLastChild(_ref2) {
3178 let {
3179 children
3180 } = _ref2;
3181 return children && children[children.length - 1];
3182 }
3183
3184 function to_html_string_append(parent, object) {
3185 if (typeof object === 'string') {
3186 object = {
3187 text: object
3188 };
3189 }
3190
3191 object.parent = parent;
3192 parent.children = parent.children || [];
3193 parent.children.push(object);
3194 return object;
3195 }
3196
3197 function to_html_string_appendText(object, text) {
3198 object.text += text;
3199 }
3200
3201 function to_html_string_getParent(_ref3) {
3202 let {
3203 parent
3204 } = _ref3;
3205 return parent;
3206 }
3207
3208 function to_html_string_isText(_ref4) {
3209 let {
3210 text
3211 } = _ref4;
3212 return typeof text === 'string';
3213 }
3214
3215 function to_html_string_getText(_ref5) {
3216 let {
3217 text
3218 } = _ref5;
3219 return text;
3220 }
3221
3222 function to_html_string_remove(object) {
3223 const index = object.parent.children.indexOf(object);
3224
3225 if (index !== -1) {
3226 object.parent.children.splice(index, 1);
3227 }
3228
3229 return object;
3230 }
3231
3232 function createElementHTML(_ref6) {
3233 let {
3234 type,
3235 attributes,
3236 object,
3237 children
3238 } = _ref6;
3239 let attributeString = '';
3240
3241 for (const key in attributes) {
3242 if (!(0,external_wp_escapeHtml_namespaceObject.isValidAttributeName)(key)) {
3243 continue;
3244 }
3245
3246 attributeString += ` ${key}="${(0,external_wp_escapeHtml_namespaceObject.escapeAttribute)(attributes[key])}"`;
3247 }
3248
3249 if (object) {
3250 return `<${type}${attributeString}>`;
3251 }
3252
3253 return `<${type}${attributeString}>${createChildrenHTML(children)}</${type}>`;
3254 }
3255
3256 function createChildrenHTML() {
3257 let children = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
3258 return children.map(child => {
3259 if (child.html !== undefined) {
3260 return child.html;
3261 }
3262
3263 return child.text === undefined ? createElementHTML(child) : (0,external_wp_escapeHtml_namespaceObject.escapeEditableHTML)(child.text);
3264 }).join('');
3265 }
3266
3267 ;// CONCATENATED MODULE: external ["wp","a11y"]
3268 const external_wp_a11y_namespaceObject = window["wp"]["a11y"];
3269 ;// CONCATENATED MODULE: external ["wp","i18n"]
3270 const external_wp_i18n_namespaceObject = window["wp"]["i18n"];
3271 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/toggle-format.js
3272 /**
3273 * WordPress dependencies
3274 */
3275
3276
3277 /**
3278 * Internal dependencies
3279 */
3280
3281
3282
3283
3284 /** @typedef {import('./create').RichTextValue} RichTextValue */
3285
3286 /** @typedef {import('./create').RichTextFormat} RichTextFormat */
3287
3288 /**
3289 * Toggles a format object to a Rich Text value at the current selection.
3290 *
3291 * @param {RichTextValue} value Value to modify.
3292 * @param {RichTextFormat} format Format to apply or remove.
3293 *
3294 * @return {RichTextValue} A new value with the format applied or removed.
3295 */
3296
3297 function toggleFormat(value, format) {
3298 if (getActiveFormat(value, format.type)) {
3299 // For screen readers, will announce if formatting control is disabled.
3300 if (format.title) {
3301 // translators: %s: title of the formatting control
3302 (0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.sprintf)((0,external_wp_i18n_namespaceObject.__)('%s removed.'), format.title), 'assertive');
3303 }
3304
3305 return removeFormat(value, format.type);
3306 } // For screen readers, will announce if formatting control is enabled.
3307
3308
3309 if (format.title) {
3310 // translators: %s: title of the formatting control
3311 (0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.sprintf)((0,external_wp_i18n_namespaceObject.__)('%s applied.'), format.title), 'assertive');
3312 }
3313
3314 return applyFormat(value, format);
3315 }
3316
3317 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/unregister-format-type.js
3318 /**
3319 * WordPress dependencies
3320 */
3321
3322 /**
3323 * Internal dependencies
3324 */
3325
3326
3327 /** @typedef {import('./register-format-type').RichTextFormatType} RichTextFormatType */
3328
3329 /**
3330 * Unregisters a format.
3331 *
3332 * @param {string} name Format name.
3333 *
3334 * @return {RichTextFormatType|undefined} The previous format value, if it has
3335 * been successfully unregistered;
3336 * otherwise `undefined`.
3337 */
3338
3339 function unregisterFormatType(name) {
3340 const oldFormat = (0,external_wp_data_namespaceObject.select)(store).getFormatType(name);
3341
3342 if (!oldFormat) {
3343 window.console.error(`Format ${name} is not registered.`);
3344 return;
3345 }
3346
3347 (0,external_wp_data_namespaceObject.dispatch)(store).removeFormatTypes(name);
3348 return oldFormat;
3349 }
3350
3351 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/can-indent-list-items.js
3352 /**
3353 * Internal dependencies
3354 */
3355
3356 /** @typedef {import('./create').RichTextValue} RichTextValue */
3357
3358 /**
3359 * Checks if the selected list item can be indented.
3360 *
3361 * @param {RichTextValue} value Value to check.
3362 *
3363 * @return {boolean} Whether or not the selected list item can be indented.
3364 */
3365
3366 function canIndentListItems(value) {
3367 const lineIndex = getLineIndex(value); // There is only one line, so the line cannot be indented.
3368
3369 if (lineIndex === undefined) {
3370 return false;
3371 }
3372
3373 const {
3374 replacements
3375 } = value;
3376 const previousLineIndex = getLineIndex(value, lineIndex);
3377 const formatsAtLineIndex = replacements[lineIndex] || [];
3378 const formatsAtPreviousLineIndex = replacements[previousLineIndex] || []; // If the indentation of the current line is greater than previous line,
3379 // then the line cannot be furter indented.
3380
3381 return formatsAtLineIndex.length <= formatsAtPreviousLineIndex.length;
3382 }
3383
3384 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/can-outdent-list-items.js
3385 /**
3386 * Internal dependencies
3387 */
3388
3389 /** @typedef {import('./create').RichTextValue} RichTextValue */
3390
3391 /**
3392 * Checks if the selected list item can be outdented.
3393 *
3394 * @param {RichTextValue} value Value to check.
3395 *
3396 * @return {boolean} Whether or not the selected list item can be outdented.
3397 */
3398
3399 function canOutdentListItems(value) {
3400 const {
3401 replacements,
3402 start
3403 } = value;
3404 const startingLineIndex = getLineIndex(value, start);
3405 return replacements[startingLineIndex] !== undefined;
3406 }
3407
3408 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/indent-list-items.js
3409 /**
3410 * Internal dependencies
3411 */
3412
3413
3414
3415 /** @typedef {import('./create').RichTextValue} RichTextValue */
3416
3417 /** @typedef {import('./create').RichTextFormat} RichTextFormat */
3418
3419 /**
3420 * Gets the line index of the first previous list item with higher indentation.
3421 *
3422 * @param {RichTextValue} value Value to search.
3423 * @param {number} lineIndex Line index of the list item to compare
3424 * with.
3425 *
3426 * @return {number|void} The line index.
3427 */
3428
3429 function getTargetLevelLineIndex(_ref, lineIndex) {
3430 let {
3431 text,
3432 replacements
3433 } = _ref;
3434 const startFormats = replacements[lineIndex] || [];
3435 let index = lineIndex;
3436
3437 while (index-- >= 0) {
3438 if (text[index] !== LINE_SEPARATOR) {
3439 continue;
3440 }
3441
3442 const formatsAtIndex = replacements[index] || []; // Return the first line index that is one level higher. If the level is
3443 // lower or equal, there is no result.
3444
3445 if (formatsAtIndex.length === startFormats.length + 1) {
3446 return index;
3447 } else if (formatsAtIndex.length <= startFormats.length) {
3448 return;
3449 }
3450 }
3451 }
3452 /**
3453 * Indents any selected list items if possible.
3454 *
3455 * @param {RichTextValue} value Value to change.
3456 * @param {RichTextFormat} rootFormat Root format.
3457 *
3458 * @return {RichTextValue} The changed value.
3459 */
3460
3461
3462 function indentListItems(value, rootFormat) {
3463 if (!canIndentListItems(value)) {
3464 return value;
3465 }
3466
3467 const lineIndex = getLineIndex(value);
3468 const previousLineIndex = getLineIndex(value, lineIndex);
3469 const {
3470 text,
3471 replacements,
3472 end
3473 } = value;
3474 const newFormats = replacements.slice();
3475 const targetLevelLineIndex = getTargetLevelLineIndex(value, lineIndex);
3476
3477 for (let index = lineIndex; index < end; index++) {
3478 if (text[index] !== LINE_SEPARATOR) {
3479 continue;
3480 } // Get the previous list, and if there's a child list, take over the
3481 // formats. If not, duplicate the last level and create a new level.
3482
3483
3484 if (targetLevelLineIndex) {
3485 const targetFormats = replacements[targetLevelLineIndex] || [];
3486 newFormats[index] = targetFormats.concat((newFormats[index] || []).slice(targetFormats.length - 1));
3487 } else {
3488 const targetFormats = replacements[previousLineIndex] || [];
3489 const lastformat = targetFormats[targetFormats.length - 1] || rootFormat;
3490 newFormats[index] = targetFormats.concat([lastformat], (newFormats[index] || []).slice(targetFormats.length));
3491 }
3492 }
3493
3494 return { ...value,
3495 replacements: newFormats
3496 };
3497 }
3498
3499 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/get-parent-line-index.js
3500 /**
3501 * Internal dependencies
3502 */
3503
3504 /** @typedef {import('./create').RichTextValue} RichTextValue */
3505
3506 /**
3507 * Gets the index of the first parent list. To get the parent list formats, we
3508 * go through every list item until we find one with exactly one format type
3509 * less.
3510 *
3511 * @param {RichTextValue} value Value to search.
3512 * @param {number} lineIndex Line index of a child list item.
3513 *
3514 * @return {number|void} The parent list line index.
3515 */
3516
3517 function getParentLineIndex(_ref, lineIndex) {
3518 let {
3519 text,
3520 replacements
3521 } = _ref;
3522 const startFormats = replacements[lineIndex] || [];
3523 let index = lineIndex;
3524
3525 while (index-- >= 0) {
3526 if (text[index] !== LINE_SEPARATOR) {
3527 continue;
3528 }
3529
3530 const formatsAtIndex = replacements[index] || [];
3531
3532 if (formatsAtIndex.length === startFormats.length - 1) {
3533 return index;
3534 }
3535 }
3536 }
3537
3538 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/get-last-child-index.js
3539 /**
3540 * Internal dependencies
3541 */
3542
3543 /** @typedef {import('./create').RichTextValue} RichTextValue */
3544
3545 /**
3546 * Gets the line index of the last child in the list.
3547 *
3548 * @param {RichTextValue} value Value to search.
3549 * @param {number} lineIndex Line index of a list item in the list.
3550 *
3551 * @return {number} The index of the last child.
3552 */
3553
3554 function getLastChildIndex(_ref, lineIndex) {
3555 let {
3556 text,
3557 replacements
3558 } = _ref;
3559 const lineFormats = replacements[lineIndex] || []; // Use the given line index in case there are no next children.
3560
3561 let childIndex = lineIndex; // `lineIndex` could be `undefined` if it's the first line.
3562
3563 for (let index = lineIndex || 0; index < text.length; index++) {
3564 // We're only interested in line indices.
3565 if (text[index] !== LINE_SEPARATOR) {
3566 continue;
3567 }
3568
3569 const formatsAtIndex = replacements[index] || []; // If the amout of formats is equal or more, store it, then return the
3570 // last one if the amount of formats is less.
3571
3572 if (formatsAtIndex.length >= lineFormats.length) {
3573 childIndex = index;
3574 } else {
3575 return childIndex;
3576 }
3577 } // If the end of the text is reached, return the last child index.
3578
3579
3580 return childIndex;
3581 }
3582
3583 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/outdent-list-items.js
3584 /**
3585 * Internal dependencies
3586 */
3587
3588
3589
3590
3591
3592 /** @typedef {import('./create').RichTextValue} RichTextValue */
3593
3594 /**
3595 * Outdents any selected list items if possible.
3596 *
3597 * @param {RichTextValue} value Value to change.
3598 *
3599 * @return {RichTextValue} The changed value.
3600 */
3601
3602 function outdentListItems(value) {
3603 if (!canOutdentListItems(value)) {
3604 return value;
3605 }
3606
3607 const {
3608 text,
3609 replacements,
3610 start,
3611 end
3612 } = value;
3613 const startingLineIndex = getLineIndex(value, start);
3614 const newFormats = replacements.slice(0);
3615 const parentFormats = replacements[getParentLineIndex(value, startingLineIndex)] || [];
3616 const endingLineIndex = getLineIndex(value, end);
3617 const lastChildIndex = getLastChildIndex(value, endingLineIndex); // Outdent all list items from the starting line index until the last child
3618 // index of the ending list. All children of the ending list need to be
3619 // outdented, otherwise they'll be orphaned.
3620
3621 for (let index = startingLineIndex; index <= lastChildIndex; index++) {
3622 // Skip indices that are not line separators.
3623 if (text[index] !== LINE_SEPARATOR) {
3624 continue;
3625 } // In the case of level 0, the formats at the index are undefined.
3626
3627
3628 const currentFormats = newFormats[index] || []; // Omit the indentation level where the selection starts.
3629
3630 newFormats[index] = parentFormats.concat(currentFormats.slice(parentFormats.length + 1));
3631
3632 if (newFormats[index].length === 0) {
3633 delete newFormats[index];
3634 }
3635 }
3636
3637 return { ...value,
3638 replacements: newFormats
3639 };
3640 }
3641
3642 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/change-list-type.js
3643 /**
3644 * Internal dependencies
3645 */
3646
3647
3648
3649 /** @typedef {import('./create').RichTextValue} RichTextValue */
3650
3651 /** @typedef {import('./create').RichTextFormat} RichTextFormat */
3652
3653 /**
3654 * Changes the list type of the selected indented list, if any. Looks at the
3655 * currently selected list item and takes the parent list, then changes the list
3656 * type of this list. When multiple lines are selected, the parent lists are
3657 * takes and changed.
3658 *
3659 * @param {RichTextValue} value Value to change.
3660 * @param {RichTextFormat} newFormat The new list format object. Choose between
3661 * `{ type: 'ol' }` and `{ type: 'ul' }`.
3662 *
3663 * @return {RichTextValue} The changed value.
3664 */
3665
3666 function changeListType(value, newFormat) {
3667 const {
3668 text,
3669 replacements,
3670 start,
3671 end
3672 } = value;
3673 const startingLineIndex = getLineIndex(value, start);
3674 const startLineFormats = replacements[startingLineIndex] || [];
3675 const endLineFormats = replacements[getLineIndex(value, end)] || [];
3676 const startIndex = getParentLineIndex(value, startingLineIndex);
3677 const newReplacements = replacements.slice();
3678 const startCount = startLineFormats.length - 1;
3679 const endCount = endLineFormats.length - 1;
3680 let changed;
3681
3682 for (let index = startIndex + 1 || 0; index < text.length; index++) {
3683 if (text[index] !== LINE_SEPARATOR) {
3684 continue;
3685 }
3686
3687 if ((newReplacements[index] || []).length <= startCount) {
3688 break;
3689 }
3690
3691 if (!newReplacements[index]) {
3692 continue;
3693 }
3694
3695 changed = true;
3696 newReplacements[index] = newReplacements[index].map((format, i) => {
3697 return i < startCount || i > endCount ? format : newFormat;
3698 });
3699 }
3700
3701 if (!changed) {
3702 return value;
3703 }
3704
3705 return { ...value,
3706 replacements: newReplacements
3707 };
3708 }
3709
3710 ;// CONCATENATED MODULE: external ["wp","element"]
3711 const external_wp_element_namespaceObject = window["wp"]["element"];
3712 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-anchor-ref.js
3713 /**
3714 * WordPress dependencies
3715 */
3716
3717 /**
3718 * Internal dependencies
3719 */
3720
3721
3722 /** @typedef {import('@wordpress/element').RefObject} RefObject */
3723
3724 /** @typedef {import('../register-format-type').RichTextFormatType} RichTextFormatType */
3725
3726 /** @typedef {import('../create').RichTextValue} RichTextValue */
3727
3728 /**
3729 * This hook, to be used in a format type's Edit component, returns the active
3730 * element that is formatted, or the selection range if no format is active.
3731 * The returned value is meant to be used for positioning UI, e.g. by passing it
3732 * to the `Popover` component.
3733 *
3734 * @param {Object} $1 Named parameters.
3735 * @param {RefObject<HTMLElement>} $1.ref React ref of the element
3736 * containing the editable content.
3737 * @param {RichTextValue} $1.value Value to check for selection.
3738 * @param {RichTextFormatType} $1.settings The format type's settings.
3739 *
3740 * @return {Element|Range} The active element or selection range.
3741 */
3742
3743 function useAnchorRef(_ref) {
3744 let {
3745 ref,
3746 value,
3747 settings = {}
3748 } = _ref;
3749 const {
3750 tagName,
3751 className,
3752 name
3753 } = settings;
3754 const activeFormat = name ? getActiveFormat(value, name) : undefined;
3755 return (0,external_wp_element_namespaceObject.useMemo)(() => {
3756 if (!ref.current) return;
3757 const {
3758 ownerDocument: {
3759 defaultView
3760 }
3761 } = ref.current;
3762 const selection = defaultView.getSelection();
3763
3764 if (!selection.rangeCount) {
3765 return;
3766 }
3767
3768 const range = selection.getRangeAt(0);
3769
3770 if (!activeFormat) {
3771 return range;
3772 }
3773
3774 let element = range.startContainer; // If the caret is right before the element, select the next element.
3775
3776 element = element.nextElementSibling || element;
3777
3778 while (element.nodeType !== element.ELEMENT_NODE) {
3779 element = element.parentNode;
3780 }
3781
3782 return element.closest(tagName + (className ? '.' + className : ''));
3783 }, [activeFormat, value.start, value.end, tagName, className]);
3784 }
3785
3786 ;// CONCATENATED MODULE: external ["wp","compose"]
3787 const external_wp_compose_namespaceObject = window["wp"]["compose"];
3788 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-default-style.js
3789 /**
3790 * WordPress dependencies
3791 */
3792
3793 /**
3794 * In HTML, leading and trailing spaces are not visible, and multiple spaces
3795 * elsewhere are visually reduced to one space. This rule prevents spaces from
3796 * collapsing so all space is visible in the editor and can be removed. It also
3797 * prevents some browsers from inserting non-breaking spaces at the end of a
3798 * line to prevent the space from visually disappearing. Sometimes these non
3799 * breaking spaces can linger in the editor causing unwanted non breaking spaces
3800 * in between words. If also prevent Firefox from inserting a trailing `br` node
3801 * to visualise any trailing space, causing the element to be saved.
3802 *
3803 * > Authors are encouraged to set the 'white-space' property on editing hosts
3804 * > and on markup that was originally created through these editing mechanisms
3805 * > to the value 'pre-wrap'. Default HTML whitespace handling is not well
3806 * > suited to WYSIWYG editing, and line wrapping will not work correctly in
3807 * > some corner cases if 'white-space' is left at its default value.
3808 *
3809 * https://html.spec.whatwg.org/multipage/interaction.html#best-practices-for-in-page-editors
3810 *
3811 * @type {string}
3812 */
3813
3814 const whiteSpace = 'pre-wrap';
3815 /**
3816 * A minimum width of 1px will prevent the rich text container from collapsing
3817 * to 0 width and hiding the caret. This is useful for inline containers.
3818 */
3819
3820 const minWidth = '1px';
3821 function useDefaultStyle() {
3822 return (0,external_wp_element_namespaceObject.useCallback)(element => {
3823 if (!element) return;
3824 element.style.whiteSpace = whiteSpace;
3825 element.style.minWidth = minWidth;
3826 }, []);
3827 }
3828
3829 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-boundary-style.js
3830 /**
3831 * WordPress dependencies
3832 */
3833
3834 /*
3835 * Calculates and renders the format boundary style when the active formats
3836 * change.
3837 */
3838
3839 function useBoundaryStyle(_ref) {
3840 let {
3841 record
3842 } = _ref;
3843 const ref = (0,external_wp_element_namespaceObject.useRef)();
3844 const {
3845 activeFormats = []
3846 } = record.current;
3847 (0,external_wp_element_namespaceObject.useEffect)(() => {
3848 // There's no need to recalculate the boundary styles if no formats are
3849 // active, because no boundary styles will be visible.
3850 if (!activeFormats || !activeFormats.length) {
3851 return;
3852 }
3853
3854 const boundarySelector = '*[data-rich-text-format-boundary]';
3855 const element = ref.current.querySelector(boundarySelector);
3856
3857 if (!element) {
3858 return;
3859 }
3860
3861 const {
3862 ownerDocument
3863 } = element;
3864 const {
3865 defaultView
3866 } = ownerDocument;
3867 const computedStyle = defaultView.getComputedStyle(element);
3868 const newColor = computedStyle.color.replace(')', ', 0.2)').replace('rgb', 'rgba');
3869 const selector = `.rich-text:focus ${boundarySelector}`;
3870 const rule = `background-color: ${newColor}`;
3871 const style = `${selector} {${rule}}`;
3872 const globalStyleId = 'rich-text-boundary-style';
3873 let globalStyle = ownerDocument.getElementById(globalStyleId);
3874
3875 if (!globalStyle) {
3876 globalStyle = ownerDocument.createElement('style');
3877 globalStyle.id = globalStyleId;
3878 ownerDocument.head.appendChild(globalStyle);
3879 }
3880
3881 if (globalStyle.innerHTML !== style) {
3882 globalStyle.innerHTML = style;
3883 }
3884 }, [activeFormats]);
3885 return ref;
3886 }
3887
3888 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-copy-handler.js
3889 /**
3890 * WordPress dependencies
3891 */
3892
3893
3894 /**
3895 * Internal dependencies
3896 */
3897
3898
3899
3900
3901
3902 function useCopyHandler(props) {
3903 const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
3904 propsRef.current = props;
3905 return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
3906 function onCopy(event) {
3907 const {
3908 record,
3909 multilineTag,
3910 preserveWhiteSpace
3911 } = propsRef.current;
3912
3913 if (isCollapsed(record.current) || !element.contains(element.ownerDocument.activeElement)) {
3914 return;
3915 }
3916
3917 const selectedRecord = slice(record.current);
3918 const plainText = getTextContent(selectedRecord);
3919 const html = toHTMLString({
3920 value: selectedRecord,
3921 multilineTag,
3922 preserveWhiteSpace
3923 });
3924 event.clipboardData.setData('text/plain', plainText);
3925 event.clipboardData.setData('text/html', html);
3926 event.clipboardData.setData('rich-text', 'true');
3927 event.clipboardData.setData('rich-text-multi-line-tag', multilineTag || '');
3928 event.preventDefault();
3929 }
3930
3931 element.addEventListener('copy', onCopy);
3932 return () => {
3933 element.removeEventListener('copy', onCopy);
3934 };
3935 }, []);
3936 }
3937
3938 ;// CONCATENATED MODULE: external ["wp","keycodes"]
3939 const external_wp_keycodes_namespaceObject = window["wp"]["keycodes"];
3940 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-format-boundaries.js
3941 /**
3942 * WordPress dependencies
3943 */
3944
3945
3946
3947 /**
3948 * Internal dependencies
3949 */
3950
3951
3952 const EMPTY_ACTIVE_FORMATS = [];
3953 function useFormatBoundaries(props) {
3954 const [, forceRender] = (0,external_wp_element_namespaceObject.useReducer)(() => ({}));
3955 const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
3956 propsRef.current = props;
3957 return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
3958 function onKeyDown(event) {
3959 const {
3960 keyCode,
3961 shiftKey,
3962 altKey,
3963 metaKey,
3964 ctrlKey
3965 } = event;
3966
3967 if ( // Only override left and right keys without modifiers pressed.
3968 shiftKey || altKey || metaKey || ctrlKey || keyCode !== external_wp_keycodes_namespaceObject.LEFT && keyCode !== external_wp_keycodes_namespaceObject.RIGHT) {
3969 return;
3970 }
3971
3972 const {
3973 record,
3974 applyRecord
3975 } = propsRef.current;
3976 const {
3977 text,
3978 formats,
3979 start,
3980 end,
3981 activeFormats: currentActiveFormats = []
3982 } = record.current;
3983 const collapsed = isCollapsed(record.current);
3984 const {
3985 ownerDocument
3986 } = element;
3987 const {
3988 defaultView
3989 } = ownerDocument; // To do: ideally, we should look at visual position instead.
3990
3991 const {
3992 direction
3993 } = defaultView.getComputedStyle(element);
3994 const reverseKey = direction === 'rtl' ? external_wp_keycodes_namespaceObject.RIGHT : external_wp_keycodes_namespaceObject.LEFT;
3995 const isReverse = event.keyCode === reverseKey; // If the selection is collapsed and at the very start, do nothing if
3996 // navigating backward.
3997 // If the selection is collapsed and at the very end, do nothing if
3998 // navigating forward.
3999
4000 if (collapsed && currentActiveFormats.length === 0) {
4001 if (start === 0 && isReverse) {
4002 return;
4003 }
4004
4005 if (end === text.length && !isReverse) {
4006 return;
4007 }
4008 } // If the selection is not collapsed, let the browser handle collapsing
4009 // the selection for now. Later we could expand this logic to set
4010 // boundary positions if needed.
4011
4012
4013 if (!collapsed) {
4014 return;
4015 }
4016
4017 const formatsBefore = formats[start - 1] || EMPTY_ACTIVE_FORMATS;
4018 const formatsAfter = formats[start] || EMPTY_ACTIVE_FORMATS;
4019 const destination = isReverse ? formatsBefore : formatsAfter;
4020 const isIncreasing = currentActiveFormats.every((format, index) => format === destination[index]);
4021 let newActiveFormatsLength = currentActiveFormats.length;
4022
4023 if (!isIncreasing) {
4024 newActiveFormatsLength--;
4025 } else if (newActiveFormatsLength < destination.length) {
4026 newActiveFormatsLength++;
4027 }
4028
4029 if (newActiveFormatsLength === currentActiveFormats.length) {
4030 record.current._newActiveFormats = destination;
4031 return;
4032 }
4033
4034 event.preventDefault();
4035 const origin = isReverse ? formatsAfter : formatsBefore;
4036 const source = isIncreasing ? destination : origin;
4037 const newActiveFormats = source.slice(0, newActiveFormatsLength);
4038 const newValue = { ...record.current,
4039 activeFormats: newActiveFormats
4040 };
4041 record.current = newValue;
4042 applyRecord(newValue);
4043 forceRender();
4044 }
4045
4046 element.addEventListener('keydown', onKeyDown);
4047 return () => {
4048 element.removeEventListener('keydown', onKeyDown);
4049 };
4050 }, []);
4051 }
4052
4053 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-select-object.js
4054 /**
4055 * WordPress dependencies
4056 */
4057
4058 function useSelectObject() {
4059 return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
4060 function onClick(event) {
4061 const {
4062 target
4063 } = event; // If the child element has no text content, it must be an object.
4064
4065 if (target === element || target.textContent) {
4066 return;
4067 }
4068
4069 const {
4070 ownerDocument
4071 } = target;
4072 const {
4073 defaultView
4074 } = ownerDocument;
4075 const range = ownerDocument.createRange();
4076 const selection = defaultView.getSelection();
4077 range.selectNode(target);
4078 selection.removeAllRanges();
4079 selection.addRange(range);
4080 }
4081
4082 element.addEventListener('click', onClick);
4083 return () => {
4084 element.removeEventListener('click', onClick);
4085 };
4086 }, []);
4087 }
4088
4089 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-indent-list-item-on-space.js
4090 /**
4091 * WordPress dependencies
4092 */
4093
4094
4095
4096 /**
4097 * Internal dependencies
4098 */
4099
4100
4101
4102
4103 function useIndentListItemOnSpace(props) {
4104 const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
4105 propsRef.current = props;
4106 return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
4107 function onKeyDown(event) {
4108 const {
4109 keyCode,
4110 shiftKey,
4111 altKey,
4112 metaKey,
4113 ctrlKey
4114 } = event;
4115 const {
4116 multilineTag,
4117 createRecord,
4118 handleChange
4119 } = propsRef.current;
4120
4121 if ( // Only override when no modifiers are pressed.
4122 shiftKey || altKey || metaKey || ctrlKey || keyCode !== external_wp_keycodes_namespaceObject.SPACE || multilineTag !== 'li') {
4123 return;
4124 }
4125
4126 const currentValue = createRecord();
4127
4128 if (!isCollapsed(currentValue)) {
4129 return;
4130 }
4131
4132 const {
4133 text,
4134 start
4135 } = currentValue;
4136 const characterBefore = text[start - 1]; // The caret must be at the start of a line.
4137
4138 if (characterBefore && characterBefore !== LINE_SEPARATOR) {
4139 return;
4140 }
4141
4142 handleChange(indentListItems(currentValue, {
4143 type: element.tagName.toLowerCase()
4144 }));
4145 event.preventDefault();
4146 }
4147
4148 element.addEventListener('keydown', onKeyDown);
4149 return () => {
4150 element.removeEventListener('keydown', onKeyDown);
4151 };
4152 }, []);
4153 }
4154
4155 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/update-formats.js
4156 /**
4157 * Internal dependencies
4158 */
4159
4160 /** @typedef {import('./create').RichTextValue} RichTextValue */
4161
4162 /**
4163 * Efficiently updates all the formats from `start` (including) until `end`
4164 * (excluding) with the active formats. Mutates `value`.
4165 *
4166 * @param {Object} $1 Named paramentes.
4167 * @param {RichTextValue} $1.value Value te update.
4168 * @param {number} $1.start Index to update from.
4169 * @param {number} $1.end Index to update until.
4170 * @param {Array} $1.formats Replacement formats.
4171 *
4172 * @return {RichTextValue} Mutated value.
4173 */
4174
4175 function updateFormats(_ref) {
4176 let {
4177 value,
4178 start,
4179 end,
4180 formats
4181 } = _ref;
4182 // Start and end may be switched in case of delete.
4183 const min = Math.min(start, end);
4184 const max = Math.max(start, end);
4185 const formatsBefore = value.formats[min - 1] || [];
4186 const formatsAfter = value.formats[max] || []; // First, fix the references. If any format right before or after are
4187 // equal, the replacement format should use the same reference.
4188
4189 value.activeFormats = formats.map((format, index) => {
4190 if (formatsBefore[index]) {
4191 if (isFormatEqual(format, formatsBefore[index])) {
4192 return formatsBefore[index];
4193 }
4194 } else if (formatsAfter[index]) {
4195 if (isFormatEqual(format, formatsAfter[index])) {
4196 return formatsAfter[index];
4197 }
4198 }
4199
4200 return format;
4201 });
4202
4203 while (--end >= start) {
4204 if (value.activeFormats.length > 0) {
4205 value.formats[end] = value.activeFormats;
4206 } else {
4207 delete value.formats[end];
4208 }
4209 }
4210
4211 return value;
4212 }
4213
4214 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-input-and-selection.js
4215 /**
4216 * WordPress dependencies
4217 */
4218
4219
4220 /**
4221 * Internal dependencies
4222 */
4223
4224
4225
4226 /**
4227 * All inserting input types that would insert HTML into the DOM.
4228 *
4229 * @see https://www.w3.org/TR/input-events-2/#interface-InputEvent-Attributes
4230 *
4231 * @type {Set}
4232 */
4233
4234 const INSERTION_INPUT_TYPES_TO_IGNORE = new Set(['insertParagraph', 'insertOrderedList', 'insertUnorderedList', 'insertHorizontalRule', 'insertLink']);
4235 const use_input_and_selection_EMPTY_ACTIVE_FORMATS = [];
4236 const PLACEHOLDER_ATTR_NAME = 'data-rich-text-placeholder';
4237 /**
4238 * If the selection is set on the placeholder element, collapse the selection to
4239 * the start (before the placeholder).
4240 *
4241 * @param {Window} defaultView
4242 */
4243
4244 function fixPlaceholderSelection(defaultView) {
4245 const selection = defaultView.getSelection();
4246 const {
4247 anchorNode,
4248 anchorOffset
4249 } = selection;
4250
4251 if (anchorNode.nodeType !== anchorNode.ELEMENT_NODE) {
4252 return;
4253 }
4254
4255 const targetNode = anchorNode.childNodes[anchorOffset];
4256
4257 if (!targetNode || targetNode.nodeType !== targetNode.ELEMENT_NODE || !targetNode.hasAttribute(PLACEHOLDER_ATTR_NAME)) {
4258 return;
4259 }
4260
4261 selection.collapseToStart();
4262 }
4263
4264 function useInputAndSelection(props) {
4265 const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
4266 propsRef.current = props;
4267 return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
4268 const {
4269 ownerDocument
4270 } = element;
4271 const {
4272 defaultView
4273 } = ownerDocument;
4274 let isComposing = false;
4275 let rafId;
4276
4277 function onInput(event) {
4278 // Do not trigger a change if characters are being composed.
4279 // Browsers will usually emit a final `input` event when the
4280 // characters are composed.
4281 // As of December 2019, Safari doesn't support
4282 // nativeEvent.isComposing.
4283 if (isComposing) {
4284 return;
4285 }
4286
4287 let inputType;
4288
4289 if (event) {
4290 inputType = event.inputType;
4291 }
4292
4293 const {
4294 record,
4295 applyRecord,
4296 createRecord,
4297 handleChange
4298 } = propsRef.current; // The browser formatted something or tried to insert HTML.
4299 // Overwrite it. It will be handled later by the format library if
4300 // needed.
4301
4302 if (inputType && (inputType.indexOf('format') === 0 || INSERTION_INPUT_TYPES_TO_IGNORE.has(inputType))) {
4303 applyRecord(record.current);
4304 return;
4305 }
4306
4307 const currentValue = createRecord();
4308 const {
4309 start,
4310 activeFormats: oldActiveFormats = []
4311 } = record.current; // Update the formats between the last and new caret position.
4312
4313 const change = updateFormats({
4314 value: currentValue,
4315 start,
4316 end: currentValue.start,
4317 formats: oldActiveFormats
4318 });
4319 handleChange(change);
4320 }
4321 /**
4322 * Syncs the selection to local state. A callback for the `selectionchange`
4323 * native events, `keyup`, `mouseup` and `touchend` synthetic events, and
4324 * animation frames after the `focus` event.
4325 *
4326 * @param {Event|DOMHighResTimeStamp} event
4327 */
4328
4329
4330 function handleSelectionChange(event) {
4331 const {
4332 record,
4333 applyRecord,
4334 createRecord,
4335 isSelected,
4336 onSelectionChange
4337 } = propsRef.current; // Check if the implementor disabled editing. `contentEditable`
4338 // does disable input, but not text selection, so we must ignore
4339 // selection changes.
4340
4341 if (element.contentEditable !== 'true') {
4342 return;
4343 } // If the selection changes where the active element is a parent of
4344 // the rich text instance (writing flow), call `onSelectionChange`
4345 // for the rich text instance that contains the start or end of the
4346 // selection.
4347
4348
4349 if (ownerDocument.activeElement !== element) {
4350 // Only process if the active elment is contentEditable, either
4351 // this rich text instance or the writing flow parent. Fixes a
4352 // bug in Firefox where it strangely selects the closest
4353 // contentEditable element, even though the click was outside
4354 // any contentEditable element.
4355 if (ownerDocument.activeElement.contentEditable !== 'true') {
4356 return;
4357 }
4358
4359 if (!ownerDocument.activeElement.contains(element)) {
4360 return;
4361 }
4362
4363 const selection = defaultView.getSelection();
4364 const {
4365 anchorNode,
4366 focusNode
4367 } = selection;
4368
4369 if (element.contains(anchorNode) && element !== anchorNode && element.contains(focusNode) && element !== focusNode) {
4370 const {
4371 start,
4372 end
4373 } = createRecord();
4374 record.current.activeFormats = use_input_and_selection_EMPTY_ACTIVE_FORMATS;
4375 onSelectionChange(start, end);
4376 } else if (element.contains(anchorNode) && element !== anchorNode) {
4377 const {
4378 start,
4379 end: offset = start
4380 } = createRecord();
4381 record.current.activeFormats = use_input_and_selection_EMPTY_ACTIVE_FORMATS;
4382 onSelectionChange(offset);
4383 } else if (element.contains(focusNode) && element !== focusNode) {
4384 const {
4385 start,
4386 end: offset = start
4387 } = createRecord();
4388 record.current.activeFormats = use_input_and_selection_EMPTY_ACTIVE_FORMATS;
4389 onSelectionChange(undefined, offset);
4390 }
4391
4392 return;
4393 }
4394
4395 if (event.type !== 'selectionchange' && !isSelected) {
4396 return;
4397 } // In case of a keyboard event, ignore selection changes during
4398 // composition.
4399
4400
4401 if (isComposing) {
4402 return;
4403 }
4404
4405 const {
4406 start,
4407 end,
4408 text
4409 } = createRecord();
4410 const oldRecord = record.current; // Fallback mechanism for IE11, which doesn't support the input event.
4411 // Any input results in a selection change.
4412
4413 if (text !== oldRecord.text) {
4414 onInput();
4415 return;
4416 }
4417
4418 if (start === oldRecord.start && end === oldRecord.end) {
4419 // Sometimes the browser may set the selection on the placeholder
4420 // element, in which case the caret is not visible. We need to set
4421 // the caret before the placeholder if that's the case.
4422 if (oldRecord.text.length === 0 && start === 0) {
4423 fixPlaceholderSelection(defaultView);
4424 }
4425
4426 return;
4427 }
4428
4429 const newValue = { ...oldRecord,
4430 start,
4431 end,
4432 // _newActiveFormats may be set on arrow key navigation to control
4433 // the right boundary position. If undefined, getActiveFormats will
4434 // give the active formats according to the browser.
4435 activeFormats: oldRecord._newActiveFormats,
4436 _newActiveFormats: undefined
4437 };
4438 const newActiveFormats = getActiveFormats(newValue, use_input_and_selection_EMPTY_ACTIVE_FORMATS); // Update the value with the new active formats.
4439
4440 newValue.activeFormats = newActiveFormats; // It is important that the internal value is updated first,
4441 // otherwise the value will be wrong on render!
4442
4443 record.current = newValue;
4444 applyRecord(newValue, {
4445 domOnly: true
4446 });
4447 onSelectionChange(start, end);
4448 }
4449
4450 function onCompositionStart() {
4451 var _element$querySelecto;
4452
4453 isComposing = true; // Do not update the selection when characters are being composed as
4454 // this rerenders the component and might destroy internal browser
4455 // editing state.
4456
4457 ownerDocument.removeEventListener('selectionchange', handleSelectionChange); // Remove the placeholder. Since the rich text value doesn't update
4458 // during composition, the placeholder doesn't get removed. There's
4459 // no need to re-add it, when the value is updated on compositionend
4460 // it will be re-added when the value is empty.
4461
4462 (_element$querySelecto = element.querySelector(`[${PLACEHOLDER_ATTR_NAME}]`)) === null || _element$querySelecto === void 0 ? void 0 : _element$querySelecto.remove();
4463 }
4464
4465 function onCompositionEnd() {
4466 isComposing = false; // Ensure the value is up-to-date for browsers that don't emit a final
4467 // input event after composition.
4468
4469 onInput({
4470 inputType: 'insertText'
4471 }); // Tracking selection changes can be resumed.
4472
4473 ownerDocument.addEventListener('selectionchange', handleSelectionChange);
4474 }
4475
4476 function onFocus() {
4477 const {
4478 record,
4479 isSelected,
4480 onSelectionChange,
4481 applyRecord
4482 } = propsRef.current; // When the whole editor is editable, let writing flow handle
4483 // selection.
4484
4485 if (element.parentElement.closest('[contenteditable="true"]')) {
4486 return;
4487 }
4488
4489 if (!isSelected) {
4490 // We know for certain that on focus, the old selection is invalid.
4491 // It will be recalculated on the next mouseup, keyup, or touchend
4492 // event.
4493 const index = undefined;
4494 record.current = { ...record.current,
4495 start: index,
4496 end: index,
4497 activeFormats: use_input_and_selection_EMPTY_ACTIVE_FORMATS
4498 };
4499 onSelectionChange(index, index);
4500 } else {
4501 applyRecord(record.current);
4502 onSelectionChange(record.current.start, record.current.end);
4503 } // Update selection as soon as possible, which is at the next animation
4504 // frame. The event listener for selection changes may be added too late
4505 // at this point, but this focus event is still too early to calculate
4506 // the selection.
4507
4508
4509 rafId = defaultView.requestAnimationFrame(handleSelectionChange);
4510 }
4511
4512 element.addEventListener('input', onInput);
4513 element.addEventListener('compositionstart', onCompositionStart);
4514 element.addEventListener('compositionend', onCompositionEnd);
4515 element.addEventListener('focus', onFocus); // Selection updates must be done at these events as they
4516 // happen before the `selectionchange` event. In some cases,
4517 // the `selectionchange` event may not even fire, for
4518 // example when the window receives focus again on click.
4519
4520 element.addEventListener('keyup', handleSelectionChange);
4521 element.addEventListener('mouseup', handleSelectionChange);
4522 element.addEventListener('touchend', handleSelectionChange);
4523 ownerDocument.addEventListener('selectionchange', handleSelectionChange);
4524 return () => {
4525 element.removeEventListener('input', onInput);
4526 element.removeEventListener('compositionstart', onCompositionStart);
4527 element.removeEventListener('compositionend', onCompositionEnd);
4528 element.removeEventListener('focus', onFocus);
4529 element.removeEventListener('keyup', handleSelectionChange);
4530 element.removeEventListener('mouseup', handleSelectionChange);
4531 element.removeEventListener('touchend', handleSelectionChange);
4532 ownerDocument.removeEventListener('selectionchange', handleSelectionChange);
4533 defaultView.cancelAnimationFrame(rafId);
4534 };
4535 }, []);
4536 }
4537
4538 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/use-delete.js
4539 /**
4540 * WordPress dependencies
4541 */
4542
4543
4544
4545 /**
4546 * Internal dependencies
4547 */
4548
4549
4550
4551
4552 function useDelete(props) {
4553 const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
4554 propsRef.current = props;
4555 return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
4556 function onKeyDown(event) {
4557 const {
4558 keyCode
4559 } = event;
4560 const {
4561 createRecord,
4562 handleChange,
4563 multilineTag
4564 } = propsRef.current;
4565
4566 if (event.defaultPrevented) {
4567 return;
4568 }
4569
4570 if (keyCode !== external_wp_keycodes_namespaceObject.DELETE && keyCode !== external_wp_keycodes_namespaceObject.BACKSPACE) {
4571 return;
4572 }
4573
4574 const currentValue = createRecord();
4575 const {
4576 start,
4577 end,
4578 text
4579 } = currentValue;
4580 const isReverse = keyCode === external_wp_keycodes_namespaceObject.BACKSPACE; // Always handle full content deletion ourselves.
4581
4582 if (start === 0 && end !== 0 && end === text.length) {
4583 handleChange(remove(currentValue));
4584 event.preventDefault();
4585 return;
4586 }
4587
4588 if (multilineTag) {
4589 let newValue; // Check to see if we should remove the first item if empty.
4590
4591 if (isReverse && currentValue.start === 0 && currentValue.end === 0 && isEmptyLine(currentValue)) {
4592 newValue = removeLineSeparator(currentValue, !isReverse);
4593 } else {
4594 newValue = removeLineSeparator(currentValue, isReverse);
4595 }
4596
4597 if (newValue) {
4598 handleChange(newValue);
4599 event.preventDefault();
4600 }
4601 }
4602 }
4603
4604 element.addEventListener('keydown', onKeyDown);
4605 return () => {
4606 element.removeEventListener('keydown', onKeyDown);
4607 };
4608 }, []);
4609 }
4610
4611 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/index.js
4612 /**
4613 * WordPress dependencies
4614 */
4615
4616
4617
4618 /**
4619 * Internal dependencies
4620 */
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633 function useRichText(_ref) {
4634 let {
4635 value = '',
4636 selectionStart,
4637 selectionEnd,
4638 placeholder,
4639 preserveWhiteSpace,
4640 onSelectionChange,
4641 onChange,
4642 __unstableMultilineTag: multilineTag,
4643 __unstableDisableFormats: disableFormats,
4644 __unstableIsSelected: isSelected,
4645 __unstableDependencies = [],
4646 __unstableAfterParse,
4647 __unstableBeforeSerialize,
4648 __unstableAddInvisibleFormats
4649 } = _ref;
4650 const registry = (0,external_wp_data_namespaceObject.useRegistry)();
4651 const [, forceRender] = (0,external_wp_element_namespaceObject.useReducer)(() => ({}));
4652 const ref = (0,external_wp_element_namespaceObject.useRef)();
4653
4654 function createRecord() {
4655 const {
4656 ownerDocument: {
4657 defaultView
4658 }
4659 } = ref.current;
4660 const selection = defaultView.getSelection();
4661 const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
4662 return create({
4663 element: ref.current,
4664 range,
4665 multilineTag,
4666 multilineWrapperTags: multilineTag === 'li' ? ['ul', 'ol'] : undefined,
4667 __unstableIsEditableTree: true,
4668 preserveWhiteSpace
4669 });
4670 }
4671
4672 function applyRecord(newRecord) {
4673 let {
4674 domOnly
4675 } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
4676 apply({
4677 value: newRecord,
4678 current: ref.current,
4679 multilineTag,
4680 multilineWrapperTags: multilineTag === 'li' ? ['ul', 'ol'] : undefined,
4681 prepareEditableTree: __unstableAddInvisibleFormats,
4682 __unstableDomOnly: domOnly,
4683 placeholder
4684 });
4685 } // Internal values are updated synchronously, unlike props and state.
4686
4687
4688 const _value = (0,external_wp_element_namespaceObject.useRef)(value);
4689
4690 const record = (0,external_wp_element_namespaceObject.useRef)();
4691
4692 function setRecordFromProps() {
4693 _value.current = value;
4694 record.current = create({
4695 html: value,
4696 multilineTag,
4697 multilineWrapperTags: multilineTag === 'li' ? ['ul', 'ol'] : undefined,
4698 preserveWhiteSpace
4699 });
4700
4701 if (disableFormats) {
4702 record.current.formats = Array(value.length);
4703 record.current.replacements = Array(value.length);
4704 }
4705
4706 if (__unstableAfterParse) {
4707 record.current.formats = __unstableAfterParse(record.current);
4708 }
4709
4710 record.current.start = selectionStart;
4711 record.current.end = selectionEnd;
4712 }
4713
4714 const hadSelectionUpdate = (0,external_wp_element_namespaceObject.useRef)(false);
4715
4716 if (!record.current) {
4717 var _record$current, _record$current$forma, _record$current$forma2;
4718
4719 setRecordFromProps(); // Sometimes formats are added programmatically and we need to make
4720 // sure it's persisted to the block store / markup. If these formats
4721 // are not applied, they could cause inconsistencies between the data
4722 // in the visual editor and the frontend. Right now, it's only relevant
4723 // to the `core/text-color` format, which is applied at runtime in
4724 // certain circunstances. See the `__unstableFilterAttributeValue`
4725 // function in `packages/format-library/src/text-color/index.js`.
4726 // @todo find a less-hacky way of solving this.
4727
4728 const hasRelevantInitFormat = ((_record$current = record.current) === null || _record$current === void 0 ? void 0 : (_record$current$forma = _record$current.formats[0]) === null || _record$current$forma === void 0 ? void 0 : (_record$current$forma2 = _record$current$forma[0]) === null || _record$current$forma2 === void 0 ? void 0 : _record$current$forma2.type) === 'core/text-color';
4729
4730 if (hasRelevantInitFormat) {
4731 handleChangesUponInit(record.current);
4732 }
4733 } else if (selectionStart !== record.current.start || selectionEnd !== record.current.end) {
4734 hadSelectionUpdate.current = isSelected;
4735 record.current = { ...record.current,
4736 start: selectionStart,
4737 end: selectionEnd
4738 };
4739 }
4740 /**
4741 * Sync the value to global state. The node tree and selection will also be
4742 * updated if differences are found.
4743 *
4744 * @param {Object} newRecord The record to sync and apply.
4745 */
4746
4747
4748 function handleChange(newRecord) {
4749 record.current = newRecord;
4750 applyRecord(newRecord);
4751
4752 if (disableFormats) {
4753 _value.current = newRecord.text;
4754 } else {
4755 _value.current = toHTMLString({
4756 value: __unstableBeforeSerialize ? { ...newRecord,
4757 formats: __unstableBeforeSerialize(newRecord)
4758 } : newRecord,
4759 multilineTag,
4760 preserveWhiteSpace
4761 });
4762 }
4763
4764 const {
4765 start,
4766 end,
4767 formats,
4768 text
4769 } = newRecord; // Selection must be updated first, so it is recorded in history when
4770 // the content change happens.
4771 // We batch both calls to only attempt to rerender once.
4772
4773 registry.batch(() => {
4774 onSelectionChange(start, end);
4775 onChange(_value.current, {
4776 __unstableFormats: formats,
4777 __unstableText: text
4778 });
4779 });
4780 forceRender();
4781 }
4782
4783 function handleChangesUponInit(newRecord) {
4784 record.current = newRecord;
4785 _value.current = toHTMLString({
4786 value: __unstableBeforeSerialize ? { ...newRecord,
4787 formats: __unstableBeforeSerialize(newRecord)
4788 } : newRecord,
4789 multilineTag,
4790 preserveWhiteSpace
4791 });
4792 const {
4793 formats,
4794 text
4795 } = newRecord;
4796 registry.batch(() => {
4797 onChange(_value.current, {
4798 __unstableFormats: formats,
4799 __unstableText: text
4800 });
4801 });
4802 forceRender();
4803 }
4804
4805 function applyFromProps() {
4806 setRecordFromProps();
4807 applyRecord(record.current);
4808 }
4809
4810 const didMount = (0,external_wp_element_namespaceObject.useRef)(false); // Value updates must happen synchonously to avoid overwriting newer values.
4811
4812 (0,external_wp_element_namespaceObject.useLayoutEffect)(() => {
4813 if (didMount.current && value !== _value.current) {
4814 applyFromProps();
4815 forceRender();
4816 }
4817 }, [value]); // Value updates must happen synchonously to avoid overwriting newer values.
4818
4819 (0,external_wp_element_namespaceObject.useLayoutEffect)(() => {
4820 if (!hadSelectionUpdate.current) {
4821 return;
4822 }
4823
4824 if (ref.current.ownerDocument.activeElement !== ref.current) {
4825 ref.current.focus();
4826 }
4827
4828 applyFromProps();
4829 hadSelectionUpdate.current = false;
4830 }, [hadSelectionUpdate.current]);
4831 const mergedRefs = (0,external_wp_compose_namespaceObject.useMergeRefs)([ref, useDefaultStyle(), useBoundaryStyle({
4832 record
4833 }), useCopyHandler({
4834 record,
4835 multilineTag,
4836 preserveWhiteSpace
4837 }), useSelectObject(), useFormatBoundaries({
4838 record,
4839 applyRecord
4840 }), useDelete({
4841 createRecord,
4842 handleChange,
4843 multilineTag
4844 }), useIndentListItemOnSpace({
4845 multilineTag,
4846 createRecord,
4847 handleChange
4848 }), useInputAndSelection({
4849 record,
4850 applyRecord,
4851 createRecord,
4852 handleChange,
4853 isSelected,
4854 onSelectionChange
4855 }), (0,external_wp_compose_namespaceObject.useRefEffect)(() => {
4856 applyFromProps();
4857 didMount.current = true;
4858 }, [placeholder, ...__unstableDependencies])]);
4859 return {
4860 value: record.current,
4861 onChange: handleChange,
4862 ref: mergedRefs
4863 };
4864 }
4865 function __experimentalRichText() {}
4866
4867 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/component/format-edit.js
4868
4869
4870 /**
4871 * Internal dependencies
4872 */
4873
4874
4875 function FormatEdit(_ref) {
4876 let {
4877 formatTypes,
4878 onChange,
4879 onFocus,
4880 value,
4881 forwardedRef
4882 } = _ref;
4883 return formatTypes.map(settings => {
4884 const {
4885 name,
4886 edit: Edit
4887 } = settings;
4888
4889 if (!Edit) {
4890 return null;
4891 }
4892
4893 const activeFormat = getActiveFormat(value, name);
4894 const isActive = activeFormat !== undefined;
4895 const activeObject = getActiveObject(value);
4896 const isObjectActive = activeObject !== undefined && activeObject.type === name;
4897 return (0,external_wp_element_namespaceObject.createElement)(Edit, {
4898 key: name,
4899 isActive: isActive,
4900 activeAttributes: isActive ? activeFormat.attributes || {} : {},
4901 isObjectActive: isObjectActive,
4902 activeObjectAttributes: isObjectActive ? activeObject.attributes || {} : {},
4903 value: value,
4904 onChange: onChange,
4905 onFocus: onFocus,
4906 contentRef: forwardedRef
4907 });
4908 });
4909 }
4910
4911 ;// CONCATENATED MODULE: ./packages/rich-text/build-module/index.js
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950 (window.wp = window.wp || {}).richText = __webpack_exports__;
4951 /******/ })()
4952 ;