diff --git a/gulp/res/js/filters.js b/gulp/res/js/filters.js index 785a76f3..9da17317 100644 --- a/gulp/res/js/filters.js +++ b/gulp/res/js/filters.js @@ -1,52 +1,64 @@ const getFiltersFromLocalStorage = () => { - const savedFilters = JSON.parse(localStorage.getItem('filters')); - /* i havent actually checked if the serialization overhead is worth the improvement in filter speed, i only assumed. - So if it turns out this is slower, i'd accept a PR to change it :^) */ + const savedFilters = JSON.parse(localStorage.getItem('filters1')); return savedFilters.reduce((acc, filter) => { - acc[filter.type].add(filter.type.endsWith('r') ? new RegExp(filter.val) : filter.val); + const regexFilter = filter.type.endsWith('r'); + if (regexFilter) { + acc[filter.type].push(new RegExp(filter.val, 'i')); //todo: serialize flags? probs not necessary + } else { + acc[filter.type].add(filter.val); + } return acc; }, { single: new Set(), fid: new Set(), fname: new Set(), + fsub: new Set(), ftrip: new Set(), - fnamer: new Set(), - ftripr: new Set(), + fnamer: [], + ftripr: [], + fsubr: [], }); }; -let { single, fid, fname, ftrip, fnamer, ftripr } = getFiltersFromLocalStorage(); +let { single, fid, fname, ftrip, fsub, fnamer, ftripr, fsubr } = getFiltersFromLocalStorage(); let filtersTable; const updateFiltersTable = () => { - [...filtersTable.children].slice(2).forEach(row => row.remove()); - filtersTable.insertAdjacentHTML('beforeend', filters({filterArr: JSON.parse(localStorage.getItem('filters'))})) + [...filtersTable.children].slice(3).forEach(row => row.remove()); + filtersTable.insertAdjacentHTML('beforeend', filters({filterArr: JSON.parse(localStorage.getItem('filters1'))})) const closeButtons = filtersTable.querySelectorAll('.close'); for (let elem of closeButtons) { - const { type: closeType, data: closeData } = elem.dataset; + let { type: closeType, data: closeData } = elem.dataset; + if (closeType.endsWith('r')) { + closeData = new RegExp(closeData, 'i'); + } elem.addEventListener('click', () => { toggleFilter(closeType, closeData) }); } } const updateSavedFilters = () => { - setLocalStorage('filters', JSON.stringify([ + setLocalStorage('filters1', JSON.stringify([ ...([...single].map(x => ({type:'single', val:x}))), ...([...fid].map(x => ({type:'fid', val:x}))), ...([...ftrip].map(x => ({type:'ftrip', val:x}))), ...([...fname].map(x => ({type:'fname', val:x}))), - ...([...fnamer].map(x => ({type:'fnamer', val:x.source.toString()}))), - ...([...ftripr].map(x => ({type:'ftripr', val:x.source.toString()}))), + ...([...fsub].map(x => ({type:'fsub', val:x}))), + ...fnamer.map(x => ({type:'fnamer', val:x.source.toString()})), + ...ftripr.map(x => ({type:'ftripr', val:x.source.toString()})), + ...fsubr.map(x => ({type:'fsubr', val:x.source.toString()})), ])); updateFiltersTable(); }; const anyFilterMatches = (filteringPost) => { - const { board, postId, userId, name, tripcode } = filteringPost.dataset; + const { board, postId, userId, name, subject, tripcode } = filteringPost.dataset; return fid.has(userId) || fname.has(name) || ftrip.has(tripcode) -// || fnamer.some(r => r.test(name)) - // || ftripr.some(r => r.test(tripcode)) + || fsub.has(tripcode) + || fnamer.some(r => r.test(name)) + || ftripr.some(r => r.test(tripcode)) + || fsubr.some(r => r.test(subject)) } const togglePostsHidden = (posts, state) => { @@ -63,8 +75,8 @@ const togglePostsHidden = (posts, state) => { const getPostsByRegex = (attribute, regex) => { const matches = []; for (let elem of document.querySelectorAll(`[${attribute}]`)) { - const value = element.getAttribute(attribute).toString(); - if (regex.test(value)) { + const value = elem.getAttribute(attribute).toString(); + if (regex.test(value) === true) { matches.push(elem); } } @@ -94,6 +106,12 @@ const getPostsByFilter = (type, data) => { case 'ftripr': posts = getPostsByRegex('data-tripcode', data); break; + case 'fsub': + posts = document.querySelectorAll(`[data-subject="${CSS.escape(data)}"]`); + break; + case 'fsubr': + posts = getPostsByRegex('data-subject', data); + break; default: break; } @@ -113,13 +131,28 @@ const setFilterState = (type, data, state) => { fname[addOrDelete](data); break; case 'fnamer': - fnamer[addOrDelete](data.source.toString()); + fnamer = fnamer.filter(r => r.source != data.source); + if (state) { + fnamer.push(data); + } break; case 'ftrip': ftrip[addOrDelete](data); break; case 'ftripr': - ftripr[addOrDelete](data.source.toString()); + ftripr = ftripr.filter(r => r.source != data.source); + if (state) { + ftripr.push(data); + } + break; + case 'fsub': + fsub[addOrDelete](data); + break; + case 'fsubr': + fsubr = fsubr.filter(r => r.source != data.source); + if (state) { + fsubr.push(data); + } break; default: break; @@ -127,9 +160,7 @@ const setFilterState = (type, data, state) => { }; const toggleFilter = (filterType, filterData, state) => { - //console.log('filtering', filterType, filterData, state); const posts = getPostsByFilter(filterType, filterData); - if (posts.length === 0) { return; } setFilterState(filterType, filterData, state); togglePostsHidden(posts, state); updateSavedFilters(); @@ -151,6 +182,9 @@ const postMenuChange = function(e) { case 'fname': filterData = postDataset.name; break; + case 'fsub': + filterData = postDataset.subject; + break; } toggleFilter(filterType, filterData, hiding); this.value = ''; @@ -172,17 +206,21 @@ const getHiddenElems = () => { for (let name of fname) { posts = posts.concat(getPostsByFilter('fname', name)); } + for (let subject of fsub) { + posts = posts.concat(getPostsByFilter('fsub', subject)); + } for (let tripcode of ftrip) { posts = posts.concat(getPostsByFilter('ftrip', tripcode)); } - -// for (let namer of fnamer) { -// posts = posts.concat(getPostsByFilter('fname', namer)); -// } -// for (let tripcoder of ftripr) { -// posts = posts.concat(getPostsByFilter('ftrip', tripcoder)); -// } - + for (let namer of fnamer) { + posts = posts.concat(getPostsByFilter('fnamer', namer)); + } + for (let tripcoder of ftripr) { + posts = posts.concat(getPostsByFilter('ftripr', tripcoder)); + } + for (let subr of fsubr) { + posts = posts.concat(getPostsByFilter('fsubr', subr)); + } return posts; }; @@ -203,14 +241,26 @@ window.addEventListener('settingsReady', function(e) { filtersTable = document.getElementById('advancedfilters'); updateFiltersTable(); + const filtersForm = document.getElementById('filter-form'); + filtersForm.addEventListener('submit', (e) => { + e.preventDefault(); + const isRegex = filtersForm.elements.regex.checked; + const type = `${filtersForm.elements.type.value}${isRegex ? 'r' : ''}`; + const val = isRegex ? new RegExp(filtersForm.elements.value.value, 'i') : filtersForm.elements.value.value; + console.log('adding filter', type, val); + toggleFilter(type, val, true); + }) + const filterClearButton = document.getElementById('filters-clear'); const clearFilters = () => { single = new Set(), fid = new Set(), fname = new Set(), + fsub = new Set(), ftrip = new Set(), - fnamer = new Set(), - ftripr = new Set(), + fnamer = [], + ftripr = [], + fsubr = [], updateFiltersTable(); togglePostsHidden(document.querySelectorAll('.post-container'), false); updateSavedFilters(); diff --git a/gulp/res/js/localstorage.js b/gulp/res/js/localstorage.js index b6618e7c..91738329 100644 --- a/gulp/res/js/localstorage.js +++ b/gulp/res/js/localstorage.js @@ -48,7 +48,7 @@ setDefaultLocalStorage('yous-setting', settings.showYous); setDefaultLocalStorage('dragtop', null); setDefaultLocalStorage('dragleft', null); -setDefaultLocalStorage('filters', '[]'); +setDefaultLocalStorage('filters1', '[]'); setDefaultLocalStorage('yous', '[]'); setDefaultLocalStorage('name', ''); setDefaultLocalStorage('theme', 'default'); diff --git a/gulp/res/js/pugfilters.js b/gulp/res/js/pugfilters.js index 0d2e93da..22cdb0b0 100644 --- a/gulp/res/js/pugfilters.js +++ b/gulp/res/js/pugfilters.js @@ -6,25 +6,30 @@ var pug_match_html=/["&<>]/;function filters(locals) {var pug_html = "", pug_mix (function (filterArr) { pug_mixins["filters"] = pug_interp = function(filterArr){ var block = (this && this.block), attributes = (this && this.attributes) || {}; -const filterTypeMap = { single: 'single', fid: 'ID', fname: 'name', ftrip: 'tripcode', fnamer: 'name regex', ftripr: 'tripcode regex' } +const filterTypeMap = { single: 'Single', fid: 'ID', fname: 'Name', ftrip: 'Tripcode', fnamer: 'Name', ftripr: 'Tripcode', fsub: 'Subject', fsubr: 'Subject' } +if (filterArr.length > 0) { // iterate filterArr ;(function(){ var $$obj = filterArr; if ('number' == typeof $$obj.length) { for (var pug_index0 = 0, $$l = $$obj.length; pug_index0 < $$l; pug_index0++) { var filter = $$obj[pug_index0]; -pug_html = pug_html + "\u003Ctr\u003E\u003Ctd\u003E" + (pug_escape(null == (pug_interp = filterTypeMap[filter.type]) ? "" : pug_interp)) + "\u003C\u002Ftd\u003E\u003Ctd\u003E" + (pug_escape(null == (pug_interp = filter.val.toString()) ? "" : pug_interp)) + "\u003C\u002Ftd\u003E\u003Ctd\u003E\u003Ca" + (" class=\"close\""+pug_attr("data-type", filter.type, true, false)+pug_attr("data-data", filter.val, true, false)) + "\u003EX\u003C\u002Fa\u003E\u003C\u002Ftd\u003E\u003C\u002Ftr\u003E"; +pug_html = pug_html + "\u003Ctr\u003E\u003Ctd\u003E" + (pug_escape(null == (pug_interp = filterTypeMap[filter.type]) ? "" : pug_interp)) + "\u003C\u002Ftd\u003E\u003Ctd\u003E" + (pug_escape(null == (pug_interp = filter.val.toString()) ? "" : pug_interp)) + "\u003C\u002Ftd\u003E\u003Ctd\u003E\u003Cinput" + (pug_attr("disabled", true, true, false)+" type=\"checkbox\""+pug_attr("checked", filter.type.endsWith('r'), true, false)) + "\u002F\u003E\u003C\u002Ftd\u003E\u003Ctd\u003E\u003Ca" + (" class=\"right close\""+pug_attr("data-type", filter.type, true, false)+pug_attr("data-data", filter.val, true, false)) + "\u003EX\u003C\u002Fa\u003E\u003C\u002Ftd\u003E\u003C\u002Ftr\u003E"; } } else { var $$l = 0; for (var pug_index0 in $$obj) { $$l++; var filter = $$obj[pug_index0]; -pug_html = pug_html + "\u003Ctr\u003E\u003Ctd\u003E" + (pug_escape(null == (pug_interp = filterTypeMap[filter.type]) ? "" : pug_interp)) + "\u003C\u002Ftd\u003E\u003Ctd\u003E" + (pug_escape(null == (pug_interp = filter.val.toString()) ? "" : pug_interp)) + "\u003C\u002Ftd\u003E\u003Ctd\u003E\u003Ca" + (" class=\"close\""+pug_attr("data-type", filter.type, true, false)+pug_attr("data-data", filter.val, true, false)) + "\u003EX\u003C\u002Fa\u003E\u003C\u002Ftd\u003E\u003C\u002Ftr\u003E"; +pug_html = pug_html + "\u003Ctr\u003E\u003Ctd\u003E" + (pug_escape(null == (pug_interp = filterTypeMap[filter.type]) ? "" : pug_interp)) + "\u003C\u002Ftd\u003E\u003Ctd\u003E" + (pug_escape(null == (pug_interp = filter.val.toString()) ? "" : pug_interp)) + "\u003C\u002Ftd\u003E\u003Ctd\u003E\u003Cinput" + (pug_attr("disabled", true, true, false)+" type=\"checkbox\""+pug_attr("checked", filter.type.endsWith('r'), true, false)) + "\u002F\u003E\u003C\u002Ftd\u003E\u003Ctd\u003E\u003Ca" + (" class=\"right close\""+pug_attr("data-type", filter.type, true, false)+pug_attr("data-data", filter.val, true, false)) + "\u003EX\u003C\u002Fa\u003E\u003C\u002Ftd\u003E\u003C\u002Ftr\u003E"; } } }).call(this); +} +else { +pug_html = pug_html + "\u003Ctd colspan=\"4\"\u003ENo Filters\u003C\u002Ftd\u003E"; +} }; pug_mixins["filters"](filterArr); }.call(this, "filterArr" in locals_for_with ? diff --git a/views/mixins/filters.pug b/views/mixins/filters.pug index 9673248d..fef800d3 100644 --- a/views/mixins/filters.pug +++ b/views/mixins/filters.pug @@ -1,7 +1,11 @@ mixin filters(filterArr) - - const filterTypeMap = { single: 'single', fid: 'ID', fname: 'name', ftrip: 'tripcode', fnamer: 'name regex', ftripr: 'tripcode regex' } - each filter in filterArr - tr - td #{filterTypeMap[filter.type]} - td #{filter.val.toString()} - td: a.close(data-type=filter.type data-data=filter.val) X + - const filterTypeMap = { single: 'Single', fid: 'ID', fname: 'Name', ftrip: 'Tripcode', fnamer: 'Name', ftripr: 'Tripcode', fsub: 'Subject', fsubr: 'Subject' } + if filterArr.length > 0 + each filter in filterArr + tr + td #{filterTypeMap[filter.type]} + td #{filter.val.toString()} + td: input(disabled type='checkbox' checked=filter.type.endsWith('r')) + td: a.right.close(data-type=filter.type data-data=filter.val) X + else + td(colspan=4) No Filters diff --git a/views/mixins/modal.pug b/views/mixins/modal.pug index e68c2b80..e6e7aacc 100644 --- a/views/mixins/modal.pug +++ b/views/mixins/modal.pug @@ -140,11 +140,26 @@ mixin modal(data) textarea#customcss-setting(rows=7) .row .table-container.text-center - table - tbody#advancedfilters - tr - th(colspan=3) Filters - tr - th Type - th Value - th: input.right#filters-clear(type='button' value='Clear') + form#filter-form + table + tbody#advancedfilters + tr + th Post Filters + th + th + th + tr + td Type + td Value + td Regex? + td: input.right#filters-clear(type='button' value='Clear') + tr + td: select(name='type') + option(value='fname') Name + option(value='ftrip') Tripcode + option(value='fsub') Subject + td + input#filter-value-input(required type='text' name='value') + td + input(type='checkbox' name='regex') + td: input.right(type='submit' value='Add') diff --git a/views/mixins/post.pug b/views/mixins/post.pug index b0f76173..048ff6b9 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 data-name=post.name data-tripcode=post.tripcode) + 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 data-subject=post.subject) - const postURL = `/${post.board}/${(modview || manage || globalmanage) ? 'manage/' : ''}thread/${post.thread || post.postId}.html`; .post-info span @@ -59,6 +59,8 @@ mixin post(post, truncate, manage=false, globalmanage=false, ban=false) option(value='fid') Filter ID if post.name option(value='fname') Filter Name + if post.subject + option(value='fsub') Filter Subject if post.tripcode option(value='ftrip') Filter Tripcode .post-data