: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
__unstableIsFullySelected,
getSelectedBlockClientIds,
getSelectedBlockClientId,
__unstableIsSelectionMergeable,
} = (0,external_wp_data_namespaceObject.useSelect)(store);
__unstableSplitSelection,
__unstableDeleteSelection,
__unstableExpandSelection,
__unstableMarkAutomaticChange
} = (0,external_wp_data_namespaceObject.useDispatch)(store);
return (0,external_wp_compose_namespaceObject.useRefEffect)(node => {
function onBeforeInput(event) {
// If writing flow is editable, NEVER allow the browser to alter the
// DOM. This will cause React errors (and the DOM should only be
// altered in a controlled fashion).
if (node.contentEditable === 'true') {
function onKeyDown(event) {
if (event.defaultPrevented) {
if (!hasMultiSelection()) {
if (event.keyCode === external_wp_keycodes_namespaceObject.ENTER) {
if (event.shiftKey || __unstableIsFullySelected()) {
const clientId = getSelectedBlockClientId();
const blockName = getBlockName(clientId);
const selectionStart = getSelectionStart();
const selectionEnd = getSelectionEnd();
if (selectionStart.attributeKey === selectionEnd.attributeKey) {
const selectedAttributeValue = getBlockAttributes(clientId)[selectionStart.attributeKey];
const transforms = (0,external_wp_blocks_namespaceObject.getBlockTransforms)('from').filter(({
const transformation = (0,external_wp_blocks_namespaceObject.findTransform)(transforms, item => {
return item.regExp.test(selectedAttributeValue);
replaceBlocks(clientId, transformation.transform({
content: selectedAttributeValue
__unstableMarkAutomaticChange();
if (!(0,external_wp_blocks_namespaceObject.hasBlockSupport)(blockName, 'splitting', false) && !event.__deprecatedOnSplit) {
// Ensure template is not locked.
if (canInsertBlockType(blockName, getBlockRootClientId(clientId))) {
__unstableSplitSelection();
if (event.keyCode === external_wp_keycodes_namespaceObject.ENTER) {
node.contentEditable = false;
if (__unstableIsFullySelected()) {
replaceBlocks(getSelectedBlockClientIds(), (0,external_wp_blocks_namespaceObject.createBlock)((0,external_wp_blocks_namespaceObject.getDefaultBlockName)()));
__unstableSplitSelection();
} else if (event.keyCode === external_wp_keycodes_namespaceObject.BACKSPACE || event.keyCode === external_wp_keycodes_namespaceObject.DELETE) {
node.contentEditable = false;
if (__unstableIsFullySelected()) {
removeBlocks(getSelectedBlockClientIds());
} else if (__unstableIsSelectionMergeable()) {
__unstableDeleteSelection(event.keyCode === external_wp_keycodes_namespaceObject.DELETE);
__unstableExpandSelection();
// If key.length is longer than 1, it's a control key that doesn't
event.key.length === 1 && !(event.metaKey || event.ctrlKey)) {
node.contentEditable = false;
if (__unstableIsSelectionMergeable()) {
__unstableDeleteSelection(event.keyCode === external_wp_keycodes_namespaceObject.DELETE);
// Safari does not stop default behaviour with either
// event.preventDefault() or node.contentEditable = false, so
// remove the selection to stop browser manipulation.
node.ownerDocument.defaultView.getSelection().removeAllRanges();
function onCompositionStart(event) {
if (!hasMultiSelection()) {
node.contentEditable = false;
if (__unstableIsSelectionMergeable()) {
__unstableDeleteSelection();
// Safari does not stop default behaviour with either
// event.preventDefault() or node.contentEditable = false, so
// remove the selection to stop browser manipulation.
node.ownerDocument.defaultView.getSelection().removeAllRanges();
node.addEventListener('beforeinput', onBeforeInput);
node.addEventListener('keydown', onKeyDown);
node.addEventListener('compositionstart', onCompositionStart);
node.removeEventListener('beforeinput', onBeforeInput);
node.removeEventListener('keydown', onKeyDown);
node.removeEventListener('compositionstart', onCompositionStart);
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/use-notify-copy.js
function useNotifyCopy() {
} = (0,external_wp_data_namespaceObject.useSelect)(store);
} = (0,external_wp_data_namespaceObject.useSelect)(external_wp_blocks_namespaceObject.store);
} = (0,external_wp_data_namespaceObject.useDispatch)(external_wp_notices_namespaceObject.store);
return (0,external_wp_element_namespaceObject.useCallback)((eventType, selectedBlockClientIds) => {
if (selectedBlockClientIds.length === 1) {
const clientId = selectedBlockClientIds[0];
const title = getBlockType(getBlockName(clientId))?.title;
notice = eventType === 'copy' ? (0,external_wp_i18n_namespaceObject.sprintf)(
// Translators: Name of the block being copied, e.g. "Paragraph".
(0,external_wp_i18n_namespaceObject.__)('Copied "%s" to clipboard.'), title) : (0,external_wp_i18n_namespaceObject.sprintf)(
// Translators: Name of the block being cut, e.g. "Paragraph".
(0,external_wp_i18n_namespaceObject.__)('Moved "%s" to clipboard.'), title);
notice = eventType === 'copy' ? (0,external_wp_i18n_namespaceObject.sprintf)(
// Translators: %d: Number of blocks being copied.
(0,external_wp_i18n_namespaceObject._n)('Copied %d block to clipboard.', 'Copied %d blocks to clipboard.', selectedBlockClientIds.length), selectedBlockClientIds.length) : (0,external_wp_i18n_namespaceObject.sprintf)(
// Translators: %d: Number of blocks being cut.
(0,external_wp_i18n_namespaceObject._n)('Moved %d block to clipboard.', 'Moved %d blocks to clipboard.', selectedBlockClientIds.length), selectedBlockClientIds.length);
createSuccessNotice(notice, {
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/utils/pasting.js
* Normalizes a given string of HTML to remove the Windows-specific "Fragment"
* comments and any preceding and trailing content.
* @param {string} html the html to be normalized
* @return {string} the normalized html
function removeWindowsFragments(html) {
const startStr = '<!--StartFragment-->';
const startIdx = html.indexOf(startStr);
html = html.substring(startIdx + startStr.length);
// No point looking for EndFragment
const endStr = '<!--EndFragment-->';
const endIdx = html.indexOf(endStr);
html = html.substring(0, endIdx);
* Removes the charset meta tag inserted by Chromium.
* - https://github.com/WordPress/gutenberg/issues/33585
* - https://bugs.chromium.org/p/chromium/issues/detail?id=1264616#c4
* @param {string} html the html to be stripped of the meta tag.
* @return {string} the cleaned html
function removeCharsetMetaTag(html) {
const metaTag = `<meta charset='utf-8'>`;
if (html.startsWith(metaTag)) {
return html.slice(metaTag.length);
function getPasteEventData({
// IE11 only supports `Text` as an argument for `getData` and will
// otherwise throw an invalid argument error, so we try the standard
// arguments first, then fallback to `Text` if they fail.
plainText = clipboardData.getData('text/plain');
html = clipboardData.getData('text/html');
html = clipboardData.getData('Text');
// Some browsers like UC Browser paste plain text by default and
// don't support clipboardData at all, so allow default
// Remove Windows-specific metadata appended within copied HTML text.
html = removeWindowsFragments(html);
html = removeCharsetMetaTag(html);
const files = (0,external_wp_dom_namespaceObject.getFilesFromDataTransfer)(clipboardData);
if (files.length && !shouldDismissPastedFiles(files, html)) {
* Given a collection of DataTransfer files and HTML and plain text strings,
* determine whether the files are to be dismissed in favor of the HTML.
* Certain office-type programs, like Microsoft Word or Apple Numbers,
* will, upon copy, generate a screenshot of the content being copied and
* attach it to the clipboard alongside the actual rich text that the user
* sought to copy. In those cases, we should let Gutenberg handle the rich text
* content and not the screenshot, since this allows Gutenberg to insert
* meaningful blocks, like paragraphs, lists or even tables.
* @param {File[]} files File objects obtained from a paste event
* @param {string} html HTML content obtained from a paste event
* @return {boolean} True if the files should be dismissed
function shouldDismissPastedFiles(files, html /*, plainText */) {
// The question is only relevant when there is actual HTML content and when
// there is exactly one image file.
if (html && files?.length === 1 && files[0].type.indexOf('image/') === 0) {
// A single <img> tag found in the HTML source suggests that the
// content being pasted revolves around an image. Sometimes there are
// other elements found, like <figure>, but we assume that the user's
// intention is to paste the actual image file.
const IMAGE_TAG = /<\s*img\b/gi;
if (html.match(IMAGE_TAG)?.length !== 1) {
// Even when there is exactly one <img> tag in the HTML payload, we
// choose to weed out local images, i.e. those whose source starts with
// "file://". These payloads occur in specific configurations, such as
// when copying an entire document from Microsoft Word, that contains
// text and exactly one image, and pasting that content using Google
const IMG_WITH_LOCAL_SRC = /<\s*img\b[^>]*\bsrc="file:\/\//i;
if (html.match(IMG_WITH_LOCAL_SRC)) {
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/writing-flow/utils.js
const requiresWrapperOnCopy = Symbol('requiresWrapperOnCopy');
* Sets the clipboard data for the provided blocks, with both HTML and plain
* @param {ClipboardEvent} event Clipboard event.
* @param {WPBlock[]} blocks Blocks to set as clipboard data.
* @param {Object} registry The registry to select from.
function setClipboardBlocks(event, blocks, registry) {
const [firstBlock] = blocks;
const firstBlockType = registry.select(external_wp_blocks_namespaceObject.store).getBlockType(firstBlock.name);
if (firstBlockType[requiresWrapperOnCopy]) {
} = registry.select(store);
const wrapperBlockClientId = getBlockRootClientId(firstBlock.clientId);
const wrapperBlockName = getBlockName(wrapperBlockClientId);
_blocks = (0,external_wp_blocks_namespaceObject.createBlock)(wrapperBlockName, getBlockAttributes(wrapperBlockClientId), _blocks);
const serialized = (0,external_wp_blocks_namespaceObject.serialize)(_blocks);
event.clipboardData.setData('text/plain', toPlainText(serialized));
event.clipboardData.setData('text/html', serialized);
* Returns the blocks to be pasted from the clipboard event.
* @param {ClipboardEvent} event The clipboard event.
* @param {boolean} canUserUseUnfilteredHTML Whether the user can or can't post unfiltered HTML.
* @return {Array|string} A list of blocks or a string, depending on `handlerMode`.
function getPasteBlocks(event, canUserUseUnfilteredHTML) {
} = getPasteEventData(event);
const fromTransforms = (0,external_wp_blocks_namespaceObject.getBlockTransforms)('from');
blocks = files.reduce((accumulator, file) => {
const transformation = (0,external_wp_blocks_namespaceObject.findTransform)(fromTransforms, transform => transform.type === 'files' && transform.isMatch([file]));
accumulator.push(transformation.transform([file]));
blocks = (0,external_wp_blocks_namespaceObject.pasteHandler)({
* Given a string of HTML representing serialized blocks, returns the plain
* text extracted after stripping the HTML of any tags and fixing line breaks.
* @param {string} html Serialized blocks.
* @return {string} The plain-text content with any html removed.
function toPlainText(html) {
// Manually handle BR tags as line breaks prior to `stripHTML` call
html = html.replace(/<br>/g, '\n');
const plainText = (0,external_wp_dom_namespaceObject.__unstableStripHTML)(html).trim();
// Merge any consecutive line breaks
return plainText.replace(/\n\n+/g, '\n\n');
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/writing-flow/use-clipboard-handler.js
function useClipboardHandler() {
const registry = (0,external_wp_data_namespaceObject.useRegistry)();
getSelectedBlockClientIds,
__unstableIsFullySelected,
__unstableIsSelectionCollapsed,
__unstableIsSelectionMergeable,
__unstableGetSelectedBlocksWithPartialSelection,
} = (0,external_wp_data_namespaceObject.useSelect)(store);
__unstableDeleteSelection,
__unstableExpandSelection,
} = (0,external_wp_data_namespaceObject.useDispatch)(store);
const notifyCopy = useNotifyCopy();
return (0,external_wp_compose_namespaceObject.useRefEffect)(node => {
function handler(event) {
if (event.defaultPrevented) {
// This was likely already handled in rich-text/use-paste-handler.js.
const selectedBlockClientIds = getSelectedBlockClientIds();
if (selectedBlockClientIds.length === 0) {
// Let native copy/paste behaviour take over in input fields.
// But always handle multiple selected blocks.
if (!hasMultiSelection()) {
// If copying, only consider actual text selection as selection.
// Otherwise, any focus on an input field is considered.
const hasSelection = event.type === 'copy' || event.type === 'cut' ? (0,external_wp_dom_namespaceObject.documentHasUncollapsedSelection)(ownerDocument) : (0,external_wp_dom_namespaceObject.documentHasSelection)(ownerDocument) && !ownerDocument.activeElement.isContentEditable;
// Let native copy behaviour take over in input fields.
} = event.target.ownerDocument;
if (!node.contains(activeElement)) {
const isSelectionMergeable = __unstableIsSelectionMergeable();
const shouldHandleWholeBlocks = __unstableIsSelectionCollapsed() || __unstableIsFullySelected();
const expandSelectionIsNeeded = !shouldHandleWholeBlocks && !isSelectionMergeable;
if (event.type === 'copy' || event.type === 'cut') {
if (selectedBlockClientIds.length === 1) {
flashBlock(selectedBlockClientIds[0]);
// If we have a partial selection that is not mergeable, just
// expand the selection to the whole blocks.
if (expandSelectionIsNeeded) {
__unstableExpandSelection();
notifyCopy(event.type, selectedBlockClientIds);
// Check if we have partial selection.
if (shouldHandleWholeBlocks) {
blocks = getBlocksByClientId(selectedBlockClientIds);
const [head, tail] = __unstableGetSelectedBlocksWithPartialSelection();
const inBetweenBlocks = getBlocksByClientId(selectedBlockClientIds.slice(1, selectedBlockClientIds.length - 1));
blocks = [head, ...inBetweenBlocks, tail];