| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 (function() { 2 "use strict"; 3 var wp; 4 (wp ||= {}).richText = (() => { 5 var __create = Object.create; 6 var __defProp = Object.defineProperty; 7 var __getOwnPropDesc = Object.getOwnPropertyDescriptor; 8 var __getOwnPropNames = Object.getOwnPropertyNames; 9 var __getProtoOf = Object.getPrototypeOf; 10 var __hasOwnProp = Object.prototype.hasOwnProperty; 11 var __commonJS = (cb, mod) => function __require() { 12 return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; 13 }; 14 var __export = (target, all) => { 15 for (var name in all) 16 __defProp(target, name, { get: all[name], enumerable: true }); 17 }; 18 var __copyProps = (to, from, except, desc) => { 19 if (from && typeof from === "object" || typeof from === "function") { 20 for (let key of __getOwnPropNames(from)) 21 if (!__hasOwnProp.call(to, key) && key !== except) 22 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); 23 } 24 return to; 25 }; 26 var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( 27 // If the importer is in node compatibility mode or this is not an ESM 28 // file that has been converted to a CommonJS file using a Babel- 29 // compatible transform (i.e. "__esModule" has not been set), then set 30 // "default" to the CommonJS "module.exports" for node compatibility. 31 isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, 32 mod 33 )); 34 var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); 35 36 // package-external:@wordpress/data 37 var require_data = __commonJS({ 38 "package-external:@wordpress/data"(exports, module) { 39 module.exports = window.wp.data; 40 } 41 }); 42 43 // package-external:@wordpress/escape-html 44 var require_escape_html = __commonJS({ 45 "package-external:@wordpress/escape-html"(exports, module) { 46 module.exports = window.wp.escapeHtml; 47 } 48 }); 49 50 // package-external:@wordpress/a11y 51 var require_a11y = __commonJS({ 52 "package-external:@wordpress/a11y"(exports, module) { 53 module.exports = window.wp.a11y; 54 } 55 }); 56 57 // package-external:@wordpress/i18n 58 var require_i18n = __commonJS({ 59 "package-external:@wordpress/i18n"(exports, module) { 60 module.exports = window.wp.i18n; 61 } 62 }); 63 64 // package-external:@wordpress/private-apis 65 var require_private_apis = __commonJS({ 66 "package-external:@wordpress/private-apis"(exports, module) { 67 module.exports = window.wp.privateApis; 68 } 69 }); 70 71 // package-external:@wordpress/element 72 var require_element = __commonJS({ 73 "package-external:@wordpress/element"(exports, module) { 74 module.exports = window.wp.element; 75 } 76 }); 77 78 // package-external:@wordpress/compose 79 var require_compose = __commonJS({ 80 "package-external:@wordpress/compose"(exports, module) { 81 module.exports = window.wp.compose; 82 } 83 }); 84 85 // package-external:@wordpress/deprecated 86 var require_deprecated = __commonJS({ 87 "package-external:@wordpress/deprecated"(exports, module) { 88 module.exports = window.wp.deprecated; 89 } 90 }); 91 92 // package-external:@wordpress/keycodes 93 var require_keycodes = __commonJS({ 94 "package-external:@wordpress/keycodes"(exports, module) { 95 module.exports = window.wp.keycodes; 96 } 97 }); 98 99 // package-external:@wordpress/dom 100 var require_dom = __commonJS({ 101 "package-external:@wordpress/dom"(exports, module) { 102 module.exports = window.wp.dom; 103 } 104 }); 105 106 // packages/rich-text/build-module/index.mjs 107 var index_exports = {}; 108 __export(index_exports, { 109 RichTextData: () => RichTextData, 110 __experimentalRichText: () => __experimentalRichText, 111 __unstableCreateElement: () => createElement, 112 __unstableToDom: () => toDom, 113 __unstableUseRichText: () => useDeprecatedRichText, 114 applyFormat: () => applyFormat, 115 concat: () => concat, 116 create: () => create, 117 getActiveFormat: () => getActiveFormat, 118 getActiveFormats: () => getActiveFormats, 119 getActiveObject: () => getActiveObject, 120 getTextContent: () => getTextContent, 121 insert: () => insert, 122 insertObject: () => insertObject, 123 isCollapsed: () => isCollapsed, 124 isEmpty: () => isEmpty, 125 join: () => join, 126 privateApis: () => privateApis, 127 registerFormatType: () => registerFormatType, 128 remove: () => remove2, 129 removeFormat: () => removeFormat, 130 replace: () => replace2, 131 slice: () => slice, 132 split: () => split, 133 store: () => store, 134 toHTMLString: () => toHTMLString, 135 toggleFormat: () => toggleFormat, 136 unregisterFormatType: () => unregisterFormatType, 137 useAnchor: () => useAnchor, 138 useAnchorRef: () => useAnchorRef 139 }); 140 141 // packages/rich-text/build-module/store/index.mjs 142 var import_data3 = __toESM(require_data(), 1); 143 144 // packages/rich-text/build-module/store/reducer.mjs 145 var import_data = __toESM(require_data(), 1); 146 function formatTypes(state = {}, action) { 147 switch (action.type) { 148 case "ADD_FORMAT_TYPES": 149 return { 150 ...state, 151 // Key format types by their name. 152 ...action.formatTypes.reduce( 153 (newFormatTypes, type) => ({ 154 ...newFormatTypes, 155 [type.name]: type 156 }), 157 {} 158 ) 159 }; 160 case "REMOVE_FORMAT_TYPES": 161 return Object.fromEntries( 162 Object.entries(state).filter( 163 ([key]) => !action.names.includes(key) 164 ) 165 ); 166 } 167 return state; 168 } 169 var reducer_default = (0, import_data.combineReducers)({ formatTypes }); 170 171 // packages/rich-text/build-module/store/selectors.mjs 172 var selectors_exports = {}; 173 __export(selectors_exports, { 174 getFormatType: () => getFormatType, 175 getFormatTypeForBareElement: () => getFormatTypeForBareElement, 176 getFormatTypeForClassName: () => getFormatTypeForClassName, 177 getFormatTypes: () => getFormatTypes 178 }); 179 var import_data2 = __toESM(require_data(), 1); 180 var getFormatTypes = (0, import_data2.createSelector)( 181 (state) => Object.values(state.formatTypes), 182 (state) => [state.formatTypes] 183 ); 184 function getFormatType(state, name) { 185 return state.formatTypes[name]; 186 } 187 function getFormatTypeForBareElement(state, bareElementTagName) { 188 const formatTypes2 = getFormatTypes(state); 189 return formatTypes2.find(({ className, tagName }) => { 190 return className === null && bareElementTagName === tagName; 191 }) || formatTypes2.find(({ className, tagName }) => { 192 return className === null && "*" === tagName; 193 }); 194 } 195 function getFormatTypeForClassName(state, elementClassName) { 196 return getFormatTypes(state).find(({ className }) => { 197 if (className === null) { 198 return false; 199 } 200 return ` $elementClassName} `.indexOf(` $className} `) >= 0; 201 }); 202 } 203 204 // packages/rich-text/build-module/store/actions.mjs 205 var actions_exports = {}; 206 __export(actions_exports, { 207 addFormatTypes: () => addFormatTypes, 208 removeFormatTypes: () => removeFormatTypes 209 }); 210 function addFormatTypes(formatTypes2) { 211 return { 212 type: "ADD_FORMAT_TYPES", 213 formatTypes: Array.isArray(formatTypes2) ? formatTypes2 : [formatTypes2] 214 }; 215 } 216 function removeFormatTypes(names) { 217 return { 218 type: "REMOVE_FORMAT_TYPES", 219 names: Array.isArray(names) ? names : [names] 220 }; 221 } 222 223 // packages/rich-text/build-module/store/index.mjs 224 var STORE_NAME = "core/rich-text"; 225 var store = (0, import_data3.createReduxStore)(STORE_NAME, { 226 reducer: reducer_default, 227 selectors: selectors_exports, 228 actions: actions_exports 229 }); 230 (0, import_data3.register)(store); 231 232 // packages/rich-text/build-module/is-format-equal.mjs 233 function isFormatEqual(format1, format2) { 234 if (format1 === format2) { 235 return true; 236 } 237 if (!format1 || !format2) { 238 return false; 239 } 240 if (format1.type !== format2.type) { 241 return false; 242 } 243 let attributes1 = format1.attributes; 244 let attributes2 = format2.attributes; 245 if (format1.unregisteredAttributes || format2.unregisteredAttributes) { 246 attributes1 = { ...attributes1, ...format1.unregisteredAttributes }; 247 attributes2 = { ...attributes2, ...format2.unregisteredAttributes }; 248 } 249 if (attributes1 === attributes2) { 250 return true; 251 } 252 if (!attributes1 || !attributes2) { 253 return false; 254 } 255 const keys1 = Object.keys(attributes1); 256 const keys2 = Object.keys(attributes2); 257 if (keys1.length !== keys2.length) { 258 return false; 259 } 260 const length = keys1.length; 261 for (let i2 = 0; i2 < length; i2++) { 262 const name = keys1[i2]; 263 if (attributes1[name] !== attributes2[name]) { 264 return false; 265 } 266 } 267 return true; 268 } 269 270 // packages/rich-text/build-module/normalise-formats.mjs 271 function normaliseFormats(value) { 272 const newFormats = value.formats.slice(); 273 newFormats.forEach((formatsAtIndex, index) => { 274 const formatsAtPreviousIndex = newFormats[index - 1]; 275 if (formatsAtPreviousIndex) { 276 const newFormatsAtIndex = formatsAtIndex.slice(); 277 newFormatsAtIndex.forEach((format, formatIndex) => { 278 const previousFormat = formatsAtPreviousIndex[formatIndex]; 279 if (isFormatEqual(format, previousFormat)) { 280 newFormatsAtIndex[formatIndex] = previousFormat; 281 } 282 }); 283 newFormats[index] = newFormatsAtIndex; 284 } 285 }); 286 return { 287 ...value, 288 formats: newFormats 289 }; 290 } 291 292 // packages/rich-text/build-module/apply-format.mjs 293 function replace(array, index, value) { 294 array = array.slice(); 295 array[index] = value; 296 return array; 297 } 298 function applyFormat(value, format, startIndex = value.start, endIndex = value.end) { 299 const { formats, activeFormats } = value; 300 const newFormats = formats.slice(); 301 if (startIndex === endIndex) { 302 const startFormat = newFormats[startIndex]?.find( 303 ({ type }) => type === format.type 304 ); 305 if (startFormat) { 306 const index = newFormats[startIndex].indexOf(startFormat); 307 while (newFormats[startIndex] && newFormats[startIndex][index] === startFormat) { 308 newFormats[startIndex] = replace( 309 newFormats[startIndex], 310 index, 311 format 312 ); 313 startIndex--; 314 } 315 endIndex++; 316 while (newFormats[endIndex] && newFormats[endIndex][index] === startFormat) { 317 newFormats[endIndex] = replace( 318 newFormats[endIndex], 319 index, 320 format 321 ); 322 endIndex++; 323 } 324 } 325 } else { 326 let position = Infinity; 327 for (let index = startIndex; index < endIndex; index++) { 328 if (newFormats[index]) { 329 newFormats[index] = newFormats[index].filter( 330 ({ type }) => type !== format.type 331 ); 332 const length = newFormats[index].length; 333 if (length < position) { 334 position = length; 335 } 336 } else { 337 newFormats[index] = []; 338 position = 0; 339 } 340 } 341 for (let index = startIndex; index < endIndex; index++) { 342 newFormats[index].splice(position, 0, format); 343 } 344 } 345 return normaliseFormats({ 346 ...value, 347 formats: newFormats, 348 // Always revise active formats. This serves as a placeholder for new 349 // inputs with the format so new input appears with the format applied, 350 // and ensures a format of the same type uses the latest values. 351 activeFormats: [ 352 ...activeFormats?.filter( 353 ({ type }) => type !== format.type 354 ) || [], 355 format 356 ] 357 }); 358 } 359 360 // packages/rich-text/build-module/create.mjs 361 var import_data5 = __toESM(require_data(), 1); 362 363 // packages/rich-text/build-module/create-element.mjs 364 function createElement({ implementation }, html) { 365 if (!createElement.body) { 366 createElement.body = implementation.createHTMLDocument("").body; 367 } 368 createElement.body.innerHTML = html; 369 return createElement.body; 370 } 371 372 // packages/rich-text/build-module/special-characters.mjs 373 var OBJECT_REPLACEMENT_CHARACTER = "\uFFFC"; 374 var ZWNBSP = "\uFEFF"; 375 376 // packages/rich-text/build-module/to-html-string.mjs 377 var import_escape_html = __toESM(require_escape_html(), 1); 378 379 // packages/rich-text/build-module/get-active-formats.mjs 380 function getActiveFormats(value, EMPTY_ACTIVE_FORMATS3 = []) { 381 const { formats, start, end, activeFormats } = value; 382 if (start === void 0) { 383 return EMPTY_ACTIVE_FORMATS3; 384 } 385 if (start === end) { 386 if (activeFormats) { 387 return activeFormats; 388 } 389 const formatsBefore = formats[start - 1] || EMPTY_ACTIVE_FORMATS3; 390 const formatsAfter = formats[start] || EMPTY_ACTIVE_FORMATS3; 391 if (formatsBefore.length < formatsAfter.length) { 392 return formatsBefore; 393 } 394 return formatsAfter; 395 } 396 if (!formats[start]) { 397 return EMPTY_ACTIVE_FORMATS3; 398 } 399 const selectedFormats = formats.slice(start, end); 400 const _activeFormats = [...selectedFormats[0]]; 401 let i2 = selectedFormats.length; 402 while (i2--) { 403 const formatsAtIndex = selectedFormats[i2]; 404 if (!formatsAtIndex) { 405 return EMPTY_ACTIVE_FORMATS3; 406 } 407 let ii = _activeFormats.length; 408 while (ii--) { 409 const format = _activeFormats[ii]; 410 if (!formatsAtIndex.find( 411 (_format) => isFormatEqual(format, _format) 412 )) { 413 _activeFormats.splice(ii, 1); 414 } 415 } 416 if (_activeFormats.length === 0) { 417 return EMPTY_ACTIVE_FORMATS3; 418 } 419 } 420 return _activeFormats || EMPTY_ACTIVE_FORMATS3; 421 } 422 423 // packages/rich-text/build-module/get-format-type.mjs 424 var import_data4 = __toESM(require_data(), 1); 425 function getFormatType2(name) { 426 return (0, import_data4.select)(store).getFormatType(name); 427 } 428 429 // packages/rich-text/build-module/to-tree.mjs 430 function restoreOnAttributes(attributes, isEditableTree) { 431 if (isEditableTree) { 432 return attributes; 433 } 434 const newAttributes = {}; 435 for (const key in attributes) { 436 let newKey = key; 437 if (key.startsWith("data-disable-rich-text-")) { 438 newKey = key.slice("data-disable-rich-text-".length); 439 } 440 newAttributes[newKey] = attributes[key]; 441 } 442 return newAttributes; 443 } 444 function fromFormat({ 445 type, 446 tagName, 447 attributes, 448 unregisteredAttributes, 449 object, 450 boundaryClass, 451 isEditableTree 452 }) { 453 const formatType = getFormatType2(type); 454 let elementAttributes = {}; 455 if (boundaryClass && isEditableTree) { 456 elementAttributes["data-rich-text-format-boundary"] = "true"; 457 } 458 if (!formatType) { 459 if (attributes) { 460 elementAttributes = { ...attributes, ...elementAttributes }; 461 } 462 return { 463 type, 464 attributes: restoreOnAttributes( 465 elementAttributes, 466 isEditableTree 467 ), 468 object 469 }; 470 } 471 elementAttributes = { ...unregisteredAttributes, ...elementAttributes }; 472 for (const name in attributes) { 473 const key = formatType.attributes ? formatType.attributes[name] : false; 474 if (key) { 475 elementAttributes[key] = attributes[name]; 476 } else { 477 elementAttributes[name] = attributes[name]; 478 } 479 } 480 if (formatType.className) { 481 if (elementAttributes.class) { 482 elementAttributes.class = `$formatType.className} $elementAttributes.class}`; 483 } else { 484 elementAttributes.class = formatType.className; 485 } 486 } 487 return { 488 type: tagName || formatType.tagName, 489 object: formatType.object, 490 attributes: restoreOnAttributes(elementAttributes, isEditableTree) 491 }; 492 } 493 function isEqualUntil(a2, b2, index) { 494 do { 495 if (a2[index] !== b2[index]) { 496 return false; 497 } 498 } while (index--); 499 return true; 500 } 501 function toTree({ 502 value, 503 preserveWhiteSpace, 504 createEmpty: createEmpty2, 505 append: append3, 506 getLastChild: getLastChild3, 507 getParent: getParent3, 508 isText: isText3, 509 getText: getText3, 510 remove: remove4, 511 appendText: appendText3, 512 onStartIndex, 513 onEndIndex, 514 isEditableTree, 515 placeholder 516 }) { 517 const { formats, replacements, text, start, end } = value; 518 const formatsLength = formats.length + 1; 519 const tree = createEmpty2(); 520 const activeFormats = getActiveFormats(value); 521 const deepestActiveFormat = activeFormats[activeFormats.length - 1]; 522 let lastCharacterFormats; 523 let lastCharacter; 524 append3(tree, ""); 525 for (let i2 = 0; i2 < formatsLength; i2++) { 526 const character = text.charAt(i2); 527 const shouldInsertPadding = isEditableTree && // Pad the line if the line is empty. 528 (!lastCharacter || // Pad the line if the previous character is a line break, otherwise 529 // the line break won't be visible. 530 lastCharacter === "\n"); 531 const characterFormats = formats[i2]; 532 let pointer = getLastChild3(tree); 533 if (characterFormats) { 534 characterFormats.forEach((format, formatIndex) => { 535 if (pointer && lastCharacterFormats && // Reuse the last element if all formats remain the same. 536 isEqualUntil( 537 characterFormats, 538 lastCharacterFormats, 539 formatIndex 540 )) { 541 pointer = getLastChild3(pointer); 542 return; 543 } 544 const { type, tagName, attributes, unregisteredAttributes } = format; 545 const boundaryClass = isEditableTree && format === deepestActiveFormat; 546 const parent = getParent3(pointer); 547 const newNode = append3( 548 parent, 549 fromFormat({ 550 type, 551 tagName, 552 attributes, 553 unregisteredAttributes, 554 boundaryClass, 555 isEditableTree 556 }) 557 ); 558 if (isText3(pointer) && getText3(pointer).length === 0) { 559 remove4(pointer); 560 } 561 pointer = append3(newNode, ""); 562 }); 563 } 564 if (i2 === 0) { 565 if (onStartIndex && start === 0) { 566 onStartIndex(tree, pointer); 567 } 568 if (onEndIndex && end === 0) { 569 onEndIndex(tree, pointer); 570 } 571 } 572 if (character === OBJECT_REPLACEMENT_CHARACTER) { 573 const replacement = replacements[i2]; 574 if (!replacement) { 575 continue; 576 } 577 const { type, attributes, innerHTML } = replacement; 578 const formatType = getFormatType2(type); 579 if (isEditableTree && type === "#comment") { 580 pointer = append3(getParent3(pointer), { 581 type: "span", 582 attributes: { 583 contenteditable: "false", 584 "data-rich-text-comment": attributes["data-rich-text-comment"] 585 } 586 }); 587 append3( 588 append3(pointer, { type: "span" }), 589 attributes["data-rich-text-comment"].trim() 590 ); 591 } else if (!isEditableTree && type === "script") { 592 pointer = append3( 593 getParent3(pointer), 594 fromFormat({ 595 type: "script", 596 isEditableTree 597 }) 598 ); 599 append3(pointer, { 600 html: decodeURIComponent( 601 attributes["data-rich-text-script"] 602 ) 603 }); 604 } else if (formatType?.contentEditable === false) { 605 if (innerHTML || isEditableTree) { 606 pointer = getParent3(pointer); 607 if (isEditableTree) { 608 const attrs = { 609 contenteditable: "false", 610 "data-rich-text-bogus": true 611 }; 612 if (start === i2 && end === i2 + 1) { 613 attrs["data-rich-text-format-boundary"] = true; 614 } 615 pointer = append3(pointer, { 616 type: "span", 617 attributes: attrs 618 }); 619 if (isEditableTree && i2 + 1 === text.length) { 620 append3(getParent3(pointer), ZWNBSP); 621 } 622 } 623 pointer = append3( 624 pointer, 625 fromFormat({ 626 ...replacement, 627 isEditableTree 628 }) 629 ); 630 if (innerHTML) { 631 append3(pointer, { 632 html: innerHTML 633 }); 634 } 635 } 636 } else { 637 pointer = append3( 638 getParent3(pointer), 639 fromFormat({ 640 ...replacement, 641 object: true, 642 isEditableTree 643 }) 644 ); 645 } 646 pointer = append3(getParent3(pointer), ""); 647 } else if (!preserveWhiteSpace && character === "\n") { 648 pointer = append3(getParent3(pointer), { 649 type: "br", 650 attributes: isEditableTree ? { 651 "data-rich-text-line-break": "true" 652 } : void 0, 653 object: true 654 }); 655 pointer = append3(getParent3(pointer), ""); 656 } else if (!isText3(pointer)) { 657 pointer = append3(getParent3(pointer), character); 658 } else { 659 appendText3(pointer, character); 660 } 661 if (onStartIndex && start === i2 + 1) { 662 onStartIndex(tree, pointer); 663 } 664 if (onEndIndex && end === i2 + 1) { 665 onEndIndex(tree, pointer); 666 } 667 if (shouldInsertPadding && i2 === text.length) { 668 append3(getParent3(pointer), ZWNBSP); 669 if (placeholder && text.length === 0) { 670 append3(getParent3(pointer), { 671 type: "span", 672 attributes: { 673 "data-rich-text-placeholder": placeholder, 674 // Necessary to prevent the placeholder from catching 675 // selection and being editable. 676 style: "pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;" 677 } 678 }); 679 } 680 } 681 lastCharacterFormats = characterFormats; 682 lastCharacter = character; 683 } 684 return tree; 685 } 686 687 // packages/rich-text/build-module/to-html-string.mjs 688 function toHTMLString({ value, preserveWhiteSpace }) { 689 const tree = toTree({ 690 value, 691 preserveWhiteSpace, 692 createEmpty, 693 append, 694 getLastChild, 695 getParent, 696 isText, 697 getText, 698 remove, 699 appendText 700 }); 701 return createChildrenHTML(tree.children); 702 } 703 function createEmpty() { 704 return {}; 705 } 706 function getLastChild({ children }) { 707 return children && children[children.length - 1]; 708 } 709 function append(parent, object) { 710 if (typeof object === "string") { 711 object = { text: object }; 712 } 713 object.parent = parent; 714 parent.children = parent.children || []; 715 parent.children.push(object); 716 return object; 717 } 718 function appendText(object, text) { 719 object.text += text; 720 } 721 function getParent({ parent }) { 722 return parent; 723 } 724 function isText({ text }) { 725 return typeof text === "string"; 726 } 727 function getText({ text }) { 728 return text; 729 } 730 function remove(object) { 731 const index = object.parent.children.indexOf(object); 732 if (index !== -1) { 733 object.parent.children.splice(index, 1); 734 } 735 return object; 736 } 737 function createElementHTML({ type, attributes, object, children }) { 738 if (type === "#comment") { 739 return `<!--$attributes["data-rich-text-comment"]}-->`; 740 } 741 let attributeString = ""; 742 for (const key in attributes) { 743 if (!(0, import_escape_html.isValidAttributeName)(key)) { 744 continue; 745 } 746 attributeString += ` $key}="${(0, import_escape_html.escapeAttribute)( 747 attributes[key] 748 )}"`; 749 } 750 if (object) { 751 return `<$type}$attributeString}>`; 752 } 753 return `<$type}$attributeString}>$createChildrenHTML( 754 children 755 )}</$type}>`; 756 } 757 function createChildrenHTML(children = []) { 758 return children.map((child) => { 759 if (child.html !== void 0) { 760 return child.html; 761 } 762 return child.text === void 0 ? createElementHTML(child) : (0, import_escape_html.escapeEditableHTML)(child.text); 763 }).join(""); 764 } 765 766 // packages/rich-text/build-module/get-text-content.mjs 767 function getTextContent({ text }) { 768 return text.replace(OBJECT_REPLACEMENT_CHARACTER, ""); 769 } 770 771 // packages/rich-text/build-module/create.mjs 772 function createEmptyValue() { 773 return { 774 formats: [], 775 replacements: [], 776 text: "" 777 }; 778 } 779 function toFormat({ tagName, attributes }) { 780 let formatType; 781 if (attributes && attributes.class) { 782 formatType = (0, import_data5.select)(store).getFormatTypeForClassName( 783 attributes.class 784 ); 785 if (formatType) { 786 attributes.class = ` $attributes.class} `.replace(` $formatType.className} `, " ").trim(); 787 if (!attributes.class) { 788 delete attributes.class; 789 } 790 } 791 } 792 if (!formatType) { 793 formatType = (0, import_data5.select)(store).getFormatTypeForBareElement(tagName); 794 } 795 if (!formatType) { 796 return attributes ? { type: tagName, attributes } : { type: tagName }; 797 } 798 if (formatType.__experimentalCreatePrepareEditableTree && !formatType.__experimentalCreateOnChangeEditableValue) { 799 return null; 800 } 801 if (!attributes) { 802 return { formatType, type: formatType.name, tagName }; 803 } 804 const registeredAttributes = {}; 805 const unregisteredAttributes = {}; 806 const _attributes = { ...attributes }; 807 for (const key in formatType.attributes) { 808 const name = formatType.attributes[key]; 809 registeredAttributes[key] = _attributes[name]; 810 delete _attributes[name]; 811 if (typeof registeredAttributes[key] === "undefined") { 812 delete registeredAttributes[key]; 813 } 814 } 815 for (const name in _attributes) { 816 unregisteredAttributes[name] = attributes[name]; 817 } 818 if (formatType.contentEditable === false) { 819 delete unregisteredAttributes.contenteditable; 820 } 821 const format = { 822 formatType, 823 type: formatType.name, 824 tagName 825 }; 826 if (Object.keys(registeredAttributes).length) { 827 format.attributes = registeredAttributes; 828 } 829 if (Object.keys(unregisteredAttributes).length) { 830 format.unregisteredAttributes = unregisteredAttributes; 831 } 832 return format; 833 } 834 var RichTextData = class _RichTextData { 835 #value; 836 static empty() { 837 return new _RichTextData(); 838 } 839 static fromPlainText(text) { 840 return new _RichTextData(create({ text })); 841 } 842 static fromHTMLString(html) { 843 return new _RichTextData(create({ html })); 844 } 845 /** 846 * Create a RichTextData instance from an HTML element. 847 * 848 * @param {HTMLElement} htmlElement The HTML element to create the instance from. 849 * @param {{preserveWhiteSpace?: boolean}} options Options. 850 * @return {RichTextData} The RichTextData instance. 851 */ 852 static fromHTMLElement(htmlElement, options = {}) { 853 const { preserveWhiteSpace = false } = options; 854 const element = preserveWhiteSpace ? htmlElement : collapseWhiteSpace(htmlElement); 855 const richTextData = new _RichTextData(create({ element })); 856 Object.defineProperty(richTextData, "originalHTML", { 857 value: htmlElement.innerHTML 858 }); 859 return richTextData; 860 } 861 constructor(init = createEmptyValue()) { 862 this.#value = init; 863 } 864 toPlainText() { 865 return getTextContent(this.#value); 866 } 867 // We could expose `toHTMLElement` at some point as well, but we'd only use 868 // it internally. 869 /** 870 * Convert the rich text value to an HTML string. 871 * 872 * @param {{preserveWhiteSpace?: boolean}} options Options. 873 * @return {string} The HTML string. 874 */ 875 toHTMLString({ preserveWhiteSpace } = {}) { 876 return this.originalHTML || toHTMLString({ value: this.#value, preserveWhiteSpace }); 877 } 878 valueOf() { 879 return this.toHTMLString(); 880 } 881 toString() { 882 return this.toHTMLString(); 883 } 884 toJSON() { 885 return this.toHTMLString(); 886 } 887 get length() { 888 return this.text.length; 889 } 890 get formats() { 891 return this.#value.formats; 892 } 893 get replacements() { 894 return this.#value.replacements; 895 } 896 get text() { 897 return this.#value.text; 898 } 899 }; 900 for (const name of Object.getOwnPropertyNames(String.prototype)) { 901 if (RichTextData.prototype.hasOwnProperty(name)) { 902 continue; 903 } 904 Object.defineProperty(RichTextData.prototype, name, { 905 value(...args) { 906 return this.toHTMLString()[name](...args); 907 } 908 }); 909 } 910 function create({ 911 element, 912 text, 913 html, 914 range, 915 __unstableIsEditableTree: isEditableTree 916 } = {}) { 917 if (html instanceof RichTextData) { 918 return { 919 text: html.text, 920 formats: html.formats, 921 replacements: html.replacements 922 }; 923 } 924 if (typeof text === "string" && text.length > 0) { 925 return { 926 formats: Array(text.length), 927 replacements: Array(text.length), 928 text 929 }; 930 } 931 if (typeof html === "string" && html.length > 0) { 932 element = createElement(document, html); 933 } 934 if (typeof element !== "object") { 935 return createEmptyValue(); 936 } 937 return createFromElement({ 938 element, 939 range, 940 isEditableTree 941 }); 942 } 943 function accumulateSelection(accumulator, node, range, value) { 944 if (!range) { 945 return; 946 } 947 const { parentNode } = node; 948 const { startContainer, startOffset, endContainer, endOffset } = range; 949 const currentLength = accumulator.text.length; 950 if (value.start !== void 0) { 951 accumulator.start = currentLength + value.start; 952 } else if (node === startContainer && node.nodeType === node.TEXT_NODE) { 953 accumulator.start = currentLength + startOffset; 954 } else if (parentNode === startContainer && node === startContainer.childNodes[startOffset]) { 955 accumulator.start = currentLength; 956 } else if (parentNode === startContainer && node === startContainer.childNodes[startOffset - 1]) { 957 accumulator.start = currentLength + value.text.length; 958 } else if (node === startContainer) { 959 accumulator.start = currentLength; 960 } 961 if (value.end !== void 0) { 962 accumulator.end = currentLength + value.end; 963 } else if (node === endContainer && node.nodeType === node.TEXT_NODE) { 964 accumulator.end = currentLength + endOffset; 965 } else if (parentNode === endContainer && node === endContainer.childNodes[endOffset - 1]) { 966 accumulator.end = currentLength + value.text.length; 967 } else if (parentNode === endContainer && node === endContainer.childNodes[endOffset]) { 968 accumulator.end = currentLength; 969 } else if (node === endContainer) { 970 accumulator.end = currentLength + endOffset; 971 } 972 } 973 function filterRange(node, range, filter) { 974 if (!range) { 975 return; 976 } 977 const { startContainer, endContainer } = range; 978 let { startOffset, endOffset } = range; 979 if (node === startContainer) { 980 startOffset = filter(node.nodeValue.slice(0, startOffset)).length; 981 } 982 if (node === endContainer) { 983 endOffset = filter(node.nodeValue.slice(0, endOffset)).length; 984 } 985 return { startContainer, startOffset, endContainer, endOffset }; 986 } 987 function collapseWhiteSpace(element, isRoot = true, hasPrecedingSpace = false, hasTrailingSpace = false) { 988 const clone = element.cloneNode(true); 989 clone.normalize(); 990 Array.from(clone.childNodes).forEach((node, i2, nodes) => { 991 if (node.nodeType === node.TEXT_NODE) { 992 let newNodeValue = node.nodeValue; 993 if (/[\n\t\r\f]/.test(newNodeValue)) { 994 newNodeValue = newNodeValue.replace(/[\n\t\r\f]+/g, " "); 995 } 996 if (newNodeValue.indexOf(" ") !== -1) { 997 newNodeValue = newNodeValue.replace(/ {2,}/g, " "); 998 } 999 if (i2 === 0 && newNodeValue.startsWith(" ") && (isRoot || hasPrecedingSpace)) { 1000 newNodeValue = newNodeValue.slice(1); 1001 } 1002 if (i2 === nodes.length - 1 && newNodeValue.endsWith(" ") && (isRoot || hasTrailingSpace)) { 1003 newNodeValue = newNodeValue.slice(0, -1); 1004 } 1005 node.nodeValue = newNodeValue; 1006 } else if (node.nodeType === node.ELEMENT_NODE) { 1007 const { previousSibling, nextSibling } = node; 1008 const prevHasSpace = previousSibling?.textContent.endsWith(" "); 1009 const nextHasSpace = nextSibling?.textContent.startsWith(" "); 1010 node.replaceWith( 1011 collapseWhiteSpace( 1012 node, 1013 false, 1014 previousSibling ? prevHasSpace : isRoot || hasPrecedingSpace, 1015 nextSibling ? nextHasSpace : isRoot || hasTrailingSpace 1016 ) 1017 ); 1018 } 1019 }); 1020 return clone; 1021 } 1022 var CARRIAGE_RETURN = "\r"; 1023 function removeReservedCharacters(string) { 1024 return string.replace( 1025 new RegExp( 1026 `[$ZWNBSP}$OBJECT_REPLACEMENT_CHARACTER}$CARRIAGE_RETURN}]`, 1027 "gu" 1028 ), 1029 "" 1030 ); 1031 } 1032 function createFromElement({ element, range, isEditableTree }) { 1033 const accumulator = createEmptyValue(); 1034 if (!element) { 1035 return accumulator; 1036 } 1037 if (!element.hasChildNodes()) { 1038 accumulateSelection(accumulator, element, range, createEmptyValue()); 1039 return accumulator; 1040 } 1041 const length = element.childNodes.length; 1042 for (let index = 0; index < length; index++) { 1043 const node = element.childNodes[index]; 1044 const tagName = node.nodeName.toLowerCase(); 1045 if (node.nodeType === node.TEXT_NODE) { 1046 const text = removeReservedCharacters(node.nodeValue); 1047 range = filterRange(node, range, removeReservedCharacters); 1048 accumulateSelection(accumulator, node, range, { text }); 1049 accumulator.formats.length += text.length; 1050 accumulator.replacements.length += text.length; 1051 accumulator.text += text; 1052 continue; 1053 } 1054 if (node.nodeType === node.COMMENT_NODE || node.nodeType === node.ELEMENT_NODE && node.tagName === "SPAN" && node.hasAttribute("data-rich-text-comment")) { 1055 const value2 = { 1056 formats: [,], 1057 replacements: [ 1058 { 1059 type: "#comment", 1060 attributes: { 1061 "data-rich-text-comment": node.nodeType === node.COMMENT_NODE ? node.nodeValue : node.getAttribute( 1062 "data-rich-text-comment" 1063 ) 1064 } 1065 } 1066 ], 1067 text: OBJECT_REPLACEMENT_CHARACTER 1068 }; 1069 accumulateSelection(accumulator, node, range, value2); 1070 mergePair(accumulator, value2); 1071 continue; 1072 } 1073 if (node.nodeType !== node.ELEMENT_NODE) { 1074 continue; 1075 } 1076 if (isEditableTree && // Ignore any line breaks that are not inserted by us. 1077 tagName === "br" && !node.getAttribute("data-rich-text-line-break")) { 1078 accumulateSelection(accumulator, node, range, createEmptyValue()); 1079 continue; 1080 } 1081 if (tagName === "script") { 1082 const value2 = { 1083 formats: [,], 1084 replacements: [ 1085 { 1086 type: tagName, 1087 attributes: { 1088 "data-rich-text-script": node.getAttribute("data-rich-text-script") || encodeURIComponent(node.innerHTML) 1089 } 1090 } 1091 ], 1092 text: OBJECT_REPLACEMENT_CHARACTER 1093 }; 1094 accumulateSelection(accumulator, node, range, value2); 1095 mergePair(accumulator, value2); 1096 continue; 1097 } 1098 if (tagName === "br") { 1099 accumulateSelection(accumulator, node, range, createEmptyValue()); 1100 mergePair(accumulator, create({ text: "\n" })); 1101 continue; 1102 } 1103 const format = toFormat({ 1104 tagName, 1105 attributes: getAttributes({ element: node }) 1106 }); 1107 if (format?.formatType?.contentEditable === false) { 1108 delete format.formatType; 1109 accumulateSelection(accumulator, node, range, createEmptyValue()); 1110 mergePair(accumulator, { 1111 formats: [,], 1112 replacements: [ 1113 { 1114 ...format, 1115 innerHTML: node.innerHTML 1116 } 1117 ], 1118 text: OBJECT_REPLACEMENT_CHARACTER 1119 }); 1120 continue; 1121 } 1122 if (format) { 1123 delete format.formatType; 1124 } 1125 const value = createFromElement({ 1126 element: node, 1127 range, 1128 isEditableTree 1129 }); 1130 accumulateSelection(accumulator, node, range, value); 1131 if (!format || node.getAttribute("data-rich-text-placeholder") || node.getAttribute("data-rich-text-bogus")) { 1132 mergePair(accumulator, value); 1133 } else if (value.text.length === 0) { 1134 if (format.attributes) { 1135 mergePair(accumulator, { 1136 formats: [,], 1137 replacements: [format], 1138 text: OBJECT_REPLACEMENT_CHARACTER 1139 }); 1140 } 1141 } else { 1142 let mergeFormats2 = function(formats) { 1143 if (mergeFormats2.formats === formats) { 1144 return mergeFormats2.newFormats; 1145 } 1146 const newFormats = formats ? [format, ...formats] : [format]; 1147 mergeFormats2.formats = formats; 1148 mergeFormats2.newFormats = newFormats; 1149 return newFormats; 1150 }; 1151 var mergeFormats = mergeFormats2; 1152 mergeFormats2.newFormats = [format]; 1153 mergePair(accumulator, { 1154 ...value, 1155 formats: Array.from(value.formats, mergeFormats2) 1156 }); 1157 } 1158 } 1159 return accumulator; 1160 } 1161 function getAttributes({ element }) { 1162 if (!element.hasAttributes()) { 1163 return; 1164 } 1165 const length = element.attributes.length; 1166 let accumulator; 1167 for (let i2 = 0; i2 < length; i2++) { 1168 const { name, value } = element.attributes[i2]; 1169 if (name.indexOf("data-rich-text-") === 0) { 1170 continue; 1171 } 1172 const safeName = /^on/i.test(name) ? "data-disable-rich-text-" + name : name; 1173 accumulator = accumulator || {}; 1174 accumulator[safeName] = value; 1175 } 1176 return accumulator; 1177 } 1178 1179 // packages/rich-text/build-module/concat.mjs 1180 function mergePair(a2, b2) { 1181 a2.formats = a2.formats.concat(b2.formats); 1182 a2.replacements = a2.replacements.concat(b2.replacements); 1183 a2.text += b2.text; 1184 return a2; 1185 } 1186 function concat(...values) { 1187 return normaliseFormats(values.reduce(mergePair, create())); 1188 } 1189 1190 // packages/rich-text/build-module/get-active-format.mjs 1191 function getActiveFormat(value, formatType) { 1192 return getActiveFormats(value).find( 1193 ({ type }) => type === formatType 1194 ); 1195 } 1196 1197 // packages/rich-text/build-module/get-active-object.mjs 1198 function getActiveObject({ start, end, replacements, text }) { 1199 if (start + 1 !== end || text[start] !== OBJECT_REPLACEMENT_CHARACTER) { 1200 return; 1201 } 1202 return replacements[start]; 1203 } 1204 1205 // packages/rich-text/build-module/is-collapsed.mjs 1206 function isCollapsed({ 1207 start, 1208 end 1209 }) { 1210 if (start === void 0 || end === void 0) { 1211 return; 1212 } 1213 return start === end; 1214 } 1215 1216 // packages/rich-text/build-module/is-empty.mjs 1217 function isEmpty({ text }) { 1218 return text.length === 0; 1219 } 1220 1221 // packages/rich-text/build-module/join.mjs 1222 function join(values, separator = "") { 1223 if (typeof separator === "string") { 1224 separator = create({ text: separator }); 1225 } 1226 return normaliseFormats( 1227 values.reduce((accumulator, { formats, replacements, text }) => ({ 1228 formats: accumulator.formats.concat(separator.formats, formats), 1229 replacements: accumulator.replacements.concat( 1230 separator.replacements, 1231 replacements 1232 ), 1233 text: accumulator.text + separator.text + text 1234 })) 1235 ); 1236 } 1237 1238 // packages/rich-text/build-module/register-format-type.mjs 1239 var import_data6 = __toESM(require_data(), 1); 1240 function registerFormatType(name, settings) { 1241 settings = { 1242 name, 1243 ...settings 1244 }; 1245 if (typeof settings.name !== "string") { 1246 window.console.error("Format names must be strings."); 1247 return; 1248 } 1249 if (!/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test(settings.name)) { 1250 window.console.error( 1251 "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" 1252 ); 1253 return; 1254 } 1255 if ((0, import_data6.select)(store).getFormatType(settings.name)) { 1256 window.console.error( 1257 'Format "' + settings.name + '" is already registered.' 1258 ); 1259 return; 1260 } 1261 if (typeof settings.tagName !== "string" || settings.tagName === "") { 1262 window.console.error("Format tag names must be a string."); 1263 return; 1264 } 1265 if ((typeof settings.className !== "string" || settings.className === "") && settings.className !== null) { 1266 window.console.error( 1267 "Format class names must be a string, or null to handle bare elements." 1268 ); 1269 return; 1270 } 1271 if (!/^[_a-zA-Z]+[a-zA-Z0-9_-]*$/.test(settings.className)) { 1272 window.console.error( 1273 "A class name must begin with a letter, followed by any number of hyphens, underscores, letters, or numbers." 1274 ); 1275 return; 1276 } 1277 if (settings.className === null) { 1278 const formatTypeForBareElement = (0, import_data6.select)( 1279 store 1280 ).getFormatTypeForBareElement(settings.tagName); 1281 if (formatTypeForBareElement && formatTypeForBareElement.name !== "core/unknown") { 1282 window.console.error( 1283 `Format "$formatTypeForBareElement.name}" is already registered to handle bare tag name "$settings.tagName}".` 1284 ); 1285 return; 1286 } 1287 } else { 1288 const formatTypeForClassName = (0, import_data6.select)( 1289 store 1290 ).getFormatTypeForClassName(settings.className); 1291 if (formatTypeForClassName) { 1292 window.console.error( 1293 `Format "$formatTypeForClassName.name}" is already registered to handle class name "$settings.className}".` 1294 ); 1295 return; 1296 } 1297 } 1298 if (!("title" in settings) || settings.title === "") { 1299 window.console.error( 1300 'The format "' + settings.name + '" must have a title.' 1301 ); 1302 return; 1303 } 1304 if ("keywords" in settings && settings.keywords.length > 3) { 1305 window.console.error( 1306 'The format "' + settings.name + '" can have a maximum of 3 keywords.' 1307 ); 1308 return; 1309 } 1310 if (typeof settings.title !== "string") { 1311 window.console.error("Format titles must be strings."); 1312 return; 1313 } 1314 (0, import_data6.dispatch)(store).addFormatTypes(settings); 1315 return settings; 1316 } 1317 1318 // packages/rich-text/build-module/remove-format.mjs 1319 function removeFormat(value, formatType, startIndex = value.start, endIndex = value.end) { 1320 const { formats, activeFormats } = value; 1321 const newFormats = formats.slice(); 1322 if (startIndex === endIndex) { 1323 const format = newFormats[startIndex]?.find( 1324 ({ type }) => type === formatType 1325 ); 1326 if (format) { 1327 while (newFormats[startIndex]?.find( 1328 (newFormat) => newFormat === format 1329 )) { 1330 filterFormats(newFormats, startIndex, formatType); 1331 startIndex--; 1332 } 1333 endIndex++; 1334 while (newFormats[endIndex]?.find( 1335 (newFormat) => newFormat === format 1336 )) { 1337 filterFormats(newFormats, endIndex, formatType); 1338 endIndex++; 1339 } 1340 } 1341 } else { 1342 for (let i2 = startIndex; i2 < endIndex; i2++) { 1343 if (newFormats[i2]) { 1344 filterFormats(newFormats, i2, formatType); 1345 } 1346 } 1347 } 1348 return normaliseFormats({ 1349 ...value, 1350 formats: newFormats, 1351 activeFormats: activeFormats?.filter(({ type }) => type !== formatType) || [] 1352 }); 1353 } 1354 function filterFormats(formats, index, formatType) { 1355 const newFormats = formats[index].filter( 1356 ({ type }) => type !== formatType 1357 ); 1358 if (newFormats.length) { 1359 formats[index] = newFormats; 1360 } else { 1361 delete formats[index]; 1362 } 1363 } 1364 1365 // packages/rich-text/build-module/insert.mjs 1366 function insert(value, valueToInsert, startIndex = value.start, endIndex = value.end) { 1367 const { formats, replacements, text } = value; 1368 if (typeof valueToInsert === "string") { 1369 valueToInsert = create({ text: valueToInsert }); 1370 } 1371 const index = startIndex + valueToInsert.text.length; 1372 return normaliseFormats({ 1373 formats: formats.slice(0, startIndex).concat(valueToInsert.formats, formats.slice(endIndex)), 1374 replacements: replacements.slice(0, startIndex).concat( 1375 valueToInsert.replacements, 1376 replacements.slice(endIndex) 1377 ), 1378 text: text.slice(0, startIndex) + valueToInsert.text + text.slice(endIndex), 1379 start: index, 1380 end: index 1381 }); 1382 } 1383 1384 // packages/rich-text/build-module/remove.mjs 1385 function remove2(value, startIndex, endIndex) { 1386 return insert(value, create(), startIndex, endIndex); 1387 } 1388 1389 // packages/rich-text/build-module/replace.mjs 1390 function replace2({ formats, replacements, text, start, end }, pattern, replacement) { 1391 text = text.replace(pattern, (match, ...rest) => { 1392 const offset = rest[rest.length - 2]; 1393 let newText = replacement; 1394 let newFormats; 1395 let newReplacements; 1396 if (typeof newText === "function") { 1397 newText = replacement(match, ...rest); 1398 } 1399 if (typeof newText === "object") { 1400 newFormats = newText.formats; 1401 newReplacements = newText.replacements; 1402 newText = newText.text; 1403 } else { 1404 newFormats = Array(newText.length); 1405 newReplacements = Array(newText.length); 1406 if (formats[offset]) { 1407 newFormats = newFormats.fill(formats[offset]); 1408 } 1409 } 1410 formats = formats.slice(0, offset).concat(newFormats, formats.slice(offset + match.length)); 1411 replacements = replacements.slice(0, offset).concat( 1412 newReplacements, 1413 replacements.slice(offset + match.length) 1414 ); 1415 if (start) { 1416 start = end = offset + newText.length; 1417 } 1418 return newText; 1419 }); 1420 return normaliseFormats({ formats, replacements, text, start, end }); 1421 } 1422 1423 // packages/rich-text/build-module/insert-object.mjs 1424 function insertObject(value, formatToInsert, startIndex, endIndex) { 1425 const valueToInsert = { 1426 formats: [,], 1427 replacements: [formatToInsert], 1428 text: OBJECT_REPLACEMENT_CHARACTER 1429 }; 1430 return insert(value, valueToInsert, startIndex, endIndex); 1431 } 1432 1433 // packages/rich-text/build-module/slice.mjs 1434 function slice(value, startIndex = value.start, endIndex = value.end) { 1435 const { formats, replacements, text } = value; 1436 if (startIndex === void 0 || endIndex === void 0) { 1437 return { ...value }; 1438 } 1439 return { 1440 formats: formats.slice(startIndex, endIndex), 1441 replacements: replacements.slice(startIndex, endIndex), 1442 text: text.slice(startIndex, endIndex) 1443 }; 1444 } 1445 1446 // packages/rich-text/build-module/split.mjs 1447 function split({ formats, replacements, text, start, end }, string) { 1448 if (typeof string !== "string") { 1449 return splitAtSelection(...arguments); 1450 } 1451 let nextStart = 0; 1452 return text.split(string).map((substring) => { 1453 const startIndex = nextStart; 1454 const value = { 1455 formats: formats.slice(startIndex, startIndex + substring.length), 1456 replacements: replacements.slice( 1457 startIndex, 1458 startIndex + substring.length 1459 ), 1460 text: substring 1461 }; 1462 nextStart += string.length + substring.length; 1463 if (start !== void 0 && end !== void 0) { 1464 if (start >= startIndex && start < nextStart) { 1465 value.start = start - startIndex; 1466 } else if (start < startIndex && end > startIndex) { 1467 value.start = 0; 1468 } 1469 if (end >= startIndex && end < nextStart) { 1470 value.end = end - startIndex; 1471 } else if (start < nextStart && end > nextStart) { 1472 value.end = substring.length; 1473 } 1474 } 1475 return value; 1476 }); 1477 } 1478 function splitAtSelection({ formats, replacements, text, start, end }, startIndex = start, endIndex = end) { 1479 if (start === void 0 || end === void 0) { 1480 return; 1481 } 1482 const before = { 1483 formats: formats.slice(0, startIndex), 1484 replacements: replacements.slice(0, startIndex), 1485 text: text.slice(0, startIndex) 1486 }; 1487 const after = { 1488 formats: formats.slice(endIndex), 1489 replacements: replacements.slice(endIndex), 1490 text: text.slice(endIndex), 1491 start: 0, 1492 end: 0 1493 }; 1494 return [before, after]; 1495 } 1496 1497 // packages/rich-text/build-module/is-range-equal.mjs 1498 function isRangeEqual(a2, b2) { 1499 return a2 === b2 || a2 && b2 && a2.startContainer === b2.startContainer && a2.startOffset === b2.startOffset && a2.endContainer === b2.endContainer && a2.endOffset === b2.endOffset; 1500 } 1501 1502 // packages/rich-text/build-module/to-dom.mjs 1503 var MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML"; 1504 function createPathToNode(node, rootNode, path) { 1505 const parentNode = node.parentNode; 1506 let i2 = 0; 1507 while (node = node.previousSibling) { 1508 i2++; 1509 } 1510 path = [i2, ...path]; 1511 if (parentNode !== rootNode) { 1512 path = createPathToNode(parentNode, rootNode, path); 1513 } 1514 return path; 1515 } 1516 function getNodeByPath(node, path) { 1517 path = [...path]; 1518 while (node && path.length > 1) { 1519 node = node.childNodes[path.shift()]; 1520 } 1521 return { 1522 node, 1523 offset: path[0] 1524 }; 1525 } 1526 function append2(element, child) { 1527 if (child.html !== void 0) { 1528 return element.innerHTML += child.html; 1529 } 1530 if (typeof child === "string") { 1531 child = element.ownerDocument.createTextNode(child); 1532 } 1533 const { type, attributes } = child; 1534 if (type) { 1535 if (type === "#comment") { 1536 child = element.ownerDocument.createComment( 1537 attributes["data-rich-text-comment"] 1538 ); 1539 } else { 1540 const parentNamespace = element.namespaceURI; 1541 if (type === "math") { 1542 child = element.ownerDocument.createElementNS( 1543 MATHML_NAMESPACE, 1544 type 1545 ); 1546 } else if (parentNamespace === MATHML_NAMESPACE) { 1547 if (element.tagName === "MTEXT") { 1548 child = element.ownerDocument.createElement(type); 1549 } else { 1550 child = element.ownerDocument.createElementNS( 1551 MATHML_NAMESPACE, 1552 type 1553 ); 1554 } 1555 } else { 1556 child = element.ownerDocument.createElement(type); 1557 } 1558 for (const key in attributes) { 1559 child.setAttribute(key, attributes[key]); 1560 } 1561 } 1562 } 1563 return element.appendChild(child); 1564 } 1565 function appendText2(node, text) { 1566 node.appendData(text); 1567 } 1568 function getLastChild2({ lastChild }) { 1569 return lastChild; 1570 } 1571 function getParent2({ parentNode }) { 1572 return parentNode; 1573 } 1574 function isText2(node) { 1575 return node.nodeType === node.TEXT_NODE; 1576 } 1577 function getText2({ nodeValue }) { 1578 return nodeValue; 1579 } 1580 function remove3(node) { 1581 return node.parentNode.removeChild(node); 1582 } 1583 function toDom({ 1584 value, 1585 prepareEditableTree, 1586 isEditableTree = true, 1587 placeholder, 1588 doc = document 1589 }) { 1590 let startPath = []; 1591 let endPath = []; 1592 if (prepareEditableTree) { 1593 value = { 1594 ...value, 1595 formats: prepareEditableTree(value) 1596 }; 1597 } 1598 const createEmpty2 = () => createElement(doc, ""); 1599 const tree = toTree({ 1600 value, 1601 createEmpty: createEmpty2, 1602 append: append2, 1603 getLastChild: getLastChild2, 1604 getParent: getParent2, 1605 isText: isText2, 1606 getText: getText2, 1607 remove: remove3, 1608 appendText: appendText2, 1609 onStartIndex(body, pointer) { 1610 startPath = createPathToNode(pointer, body, [ 1611 pointer.nodeValue.length 1612 ]); 1613 }, 1614 onEndIndex(body, pointer) { 1615 endPath = createPathToNode(pointer, body, [ 1616 pointer.nodeValue.length 1617 ]); 1618 }, 1619 isEditableTree, 1620 placeholder 1621 }); 1622 return { 1623 body: tree, 1624 selection: { startPath, endPath } 1625 }; 1626 } 1627 function apply({ 1628 value, 1629 current, 1630 prepareEditableTree, 1631 __unstableDomOnly, 1632 placeholder 1633 }) { 1634 const { body, selection } = toDom({ 1635 value, 1636 prepareEditableTree, 1637 placeholder, 1638 doc: current.ownerDocument 1639 }); 1640 applyValue(body, current); 1641 if (value.start !== void 0 && !__unstableDomOnly) { 1642 applySelection(selection, current); 1643 } 1644 } 1645 function applyValue(future, current) { 1646 let i2 = 0; 1647 let futureChild; 1648 while (futureChild = future.firstChild) { 1649 const currentChild = current.childNodes[i2]; 1650 if (!currentChild) { 1651 current.appendChild(futureChild); 1652 } else if (!currentChild.isEqualNode(futureChild)) { 1653 if (currentChild.nodeName !== futureChild.nodeName || currentChild.nodeType === currentChild.TEXT_NODE && currentChild.data !== futureChild.data) { 1654 current.replaceChild(futureChild, currentChild); 1655 } else { 1656 const currentAttributes = currentChild.attributes; 1657 const futureAttributes = futureChild.attributes; 1658 if (currentAttributes) { 1659 let ii = currentAttributes.length; 1660 while (ii--) { 1661 const { name } = currentAttributes[ii]; 1662 if (!futureChild.getAttribute(name)) { 1663 currentChild.removeAttribute(name); 1664 } 1665 } 1666 } 1667 if (futureAttributes) { 1668 for (let ii = 0; ii < futureAttributes.length; ii++) { 1669 const { name, value } = futureAttributes[ii]; 1670 if (currentChild.getAttribute(name) !== value) { 1671 currentChild.setAttribute(name, value); 1672 } 1673 } 1674 } 1675 applyValue(futureChild, currentChild); 1676 future.removeChild(futureChild); 1677 } 1678 } else { 1679 future.removeChild(futureChild); 1680 } 1681 i2++; 1682 } 1683 while (current.childNodes[i2]) { 1684 current.removeChild(current.childNodes[i2]); 1685 } 1686 } 1687 function applySelection({ startPath, endPath }, current) { 1688 const { node: startContainer, offset: startOffset } = getNodeByPath( 1689 current, 1690 startPath 1691 ); 1692 const { node: endContainer, offset: endOffset } = getNodeByPath( 1693 current, 1694 endPath 1695 ); 1696 const { ownerDocument } = current; 1697 const { defaultView } = ownerDocument; 1698 const selection = defaultView.getSelection(); 1699 const range = ownerDocument.createRange(); 1700 range.setStart(startContainer, startOffset); 1701 range.setEnd(endContainer, endOffset); 1702 const { activeElement } = ownerDocument; 1703 if (selection.rangeCount > 0) { 1704 if (isRangeEqual(range, selection.getRangeAt(0))) { 1705 return; 1706 } 1707 selection.removeAllRanges(); 1708 } 1709 selection.addRange(range); 1710 if (activeElement !== ownerDocument.activeElement) { 1711 if (activeElement instanceof defaultView.HTMLElement) { 1712 activeElement.focus(); 1713 } 1714 } 1715 } 1716 1717 // packages/rich-text/build-module/toggle-format.mjs 1718 var import_a11y = __toESM(require_a11y(), 1); 1719 var import_i18n = __toESM(require_i18n(), 1); 1720 function toggleFormat(value, format) { 1721 if (getActiveFormat(value, format.type)) { 1722 if (format.title) { 1723 (0, import_a11y.speak)((0, import_i18n.sprintf)((0, import_i18n.__)("%s removed."), format.title), "assertive"); 1724 } 1725 return removeFormat(value, format.type); 1726 } 1727 if (format.title) { 1728 (0, import_a11y.speak)((0, import_i18n.sprintf)((0, import_i18n.__)("%s applied."), format.title), "assertive"); 1729 } 1730 return applyFormat(value, format); 1731 } 1732 1733 // packages/rich-text/build-module/unregister-format-type.mjs 1734 var import_data7 = __toESM(require_data(), 1); 1735 function unregisterFormatType(name) { 1736 const oldFormat = (0, import_data7.select)(store).getFormatType(name); 1737 if (!oldFormat) { 1738 window.console.error(`Format $name} is not registered.`); 1739 return; 1740 } 1741 (0, import_data7.dispatch)(store).removeFormatTypes(name); 1742 return oldFormat; 1743 } 1744 1745 // packages/rich-text/build-module/lock-unlock.mjs 1746 var import_private_apis = __toESM(require_private_apis(), 1); 1747 var { lock, unlock } = (0, import_private_apis.__dangerousOptInToUnstableAPIsOnlyForCoreModules)( 1748 "I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.", 1749 "@wordpress/rich-text" 1750 ); 1751 1752 // packages/rich-text/build-module/hook/index.mjs 1753 var import_element5 = __toESM(require_element(), 1); 1754 var import_compose8 = __toESM(require_compose(), 1); 1755 var import_data9 = __toESM(require_data(), 1); 1756 var import_deprecated = __toESM(require_deprecated(), 1); 1757 1758 // packages/rich-text/build-module/owns-selection.mjs 1759 function ownsSelection(element) { 1760 const { ownerDocument } = element; 1761 const { activeElement } = ownerDocument; 1762 if (activeElement === element) { 1763 return true; 1764 } 1765 if (!activeElement || activeElement.contentEditable !== "true" || element.contentEditable !== "true" || !activeElement.contains(element)) { 1766 return false; 1767 } 1768 const selection = ownerDocument.defaultView.getSelection(); 1769 const { anchorNode, focusNode } = selection; 1770 return !!anchorNode && !!focusNode && element.contains(anchorNode) && element.contains(focusNode); 1771 } 1772 1773 // packages/rich-text/build-module/hook/use-default-style.mjs 1774 var import_element = __toESM(require_element(), 1); 1775 var whiteSpace = "pre-wrap"; 1776 function useDefaultStyle() { 1777 return (0, import_element.useCallback)((element) => { 1778 if (!element) { 1779 return; 1780 } 1781 element.style.whiteSpace = element.style.whiteSpace || whiteSpace; 1782 }, []); 1783 } 1784 1785 // node_modules/colord/index.mjs 1786 var r = { grad: 0.9, turn: 360, rad: 360 / (2 * Math.PI) }; 1787 var t = function(r2) { 1788 return "string" == typeof r2 ? r2.length > 0 : "number" == typeof r2; 1789 }; 1790 var n = function(r2, t2, n2) { 1791 return void 0 === t2 && (t2 = 0), void 0 === n2 && (n2 = Math.pow(10, t2)), Math.round(n2 * r2) / n2 + 0; 1792 }; 1793 var e = function(r2, t2, n2) { 1794 return void 0 === t2 && (t2 = 0), void 0 === n2 && (n2 = 1), r2 > n2 ? n2 : r2 > t2 ? r2 : t2; 1795 }; 1796 var u = function(r2) { 1797 return (r2 = isFinite(r2) ? r2 % 360 : 0) > 0 ? r2 : r2 + 360; 1798 }; 1799 var a = function(r2) { 1800 return { r: e(r2.r, 0, 255), g: e(r2.g, 0, 255), b: e(r2.b, 0, 255), a: e(r2.a) }; 1801 }; 1802 var o = function(r2) { 1803 return { r: n(r2.r), g: n(r2.g), b: n(r2.b), a: n(r2.a, 3) }; 1804 }; 1805 var i = /^#([0-9a-f]{3,8})$/i; 1806 var s = function(r2) { 1807 var t2 = r2.toString(16); 1808 return t2.length < 2 ? "0" + t2 : t2; 1809 }; 1810 var h = function(r2) { 1811 var t2 = r2.r, n2 = r2.g, e2 = r2.b, u2 = r2.a, a2 = Math.max(t2, n2, e2), o2 = a2 - Math.min(t2, n2, e2), i2 = o2 ? a2 === t2 ? (n2 - e2) / o2 : a2 === n2 ? 2 + (e2 - t2) / o2 : 4 + (t2 - n2) / o2 : 0; 1812 return { h: 60 * (i2 < 0 ? i2 + 6 : i2), s: a2 ? o2 / a2 * 100 : 0, v: a2 / 255 * 100, a: u2 }; 1813 }; 1814 var b = function(r2) { 1815 var t2 = r2.h, n2 = r2.s, e2 = r2.v, u2 = r2.a; 1816 t2 = t2 / 360 * 6, n2 /= 100, e2 /= 100; 1817 var a2 = Math.floor(t2), o2 = e2 * (1 - n2), i2 = e2 * (1 - (t2 - a2) * n2), s2 = e2 * (1 - (1 - t2 + a2) * n2), h2 = a2 % 6; 1818 return { r: 255 * [e2, i2, o2, o2, s2, e2][h2], g: 255 * [s2, e2, e2, i2, o2, o2][h2], b: 255 * [o2, o2, s2, e2, e2, i2][h2], a: u2 }; 1819 }; 1820 var g = function(r2) { 1821 return { h: u(r2.h), s: e(r2.s, 0, 100), l: e(r2.l, 0, 100), a: e(r2.a) }; 1822 }; 1823 var d = function(r2) { 1824 return { h: n(r2.h), s: n(r2.s), l: n(r2.l), a: n(r2.a, 3) }; 1825 }; 1826 var f = function(r2) { 1827 return b((n2 = (t2 = r2).s, { h: t2.h, s: (n2 *= ((e2 = t2.l) < 50 ? e2 : 100 - e2) / 100) > 0 ? 2 * n2 / (e2 + n2) * 100 : 0, v: e2 + n2, a: t2.a })); 1828 var t2, n2, e2; 1829 }; 1830 var c = function(r2) { 1831 return { h: (t2 = h(r2)).h, s: (u2 = (200 - (n2 = t2.s)) * (e2 = t2.v) / 100) > 0 && u2 < 200 ? n2 * e2 / 100 / (u2 <= 100 ? u2 : 200 - u2) * 100 : 0, l: u2 / 2, a: t2.a }; 1832 var t2, n2, e2, u2; 1833 }; 1834 var l = /^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s*,\s*([+-]?\d*\.?\d+)%\s*,\s*([+-]?\d*\.?\d+)%\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i; 1835 var p = /^hsla?\(\s*([+-]?\d*\.?\d+)(deg|rad|grad|turn)?\s+([+-]?\d*\.?\d+)%\s+([+-]?\d*\.?\d+)%\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i; 1836 var v = /^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*,\s*([+-]?\d*\.?\d+)(%)?\s*(?:,\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i; 1837 var m = /^rgba?\(\s*([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s+([+-]?\d*\.?\d+)(%)?\s*(?:\/\s*([+-]?\d*\.?\d+)(%)?\s*)?\)$/i; 1838 var y = { string: [[function(r2) { 1839 var t2 = i.exec(r2); 1840 return t2 ? (r2 = t2[1]).length <= 4 ? { r: parseInt(r2[0] + r2[0], 16), g: parseInt(r2[1] + r2[1], 16), b: parseInt(r2[2] + r2[2], 16), a: 4 === r2.length ? n(parseInt(r2[3] + r2[3], 16) / 255, 2) : 1 } : 6 === r2.length || 8 === r2.length ? { r: parseInt(r2.substr(0, 2), 16), g: parseInt(r2.substr(2, 2), 16), b: parseInt(r2.substr(4, 2), 16), a: 8 === r2.length ? n(parseInt(r2.substr(6, 2), 16) / 255, 2) : 1 } : null : null; 1841 }, "hex"], [function(r2) { 1842 var t2 = v.exec(r2) || m.exec(r2); 1843 return t2 ? t2[2] !== t2[4] || t2[4] !== t2[6] ? null : a({ r: Number(t2[1]) / (t2[2] ? 100 / 255 : 1), g: Number(t2[3]) / (t2[4] ? 100 / 255 : 1), b: Number(t2[5]) / (t2[6] ? 100 / 255 : 1), a: void 0 === t2[7] ? 1 : Number(t2[7]) / (t2[8] ? 100 : 1) }) : null; 1844 }, "rgb"], [function(t2) { 1845 var n2 = l.exec(t2) || p.exec(t2); 1846 if (!n2) return null; 1847 var e2, u2, a2 = g({ h: (e2 = n2[1], u2 = n2[2], void 0 === u2 && (u2 = "deg"), Number(e2) * (r[u2] || 1)), s: Number(n2[3]), l: Number(n2[4]), a: void 0 === n2[5] ? 1 : Number(n2[5]) / (n2[6] ? 100 : 1) }); 1848 return f(a2); 1849 }, "hsl"]], object: [[function(r2) { 1850 var n2 = r2.r, e2 = r2.g, u2 = r2.b, o2 = r2.a, i2 = void 0 === o2 ? 1 : o2; 1851 return t(n2) && t(e2) && t(u2) ? a({ r: Number(n2), g: Number(e2), b: Number(u2), a: Number(i2) }) : null; 1852 }, "rgb"], [function(r2) { 1853 var n2 = r2.h, e2 = r2.s, u2 = r2.l, a2 = r2.a, o2 = void 0 === a2 ? 1 : a2; 1854 if (!t(n2) || !t(e2) || !t(u2)) return null; 1855 var i2 = g({ h: Number(n2), s: Number(e2), l: Number(u2), a: Number(o2) }); 1856 return f(i2); 1857 }, "hsl"], [function(r2) { 1858 var n2 = r2.h, a2 = r2.s, o2 = r2.v, i2 = r2.a, s2 = void 0 === i2 ? 1 : i2; 1859 if (!t(n2) || !t(a2) || !t(o2)) return null; 1860 var h2 = (function(r3) { 1861 return { h: u(r3.h), s: e(r3.s, 0, 100), v: e(r3.v, 0, 100), a: e(r3.a) }; 1862 })({ h: Number(n2), s: Number(a2), v: Number(o2), a: Number(s2) }); 1863 return b(h2); 1864 }, "hsv"]] }; 1865 var N = function(r2, t2) { 1866 for (var n2 = 0; n2 < t2.length; n2++) { 1867 var e2 = t2[n2][0](r2); 1868 if (e2) return [e2, t2[n2][1]]; 1869 } 1870 return [null, void 0]; 1871 }; 1872 var x = function(r2) { 1873 return "string" == typeof r2 ? N(r2.trim(), y.string) : "object" == typeof r2 && null !== r2 ? N(r2, y.object) : [null, void 0]; 1874 }; 1875 var M = function(r2, t2) { 1876 var n2 = c(r2); 1877 return { h: n2.h, s: e(n2.s + 100 * t2, 0, 100), l: n2.l, a: n2.a }; 1878 }; 1879 var H = function(r2) { 1880 return (299 * r2.r + 587 * r2.g + 114 * r2.b) / 1e3 / 255; 1881 }; 1882 var $ = function(r2, t2) { 1883 var n2 = c(r2); 1884 return { h: n2.h, s: n2.s, l: e(n2.l + 100 * t2, 0, 100), a: n2.a }; 1885 }; 1886 var j = (function() { 1887 function r2(r3) { 1888 this.parsed = x(r3)[0], this.rgba = this.parsed || { r: 0, g: 0, b: 0, a: 1 }; 1889 } 1890 return r2.prototype.isValid = function() { 1891 return null !== this.parsed; 1892 }, r2.prototype.brightness = function() { 1893 return n(H(this.rgba), 2); 1894 }, r2.prototype.isDark = function() { 1895 return H(this.rgba) < 0.5; 1896 }, r2.prototype.isLight = function() { 1897 return H(this.rgba) >= 0.5; 1898 }, r2.prototype.toHex = function() { 1899 return r3 = o(this.rgba), t2 = r3.r, e2 = r3.g, u2 = r3.b, i2 = (a2 = r3.a) < 1 ? s(n(255 * a2)) : "", "#" + s(t2) + s(e2) + s(u2) + i2; 1900 var r3, t2, e2, u2, a2, i2; 1901 }, r2.prototype.toRgb = function() { 1902 return o(this.rgba); 1903 }, r2.prototype.toRgbString = function() { 1904 return r3 = o(this.rgba), t2 = r3.r, n2 = r3.g, e2 = r3.b, (u2 = r3.a) < 1 ? "rgba(" + t2 + ", " + n2 + ", " + e2 + ", " + u2 + ")" : "rgb(" + t2 + ", " + n2 + ", " + e2 + ")"; 1905 var r3, t2, n2, e2, u2; 1906 }, r2.prototype.toHsl = function() { 1907 return d(c(this.rgba)); 1908 }, r2.prototype.toHslString = function() { 1909 return r3 = d(c(this.rgba)), t2 = r3.h, n2 = r3.s, e2 = r3.l, (u2 = r3.a) < 1 ? "hsla(" + t2 + ", " + n2 + "%, " + e2 + "%, " + u2 + ")" : "hsl(" + t2 + ", " + n2 + "%, " + e2 + "%)"; 1910 var r3, t2, n2, e2, u2; 1911 }, r2.prototype.toHsv = function() { 1912 return r3 = h(this.rgba), { h: n(r3.h), s: n(r3.s), v: n(r3.v), a: n(r3.a, 3) }; 1913 var r3; 1914 }, r2.prototype.invert = function() { 1915 return w({ r: 255 - (r3 = this.rgba).r, g: 255 - r3.g, b: 255 - r3.b, a: r3.a }); 1916 var r3; 1917 }, r2.prototype.saturate = function(r3) { 1918 return void 0 === r3 && (r3 = 0.1), w(M(this.rgba, r3)); 1919 }, r2.prototype.desaturate = function(r3) { 1920 return void 0 === r3 && (r3 = 0.1), w(M(this.rgba, -r3)); 1921 }, r2.prototype.grayscale = function() { 1922 return w(M(this.rgba, -1)); 1923 }, r2.prototype.lighten = function(r3) { 1924 return void 0 === r3 && (r3 = 0.1), w($(this.rgba, r3)); 1925 }, r2.prototype.darken = function(r3) { 1926 return void 0 === r3 && (r3 = 0.1), w($(this.rgba, -r3)); 1927 }, r2.prototype.rotate = function(r3) { 1928 return void 0 === r3 && (r3 = 15), this.hue(this.hue() + r3); 1929 }, r2.prototype.alpha = function(r3) { 1930 return "number" == typeof r3 ? w({ r: (t2 = this.rgba).r, g: t2.g, b: t2.b, a: r3 }) : n(this.rgba.a, 3); 1931 var t2; 1932 }, r2.prototype.hue = function(r3) { 1933 var t2 = c(this.rgba); 1934 return "number" == typeof r3 ? w({ h: r3, s: t2.s, l: t2.l, a: t2.a }) : n(t2.h); 1935 }, r2.prototype.isEqual = function(r3) { 1936 return this.toHex() === w(r3).toHex(); 1937 }, r2; 1938 })(); 1939 var w = function(r2) { 1940 return r2 instanceof j ? r2 : new j(r2); 1941 }; 1942 1943 // packages/rich-text/build-module/hook/use-boundary-style.mjs 1944 var import_element2 = __toESM(require_element(), 1); 1945 function useBoundaryStyle({ record }) { 1946 const ref = (0, import_element2.useRef)(); 1947 const { activeFormats = [], replacements, start } = record.current; 1948 const activeReplacement = replacements[start]; 1949 (0, import_element2.useEffect)(() => { 1950 if ((!activeFormats || !activeFormats.length) && !activeReplacement) { 1951 return; 1952 } 1953 const boundarySelector = "*[data-rich-text-format-boundary]"; 1954 const element = ref.current.querySelector(boundarySelector); 1955 if (!element) { 1956 return; 1957 } 1958 const { ownerDocument } = element; 1959 const { defaultView } = ownerDocument; 1960 const computedStyle = defaultView.getComputedStyle(element); 1961 const newColor = w(computedStyle.color).alpha(0.2).toRgbString(); 1962 const selector = `.rich-text:focus $boundarySelector}`; 1963 const rule = `background-color: $newColor}`; 1964 const style = `$selector} {$rule}}`; 1965 const globalStyleId = "rich-text-boundary-style"; 1966 let globalStyle = ownerDocument.getElementById(globalStyleId); 1967 if (!globalStyle) { 1968 globalStyle = ownerDocument.createElement("style"); 1969 globalStyle.id = globalStyleId; 1970 ownerDocument.head.appendChild(globalStyle); 1971 } 1972 if (globalStyle.innerHTML !== style) { 1973 globalStyle.innerHTML = style; 1974 } 1975 }, [activeFormats, activeReplacement]); 1976 return ref; 1977 } 1978 1979 // packages/rich-text/build-module/hook/event-listeners/index.mjs 1980 var import_element3 = __toESM(require_element(), 1); 1981 var import_compose7 = __toESM(require_compose(), 1); 1982 1983 // packages/rich-text/build-module/hook/event-listeners/copy-handler.mjs 1984 var import_compose = __toESM(require_compose(), 1); 1985 var { subscribeDelegatedListener } = unlock(import_compose.privateApis); 1986 var copy_handler_default = (props) => (element) => { 1987 function onCopy(event) { 1988 const { record, handleChange } = props.current; 1989 const { ownerDocument } = element; 1990 if ( 1991 // Another handler may have already claimed the clipboard, e.g. 1992 // the block editor copying the whole block when its entire 1993 // text is selected. 1994 event.defaultPrevented || isCollapsed(record.current) || !element.contains(ownerDocument.activeElement) && !ownsSelection(element) 1995 ) { 1996 return; 1997 } 1998 const selectedRecord = slice(record.current); 1999 const plainText = getTextContent(selectedRecord); 2000 const html = toHTMLString({ value: selectedRecord }); 2001 event.clipboardData.setData("text/plain", plainText); 2002 event.clipboardData.setData("text/html", html); 2003 event.clipboardData.setData("rich-text", "true"); 2004 event.preventDefault(); 2005 if (event.type === "cut") { 2006 handleChange(remove2(record.current)); 2007 } 2008 } 2009 const { defaultView } = element.ownerDocument; 2010 const unsubscribeCopy = subscribeDelegatedListener( 2011 defaultView, 2012 "copy", 2013 onCopy 2014 ); 2015 const unsubscribeCut = subscribeDelegatedListener( 2016 defaultView, 2017 "cut", 2018 onCopy 2019 ); 2020 return () => { 2021 unsubscribeCopy(); 2022 unsubscribeCut(); 2023 }; 2024 }; 2025 2026 // packages/rich-text/build-module/hook/event-listeners/select-object.mjs 2027 var import_compose2 = __toESM(require_compose(), 1); 2028 var { subscribeDelegatedListener: subscribeDelegatedListener2 } = unlock(import_compose2.privateApis); 2029 var select_object_default = () => (element) => { 2030 function onClick(event) { 2031 const { target } = event; 2032 if (target === element || target.textContent && target.isContentEditable) { 2033 return; 2034 } 2035 const { ownerDocument } = target; 2036 const { defaultView } = ownerDocument; 2037 const selection = defaultView.getSelection(); 2038 if (selection.containsNode(target)) { 2039 return; 2040 } 2041 const range = ownerDocument.createRange(); 2042 const nodeToSelect = target.isContentEditable ? target : target.closest("[contenteditable]"); 2043 range.selectNode(nodeToSelect); 2044 selection.removeAllRanges(); 2045 selection.addRange(range); 2046 event.preventDefault(); 2047 } 2048 function onFocusIn(event) { 2049 if (event.relatedTarget && !element.contains(event.relatedTarget)) { 2050 onClick(event); 2051 } 2052 } 2053 const unsubscribeClick = subscribeDelegatedListener2( 2054 element, 2055 "click", 2056 onClick 2057 ); 2058 const unsubscribeFocusIn = subscribeDelegatedListener2( 2059 element, 2060 "focusin", 2061 onFocusIn 2062 ); 2063 return () => { 2064 unsubscribeClick(); 2065 unsubscribeFocusIn(); 2066 }; 2067 }; 2068 2069 // packages/rich-text/build-module/hook/event-listeners/format-boundaries.mjs 2070 var import_keycodes = __toESM(require_keycodes(), 1); 2071 2072 // packages/rich-text/build-module/subscribe-owned-listener.mjs 2073 var import_compose3 = __toESM(require_compose(), 1); 2074 var { subscribeDelegatedListener: subscribeDelegatedListener3 } = unlock(import_compose3.privateApis); 2075 function subscribeOwnedListener(element, eventType, callback, capture = false) { 2076 return subscribeDelegatedListener3( 2077 element.ownerDocument, 2078 eventType, 2079 (event) => { 2080 if (!element.contains(event.target) && !ownsSelection(element)) { 2081 return; 2082 } 2083 callback(event); 2084 }, 2085 capture 2086 ); 2087 } 2088 2089 // packages/rich-text/build-module/hook/event-listeners/format-boundaries.mjs 2090 var EMPTY_ACTIVE_FORMATS = []; 2091 var format_boundaries_default = (props) => (element) => { 2092 function onKeyDown(event) { 2093 const { keyCode, shiftKey, altKey, metaKey, ctrlKey } = event; 2094 if ( 2095 // Only override left and right keys without modifiers pressed. 2096 shiftKey || altKey || metaKey || ctrlKey || keyCode !== import_keycodes.LEFT && keyCode !== import_keycodes.RIGHT 2097 ) { 2098 return; 2099 } 2100 const { record, applyRecord, forceRender } = props.current; 2101 const { 2102 text, 2103 formats, 2104 start, 2105 end, 2106 activeFormats: currentActiveFormats = [] 2107 } = record.current; 2108 const collapsed = isCollapsed(record.current); 2109 const { defaultView } = element.ownerDocument; 2110 const { direction } = defaultView.getComputedStyle(element); 2111 const reverseKey = direction === "rtl" ? import_keycodes.RIGHT : import_keycodes.LEFT; 2112 const isReverse = event.keyCode === reverseKey; 2113 if (collapsed && currentActiveFormats.length === 0) { 2114 if (start === 0 && isReverse) { 2115 return; 2116 } 2117 if (end === text.length && !isReverse) { 2118 return; 2119 } 2120 } 2121 if (!collapsed) { 2122 return; 2123 } 2124 const formatsBefore = formats[start - 1] || EMPTY_ACTIVE_FORMATS; 2125 const formatsAfter = formats[start] || EMPTY_ACTIVE_FORMATS; 2126 const destination = isReverse ? formatsBefore : formatsAfter; 2127 const isIncreasing = currentActiveFormats.every( 2128 (format, index) => format === destination[index] 2129 ); 2130 let newActiveFormatsLength = currentActiveFormats.length; 2131 if (!isIncreasing) { 2132 newActiveFormatsLength--; 2133 } else if (newActiveFormatsLength < destination.length) { 2134 newActiveFormatsLength++; 2135 } 2136 if (newActiveFormatsLength === currentActiveFormats.length) { 2137 record.current._newActiveFormats = destination; 2138 return; 2139 } 2140 event.preventDefault(); 2141 const origin = isReverse ? formatsAfter : formatsBefore; 2142 const source = isIncreasing ? destination : origin; 2143 const newActiveFormats = source.slice(0, newActiveFormatsLength); 2144 const newValue = { 2145 ...record.current, 2146 activeFormats: newActiveFormats 2147 }; 2148 record.current = newValue; 2149 applyRecord(newValue); 2150 forceRender(); 2151 } 2152 return subscribeOwnedListener(element, "keydown", onKeyDown, true); 2153 }; 2154 2155 // packages/rich-text/build-module/hook/event-listeners/delete.mjs 2156 var import_keycodes2 = __toESM(require_keycodes(), 1); 2157 var delete_default = (props) => (element) => { 2158 function onKeyDown(event) { 2159 const { keyCode } = event; 2160 if (event.defaultPrevented) { 2161 return; 2162 } 2163 if (keyCode !== import_keycodes2.DELETE && keyCode !== import_keycodes2.BACKSPACE) { 2164 return; 2165 } 2166 const { createRecord, handleChange } = props.current; 2167 const currentValue = createRecord(); 2168 const { start, end, text } = currentValue; 2169 if (start === 0 && end !== 0 && end === text.length) { 2170 handleChange(remove2(currentValue)); 2171 event.preventDefault(); 2172 } 2173 } 2174 return subscribeOwnedListener(element, "keydown", onKeyDown); 2175 }; 2176 2177 // packages/rich-text/build-module/hook/event-listeners/input-and-selection.mjs 2178 var import_compose4 = __toESM(require_compose(), 1); 2179 2180 // packages/rich-text/build-module/update-formats.mjs 2181 function updateFormats({ value, start, end, formats }) { 2182 const min = Math.min(start, end); 2183 const max = Math.max(start, end); 2184 const formatsBefore = value.formats[min - 1] || []; 2185 const formatsAfter = value.formats[max] || []; 2186 value.activeFormats = formats.map((format, index) => { 2187 if (formatsBefore[index]) { 2188 if (isFormatEqual(format, formatsBefore[index])) { 2189 return formatsBefore[index]; 2190 } 2191 } else if (formatsAfter[index]) { 2192 if (isFormatEqual(format, formatsAfter[index])) { 2193 return formatsAfter[index]; 2194 } 2195 } 2196 return format; 2197 }); 2198 while (--end >= start) { 2199 if (value.activeFormats.length > 0) { 2200 value.formats[end] = value.activeFormats; 2201 } else { 2202 delete value.formats[end]; 2203 } 2204 } 2205 return value; 2206 } 2207 2208 // packages/rich-text/build-module/hook/event-listeners/input-and-selection.mjs 2209 var { subscribeDelegatedListener: subscribeDelegatedListener4 } = unlock(import_compose4.privateApis); 2210 var INSERTION_INPUT_TYPES_TO_IGNORE = /* @__PURE__ */ new Set([ 2211 "insertParagraph", 2212 "insertOrderedList", 2213 "insertUnorderedList", 2214 "insertHorizontalRule", 2215 "insertLink" 2216 ]); 2217 var EMPTY_ACTIVE_FORMATS2 = []; 2218 var PLACEHOLDER_ATTR_NAME = "data-rich-text-placeholder"; 2219 function fixPlaceholderSelection(defaultView) { 2220 const selection = defaultView.getSelection(); 2221 const { anchorNode, anchorOffset } = selection; 2222 if (anchorNode.nodeType !== anchorNode.ELEMENT_NODE) { 2223 return; 2224 } 2225 const targetNode = anchorNode.childNodes[anchorOffset]; 2226 if (!targetNode || targetNode.nodeType !== targetNode.ELEMENT_NODE || !targetNode.hasAttribute(PLACEHOLDER_ATTR_NAME)) { 2227 return; 2228 } 2229 selection.collapseToStart(); 2230 } 2231 var input_and_selection_default = (props) => (element) => { 2232 const { ownerDocument } = element; 2233 const { defaultView } = ownerDocument; 2234 let isComposing = false; 2235 function onInput(event) { 2236 if (isComposing) { 2237 return; 2238 } 2239 let inputType; 2240 if (event) { 2241 inputType = event.inputType; 2242 } 2243 const { record, applyRecord, createRecord, handleChange } = props.current; 2244 if (inputType && (inputType.indexOf("format") === 0 || INSERTION_INPUT_TYPES_TO_IGNORE.has(inputType))) { 2245 applyRecord(record.current); 2246 return; 2247 } 2248 const currentValue = createRecord(); 2249 const { start, activeFormats: oldActiveFormats = [] } = record.current; 2250 const clearFormats = !isCollapsed(record.current) && currentValue.start <= start; 2251 const change = updateFormats({ 2252 value: currentValue, 2253 start, 2254 end: currentValue.start, 2255 formats: clearFormats ? [] : oldActiveFormats 2256 }); 2257 handleChange(change); 2258 } 2259 let selectionSnapshot; 2260 function handleSelectionChange() { 2261 const { record, applyRecord, createRecord, onSelectionChange } = props.current; 2262 if (element.contentEditable !== "true") { 2263 return; 2264 } 2265 if (ownerDocument.activeElement !== element && !ownsSelection(element)) { 2266 return; 2267 } 2268 if (isComposing) { 2269 return; 2270 } 2271 const selection = defaultView.getSelection(); 2272 if (selectionSnapshot && selectionSnapshot.anchorNode === selection.anchorNode && selectionSnapshot.anchorOffset === selection.anchorOffset && selectionSnapshot.focusNode === selection.focusNode && selectionSnapshot.focusOffset === selection.focusOffset && selectionSnapshot.processedStart === record.current.start && selectionSnapshot.processedEnd === record.current.end) { 2273 return; 2274 } 2275 const { start, end, text } = createRecord(); 2276 const oldRecord = record.current; 2277 selectionSnapshot = { 2278 anchorNode: selection.anchorNode, 2279 anchorOffset: selection.anchorOffset, 2280 focusNode: selection.focusNode, 2281 focusOffset: selection.focusOffset, 2282 processedStart: start, 2283 processedEnd: end 2284 }; 2285 if (text !== oldRecord.text) { 2286 onInput(); 2287 return; 2288 } 2289 if (start === oldRecord.start && end === oldRecord.end) { 2290 if (oldRecord.text.length === 0 && start === 0) { 2291 fixPlaceholderSelection(defaultView); 2292 } 2293 return; 2294 } 2295 const newValue = { 2296 ...oldRecord, 2297 start, 2298 end, 2299 // _newActiveFormats may be set on arrow key navigation to control 2300 // the right boundary position. If undefined, getActiveFormats will 2301 // give the active formats according to the browser. 2302 activeFormats: oldRecord._newActiveFormats, 2303 _newActiveFormats: void 0 2304 }; 2305 const newActiveFormats = getActiveFormats( 2306 newValue, 2307 EMPTY_ACTIVE_FORMATS2 2308 ); 2309 newValue.activeFormats = newActiveFormats; 2310 record.current = newValue; 2311 applyRecord(newValue, { domOnly: true }); 2312 onSelectionChange(start, end); 2313 } 2314 function onCompositionStart() { 2315 isComposing = true; 2316 element.querySelector(`[$PLACEHOLDER_ATTR_NAME}]`)?.remove(); 2317 } 2318 function onCompositionEnd() { 2319 isComposing = false; 2320 onInput({ inputType: "insertText" }); 2321 } 2322 function onFocus(event) { 2323 if (event.target !== element) { 2324 return; 2325 } 2326 if (element.contentEditable !== "true") { 2327 return; 2328 } 2329 const { record, isSelected, onSelectionChange, applyRecord } = props.current; 2330 if (element.parentElement.closest('[contenteditable="true"]')) { 2331 const selection = defaultView.getSelection(); 2332 if (!selection.anchorNode || !element.contains(selection.anchorNode)) { 2333 if (isSelected && record.current.start !== void 0) { 2334 applyRecord(record.current); 2335 } else { 2336 selection.collapse(element, 0); 2337 } 2338 } 2339 return; 2340 } 2341 if (!isSelected) { 2342 const index = void 0; 2343 record.current = { 2344 ...record.current, 2345 start: index, 2346 end: index, 2347 activeFormats: EMPTY_ACTIVE_FORMATS2 2348 }; 2349 selectionSnapshot = void 0; 2350 } else { 2351 applyRecord(record.current); 2352 } 2353 onSelectionChange(record.current.start, record.current.end); 2354 window.queueMicrotask(handleSelectionChange); 2355 } 2356 const unsubscribeInput = subscribeOwnedListener( 2357 element, 2358 "input", 2359 onInput, 2360 true 2361 ); 2362 const unsubscribeCompositionStart = subscribeOwnedListener( 2363 element, 2364 "compositionstart", 2365 onCompositionStart 2366 ); 2367 const unsubscribeCompositionEnd = subscribeOwnedListener( 2368 element, 2369 "compositionend", 2370 onCompositionEnd, 2371 true 2372 ); 2373 const unsubscribeFocus = subscribeDelegatedListener4( 2374 element, 2375 "focusin", 2376 onFocus 2377 ); 2378 const unsubscribeSelectionChange = subscribeDelegatedListener4( 2379 ownerDocument, 2380 "selectionchange", 2381 handleSelectionChange 2382 ); 2383 const unsubscribeEnsureSelectionSync = [ 2384 "keydown", 2385 "beforeinput", 2386 "copy", 2387 "cut", 2388 "paste" 2389 ].map( 2390 (eventType) => subscribeDelegatedListener4( 2391 ownerDocument, 2392 eventType, 2393 handleSelectionChange, 2394 true 2395 ) 2396 ); 2397 return () => { 2398 unsubscribeInput(); 2399 unsubscribeCompositionStart(); 2400 unsubscribeCompositionEnd(); 2401 unsubscribeFocus(); 2402 unsubscribeSelectionChange(); 2403 unsubscribeEnsureSelectionSync.forEach( 2404 (unsubscribe) => unsubscribe() 2405 ); 2406 }; 2407 }; 2408 2409 // packages/rich-text/build-module/hook/event-listeners/selection-change-compat.mjs 2410 var import_compose5 = __toESM(require_compose(), 1); 2411 var { subscribeDelegatedListener: subscribeDelegatedListener5 } = unlock(import_compose5.privateApis); 2412 var selection_change_compat_default = () => (element) => { 2413 const { ownerDocument } = element; 2414 const { defaultView } = ownerDocument; 2415 const selection = defaultView?.getSelection(); 2416 let range; 2417 function getRange() { 2418 return selection.rangeCount ? selection.getRangeAt(0) : null; 2419 } 2420 function onDown(event) { 2421 const type = event.type === "keydown" ? "keyup" : "pointerup"; 2422 function onCancel() { 2423 ownerDocument.removeEventListener(type, onUp); 2424 ownerDocument.removeEventListener("selectionchange", onCancel); 2425 ownerDocument.removeEventListener("input", onCancel); 2426 } 2427 function onUp() { 2428 onCancel(); 2429 if (isRangeEqual(range, getRange())) { 2430 return; 2431 } 2432 ownerDocument.dispatchEvent(new Event("selectionchange")); 2433 } 2434 ownerDocument.addEventListener(type, onUp); 2435 ownerDocument.addEventListener("selectionchange", onCancel); 2436 ownerDocument.addEventListener("input", onCancel); 2437 range = getRange(); 2438 } 2439 const unsubscribePointerDown = subscribeDelegatedListener5( 2440 element, 2441 "pointerdown", 2442 onDown 2443 ); 2444 const unsubscribeKeyDown = subscribeDelegatedListener5( 2445 element, 2446 "keydown", 2447 onDown 2448 ); 2449 return () => { 2450 unsubscribePointerDown(); 2451 unsubscribeKeyDown(); 2452 }; 2453 }; 2454 2455 // packages/rich-text/build-module/hook/event-listeners/prevent-focus-capture.mjs 2456 var import_compose6 = __toESM(require_compose(), 1); 2457 var { subscribeDelegatedListener: subscribeDelegatedListener6 } = unlock(import_compose6.privateApis); 2458 function preventFocusCapture() { 2459 return (element) => { 2460 const { ownerDocument } = element; 2461 const { defaultView } = ownerDocument; 2462 let value = null; 2463 function onPointerDown(event) { 2464 if (event.defaultPrevented) { 2465 return; 2466 } 2467 if (event.target === element) { 2468 return; 2469 } 2470 if (!event.target.contains(element)) { 2471 return; 2472 } 2473 value = element.getAttribute("contenteditable"); 2474 element.setAttribute("contenteditable", "false"); 2475 defaultView.getSelection().removeAllRanges(); 2476 } 2477 function onPointerUp() { 2478 if (value !== null) { 2479 element.setAttribute("contenteditable", value); 2480 value = null; 2481 } 2482 } 2483 const unsubscribePointerDown = subscribeDelegatedListener6( 2484 defaultView, 2485 "pointerdown", 2486 onPointerDown 2487 ); 2488 const unsubscribePointerUp = subscribeDelegatedListener6( 2489 defaultView, 2490 "pointerup", 2491 onPointerUp 2492 ); 2493 return () => { 2494 unsubscribePointerDown(); 2495 unsubscribePointerUp(); 2496 }; 2497 }; 2498 } 2499 2500 // packages/rich-text/build-module/hook/event-listeners/index.mjs 2501 var allEventListeners = [ 2502 input_and_selection_default, 2503 copy_handler_default, 2504 select_object_default, 2505 format_boundaries_default, 2506 delete_default, 2507 selection_change_compat_default, 2508 preventFocusCapture 2509 ]; 2510 function useEventListeners(props) { 2511 const propsRef = (0, import_element3.useRef)(props); 2512 (0, import_element3.useInsertionEffect)(() => { 2513 propsRef.current = props; 2514 }); 2515 const refEffects = (0, import_element3.useMemo)( 2516 () => allEventListeners.map((refEffect) => refEffect(propsRef)), 2517 [propsRef] 2518 ); 2519 return (0, import_compose7.useRefEffect)( 2520 (element) => { 2521 const cleanups = refEffects.map((effect) => effect(element)); 2522 return () => { 2523 cleanups.forEach((cleanup) => cleanup()); 2524 }; 2525 }, 2526 [refEffects] 2527 ); 2528 } 2529 2530 // packages/rich-text/build-module/hook/use-format-types.mjs 2531 var import_element4 = __toESM(require_element(), 1); 2532 var import_data8 = __toESM(require_data(), 1); 2533 function formatTypesSelector(select5) { 2534 return select5(store).getFormatTypes(); 2535 } 2536 var interactiveContentTags = /* @__PURE__ */ new Set([ 2537 "a", 2538 "audio", 2539 "button", 2540 "details", 2541 "embed", 2542 "iframe", 2543 "input", 2544 "label", 2545 "select", 2546 "textarea", 2547 "video" 2548 ]); 2549 function prefixSelectKeys(selected, prefix) { 2550 if (typeof selected !== "object") { 2551 return { [prefix]: selected }; 2552 } 2553 return Object.fromEntries( 2554 Object.entries(selected).map(([key, value]) => [ 2555 `$prefix}.$key}`, 2556 value 2557 ]) 2558 ); 2559 } 2560 function getPrefixedSelectKeys(selected, prefix) { 2561 if (selected[prefix]) { 2562 return selected[prefix]; 2563 } 2564 return Object.keys(selected).filter((key) => key.startsWith(prefix + ".")).reduce((accumulator, key) => { 2565 accumulator[key.slice(prefix.length + 1)] = selected[key]; 2566 return accumulator; 2567 }, {}); 2568 } 2569 function useFormatTypes({ 2570 allowedFormats, 2571 withoutInteractiveFormatting, 2572 __unstableFormatTypeHandlerContext 2573 }) { 2574 const allFormatTypes = (0, import_data8.useSelect)(formatTypesSelector, []); 2575 const formatTypes2 = (0, import_element4.useMemo)(() => { 2576 return allFormatTypes.filter(({ name, interactive, tagName }) => { 2577 if (allowedFormats && !allowedFormats.includes(name)) { 2578 return false; 2579 } 2580 if (withoutInteractiveFormatting && (interactive || interactiveContentTags.has(tagName))) { 2581 return false; 2582 } 2583 return true; 2584 }); 2585 }, [allFormatTypes, allowedFormats, withoutInteractiveFormatting]); 2586 const keyedSelected = (0, import_data8.useSelect)( 2587 (select5) => formatTypes2.reduce((accumulator, type) => { 2588 if (!type.__experimentalGetPropsForEditableTreePreparation || !__unstableFormatTypeHandlerContext) { 2589 return accumulator; 2590 } 2591 return { 2592 ...accumulator, 2593 ...prefixSelectKeys( 2594 type.__experimentalGetPropsForEditableTreePreparation( 2595 select5, 2596 __unstableFormatTypeHandlerContext 2597 ), 2598 type.name 2599 ) 2600 }; 2601 }, {}), 2602 [formatTypes2, __unstableFormatTypeHandlerContext] 2603 ); 2604 const dispatch3 = (0, import_data8.useDispatch)(); 2605 const prepareHandlers = []; 2606 const valueHandlers = []; 2607 const changeHandlers = []; 2608 const dependencies = []; 2609 for (const key in keyedSelected) { 2610 dependencies.push(keyedSelected[key]); 2611 } 2612 formatTypes2.forEach((type) => { 2613 if (type.__experimentalCreatePrepareEditableTree && __unstableFormatTypeHandlerContext) { 2614 const handler = type.__experimentalCreatePrepareEditableTree( 2615 getPrefixedSelectKeys(keyedSelected, type.name), 2616 __unstableFormatTypeHandlerContext 2617 ); 2618 if (type.__experimentalCreateOnChangeEditableValue) { 2619 valueHandlers.push(handler); 2620 } else { 2621 prepareHandlers.push(handler); 2622 } 2623 } 2624 if (type.__experimentalCreateOnChangeEditableValue && __unstableFormatTypeHandlerContext) { 2625 let dispatchers = {}; 2626 if (type.__experimentalGetPropsForEditableTreeChangeHandler) { 2627 dispatchers = type.__experimentalGetPropsForEditableTreeChangeHandler( 2628 dispatch3, 2629 __unstableFormatTypeHandlerContext 2630 ); 2631 } 2632 const selected = getPrefixedSelectKeys(keyedSelected, type.name); 2633 changeHandlers.push( 2634 type.__experimentalCreateOnChangeEditableValue( 2635 { 2636 ...typeof selected === "object" ? selected : {}, 2637 ...dispatchers 2638 }, 2639 __unstableFormatTypeHandlerContext 2640 ) 2641 ); 2642 } 2643 }); 2644 return { 2645 formatTypes: formatTypes2, 2646 prepareHandlers, 2647 valueHandlers, 2648 changeHandlers, 2649 dependencies 2650 }; 2651 } 2652 2653 // packages/rich-text/build-module/hook/index.mjs 2654 function useRichTextBase({ 2655 value = "", 2656 selectionStart, 2657 selectionEnd, 2658 placeholder, 2659 onSelectionChange, 2660 preserveWhiteSpace, 2661 onChange, 2662 __unstableDisableFormats: disableFormats, 2663 __unstableIsSelected: isSelected, 2664 __unstableDependencies = [], 2665 __unstableAfterParse, 2666 __unstableBeforeSerialize, 2667 __unstableAddInvisibleFormats 2668 }) { 2669 const registry = (0, import_data9.useRegistry)(); 2670 const [, forceRender] = (0, import_element5.useReducer)(() => ({})); 2671 const ref = (0, import_element5.useRef)(); 2672 function createRecord() { 2673 const { 2674 ownerDocument: { defaultView } 2675 } = ref.current; 2676 const selection = defaultView.getSelection(); 2677 const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null; 2678 return create({ 2679 element: ref.current, 2680 range, 2681 __unstableIsEditableTree: true 2682 }); 2683 } 2684 function applyRecord(newRecord, { domOnly } = {}) { 2685 apply({ 2686 value: newRecord, 2687 current: ref.current, 2688 prepareEditableTree: __unstableAddInvisibleFormats, 2689 __unstableDomOnly: domOnly, 2690 placeholder 2691 }); 2692 } 2693 const _valueRef = (0, import_element5.useRef)(value); 2694 const recordRef = (0, import_element5.useRef)(); 2695 function setRecordFromProps() { 2696 const activeFormats = recordRef.current?.activeFormats; 2697 _valueRef.current = value; 2698 recordRef.current = value; 2699 if (!(value instanceof RichTextData)) { 2700 recordRef.current = value ? RichTextData.fromHTMLString(value, { preserveWhiteSpace }) : RichTextData.empty(); 2701 } 2702 recordRef.current = { 2703 text: recordRef.current.text, 2704 formats: recordRef.current.formats, 2705 replacements: recordRef.current.replacements, 2706 activeFormats 2707 }; 2708 if (disableFormats) { 2709 recordRef.current.formats = Array(value.length); 2710 recordRef.current.replacements = Array(value.length); 2711 } 2712 if (__unstableAfterParse) { 2713 recordRef.current.formats = __unstableAfterParse( 2714 recordRef.current 2715 ); 2716 } 2717 recordRef.current.start = selectionStart; 2718 recordRef.current.end = selectionEnd; 2719 } 2720 const hadSelectionUpdateRef = (0, import_element5.useRef)(false); 2721 if (!recordRef.current) { 2722 hadSelectionUpdateRef.current = isSelected; 2723 setRecordFromProps(); 2724 } else if (selectionStart !== recordRef.current.start || selectionEnd !== recordRef.current.end) { 2725 hadSelectionUpdateRef.current = isSelected; 2726 recordRef.current = { 2727 ...recordRef.current, 2728 start: selectionStart, 2729 end: selectionEnd, 2730 activeFormats: void 0 2731 }; 2732 } 2733 function handleChange(newRecord) { 2734 recordRef.current = newRecord; 2735 applyRecord(newRecord); 2736 if (disableFormats) { 2737 _valueRef.current = newRecord.text; 2738 } else { 2739 const newFormats = __unstableBeforeSerialize ? __unstableBeforeSerialize(newRecord) : newRecord.formats; 2740 newRecord = { ...newRecord, formats: newFormats }; 2741 if (typeof value === "string") { 2742 _valueRef.current = toHTMLString({ 2743 value: newRecord, 2744 preserveWhiteSpace 2745 }); 2746 } else { 2747 _valueRef.current = new RichTextData(newRecord); 2748 } 2749 } 2750 const { start, end, formats, text } = recordRef.current; 2751 registry.batch(() => { 2752 onSelectionChange(start, end); 2753 onChange(_valueRef.current, { 2754 __unstableFormats: formats, 2755 __unstableText: text 2756 }); 2757 }); 2758 forceRender(); 2759 } 2760 function applyFromProps() { 2761 const previousValue = _valueRef.current; 2762 setRecordFromProps(); 2763 const contentLengthChanged = previousValue && typeof previousValue === "string" && typeof value === "string" && previousValue.length !== value.length; 2764 const hasFocus = ref.current?.contains(ref.current.ownerDocument.activeElement) || ownsSelection(ref.current); 2765 const skipSelection = contentLengthChanged && !hasFocus; 2766 applyRecord(recordRef.current, { domOnly: skipSelection }); 2767 } 2768 const didMountRef = (0, import_element5.useRef)(false); 2769 (0, import_element5.useLayoutEffect)(() => { 2770 if (didMountRef.current && value !== _valueRef.current) { 2771 applyFromProps(); 2772 forceRender(); 2773 } 2774 }, [value]); 2775 (0, import_element5.useLayoutEffect)(() => { 2776 if (!hadSelectionUpdateRef.current) { 2777 return; 2778 } 2779 if (ref.current.ownerDocument.activeElement !== ref.current && !ownsSelection(ref.current)) { 2780 ref.current.focus(); 2781 } 2782 applyRecord(recordRef.current); 2783 hadSelectionUpdateRef.current = false; 2784 }, [hadSelectionUpdateRef.current]); 2785 const mergedRefs = (0, import_compose8.useMergeRefs)([ 2786 ref, 2787 useDefaultStyle(), 2788 useBoundaryStyle({ record: recordRef }), 2789 useEventListeners({ 2790 record: recordRef, 2791 handleChange, 2792 applyRecord, 2793 createRecord, 2794 isSelected, 2795 onSelectionChange, 2796 forceRender 2797 }), 2798 (0, import_compose8.useRefEffect)(() => { 2799 applyFromProps(); 2800 didMountRef.current = true; 2801 }, [placeholder, ...__unstableDependencies]) 2802 ]); 2803 return { 2804 value: recordRef.current, 2805 // A function to get the most recent value so event handlers in 2806 // useRichText implementations have access to it. For example when 2807 // listening to input events, we internally update the state, but this 2808 // state is not yet available to the input event handler because React 2809 // may re-render asynchronously. 2810 getValue: () => recordRef.current, 2811 onChange: handleChange, 2812 ref: mergedRefs 2813 }; 2814 } 2815 function useRichText({ 2816 allowedFormats, 2817 withoutInteractiveFormatting, 2818 onChange, 2819 __unstableDependencies = [], 2820 __unstableFormatTypeHandlerContext, 2821 ...props 2822 }) { 2823 const { 2824 formatTypes: formatTypes2, 2825 prepareHandlers, 2826 valueHandlers, 2827 changeHandlers, 2828 dependencies 2829 } = useFormatTypes({ 2830 allowedFormats, 2831 withoutInteractiveFormatting, 2832 __unstableFormatTypeHandlerContext 2833 }); 2834 function addEditorOnlyFormats(record) { 2835 return valueHandlers.reduce( 2836 (accumulator, fn) => fn(accumulator, record.text), 2837 record.formats 2838 ); 2839 } 2840 function removeEditorOnlyFormats(record) { 2841 formatTypes2.forEach((formatType) => { 2842 if (formatType.__experimentalCreatePrepareEditableTree) { 2843 record = removeFormat( 2844 record, 2845 formatType.name, 2846 0, 2847 record.text.length 2848 ); 2849 } 2850 }); 2851 return record.formats; 2852 } 2853 function addInvisibleFormats(record) { 2854 return prepareHandlers.reduce( 2855 (accumulator, fn) => fn(accumulator, record.text), 2856 record.formats 2857 ); 2858 } 2859 const result = useRichTextBase({ 2860 ...props, 2861 onChange(value, { __unstableFormats, __unstableText }) { 2862 onChange(value, { __unstableFormats, __unstableText }); 2863 Object.values(changeHandlers).forEach((changeHandler) => { 2864 changeHandler(__unstableFormats, __unstableText); 2865 }); 2866 }, 2867 __unstableDependencies: [...dependencies, ...__unstableDependencies], 2868 __unstableAfterParse: addEditorOnlyFormats, 2869 __unstableBeforeSerialize: removeEditorOnlyFormats, 2870 __unstableAddInvisibleFormats: addInvisibleFormats 2871 }); 2872 return { ...result, formatTypes: formatTypes2 }; 2873 } 2874 function useDeprecatedRichText(props) { 2875 (0, import_deprecated.default)("`__unstableUseRichText` hook", { 2876 since: "7.0" 2877 }); 2878 return useRichTextBase(props); 2879 } 2880 2881 // packages/rich-text/build-module/contexts.mjs 2882 var import_element6 = __toESM(require_element(), 1); 2883 var KeyboardShortcutContext = (0, import_element6.createContext)(); 2884 KeyboardShortcutContext.displayName = "KeyboardShortcutContext"; 2885 var InputEventContext = (0, import_element6.createContext)(); 2886 InputEventContext.displayName = "InputEventContext"; 2887 2888 // packages/rich-text/build-module/keyboard-shortcut.mjs 2889 var import_keycodes3 = __toESM(require_keycodes(), 1); 2890 var import_element7 = __toESM(require_element(), 1); 2891 var import_compose9 = __toESM(require_compose(), 1); 2892 function RichTextShortcut({ character, type, onUse }) { 2893 const keyboardShortcuts = (0, import_element7.useContext)(KeyboardShortcutContext); 2894 const stableOnUse = (0, import_compose9.useEvent)(onUse); 2895 (0, import_element7.useEffect)(() => { 2896 const shortcuts = keyboardShortcuts.current; 2897 function callback(event) { 2898 if (import_keycodes3.isKeyboardEvent[type](event, character)) { 2899 stableOnUse(); 2900 event.preventDefault(); 2901 } 2902 } 2903 shortcuts.add(callback); 2904 return () => { 2905 shortcuts.delete(callback); 2906 }; 2907 }, [character, type, keyboardShortcuts, stableOnUse]); 2908 return null; 2909 } 2910 2911 // packages/rich-text/build-module/input-event.mjs 2912 var import_element8 = __toESM(require_element(), 1); 2913 var import_compose10 = __toESM(require_compose(), 1); 2914 function RichTextInputEvent({ inputType, onInput }) { 2915 const callbacks = (0, import_element8.useContext)(InputEventContext); 2916 const stableOnInput = (0, import_compose10.useEvent)(onInput); 2917 (0, import_element8.useEffect)(() => { 2918 const inputCallbacks = callbacks.current; 2919 function callback(event) { 2920 if (event.inputType === inputType) { 2921 stableOnInput(); 2922 event.preventDefault(); 2923 } 2924 } 2925 inputCallbacks.add(callback); 2926 return () => { 2927 inputCallbacks.delete(callback); 2928 }; 2929 }, [inputType, callbacks, stableOnInput]); 2930 return null; 2931 } 2932 2933 // packages/rich-text/build-module/event-listeners.mjs 2934 var shortcutsListener = (props) => (element) => { 2935 const { keyboardShortcuts } = props.current; 2936 function onKeyDown(event) { 2937 for (const keyboardShortcut of keyboardShortcuts.current) { 2938 keyboardShortcut(event); 2939 } 2940 } 2941 element.addEventListener("keydown", onKeyDown); 2942 return () => { 2943 element.removeEventListener("keydown", onKeyDown); 2944 }; 2945 }; 2946 var inputEventsListener = (props) => (element) => { 2947 const { inputEvents } = props.current; 2948 function onInput(event) { 2949 for (const inputEventHandler of inputEvents.current) { 2950 inputEventHandler(event); 2951 } 2952 } 2953 element.addEventListener("input", onInput); 2954 return () => { 2955 element.removeEventListener("input", onInput); 2956 }; 2957 }; 2958 2959 // packages/rich-text/build-module/private-apis.mjs 2960 var privateApis = {}; 2961 lock(privateApis, { 2962 useRichText, 2963 KeyboardShortcutContext, 2964 InputEventContext, 2965 RichTextShortcut, 2966 RichTextInputEvent, 2967 shortcutsListener, 2968 inputEventsListener, 2969 ownsSelection, 2970 subscribeOwnedListener 2971 }); 2972 2973 // packages/rich-text/build-module/hook/use-anchor-ref.mjs 2974 var import_element9 = __toESM(require_element(), 1); 2975 var import_deprecated2 = __toESM(require_deprecated(), 1); 2976 function useAnchorRef({ ref, value, settings = {} }) { 2977 (0, import_deprecated2.default)("`useAnchorRef` hook", { 2978 since: "6.1", 2979 alternative: "`useAnchor` hook" 2980 }); 2981 const { tagName, className, name } = settings; 2982 const activeFormat = name ? getActiveFormat(value, name) : void 0; 2983 return (0, import_element9.useMemo)(() => { 2984 if (!ref.current) { 2985 return; 2986 } 2987 const { 2988 ownerDocument: { defaultView } 2989 } = ref.current; 2990 const selection = defaultView.getSelection(); 2991 if (!selection.rangeCount) { 2992 return; 2993 } 2994 const range = selection.getRangeAt(0); 2995 if (!activeFormat) { 2996 return range; 2997 } 2998 let element = range.startContainer; 2999 element = element.nextElementSibling || element; 3000 while (element.nodeType !== element.ELEMENT_NODE) { 3001 element = element.parentNode; 3002 } 3003 return element.closest( 3004 tagName + (className ? "." + className : "") 3005 ); 3006 }, [activeFormat, value.start, value.end, tagName, className]); 3007 } 3008 3009 // packages/rich-text/build-module/hook/use-anchor.mjs 3010 var import_compose11 = __toESM(require_compose(), 1); 3011 var import_element10 = __toESM(require_element(), 1); 3012 var import_dom = __toESM(require_dom(), 1); 3013 function getFormatElement(range, editableContentElement, tagName, className) { 3014 let element = range.startContainer; 3015 if (element.nodeType === element.TEXT_NODE && element instanceof window.Text && range.startOffset === element.length && element.nextSibling) { 3016 element = element.nextSibling; 3017 while (element.firstChild) { 3018 element = element.firstChild; 3019 } 3020 } 3021 if (element.nodeType !== element.ELEMENT_NODE) { 3022 if (!element.parentElement) { 3023 return; 3024 } 3025 element = element.parentElement; 3026 } 3027 if (element === editableContentElement) { 3028 return; 3029 } 3030 if (!editableContentElement.contains(element)) { 3031 return; 3032 } 3033 const selector = tagName + (className ? "." + className : ""); 3034 if (!selector) { 3035 return; 3036 } 3037 if (!(element instanceof window.HTMLElement)) { 3038 return; 3039 } 3040 let closestElement = element; 3041 while (closestElement && closestElement !== editableContentElement) { 3042 if (closestElement.matches(selector)) { 3043 return closestElement; 3044 } 3045 closestElement = closestElement.parentElement; 3046 } 3047 return void 0; 3048 } 3049 function createVirtualAnchorElement(range, editableContentElement) { 3050 return { 3051 contextElement: editableContentElement, 3052 getBoundingClientRect() { 3053 if (editableContentElement.contains(range.startContainer)) { 3054 return (0, import_dom.getRectangleFromRange)(range) ?? range.getBoundingClientRect(); 3055 } 3056 return editableContentElement.getBoundingClientRect(); 3057 } 3058 }; 3059 } 3060 function getAnchor(editableContentElement, tagName, className) { 3061 if (!editableContentElement) { 3062 return; 3063 } 3064 const { ownerDocument } = editableContentElement; 3065 const { defaultView } = ownerDocument; 3066 const selection = defaultView?.getSelection(); 3067 if (!selection) { 3068 return; 3069 } 3070 if (!selection.rangeCount) { 3071 return; 3072 } 3073 const range = selection.getRangeAt(0); 3074 if (!range || !range.startContainer) { 3075 return; 3076 } 3077 if (!tagName && !className) { 3078 return createVirtualAnchorElement(range, editableContentElement); 3079 } 3080 return getFormatElement(range, editableContentElement, tagName, className) ?? createVirtualAnchorElement(range, editableContentElement); 3081 } 3082 var DEFAULT_SETTINGS = { 3083 tagName: "", 3084 className: "" 3085 }; 3086 function useAnchor({ 3087 editableContentElement, 3088 settings 3089 }) { 3090 const { tagName, className } = settings ?? DEFAULT_SETTINGS; 3091 const isActive = !!(settings && "isActive" in settings && settings.isActive); 3092 const [anchor, setAnchor] = (0, import_element10.useState)( 3093 () => getAnchor(editableContentElement, tagName, className ?? "") 3094 ); 3095 const wasActive = (0, import_compose11.usePrevious)(isActive); 3096 (0, import_element10.useLayoutEffect)(() => { 3097 if (!editableContentElement) { 3098 return; 3099 } 3100 function callback() { 3101 setAnchor( 3102 getAnchor(editableContentElement, tagName, className ?? "") 3103 ); 3104 } 3105 function attach() { 3106 ownerDocument.addEventListener("selectionchange", callback); 3107 } 3108 function detach() { 3109 ownerDocument.removeEventListener("selectionchange", callback); 3110 } 3111 const { ownerDocument } = editableContentElement; 3112 if (ownsSelection(editableContentElement) || // When a link is created, we need to attach the popover to the newly created anchor. 3113 !wasActive && isActive || // Sometimes we're _removing_ an active anchor, such as the inline color popover. 3114 // When we add the color, it switches from a virtual anchor to a `<mark>` element. 3115 // When we _remove_ the color, it switches from a `<mark>` element to a virtual anchor. 3116 wasActive && !isActive) { 3117 setAnchor( 3118 getAnchor(editableContentElement, tagName, className ?? "") 3119 ); 3120 attach(); 3121 } 3122 editableContentElement.addEventListener("focusin", attach); 3123 editableContentElement.addEventListener("focusout", detach); 3124 return () => { 3125 detach(); 3126 editableContentElement.removeEventListener("focusin", attach); 3127 editableContentElement.removeEventListener("focusout", detach); 3128 }; 3129 }, [editableContentElement, tagName, className, isActive, wasActive]); 3130 return anchor; 3131 } 3132 3133 // packages/rich-text/build-module/index.mjs 3134 function __experimentalRichText() { 3135 } 3136 return __toCommonJS(index_exports); 3137 })(); 3138 (window.wp ||= {}).richText = wp.richText; 3139 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Fri Aug 14 08:20:23 2026 | Cross-referenced by PHPXref |