123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- const JSZip = require('jszip');
- window.ExportHtml = Popup => {
- const saveAs = function(blob, filename) {
- const dl = document.createElement('a');
- dl.href = window.URL.createObjectURL(blob);
- dl.onclick = event => document.body.removeChild(event.target);
- dl.style.display = 'none';
- dl.target = '_blank';
- dl.download = filename;
- document.body.appendChild(dl);
- dl.click();
- };
- const asyncForEach = async function(array, callback) {
- for (let index = 0; index < array.length; index++) {
- await callback(array[index], index, array);
- }
- };
- const getPageHtmlString = () => {
- return `<!doctype html>${window.document.querySelector('html').outerHTML}`;
- };
- const removeAnchors = htmlString => {
- const replaceOpenAnchor = htmlString.replace(
- new RegExp('<a ', 'gim'),
- '<span ',
- );
- return replaceOpenAnchor.replace(new RegExp('</a', 'gim'), '</span');
- };
- const ensureSidebarRemoved = () => {
- document.querySelector('.board-sidebar.sidebar').remove();
- };
- const addJsonExportToZip = async (zip, boardSlug) => {
- const downloadJSONLink = document.querySelector('.download-json-link');
- const downloadJSONURL = downloadJSONLink.href;
- const response = await fetch(downloadJSONURL);
- const responseBody = await response.text();
- zip.file(`data/${boardSlug}.json`, responseBody);
- };
- const closeSidebar = () => {
- document.querySelector('.board-header-btn.js-toggle-sidebar').click();
- };
- const cleanBoardHtml = () => {
- Array.from(document.querySelectorAll('script')).forEach(elem =>
- elem.remove(),
- );
- Array.from(
- document.querySelectorAll('link:not([rel="stylesheet"])'),
- ).forEach(elem => elem.remove());
- document.querySelector('#header-quick-access').remove();
- Array.from(
- document.querySelectorAll('#header-main-bar .board-header-btns'),
- ).forEach(elem => elem.remove());
- Array.from(
- document.querySelectorAll('.js-pop-over, .pop-over'),
- ).forEach(elem => elem.remove());
- Array.from(document.querySelectorAll('.list-composer')).forEach(elem =>
- elem.remove(),
- );
- Array.from(
- document.querySelectorAll(
- '.list-composer,.js-card-composer, .js-add-card',
- ),
- ).forEach(elem => elem.remove());
- Array.from(document.querySelectorAll('[href]:not(link)')).forEach(elem =>
- elem.attributes.removeNamedItem('href'),
- );
- Array.from(document.querySelectorAll('[href]')).forEach(elem => {
- // eslint-disable-next-line no-self-assign
- elem.href = elem.href;
- // eslint-disable-next-line no-self-assign
- elem.src = elem.src;
- });
- Array.from(document.querySelectorAll('.is-editable')).forEach(elem => {
- elem.classList.remove('is-editable');
- });
- };
- const getBoardSlug = () => {
- return window.location.href.split('/').pop();
- };
- const getStylesheetList = () => {
- return Array.from(
- document.querySelectorAll('link[href][rel="stylesheet"]'),
- );
- };
- const downloadStylesheets = async (stylesheets, zip) => {
- await asyncForEach(stylesheets, async elem => {
- const response = await fetch(elem.href);
- const responseBody = await response.text();
- const finalResponse = responseBody.replace(
- new RegExp('packages/[^/]+/upstream/', 'gim'),
- '../',
- );
- const filename = elem.href
- .split('/')
- .pop()
- .split('?')
- .shift();
- const fileFullPath = `style/${filename}`;
- zip.file(fileFullPath, finalResponse);
- elem.href = `../${fileFullPath}`;
- });
- };
- const getSrcAttached = () => {
- return Array.from(document.querySelectorAll('[src]'));
- };
- const downloadSrcAttached = async (elements, zip, boardSlug) => {
- await asyncForEach(elements, async elem => {
- const response = await fetch(elem.src);
- const responseBody = await response.blob();
- const filename = elem.src
- .split('/')
- .pop()
- .split('?')
- .shift();
- const fileFullPath = `${boardSlug}/${elem.tagName.toLowerCase()}/${filename}`;
- zip.file(fileFullPath, responseBody);
- elem.src = `./${elem.tagName.toLowerCase()}/${filename}`;
- });
- };
- const removeCssUrlSurround = url => {
- const working = url || '';
- return working
- .split('url(')
- .join('')
- .split('")')
- .join('')
- .split('"')
- .join('')
- .split("')")
- .join('')
- .split("'")
- .join('')
- .split(')')
- .join('');
- };
- const getCardCovers = () => {
- return Array.from(document.querySelectorAll('.minicard-cover')).filter(
- elem => elem.style['background-image'],
- );
- };
- const getWebFonts = () => {
- fontUrls = [];
- for (let sheet of document.styleSheets) {
- // Get the base URL of the stylesheet
- let baseUrl = sheet.href ? new URL(sheet.href).origin : window.location.origin;
- try {
- for (let rule of sheet.cssRules) {
- if (rule.type === CSSRule.FONT_FACE_RULE) {
- let src = rule.style.getPropertyValue('src');
- let urlMatch = src.match(/url\(["']?(.+?)["']?\)/);
- if (urlMatch) {
- let fontUrl = urlMatch[1];
- // Resolve the URL relative to the stylesheet's base URL
- let resolvedUrl = new URL(fontUrl, baseUrl);
- fontUrls.push(resolvedUrl.href); // Using .href to get the absolute URL
- }
- }
- }
- } catch (e) {
- console.log('Access to stylesheet blocked:', e);
- }
- }
- return fontUrls;
- };
- const downloadFonts = async(elements, zip) => {
- await asyncForEach(elements, async elem => {
- const response = await fetch(elem);
- const responseBody = await response.blob();
- const filename = elem.split('/')
- .pop()
- .split('?')
- .shift()
- .split('#')
- .shift();
- const fileFullPath = `webfonts/${filename}`;
- zip.file(fileFullPath, responseBody);
- });
- }
- const downloadCardCovers = async (elements, zip, boardSlug) => {
- await asyncForEach(elements, async elem => {
- const response = await fetch(
- removeCssUrlSurround(elem.style['background-image']),
- );
- const responseBody = await response.blob();
- const filename = removeCssUrlSurround(elem.style['background-image'])
- .split('/')
- .pop()
- .split('?')
- .shift()
- .split('#')
- .shift();
- const fileFullPath = `${boardSlug}/covers/${filename}`;
- zip.file(fileFullPath, responseBody);
- elem.style = "background-image: url('" + `covers/${filename}` + "')";
- });
- };
- const addBoardHTMLToZip = (boardSlug, zip) => {
- ensureSidebarRemoved();
- const htmlOutputPath = `${boardSlug}/index.html`;
- zip.file(
- htmlOutputPath,
- new Blob([removeAnchors(getPageHtmlString())], {
- type: 'application/html',
- }),
- );
- };
- return async () => {
- const zip = new JSZip();
- const boardSlug = getBoardSlug();
- await addJsonExportToZip(zip, boardSlug);
- Popup.back();
- closeSidebar();
- cleanBoardHtml();
- await downloadStylesheets(getStylesheetList(), zip);
- await downloadSrcAttached(getSrcAttached(), zip, boardSlug);
- await downloadCardCovers(getCardCovers(), zip, boardSlug);
- await downloadFonts(getWebFonts(), zip);
- addBoardHTMLToZip(boardSlug, zip);
- const content = await zip.generateAsync({ type: 'blob' });
- saveAs(content, `${boardSlug}.zip`);
- window.location.reload();
- };
- };
|