|
@@ -0,0 +1,206 @@
|
|
|
+const JSZip = require('jszip');
|
|
|
+
|
|
|
+window.ExportHtml = (Popup) => {
|
|
|
+ const saveAs = function(blob, filename) {
|
|
|
+ let 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('.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('.js-perfect-scrollbar > div:nth-of-type(n+2)'),
|
|
|
+ ).forEach(elem => elem.remove());
|
|
|
+ Array.from(document.querySelectorAll('.js-perfect-scrollbar')).forEach(
|
|
|
+ elem => {
|
|
|
+ elem.style = 'overflow-y: auto !important;';
|
|
|
+ elem.classList.remove('js-perfect-scrollbar');
|
|
|
+ },
|
|
|
+ );
|
|
|
+ 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 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.close();
|
|
|
+ closeSidebar();
|
|
|
+ cleanBoardHtml();
|
|
|
+
|
|
|
+ await downloadStylesheets(getStylesheetList(), zip);
|
|
|
+ await downloadSrcAttached(getSrcAttached(), zip, boardSlug);
|
|
|
+ await downloadCardCovers(getCardCovers(), zip, boardSlug);
|
|
|
+
|
|
|
+ addBoardHTMLToZip(boardSlug, zip);
|
|
|
+
|
|
|
+ const content = await zip.generateAsync({ type: 'blob' });
|
|
|
+ saveAs(content, `${boardSlug}.zip`);
|
|
|
+ window.location.reload();
|
|
|
+ }
|
|
|
+};
|