summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/components/boards/boardColors.styl415
-rw-r--r--client/components/boards/boardsList.jade4
-rw-r--r--client/components/boards/boardsList.js6
-rw-r--r--client/components/boards/boardsList.styl16
-rw-r--r--client/components/lists/list.js24
-rw-r--r--client/components/main/fonts.styl24
-rw-r--r--client/components/main/layouts.styl2
-rw-r--r--client/components/rules/actions/boardActions.js4
-rw-r--r--client/components/sidebar/sidebar.jade16
-rw-r--r--client/components/sidebar/sidebar.js7
-rw-r--r--client/lib/exportHTML.js206
11 files changed, 704 insertions, 20 deletions
diff --git a/client/components/boards/boardColors.styl b/client/components/boards/boardColors.styl
index 3be9c0c3..0081143f 100644
--- a/client/components/boards/boardColors.styl
+++ b/client/components/boards/boardColors.styl
@@ -293,3 +293,418 @@ setBoardColor(color)
//.header-quick-access
// backgroud-color: #568ba2
+
+
+/*
+ Alternate "Clear" Styling
+*/
+setBoardClear(color1,color2)
+ //color1: The quick access color
+ //color2: The main bar color
+
+ &.sk-spinner div,
+ .board-backgrounds-list &.background-box,
+ .board-list & a
+ background: linear-gradient(180deg, color1 0%, color2 100%)
+ //background: linear-gradient(180deg, rgb(73, 155, 234) 0%, rgb(0, 174, 204) 100%)
+
+ .is-selected .minicard
+ border-left: 3px solid color1
+
+ &.pop-over .pop-over-list li a:not(.disabled):hover,
+ .sidebar .sidebar-content .sidebar-btn:hover,
+ .sidebar-list li a:hover
+ background-color: lighten(color1, 10%)
+
+ &#header ul li.current, &#header-quick-access ul li.current
+ border-bottom: 4px solid lighten(color2, 10%)
+
+ &#header-quick-access
+ background: darken(color1, 10%)
+ //background: rgba(66,137,204,1)
+ color: #FFF
+
+ &#header-quick-access #header-new-board-icon,
+ &#header-quick-access #header-user-bar,
+ &#header-quick-access ul li
+ color: rgba(255,255,255,0.5)
+
+ // The background-color value here is not seen,
+ // its covered by the background of #header-main-bar
+ // it's just to aid transitions between boards
+ &#header
+ background-color: color2
+ border-bottom: 1px solid darken(color2, 20%)
+ border-top: 1px solid darken(color2, 40%)
+
+ // Since the theme uses a gradient for the header
+ // and gradients break transitions, it has to be set here
+ &#header #header-main-bar
+ background: linear-gradient(180deg, color1 0%, color2 100%)
+
+ &#header #header-main-bar p
+ margin-bottom: 6px
+
+ &#header #header-main-bar .board-header-btn.emphasis
+ background: lighten(color2, 10%)
+
+ &:hover,
+ .board-header-btn-close
+ background: rgba(0,0,0,0.2)
+
+ &:hover .board-header-btn-close
+ background: rgba(0,0,0,0.2)
+
+ .materialCheckBox.is-checked
+ border-bottom: 2px solid color1
+ border-right: 2px solid color1
+
+ .is-multiselection-active .multi-selection-checkbox
+ &.is-checked + .minicard
+ background: lighten(color2, 90%)
+
+ &:not(.is-checked) + .minicard:hover:not(.minicard-composer)
+ background: lighten(color2, 97%)
+
+ .toggle-switch:checked ~ .toggle-label
+ background-color: lighten(color1, 20%)
+
+ &:after
+ background-color: darken(color1, 20%)
+
+ .board-canvas
+ background: linear-gradient(135deg, color1 0%, color2 100%)
+
+ .swimlane
+ background: none
+
+ .list:first-child
+ margin-left: 15px
+
+ .list
+ background: rgba(255,255,255,0.35)
+ margin: 10px
+ border: 0
+ border-radius: 14px
+
+ .list.list-composer
+ background: rgba(255,255,255,0.1)
+ height: min-content
+ flex: unset
+ width: 270px
+ padding-bottom: 16px
+
+ .list.list-composer .open-list-composer
+ border-radius: 7px
+ color: rgba(0,0,0,0.3)
+ padding: 7px 10px
+ display: block
+
+ .list.list-composer .open-list-composer:hover
+ box-shadow: 0 1px 2px rgba(0,0,0,.2)
+ background: rgba(255,255,255,0.7)
+ color: rgba(0,0,0,0.6)
+
+ .list-header
+ background-color: rgba(255,255,255,0.25)
+ border-radius: 14px 14px 0 0
+
+ .list-header:not([class*="list-header-"])
+ border-bottom: 6px solid rgba(255,255,255,0)
+
+ .list-header .list-header-name
+ color: rgba(0,0,0,0.6)
+
+ .list-body
+ padding: 11px
+
+ .minicard
+ border-radius: 7px
+ padding: 10px 10px 4px 10px
+ box-shadow: 2px 2px 4px 0px rgba(0,0,0,0.15)
+ color: #222
+
+ .card-details
+ border-radius: 0 0 14px 14px
+ box-shadow: 0 0 7px 0 rgba(0,0,0,0.5)
+ margin-left: -10px
+
+ .list-body .open-minicard-composer
+ border-radius: 7px
+ color: rgba(0,0,0,.3)
+ margin-bottom: 11px
+
+ .list-body .open-minicard-composer:hover
+ background: rgba(255,255,255,0.7)
+ color: rgba(0,0,0,0.6)
+
+ button[type=submit].primary, input[type=submit].primary
+ box-shadow: none
+ background-color: rgba(255,255,255,0.5)
+ color: rgba(0,0,0,0.55)
+ border-radius: 7px
+ border: 0
+
+ button[type="submit"].primary:hover, input[type="submit"].primary:hover
+ background-color: rgba(255,255,255,0.7)
+ color: rgba(0,0,0,0.8)
+ box-shadow: 0 1px 2px rgba(0,0,0,.2)
+
+ .quiet, .quiet a
+ color: rgba(0,0,0,0.4)
+
+ .list-header .list-header-watch-icon
+ color: rgba(0,0,0,0.5)
+ position: absolute
+ margin-top: -34px
+ margin-let: -11px
+
+ a.fa, a i.fa
+ color: rgba(0,0,0,0.3)
+
+ a:not(.disabled).is-active.fa, a:not(.disabled).is-active i.fa, a:not(.disabled):hover.fa, a:not(.disabled):hover i.fa
+ color: rgba(0,0,0,0.6)
+
+ input[type="email"], input[type="password"], input[type="text"]
+ border: 0
+ border-radius: 7px
+
+ .sidebar-shadow
+ box-shadow: none
+ border-left: 9px solid color2
+
+ .is-open .sidebar-shadow
+ box-shadow: -10px 0 8px rgba(0,0,0,0.3)
+
+ .list.ui-sortable-helper
+ transform:rotate(0deg)
+
+ .minicard-wrapper.placeholder
+ background: rgba(0,0,0,0.1)
+
+ .minicard-wrapper.ui-sortable-helper
+ transform:rotate(0deg)
+ opacity: 0.8
+
+ .list-body .open-minicard-composer
+ color: rgba(0,0,0,.3)
+
+ .swinlane.ui-sortable-helper
+ transform:rotate(0deg)
+
+ .swimlane .swimlane-header-wrap
+ background: linear-gradient(0deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.25) 100%)
+
+ .swimlane-header-wrap .inlined-form
+ width: 100%
+
+ .swimlane-header-wrap .list-composer
+ text-align: center
+ margin: 5px
+
+ .swimlane-header-wrap .list-name-input.full-line
+ margin: 0
+ display: inline-block
+ width: 270px
+
+ .swimlane-header-wrap .edit-controls
+ display: inline-block
+ vertical-align: middle
+
+ .swimlane-header-wrap .primary.confirm
+ margin-right: 0
+
+ .swimlane-header-wrap .fa.fa-times-thin
+ margin-top: 2px
+
+ // This is a general fix so that the little grabby hand appears when dragging the list via the title
+ .list.ui-sortable-helper,
+ .list.ui-sortable-helper .list-header.ui-sortable-handle,
+ .list.ui-sortable-helper .viewer
+ cursor:-webkit-grabbing;
+ cursor:grabbing
+
+.board-color-clearblue
+ setBoardClear(rgb(73, 155, 234),rgb(0, 174, 204))
+
+/*
+ Alternate "Natural" Styling
+*/
+.board-color-natural
+ setBoardColor(#596557)
+
+ &#header-quick-access
+ background-color: #2d392b
+
+ .ui-sortable
+ background-color:#dedede
+
+ .list-header
+ background-color: #c9cfc3
+ border-bottom: 6px solid #c9cfc3
+
+ .swimlane .swimlane-header-wrap
+ background-color: #c2c0ab
+
+/*
+ Alternate "Modern" Styling
+*/
+.board-color-modern
+ setBoardColor(#2A80B8)
+
+ /* General */
+ body
+ background: #f5f5f5
+
+ &#header-quick-access
+ padding: 10px
+ font-size: 14px
+ background: #333 !important
+
+ &#header-quick-access ul
+ overflow: visible
+
+ &#header-quick-access ul li.current
+ border: 0 !important
+ font-weight: bold
+
+ &#header-quick-access ul li.separator
+ display: none
+
+ &#header-quick-access ul li:nth-child(3)
+ margin-right: 10px
+
+ &#header-quick-access ul li a
+ padding: 5px 10px
+ border-radius: 2px
+
+ &#header-quick-access ul li.current a
+ border-radius: 2px
+ background: rgba(255,255,255,.2)
+
+ &#header #header-main-bar h1
+ font-family: Poppins
+ font-weight: bold
+ &#header-quick-access #header-user-bar
+ position relative
+
+ &#header-quick-access #header-user-bar .header-user-bar-name
+ margin: 5px 3px 0 0;
+
+ section#notifications-drawer
+ top: 46px;
+ box-shadow: 0 4px 20px rgba(0,0,0,.1)
+ max-width: 100%
+
+ section#notifications-drawer .header
+ top: 46px;
+ border-radius: 0 3px
+ height: 21px
+ background: #f7f7f7
+
+ /* Swimlane */
+ .swimlane
+ background: #f5f5f5
+
+ .swimlane .swimlane-header-wrap .swimlane-header
+ font-family: Poppins
+
+ /* All board views */
+ .board-list .board-list-item
+ padding: 20px
+
+ .board-list-item-name
+ font-family: Poppins
+
+ /* Board */
+ .list
+ background: transparent
+ border-left: 0
+ margin: 10px 0
+ padding: 0px
+ border-radius: 5px
+ min-width: 300px
+
+ .list-body .open-minicard-composer:hover /*me*/
+ background: none
+ box-shadow: none
+
+ .list:first-child
+ margin-left: 5px
+
+ .list.list-composer.js-list-composer
+ transition: all .3s ease
+ min-width: 80px
+
+ .open-list-composer.js-open-inlined-form:hover
+ color: #222
+
+ .list-header
+ background: none
+ border-bottom-width: 0px
+
+ .list-header .list-header-name
+ font-family: Poppins
+ color: #000
+ font-weight: 500
+
+ /* Card changes */
+ .minicard
+ background: #FFF
+ padding: 15px 15px 10px
+ box-shadow: 0 3px 8px rgba(0,0,0,.05)
+
+ .minicard-plum:hover:not(.minicard-composer), .is-selected .minicard-plum, .draggable-hover-card .minicard-plum
+ background: none
+
+ .minicard-title
+ line-height: 1.5em
+
+ .minicard .minicard-cover
+ background-size: cover
+ margin: -15px -15px 10px
+ height: 100px
+
+ .card-label-orange
+ color: #fff
+
+ .card-date
+ font-size: 12px
+ padding: 3px 5px
+
+ /* Pop over */
+ .header-title
+ font-family: Poppins
+ font-size: 16px
+ color: #333
+
+ .pop-over
+ box-shadow: 0 4px 20px rgba(0,0,0,.1)
+ border: 0
+ border-radius: 5px
+
+ .pop-over .header
+ padding: 10px
+ border-bottom: 0
+ border-radius: 5px 5px 0 0
+
+ .pop-over .content-container .content
+ padding: 5px 20px 20px
+ width: 260px
+
+ .pop-over-list li > a
+ border-radius: 5px
+
+ .pop-over-list li > a > i
+ margin-right: 5px
+
+ .pop-over-list li>a .sub-name
+ margin-bottom: 8px
+
+ /* Sidebar */
+ .sidebar .sidebar-shadow
+ box-shadow: 0 0 60px rgba(0,0,0,.2)
+
+ .sidebar .sidebar-content
+ padding: 30px
+
diff --git a/client/components/boards/boardsList.jade b/client/components/boards/boardsList.jade
index bbce1d6f..7fd7c2ba 100644
--- a/client/components/boards/boardsList.jade
+++ b/client/components/boards/boardsList.jade
@@ -31,6 +31,10 @@ template(name="boardList")
i.fa.js-has-spenttime-cards(
class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}"
title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}")
+ if isMiniScreen
+ i.fa.board-handle(
+ class="fa-arrows"
+ title="{{_ 'Drag board'}}")
unless isMiniScreen
if isSandstorm
i.fa.js-clone-board(
diff --git a/client/components/boards/boardsList.js b/client/components/boards/boardsList.js
index b99c0c31..eee119ea 100644
--- a/client/components/boards/boardsList.js
+++ b/client/components/boards/boardsList.js
@@ -72,6 +72,12 @@ BlazeComponent.extendComponent({
// Disable drag-dropping if the current user is not a board member or is comment only
this.autorun(() => {
+ if (Utils.isMiniScreen()) {
+ $boards.sortable({
+ handle: '.board-handle',
+ });
+ }
+
$boards.sortable('option', 'disabled', !userIsAllowedToMove());
});
},
diff --git a/client/components/boards/boardsList.styl b/client/components/boards/boardsList.styl
index 97d4f195..0cadbf84 100644
--- a/client/components/boards/boardsList.styl
+++ b/client/components/boards/boardsList.styl
@@ -208,6 +208,22 @@ $spaceBetweenTiles = 16px
top: -100px
left: -100px
+ .board-handle
+ position: absolute
+ padding: 7px
+ top: 50%
+ transform: translateY(-50%)
+ right: 10px
+ font-size: 24px
+
@media screen and (max-width: 360px)
li
width: 100%
+
+ .board-handle
+ position: absolute
+ padding: 7px
+ top: 50%
+ transform: translateY(-50%)
+ right: 10px
+ font-size: 24px
diff --git a/client/components/lists/list.js b/client/components/lists/list.js
index 839304f8..5c315588 100644
--- a/client/components/lists/list.js
+++ b/client/components/lists/list.js
@@ -74,18 +74,16 @@ BlazeComponent.extendComponent({
const sortIndex = calculateIndex(prevCardDom, nextCardDom, nCards);
const listId = Blaze.getData(ui.item.parents('.list').get(0))._id;
const currentBoard = Boards.findOne(Session.get('currentBoard'));
- let swimlaneId = '';
+ const defaultSwimlaneId = currentBoard.getDefaultSwimline()._id;
+ let targetSwimlaneId = null;
+
+ // only set a new swimelane ID if the swimlanes view is active
if (
Utils.boardView() === 'board-view-swimlanes' ||
currentBoard.isTemplatesBoard()
)
- swimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))._id;
- else if (
- Utils.boardView() === 'board-view-lists' ||
- Utils.boardView() === 'board-view-cal' ||
- !Utils.boardView
- )
- swimlaneId = currentBoard.getDefaultSwimline()._id;
+ targetSwimlaneId = Blaze.getData(ui.item.parents('.swimlane').get(0))
+ ._id;
// Normally the jquery-ui sortable library moves the dragged DOM element
// to its new position, which disrupts Blaze reactive updates mechanism
@@ -98,9 +96,12 @@ BlazeComponent.extendComponent({
if (MultiSelection.isActive()) {
Cards.find(MultiSelection.getMongoSelector()).forEach((card, i) => {
+ const newSwimlaneId = targetSwimlaneId
+ ? targetSwimlaneId
+ : card.swimlaneId || defaultSwimlaneId;
card.move(
currentBoard._id,
- swimlaneId,
+ newSwimlaneId,
listId,
sortIndex.base + i * sortIndex.increment,
);
@@ -108,7 +109,10 @@ BlazeComponent.extendComponent({
} else {
const cardDomElement = ui.item.get(0);
const card = Blaze.getData(cardDomElement);
- card.move(currentBoard._id, swimlaneId, listId, sortIndex.base);
+ const newSwimlaneId = targetSwimlaneId
+ ? targetSwimlaneId
+ : card.swimlaneId || defaultSwimlaneId;
+ card.move(currentBoard._id, newSwimlaneId, listId, sortIndex.base);
}
boardComponent.setIsDragging(false);
},
diff --git a/client/components/main/fonts.styl b/client/components/main/fonts.styl
index fc8c8f00..5d6fb558 100644
--- a/client/components/main/fonts.styl
+++ b/client/components/main/fonts.styl
@@ -15,3 +15,27 @@
local('Roboto-Bold'),
url('/fonts/roboto-bold.woff2') format('woff2'),
url('/fonts/roboto-bold.woff') format('woff')
+
+@font-face
+ font-family: 'Poppins'
+ font-style: normal
+ font-weight: 400
+ src: local('Poppins'),
+ local('Poppins-Regular'),
+ url('/fonts/poppins-regular.woff') format('woff')
+
+@font-face
+ font-family: 'Poppins'
+ font-style: normal
+ font-weight: 500
+ src: local('Poppins Medium'),
+ local('Poppins-Medium'),
+ url('/fonts/poppins-medium.woff') format('woff')
+
+@font-face
+ font-family: 'Poppins'
+ font-style: normal
+ font-weight: 700
+ src: local('Poppins Bold'),
+ local('Poppins-Bold'),
+ url('/fonts/poppins-bold.woff') format('woff')
diff --git a/client/components/main/layouts.styl b/client/components/main/layouts.styl
index 01ce2f16..85a5f1b2 100644
--- a/client/components/main/layouts.styl
+++ b/client/components/main/layouts.styl
@@ -32,7 +32,7 @@ a:hover,a:focus
border-radius: unset
html, body, input, select, textarea, button
- font: 14px Roboto, "Helvetica Neue", Arial, Helvetica, sans-serif
+ font: 14px Roboto, Poppins, "Helvetica Neue", Arial, Helvetica, sans-serif
line-height: 18px
color: #4d4d4d
diff --git a/client/components/rules/actions/boardActions.js b/client/components/rules/actions/boardActions.js
index 02910cc1..5675873f 100644
--- a/client/components/rules/actions/boardActions.js
+++ b/client/components/rules/actions/boardActions.js
@@ -68,8 +68,8 @@ BlazeComponent.extendComponent({
const ruleName = this.data().ruleName.get();
const trigger = this.data().triggerVar.get();
const actionSelected = this.find('#move-spec-action').value;
- const swimlaneName = this.find('#swimlaneName').value;
- const listName = this.find('#listName').value;
+ const swimlaneName = this.find('#swimlaneName').value || '*';
+ const listName = this.find('#listName').value || '*';
const boardId = Session.get('currentBoard');
const destBoardId = this.find('#board-id').value;
const desc = Utils.getTriggerActionDesc(event, this);
diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade
index 280eaeaf..04f2a8c2 100644
--- a/client/components/sidebar/sidebar.jade
+++ b/client/components/sidebar/sidebar.jade
@@ -329,12 +329,10 @@ template(name="boardMenuPopup")
if isSandstorm
hr
ul.pop-over-list
- if withApi
- li
- a(href="{{exportUrl}}", download="{{exportFilename}}")
- i.fa.fa-share-alt
- i.fa.fa-sign-out
- | {{_ 'export-board'}}
+ li
+ a.js-export-board
+ i.fa.fa-share-alt
+ | {{_ 'export-board'}}
li
a.js-import-board
i.fa.fa-share-alt
@@ -365,7 +363,7 @@ template(name="boardMenuPopup")
template(name="exportBoard")
ul.pop-over-list
li
- a(href="{{exportUrl}}", download="{{exportJsonFilename}}")
+ a.download-json-link(href="{{exportUrl}}", download="{{exportJsonFilename}}")
i.fa.fa-share-alt
| {{_ 'export-board-json'}}
li
@@ -376,6 +374,10 @@ template(name="exportBoard")
a(href="{{exportTsvUrl}}", download="{{exportTsvFilename}}")
i.fa.fa-share-alt
| {{_ 'export-board-tsv'}}
+ li
+ a.html-export-board
+ i.fa.fa-archive
+ | {{_ 'export-board-html'}}
template(name="labelsWidget")
.board-widget.board-widget-labels
diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js
index 2c1cfd75..0e535041 100644
--- a/client/components/sidebar/sidebar.js
+++ b/client/components/sidebar/sidebar.js
@@ -463,6 +463,13 @@ BlazeComponent.extendComponent({
},
}).register('exportBoardPopup');
+Template.exportBoard.events({
+ 'click .html-export-board': async event => {
+ event.preventDefault();
+ await ExportHtml(Popup)();
+ }
+});
+
Template.labelsWidget.events({
'click .js-label': Popup.open('editLabel'),
'click .js-add-label': Popup.open('createLabel'),
diff --git a/client/lib/exportHTML.js b/client/lib/exportHTML.js
new file mode 100644
index 00000000..fe15b6aa
--- /dev/null
+++ b/client/lib/exportHTML.js
@@ -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();
+ }
+};