diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx
index 202c1a375..f13f63cf9 100644
--- a/client/homebrew/brewRenderer/brewRenderer.jsx
+++ b/client/homebrew/brewRenderer/brewRenderer.jsx
@@ -29,7 +29,6 @@ const TOOLBAR_STATE_KEY = 'HB_renderer_toolbarState';
const INITIAL_CONTENT = dedent`
-
@@ -42,6 +41,7 @@ const BrewPage = (props)=>{
props = {
contents : '',
index : 0,
+ hoisted : false,
...props
};
const pageRef = useRef(null);
@@ -221,7 +221,8 @@ const BrewRenderer = (props)=>{
}
};
- const renderPages = ()=>{
+ const renderPages = (checkHoists = false)=>{
+
if(props.errors && props.errors.length)
return renderedPages;
@@ -233,10 +234,16 @@ const BrewRenderer = (props)=>{
renderedPages[props.currentEditorCursorPageNum - 1] = renderPage(rawPages[props.currentEditorCursorPageNum - 1], props.currentEditorCursorPageNum - 1);
_.forEach(rawPages, (page, index)=>{
- if((isInView(index) || !renderedPages[index]) && typeof window !== 'undefined'){
+ const varsOnPageRegex = /([!$]?)\[((?!\s*\])(?:\\.|[^\[\]\\])+)\]/g; // Find out if there are any vars on the page.
+ const forceRender = checkHoists &&
+ !props.hoisted &&
+ (page.match(varsOnPageRegex)); // forceRender forces pages outside of the PPR range to render if true.
+ // This is necessary on the first load to fully populate the variable table.
+ if((isInView(index) || !renderedPages[index] || forceRender) && typeof window !== 'undefined'){
renderedPages[index] = renderPage(page, index); // Render any page not yet rendered, but only re-render those in PPR range
}
});
+ if(!props.hoisted) { props.hoisted = true; } // Only fully hoist once.
return renderedPages;
};
@@ -276,7 +283,7 @@ const BrewRenderer = (props)=>{
window.addEventListener('hashchange', ()=>scrollToHash(window.location.hash));
setTimeout(()=>{ //We still see a flicker where the style isn't applied yet, so wait 100ms before showing iFrame
- renderPages(); //Make sure page is renderable before showing
+ renderPages(true); //Make sure page is renderable before showing
setState((prevState)=>({
...prevState,
isMounted : true,
diff --git a/client/homebrew/navbar/print.navitem.jsx b/client/homebrew/navbar/print.navitem.jsx
index ea262cf03..e669214b3 100644
--- a/client/homebrew/navbar/print.navitem.jsx
+++ b/client/homebrew/navbar/print.navitem.jsx
@@ -1,9 +1,25 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import Nav from './nav.jsx';
import { printCurrentBrew } from '@shared/helpers.js';
export default function(){
+ const [printing, setPrinting] = useState(false);
+
+ // listen for print cycle events to display "loading" message since it can take some time.
+ useEffect(()=>{
+ document.addEventListener('print:startprep', handlePrintStartPrep);
+ document.addEventListener('print:finishedprep', handlePrintPrepFinished);
+ return ()=>{
+ document.removeEventListener('print:startprep', handlePrintStartPrep);
+ document.removeEventListener('print:finishedprep', handlePrintPrepFinished);
+ }
+ }, []);
+
+ const handlePrintStartPrep = ()=>{ setPrinting(true); };
+
+ const handlePrintPrepFinished = ()=>{ setPrinting(false); };
+
return
- get PDF
+ {printing ? 'loading' : 'get PDF'}
;
};
diff --git a/index.html b/index.html
index fad6fd43a..d6bd3157d 100644
--- a/index.html
+++ b/index.html
@@ -5,10 +5,6 @@
-
diff --git a/shared/helpers.js b/shared/helpers.js
index eb3dba6d8..3610ccea4 100644
--- a/shared/helpers.js
+++ b/shared/helpers.js
@@ -105,14 +105,35 @@ const splitTextStyleAndMetadata = (brew)=>{
if(typeof brew.tags === 'string') brew.tags = brew.tags ? [brew.tags] : [];
};
-const printCurrentBrew = ()=>{
+const printCurrentBrew = async ()=>{
if(window.typeof !== 'undefined') {
- window.frames['BrewRenderer'].contentWindow.print();
- //Force DOM reflow; Print dialog causes a repaint, and @media print CSS somehow makes out-of-view pages disappear
- const node = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer').item(0);
- node.style.display='none';
- node.offsetHeight; // accessing this is enough to trigger a reflow
- node.style.display='';
+ // fire a custom event for the print cycle
+ document.dispatchEvent(new CustomEvent('print:startprep'));
+ try {
+ const iframeDoc = window.frames['BrewRenderer'].contentDocument;
+
+ // get all img elements with lazy loading (currently only elements generated through MarkedJS)
+ const lazyImages = [...iframeDoc.querySelectorAll('img[loading="lazy"]')];
+ lazyImages.forEach((img)=>{ img.loading = 'eager'; });
+
+ // waits for images to load before resolving promise and opening print dialog
+ await Promise.all(
+ lazyImages
+ .filter((img)=>!img.complete)
+ .map((img)=>new Promise((resolve)=>{ img.onload = resolve; img.onerror = resolve; }))
+ );
+
+ window.frames['BrewRenderer'].contentWindow.print();
+
+ //Force DOM reflow; Print dialog causes a repaint, and @media print CSS somehow makes out-of-view pages disappear
+ const node = iframeDoc.getElementsByClassName('brewRenderer').item(0);
+ node.style.display='none';
+ node.offsetHeight; // accessing this is enough to trigger a reflow
+ node.style.display='';
+ } finally {
+ // when lazy load images have all been loaded, and the doc re-rendered for print preview, emit 'finished' event.
+ document.dispatchEvent(new CustomEvent('print:finishedprep'));
+ }
}
};
diff --git a/shared/markdown.js b/shared/markdown.js
index d2a108e01..05a564254 100644
--- a/shared/markdown.js
+++ b/shared/markdown.js
@@ -83,7 +83,7 @@ renderer.image = function (token) {
if(href === null)
return text;
- let out = ` {
it('Renders an image element with injected style', function() {
const source = '{position:absolute}';
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
');
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
');
});
it('Renders an element modified by only the first of two consecutive injections', function() {
@@ -343,19 +343,19 @@ describe('Injection: When an injection tag follows an element', ()=>{
it('Renders an image with added attributes', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
it('Renders an image with "=" in the url, and added attributes', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
it('Renders an image and added attributes with "=" in the value, ', function() {
const source = ` {position:absolute,bottom:20px,left:130px,width:220px,a="b and c",d=e,otherUrl="url?auth=12345"}`;
const rendered = Markdown.render(source).trimReturns();
- expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
+ expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
`);
});
});
diff --git a/tests/markdown/variables.test.js b/tests/markdown/variables.test.js
index 884553703..ad23c87c1 100644
--- a/tests/markdown/variables.test.js
+++ b/tests/markdown/variables.test.js
@@ -315,21 +315,21 @@ describe('Normal Links and Images', ()=>{
const source = ``;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
-
`.trimReturns());
+
`.trimReturns());
});
it('Renders normal images with a title', function() {
const source = 'An image !';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
- An image !
`.trimReturns());
+ An image !
`.trimReturns());
});
it('Applies curly injectors to images', function() {
const source = `{width:100px}`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
-
`.trimReturns());
+
`.trimReturns());
});
it('Renders normal links', function() {
@@ -438,25 +438,25 @@ describe('Regression Tests', ()=>{
it('Handle Extra spaces in image alt-text 1', function(){
const source='';
const rendered = Markdown.render(source).trimReturns();
- expect(rendered).toBe('
');
+ expect(rendered).toBe('
');
});
it('Handle Extra spaces in image alt-text 2', function(){
const source='';
const rendered = Markdown.render(source).trimReturns();
- expect(rendered).toBe('
');
+ expect(rendered).toBe('
');
});
it('Handle Extra spaces in image alt-text 3', function(){
const source='';
const rendered = Markdown.render(source).trimReturns();
- expect(rendered).toBe('
');
+ expect(rendered).toBe('
');
});
it('Handle Extra spaces in image alt-text 4', function(){
const source='{height=20%,width=20%}';
const rendered = Markdown.render(source).trimReturns();
- expect(rendered).toBe('
');
+ expect(rendered).toBe('
');
});
});