| 1 |
/** Private static section previews. Never changes the editor's viewport or saved blocks. */ |
| 2 |
(function () { |
| 3 |
'use strict'; |
| 4 |
window.bkbgCaptureSection = async function (source, viewport, signal, rendered) { |
| 5 |
if (!source || !source.isConnected) { throw new Error('The editor canvas is unavailable. Wait for the section to render or open the editor in a supported browser.'); } |
| 6 |
if (!window.htmlToImage) { throw new Error('Screenshot renderer is unavailable. Reload the editor.'); } |
| 7 |
var width = viewport === 'mobile' ? 390 : 1280; |
| 8 |
var frame = document.createElement('iframe'); |
| 9 |
frame.setAttribute('sandbox', 'allow-same-origin'); |
| 10 |
frame.setAttribute('aria-hidden', 'true'); frame.tabIndex = -1; |
| 11 |
frame.style.cssText = 'position:fixed;left:-100000px;top:0;width:' + width + 'px;height:2000px;border:0;pointer-events:none;'; |
| 12 |
var timer, rejectStop; |
| 13 |
var stopped = new Promise(function (_, reject) { rejectStop = reject; }); |
| 14 |
function abort() { rejectStop(new Error('Section capture cancelled.')); } |
| 15 |
function check() { if ((signal && signal.aborted) || !source.isConnected || !frame.isConnected) { throw new Error('Section changed or capture cancelled.'); } } |
| 16 |
if (signal) { signal.addEventListener('abort', abort, { once: true }); } |
| 17 |
timer = setTimeout(function () { rejectStop(new Error('Section capture timed out. Check image and font loading, then retry.')); }, 30000); |
| 18 |
async function capture() { |
| 19 |
document.body.appendChild(frame); |
| 20 |
var doc = frame.contentDocument, original = source.ownerDocument; |
| 21 |
if (!doc || !doc.head || !doc.body) { throw new Error('Capture canvas is not ready. Retry.'); } |
| 22 |
doc.documentElement.className = original.documentElement.className; |
| 23 |
doc.documentElement.style.cssText = original.documentElement.style.cssText; |
| 24 |
doc.body.style.cssText = original.body.style.cssText; |
| 25 |
doc.documentElement.lang = original.documentElement.lang; |
| 26 |
doc.documentElement.dir = original.documentElement.dir; |
| 27 |
var loads = []; |
| 28 |
original.querySelectorAll('style,link[rel="stylesheet"]').forEach(function (item) { |
| 29 |
var clone = item.cloneNode(true); |
| 30 |
Array.from(clone.attributes).forEach(function (attr) { if (/^on/i.test(attr.name)) { clone.removeAttribute(attr.name); } }); |
| 31 |
if (clone.tagName === 'LINK') { |
| 32 |
clone.href = item.href; |
| 33 |
loads.push(new Promise(function (resolve, reject) { clone.onload = resolve; clone.onerror = function () { reject(new Error('A stylesheet could not load in the preview.')); }; })); |
| 34 |
} |
| 35 |
doc.head.appendChild(clone); |
| 36 |
}); |
| 37 |
doc.body.className = original.body.className; |
| 38 |
var host = doc.createElement('div'); |
| 39 |
host.className = 'editor-styles-wrapper'; |
| 40 |
host.style.cssText = 'width:100%;max-width:none;margin:0;padding:0;'; |
| 41 |
doc.body.appendChild(host); |
| 42 |
if (!rendered || !rendered.html) { throw new Error('WordPress did not render this section.'); } |
| 43 |
host.innerHTML = rendered.html; |
| 44 |
var theme = doc.createElement('style'); theme.textContent = rendered.themeCss || ''; doc.head.appendChild(theme); |
| 45 |
host.querySelectorAll('style').forEach(function (style) { doc.head.appendChild(style); }); |
| 46 |
var target = host.firstElementChild; |
| 47 |
if (!target) { throw new Error('The rendered section is empty.'); } |
| 48 |
var warnings = []; |
| 49 |
if (target.querySelector('video,canvas,iframe')) { warnings.push('Video, canvas, embedded frames and interactive states may not be represented. Inspect them with browser tools separately.'); } |
| 50 |
target.querySelectorAll('script,iframe,video,canvas').forEach(function (node) { node.remove(); }); |
| 51 |
[target].concat(Array.from(doc.body.querySelectorAll('*'))).forEach(function (node) { |
| 52 |
Array.from(node.attributes).forEach(function (attr) { if (/^on/i.test(attr.name) || attr.name === 'autofocus') { node.removeAttribute(attr.name); } }); |
| 53 |
node.removeAttribute('contenteditable'); |
| 54 |
}); |
| 55 |
var css = doc.createElement('style'); |
| 56 |
css.textContent = 'html,body{margin:0!important;padding:0!important;min-width:0!important;}body{width:100%!important;}*,*::before,*::after{animation:none!important;transition:none!important;caret-color:transparent!important;} .block-editor-block-list__block::before,.block-editor-block-list__block::after{display:none!important;} .block-editor-block-list__block{outline:none!important;} .block-editor-block-list__layout{max-width:none!important;margin:0!important;}'; |
| 57 |
css.textContent += '.editor-styles-wrapper{padding:0!important;margin:0!important;max-width:none!important;width:100%!important;min-height:0!important;height:auto!important;display:flow-root!important;}'; |
| 58 |
doc.head.appendChild(css); |
| 59 |
await Promise.all(loads); check(); |
| 60 |
// Two layout frames let styles/media queries settle in the isolated viewport. |
| 61 |
await new Promise(function (resolve) { requestAnimationFrame(function () { requestAnimationFrame(resolve); }); }); |
| 62 |
if (doc.fonts) { await doc.fonts.ready; } check(); |
| 63 |
await Promise.all(Array.from(target.querySelectorAll('img')).map(async function (img) { |
| 64 |
img.loading = 'eager'; |
| 65 |
if (img.decode) { try { await img.decode(); } catch (e) { warnings.push('An image could not be loaded: ' + (img.alt || 'unnamed image')); } } |
| 66 |
})); |
| 67 |
check(); |
| 68 |
var rect = target.getBoundingClientRect(), height = Math.ceil(rect.height); |
| 69 |
if (height < 1 || height > 6000 || rect.width < 1) { throw new Error('Section is empty or too tall to capture. Capture a smaller nested block (maximum height 6000px).'); } |
| 70 |
var png = await window.htmlToImage.toPng(target, { pixelRatio: 1, width: Math.ceil(rect.width), height: height, backgroundColor: '#ffffff', preferredFontFormat: 'woff2' }); |
| 71 |
check(); |
| 72 |
if (png.length > 4200000) { throw new Error('Section screenshot is too large. Capture a smaller nested block.'); } |
| 73 |
return { data: png, viewport: viewport, width: Math.ceil(rect.width), height: height, warnings: warnings, kind: 'wordpress-static-render' }; |
| 74 |
} |
| 75 |
try { return await Promise.race([capture(), stopped]); } |
| 76 |
finally { clearTimeout(timer); if (signal) { signal.removeEventListener('abort', abort); } frame.remove(); } |
| 77 |
}; |
| 78 |
})(); |
| 79 |
|