mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-05-07 18:48:39 +00:00
Rework HTML Download options to use a DOM snatch and grab
Still requires path manipulation. Stubs the same for Zipfiles.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Nav from './nav.jsx';
|
import Nav from './nav.jsx';
|
||||||
import { printCurrentBrew } from '../../../shared/helpers.js';
|
import { printCurrentBrew, scrapeBrewHTML, scrapeBrewZip } from '../../../shared/helpers.js';
|
||||||
|
|
||||||
export default function(props){
|
export default function(props){
|
||||||
return <Nav.dropdown>
|
return <Nav.dropdown>
|
||||||
@@ -10,10 +10,10 @@ export default function(props){
|
|||||||
<Nav.item onClick={printCurrentBrew} color='purple' icon='far fa-file-pdf'>
|
<Nav.item onClick={printCurrentBrew} color='purple' icon='far fa-file-pdf'>
|
||||||
get PDF
|
get PDF
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<Nav.item color='orange' icon='fas fa-file-code' href={`/export/slimHTML/${props?.brew?.editId || props?.brew?.shareId}`}>
|
<Nav.item onClick={scrapeBrewHTML} color='orange' icon='fas fa-file-code'>
|
||||||
get HTML
|
get HTML
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
<Nav.item color='orange' icon='fas fa-file-archive' href={`/export/zipHTML/${props?.brew?.editId || props?.brew?.shareId}`}>
|
<Nav.item onClick={scrapeBrewZip} color='orange' icon='fas fa-file-archive'>
|
||||||
get HTML (Zip)
|
get HTML (Zip)
|
||||||
</Nav.item>
|
</Nav.item>
|
||||||
</Nav.dropdown>;
|
</Nav.dropdown>;
|
||||||
|
|||||||
+2
-22
@@ -27,9 +27,8 @@ import asyncHandler from 'express-async-handler';
|
|||||||
import templateFn from '../client/template.js';
|
import templateFn from '../client/template.js';
|
||||||
import { model as HomebrewModel } from './homebrew.model.js';
|
import { model as HomebrewModel } from './homebrew.model.js';
|
||||||
|
|
||||||
import { DEFAULT_BREW } from './brewDefaults.js';
|
import { DEFAULT_BREW } from './brewDefaults.js';
|
||||||
import { splitTextStyleAndMetadata,
|
import { splitTextStyleAndMetadata } from '../shared/helpers.js';
|
||||||
simulateRender } from '../shared/helpers.js';
|
|
||||||
|
|
||||||
//==== Middleware Imports ====//
|
//==== Middleware Imports ====//
|
||||||
import contentNegotiation from './middleware/content-negotiation.js';
|
import contentNegotiation from './middleware/content-negotiation.js';
|
||||||
@@ -240,25 +239,6 @@ app.get('/source/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
|||||||
res.status(200).send(text);
|
res.status(200).send(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Export the Brew as HTML
|
|
||||||
app.get('/export/:mode/:id', asyncHandler(getBrew('admin')), asyncHandler(simulateRender), (req, res)=>{
|
|
||||||
|
|
||||||
const id = req.params.id;
|
|
||||||
const mode = req.params.mode;
|
|
||||||
const { brew } = req;
|
|
||||||
sanitizeBrew(brew, 'share');
|
|
||||||
const prefix = 'HB - ';
|
|
||||||
|
|
||||||
let fileName = sanitizeFilename(`${prefix}${brew.title}`).replaceAll(' ', '');
|
|
||||||
if(!fileName || !fileName.length) { fileName = `${prefix}-Untitled-Brew`; };
|
|
||||||
// res.set({
|
|
||||||
// 'Cache-Control' : 'no-cache',
|
|
||||||
// 'Content-Type' : 'text/plain',
|
|
||||||
// 'Content-Disposition' : `attachment; filename*=UTF-8''${encodeRFC3986ValueChars(fileName)}.html`
|
|
||||||
// });
|
|
||||||
res.status(200).send(brew.html);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//Download brew source page
|
//Download brew source page
|
||||||
app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
|
||||||
|
|||||||
+27
-88
@@ -176,99 +176,37 @@ const debugTextMismatch = (clientTextRaw, serverTextRaw, label)=>{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const simulateRenderPage = (pageText, index, renderer)=>{
|
const scrapeBrew = ()=>{
|
||||||
|
const htmlBody = `<html>\n${window.frames['BrewRenderer'].contentDocument.documentElement.innerHTML}\n</html>`;
|
||||||
let styles = {};
|
return htmlBody;
|
||||||
|
|
||||||
let classes = 'page';
|
|
||||||
let attributes = {};
|
|
||||||
|
|
||||||
if(renderer == 'legacy') {
|
|
||||||
pageText.replace(COLUMNBREAK_REGEX_LEGACY, '```\n````\n'); // Allow Legacy brews to use `\column(break)`
|
|
||||||
// const html = MarkdownLegacy.render(pageText);
|
|
||||||
const html = "Markdown Legacy currently unsupported"
|
|
||||||
|
|
||||||
return `<div className='page phb' index=${index} key=${index} style=${styles}>\n${html}\n</div>\n`;
|
|
||||||
} else {
|
|
||||||
if(pageText.startsWith('\\page')) {
|
|
||||||
const firstLineTokens = Markdown.marked.lexer(pageText.split('\n', 1)[0])[0].tokens;
|
|
||||||
const injectedTags = firstLineTokens?.find((obj)=>obj.injectedTags !== undefined)?.injectedTags;
|
|
||||||
if(injectedTags) {
|
|
||||||
styles = { ...styles, ...injectedTags.styles };
|
|
||||||
styles = _.mapKeys(styles, (v, k)=>k.startsWith('--') ? k : _.camelCase(k)).join(''); // Convert CSS to camelCase for React
|
|
||||||
classes = [classes, injectedTags.classes].join(' ').trim();
|
|
||||||
attributes = injectedTags.attributes;
|
|
||||||
}
|
|
||||||
pageText = pageText.includes('\n') ? pageText.substring(pageText.indexOf('\n') + 1) : ''; // Remove the \page line
|
|
||||||
}
|
|
||||||
|
|
||||||
// DO NOT REMOVE!!! REQUIRED FOR BACKWARDS COMPATIBILITY WITH NON-UPGRADABLE VERSIONS OF CHROME.
|
|
||||||
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 `<div class=${classes} id=p${index} key=${index} style=${styles} ${attributes}>\n${html}\n</div>`;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const simulateRender = async (req, res, next)=>{
|
const downloadBlob = (brewHtml, fileName)=>{
|
||||||
let htmlHead = '';
|
const blob = new Blob([brewHtml], { type: 'text/plain' });
|
||||||
let htmlStyles = '';
|
const url = URL.createObjectURL(blob);
|
||||||
let htmlBody = '';
|
const a = document.createElement('a');
|
||||||
let errorMsg = {};
|
a.href = url;
|
||||||
// Build HTML similar to the BrewRender ?
|
a.download = fileName || 'download';
|
||||||
|
const clickHandler = ()=>{
|
||||||
const setError = (error)=>{
|
setTimeout(()=>{
|
||||||
errorMsg = error;
|
URL.revokeObjectURL(url);
|
||||||
|
removeEventListener('click', clickHandler);
|
||||||
|
}, 150);
|
||||||
};
|
};
|
||||||
|
a.addEventListener('click', clickHandler, false);
|
||||||
|
a.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrapeBrewZip = ()=>{
|
||||||
|
const htmlBody = scrapeBrew();
|
||||||
|
// DO STUFF!
|
||||||
|
};
|
||||||
|
|
||||||
splitTextStyleAndMetadata(req.brew);
|
const scrapeBrewHTML = ()=>{
|
||||||
|
const htmlBody = scrapeBrew();
|
||||||
const PORT = req.header('host').indexOf[':'] > -1 ? req.header('host').split(':')[1] : '8000';
|
// Manipulate the body to change all relative path references to full URLs
|
||||||
|
downloadBlob(htmlBody, 'testDownload.html');
|
||||||
const themeRes = await request
|
|
||||||
.get(`http://localhost:${PORT}/api/theme/${req.brew.renderer}/${req.brew.theme}`)
|
|
||||||
.set('Homebrewery-Version', packageJSON.version)
|
|
||||||
.catch((err)=>{
|
|
||||||
setError(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
const htmlThemeBundle = themeRes.body.styles.map((style)=>`<style>${style}</style>`).join('\n\n');
|
|
||||||
|
|
||||||
// Create Head
|
|
||||||
htmlHead += ` <head>
|
|
||||||
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css">
|
|
||||||
<link href="/homebrew/bundle.css" type="text/css" rel="stylesheet">
|
|
||||||
<base target="_blank">
|
|
||||||
<title>${req.brew.title}</title>
|
|
||||||
</head>`;
|
|
||||||
|
|
||||||
htmlStyles = `\t<div style="display:none;">\n` +
|
|
||||||
`\t\t${htmlThemeBundle}\n` +
|
|
||||||
`\t\t<style>\n${req.brew.style}\n</style>\n` +
|
|
||||||
`\t</div>`;
|
|
||||||
|
|
||||||
let rawPages = [];
|
|
||||||
let renderedPages = [];
|
|
||||||
|
|
||||||
if(req.brew.renderer == 'legacy') {
|
|
||||||
rawPages = req.brew.text.split(PAGEBREAK_REGEX_LEGACY);
|
|
||||||
} else {
|
|
||||||
rawPages = req.brew.text.split(PAGEBREAK_REGEX_V3);
|
|
||||||
}
|
|
||||||
|
|
||||||
_.forEach(rawPages, (page, index)=>{
|
|
||||||
renderedPages[index] = simulateRenderPage(page, index, req.brew.renderer);
|
|
||||||
});
|
|
||||||
|
|
||||||
htmlBody = `<div class="pages recto single" lang="en" style="zoom: 100%; gap: 5px 10px;">${renderedPages.join('\n')}\n</div`;
|
|
||||||
|
|
||||||
const result = `<html>\n${htmlHead}\n<body>\n\t<div>\n\t\t<div class="frame-content">\n\t\t\t<div class="brewRenderer">\n` +
|
|
||||||
`\t\t\t\t${htmlStyles}\n${htmlBody}\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</body>\n</html>`;
|
|
||||||
req.brew.html = result;
|
|
||||||
next();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -277,5 +215,6 @@ export {
|
|||||||
fetchThemeBundle,
|
fetchThemeBundle,
|
||||||
brewSnippetsToJSON,
|
brewSnippetsToJSON,
|
||||||
debugTextMismatch,
|
debugTextMismatch,
|
||||||
simulateRender,
|
scrapeBrewHTML,
|
||||||
|
scrapeBrewZip,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user