diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..4af5f2ccf --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +*.min.js +node_modules/ +p/scripts/vendor/ diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..a528c8f59 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "env": { + "browser": true + }, + "extends": [ + "eslint:recommended", + "standard" + ], + "rules": { + "camelcase": "off", + "comma-dangle": ["warn", "always-multiline"], + "eqeqeq": "off", + "indent": ["warn", "tab", { "SwitchCase": 1 }], + "linebreak-style": ["error", "unix"], + "max-len": ["warn", 165], + "no-tabs": "off", + "semi": ["warn", "always"], + "space-before-function-paren": ["warn", { + "anonymous": "always", + "named": "never", + "asyncArrow": "always" + }], + "yoda": "off" + }, + "root": true +} diff --git a/.gitignore b/.gitignore index 6228619ed..c6ef860c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /bin /node_modules -package*.json +package-lock.json constants.local.php # Temp files diff --git a/.jshintrc b/.jshintrc index 07d282b1c..36d4e3ff3 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,8 +1,9 @@ { - "esversion" : 6, + "esversion" : 8, "browser" : true, "globals": { "confirm": true, "console": true - } + }, + "strict": "global" } diff --git a/.stylelintrc b/.stylelintrc index c8efe2ca9..b14138d3a 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -33,11 +33,9 @@ "no-eol-whitespace": true, "property-no-vendor-prefix": true, "rule-empty-line-before": [ - "always", - "except": [ - "after-single-line-comment", - "first-nested" - ] + "always", { + "except": ["after-single-line-comment","first-nested"] + } ], "order/properties-order": [ "margin", diff --git a/.travis.yml b/.travis.yml index 9a8bbac0a..015c84a04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,12 +45,9 @@ jobs: env: - HADOLINT="$HOME/hadolint" install: - - npm install --save-dev jshint stylelint stylelint-order stylelint-scss stylelint-config-recommended-scss + - npm install - curl -sL -o ${HADOLINT} "https://github.com/hadolint/hadolint/releases/download/v1.18.0/hadolint-$(uname -s)-$(uname -m)" && chmod 700 ${HADOLINT} script: - - node_modules/jshint/bin/jshint . - # check SCSS separately - - stylelint --syntax scss "**/*.scss" - - stylelint "**/*.css" + - npm test - bash tests/shellchecks.sh - git ls-files --exclude='*Dockerfile*' --ignored | xargs --max-lines=1 "$HADOLINT" diff --git a/p/scripts/api.js b/p/scripts/api.js index f9dcabf01..ce52c50ad 100644 --- a/p/scripts/api.js +++ b/p/scripts/api.js @@ -1,6 +1,5 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; -/* jshint esversion:6, strict:global */ +'use strict'; function check(url, next) { if (!url || !next) { @@ -10,8 +9,8 @@ function check(url, next) { req.open('GET', url, true); req.setRequestHeader('Authorization', 'GoogleLogin auth=test/1'); req.onerror = function (e) { - next('FAIL: HTTP ' + e); - }; + next('FAIL: HTTP ' + e); + }; req.onload = function () { if (this.status == 200) { next(this.response); @@ -25,40 +24,40 @@ function check(url, next) { const jsonVars = JSON.parse(document.getElementById('jsonVars').innerHTML); check(jsonVars.greader + '/check/compatibility', function next(result1) { - const greaderOutput = document.getElementById('greaderOutput'); - if (result1 === 'PASS') { - greaderOutput.innerHTML = '✔️ ' + result1; - } else { - check(jsonVars.greader + '/check%2Fcompatibility', function next(result2) { - if (result2 === 'PASS') { - greaderOutput.innerHTML = '⚠️ WARN: no %2F support, so some clients will not work!'; - } else { - check('./greader.php/check/compatibility', function next(result3) { - if (result3 === 'PASS') { - greaderOutput.innerHTML = '⚠️ WARN: Probable invalid base URL in ./data/config.php'; - } else { - greaderOutput.innerHTML = '❌ ' + result1; - } - }); - } - }); - } - }); - -check(jsonVars.fever + '?api', function next(result1) { - const feverOutput = document.getElementById('feverOutput'); - try { - JSON.parse(result1); - feverOutput.innerHTML = '✔️ PASS'; - } catch (ex) { - check('./fever.php?api', function next(result2) { - try { - JSON.parse(result2); - feverOutput.innerHTML = '⚠️ WARN: Probable invalid base URL in ./data/config.php'; - } catch (ex) { - feverOutput.innerHTML = '❌ ' + result1; + const greaderOutput = document.getElementById('greaderOutput'); + if (result1 === 'PASS') { + greaderOutput.innerHTML = '✔️ ' + result1; + } else { + check(jsonVars.greader + '/check%2Fcompatibility', function next(result2) { + if (result2 === 'PASS') { + greaderOutput.innerHTML = '⚠️ WARN: no %2F support, so some clients will not work!'; + } else { + check('./greader.php/check/compatibility', function next(result3) { + if (result3 === 'PASS') { + greaderOutput.innerHTML = '⚠️ WARN: Probable invalid base URL in ./data/config.php'; + } else { + greaderOutput.innerHTML = '❌ ' + result1; } }); - } - }); + } + }); + } +}); + +check(jsonVars.fever + '?api', function next(result1) { + const feverOutput = document.getElementById('feverOutput'); + try { + JSON.parse(result1); + feverOutput.innerHTML = '✔️ PASS'; + } catch (ex) { + check('./fever.php?api', function next(result2) { + try { + JSON.parse(result2); + feverOutput.innerHTML = '⚠️ WARN: Probable invalid base URL in ./data/config.php'; + } catch (ex) { + feverOutput.innerHTML = '❌ ' + result1; + } + }); + } +}); // @license-end diff --git a/p/scripts/category.js b/p/scripts/category.js index 2a24bc47d..e75c04571 100644 --- a/p/scripts/category.js +++ b/p/scripts/category.js @@ -1,16 +1,15 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; +'use strict'; /* globals context */ -/* jshint esversion:6, strict:global */ -var loading = false, - dnd_successful = false; +let loading = false; +let dnd_successful = false; function dragend_process(t) { t.setAttribute('draggable', 'false'); if (loading) { - setTimeout(function() { + setTimeout(function () { dragend_process(t); }, 50); return; @@ -25,13 +24,14 @@ function dragend_process(t) { t.remove(); if (p.childElementCount <= 1) { - p.insertAdjacentHTML('afterbegin', '
  • ' + context.i18n.category_empty + '
  • '); + p.insertAdjacentHTML('afterbegin', + '
  • ' + context.i18n.category_empty + '
  • '); } } } -var dragFeedId = '', - dragHtml = ''; +let dragFeedId = ''; +let dragHtml = ''; function init_draggable() { if (!window.context) { @@ -42,99 +42,99 @@ function init_draggable() { return; } - const draggable = '[draggable="true"]', - dropzone = '[dropzone="move"]', - dropSection = document.querySelector('.drop-section'); + const draggable = '[draggable="true"]'; + const dropzone = '[dropzone="move"]'; + const dropSection = document.querySelector('.drop-section'); - dropSection.ondragstart = function(ev) { - const li = ev.target.closest ? ev.target.closest(draggable) : null; - if (li) { - const drag = ev.target.closest('[draggable]'); - ev.dataTransfer.effectAllowed = 'move'; - dragHtml = drag.outerHTML; - dragFeedId = drag.getAttribute('data-feed-id'); - ev.dataTransfer.setData('text', dragFeedId); - drag.style.opacity = 0.3; - dnd_successful = false; - } - }; + dropSection.ondragstart = function (ev) { + const li = ev.target.closest ? ev.target.closest(draggable) : null; + if (li) { + const drag = ev.target.closest('[draggable]'); + ev.dataTransfer.effectAllowed = 'move'; + dragHtml = drag.outerHTML; + dragFeedId = drag.getAttribute('data-feed-id'); + ev.dataTransfer.setData('text', dragFeedId); + drag.style.opacity = 0.3; + dnd_successful = false; + } + }; - dropSection.ondragend = function(ev) { - const li = ev.target.closest ? ev.target.closest(draggable) : null; - if (li) { - dragend_process(li); - } - }; + dropSection.ondragend = function (ev) { + const li = ev.target.closest ? ev.target.closest(draggable) : null; + if (li) { + dragend_process(li); + } + }; - dropSection.ondragenter = function(ev) { - const li = ev.target.closest ? ev.target.closest(dropzone) : null; - if (li) { - li.classList.add('drag-hover'); - return false; - } - }; + dropSection.ondragenter = function (ev) { + const li = ev.target.closest ? ev.target.closest(dropzone) : null; + if (li) { + li.classList.add('drag-hover'); + return false; + } + }; - dropSection.onddragleave = function(ev) { - const li = ev.target.closest ? ev.target.closest(dropzone) : null; - if (li) { - const scroll_top = document.documentElement.scrollTop, - top = li.offsetTop, - left = li.offsetLeft, - right = left + li.clientWidth, - bottom = top + li.clientHeight, - mouse_x = ev.screenX, - mouse_y = ev.clientY + scroll_top; + dropSection.onddragleave = function (ev) { + const li = ev.target.closest ? ev.target.closest(dropzone) : null; + if (li) { + const scroll_top = document.documentElement.scrollTop; + const top = li.offsetTop; + const left = li.offsetLeft; + const right = left + li.clientWidth; + const bottom = top + li.clientHeight; + const mouse_x = ev.screenX; + const mouse_y = ev.clientY + scroll_top; - if (left <= mouse_x && mouse_x <= right && + if (left <= mouse_x && mouse_x <= right && top <= mouse_y && mouse_y <= bottom) { - // HACK because dragleave is triggered when hovering children! - return; + // HACK because dragleave is triggered when hovering children! + return; + } + li.classList.remove('drag-hover'); + } + }; + + dropSection.ondragover = function (ev) { + const li = ev.target.closest ? ev.target.closest(dropzone) : null; + if (li) { + ev.dataTransfer.dropEffect = 'move'; + return false; + } + }; + + dropSection.ondrop = function (ev) { + const li = ev.target.closest ? ev.target.closest(dropzone) : null; + if (li) { + loading = true; + + const req = new XMLHttpRequest(); + req.open('POST', './?c=feed&a=move', true); + req.responseType = 'json'; + req.onload = function (e) { + if (this.status == 200) { + li.insertAdjacentHTML('afterend', dragHtml); + if (li.classList.contains('disabled')) { + li.remove(); + } + dnd_successful = true; } - li.classList.remove('drag-hover'); - } - }; + }; + req.onloadend = function (e) { + loading = false; + dragFeedId = ''; + dragHtml = ''; + }; + req.setRequestHeader('Content-Type', 'application/json'); + req.send(JSON.stringify({ + f_id: dragFeedId, + c_id: li.parentElement.getAttribute('data-cat-id'), + _csrf: context.csrf, + })); - dropSection.ondragover = function(ev) { - const li = ev.target.closest ? ev.target.closest(dropzone) : null; - if (li) { - ev.dataTransfer.dropEffect = "move"; - return false; - } - }; - - dropSection.ondrop = function(ev) { - const li = ev.target.closest ? ev.target.closest(dropzone) : null; - if (li) { - loading = true; - - const req = new XMLHttpRequest(); - req.open('POST', './?c=feed&a=move', true); - req.responseType = 'json'; - req.onload = function (e) { - if (this.status == 200) { - li.insertAdjacentHTML('afterend', dragHtml); - if (li.classList.contains('disabled')) { - li.remove(); - } - dnd_successful = true; - } - }; - req.onloadend = function (e) { - loading = false; - dragFeedId = ''; - dragHtml = ''; - }; - req.setRequestHeader('Content-Type', 'application/json'); - req.send(JSON.stringify({ - f_id: dragFeedId, - c_id: li.parentElement.getAttribute('data-cat-id'), - _csrf: context.csrf, - })); - - li.classList.remove('drag-hover'); - return false; - } - }; + li.classList.remove('drag-hover'); + return false; + } + }; } function archiving() { @@ -143,7 +143,7 @@ function archiving() { if (e.target.id === 'use_default_purge_options') { slider.querySelectorAll('.archiving').forEach(function (element) { element.hidden = e.target.checked; - if (!e.target.checked) element.style.visibility = 'visible'; //Help for Edge 44 + if (!e.target.checked) element.style.visibility = 'visible'; // Help for Edge 44 }); } }); diff --git a/p/scripts/draggable.js b/p/scripts/draggable.js index d9d1d1cc0..8b98346f6 100644 --- a/p/scripts/draggable.js +++ b/p/scripts/draggable.js @@ -1,8 +1,7 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; -/* jshint esversion:6, strict:global */ +'use strict'; -const init_draggable_list = function() { +const init_draggable_list = function () { if (!window.context) { if (window.console) { console.log('FreshRSS draggable list waiting for JS…'); diff --git a/p/scripts/extra.js b/p/scripts/extra.js index a78b5b10d..3aa1b6d46 100644 --- a/p/scripts/extra.js +++ b/p/scripts/extra.js @@ -1,7 +1,6 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; +'use strict'; /* globals context, openNotification, openPopupWithSource, xmlHttpRequestJson */ -/* jshint esversion:6, strict:global */ function fix_popup_preview_selector() { const link = document.getElementById('popup-preview-selector'); @@ -20,8 +19,8 @@ function fix_popup_preview_selector() { }); } -// -function poormanSalt() { //If crypto.getRandomValues is not available +// +function poormanSalt() { // If crypto.getRandomValues is not available const base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789/abcdefghijklmnopqrstuvwxyz'; let text = '$2a$04$'; for (let i = 22; i > 0; i--) { @@ -61,8 +60,8 @@ function init_crypto_form() { const req = new XMLHttpRequest(); req.open('GET', './?c=javascript&a=nonce&user=' + document.getElementById('username').value, false); req.onerror = function () { - openNotification('Communication error!', 'bad'); - }; + openNotification('Communication error!', 'bad'); + }; req.send(); if (req.status == 200) { const json = xmlHttpRequestJson(req); @@ -70,9 +69,9 @@ function init_crypto_form() { openNotification('Invalid user!', 'bad'); } else { try { - const strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'), - s = dcodeIO.bcrypt.hashSync(document.getElementById('passwordPlain').value, json.salt1), - c = dcodeIO.bcrypt.hashSync(json.nonce + s, strong ? dcodeIO.bcrypt.genSaltSync(4) : poormanSalt()); + const strong = window.Uint32Array && window.crypto && (typeof window.crypto.getRandomValues === 'function'); + const s = dcodeIO.bcrypt.hashSync(document.getElementById('passwordPlain').value, json.salt1); + const c = dcodeIO.bcrypt.hashSync(json.nonce + s, strong ? dcodeIO.bcrypt.genSaltSync(4) : poormanSalt()); document.getElementById('challenge').value = c; if (!s || !c) { openNotification('Crypto error!', 'bad'); @@ -91,83 +90,83 @@ function init_crypto_form() { return success; }; } -// +// function init_password_observers() { document.querySelectorAll('.toggle-password').forEach(function (a) { - a.onmousedown = function (ev) { - const passwordField = document.getElementById(this.getAttribute('data-toggle')); - passwordField.setAttribute('type', 'text'); - this.classList.add('active'); - return false; - }; - a.onmouseup = function (ev) { - const passwordField = document.getElementById(this.getAttribute('data-toggle')); - passwordField.setAttribute('type', 'password'); - this.classList.remove('active'); - return false; - }; - }); + a.onmousedown = function (ev) { + const passwordField = document.getElementById(this.getAttribute('data-toggle')); + passwordField.setAttribute('type', 'text'); + this.classList.add('active'); + return false; + }; + a.onmouseup = function (ev) { + const passwordField = document.getElementById(this.getAttribute('data-toggle')); + passwordField.setAttribute('type', 'password'); + this.classList.remove('active'); + return false; + }; + }); } function init_select_observers() { document.querySelectorAll('.select-change').forEach(function (s) { - s.onchange = function (ev) { - const opt = s.options[s.selectedIndex], - url = opt.getAttribute('data-url'); - if (url) { - s.disabled = true; - s.value = ''; - if (s.form) { - s.form.querySelectorAll('[type=submit]').forEach(function (b) { - b.disabled = true; - }); - } - location.href = url; - } - }; - }); + s.onchange = function (ev) { + const opt = s.options[s.selectedIndex]; + const url = opt.getAttribute('data-url'); + if (url) { + s.disabled = true; + s.value = ''; + if (s.form) { + s.form.querySelectorAll('[type=submit]').forEach(function (b) { + b.disabled = true; + }); + } + location.href = url; + } + }; + }); } function init_slider_observers() { - const slider = document.getElementById('slider'), - closer = document.getElementById('close-slider'); + const slider = document.getElementById('slider'); + const closer = document.getElementById('close-slider'); if (!slider) { return; } document.querySelector('.post').onclick = function (ev) { - const a = ev.target.closest('.open-slider'); - if (a) { - if (!context.ajax_loading) { - context.ajax_loading = true; + const a = ev.target.closest('.open-slider'); + if (a) { + if (!context.ajax_loading) { + context.ajax_loading = true; - const req = new XMLHttpRequest(); - req.open('GET', a.href + '&ajax=1', true); - req.responseType = 'document'; - req.onload = function (e) { - slider.innerHTML = this.response.body.innerHTML; - slider.classList.add('active'); - closer.classList.add('active'); - context.ajax_loading = false; - fix_popup_preview_selector(); - }; - req.send(); - return false; - } - } - }; - - closer.onclick = function (ev) { - if (data_leave_validation() || confirm(context.i18n.confirmation_default)) { - slider.querySelectorAll('form').forEach(function (f) { f.reset(); }); - closer.classList.remove('active'); - slider.classList.remove('active'); - return true; - } else { + const req = new XMLHttpRequest(); + req.open('GET', a.href + '&ajax=1', true); + req.responseType = 'document'; + req.onload = function (e) { + slider.innerHTML = this.response.body.innerHTML; + slider.classList.add('active'); + closer.classList.add('active'); + context.ajax_loading = false; + fix_popup_preview_selector(); + }; + req.send(); return false; } - }; + } + }; + + closer.onclick = function (ev) { + if (data_leave_validation() || confirm(context.i18n.confirmation_default)) { + slider.querySelectorAll('form').forEach(function (f) { f.reset(); }); + closer.classList.remove('active'); + slider.classList.remove('active'); + return true; + } else { + return false; + } + }; } function data_leave_validation() { @@ -187,16 +186,16 @@ function data_leave_validation() { function init_configuration_alert() { window.onsubmit = function (e) { - window.hasSubmit = true; - }; + window.hasSubmit = true; + }; window.onbeforeunload = function (e) { - if (window.hasSubmit) { - return; - } - if (!data_leave_validation()) { - return false; - } - }; + if (window.hasSubmit) { + return; + } + if (!data_leave_validation()) { + return false; + } + }; } function init_extra() { @@ -204,7 +203,7 @@ function init_extra() { if (window.console) { console.log('FreshRSS extra waiting for JS…'); } - window.setTimeout(init_extra, 50); //Wait for all js to be loaded + window.setTimeout(init_extra, 50); // Wait for all js to be loaded return; } init_crypto_form(); @@ -219,10 +218,10 @@ if (document.readyState && document.readyState !== 'loading') { init_extra(); } else { document.addEventListener('DOMContentLoaded', function () { - if (window.console) { - console.log('FreshRSS extra waiting for DOMContentLoaded…'); - } - init_extra(); - }, false); + if (window.console) { + console.log('FreshRSS extra waiting for DOMContentLoaded…'); + } + init_extra(); + }, false); } // @license-end diff --git a/p/scripts/global_view.js b/p/scripts/global_view.js index e9515be6a..e3a07382a 100644 --- a/p/scripts/global_view.js +++ b/p/scripts/global_view.js @@ -1,9 +1,8 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; +'use strict'; /* globals context, init_load_more, init_posts, init_stream */ -/* jshint esversion:6, strict:global */ -var panel_loading = false; +let panel_loading = false; function load_panel(link) { if (panel_loading) { @@ -16,63 +15,63 @@ function load_panel(link) { req.open('GET', link, true); req.responseType = 'document'; req.onload = function (e) { - if (this.status != 200) { - return; + if (this.status != 200) { + return; + } + const html = this.response; + const foreign = html.querySelectorAll('.nav_menu, #stream .day, #stream .flux, #stream .pagination, #stream.prompt'); + const panel = document.getElementById('panel'); + foreign.forEach(function (el) { + panel.appendChild(document.adoptNode(el)); + }); + panel.querySelectorAll('.nav_menu > :not([id="nav_menu_read_all"])').forEach(function (el) { + el.remove(); + }); + + init_load_more(panel); + init_posts(); + + document.getElementById('overlay').classList.add('visible'); + panel.classList.add('visible'); + + // force le démarrage du scroll en haut. + // Sans ça, si l'on scroll en lisant une catégorie par exemple, + // en en ouvrant une autre ensuite, on se retrouve au même point de scroll + panel.scrollTop = 0; + document.documentElement.scrollTop = 0; + + // We already have a click listener in main.js + panel.addEventListener('click', function (ev) { + const b = ev.target.closest('#nav_menu_read_all button, #bigMarkAsRead'); + if (b) { + console.log(b.formAction); + + const req2 = new XMLHttpRequest(); + req2.open('POST', b.formAction, false); + req2.setRequestHeader('Content-Type', 'application/json'); + req2.send(JSON.stringify({ + _csrf: context.csrf, + })); + if (req2.status == 200) { + location.reload(false); + return false; + } } - const html = this.response, - foreign = html.querySelectorAll('.nav_menu, #stream .day, #stream .flux, #stream .pagination, #stream.prompt'), - panel = document.getElementById('panel'); - foreign.forEach(function (el) { - panel.appendChild(document.adoptNode(el)); - }); - panel.querySelectorAll('.nav_menu > :not([id="nav_menu_read_all"])').forEach(function (el) { - el.remove(); - }); + }); - init_load_more(panel); - init_posts(); - - document.getElementById('overlay').classList.add('visible'); - panel.classList.add('visible'); - - // force le démarrage du scroll en haut. - // Sans ça, si l'on scroll en lisant une catégorie par exemple, - // en en ouvrant une autre ensuite, on se retrouve au même point de scroll - panel.scrollTop = 0; - document.documentElement.scrollTop = 0; - - //We already have a click listener in main.js - panel.addEventListener('click', function (ev) { - const b = ev.target.closest('#nav_menu_read_all button, #bigMarkAsRead'); - if (b) { - console.log(b.formAction); - - const req2 = new XMLHttpRequest(); - req2.open('POST', b.formAction, false); - req2.setRequestHeader('Content-Type', 'application/json'); - req2.send(JSON.stringify({ - _csrf: context.csrf, - })); - if (req2.status == 200) { - location.reload(false); - return false; - } - } - }); - - panel_loading = false; - }; + panel_loading = false; + }; req.send(); } function init_close_panel() { const panel = document.getElementById('panel'); document.querySelector('#overlay .close').onclick = function (ev) { - panel.innerHTML = ''; - panel.classList.remove('visible'); - document.getElementById('overlay').classList.remove('visible'); - return false; - }; + panel.innerHTML = ''; + panel.classList.remove('visible'); + document.getElementById('overlay').classList.remove('visible'); + return false; + }; document.addEventListener('keydown', ev => { const k = (ev.key.trim() || ev.code).toUpperCase(); if (k === 'ESCAPE' || k === 'ESC') { @@ -85,15 +84,15 @@ function init_close_panel() { function init_global_view() { // TODO: should be based on generic classes document.querySelectorAll('.box a').forEach(function (a) { - a.onclick = function (ev) { - load_panel(a.href); - return false; - }; - }); + a.onclick = function (ev) { + load_panel(a.href); + return false; + }; + }); document.querySelectorAll('.nav_menu #nav_menu_read_all, .nav_menu .toggle_aside').forEach(function (el) { - el.remove(); - }); + el.remove(); + }); const panel = document.getElementById('panel'); init_stream(panel); @@ -104,7 +103,7 @@ function init_all_global_view() { if (window.console) { console.log('FreshRSS Global view waiting for JS…'); } - window.setTimeout(init_all_global_view, 50); //Wait for all js to be loaded + window.setTimeout(init_all_global_view, 50); // Wait for all js to be loaded return; } init_global_view(); diff --git a/p/scripts/install.js b/p/scripts/install.js index 448f70b56..7c802531e 100644 --- a/p/scripts/install.js +++ b/p/scripts/install.js @@ -1,6 +1,5 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; -/* jshint esversion:6, strict:global */ +'use strict'; function show_password(ev) { const button = ev.currentTarget; @@ -13,7 +12,7 @@ function hide_password(ev) { const button = ev.currentTarget; const passwordField = document.getElementById(button.getAttribute('data-toggle')); passwordField.setAttribute('type', 'password'); - button.className = button.className.replace(/(?:^|\s)active(?!\S)/g , ''); + button.className = button.className.replace(/(?:^|\s)active(?!\S)/g, ''); return false; } const toggles = document.getElementsByClassName('toggle-password'); @@ -25,8 +24,8 @@ for (let i = 0; i < toggles.length; i++) { const auth_type = document.getElementById('auth_type'); function auth_type_change() { if (auth_type) { - const auth_value = auth_type.value, - password_input = document.getElementById('passwordPlain'); + const auth_value = auth_type.value; + const password_input = document.getElementById('passwordPlain'); if (auth_value === 'form') { password_input.required = true; diff --git a/p/scripts/integration.js b/p/scripts/integration.js index bf9ac1ca6..acd629573 100644 --- a/p/scripts/integration.js +++ b/p/scripts/integration.js @@ -1,8 +1,7 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; -/* jshint esversion:6, strict:global */ +'use strict'; -const init_integration = function() { +const init_integration = function () { if (!window.context) { if (window.console) { console.log('FreshRSS integration waiting for JS…'); diff --git a/p/scripts/main.js b/p/scripts/main.js index f44692103..13a49060b 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -1,12 +1,14 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; -/* jshint esversion:6, strict:global */ +'use strict'; -// +// if (!document.scrollingElement) document.scrollingElement = document.documentElement; if (!NodeList.prototype.forEach) NodeList.prototype.forEach = Array.prototype.forEach; -if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.webkitMatchesSelector; -if (!Element.prototype.closest) Element.prototype.closest = function (s) { +if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.webkitMatchesSelector; +} +if (!Element.prototype.closest) { + Element.prototype.closest = function (s) { let el = this; do { if (el.matches(s)) return el; @@ -14,13 +16,14 @@ if (!Element.prototype.closest) Element.prototype.closest = function (s) { } while (el); return null; }; +} if (!Element.prototype.remove) Element.prototype.remove = function () { if (this.parentNode) this.parentNode.removeChild(this); }; -// +// -// +// function xmlHttpRequestJson(req) { let json = req.response; - if (req.responseType !== 'json') { //IE11 + if (req.responseType !== 'json') { // IE11 try { json = JSON.parse(req.responseText); } catch (ex) { @@ -29,14 +32,14 @@ function xmlHttpRequestJson(req) { } return json; } -// +// -// -var context; +// +let context; (function parseJsonVars() { - const jsonVars = document.getElementById('jsonVars'), - json = JSON.parse(jsonVars.innerHTML); + const jsonVars = document.getElementById('jsonVars'); + const json = JSON.parse(jsonVars.innerHTML); jsonVars.outerHTML = ''; context = json.context; context.ajax_loading = false; @@ -48,7 +51,7 @@ var context; context.icons.unread = decodeURIComponent(context.icons.unread); context.extensions = json.extensions; }()); -// +// function badAjax(reload) { openNotification(context.i18n.notif_request_failed, 'bad'); @@ -59,11 +62,13 @@ function badAjax(reload) { } function needsScroll(elem) { - const winBottom = document.scrollingElement.scrollTop + document.scrollingElement.clientHeight, - elemTop = elem.offsetParent.offsetTop + elem.offsetTop, - elemBottom = elemTop + elem.offsetHeight; - return (elemTop < document.scrollingElement.scrollTop || elemBottom > winBottom) ? - elemTop - (document.scrollingElement.clientHeight / 2) : 0; + const winBottom = document.scrollingElement.scrollTop + document.scrollingElement.clientHeight; + const elemTop = elem.offsetParent.offsetTop + elem.offsetTop; + const elemBottom = elemTop + elem.offsetHeight; + if (elemTop < document.scrollingElement.scrollTop || elemBottom > winBottom) { + return elemTop - (document.scrollingElement.clientHeight / 2); + } + return 0; } function str2int(str) { @@ -79,9 +84,9 @@ function numberFormat(nStr) { } // http://www.mredkj.com/javascript/numberFormat.html nStr += ''; - const x = nStr.split('.'), - x2 = x.length > 1 ? '.' + x[1] : '', - rgx = /(\d+)(\d{3})/; + const x = nStr.split('.'); + const x2 = x.length > 1 ? '.' + x[1] : ''; + const rgx = /(\d+)(\d{3})/; let x1 = x[0]; while (rgx.test(x1)) { x1 = x1.replace(rgx, '$1 $2'); @@ -95,10 +100,10 @@ function incLabel(p, inc, spaceAfter) { } function incUnreadsFeed(article, feed_id, nb) { - //Update unread: feed - let elem = document.getElementById(feed_id), - feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0, - feed_priority = elem ? str2int(elem.getAttribute('data-priority')) : 0; + // Update unread: feed + let elem = document.getElementById(feed_id); + let feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; + const feed_priority = elem ? str2int(elem.getAttribute('data-priority')) : 0; if (elem) { elem.setAttribute('data-unread', feed_unreads + nb); elem = elem.querySelector('.item-title'); @@ -107,7 +112,7 @@ function incUnreadsFeed(article, feed_id, nb) { } } - //Update unread: category + // Update unread: category elem = document.getElementById(feed_id).closest('.category'); feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; if (elem) { @@ -118,7 +123,7 @@ function incUnreadsFeed(article, feed_id, nb) { } } - //Update unread: all + // Update unread: all if (feed_priority > 0) { elem = document.querySelector('#aside_feed .all .title'); if (elem) { @@ -127,7 +132,7 @@ function incUnreadsFeed(article, feed_id, nb) { } } - //Update unread: favourites + // Update unread: favourites if (article && article.closest('div').classList.contains('favorite')) { elem = document.querySelector('#aside_feed .favorites .title'); if (elem) { @@ -138,7 +143,7 @@ function incUnreadsFeed(article, feed_id, nb) { let isCurrentView = false; // Update unread: title - document.title = document.title.replace(/^((?:\([ 0-9]+\) )?)/, function (m, p1) { + document.title = document.title.replace(/^((?:\([\s0-9]+\) )?)/, function (m, p1) { const feed = document.getElementById(feed_id); if (article || feed.closest('.active')) { isCurrentView = true; @@ -156,13 +161,13 @@ function incUnreadsFeed(article, feed_id, nb) { function incUnreadsTag(tag_id, nb) { let t = document.getElementById(tag_id); if (t) { - let unreads = str2int(t.getAttribute('data-unread')); + const unreads = str2int(t.getAttribute('data-unread')); t.setAttribute('data-unread', unreads + nb); t.querySelector('.item-title').setAttribute('data-unread', numberFormat(unreads + nb)); } t = document.querySelector('.category.tags .title'); if (t) { - let unreads = str2int(t.getAttribute('data-unread')); + const unreads = str2int(t.getAttribute('data-unread')); t.setAttribute('data-unread', numberFormat(unreads + nb)); } } @@ -173,8 +178,8 @@ function removeArticle(div) { } let scrollTop = box_to_follow.scrollTop; let dirty = false; - const p = div.previousElementSibling, - n = div.nextElementSibling; + const p = div.previousElementSibling; + const n = div.nextElementSibling; if (p && p.classList.contains('day') && n && n.classList.contains('day')) { scrollTop -= p.offsetHeight; dirty = true; @@ -190,80 +195,80 @@ function removeArticle(div) { } } -var pending_entries = {}, - mark_read_queue = []; +const pending_entries = {}; +let mark_read_queue = []; function send_mark_read_queue(queue, asRead, callback) { const req = new XMLHttpRequest(); req.open('POST', '.?c=entry&a=read' + (asRead ? '' : '&is_read=0'), true); req.responseType = 'json'; req.onerror = function (e) { - for (let i = queue.length - 1; i >= 0; i--) { - delete pending_entries['flux_' + queue[i]]; - } - badAjax(this.status == 403); - }; + for (let i = queue.length - 1; i >= 0; i--) { + delete pending_entries['flux_' + queue[i]]; + } + badAjax(this.status == 403); + }; req.onload = function (e) { - if (this.status != 200) { - return req.onerror(e); - } - const json = xmlHttpRequestJson(this); - if (!json) { - return req.onerror(e); - } - for (let i = queue.length - 1; i >= 0; i--) { - const div = document.getElementById('flux_' + queue[i]), - myIcons = context.icons; - let inc = 0; - if (div.classList.contains('not_read')) { - div.classList.remove('not_read'); - div.querySelectorAll('a.read').forEach(function (a) { - a.href = a.href.replace('&is_read=0', '') + '&is_read=1'; - }); - div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.read; }); - inc--; - if (context.auto_remove_article) { - removeArticle(div); - } - } else { - div.classList.add('not_read'); - div.classList.add('keep_unread'); //Split for IE11 - div.querySelectorAll('a.read').forEach(function (a) { - a.href = a.href.replace('&is_read=1', ''); - }); - div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.unread; }); - inc++; + if (this.status != 200) { + return req.onerror(e); + } + const json = xmlHttpRequestJson(this); + if (!json) { + return req.onerror(e); + } + for (let i = queue.length - 1; i >= 0; i--) { + const div = document.getElementById('flux_' + queue[i]); + const myIcons = context.icons; + let inc = 0; + if (div.classList.contains('not_read')) { + div.classList.remove('not_read'); + div.querySelectorAll('a.read').forEach(function (a) { + a.href = a.href.replace('&is_read=0', '') + '&is_read=1'; + }); + div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.read; }); + inc--; + if (context.auto_remove_article) { + removeArticle(div); } - let feed_link = div.querySelector('.website > a, a.website'); - if (feed_link) { - const feed_url = feed_link.href, - feed_id = feed_url.substr(feed_url.lastIndexOf('f_')); - incUnreadsFeed(div, feed_id, inc); - } - delete pending_entries['flux_' + queue[i]]; + } else { + div.classList.add('not_read'); + div.classList.add('keep_unread'); // Split for IE11 + div.querySelectorAll('a.read').forEach(function (a) { + a.href = a.href.replace('&is_read=1', ''); + }); + div.querySelectorAll('a.read > .icon').forEach(function (img) { img.outerHTML = myIcons.unread; }); + inc++; } - faviconNbUnread(); - if (json.tags) { - const tagIds = Object.keys(json.tags); - for (let i = tagIds.length - 1; i >= 0; i--) { - let tagId = tagIds[i]; - incUnreadsTag(tagId, (asRead ? -1 : 1) * json.tags[tagId].length); - } + const feed_link = div.querySelector('.website > a, a.website'); + if (feed_link) { + const feed_url = feed_link.href; + const feed_id = feed_url.substr(feed_url.lastIndexOf('f_')); + incUnreadsFeed(div, feed_id, inc); } - onScroll(); - if (callback) { - callback(); + delete pending_entries['flux_' + queue[i]]; + } + faviconNbUnread(); + if (json.tags) { + const tagIds = Object.keys(json.tags); + for (let i = tagIds.length - 1; i >= 0; i--) { + const tagId = tagIds[i]; + incUnreadsTag(tagId, (asRead ? -1 : 1) * json.tags[tagId].length); } - }; + } + onScroll(); + if (callback) { + callback(); + } + }; req.setRequestHeader('Content-Type', 'application/json'); req.send(JSON.stringify({ - ajax: true, - _csrf: context.csrf, - id: queue, - })); + ajax: true, + _csrf: context.csrf, + id: queue, + })); } -var send_mark_read_queue_timeout = 0; +let send_mark_read_queue_timeout = 0; function send_mark_queue_tick(callback) { send_mark_read_queue_timeout = 0; @@ -271,7 +276,7 @@ function send_mark_queue_tick(callback) { mark_read_queue = []; send_mark_read_queue(queue, true, callback); } -var delayedFunction = send_mark_queue_tick; +const delayedFunction = send_mark_queue_tick; function delayedClick(a) { if (a) { @@ -289,15 +294,15 @@ function mark_read(div, only_not_read, asBatch) { } pending_entries[div.id] = true; - const asRead = div.classList.contains('not_read'), - entryId = div.id.replace(/^flux_/, ''); + const asRead = div.classList.contains('not_read'); + const entryId = div.id.replace(/^flux_/, ''); if (asRead && asBatch) { mark_read_queue.push(entryId); if (send_mark_read_queue_timeout == 0) { send_mark_read_queue_timeout = setTimeout(function () { send_mark_queue_tick(null); }, 1000); } } else { - const queue = [ entryId ]; + const queue = [entryId]; send_mark_read_queue(queue, asRead); } } @@ -314,8 +319,8 @@ function mark_favorite(div) { return false; } - let a = div.querySelector('a.bookmark'), - url = a ? a.href : ''; + const a = div.querySelector('a.bookmark'); + const url = a ? a.href : ''; if (!url) { return false; } @@ -329,53 +334,53 @@ function mark_favorite(div) { req.open('POST', url, true); req.responseType = 'json'; req.onerror = function (e) { - delete pending_entries[div.id]; - badAjax(this.status == 403); - }; + delete pending_entries[div.id]; + badAjax(this.status == 403); + }; req.onload = function (e) { - if (this.status != 200) { - return req.onerror(e); - } - const json = xmlHttpRequestJson(this); - if (!json) { - return req.onerror(e); - } - let inc = 0; - if (div.classList.contains('favorite')) { - div.classList.remove('favorite'); - inc--; - } else { - div.classList.add('favorite'); - inc++; - } - div.querySelectorAll('a.bookmark').forEach(function (a) { a.href = json.url; }); - div.querySelectorAll('a.bookmark > .icon').forEach(function (img) { img.outerHTML = json.icon; }); + if (this.status != 200) { + return req.onerror(e); + } + const json = xmlHttpRequestJson(this); + if (!json) { + return req.onerror(e); + } + let inc = 0; + if (div.classList.contains('favorite')) { + div.classList.remove('favorite'); + inc--; + } else { + div.classList.add('favorite'); + inc++; + } + div.querySelectorAll('a.bookmark').forEach(function (a) { a.href = json.url; }); + div.querySelectorAll('a.bookmark > .icon').forEach(function (img) { img.outerHTML = json.icon; }); - const favourites = document.querySelector('#aside_feed .favorites .title'); - if (favourites) { - favourites.textContent = favourites.textContent.replace(/((?: \([ 0-9]+\))?\s*)$/, function (m, p1) { - return incLabel(p1, inc, false); - }); - } + const favourites = document.querySelector('#aside_feed .favorites .title'); + if (favourites) { + favourites.textContent = favourites.textContent.replace(/((?: \([\s0-9]+\))?\s*)$/, function (m, p1) { + return incLabel(p1, inc, false); + }); + } - if (div.classList.contains('not_read')) { - const elem = document.querySelector('#aside_feed .favorites .title'), - feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; - if (elem) { - elem.setAttribute('data-unread', numberFormat(feed_unreads + inc)); - } + if (div.classList.contains('not_read')) { + const elem = document.querySelector('#aside_feed .favorites .title'); + const feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; + if (elem) { + elem.setAttribute('data-unread', numberFormat(feed_unreads + inc)); } + } - delete pending_entries[div.id]; - }; + delete pending_entries[div.id]; + }; req.setRequestHeader('Content-Type', 'application/json'); req.send(JSON.stringify({ - ajax: true, - _csrf: context.csrf, - })); + ajax: true, + _csrf: context.csrf, + })); } -var freshrssOpenArticleEvent = document.createEvent('Event'); +const freshrssOpenArticleEvent = document.createEvent('Event'); freshrssOpenArticleEvent.initEvent('freshrss:openArticle', true, true); function toggleContent(new_active, old_active, skipping) { @@ -398,7 +403,7 @@ function toggleContent(new_active, old_active, skipping) { new_active.classList.add('current'); if (old_active) { old_active.classList.remove('active'); - old_active.classList.remove('current'); //Split for IE11 + old_active.classList.remove('current'); // Split for IE11 if (context.auto_remove_article) { removeArticle(old_active); } @@ -407,12 +412,12 @@ function toggleContent(new_active, old_active, skipping) { new_active.classList.toggle('active'); } - const relative_move = context.current_view === 'global', - box_to_move = relative_move ? document.getElementById('panel') : document.scrollingElement; + const relative_move = context.current_view === 'global'; + const box_to_move = relative_move ? document.getElementById('panel') : document.scrollingElement; - if (context.sticky_post) { //Stick the article to the top when opened - let prev_article = new_active.previousElementSibling, - new_pos = new_active.offsetParent.offsetTop + new_active.offsetTop; + if (context.sticky_post) { // Stick the article to the top when opened + const prev_article = new_active.previousElementSibling; + let new_pos = new_active.offsetParent.offsetTop + new_active.offsetTop; if (prev_article && new_active.offsetTop - prev_article.offsetTop <= 150) { new_pos = prev_article.offsetParent.offsetTop + prev_article.offsetTop; @@ -627,8 +632,8 @@ function toggle_media() { } function user_filter(key) { - const filter = document.getElementById('dropdown-query'), - filters = filter.parentElement.querySelectorAll('.dropdown-menu > .query > a'); + const filter = document.getElementById('dropdown-query'); + const filters = filter.parentElement.querySelectorAll('.dropdown-menu > .query > a'); if (typeof key === 'undefined') { if (!filters.length) { return; @@ -683,7 +688,7 @@ function auto_share(key) { } } -var box_to_follow; +let box_to_follow; function onScroll() { if (!box_to_follow) { @@ -692,11 +697,11 @@ function onScroll() { if (context.auto_mark_scroll) { const minTop = 40 + box_to_follow.scrollTop; document.querySelectorAll('.not_read:not(.keep_unread)').forEach(function (div) { - if (div.offsetHeight > 0 && + if (div.offsetHeight > 0 && div.offsetParent.offsetTop + div.offsetTop + div.offsetHeight < minTop) { - mark_read(div, true, true); - } - }); + mark_read(div, true, true); + } + }); } if (context.auto_load_more) { const pagination = document.getElementById('mark-read-pagination'); @@ -710,8 +715,8 @@ function onScroll() { function init_posts() { if (context.auto_load_more || context.auto_mark_scroll || context.auto_remove_article) { box_to_follow = context.current_view === 'global' ? document.getElementById('panel') : document.scrollingElement; - let lastScroll = 0, //Throttle - timerId = 0; + let lastScroll = 0; // Throttle + let timerId = 0; (box_to_follow === document.scrollingElement ? window : box_to_follow).onscroll = function () { clearTimeout(timerId); if (lastScroll + 500 < Date.now()) { @@ -750,10 +755,10 @@ function init_column_categories() { return; } - //Restore sidebar scroll position + // Restore sidebar scroll position document.getElementById('sidebar').scrollTop = +sessionStorage.getItem('FreshRSS_sidebar_scrollTop'); - //Restore open categories + // Restore open categories if (context.display_categories === 'remember') { const open_categories = JSON.parse(localStorage.getItem('FreshRSS_open_categories') || '{}'); Object.keys(open_categories).forEach(function (category_id) { @@ -784,21 +789,21 @@ function init_column_categories() { } } ul.classList.toggle('active'); - //CSS transition does not work on max-height:auto + // CSS transition does not work on max-height:auto ul.style.maxHeight = ul.classList.contains('active') ? (nbVisibleItems * 4) + 'em' : 0; return false; } a = ev.target.closest('.tree-folder-items > .feed .dropdown-toggle'); if (a) { - const itemId = a.closest('.item').id, - templateId = itemId.substring(0, 2) === 't_' ? 'tag_config_template' : 'feed_config_template', - id = itemId.substr(2), - feed_web = a.getAttribute('data-fweb'), - div = a.parentElement, - dropdownMenu = div.querySelector('.dropdown-menu'), - template = document.getElementById(templateId) - .innerHTML.replace(/------/g, id).replace('http://example.net/', feed_web); + const itemId = a.closest('.item').id; + const templateId = itemId.substring(0, 2) === 't_' ? 'tag_config_template' : 'feed_config_template'; + const id = itemId.substr(2); + const feed_web = a.getAttribute('data-fweb'); + const div = a.parentElement; + const dropdownMenu = div.querySelector('.dropdown-menu'); + const template = document.getElementById(templateId) + .innerHTML.replace(/------/g, id).replace('http://example.net/', feed_web); if (!dropdownMenu) { a.href = '#dropdown-' + id; div.querySelector('.dropdown-target').id = 'dropdown-' + id; @@ -822,137 +827,137 @@ function init_column_categories() { function init_shortcuts() { Object.keys(context.shortcuts).forEach(function (k) { - context.shortcuts[k] = (context.shortcuts[k] || '').toUpperCase(); - }); + context.shortcuts[k] = (context.shortcuts[k] || '').toUpperCase(); + }); document.addEventListener('keydown', ev => { - if (ev.target.closest('input, textarea') || + if (ev.target.closest('input, textarea') || ev.ctrlKey || ev.metaKey || (ev.altKey && ev.shiftKey)) { - return true; - } - - const s = context.shortcuts; - let k = (ev.key.trim() || ev.code || 'Space').toUpperCase(); - - //IE11 - if (k === 'SPACEBAR') k = 'SPACE'; - else if (k === 'DEL') k = 'DELETE'; - else if (k === 'ESC') k = 'ESCAPE'; - - if (location.hash.match(/^#dropdown-/)) { - const n = parseInt(k); - if (n) { - if (location.hash === '#dropdown-query') { - user_filter(n); - } else { - auto_share(n); - } - return false; - } - } - if (k === s.next_entry) { - if (ev.altKey) { - next_category(); - } else if (ev.shiftKey) { - next_feed(); - } else { - next_entry(false); - } - return false; - } - if (k === s.next_unread_entry) { - if (ev.altKey) { - next_unread_category(); - } else if (ev.shiftKey) { - next_feed(); - } else { - next_unread_entry(false); - } - return false; - } - if (k === s.prev_entry) { - if (ev.altKey) { - prev_category(); - } else if (ev.shiftKey) { - prev_feed(); - } else { - prev_entry(false); - } - return false; - } - if (k === s.mark_read) { - if (ev.altKey) { - mark_previous_read(document.querySelector('.flux.current')); - } else if (ev.shiftKey) { - document.querySelector('.nav_menu .read_all').click(); - } else { // Toggle the read state - mark_read(document.querySelector('.flux.current'), false, false); - } - return false; - } - if (k === s.first_entry) { - if (ev.altKey) { - first_category(); - } else if (ev.shiftKey) { - first_feed(); - } else { - const old_active = document.querySelector('.flux.current'), - first = document.querySelector('.flux'); - if (first.classList.contains('flux')) { - toggleContent(first, old_active, false); - } - } - return false; - } - if (k === s.last_entry) { - if (ev.altKey) { - last_category(); - } else if (ev.shiftKey) { - last_feed(); - } else { - const old_active = document.querySelector('.flux.current'), - last = document.querySelector('.flux:last-of-type'); - if (last.classList.contains('flux')) { - toggleContent(last, old_active, false); - } - } - return false; - } - - if (ev.altKey || ev.shiftKey) { - return true; - } - if (k === s.mark_favorite) { // Toggle the favorite state - mark_favorite(document.querySelector('.flux.current')); - return false; - } - if (k === s.go_website) { - if (context.auto_mark_site) { - mark_read(document.querySelector('.flux.current'), true, false); - } - const newWindow = window.open(); - if (newWindow) { - newWindow.opener = null; - newWindow.location = document.querySelector('.flux.current a.go_website').href; - } - return false; - } - if (k === s.skip_next_entry) { next_entry(true); return false; } - if (k === s.skip_prev_entry) { prev_entry(true); return false; } - if (k === s.collapse_entry) { collapse_entry(); return false; } - if (k === s.auto_share) { auto_share(); return false; } - if (k === s.user_filter) { user_filter(); return false; } - if (k === s.load_more) { load_more_posts(); return false; } - if (k === s.close_dropdown) { location.hash = null; return false; } - if (k === s.help) { window.open(context.urls.help); return false; } - if (k === s.focus_search) { document.getElementById('search').focus(); return false; } - if (k === s.normal_view) { delayedClick(document.querySelector('#nav_menu_views .view-normal')); return false; } - if (k === s.reading_view) { delayedClick(document.querySelector('#nav_menu_views .view-reader')); return false; } - if (k === s.global_view) { delayedClick(document.querySelector('#nav_menu_views .view-global')); return false; } - if (k === s.rss_view) { delayedClick(document.querySelector('#nav_menu_views .view-rss')); return false; } - if (k === s.toggle_media) { toggle_media(); return false; } return true; - }); + } + + const s = context.shortcuts; + let k = (ev.key.trim() || ev.code || 'Space').toUpperCase(); + + // IE11 + if (k === 'SPACEBAR') k = 'SPACE'; + else if (k === 'DEL') k = 'DELETE'; + else if (k === 'ESC') k = 'ESCAPE'; + + if (location.hash.match(/^#dropdown-/)) { + const n = parseInt(k); + if (n) { + if (location.hash === '#dropdown-query') { + user_filter(n); + } else { + auto_share(n); + } + return false; + } + } + if (k === s.next_entry) { + if (ev.altKey) { + next_category(); + } else if (ev.shiftKey) { + next_feed(); + } else { + next_entry(false); + } + return false; + } + if (k === s.next_unread_entry) { + if (ev.altKey) { + next_unread_category(); + } else if (ev.shiftKey) { + next_feed(); + } else { + next_unread_entry(false); + } + return false; + } + if (k === s.prev_entry) { + if (ev.altKey) { + prev_category(); + } else if (ev.shiftKey) { + prev_feed(); + } else { + prev_entry(false); + } + return false; + } + if (k === s.mark_read) { + if (ev.altKey) { + mark_previous_read(document.querySelector('.flux.current')); + } else if (ev.shiftKey) { + document.querySelector('.nav_menu .read_all').click(); + } else { // Toggle the read state + mark_read(document.querySelector('.flux.current'), false, false); + } + return false; + } + if (k === s.first_entry) { + if (ev.altKey) { + first_category(); + } else if (ev.shiftKey) { + first_feed(); + } else { + const old_active = document.querySelector('.flux.current'); + const first = document.querySelector('.flux'); + if (first.classList.contains('flux')) { + toggleContent(first, old_active, false); + } + } + return false; + } + if (k === s.last_entry) { + if (ev.altKey) { + last_category(); + } else if (ev.shiftKey) { + last_feed(); + } else { + const old_active = document.querySelector('.flux.current'); + const last = document.querySelector('.flux:last-of-type'); + if (last.classList.contains('flux')) { + toggleContent(last, old_active, false); + } + } + return false; + } + + if (ev.altKey || ev.shiftKey) { + return true; + } + if (k === s.mark_favorite) { // Toggle the favorite state + mark_favorite(document.querySelector('.flux.current')); + return false; + } + if (k === s.go_website) { + if (context.auto_mark_site) { + mark_read(document.querySelector('.flux.current'), true, false); + } + const newWindow = window.open(); + if (newWindow) { + newWindow.opener = null; + newWindow.location = document.querySelector('.flux.current a.go_website').href; + } + return false; + } + if (k === s.skip_next_entry) { next_entry(true); return false; } + if (k === s.skip_prev_entry) { prev_entry(true); return false; } + if (k === s.collapse_entry) { collapse_entry(); return false; } + if (k === s.auto_share) { auto_share(); return false; } + if (k === s.user_filter) { user_filter(); return false; } + if (k === s.load_more) { load_more_posts(); return false; } + if (k === s.close_dropdown) { location.hash = null; return false; } + if (k === s.help) { window.open(context.urls.help); return false; } + if (k === s.focus_search) { document.getElementById('search').focus(); return false; } + if (k === s.normal_view) { delayedClick(document.querySelector('#nav_menu_views .view-normal')); return false; } + if (k === s.reading_view) { delayedClick(document.querySelector('#nav_menu_views .view-reader')); return false; } + if (k === s.global_view) { delayedClick(document.querySelector('#nav_menu_views .view-global')); return false; } + if (k === s.rss_view) { delayedClick(document.querySelector('#nav_menu_views .view-rss')); return false; } + if (k === s.toggle_media) { toggle_media(); return false; } + return true; + }); } function init_stream(stream) { @@ -990,9 +995,9 @@ function init_stream(stream) { } el = ev.target.closest('.item.share > a[data-type="print"]'); - if (el) { //Print + if (el) { // Print const tmp_window = window.open(); - for (var i = 0; i < document.styleSheets.length; i++) { + for (let i = 0; i < document.styleSheets.length; i++) { tmp_window.document.writeln(''); } tmp_window.document.writeln(el.closest('.flux_content').querySelector('.content').innerHTML); @@ -1004,13 +1009,13 @@ function init_stream(stream) { } el = ev.target.closest('.item.share > a[data-type="clipboard"]'); - if (el && navigator.clipboard) { //Clipboard + if (el && navigator.clipboard) { // Clipboard navigator.clipboard.writeText(el.href); return false; } el = ev.target.closest('.item.share > a[href="POST"]'); - if (el) { //Share by POST + if (el) { // Share by POST const f = el.parentElement.querySelector('form'); f.disabled = false; f.submit(); @@ -1018,7 +1023,7 @@ function init_stream(stream) { } el = ev.target.closest('.flux_header, .flux_content'); - if (el) { //flux_toggle + if (el) { // flux_toggle if (ev.target.closest('.content, .item.website, .item.link, .dropdown-menu')) { return true; } @@ -1026,9 +1031,9 @@ function init_stream(stream) { // setting for not-closing after clicking outside article area return false; } - const old_active = document.querySelector('.flux.current'), - new_active = el.parentNode; - if (ev.target.tagName.toUpperCase() === 'A') { //Leave real links alone + const old_active = document.querySelector('.flux.current'); + const new_active = el.parentNode; + if (ev.target.tagName.toUpperCase() === 'A') { // Leave real links alone if (context.auto_mark_article) { mark_read(new_active, true, false); } @@ -1047,14 +1052,14 @@ function init_stream(stream) { let el = ev.target.closest('.item.title > a'); if (el) { if (ev.which == 1) { - if (ev.ctrlKey) { //Control+click + if (ev.ctrlKey) { // Control+click if (context.auto_mark_site) { mark_read(el.closest('.flux'), true, false); } } else { - el.parentElement.click(); //Normal click, just toggle article. + el.parentElement.click(); // Normal click, just toggle article. } - } else if (ev.which == 2 && !ev.ctrlKey) { //Simple middle click: same behaviour as CTRL+click + } else if (ev.which == 2 && !ev.ctrlKey) { // Simple middle click: same behaviour as CTRL+click if (context.auto_mark_article) { const new_active = el.closest('.flux'); mark_read(new_active, true, false); @@ -1077,68 +1082,68 @@ function init_stream(stream) { }; stream.onchange = function (ev) { - const checkboxTag = ev.target.closest('.checkboxTag'); - if (checkboxTag) { //Dynamic tags - ev.stopPropagation(); - const isChecked = checkboxTag.checked, - tagId = checkboxTag.name.replace(/^t_/, ''), - tagName = checkboxTag.nextElementSibling ? checkboxTag.nextElementSibling.value : '', - entry = checkboxTag.closest('div.flux'), - entryId = entry.id.replace(/^flux_/, ''); - checkboxTag.disabled = true; + const checkboxTag = ev.target.closest('.checkboxTag'); + if (checkboxTag) { // Dynamic tags + ev.stopPropagation(); + const isChecked = checkboxTag.checked; + const tagId = checkboxTag.name.replace(/^t_/, ''); + const tagName = checkboxTag.nextElementSibling ? checkboxTag.nextElementSibling.value : ''; + const entry = checkboxTag.closest('div.flux'); + const entryId = entry.id.replace(/^flux_/, ''); + checkboxTag.disabled = true; - const req = new XMLHttpRequest(); - req.open('POST', './?c=tag&a=tagEntry', true); - req.responseType = 'json'; - req.onerror = function (e) { - checkboxTag.checked = !isChecked; - badAjax(this.status == 403); - }; - req.onload = function (e) { - if (this.status != 200) { - return req.onerror(e); - } - if (entry.classList.contains('not_read')) { - incUnreadsTag('t_' + tagId, isChecked ? 1 : -1); - } - }; - req.onloadend = function (e) { - checkboxTag.disabled = false; - if (tagId == 0) { - loadDynamicTags(checkboxTag.closest('div.dropdown')); - } - }; - req.setRequestHeader('Content-Type', 'application/json'); - req.send(JSON.stringify({ - _csrf: context.csrf, - id_tag: tagId, - name_tag: tagId == 0 ? tagName : '', - id_entry: entryId, - checked: isChecked, - })); - } - }; + const req = new XMLHttpRequest(); + req.open('POST', './?c=tag&a=tagEntry', true); + req.responseType = 'json'; + req.onerror = function (e) { + checkboxTag.checked = !isChecked; + badAjax(this.status == 403); + }; + req.onload = function (e) { + if (this.status != 200) { + return req.onerror(e); + } + if (entry.classList.contains('not_read')) { + incUnreadsTag('t_' + tagId, isChecked ? 1 : -1); + } + }; + req.onloadend = function (e) { + checkboxTag.disabled = false; + if (tagId == 0) { + loadDynamicTags(checkboxTag.closest('div.dropdown')); + } + }; + req.setRequestHeader('Content-Type', 'application/json'); + req.send(JSON.stringify({ + _csrf: context.csrf, + id_tag: tagId, + name_tag: tagId == 0 ? tagName : '', + id_entry: entryId, + checked: isChecked, + })); + } + }; } function init_nav_entries() { const nav_entries = document.getElementById('nav_entries'); if (nav_entries) { nav_entries.querySelector('.previous_entry').onclick = function (e) { - prev_entry(false); - return false; - }; + prev_entry(false); + return false; + }; nav_entries.querySelector('.next_entry').onclick = function (e) { - next_entry(false); - return false; - }; + next_entry(false); + return false; + }; nav_entries.querySelector('.up').onclick = function (e) { - const active_item = (document.querySelector('.flux.current') || document.querySelector('.flux')), - windowTop = document.scrollingElement.scrollTop, - item_top = active_item.offsetParent.offsetTop + active_item.offsetTop; + const active_item = (document.querySelector('.flux.current') || document.querySelector('.flux')); + const windowTop = document.scrollingElement.scrollTop; + const item_top = active_item.offsetParent.offsetTop + active_item.offsetTop; - document.scrollingElement.scrollTop = windowTop > item_top ? item_top : 0; - return false; - }; + document.scrollingElement.scrollTop = windowTop > item_top ? item_top : 0; + return false; + }; } } @@ -1151,32 +1156,32 @@ function loadDynamicTags(div) { req.open('GET', './?c=tag&a=getTagsForEntry&id_entry=' + entryId, true); req.responseType = 'json'; req.onerror = function (e) { - div.querySelectorAll('li.item').forEach(function (li) { li.remove(); }); - div.classList.add('dynamictags'); - }; + div.querySelectorAll('li.item').forEach(function (li) { li.remove(); }); + div.classList.add('dynamictags'); + }; req.onload = function (e) { - if (this.status != 200) { - return req.onerror(e); - } - const json = xmlHttpRequestJson(this); - if (!json) { - return req.onerror(e); - } - let html = '
  • '; - if (json && json.length) { - for (let i = 0; i < json.length; i++) { - const tag = json[i]; - html += '
  • '; - } } - div.querySelector('.dropdown-menu').insertAdjacentHTML('beforeend', html); - }; + } + div.querySelector('.dropdown-menu').insertAdjacentHTML('beforeend', html); + }; req.send(); } // -var feed_processed = 0; +let feed_processed = 0; function updateFeed(feeds, feeds_count) { const feed = feeds.pop(); @@ -1186,34 +1191,34 @@ function updateFeed(feeds, feeds_count) { const req = new XMLHttpRequest(); req.open('POST', feed.url, true); req.onloadend = function (e) { - if (this.status != 200) { - return badAjax(false); - } - feed_processed++; - const div = document.getElementById('actualizeProgress'); - div.querySelector('.progress').innerHTML = feed_processed + ' / ' + feeds_count; - div.querySelector('.title').innerHTML = feed.title; - if (feed_processed === feeds_count) { - //Empty request to commit new articles - const req2 = new XMLHttpRequest(); - req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true); - req2.onloadend = function (e) { - delayedFunction(function () { location.reload(); }); - }; - req2.setRequestHeader('Content-Type', 'application/json'); - req2.send(JSON.stringify({ - _csrf: context.csrf, - noCommit: 0, - })); - } else { - updateFeed(feeds, feeds_count); - } - }; + if (this.status != 200) { + return badAjax(false); + } + feed_processed++; + const div = document.getElementById('actualizeProgress'); + div.querySelector('.progress').innerHTML = feed_processed + ' / ' + feeds_count; + div.querySelector('.title').innerHTML = feed.title; + if (feed_processed === feeds_count) { + // Empty request to commit new articles + const req2 = new XMLHttpRequest(); + req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true); + req2.onloadend = function (e) { + delayedFunction(function () { location.reload(); }); + }; + req2.setRequestHeader('Content-Type', 'application/json'); + req2.send(JSON.stringify({ + _csrf: context.csrf, + noCommit: 0, + })); + } else { + updateFeed(feeds, feeds_count); + } + }; req.setRequestHeader('Content-Type', 'application/json'); req.send(JSON.stringify({ - _csrf: context.csrf, - noCommit: 1, - })); + _csrf: context.csrf, + noCommit: 1, + })); } function init_actualize() { @@ -1234,46 +1239,46 @@ function init_actualize() { req.open('POST', './?c=javascript&a=actualize', true); req.responseType = 'json'; req.onload = function (e) { - if (this.status != 200) { - return badAjax(false); - } - const json = xmlHttpRequestJson(this); - if (!json) { - return badAjax(false); - } - if (auto && json.feeds.length < 1) { - auto = false; + if (this.status != 200) { + return badAjax(false); + } + const json = xmlHttpRequestJson(this); + if (!json) { + return badAjax(false); + } + if (auto && json.feeds.length < 1) { + auto = false; + context.ajax_loading = false; + return false; + } + if (json.feeds.length === 0) { + openNotification(json.feedback_no_refresh, 'good'); + // Empty request to commit new articles + const req2 = new XMLHttpRequest(); + req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true); + req2.onloadend = function (e) { context.ajax_loading = false; - return false; - } - if (json.feeds.length === 0) { - openNotification(json.feedback_no_refresh, 'good'); - //Empty request to commit new articles - const req2 = new XMLHttpRequest(); - req2.open('POST', './?c=feed&a=actualize&id=-1&ajax=1', true); - req2.onloadend = function (e) { - context.ajax_loading = false; - }; - req2.setRequestHeader('Content-Type', 'application/json'); - req2.send(JSON.stringify({ - _csrf: context.csrf, - noCommit: 0, - })); - return; - } - //Progress bar - const feeds_count = json.feeds.length; - document.body.insertAdjacentHTML('beforeend', '
    ' + + }; + req2.setRequestHeader('Content-Type', 'application/json'); + req2.send(JSON.stringify({ + _csrf: context.csrf, + noCommit: 0, + })); + return; + } + // Progress bar + const feeds_count = json.feeds.length; + document.body.insertAdjacentHTML('beforeend', '
    ' + json.feedback_actualize + '
    /
    0 / ' + feeds_count + '
    '); - for (let i = 10; i > 0; i--) { - updateFeed(json.feeds, feeds_count); - } - }; + for (let i = 10; i > 0; i--) { + updateFeed(json.feeds, feeds_count); + } + }; req.setRequestHeader('Content-Type', 'application/json'); req.send(JSON.stringify({ - _csrf: context.csrf, - })); + _csrf: context.csrf, + })); return false; }; @@ -1286,9 +1291,9 @@ function init_actualize() { // // -var notification = null, - notification_interval = null, - notification_working = false; +let notification = null; +let notification_interval = null; +let notification_working = false; function openNotification(msg, status) { if (notification_working === true) { @@ -1312,9 +1317,9 @@ function init_notifications() { notification = document.getElementById('notification'); notification.querySelector('a.close').onclick = function () { - closeNotification(); - return false; - }; + closeNotification(); + return false; + }; if (notification.querySelector('.msg').innerHTML.length > 0) { notification_working = true; @@ -1324,12 +1329,14 @@ function init_notifications() { // // -let popup = null, - popup_iframe_container = null, - popup_iframe = null, - popup_txt = null, - popup_working = false; +let popup = null; +let popup_iframe_container = null; +let popup_iframe = null; +let popup_txt = null; +let popup_working = false; +/* eslint-disable no-unused-vars */ +// TODO: Re-enable no-unused-vars function openPopupWithMessage(msg) { if (popup_working === true) { return false; @@ -1355,6 +1362,7 @@ function openPopupWithSource(source) { popup_iframe_container.style.display = 'table-row'; popup.style.display = 'block'; } +/* eslint-enable no-unused-vars */ function closePopup() { popup.style.display = 'none'; @@ -1367,7 +1375,7 @@ function closePopup() { } function init_popup() { - //Fetch elements. + // Fetch elements. popup = document.getElementById('popup'); popup_iframe_container = document.getElementById('popup-iframe-container'); @@ -1375,12 +1383,12 @@ function init_popup() { popup_txt = document.getElementById('popup-txt'); - //Configure close button. + // Configure close button. document.getElementById('popup-close').addEventListener('click', function (ev) { closePopup(); }); - //Configure close-on-click. + // Configure close-on-click. window.addEventListener('click', function (ev) { if (ev.target == popup) { closePopup(); @@ -1390,7 +1398,7 @@ function init_popup() { // // -var notifs_html5_permission = 'denied'; +let notifs_html5_permission = 'denied'; function notifs_html5_is_supported() { return window.Notification !== undefined; @@ -1414,12 +1422,12 @@ function notifs_html5_show(nb) { }); notification.onclick = function () { - delayedFunction(function() { - location.reload(); - window.focus(); - notification.close(); - }); - }; + delayedFunction(function () { + location.reload(); + window.focus(); + notification.close(); + }); + }; if (context.html5_notif_timeout !== 0) { setTimeout(function () { @@ -1442,61 +1450,61 @@ function refreshUnreads() { req.open('GET', './?c=javascript&a=nbUnreadsPerFeed', true); req.responseType = 'json'; req.onload = function (e) { - const json = xmlHttpRequestJson(this); - if (!json) { - return badAjax(false); - } - const isAll = document.querySelector('.category.all.active'); - let new_articles = false; + const json = xmlHttpRequestJson(this); + if (!json) { + return badAjax(false); + } + const isAll = document.querySelector('.category.all.active'); + let new_articles = false; - Object.keys(json.feeds).forEach(function (feed_id) { - const nbUnreads = json.feeds[feed_id]; - feed_id = 'f_' + feed_id; - const elem = document.getElementById(feed_id), - feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; + Object.keys(json.feeds).forEach(function (feed_id) { + const nbUnreads = json.feeds[feed_id]; + feed_id = 'f_' + feed_id; + const elem = document.getElementById(feed_id); + const feed_unreads = elem ? str2int(elem.getAttribute('data-unread')) : 0; - if ((incUnreadsFeed(null, feed_id, nbUnreads - feed_unreads) || isAll) && //Update of current view? + if ((incUnreadsFeed(null, feed_id, nbUnreads - feed_unreads) || isAll) && // Update of current view? (nbUnreads - feed_unreads > 0)) { - const newArticle = document.getElementById('new-article'); - newArticle.setAttribute('aria-hidden', 'false'); - newArticle.style.display = 'block'; - new_articles = true; - } - }); - - let nbUnreadTags = 0; - - Object.keys(json.tags).forEach(function (tag_id) { - const nbUnreads = json.tags[tag_id]; - nbUnreadTags += nbUnreads; - const tag = document.getElementById('t_' + tag_id); - if (tag) { - tag.setAttribute('data-unread', nbUnreads); - tag.querySelector('.item-title').setAttribute('data-unread', numberFormat(nbUnreads)); - } - }); - - const tags = document.querySelector('.category.tags'); - if (tags) { - tags.setAttribute('data-unread', nbUnreadTags); - tags.querySelector('.title').setAttribute('data-unread', numberFormat(nbUnreadTags)); + const newArticle = document.getElementById('new-article'); + newArticle.setAttribute('aria-hidden', 'false'); + newArticle.style.display = 'block'; + new_articles = true; } + }); - const title = document.querySelector('.category.all .title'), - nb_unreads = title ? str2int(title.getAttribute('data-unread')) : 0; + let nbUnreadTags = 0; - if (nb_unreads > 0 && new_articles) { - faviconNbUnread(nb_unreads); - notifs_html5_show(nb_unreads); + Object.keys(json.tags).forEach(function (tag_id) { + const nbUnreads = json.tags[tag_id]; + nbUnreadTags += nbUnreads; + const tag = document.getElementById('t_' + tag_id); + if (tag) { + tag.setAttribute('data-unread', nbUnreads); + tag.querySelector('.item-title').setAttribute('data-unread', numberFormat(nbUnreads)); } - }; + }); + + const tags = document.querySelector('.category.tags'); + if (tags) { + tags.setAttribute('data-unread', nbUnreadTags); + tags.querySelector('.title').setAttribute('data-unread', numberFormat(nbUnreadTags)); + } + + const title = document.querySelector('.category.all .title'); + const nb_unreads = title ? str2int(title.getAttribute('data-unread')) : 0; + + if (nb_unreads > 0 && new_articles) { + faviconNbUnread(nb_unreads); + notifs_html5_show(nb_unreads); + } + }; req.send(); } -// -var url_load_more = '', - load_more = false, - box_load_more = null; +// +let url_load_more = ''; +let load_more = false; +let box_load_more = null; function load_more_posts() { if (load_more || !url_load_more || !box_load_more) { @@ -1509,50 +1517,50 @@ function load_more_posts() { req.open('GET', url_load_more, true); req.responseType = 'document'; req.onload = function (e) { - const html = this.response, - formPagination = document.getElementById('mark-read-pagination'); + const html = this.response; + const formPagination = document.getElementById('mark-read-pagination'); - const streamAdopted = document.adoptNode(html.getElementById('stream')); - streamAdopted.querySelectorAll('.flux, .day').forEach(function (div) { - box_load_more.insertBefore(div, formPagination); - }); + const streamAdopted = document.adoptNode(html.getElementById('stream')); + streamAdopted.querySelectorAll('.flux, .day').forEach(function (div) { + box_load_more.insertBefore(div, formPagination); + }); - const paginationOld = formPagination.querySelector('.pagination'), - paginationNew = streamAdopted.querySelector('.pagination'); - formPagination.replaceChild(paginationNew, paginationOld); + const paginationOld = formPagination.querySelector('.pagination'); + const paginationNew = streamAdopted.querySelector('.pagination'); + formPagination.replaceChild(paginationNew, paginationOld); - const bigMarkAsRead = document.getElementById('bigMarkAsRead'); - if (bigMarkAsRead) { - if (context.display_order === 'ASC') { - document.querySelector('#nav_menu_read_all .read_all').formAction = bigMarkAsRead.formAction; - } else { - bigMarkAsRead.formAction = document.querySelector('#nav_menu_read_all .read_all').formAction; - } + const bigMarkAsRead = document.getElementById('bigMarkAsRead'); + if (bigMarkAsRead) { + if (context.display_order === 'ASC') { + document.querySelector('#nav_menu_read_all .read_all').formAction = bigMarkAsRead.formAction; + } else { + bigMarkAsRead.formAction = document.querySelector('#nav_menu_read_all .read_all').formAction; } + } - document.querySelectorAll('[id^=day_]').forEach(function (div) { - const ids = document.querySelectorAll('[id="' + div.id + '"]'); - for (let i = ids.length - 1; i > 0; i--) { //Keep only the first - ids[i].remove(); - } - }); - - init_load_more(box_load_more); - - const div_load_more = document.getElementById('load_more'); - if (bigMarkAsRead) { - bigMarkAsRead.removeAttribute('disabled'); - } - if (div_load_more) { - div_load_more.classList.remove('loading'); + document.querySelectorAll('[id^=day_]').forEach(function (div) { + const ids = document.querySelectorAll('[id="' + div.id + '"]'); + for (let i = ids.length - 1; i > 0; i--) { // Keep only the first + ids[i].remove(); } + }); - load_more = false; - }; + init_load_more(box_load_more); + + const div_load_more = document.getElementById('load_more'); + if (bigMarkAsRead) { + bigMarkAsRead.removeAttribute('disabled'); + } + if (div_load_more) { + div_load_more.classList.remove('loading'); + } + + load_more = false; + }; req.send(); } -var freshrssLoadMoreEvent = document.createEvent('Event'); +const freshrssLoadMoreEvent = document.createEvent('Event'); freshrssLoadMoreEvent.initEvent('freshrss:load-more', true, true); function init_load_more(box) { @@ -1569,23 +1577,23 @@ function init_load_more(box) { url_load_more = next_link.href; next_link.onclick = function (e) { - load_more_posts(); - return false; - }; + load_more_posts(); + return false; + }; } -// +// function init_confirm_action() { document.body.onclick = function (ev) { - const b = ev.target.closest('.confirm'); - if (b) { - let str_confirmation = this.getAttribute('data-str-confirm'); - if (!str_confirmation) { - str_confirmation = context.i18n.confirmation_default; - } - return confirm(str_confirmation); + const b = ev.target.closest('.confirm'); + if (b) { + let str_confirmation = this.getAttribute('data-str-confirm'); + if (!str_confirmation) { + str_confirmation = context.i18n.confirmation_default; } - }; + return confirm(str_confirmation); + } + }; document.querySelectorAll('button.confirm').forEach(function (b) { b.disabled = false; }); } @@ -1594,10 +1602,10 @@ function faviconNbUnread(n) { const t = document.querySelector('.category.all .title'); n = t ? str2int(t.getAttribute('data-unread')) : 0; } - //http://remysharp.com/2010/08/24/dynamic-favicons/ - const canvas = document.createElement('canvas'), - link = document.getElementById('favicon').cloneNode(true), - ratio = window.devicePixelRatio; + // http://remysharp.com/2010/08/24/dynamic-favicons/ + const canvas = document.createElement('canvas'); + const link = document.getElementById('favicon').cloneNode(true); + const ratio = window.devicePixelRatio; if (canvas.getContext && link) { canvas.height = canvas.width = 16 * ratio; const img = document.createElement('img'); @@ -1651,7 +1659,7 @@ function init_normal() { window.onbeforeunload = function (e) { const sidebar = document.getElementById('sidebar'); - if (sidebar) { //Save sidebar scroll position + if (sidebar) { // Save sidebar scroll position sessionStorage.setItem('FreshRSS_sidebar_scrollTop', sidebar.scrollTop); } if (mark_read_queue && mark_read_queue.length > 0) { @@ -1687,7 +1695,7 @@ function init_afterDOM() { } } -init_beforeDOM(); //Can be called before DOM is fully loaded +init_beforeDOM(); // Can be called before DOM is fully loaded if (document.readyState && document.readyState !== 'loading') { init_afterDOM(); diff --git a/p/scripts/preview.js b/p/scripts/preview.js index d52657a5a..48675d42c 100644 --- a/p/scripts/preview.js +++ b/p/scripts/preview.js @@ -1,12 +1,10 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; -/* jshint esversion:6, strict:global */ - -let rendered_node = null, - rendered_view = null, - raw_node = null, - raw_view = null; +'use strict'; +let rendered_node = null; +let rendered_view = null; +let raw_node = null; +let raw_view = null; function update_ui() { if (rendered_node.checked && !raw_node.checked) { @@ -29,7 +27,6 @@ function init_afterDOM() { raw_node.addEventListener('click', update_ui); } - if (document.readyState && document.readyState !== 'loading') { init_afterDOM(); } else { diff --git a/p/scripts/statsWithChartjs.js b/p/scripts/statsWithChartjs.js index 0ca511132..2f5c39ce8 100644 --- a/p/scripts/statsWithChartjs.js +++ b/p/scripts/statsWithChartjs.js @@ -1,7 +1,6 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 -"use strict"; +'use strict'; /* globals Chart */ -/* jshint esversion:6, strict:global */ function initCharts() { if (!window.Chart) { @@ -14,13 +13,13 @@ function initCharts() { const jsonData = document.getElementsByClassName('jsonData-stats'); - var jsonDataParsed; - var chartConfig; + let jsonDataParsed; + let chartConfig; - for (var i = 0; i < jsonData.length; i++) { + for (let i = 0; i < jsonData.length; i++) { jsonDataParsed = JSON.parse(jsonData[i].innerHTML); - switch(jsonDataParsed.charttype) { + switch (jsonDataParsed.charttype) { case 'bar': chartConfig = jsonChartBar(jsonDataParsed.label, jsonDataParsed.data, jsonDataParsed.xAxisLabels); break; @@ -28,13 +27,13 @@ function initCharts() { chartConfig = jsonChartDoughnut(jsonDataParsed.labels, jsonDataParsed.data); break; case 'barWithAverage': - chartConfig = jsonChartBarWithAvarage(jsonDataParsed.labelBarChart, jsonDataParsed.dataBarChart, jsonDataParsed.labelAverage, jsonDataParsed.dataAverage, jsonDataParsed.xAxisLabels); + chartConfig = jsonChartBarWithAvarage(jsonDataParsed.labelBarChart, jsonDataParsed.dataBarChart, + jsonDataParsed.labelAverage, jsonDataParsed.dataAverage, jsonDataParsed.xAxisLabels); } - new Chart( - document.getElementById(jsonDataParsed.canvasID), - chartConfig - ); + /* eslint-disable no-new */ + new Chart(document.getElementById(jsonDataParsed.canvasID), chartConfig); + /* eslint-enable no-new */ } if (window.console) { @@ -55,25 +54,25 @@ function jsonChartBar(label, data, xAxisLabels = '') { barPercentage: 1.0, categoryPercentage: 1.0, order: 2, - }] + }], }, options: { scales: { y: { - beginAtZero: true + beginAtZero: true, }, x: { grid: { display: false, - } - } + }, + }, }, plugins: { legend: { display: false, - } - } - } + }, + }, + }, }; } @@ -84,22 +83,22 @@ function jsonChartDoughnut(labels, data) { labels: labels, datasets: [{ backgroundColor: [ - '#0b84a5', //petrol + '#0b84a5', // petrol '#f6c85f', // sand - '#6f4e7c', //purple - '#9dd866', //green - '#ca472f', //red - '#ffa056', //orange + '#6f4e7c', // purple + '#9dd866', // green + '#ca472f', // red + '#ffa056', // orange '#8dddd0', // turkis '#f6c85f', // sand - '#6f4e7c', //purple - '#9dd866', //green - '#ca472f', //red - '#ffa056', //orange + '#6f4e7c', // purple + '#9dd866', // green + '#ca472f', // red + '#ffa056', // orange '#8dddd0', // turkis ], data: data, - }] + }], }, options: { layout: { @@ -109,9 +108,9 @@ function jsonChartDoughnut(labels, data) { legend: { position: 'bottom', align: 'start', - } - } - } + }, + }, + }, }; } @@ -133,15 +132,15 @@ function jsonChartBarWithAvarage(labelBarChart, dataBarChart, labelAverage, data { // average line chart type: 'line', - label: labelAverage, // Todo: i18n + label: labelAverage, // Todo: i18n borderColor: 'rgb(192,216,0)', data: { - '-30' : dataAverage, - '-1' : dataAverage, + '-30': dataAverage, + '-1': dataAverage, }, order: 1, - } - ] + }, + ], }, options: { @@ -151,41 +150,41 @@ function jsonChartBarWithAvarage(labelBarChart, dataBarChart, labelAverage, data }, x: { ticks: { - callback: function(val){ + callback: function (val) { if (xAxisLabels.length > 0) { return xAxisLabels[val]; } else { return val; } - } + }, }, grid: { display: false, - } - } + }, + }, }, elements: { point: { radius: 0, - } + }, }, plugins: { tooltip: { callbacks: { - title: function(tooltipitem) { + title: function (tooltipitem) { if (xAxisLabels.length > 0) { return xAxisLabels[tooltipitem[0].dataIndex]; } else { return tooltipitem[0].label; } - } - } + }, + }, }, legend: { display: false, - } - } - } + }, + }, + }, }; } diff --git a/p/themes/base-theme/template.css b/p/themes/base-theme/template.css index 2ec7dea34..6d3279bcd 100644 --- a/p/themes/base-theme/template.css +++ b/p/themes/base-theme/template.css @@ -879,7 +879,7 @@ input[type="search"] { } .subtitle > div:not(:first-of-type)::before { - content: ' · '; + content: ' · '; } br { diff --git a/p/themes/base-theme/template.rtl.css b/p/themes/base-theme/template.rtl.css index d6f0d0168..f74b9f9da 100644 --- a/p/themes/base-theme/template.rtl.css +++ b/p/themes/base-theme/template.rtl.css @@ -465,7 +465,7 @@ a.btn { /*=== Boxes */ .box { - margin: 20px 10px; + margin: 20px 0 20px 20px; display: inline-block; max-width: 95%; width: 20rem; @@ -473,6 +473,11 @@ a.btn { vertical-align: top; } +.box.visible-semi { + border-style: dashed; + opacity: 0.5; +} + .box .box-title { position: relative; font-size: 1.2rem; @@ -874,7 +879,7 @@ input[type="search"] { } .subtitle > div:not(:first-of-type)::before { - content: ' · '; + content: ' · '; } br { @@ -1114,7 +1119,7 @@ br { display: none; position: fixed; top: 2%; bottom: 2%; - left: 3%; right: 3%; + right: 3%; left: 3%; overflow: auto; } diff --git a/package.json b/package.json new file mode 100644 index 000000000..4ae01c19a --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "freshrss", + "description": "A free, self-hostable aggregator", + "homepage": "https://freshrss.org/", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/FreshRSS/FreshRSS/issues" + }, + "keywords": [ + "news", + "aggregator", + "RSS", + "Atom", + "WebSub" + ], + "repository": { + "type": "git", + "url": "https://github.com/FreshRSS/FreshRSS.git" + }, + "license": "AGPL-3.0", + "scripts": { + "eslint": "eslint --ext .js .", + "eslint_fix": "eslint --fix --ext .js .", + "rtlcss": "rtlcss -d p/themes && find . -type f -name '*.rtl.rtl.css' -delete", + "stylelint": "stylelint '**/*.css' && stylelint --syntax scss '**/*.scss'", + "stylelint_fix": "stylelint --fix '**/*.css' && stylelint --fix --syntax scss '**/*.scss'", + "test": "npm run eslint && npm run stylelint", + "fix": "npm run rtlcss && npm run stylelint_fix && npm run eslint_fix" + }, + "devDependencies": { + "eslint": "^7.32.0", + "eslint-config-standard": "^16.0.3", + "eslint-plugin-import": "^2.24.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.0", + "rtlcss": "^3.4.0", + "stylelint": "^13.13.1", + "stylelint-config-recommended-scss": "^4.3.0", + "stylelint-order": "^4.1.0", + "stylelint-scss": "^3.21.0" + } +}