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

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

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