Skip to content

Commit

Permalink
implement mutation observer
Browse files Browse the repository at this point in the history
  • Loading branch information
Sidsector9 committed Jun 22, 2024
1 parent 30571b2 commit 33920e4
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 19 deletions.
4 changes: 2 additions & 2 deletions assets/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class ConvertToBlocksEditorSupport {
loaded = true;

// This delay allows Gutenberg to initialize legacy content into freeform blocks
window._wpLoadBlockEditor.then(function () {
const result = transformer.execute();
window._wpLoadBlockEditor.then(async () => {
const result = await transformer.execute();
const config = window.convert_to_blocks_agent || false;

// if no migration config, then ignore this request
Expand Down
102 changes: 85 additions & 17 deletions assets/js/editor/transform/ClassicBlockTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ class ClassicBlockTransformer {
*
* @returns {boolean} The result of the transformation.
*/
execute() {
async execute() {
const coreEditor = this.wp.data.select('core/block-editor');
const blocks = coreEditor.getBlocks();

if (this.validBlocks(blocks)) {
/* Currently set to do 3 levels of recursion */
this.convertBlocks(blocks, 1, 3);
await this.convertBlocks(blocks, 1, 3);
}

return this.didTransform;
Expand All @@ -41,22 +41,18 @@ class ClassicBlockTransformer {
* @param {number} depth The current call stack depth
* @param {number} maxDepth The maximum allowed depth
*/
convertBlocks(blocks, depth = 1, maxDepth = 3) {
const n = blocks.length;
let i;
let block;
let innerBlocks;

for (i = 0; i < n; i++) {
block = blocks[i];
innerBlocks = { block };
async convertBlocks(blocks, depth = 1, maxDepth = 3) {
const promises = blocks.map(async (block) => {
const innerBlocks = { block };

this.transform(block);
await this.transform(block);

if (depth <= maxDepth && this.validBlocks(innerBlocks)) {
this.convertBlocks(innerBlocks, depth + 1, maxDepth);
await this.convertBlocks(innerBlocks, depth + 1, maxDepth);
}
}
});

await Promise.all(promises);
}

/**
Expand All @@ -65,18 +61,90 @@ class ClassicBlockTransformer {
*
* @param {object} block The current block object
*/
transform(block) {
async transform(block) {
if (this.isFreeformBlock(block)) {
const gutenbergBlocks = this.blockHandler(block);

this.wp.data
.dispatch('core/block-editor')
.replaceBlocks(block.clientId, this.blockHandler(block));
.replaceBlocks(block.clientId, gutenbergBlocks);

if (Array.isArray(gutenbergBlocks)) {
const promises = gutenbergBlocks.map((block) =>
this.waitForDOMManipulation(block.clientId),
);
await Promise.all(promises);
}

this.didTransform = true;
} else if (block.innerBlocks && block.innerBlocks.length > 0) {
this.convertBlocks(block.innerBlocks);
await this.convertBlocks(block.innerBlocks);
}
}

/**
* Waits for DOM manipulation to finish.
*
* @param {string} clientId The block ID.
*
* @returns {Promise<void>} A promise that resolves when DOM manipulation is detected or when no DOM manipulation happens.
*/
async waitForDOMManipulation(clientId = '') {
let iframeElement;
let block;

// Resolves after the editor iframe canvas is inserted.
await new Promise((resolve) => {
const intervalId = setInterval(() => {
iframeElement = document.getElementsByName('editor-canvas');

if (iframeElement.length > 0) {
iframeElement = iframeElement[0];
clearInterval(intervalId);
resolve();
}
}, 100);
});

// Resolves after the transformed block is inserted.
await new Promise((resolve) => {
const intervalId = setInterval(() => {
const iframeDocument =
iframeElement.contentDocument || iframeElement.contentWindow.document;

if (iframeDocument.body) {
block = iframeDocument.getElementById(`block-${clientId}`);

if (block) {
clearInterval(intervalId);
resolve();
}
}
}, 100);
});

return new Promise((resolve) => {
const observer = new MutationObserver((mutationList, observer) => {
if (observer.timeoutId) {
clearTimeout(observer.timeoutId);
}

observer.timeoutId = setTimeout(() => {
observer.disconnect();
resolve();
}, 100);
});

observer.observe(block, { childList: true, subtree: true });

// We resolve if there is no DOM manipulations happening
setTimeout(() => {
observer.disconnect();
resolve();
}, 100);
});
}

/**
* Uses the Core Raw HTML Block Handler to convert classic block to
* corresponding blocks
Expand Down

0 comments on commit 33920e4

Please sign in to comment.