|
|
|
@ -1,89 +1,160 @@ |
|
|
|
|
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'; |
|
|
|
|
} |
|
|
|
|
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 :^) */ |
|
|
|
|
return savedFilters.reduce((acc, filter) => { |
|
|
|
|
acc[filter.type].add(filter.type.endsWith('r') ? new RegExp(filter.val) : filter.val); |
|
|
|
|
return acc; |
|
|
|
|
}, { |
|
|
|
|
single: new Set(), |
|
|
|
|
fid: new Set(), |
|
|
|
|
fname: new Set(), |
|
|
|
|
ftrip: new Set(), |
|
|
|
|
fnamer: new Set(), |
|
|
|
|
ftripr: new Set(), |
|
|
|
|
}); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let { single, fid, fname, ftrip, fnamer, ftripr } = getFiltersFromLocalStorage(); |
|
|
|
|
|
|
|
|
|
//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); |
|
|
|
|
let filtersTable; |
|
|
|
|
const updateFiltersTable = () => { |
|
|
|
|
[...filtersTable.children].slice(2).forEach(row => row.remove()); |
|
|
|
|
filtersTable.insertAdjacentHTML('beforeend', filters({filterArr: JSON.parse(localStorage.getItem('filters'))})) |
|
|
|
|
const closeButtons = filtersTable.querySelectorAll('.close'); |
|
|
|
|
for (let elem of closeButtons) { |
|
|
|
|
const { type: closeType, data: closeData } = elem.dataset; |
|
|
|
|
elem.addEventListener('click', () => { toggleFilter(closeType, closeData) }); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const updateSavedFilters = () => { |
|
|
|
|
hidePostsList.value = [...hiddenSingle, ...filteredId, ...filteredName, ...filteredTripcode]; |
|
|
|
|
setLocalStorage('filters', JSON.stringify({ |
|
|
|
|
hiddenSingle: [...hiddenSingle], |
|
|
|
|
filteredId: [...filteredId], |
|
|
|
|
filteredName: [...filteredName], |
|
|
|
|
filteredTripcode: [...filteredTripcode], |
|
|
|
|
})); |
|
|
|
|
setLocalStorage('filters', 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()}))), |
|
|
|
|
])); |
|
|
|
|
updateFiltersTable(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const anyFilterMatches = (filteringPost) => { |
|
|
|
|
const { board, postId, userId, name, 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))
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const togglePostsHidden = (posts, state) => { |
|
|
|
|
for (let elem of posts) { |
|
|
|
|
elem.classList[state ? 'add' : 'remove']('hidden'); |
|
|
|
|
if (!state && !anyFilterMatches(elem)) { //possible fix for multiple filters & unhiding conflicts
|
|
|
|
|
elem.classList['remove']('hidden'); |
|
|
|
|
} else { |
|
|
|
|
elem.classList['add']('hidden'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
//I wish this wasn't necessary, but css selectors dont support regex :(
|
|
|
|
|
const getPostsByRegex = (attribute, regex) => { |
|
|
|
|
const matches = []; |
|
|
|
|
for (let elem of document.querySelectorAll(`[${attribute}]`)) { |
|
|
|
|
const value = element.getAttribute(attribute).toString(); |
|
|
|
|
if (regex.test(value)) { |
|
|
|
|
matches.push(elem); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return matches; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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] : []; |
|
|
|
|
case 'single': |
|
|
|
|
const [dataBoard, dataPostId] = data.split('-'); |
|
|
|
|
const singlePost = document.querySelector(`[data-board="${dataBoard}"][data-post-id="${dataPostId}"]`); |
|
|
|
|
posts = singlePost ? [singlePost] : []; |
|
|
|
|
break; |
|
|
|
|
case 'fid': |
|
|
|
|
posts = document.querySelectorAll(`[data-user-id="${data}"]`); |
|
|
|
|
break; |
|
|
|
|
case 'id': |
|
|
|
|
posts = document.querySelectorAll(`[data-user-id="${data.userId}"]`); |
|
|
|
|
case 'fname': |
|
|
|
|
posts = document.querySelectorAll(`[data-name="${CSS.escape(data)}"]`); |
|
|
|
|
break; |
|
|
|
|
case 'name': |
|
|
|
|
posts = document.querySelectorAll(`[data-name="${CSS.escape(data.name)}"]`); |
|
|
|
|
case 'fnamer': |
|
|
|
|
posts = getPostsByRegex('data-name', data); |
|
|
|
|
break; |
|
|
|
|
case 'tripcode': |
|
|
|
|
posts = document.querySelectorAll(`[data-tripcode="${CSS.escape(data.tripcode)}"]`); |
|
|
|
|
case 'ftrip': |
|
|
|
|
posts = document.querySelectorAll(`[data-tripcode="${CSS.escape(data)}"]`); |
|
|
|
|
break; |
|
|
|
|
case 'ftripr': |
|
|
|
|
posts = getPostsByRegex('data-tripcode', data); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
return [...posts] |
|
|
|
|
} |
|
|
|
|
return [...posts]; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const setFilterState = (type, data, state) => { |
|
|
|
|
const addOrDelete = state ? 'add' : 'delete'; |
|
|
|
|
switch (type) { |
|
|
|
|
case 'hide': |
|
|
|
|
hiddenSingle[state ? 'add' : 'delete'](`${data.board}-${data.postId}`); |
|
|
|
|
case 'single': |
|
|
|
|
single[addOrDelete](data); |
|
|
|
|
break; |
|
|
|
|
case 'id': |
|
|
|
|
filteredId[state ? 'add' : 'delete'](data.userId); |
|
|
|
|
case 'fid': |
|
|
|
|
fid[addOrDelete](data); |
|
|
|
|
break; |
|
|
|
|
case 'name': |
|
|
|
|
filteredName[state ? 'add' : 'delete'](data.name); |
|
|
|
|
case 'fname': |
|
|
|
|
fname[addOrDelete](data); |
|
|
|
|
break; |
|
|
|
|
case 'tripcode': |
|
|
|
|
filteredTripcode[state ? 'add' : 'delete'](data.tripcode); |
|
|
|
|
case 'fnamer': |
|
|
|
|
fnamer[addOrDelete](data.source.toString()); |
|
|
|
|
break; |
|
|
|
|
case 'ftrip': |
|
|
|
|
ftrip[addOrDelete](data); |
|
|
|
|
break; |
|
|
|
|
case 'ftripr': |
|
|
|
|
ftripr[addOrDelete](data.source.toString()); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const postMenuChange = function(e) { |
|
|
|
|
const postContainer = this.parentElement.parentElement.parentElement; |
|
|
|
|
const postDataset = postContainer.dataset |
|
|
|
|
const filterType = this.value; |
|
|
|
|
const posts = getPostsByFilter(filterType, postContainer.dataset); |
|
|
|
|
if (posts.length === 0) { return; } |
|
|
|
|
const hiding = !postContainer.classList.contains('hidden'); |
|
|
|
|
setFilterState(filterType, postContainer.dataset, hiding); |
|
|
|
|
let filterData; |
|
|
|
|
switch (filterType) { |
|
|
|
|
case 'single': |
|
|
|
|
filterData = `${postDataset.board}-${postDataset.postId}`; |
|
|
|
|
break; |
|
|
|
|
case 'fid': |
|
|
|
|
filterData = postDataset.userId; |
|
|
|
|
break; |
|
|
|
|
case 'fname': |
|
|
|
|
filterData = postDataset.name; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
toggleFilter(filterType, filterData, hiding); |
|
|
|
|
this.value = ''; |
|
|
|
|
togglePostsHidden(posts, hiding); |
|
|
|
|
updateSavedFilters(); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
for (let menu of document.getElementsByClassName('postmenu')) { |
|
|
|
|
menu.value = ''; |
|
|
|
@ -92,53 +163,59 @@ for (let menu of document.getElementsByClassName('postmenu')) { |
|
|
|
|
|
|
|
|
|
const getHiddenElems = () => { |
|
|
|
|
let posts = []; |
|
|
|
|
for (let elem of hiddenSingle) { |
|
|
|
|
const [board, postId] = elem.split('-'); |
|
|
|
|
posts = posts.concat(getPostsByFilter('hide', { board, postId })); |
|
|
|
|
for (let elem of single) { |
|
|
|
|
posts = posts.concat(getPostsByFilter('single', elem)); |
|
|
|
|
} |
|
|
|
|
for (let id of filteredId) { |
|
|
|
|
posts = posts.concat(getPostsByFilter('id', { userId: id })); |
|
|
|
|
for (let id of fid) { |
|
|
|
|
posts = posts.concat(getPostsByFilter('fid', id)); |
|
|
|
|
} |
|
|
|
|
for (let name of filteredName) { |
|
|
|
|
posts = posts.concat(getPostsByFilter('name', { name })); |
|
|
|
|
for (let name of fname) { |
|
|
|
|
posts = posts.concat(getPostsByFilter('fname', name)); |
|
|
|
|
} |
|
|
|
|
for (let tripcode of filteredTripcode) { |
|
|
|
|
posts = posts.concat(getPostsByFilter('tripcode', { tripcode })); |
|
|
|
|
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));
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
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 newPost = e.detail.post; |
|
|
|
|
if (anyFilterMatches(newPost)) { |
|
|
|
|
newPost.classList.add('hidden'); |
|
|
|
|
} |
|
|
|
|
const menu = post.querySelector('.postmenu'); |
|
|
|
|
const menu = newPost.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(); |
|
|
|
|
filtersTable = document.getElementById('advancedfilters'); |
|
|
|
|
updateFiltersTable(); |
|
|
|
|
|
|
|
|
|
const filterClearButton = document.getElementById('filters-clear'); |
|
|
|
|
const clearFilters = () => { |
|
|
|
|
single = new Set(), |
|
|
|
|
fid = new Set(), |
|
|
|
|
fname = new Set(), |
|
|
|
|
ftrip = new Set(), |
|
|
|
|
fnamer = new Set(), |
|
|
|
|
ftripr = new Set(), |
|
|
|
|
updateFiltersTable(); |
|
|
|
|
togglePostsHidden(document.querySelectorAll('.post-container'), false); |
|
|
|
|
updateSavedFilters(); |
|
|
|
|
console.log('cleared hidden posts'); |
|
|
|
|
} |
|
|
|
|
hidePostsListClearButton.addEventListener('click', clearhidePostsList, false); |
|
|
|
|
filterClearButton.addEventListener('click', clearFilters, false); |
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|