mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-27 03:51:08 +00:00
Merge pull request #1683 from bookwyrm-social/prettier
Adds Prettier javascript formatter
This commit is contained in:
commit
934a202738
9 changed files with 2012 additions and 1958 deletions
24
.github/workflows/prettier.yaml
vendored
Normal file
24
.github/workflows/prettier.yaml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# @url https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
|
||||
name: JavaScript Prettier (run ./bw-dev prettier to fix)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint with Prettier
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it.
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install modules
|
||||
run: npm install .
|
||||
|
||||
# See .stylelintignore for files that are not linted.
|
||||
- name: Run Prettier
|
||||
run: npx prettier --check bookwyrm/static/js/*.js
|
|
@ -1,9 +1,10 @@
|
|||
/* exported BlockHref */
|
||||
|
||||
let BlockHref = new class {
|
||||
let BlockHref = new (class {
|
||||
constructor() {
|
||||
document.querySelectorAll('[data-href]')
|
||||
.forEach(t => t.addEventListener('click', this.followLink.bind(this)));
|
||||
document
|
||||
.querySelectorAll("[data-href]")
|
||||
.forEach((t) => t.addEventListener("click", this.followLink.bind(this)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,5 +18,4 @@ let BlockHref = new class {
|
|||
|
||||
window.location.href = url;
|
||||
}
|
||||
}();
|
||||
|
||||
})();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* exported BookWyrm */
|
||||
/* globals TabGroup */
|
||||
|
||||
let BookWyrm = new class {
|
||||
let BookWyrm = new (class {
|
||||
constructor() {
|
||||
this.MAX_FILE_SIZE_BYTES = 10 * 1000000;
|
||||
this.initOnDOMLoaded();
|
||||
|
@ -10,60 +10,42 @@ let BookWyrm = new class {
|
|||
}
|
||||
|
||||
initEventListeners() {
|
||||
document.querySelectorAll('[data-controls]')
|
||||
.forEach(button => button.addEventListener(
|
||||
'click',
|
||||
this.toggleAction.bind(this))
|
||||
document
|
||||
.querySelectorAll("[data-controls]")
|
||||
.forEach((button) => button.addEventListener("click", this.toggleAction.bind(this)));
|
||||
|
||||
document
|
||||
.querySelectorAll(".interaction")
|
||||
.forEach((button) => button.addEventListener("submit", this.interact.bind(this)));
|
||||
|
||||
document
|
||||
.querySelectorAll(".hidden-form input")
|
||||
.forEach((button) => button.addEventListener("change", this.revealForm.bind(this)));
|
||||
|
||||
document
|
||||
.querySelectorAll("[data-hides]")
|
||||
.forEach((button) => button.addEventListener("change", this.hideForm.bind(this)));
|
||||
|
||||
document
|
||||
.querySelectorAll("[data-back]")
|
||||
.forEach((button) => button.addEventListener("click", this.back));
|
||||
|
||||
document
|
||||
.querySelectorAll('input[type="file"]')
|
||||
.forEach((node) => node.addEventListener("change", this.disableIfTooLarge.bind(this)));
|
||||
|
||||
document
|
||||
.querySelectorAll("button[data-modal-open]")
|
||||
.forEach((node) => node.addEventListener("click", this.handleModalButton.bind(this)));
|
||||
|
||||
document
|
||||
.querySelectorAll("[data-duplicate]")
|
||||
.forEach((node) => node.addEventListener("click", this.duplicateInput.bind(this)));
|
||||
document
|
||||
.querySelectorAll("details.dropdown")
|
||||
.forEach((node) =>
|
||||
node.addEventListener("toggle", this.handleDetailsDropdown.bind(this))
|
||||
);
|
||||
|
||||
document.querySelectorAll('.interaction')
|
||||
.forEach(button => button.addEventListener(
|
||||
'submit',
|
||||
this.interact.bind(this))
|
||||
);
|
||||
|
||||
document.querySelectorAll('.hidden-form input')
|
||||
.forEach(button => button.addEventListener(
|
||||
'change',
|
||||
this.revealForm.bind(this))
|
||||
);
|
||||
|
||||
document.querySelectorAll('[data-hides]')
|
||||
.forEach(button => button.addEventListener(
|
||||
'change',
|
||||
this.hideForm.bind(this))
|
||||
);
|
||||
|
||||
document.querySelectorAll('[data-back]')
|
||||
.forEach(button => button.addEventListener(
|
||||
'click',
|
||||
this.back)
|
||||
);
|
||||
|
||||
document.querySelectorAll('input[type="file"]')
|
||||
.forEach(node => node.addEventListener(
|
||||
'change',
|
||||
this.disableIfTooLarge.bind(this)
|
||||
));
|
||||
|
||||
document.querySelectorAll('button[data-modal-open]')
|
||||
.forEach(node => node.addEventListener(
|
||||
'click',
|
||||
this.handleModalButton.bind(this)
|
||||
));
|
||||
|
||||
document.querySelectorAll('[data-duplicate]')
|
||||
.forEach(node => node.addEventListener(
|
||||
'click',
|
||||
this.duplicateInput.bind(this)
|
||||
|
||||
))
|
||||
document.querySelectorAll('details.dropdown')
|
||||
.forEach(node => node.addEventListener(
|
||||
'toggle',
|
||||
this.handleDetailsDropdown.bind(this)
|
||||
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,15 +54,12 @@ let BookWyrm = new class {
|
|||
initOnDOMLoaded() {
|
||||
const bookwyrm = this;
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.tab-group')
|
||||
.forEach(tabs => new TabGroup(tabs));
|
||||
document.querySelectorAll('input[type="file"]').forEach(
|
||||
bookwyrm.disableIfTooLarge.bind(bookwyrm)
|
||||
);
|
||||
document.querySelectorAll('[data-copytext]').forEach(
|
||||
bookwyrm.copyText.bind(bookwyrm)
|
||||
);
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
document.querySelectorAll(".tab-group").forEach((tabs) => new TabGroup(tabs));
|
||||
document
|
||||
.querySelectorAll('input[type="file"]')
|
||||
.forEach(bookwyrm.disableIfTooLarge.bind(bookwyrm));
|
||||
document.querySelectorAll("[data-copytext]").forEach(bookwyrm.copyText.bind(bookwyrm));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -89,8 +68,7 @@ let BookWyrm = new class {
|
|||
*/
|
||||
initReccuringTasks() {
|
||||
// Polling
|
||||
document.querySelectorAll('[data-poll]')
|
||||
.forEach(liveArea => this.polling(liveArea));
|
||||
document.querySelectorAll("[data-poll]").forEach((liveArea) => this.polling(liveArea));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,15 +94,19 @@ let BookWyrm = new class {
|
|||
const bookwyrm = this;
|
||||
|
||||
delay = delay || 10000;
|
||||
delay += (Math.random() * 1000);
|
||||
delay += Math.random() * 1000;
|
||||
|
||||
setTimeout(function() {
|
||||
fetch('/api/updates/' + counter.dataset.poll)
|
||||
.then(response => response.json())
|
||||
.then(data => bookwyrm.updateCountElement(counter, data));
|
||||
setTimeout(
|
||||
function () {
|
||||
fetch("/api/updates/" + counter.dataset.poll)
|
||||
.then((response) => response.json())
|
||||
.then((data) => bookwyrm.updateCountElement(counter, data));
|
||||
|
||||
bookwyrm.polling(counter, delay * 1.25);
|
||||
}, delay, counter);
|
||||
bookwyrm.polling(counter, delay * 1.25);
|
||||
},
|
||||
delay,
|
||||
counter
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -139,60 +121,56 @@ let BookWyrm = new class {
|
|||
const count_by_type = data.count_by_type;
|
||||
const currentCount = counter.innerText;
|
||||
const hasMentions = data.has_mentions;
|
||||
const allowedStatusTypesEl = document.getElementById('unread-notifications-wrapper');
|
||||
const allowedStatusTypesEl = document.getElementById("unread-notifications-wrapper");
|
||||
|
||||
// If we're on the right counter element
|
||||
if (counter.closest('[data-poll-wrapper]').contains(allowedStatusTypesEl)) {
|
||||
if (counter.closest("[data-poll-wrapper]").contains(allowedStatusTypesEl)) {
|
||||
const allowedStatusTypes = JSON.parse(allowedStatusTypesEl.textContent);
|
||||
|
||||
// For keys in common between allowedStatusTypes and count_by_type
|
||||
// This concerns 'review', 'quotation', 'comment'
|
||||
count = allowedStatusTypes.reduce(function(prev, currentKey) {
|
||||
count = allowedStatusTypes.reduce(function (prev, currentKey) {
|
||||
const currentValue = count_by_type[currentKey] | 0;
|
||||
|
||||
return prev + currentValue;
|
||||
}, 0);
|
||||
|
||||
// Add all the "other" in count_by_type if 'everything' is allowed
|
||||
if (allowedStatusTypes.includes('everything')) {
|
||||
if (allowedStatusTypes.includes("everything")) {
|
||||
// Clone count_by_type with 0 for reviews/quotations/comments
|
||||
const count_by_everything_else = Object.assign(
|
||||
{},
|
||||
count_by_type,
|
||||
{review: 0, quotation: 0, comment: 0}
|
||||
);
|
||||
const count_by_everything_else = Object.assign({}, count_by_type, {
|
||||
review: 0,
|
||||
quotation: 0,
|
||||
comment: 0,
|
||||
});
|
||||
|
||||
count = Object.keys(count_by_everything_else).reduce(
|
||||
function(prev, currentKey) {
|
||||
const currentValue =
|
||||
count_by_everything_else[currentKey] | 0
|
||||
count = Object.keys(count_by_everything_else).reduce(function (prev, currentKey) {
|
||||
const currentValue = count_by_everything_else[currentKey] | 0;
|
||||
|
||||
return prev + currentValue;
|
||||
},
|
||||
count
|
||||
);
|
||||
return prev + currentValue;
|
||||
}, count);
|
||||
}
|
||||
}
|
||||
|
||||
if (count != currentCount) {
|
||||
this.addRemoveClass(counter.closest('[data-poll-wrapper]'), 'is-hidden', count < 1);
|
||||
this.addRemoveClass(counter.closest("[data-poll-wrapper]"), "is-hidden", count < 1);
|
||||
counter.innerText = count;
|
||||
this.addRemoveClass(counter.closest('[data-poll-wrapper]'), 'is-danger', hasMentions);
|
||||
this.addRemoveClass(counter.closest("[data-poll-wrapper]"), "is-danger", hasMentions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show form.
|
||||
*
|
||||
*
|
||||
* @param {Event} event
|
||||
* @return {undefined}
|
||||
*/
|
||||
revealForm(event) {
|
||||
let trigger = event.currentTarget;
|
||||
let hidden = trigger.closest('.hidden-form').querySelectorAll('.is-hidden')[0];
|
||||
let hidden = trigger.closest(".hidden-form").querySelectorAll(".is-hidden")[0];
|
||||
|
||||
if (hidden) {
|
||||
this.addRemoveClass(hidden, 'is-hidden', !hidden);
|
||||
this.addRemoveClass(hidden, "is-hidden", !hidden);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,10 +182,10 @@ let BookWyrm = new class {
|
|||
*/
|
||||
hideForm(event) {
|
||||
let trigger = event.currentTarget;
|
||||
let targetId = trigger.dataset.hides
|
||||
let visible = document.getElementById(targetId)
|
||||
let targetId = trigger.dataset.hides;
|
||||
let visible = document.getElementById(targetId);
|
||||
|
||||
this.addRemoveClass(visible, 'is-hidden', true);
|
||||
this.addRemoveClass(visible, "is-hidden", true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,31 +200,34 @@ let BookWyrm = new class {
|
|||
if (!trigger.dataset.allowDefault || event.currentTarget == event.target) {
|
||||
event.preventDefault();
|
||||
}
|
||||
let pressed = trigger.getAttribute('aria-pressed') === 'false';
|
||||
let pressed = trigger.getAttribute("aria-pressed") === "false";
|
||||
let targetId = trigger.dataset.controls;
|
||||
|
||||
// Toggle pressed status on all triggers controlling the same target.
|
||||
document.querySelectorAll('[data-controls="' + targetId + '"]')
|
||||
.forEach(otherTrigger => otherTrigger.setAttribute(
|
||||
'aria-pressed',
|
||||
otherTrigger.getAttribute('aria-pressed') === 'false'
|
||||
));
|
||||
document
|
||||
.querySelectorAll('[data-controls="' + targetId + '"]')
|
||||
.forEach((otherTrigger) =>
|
||||
otherTrigger.setAttribute(
|
||||
"aria-pressed",
|
||||
otherTrigger.getAttribute("aria-pressed") === "false"
|
||||
)
|
||||
);
|
||||
|
||||
// @todo Find a better way to handle the exception.
|
||||
if (targetId && ! trigger.classList.contains('pulldown-menu')) {
|
||||
if (targetId && !trigger.classList.contains("pulldown-menu")) {
|
||||
let target = document.getElementById(targetId);
|
||||
|
||||
this.addRemoveClass(target, 'is-hidden', !pressed);
|
||||
this.addRemoveClass(target, 'is-active', pressed);
|
||||
this.addRemoveClass(target, "is-hidden", !pressed);
|
||||
this.addRemoveClass(target, "is-active", pressed);
|
||||
}
|
||||
|
||||
// Show/hide pulldown-menus.
|
||||
if (trigger.classList.contains('pulldown-menu')) {
|
||||
if (trigger.classList.contains("pulldown-menu")) {
|
||||
this.toggleMenu(trigger, targetId);
|
||||
}
|
||||
|
||||
// Show/hide container.
|
||||
let container = document.getElementById('hide_' + targetId);
|
||||
let container = document.getElementById("hide_" + targetId);
|
||||
|
||||
if (container) {
|
||||
this.toggleContainer(container, pressed);
|
||||
|
@ -283,14 +264,14 @@ let BookWyrm = new class {
|
|||
* @return {undefined}
|
||||
*/
|
||||
toggleMenu(trigger, targetId) {
|
||||
let expanded = trigger.getAttribute('aria-expanded') == 'false';
|
||||
let expanded = trigger.getAttribute("aria-expanded") == "false";
|
||||
|
||||
trigger.setAttribute('aria-expanded', expanded);
|
||||
trigger.setAttribute("aria-expanded", expanded);
|
||||
|
||||
if (targetId) {
|
||||
let target = document.getElementById(targetId);
|
||||
|
||||
this.addRemoveClass(target, 'is-active', expanded);
|
||||
this.addRemoveClass(target, "is-active", expanded);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,7 +283,7 @@ let BookWyrm = new class {
|
|||
* @return {undefined}
|
||||
*/
|
||||
toggleContainer(container, pressed) {
|
||||
this.addRemoveClass(container, 'is-hidden', pressed);
|
||||
this.addRemoveClass(container, "is-hidden", pressed);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -339,7 +320,7 @@ let BookWyrm = new class {
|
|||
|
||||
node.focus();
|
||||
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
node.selectionStart = node.selectionEnd = 10000;
|
||||
}, 0);
|
||||
}
|
||||
|
@ -359,15 +340,17 @@ let BookWyrm = new class {
|
|||
const relatedforms = document.querySelectorAll(`.${form.dataset.id}`);
|
||||
|
||||
// Toggle class on all related forms.
|
||||
relatedforms.forEach(relatedForm => bookwyrm.addRemoveClass(
|
||||
relatedForm,
|
||||
'is-hidden',
|
||||
relatedForm.className.indexOf('is-hidden') == -1
|
||||
));
|
||||
relatedforms.forEach((relatedForm) =>
|
||||
bookwyrm.addRemoveClass(
|
||||
relatedForm,
|
||||
"is-hidden",
|
||||
relatedForm.className.indexOf("is-hidden") == -1
|
||||
)
|
||||
);
|
||||
|
||||
this.ajaxPost(form).catch(error => {
|
||||
this.ajaxPost(form).catch((error) => {
|
||||
// @todo Display a notification in the UI instead.
|
||||
console.warn('Request failed:', error);
|
||||
console.warn("Request failed:", error);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -379,11 +362,11 @@ let BookWyrm = new class {
|
|||
*/
|
||||
ajaxPost(form) {
|
||||
return fetch(form.action, {
|
||||
method : "POST",
|
||||
method: "POST",
|
||||
body: new FormData(form),
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
Accept: "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -408,21 +391,16 @@ let BookWyrm = new class {
|
|||
const element = eventOrElement.currentTarget || eventOrElement;
|
||||
|
||||
const submits = element.form.querySelectorAll('[type="submit"]');
|
||||
const warns = element.parentElement.querySelectorAll('.file-too-big');
|
||||
const isTooBig = element.files &&
|
||||
element.files[0] &&
|
||||
element.files[0].size > MAX_FILE_SIZE_BYTES;
|
||||
const warns = element.parentElement.querySelectorAll(".file-too-big");
|
||||
const isTooBig =
|
||||
element.files && element.files[0] && element.files[0].size > MAX_FILE_SIZE_BYTES;
|
||||
|
||||
if (isTooBig) {
|
||||
submits.forEach(submitter => submitter.disabled = true);
|
||||
warns.forEach(
|
||||
sib => addRemoveClass(sib, 'is-hidden', false)
|
||||
);
|
||||
submits.forEach((submitter) => (submitter.disabled = true));
|
||||
warns.forEach((sib) => addRemoveClass(sib, "is-hidden", false));
|
||||
} else {
|
||||
submits.forEach(submitter => submitter.disabled = false);
|
||||
warns.forEach(
|
||||
sib => addRemoveClass(sib, 'is-hidden', true)
|
||||
);
|
||||
submits.forEach((submitter) => (submitter.disabled = false));
|
||||
warns.forEach((sib) => addRemoveClass(sib, "is-hidden", true));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -440,79 +418,75 @@ let BookWyrm = new class {
|
|||
handleModalButton(event) {
|
||||
const modalButton = event.currentTarget;
|
||||
const targetModalId = modalButton.dataset.modalOpen;
|
||||
const htmlElement = document.querySelector('html');
|
||||
const htmlElement = document.querySelector("html");
|
||||
const modal = document.getElementById(targetModalId);
|
||||
|
||||
|
||||
if (!modal) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function handleModalOpen(modalElement) {
|
||||
htmlElement.classList.add('is-clipped');
|
||||
modalElement.classList.add('is-active');
|
||||
modalElement.getElementsByClassName('modal-card')[0].focus();
|
||||
htmlElement.classList.add("is-clipped");
|
||||
modalElement.classList.add("is-active");
|
||||
modalElement.getElementsByClassName("modal-card")[0].focus();
|
||||
|
||||
const closeButtons = modalElement.querySelectorAll("[data-modal-close]");
|
||||
|
||||
|
||||
closeButtons.forEach((button) => {
|
||||
button.addEventListener(
|
||||
'click',
|
||||
function() { handleModalClose(modalElement) }
|
||||
);
|
||||
button.addEventListener("click", function () {
|
||||
handleModalClose(modalElement);
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener(
|
||||
'keydown',
|
||||
function(event) {
|
||||
if (event.key === 'Escape') {
|
||||
handleModalClose(modalElement);
|
||||
}
|
||||
document.addEventListener("keydown", function (event) {
|
||||
if (event.key === "Escape") {
|
||||
handleModalClose(modalElement);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
modalElement.addEventListener('keydown', handleFocusTrap)
|
||||
modalElement.addEventListener("keydown", handleFocusTrap);
|
||||
}
|
||||
|
||||
function handleModalClose(modalElement) {
|
||||
modalElement.removeEventListener('keydown', handleFocusTrap)
|
||||
htmlElement.classList.remove('is-clipped');
|
||||
modalElement.classList.remove('is-active');
|
||||
modalElement.removeEventListener("keydown", handleFocusTrap);
|
||||
htmlElement.classList.remove("is-clipped");
|
||||
modalElement.classList.remove("is-active");
|
||||
modalButton.focus();
|
||||
}
|
||||
|
||||
function handleFocusTrap(event) {
|
||||
if (event.key !== 'Tab') {
|
||||
return;
|
||||
if (event.key !== "Tab") {
|
||||
return;
|
||||
}
|
||||
|
||||
const focusableEls = event.currentTarget.querySelectorAll(
|
||||
[
|
||||
'a[href]:not([disabled])',
|
||||
'button:not([disabled])',
|
||||
'textarea:not([disabled])',
|
||||
"a[href]:not([disabled])",
|
||||
"button:not([disabled])",
|
||||
"textarea:not([disabled])",
|
||||
'input:not([type="hidden"]):not([disabled])',
|
||||
'select:not([disabled])',
|
||||
'details:not([disabled])',
|
||||
'[tabindex]:not([tabindex="-1"]):not([disabled])'
|
||||
].join(',')
|
||||
"select:not([disabled])",
|
||||
"details:not([disabled])",
|
||||
'[tabindex]:not([tabindex="-1"]):not([disabled])',
|
||||
].join(",")
|
||||
);
|
||||
const firstFocusableEl = focusableEls[0];
|
||||
const firstFocusableEl = focusableEls[0];
|
||||
const lastFocusableEl = focusableEls[focusableEls.length - 1];
|
||||
|
||||
if (event.shiftKey ) /* Shift + tab */ {
|
||||
if (document.activeElement === firstFocusableEl) {
|
||||
if (event.shiftKey) {
|
||||
/* Shift + tab */ if (document.activeElement === firstFocusableEl) {
|
||||
lastFocusableEl.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
} else /* Tab */ {
|
||||
} /* Tab */ else {
|
||||
if (document.activeElement === lastFocusableEl) {
|
||||
firstFocusableEl.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Open modal
|
||||
handleModalOpen(modal);
|
||||
}
|
||||
|
@ -525,31 +499,27 @@ let BookWyrm = new class {
|
|||
* @return {undefined}
|
||||
*/
|
||||
displayPopUp(url, windowName) {
|
||||
window.open(
|
||||
url,
|
||||
windowName,
|
||||
"left=100,top=100,width=430,height=600"
|
||||
);
|
||||
window.open(url, windowName, "left=100,top=100,width=430,height=600");
|
||||
}
|
||||
|
||||
duplicateInput (event ) {
|
||||
duplicateInput(event) {
|
||||
const trigger = event.currentTarget;
|
||||
const input_id = trigger.dataset['duplicate']
|
||||
const input_id = trigger.dataset["duplicate"];
|
||||
const orig = document.getElementById(input_id);
|
||||
const parent = orig.parentNode;
|
||||
const new_count = parent.querySelectorAll("input").length + 1
|
||||
const new_count = parent.querySelectorAll("input").length + 1;
|
||||
|
||||
let input = orig.cloneNode();
|
||||
|
||||
input.id += ("-" + (new_count))
|
||||
input.value = ""
|
||||
input.id += "-" + new_count;
|
||||
input.value = "";
|
||||
|
||||
let label = parent.querySelector("label").cloneNode();
|
||||
|
||||
label.setAttribute("for", input.id)
|
||||
label.setAttribute("for", input.id);
|
||||
|
||||
parent.appendChild(label)
|
||||
parent.appendChild(input)
|
||||
parent.appendChild(label);
|
||||
parent.appendChild(input);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -564,24 +534,19 @@ let BookWyrm = new class {
|
|||
copyText(textareaEl) {
|
||||
const text = textareaEl.textContent;
|
||||
|
||||
const copyButtonEl = document.createElement('button');
|
||||
const copyButtonEl = document.createElement("button");
|
||||
|
||||
copyButtonEl.textContent = textareaEl.dataset.copytextLabel;
|
||||
copyButtonEl.classList.add(
|
||||
"button",
|
||||
"is-small",
|
||||
"is-primary",
|
||||
"is-light"
|
||||
);
|
||||
copyButtonEl.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
textareaEl.classList.add('is-success');
|
||||
copyButtonEl.classList.replace('is-primary', 'is-success');
|
||||
copyButtonEl.classList.add("button", "is-small", "is-primary", "is-light");
|
||||
copyButtonEl.addEventListener("click", () => {
|
||||
navigator.clipboard.writeText(text).then(function () {
|
||||
textareaEl.classList.add("is-success");
|
||||
copyButtonEl.classList.replace("is-primary", "is-success");
|
||||
copyButtonEl.textContent = textareaEl.dataset.copytextSuccess;
|
||||
});
|
||||
});
|
||||
|
||||
textareaEl.parentNode.appendChild(copyButtonEl)
|
||||
textareaEl.parentNode.appendChild(copyButtonEl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -593,41 +558,41 @@ let BookWyrm = new class {
|
|||
*/
|
||||
handleDetailsDropdown(event) {
|
||||
const detailsElement = event.target;
|
||||
const summaryElement = detailsElement.querySelector('summary');
|
||||
const menuElement = detailsElement.querySelector('.dropdown-menu');
|
||||
const htmlElement = document.querySelector('html');
|
||||
const summaryElement = detailsElement.querySelector("summary");
|
||||
const menuElement = detailsElement.querySelector(".dropdown-menu");
|
||||
const htmlElement = document.querySelector("html");
|
||||
|
||||
if (detailsElement.open) {
|
||||
// Focus first menu element
|
||||
menuElement.querySelectorAll(
|
||||
'a[href]:not([disabled]), button:not([disabled])'
|
||||
)[0].focus();
|
||||
|
||||
menuElement
|
||||
.querySelectorAll("a[href]:not([disabled]), button:not([disabled])")[0]
|
||||
.focus();
|
||||
|
||||
// Enable focus trap
|
||||
menuElement.addEventListener('keydown', this.handleFocusTrap);
|
||||
|
||||
menuElement.addEventListener("keydown", this.handleFocusTrap);
|
||||
|
||||
// Close on Esc
|
||||
detailsElement.addEventListener('keydown', handleEscKey);
|
||||
detailsElement.addEventListener("keydown", handleEscKey);
|
||||
|
||||
// Clip page if Mobile
|
||||
if (this.isMobile()) {
|
||||
htmlElement.classList.add('is-clipped');
|
||||
htmlElement.classList.add("is-clipped");
|
||||
}
|
||||
} else {
|
||||
summaryElement.focus();
|
||||
|
||||
|
||||
// Disable focus trap
|
||||
menuElement.removeEventListener('keydown', this.handleFocusTrap);
|
||||
menuElement.removeEventListener("keydown", this.handleFocusTrap);
|
||||
|
||||
// Unclip page
|
||||
if (this.isMobile()) {
|
||||
htmlElement.classList.remove('is-clipped');
|
||||
htmlElement.classList.remove("is-clipped");
|
||||
}
|
||||
}
|
||||
|
||||
function handleEscKey(event) {
|
||||
if (event.key !== 'Escape') {
|
||||
return;
|
||||
if (event.key !== "Escape") {
|
||||
return;
|
||||
}
|
||||
|
||||
summaryElement.click();
|
||||
|
@ -650,34 +615,34 @@ let BookWyrm = new class {
|
|||
* @return {undefined}
|
||||
*/
|
||||
handleFocusTrap(event) {
|
||||
if (event.key !== 'Tab') {
|
||||
return;
|
||||
if (event.key !== "Tab") {
|
||||
return;
|
||||
}
|
||||
|
||||
const focusableEls = event.currentTarget.querySelectorAll(
|
||||
[
|
||||
'a[href]:not([disabled])',
|
||||
'button:not([disabled])',
|
||||
'textarea:not([disabled])',
|
||||
"a[href]:not([disabled])",
|
||||
"button:not([disabled])",
|
||||
"textarea:not([disabled])",
|
||||
'input:not([type="hidden"]):not([disabled])',
|
||||
'select:not([disabled])',
|
||||
'details:not([disabled])',
|
||||
'[tabindex]:not([tabindex="-1"]):not([disabled])'
|
||||
].join(',')
|
||||
"select:not([disabled])",
|
||||
"details:not([disabled])",
|
||||
'[tabindex]:not([tabindex="-1"]):not([disabled])',
|
||||
].join(",")
|
||||
);
|
||||
const firstFocusableEl = focusableEls[0];
|
||||
const firstFocusableEl = focusableEls[0];
|
||||
const lastFocusableEl = focusableEls[focusableEls.length - 1];
|
||||
|
||||
if (event.shiftKey ) /* Shift + tab */ {
|
||||
if (document.activeElement === firstFocusableEl) {
|
||||
if (event.shiftKey) {
|
||||
/* Shift + tab */ if (document.activeElement === firstFocusableEl) {
|
||||
lastFocusableEl.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
} else /* Tab */ {
|
||||
} /* Tab */ else {
|
||||
if (document.activeElement === lastFocusableEl) {
|
||||
firstFocusableEl.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
}();
|
||||
})();
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
/* exported LocalStorageTools */
|
||||
/* globals BookWyrm */
|
||||
|
||||
let LocalStorageTools = new class {
|
||||
let LocalStorageTools = new (class {
|
||||
constructor() {
|
||||
document.querySelectorAll('[data-hide]')
|
||||
.forEach(t => this.setDisplay(t));
|
||||
document.querySelectorAll("[data-hide]").forEach((t) => this.setDisplay(t));
|
||||
|
||||
document.querySelectorAll('.set-display')
|
||||
.forEach(t => t.addEventListener('click', this.updateDisplay.bind(this)));
|
||||
document
|
||||
.querySelectorAll(".set-display")
|
||||
.forEach((t) => t.addEventListener("click", this.updateDisplay.bind(this)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,8 +23,9 @@ let LocalStorageTools = new class {
|
|||
|
||||
window.localStorage.setItem(key, value);
|
||||
|
||||
document.querySelectorAll('[data-hide="' + key + '"]')
|
||||
.forEach(node => this.setDisplay(node));
|
||||
document
|
||||
.querySelectorAll('[data-hide="' + key + '"]')
|
||||
.forEach((node) => this.setDisplay(node));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,6 +39,6 @@ let LocalStorageTools = new class {
|
|||
let key = node.dataset.hide;
|
||||
let value = window.localStorage.getItem(key);
|
||||
|
||||
BookWyrm.addRemoveClass(node, 'is-hidden', value);
|
||||
BookWyrm.addRemoveClass(node, "is-hidden", value);
|
||||
}
|
||||
}();
|
||||
})();
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
/* exported StatusCache */
|
||||
/* globals BookWyrm */
|
||||
|
||||
let StatusCache = new class {
|
||||
let StatusCache = new (class {
|
||||
constructor() {
|
||||
document.querySelectorAll('[data-cache-draft]')
|
||||
.forEach(t => t.addEventListener('change', this.updateDraft.bind(this)));
|
||||
document
|
||||
.querySelectorAll("[data-cache-draft]")
|
||||
.forEach((t) => t.addEventListener("change", this.updateDraft.bind(this)));
|
||||
|
||||
document.querySelectorAll('[data-cache-draft]')
|
||||
.forEach(t => this.populateDraft(t));
|
||||
document.querySelectorAll("[data-cache-draft]").forEach((t) => this.populateDraft(t));
|
||||
|
||||
document.querySelectorAll('.submit-status')
|
||||
.forEach(button => button.addEventListener(
|
||||
'submit',
|
||||
this.submitStatus.bind(this))
|
||||
);
|
||||
document
|
||||
.querySelectorAll(".submit-status")
|
||||
.forEach((button) => button.addEventListener("submit", this.submitStatus.bind(this)));
|
||||
|
||||
document.querySelectorAll('.form-rate-stars label.icon')
|
||||
.forEach(button => button.addEventListener('click', this.toggleStar.bind(this)));
|
||||
document
|
||||
.querySelectorAll(".form-rate-stars label.icon")
|
||||
.forEach((button) => button.addEventListener("click", this.toggleStar.bind(this)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,25 +79,26 @@ let StatusCache = new class {
|
|||
|
||||
event.preventDefault();
|
||||
|
||||
BookWyrm.addRemoveClass(form, 'is-processing', true);
|
||||
trigger.setAttribute('disabled', null);
|
||||
BookWyrm.addRemoveClass(form, "is-processing", true);
|
||||
trigger.setAttribute("disabled", null);
|
||||
|
||||
BookWyrm.ajaxPost(form).finally(() => {
|
||||
// Change icon to remove ongoing activity on the current UI.
|
||||
// Enable back the element used to submit the form.
|
||||
BookWyrm.addRemoveClass(form, 'is-processing', false);
|
||||
trigger.removeAttribute('disabled');
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error();
|
||||
}
|
||||
this.submitStatusSuccess(form);
|
||||
})
|
||||
.catch(error => {
|
||||
console.warn(error);
|
||||
this.announceMessage('status-error-message');
|
||||
});
|
||||
BookWyrm.ajaxPost(form)
|
||||
.finally(() => {
|
||||
// Change icon to remove ongoing activity on the current UI.
|
||||
// Enable back the element used to submit the form.
|
||||
BookWyrm.addRemoveClass(form, "is-processing", false);
|
||||
trigger.removeAttribute("disabled");
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error();
|
||||
}
|
||||
this.submitStatusSuccess(form);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn(error);
|
||||
this.announceMessage("status-error-message");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,12 +112,16 @@ let StatusCache = new class {
|
|||
let copy = element.cloneNode(true);
|
||||
|
||||
copy.id = null;
|
||||
element.insertAdjacentElement('beforebegin', copy);
|
||||
element.insertAdjacentElement("beforebegin", copy);
|
||||
|
||||
BookWyrm.addRemoveClass(copy, 'is-hidden', false);
|
||||
setTimeout(function() {
|
||||
copy.remove();
|
||||
}, 10000, copy);
|
||||
BookWyrm.addRemoveClass(copy, "is-hidden", false);
|
||||
setTimeout(
|
||||
function () {
|
||||
copy.remove();
|
||||
},
|
||||
10000,
|
||||
copy
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,8 +135,9 @@ let StatusCache = new class {
|
|||
form.reset();
|
||||
|
||||
// Clear localstorage
|
||||
form.querySelectorAll('[data-cache-draft]')
|
||||
.forEach(node => window.localStorage.removeItem(node.dataset.cacheDraft));
|
||||
form.querySelectorAll("[data-cache-draft]").forEach((node) =>
|
||||
window.localStorage.removeItem(node.dataset.cacheDraft)
|
||||
);
|
||||
|
||||
// Close modals
|
||||
let modal = form.closest(".modal.is-active");
|
||||
|
@ -142,8 +147,11 @@ let StatusCache = new class {
|
|||
|
||||
// Update shelve buttons
|
||||
if (form.reading_status) {
|
||||
document.querySelectorAll("[data-shelve-button-book='" + form.book.value +"']")
|
||||
.forEach(button => this.cycleShelveButtons(button, form.reading_status.value));
|
||||
document
|
||||
.querySelectorAll("[data-shelve-button-book='" + form.book.value + "']")
|
||||
.forEach((button) =>
|
||||
this.cycleShelveButtons(button, form.reading_status.value)
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -156,7 +164,7 @@ let StatusCache = new class {
|
|||
document.querySelector("[data-controls=" + reply.id + "]").click();
|
||||
}
|
||||
|
||||
this.announceMessage('status-success-message');
|
||||
this.announceMessage("status-success-message");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,8 +180,9 @@ let StatusCache = new class {
|
|||
let next_identifier = shelf.dataset.shelfNext;
|
||||
|
||||
// Set all buttons to hidden
|
||||
button.querySelectorAll("[data-shelf-identifier]")
|
||||
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", true));
|
||||
button
|
||||
.querySelectorAll("[data-shelf-identifier]")
|
||||
.forEach((item) => BookWyrm.addRemoveClass(item, "is-hidden", true));
|
||||
|
||||
// Button that should be visible now
|
||||
let next = button.querySelector("[data-shelf-identifier=" + next_identifier + "]");
|
||||
|
@ -183,15 +192,17 @@ let StatusCache = new class {
|
|||
|
||||
// ------ update the dropdown buttons
|
||||
// Remove existing hidden class
|
||||
button.querySelectorAll("[data-shelf-dropdown-identifier]")
|
||||
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", false));
|
||||
button
|
||||
.querySelectorAll("[data-shelf-dropdown-identifier]")
|
||||
.forEach((item) => BookWyrm.addRemoveClass(item, "is-hidden", false));
|
||||
|
||||
// Remove existing disabled states
|
||||
|
||||
button.querySelectorAll("[data-shelf-dropdown-identifier] button")
|
||||
.forEach(item => item.disabled = false);
|
||||
button
|
||||
.querySelectorAll("[data-shelf-dropdown-identifier] button")
|
||||
.forEach((item) => (item.disabled = false));
|
||||
|
||||
next_identifier = next_identifier == 'complete' ? 'read' : next_identifier;
|
||||
next_identifier = next_identifier == "complete" ? "read" : next_identifier;
|
||||
|
||||
// Disable the current state
|
||||
button.querySelector(
|
||||
|
@ -206,8 +217,9 @@ let StatusCache = new class {
|
|||
BookWyrm.addRemoveClass(main_button, "is-hidden", true);
|
||||
|
||||
// Just hide the other two menu options, idk what to do with them
|
||||
button.querySelectorAll("[data-extra-options]")
|
||||
.forEach(item => BookWyrm.addRemoveClass(item, "is-hidden", true));
|
||||
button
|
||||
.querySelectorAll("[data-extra-options]")
|
||||
.forEach((item) => BookWyrm.addRemoveClass(item, "is-hidden", true));
|
||||
|
||||
// Close menu
|
||||
let menu = button.querySelector("details[open]");
|
||||
|
@ -235,5 +247,4 @@ let StatusCache = new class {
|
|||
halfStar.checked = "checked";
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
})();
|
||||
|
|
3
bw-dev
3
bw-dev
|
@ -130,6 +130,9 @@ case "$CMD" in
|
|||
black)
|
||||
makeitblack
|
||||
;;
|
||||
prettier)
|
||||
npx prettier --write bookwyrm/static/js/*.js
|
||||
;;
|
||||
populate_streams)
|
||||
runweb python manage.py populate_streams "$@"
|
||||
;;
|
||||
|
|
|
@ -20,6 +20,7 @@ update_locales
|
|||
build
|
||||
clean
|
||||
black
|
||||
prettier
|
||||
populate_streams
|
||||
populate_suggestions
|
||||
generate_thumbnails
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.23.0",
|
||||
"prettier": "2.5.1",
|
||||
"stylelint": "^13.12.0",
|
||||
"stylelint-config-standard": "^21.0.0",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"watch": "^1.0.2"
|
||||
"watch": "^0.13.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"merge": "2.1.1",
|
||||
|
|
Loading…
Reference in a new issue