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