diff --git a/gulp/res/js/csstoggles.js b/gulp/res/js/csstoggles.js new file mode 100644 index 00000000..59987d02 --- /dev/null +++ b/gulp/res/js/csstoggles.js @@ -0,0 +1,62 @@ +const renderCSSLink = document.createElement('style'); +renderCSSLink.type = 'text/css'; +renderCSSLink.id = 'rendercss'; +document.head.appendChild(renderCSSLink); +const renderSheet = renderCSSLink.sheet; +const rulesKey = renderSheet.rules ? 'rules' : 'cssRules'; + +class CssToggle { + constructor (settingId, localStorageKey, localStorageDefault, settingCss) { + this.localStorageKey = localStorageKey; + this.localStorageDefault = localStorageDefault; + setDefaultLocalStorage(this.localStorageKey, this.localStorageDefault); + this.settingBoolean = localStorage.getItem(this.localStorageKey) == 'true'; + this.settingCss = settingCss; + window.addEventListener('settingsReady', () => { + //on event fire, set boolean to correct checked stats + this.setting = document.getElementById(settingId); + this.setting.checked = this.settingBoolean; + this.setting.addEventListener('change', () => { + this.toggle(); + }, false); + }); + this.apply(); + } + toggle () { + this.settingBoolean = !this.settingBoolean; + console.log('toggling', this.localStorageKey, this.settingBoolean); + this.apply(); + setLocalStorage(this.localStorageKey, this.settingBoolean); + } + apply () { + if (this.settingBoolean) { + renderSheet.insertRule(this.settingCss); + } else { + for (let i = 0; i < renderSheet[rulesKey].length; i++) { + if (renderSheet[rulesKey][i].selectorText == this.settingCss.split(' {')[0]) { + renderSheet.deleteRule(i); + } + } + } + } +}; + +//define the css +const hidePostStubsCss = `.post-container.hidden, .catalog-tile.hidden { visibility: hidden;margin-top: -1.5em;height: 0; }`; +const hideThumbnailsCss = `.file-thumb, .catalog-thumb { visibility: hidden !important; }`; +const hideRecursiveCss = `.op.hidden ~ .anchor, .op.hidden ~ .post-container { display: none; }`; +const heightUnlimitCss = `img, video { max-height: unset; }`; +const crispCss = `img { image-rendering: crisp-edges; }`; +const nonColorIdsCss = `.user-id { background: transparent none repeat scroll 0% 0% !important; border-color: transparent; text-shadow: none; color: var(--font-color); }`; +const alwaysShowSpoilersCss = `.spoiler { color: var(--font-color) !important; background: transparent none repeat scroll 0% 0%; outline: 1px solid black; cursor: auto; }`; +const smoothScrollingCss = `html { scroll-behavior: smooth; }`; + +//make classes with css +new CssToggle('hiderecursive-setting', 'hiderecursive', settings.hideRecursive, hideRecursiveCss); +new CssToggle('heightlimit-setting', 'heightlimit', settings.heightUnlimit, heightUnlimitCss); +new CssToggle('crispimages-setting', 'crispimages', settings.crispImages, crispCss); +new CssToggle('hidethumbnails-setting', 'hidethumbnails', settings.hideThumbnails, hideThumbnailsCss); +new CssToggle('noncolorids-setting', 'noncolorids', settings.nonColorIds, nonColorIdsCss); +new CssToggle('alwaysshowspoilers-setting', 'alwaysshowspoilers', settings.alwaysShowSpoilers, alwaysShowSpoilersCss); +new CssToggle('hidepoststubs-setting', 'hidepoststubs', settings.hidePostStubs, hidePostStubsCss); +new CssToggle('smoothscrolling-setting', 'smoothscrolling', settings.smoothScrolling, smoothScrollingCss); diff --git a/gulp/res/js/filters.js b/gulp/res/js/filters.js new file mode 100644 index 00000000..3940c68b --- /dev/null +++ b/gulp/res/js/filters.js @@ -0,0 +1,145 @@ +const fileInput = document.getElementById('file'); +if (fileInput) { + //not using display: none because we still want to show the browser prompt for a "required" file + fileInput.style.position = 'absolute'; + fileInput.style.border = 'none'; + fileInput.style.height = '0'; + fileInput.style.width = '0'; + fileInput.style.opacity = '0'; +} + +//lists separate until i come up with something better +let hidePostsList; +let { hiddenSingle, filteredId, filteredName, filteredTripcode } = JSON.parse(localStorage.getItem('filters')); +hiddenSingle = new Set(hiddenSingle); +filteredId = new Set(filteredId); +filteredName = new Set(filteredName); +filteredTripcode = new Set(filteredTripcode); + +const updateSavedFilters = () => { + hidePostsList.value = [...hiddenSingle, ...filteredId, ...filteredName, ...filteredTripcode]; + setLocalStorage('filters', JSON.stringify({ + hiddenSingle: [...hiddenSingle], + filteredId: [...filteredId], + filteredName: [...filteredName], + filteredTripcode: [...filteredTripcode], + })); +} + +const togglePostsHidden = (posts, state) => { + for (let elem of posts) { + elem.classList[state ? 'add' : 'remove']('hidden'); + } +} + +const getPostsByFilter = (type, data) => { + let posts = []; + switch (type) { + case 'hide': + const post = document.querySelector(`[data-board="${data.board}"][data-post-id="${data.postId}"]`); + posts = post ? [post] : []; + break; + case 'id': + posts = document.querySelectorAll(`[data-user-id="${data.userId}"]`); + break; + case 'name': + posts = document.querySelectorAll(`[data-name="${CSS.escape(data.name)}"]`); + break; + case 'tripcode': + posts = document.querySelectorAll(`[data-tripcode="${CSS.escape(data.tripcode)}"]`); + break; + default: + break; + } + return [...posts] +} + +const setFilterState = (type, data, state) => { + switch (type) { + case 'hide': + hiddenSingle[state ? 'add' : 'delete'](`${data.board}-${data.postId}`); + break; + case 'id': + filteredId[state ? 'add' : 'delete'](data.userId); + break; + case 'name': + filteredName[state ? 'add' : 'delete'](data.name); + break; + case 'tripcode': + filteredTripcode[state ? 'add' : 'delete'](data.tripcode); + break; + default: + break; + } +} + +const postMenuChange = function(e) { + const postContainer = this.parentElement.parentElement.parentElement; + const filterType = this.value; + const posts = getPostsByFilter(filterType, postContainer.dataset); + if (posts.length === 0) { return; } +//TODO: unhiding + setFilterState(filterType, postContainer.dataset, true); + this.value = ''; + togglePostsHidden(posts, true); + updateSavedFilters(); +} + +for (let menu of document.getElementsByClassName('postmenu')) { + menu.value = ''; + menu.addEventListener('change', postMenuChange, false); +} + +const getHiddenElems = () => { + let posts = []; + for (let elem of hiddenSingle) { + const [board, postId] = elem.split('-'); + posts = posts.concat(getPostsByFilter('hide', { board, postId })); + } + for (let id of filteredId) { + posts = posts.concat(getPostsByFilter('id', { userId: id })); + } + for (let name of filteredName) { + posts = posts.concat(getPostsByFilter('name', { name })); + } + for (let tripcode of filteredTripcode) { + posts = posts.concat(getPostsByFilter('tripcode', { tripcode })); + } + return posts; +} + +togglePostsHidden(getHiddenElems(), true); + +window.addEventListener('addPost', function(e) { + const post = e.detail.post; + const { board, postId, userId, name, tripcode } = post.dataset; + if (filteredId.has(userId) + || filteredName.has(name) + || filteredTripcode.has(tripcode)) { + post.classList.add('hidden'); + } + const menu = post.querySelector('.postmenu'); + menu.value = ''; + menu.addEventListener('change', postMenuChange, false); + +}); + +window.addEventListener('settingsReady', function(e) { + + hidePostsList = document.getElementById('hiddenpostslist-setting'); + hidePostsList.value = [...hiddenSingle, ...filteredId, ...filteredName, ...filteredTripcode]; + + const hidePostsListClearButton = document.getElementById('hiddenpostslist-clear'); + const clearhidePostsList = () => { + togglePostsHidden(getHiddenElems(), false); + hidePostsList.value = ''; + hiddenSingle = new Set(); + filteredId = new Set(); + filteredName = new Set(); + filteredTripcode = new Set(); + updateSavedFilters(); + console.log('cleared hidden posts'); + } + hidePostsListClearButton.addEventListener('click', clearhidePostsList, false); + +}); diff --git a/gulp/res/js/hide.js b/gulp/res/js/hide.js deleted file mode 100644 index 76eb8a3c..00000000 --- a/gulp/res/js/hide.js +++ /dev/null @@ -1,188 +0,0 @@ -const fileInput = document.getElementById('file'); -if (fileInput) { - fileInput.style.position = 'absolute'; - fileInput.style.border = 'none'; - fileInput.style.height = '0'; - fileInput.style.width = '0'; - fileInput.style.opacity = '0'; -} - -let hidePostsList; -let hidden = new Set(JSON.parse(localStorage.getItem('hidden'))); - -const setHidden = (posts, hide) => { - if (posts && posts.length > 0) { - for (let i = 0; i < posts.length; i++) { - const post = posts[i]; - if (!post.dataset) { - continue; - } - const menu = post.querySelector('.postmenu'); - if (menu) { - for (let i = 0; i < menu.children.length; i++) { - if (hide) { - menu.children[i].innerText = 'Un'+menu.children[i].originalText; - } else { - menu.children[i].innerText = menu.children[i].originalText; - } - } - } - const { board, postId, userId } = post.dataset; - if (hide) { - post.classList.add('hidden'); - } else { - hidden.delete(`${board}-${postId}`); - post.classList.remove('hidden'); - } - } - } -} - -const changeOption = function(e) { - const option = this.value; - const postContainer = this.parentElement.parentElement.parentElement; - const { board, postId, userId } = postContainer.dataset; - let posts = [postContainer]; - const hiding = !option.startsWith('Un'); - if (option.endsWith('ID')) { - const idPosts = document.querySelectorAll(`[data-user-id="${userId}"]`); - if (idPosts && idPosts.length > 0) { - posts = idPosts; - } - if (hiding) { - hidden.add(userId); - } else { - hidden.delete(userId); - } - } - if (hiding) { - hidden.add(`${board}-${postId}`); - } - this.value = ''; - setHidden(posts, hiding); - const hiddenArray = [...hidden]; - hidePostsList.value = hiddenArray.toString(); - setLocalStorage('hidden', JSON.stringify(hiddenArray)); -} - -for (let menu of document.getElementsByClassName('postmenu')) { - menu.value = ''; - for (let i = 0; i < menu.children.length; i++) { - menu.children[i].originalText = menu.children[i].innerText; - } - menu.addEventListener('change', changeOption, false); -} - -const getHiddenElems = () => { - let posts = []; - for (let elem of hidden) { - if (elem.includes('-')) { - const [board, postId] = elem.split('-'); - const post = document.querySelector(`[data-board="${board}"][data-post-id="${postId}"]`); - if (post) { - posts.push(post); - } - } else { - const idPosts = document.querySelectorAll(`[data-user-id="${elem}"]`); - if (idPosts && idPosts.length > 0) { - posts = posts.concat(idPosts); - } - } - } - return posts; -} - -setHidden(getHiddenElems(), true); - -const renderCSSLink = document.createElement('style'); -renderCSSLink.type = 'text/css'; -renderCSSLink.id = 'rendercss'; -document.head.appendChild(renderCSSLink); -const renderSheet = renderCSSLink.sheet; -const rulesKey = renderSheet.rules ? 'rules' : 'cssRules'; - -class CssToggle { - constructor (settingId, localStorageKey, localStorageDefault, settingCss) { - this.localStorageKey = localStorageKey; - this.localStorageDefault = localStorageDefault; - setDefaultLocalStorage(this.localStorageKey, this.localStorageDefault); - this.settingBoolean = localStorage.getItem(this.localStorageKey) == 'true'; - this.settingCss = settingCss; - window.addEventListener('settingsReady', () => { - //on event fire, set boolean to correct checked stats - this.setting = document.getElementById(settingId); - this.setting.checked = this.settingBoolean; - this.setting.addEventListener('change', () => { - this.toggle(); - }, false); - }); - this.apply(); - } - toggle () { - this.settingBoolean = !this.settingBoolean; - console.log('toggling', this.localStorageKey, this.settingBoolean); - this.apply(); - setLocalStorage(this.localStorageKey, this.settingBoolean); - } - apply () { - if (this.settingBoolean) { - renderSheet.insertRule(this.settingCss); - } else { - for (let i = 0; i < renderSheet[rulesKey].length; i++) { - if (renderSheet[rulesKey][i].selectorText == this.settingCss.split(' {')[0]) { - renderSheet.deleteRule(i); - } - } - } - } -}; - -//define the css -const hidePostStubsCss = `.post-container.hidden, .catalog-tile.hidden { visibility: hidden;margin-top: -1.5em;height: 0; }`; -const hideThumbnailsCss = `.file-thumb, .catalog-thumb { visibility: hidden !important; }`; -const hideRecursiveCss = `.op.hidden ~ .anchor, .op.hidden ~ .post-container { display: none; }`; -const heightUnlimitCss = `img, video { max-height: unset; }`; -const crispCss = `img { image-rendering: crisp-edges; }`; -const nonColorIdsCss = `.user-id { background: transparent none repeat scroll 0% 0% !important; border-color: transparent; text-shadow: none; color: var(--font-color); }`; -const alwaysShowSpoilersCss = `.spoiler { color: var(--font-color) !important; background: transparent none repeat scroll 0% 0%; outline: 1px solid black; cursor: auto; }`; -const smoothScrollingCss = `html { scroll-behavior: smooth; }`; -//make classes with css -new CssToggle('hiderecursive-setting', 'hiderecursive', settings.hideRecursive, hideRecursiveCss); -new CssToggle('heightlimit-setting', 'heightlimit', settings.heightUnlimit, heightUnlimitCss); -new CssToggle('crispimages-setting', 'crispimages', settings.crispImages, crispCss); -new CssToggle('hidethumbnails-setting', 'hidethumbnails', settings.hideThumbnails, hideThumbnailsCss); -new CssToggle('noncolorids-setting', 'noncolorids', settings.nonColorIds, nonColorIdsCss); -new CssToggle('alwaysshowspoilers-setting', 'alwaysshowspoilers', settings.alwaysShowSpoilers, alwaysShowSpoilersCss); -new CssToggle('hidepoststubs-setting', 'hidepoststubs', settings.hidePostStubs, hidePostStubsCss); -new CssToggle('smoothscrolling-setting', 'smoothscrolling', settings.smoothScrolling, smoothScrollingCss); - -window.addEventListener('addPost', function(e) { - const post = e.detail.post; - const { board, postId, userId } = post.dataset; - const hiddenKey = `${board}-${postId}`; - if (hidden.has(hiddenKey) || hidden.has(userId)) { - post.classList.add('hidden'); - } - const menu = post.querySelector('.postmenu'); - for (let i = 0; i < menu.children.length; i++) { - menu.children[i].originalText = menu.children[i].innerText; - } - menu.value = ''; - menu.addEventListener('change', changeOption, false); -}); - -window.addEventListener('settingsReady', function(e) { - hidePostsList = document.getElementById('hiddenpostslist-setting'); - hidePostsList.value = [...hidden]; - const hidePostsListClearButton = document.getElementById('hiddenpostslist-clear'); - const clearhidePostsList = () => { - setHidden(getHiddenElems(), false); - hidden = new Set(); - hidePostsList.value = ''; - -setLocalStorage('hidden', '[]'); - console.log('cleared hidden posts'); - } - hidePostsListClearButton.addEventListener('click', clearhidePostsList, false); -}); - diff --git a/gulp/res/js/hover.js b/gulp/res/js/hover.js index f0d1ae52..493a7da0 100644 --- a/gulp/res/js/hover.js +++ b/gulp/res/js/hover.js @@ -190,7 +190,7 @@ window.addEventListener('settingsReady', function(e) { const clearHoverCacheList = () => { deleteStartsWith('hovercache'); hoverCacheList.value = ''; - console.log('cleared hover cache'); + console.log('cleared cache'); } hoverCacheListClearButton.addEventListener('click', clearHoverCacheList, false); }); diff --git a/gulp/res/js/localstorage.js b/gulp/res/js/localstorage.js index d692a58b..b31f5d41 100644 --- a/gulp/res/js/localstorage.js +++ b/gulp/res/js/localstorage.js @@ -1,4 +1,3 @@ - const isCatalog = window.location.pathname.endsWith('catalog.html'); const isThread = /\/\w+\/thread\/\d+.html/.test(window.location.pathname); const isModView = /\/\w+\/manage\/(thread\/)?(index|\d+).html/.test(window.location.pathname); @@ -18,9 +17,9 @@ function appendLocalStorageArray(key, value) { storedArray.push(value); setLocalStorage(key, JSON.stringify(storedArray)); } -function deleteStartsWith(startString = 'hovercache') { - //clears hover cache when localstorage gets full +function deleteStartsWith(startString='hovercache') { + //clears cache when localstorage gets full const hoverCaches = Object.keys(localStorage).filter(k => k.startsWith(startString)); for(let i = 0; i < hoverCaches.length; i++) { localStorage.removeItem(hoverCaches[i]); @@ -48,7 +47,7 @@ setDefaultLocalStorage('yous-setting', settings.showYous); setDefaultLocalStorage('dragtop', null); setDefaultLocalStorage('dragleft', null); -setDefaultLocalStorage('hidden', '[]'); +setDefaultLocalStorage('filters', '{"hiddenSingle":[],"filteredId":[],"filteredName":[],"filteredTripcode":[]}'); setDefaultLocalStorage('yous', '[]'); setDefaultLocalStorage('name', ''); setDefaultLocalStorage('theme', 'default'); diff --git a/gulpfile.js b/gulpfile.js index 87700952..4e462644 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -270,7 +270,7 @@ const settings = ${JSON.stringify(configs.frontendScriptDefault)}; `${paths.scripts.src}/forms.js`, `${paths.scripts.src}/*.js`, `!${paths.scripts.src}/dragable.js`, - `!${paths.scripts.src}/hide.js`, + `!${paths.scripts.src}/filters.js`, `!${paths.scripts.src}/yous.js`, `!${paths.scripts.src}/catalog.js`, `!${paths.scripts.src}/time.js`, @@ -278,17 +278,17 @@ const settings = ${JSON.stringify(configs.frontendScriptDefault)}; `!${paths.scripts.src}/timezone.js`, ]) .pipe(concat('all.js')) - .pipe(uglify({compress:false})) +// .pipe(uglify({compress:false})) .pipe(gulp.dest(paths.scripts.dest)); return gulp.src([ `${paths.scripts.src}/dragable.js`, `${paths.scripts.src}/yous.js`, - `${paths.scripts.src}/hide.js`, + `${paths.scripts.src}/filters.js`, `${paths.scripts.src}/catalog.js`, `${paths.scripts.src}/time.js`, ]) .pipe(concat('render.js')) - .pipe(uglify({compress:false})) +// .pipe(uglify({compress:false})) .pipe(gulp.dest(paths.scripts.dest)); } diff --git a/views/mixins/post.pug b/views/mixins/post.pug index da1d4bb1..83733acd 100644 --- a/views/mixins/post.pug +++ b/views/mixins/post.pug @@ -1,7 +1,7 @@ include ./report.pug mixin post(post, truncate, manage=false, globalmanage=false, ban=false) .anchor(id=post.postId) - div(class=`post-container ${post.thread || ban === true ? '' : 'op'}` data-board=post.board data-post-id=post.postId data-user-id=post.userId) + div(class=`post-container ${post.thread || ban === true ? '' : 'op'}` data-board=post.board data-post-id=post.postId data-user-id=post.userId data-name=post.name data-tripcode=post.tripcode) - const postURL = `/${post.board}/${(modview || manage || globalmanage) ? 'manage/' : ''}thread/${post.thread || post.postId}.html`; .post-info span @@ -54,9 +54,13 @@ mixin post(post, truncate, manage=false, globalmanage=false, ban=false) span.noselect: a(href=`${postURL}#postform`) [Reply] | select.jsonly.postmenu - option Hide + option(value='hide') Hide if post.userId - option Hide by ID + option(value='id') Filter ID + if post.name + option(value='name') Filter Name + if post.tripcode + option(value='tripcode') Filter Tripcode .post-data if post.files.length > 0 .post-files(class=(post.files.length > 1 ? 'fn' : ''))