;
};
const renderPage = (pageText, index)=>{
if(props.renderer == 'legacy') {
const html = MarkdownLegacy.render(pageText);
return ;
} else {
pageText += `\n\n \n\\column\n `; //Artificial column break at page end to emulate column-fill:auto (until `wide` is used, when column-fill:balance will reappear)
const html = Markdown.render(pageText, index);
return ;
}
};
const renderPages = ()=>{
if(props.errors && props.errors.length)
return renderedPages;
if(rawPages.length != renderedPages.length) // Re-render all pages when page count changes
renderedPages.length = 0;
// Render currently-edited page first so cross-page effects (variables, links) can propagate out first
renderedPages[props.currentEditorPage] = renderPage(rawPages[props.currentEditorPage], props.currentEditorPage);
_.forEach(rawPages, (page, index)=>{
if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
}
});
return renderedPages;
};
const handleControlKeys = (e)=>{
if(!(e.ctrlKey || e.metaKey)) return;
const P_KEY = 80;
if(e.keyCode == P_KEY && props.allowPrint) printCurrentBrew();
if(e.keyCode == P_KEY) {
e.stopPropagation();
e.preventDefault();
}
};
const loadAllBrewStylesAndSnippets = ()=>{
/*
Loads the theme bundle and parses it out.
These functionally replaces the previous renderStyles() function but needs to wait until the window is mounted.
*/
const rendererPath = isStaticTheme(props.renderer, props.theme) ? `/${props.renderer}/` : '/';
// Check for a User or Static Theme to change the endpoint path
fetch(`${window.location.protocol}//${window.location.host}/theme${rendererPath}${props.theme}`).then((response)=>response.json()).then((themeBundle)=>{
// Load the themeBundle from the endpoint as an object.
const documentFrame = document.getElementById('BrewRenderer');
const iframeDocument = documentFrame.contentDocument || documentFrame.contentWindow.document;
// Find the brew frame Document root.
for (let style=0; style < themeBundle.styles.length; style++){
/*
Walk through the styles array on the Theme Bundle.
Create a new style node and add it to the Brew Frame
*/
const newStyles = document.createElement('style');
newStyles.appendChild(document.createTextNode(`${themeBundle.styles[style]}\n`));
iframeDocument.head.appendChild(newStyles);
}
/*
Add the local brew styling to the Brew Frame
*/
const newStyles = document.createElement('style');
const cleanStyle = props.style; //DOMPurify.sanitize(props.style, purifyConfig);
newStyles.appendChild(document.createTextNode(`/* Local Brew Styling */\n\n${cleanStyle}`));
iframeDocument.head.appendChild(newStyles);
// TO-DO - Walk the snippets returns and add them to the appropriate menu.
});
};
const frameDidMount = ()=>{ //This triggers when iFrame finishes internal "componentDidMount"
loadAllBrewStylesAndSnippets(); // Load the brew's inherited and local styles.
setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame
updateSize();
window.addEventListener('resize', updateSize);
renderPages(); //Make sure page is renderable before showing
setState((prevState)=>({
...prevState,
isMounted : true,
visibility : 'visible'
}));
}, 100);
};
const emitClick = ()=>{ // Allow clicks inside iFrame to interact with dropdowns, etc. from outside
if(!window || !document) return;
document.dispatchEvent(new MouseEvent('click'));
};
let rendererPath = '';
const themePath = props.theme;
if(staticThemes[_.upperFirst(props.renderer)]?.[props.theme] !== undefined) //Change CSS path if is staticTheme
rendererPath = `${_.upperFirst(props.renderer)}/`;
return (
<>
{/*render dummy page while iFrame is mounting.*/}
{!state.isMounted
?
{renderDummyPage(1)}
: null}
{/*render in iFrame so broken code doesn't crash the site.*/}
{emitClick();}}
>
{/* Apply CSS from Style tab and render pages from Markdown tab */}
{state.isMounted
&&
<>