From 2f2f7db0865972401ea6a092bb82c3032007d19f Mon Sep 17 00:00:00 2001 From: Fabien Basmaison Date: Tue, 6 Apr 2021 09:44:59 +0200 Subject: [PATCH] [assets] Use classes for JS files: - Classes strictly parse code implicitly. - Fix previously updated code. --- bookwyrm/static/js/bookwyrm.js | 304 +++++++++++++++-------------- bookwyrm/static/js/localstorage.js | 54 ++--- 2 files changed, 187 insertions(+), 171 deletions(-) diff --git a/bookwyrm/static/js/bookwyrm.js b/bookwyrm/static/js/bookwyrm.js index 7d542747c..0ac33e0f1 100644 --- a/bookwyrm/static/js/bookwyrm.js +++ b/bookwyrm/static/js/bookwyrm.js @@ -1,156 +1,168 @@ +/* exported BookWyrm */ /* globals TabGroup */ -// set up javascript listeners -window.onload = function() { - // buttons that display or hide content - document.querySelectorAll('[data-controls]') - .forEach(t => t.onclick = toggleAction); - - // javascript interactions (boost/fav) - document.querySelectorAll('.interaction') - .forEach(t => t.onsubmit = interact); - - // handle aria settings on menus - document.querySelectorAll('.pulldown-menu') - .forEach(t => t.onclick = toggleMenu); - - // hidden submit button in a form - document.querySelectorAll('.hidden-form input') - .forEach(t => t.onchange = revealForm); - - // polling - document.querySelectorAll('[data-poll]') - .forEach(el => polling(el)); - - // browser back behavior - document.querySelectorAll('[data-back]') - .forEach(t => t.onclick = back); - - document.querySelectorAll('.tab-group') - .forEach(t => new TabGroup(t)); -}; - -function back(e) { - e.preventDefault(); - history.back(); -} - -function polling(el, delay) { - delay = delay || 10000; - delay += (Math.random() * 1000); - setTimeout(function() { - fetch('/api/updates/' + el.getAttribute('data-poll')) - .then(response => response.json()) - .then(data => updateCountElement(el, data)); - polling(el, delay * 1.25); - }, delay, el); -} - -function updateCountElement(el, data) { - const currentCount = el.innerText; - const count = data.count; - if (count != currentCount) { - addRemoveClass(el.closest('[data-poll-wrapper]'), 'hidden', count < 1); - el.innerText = count; - } -} - - -function revealForm(e) { - var hidden = e.currentTarget.closest('.hidden-form').getElementsByClassName('hidden')[0]; - if (hidden) { - removeClass(hidden, 'hidden'); - } -} - - -function toggleAction(e) { - var el = e.currentTarget; - var pressed = el.getAttribute('aria-pressed') == 'false'; - - var targetId = el.getAttribute('data-controls'); - document.querySelectorAll('[data-controls="' + targetId + '"]') - .forEach(t => t.setAttribute('aria-pressed', (t.getAttribute('aria-pressed') == 'false'))); - - if (targetId) { - var target = document.getElementById(targetId); - addRemoveClass(target, 'hidden', !pressed); - addRemoveClass(target, 'is-active', pressed); +let BookWyrm = new class { + constructor() { + this.initOnLoad(); + this.initEventListeners(); } - // show/hide container - var container = document.getElementById('hide-' + targetId); - if (container) { - addRemoveClass(container, 'hidden', pressed); + initEventListeners() { + // buttons that display or hide content + document.querySelectorAll('[data-controls]') + .forEach(t => t.onclick = this.toggleAction.bind(this)); + + // javascript interactions (boost/fav) + document.querySelectorAll('.interaction') + .forEach(t => t.onsubmit = this.interact.bind(this)); + + // handle aria settings on menus + document.querySelectorAll('.pulldown-menu') + .forEach(t => t.onclick = this.toggleMenu.bind(this)); + + // hidden submit button in a form + document.querySelectorAll('.hidden-form input') + .forEach(t => t.onchange = this.revealForm.bind(this)); + + // polling + document.querySelectorAll('[data-poll]') + .forEach(el => this.polling(el)); + + // browser back behavior + document.querySelectorAll('[data-back]') + .forEach(t => t.onclick = this.back); } - // set checkbox, if appropriate - var checkbox = el.getAttribute('data-controls-checkbox'); - if (checkbox) { - document.getElementById(checkbox).checked = !!pressed; + initOnLoad(){ + // set up javascript listeners + window.onload = function() { + document.querySelectorAll('.tab-group') + .forEach(t => new TabGroup(t)); + }; } - // set focus, if appropriate - var focus = el.getAttribute('data-focus-target'); - if (focus) { - var focusEl = document.getElementById(focus); - focusEl.focus(); - setTimeout(function(){ focusEl.selectionStart = focusEl.selectionEnd = 10000; }, 0); + back(e) { + e.preventDefault(); + history.back(); + } + + polling(el, delay) { + let poller = this; + delay = delay || 10000; + delay += (Math.random() * 1000); + setTimeout(function() { + fetch('/api/updates/' + el.getAttribute('data-poll')) + .then(response => response.json()) + .then(data => poller.updateCountElement(el, data)); + poller.polling(el, delay * 1.25); + }, delay, el); + } + + updateCountElement(el, data) { + const currentCount = el.innerText; + const count = data.count; + if (count != currentCount) { + this.addRemoveClass(el.closest('[data-poll-wrapper]'), 'hidden', count < 1); + el.innerText = count; + } + } + + revealForm(e) { + var hidden = e.currentTarget.closest('.hidden-form').getElementsByClassName('hidden')[0]; + if (hidden) { + this.removeClass(hidden, 'hidden'); + } + } + + toggleAction(e) { + var el = e.currentTarget; + var pressed = el.getAttribute('aria-pressed') == 'false'; + + var targetId = el.getAttribute('data-controls'); + document.querySelectorAll('[data-controls="' + targetId + '"]') + .forEach(t => t.setAttribute('aria-pressed', (t.getAttribute('aria-pressed') == 'false'))); + + if (targetId) { + var target = document.getElementById(targetId); + this.addRemoveClass(target, 'hidden', !pressed); + this.addRemoveClass(target, 'is-active', pressed); + } + + // show/hide container + var container = document.getElementById('hide-' + targetId); + if (container) { + this.addRemoveClass(container, 'hidden', pressed); + } + + // set checkbox, if appropriate + var checkbox = el.getAttribute('data-controls-checkbox'); + if (checkbox) { + document.getElementById(checkbox).checked = !!pressed; + } + + // set focus, if appropriate + var focus = el.getAttribute('data-focus-target'); + if (focus) { + var focusEl = document.getElementById(focus); + focusEl.focus(); + setTimeout(function(){ focusEl.selectionStart = focusEl.selectionEnd = 10000; }, 0); + } + } + + // @todo Only update status if the promise is successful. + interact(e) { + e.preventDefault(); + this.ajaxPost(e.target); + var identifier = e.target.getAttribute('data-id'); + + // @todo This probably should be done with IDs. + document.querySelectorAll(`.${identifier}`) + .forEach(t => this.addRemoveClass(t, 'hidden', t.className.indexOf('hidden') == -1)); + } + + toggleMenu(e) { + var el = e.currentTarget; + var expanded = el.getAttribute('aria-expanded') == 'false'; + el.setAttribute('aria-expanded', expanded); + var targetId = el.getAttribute('data-controls'); + if (targetId) { + var target = document.getElementById(targetId); + this.addRemoveClass(target, 'is-active', expanded); + } + } + + ajaxPost(form) { + fetch(form.action, { + method : "POST", + body: new FormData(form) + }); + } + + addRemoveClass(el, classname, bool) { + if (bool) { + this.addClass(el, classname); + } else { + this.removeClass(el, classname); + } + } + + addClass(el, classname) { + var classes = el.className.split(' '); + if (classes.indexOf(classname) > -1) { + return; + } + el.className = classes.concat(classname).join(' '); + } + + removeClass(el, className) { + var classes = []; + if (el.className) { + classes = el.className.split(' '); + } + const idx = classes.indexOf(className); + if (idx > -1) { + classes.splice(idx, 1); + } + el.className = classes.join(' '); } } - -function interact(e) { - e.preventDefault(); - ajaxPost(e.target); - var identifier = e.target.getAttribute('data-id'); - // @todo This should be `querySelector`, unless there are duplicated IDs, - // which is a problem in itself. - document.querySelectorAll(`#${identifier}`) - .forEach(t => addRemoveClass(t, 'hidden', t.className.indexOf('hidden') == -1)); -} - -function toggleMenu(e) { - var el = e.currentTarget; - var expanded = el.getAttribute('aria-expanded') == 'false'; - el.setAttribute('aria-expanded', expanded); - var targetId = el.getAttribute('data-controls'); - if (targetId) { - var target = document.getElementById(targetId); - addRemoveClass(target, 'is-active', expanded); - } -} - -function ajaxPost(form) { - fetch(form.action, { - method : "POST", - body: new FormData(form) - }); -} - -function addRemoveClass(el, classname, bool) { - if (bool) { - addClass(el, classname); - } else { - removeClass(el, classname); - } -} - -function addClass(el, classname) { - var classes = el.className.split(' '); - if (classes.indexOf(classname) > -1) { - return; - } - el.className = classes.concat(classname).join(' '); -} - -function removeClass(el, className) { - var classes = []; - if (el.className) { - classes = el.className.split(' '); - } - const idx = classes.indexOf(className); - if (idx > -1) { - classes.splice(idx, 1); - } - el.className = classes.join(' '); -} diff --git a/bookwyrm/static/js/localstorage.js b/bookwyrm/static/js/localstorage.js index 514d8748e..b8ef02cd8 100644 --- a/bookwyrm/static/js/localstorage.js +++ b/bookwyrm/static/js/localstorage.js @@ -1,28 +1,32 @@ -/* exported updateDisplay */ -/* globals addRemoveClass */ +/* exported LocalStorageTools */ +/* globals BookWyrm */ -// set javascript listeners -function updateDisplay(e) { - // used in set reading goal - var key = e.target.getAttribute('data-id'); - var value = e.target.getAttribute('data-value'); - window.localStorage.setItem(key, value); +let LocalStorageTools = new class { + constructor() { + // display based on localstorage vars + document.querySelectorAll('[data-hide]') + .forEach(t => this.setDisplay(t)); - document.querySelectorAll('[data-hide="' + key + '"]') - .forEach(t => setDisplay(t)); + // update localstorage + document.querySelectorAll('.set-display') + .forEach(t => t.onclick = this.updateDisplay.bind(this)); + } + + // set javascript listeners + updateDisplay(e) { + // used in set reading goal + var key = e.target.getAttribute('data-id'); + var value = e.target.getAttribute('data-value'); + window.localStorage.setItem(key, value); + + document.querySelectorAll('[data-hide="' + key + '"]') + .forEach(t => this.setDisplay(t)); + } + + setDisplay(el) { + // used in set reading goal + var key = el.getAttribute('data-hide'); + var value = window.localStorage.getItem(key); + BookWyrm.addRemoveClass(el, 'hidden', value); + } } - -function setDisplay(el) { - // used in set reading goal - var key = el.getAttribute('data-hide'); - var value = window.localStorage.getItem(key); - addRemoveClass(el, 'hidden', value); -} - -// display based on localstorage vars -document.querySelectorAll('[data-hide]') - .forEach(t => setDisplay(t)); - -// update localstorage -document.querySelectorAll('.set-display') - .forEach(t => t.onclick = updateDisplay);