Last active
August 14, 2020 13:50
-
-
Save jjspace/b9dec89b1aa68ee9356270b6507bc27c to your computer and use it in GitHub Desktop.
DIM Quickfilters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name DIM Quickfilters | |
// @description Add a quick filter dropdown for DIM | |
// | |
// @author jjspace | |
// @namespace http://github.com/jjspace | |
// @downloadURL https://gist.github.com/jjspace/b9dec89b1aa68ee9356270b6507bc27c/raw/dimQuickfilters.user.js | |
// | |
// @version 1.0.6 | |
// @updateUrl https://gist.github.com/jjspace/b9dec89b1aa68ee9356270b6507bc27c/raw/dimQuickfilters.user.js | |
// | |
// @match https://app.destinyitemmanager.com/* | |
// | |
// @run-at document-end | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// ================= js-cookie ======================== | |
/*! | |
* JavaScript Cookie v2.2.0 | |
* https://github.com/js-cookie/js-cookie | |
* | |
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack | |
* Released under the MIT license | |
*/ | |
(function (factory) { | |
var registeredInModuleLoader = false; | |
if (typeof define === 'function' && define.amd) { | |
define(factory); | |
registeredInModuleLoader = true; | |
} | |
if (typeof exports === 'object') { | |
module.exports = factory(); | |
registeredInModuleLoader = true; | |
} | |
if (!registeredInModuleLoader) { | |
var OldCookies = window.Cookies; | |
var api = window.Cookies = factory(); | |
api.noConflict = function () { | |
window.Cookies = OldCookies; | |
return api; | |
}; | |
} | |
}(function () { | |
function extend () { | |
var i = 0; | |
var result = {}; | |
for (; i < arguments.length; i++) { | |
var attributes = arguments[ i ]; | |
for (var key in attributes) { | |
result[key] = attributes[key]; | |
} | |
} | |
return result; | |
} | |
function init (converter) { | |
function api (key, value, attributes) { | |
var result; | |
if (typeof document === 'undefined') { | |
return; | |
} | |
// Write | |
if (arguments.length > 1) { | |
attributes = extend({ | |
path: '/' | |
}, api.defaults, attributes); | |
if (typeof attributes.expires === 'number') { | |
var expires = new Date(); | |
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); | |
attributes.expires = expires; | |
} | |
// We're using "expires" because "max-age" is not supported by IE | |
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : ''; | |
try { | |
result = JSON.stringify(value); | |
if (/^[\{\[]/.test(result)) { | |
value = result; | |
} | |
} catch (e) {} | |
if (!converter.write) { | |
value = encodeURIComponent(String(value)) | |
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); | |
} else { | |
value = converter.write(value, key); | |
} | |
key = encodeURIComponent(String(key)); | |
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); | |
key = key.replace(/[\(\)]/g, escape); | |
var stringifiedAttributes = ''; | |
for (var attributeName in attributes) { | |
if (!attributes[attributeName]) { | |
continue; | |
} | |
stringifiedAttributes += '; ' + attributeName; | |
if (attributes[attributeName] === true) { | |
continue; | |
} | |
stringifiedAttributes += '=' + attributes[attributeName]; | |
} | |
return (document.cookie = key + '=' + value + stringifiedAttributes); | |
} | |
// Read | |
if (!key) { | |
result = {}; | |
} | |
// To prevent the for loop in the first place assign an empty array | |
// in case there are no cookies at all. Also prevents odd result when | |
// calling "get()" | |
var cookies = document.cookie ? document.cookie.split('; ') : []; | |
var rdecode = /(%[0-9A-Z]{2})+/g; | |
var i = 0; | |
for (; i < cookies.length; i++) { | |
var parts = cookies[i].split('='); | |
var cookie = parts.slice(1).join('='); | |
if (!this.json && cookie.charAt(0) === '"') { | |
cookie = cookie.slice(1, -1); | |
} | |
try { | |
var name = parts[0].replace(rdecode, decodeURIComponent); | |
cookie = converter.read ? | |
converter.read(cookie, name) : converter(cookie, name) || | |
cookie.replace(rdecode, decodeURIComponent); | |
if (this.json) { | |
try { | |
cookie = JSON.parse(cookie); | |
} catch (e) {} | |
} | |
if (key === name) { | |
result = cookie; | |
break; | |
} | |
if (!key) { | |
result[name] = cookie; | |
} | |
} catch (e) {} | |
} | |
return result; | |
} | |
api.set = api; | |
api.get = function (key) { | |
return api.call(api, key); | |
}; | |
api.getJSON = function () { | |
return api.apply({ | |
json: true | |
}, [].slice.call(arguments)); | |
}; | |
api.defaults = {}; | |
api.remove = function (key, attributes) { | |
api(key, '', extend(attributes, { | |
expires: -1 | |
})); | |
}; | |
api.withConverter = init; | |
return api; | |
} | |
return init(function () {}); | |
})); | |
// end jscookie | |
//src: http://stackoverflow.com/a/28002292/1329367 | |
function getScript(source, callback) { | |
var script = document.createElement('script'); | |
var prior = document.getElementsByTagName('script')[0]; | |
script.async = 1; | |
script.onload = script.onreadystatechange = function( _, isAbort ) { | |
if(isAbort || !script.readyState || /loaded|complete/.test(script.readyState) ) { | |
script.onload = script.onreadystatechange = null; | |
script = undefined; | |
if(!isAbort) { if(callback) callback(); } | |
} | |
}; | |
script.src = source; | |
prior.parentNode.insertBefore(script, prior); | |
} | |
const addStylesheet = (rules) => { | |
const styleEl = document.createElement('style'); | |
styleEl.id = 'customSheet'; | |
document.head.appendChild(styleEl); | |
const styleSheet = styleEl.sheet; | |
rules.forEach((rule) => { | |
const [selector, props] = rule; | |
let propStr = Object.entries(props).reduce((acc, [prop, val]) => { | |
return acc + prop.replace(/[A-Z]/g, (m) => '-'+m.toLowerCase()) + `:${val};`; | |
}, ''); | |
styleSheet.insertRule(`${selector}{${propStr}}`, styleSheet.cssRules.length); | |
}); | |
return styleSheet; | |
} | |
function createStylesheet() { | |
const DIM_YELLOW = '#e8a534'; | |
const DIM_GRAY = '#151515'; | |
const DIM_GRAY_LIGHT = '#313233'; | |
addStylesheet([ | |
['.qf-dropdown', { | |
boxSizing: 'border-box', | |
position: 'relative', | |
display: 'inline-block', | |
cursor: 'pointer', | |
height: '100%', | |
lineHeight: '28px', | |
padding: '8px 8px 6px 8px', | |
borderBottom: `2px solid ${DIM_YELLOW}`, | |
}], | |
['.qf-dropdown::after', { | |
content: '"\\25BC"', | |
position: 'absolute', | |
right: '7px', | |
top: '8px', | |
fontSize: '9px', | |
color: '#ccc', | |
}], | |
['#header .qf-dropdown span', {margin: '0'}], | |
['.qf-title', { | |
height: '100%', | |
paddingRight: '19px', | |
}], | |
['.qf-list', { | |
display: 'none', | |
position: 'absolute', | |
top: 'calc(100% + 2px)', | |
right: '0', | |
width: '100%', | |
background: 'black', | |
}], | |
['.qf-list.active', {display: 'block'}], | |
['.qf-item', { | |
position: 'relative', | |
cursor: 'pointer', | |
lineHeight: '13px', | |
height: '36px', | |
display: 'flex', | |
alignItems: 'center', | |
justifyContent: 'space-between', | |
padding: '0 8px', | |
}], | |
['.qf-item:hover', {background: DIM_GRAY}], | |
['.qf-item:after', { | |
content: 'attr(data-filter)', | |
visibility: 'hidden', | |
width: '120px', | |
minHeight: '1em', | |
backgroundColor: 'black', | |
color: 'white', | |
textAlign: 'center', | |
borderRadius: '5px', | |
padding: '5px 0', | |
zIndex: '1', | |
position: 'absolute', | |
top: 'calc(50% - 1em + 1px)', | |
left: '110%', | |
}], | |
['.qf-item:before', { | |
content: '""', | |
visibility: 'hidden', | |
position: 'absolute', | |
top: '50%', | |
right: '-10%', | |
marginTop: '-5px', | |
border: '5px solid transparent', | |
borderRightColor: 'black', | |
}], | |
['.qf-item:hover:after, .qf-item:hover:before', {visibility: 'visible'}], | |
['.qf-item-name', {flex: '1'}], | |
['.qf-item-icon', {padding: '4px'}], | |
['.qf-item-icon:hover', {color: DIM_YELLOW}], | |
['.qf-form', {cursor: 'auto', paddingTop: '6px'}], | |
['#qf-new-form', { | |
display: 'flex', | |
flexDirection: 'column', | |
padding: '0 6px', | |
}], | |
['.qf-form input', { | |
background: 'transparent', | |
border: 'none', | |
color: 'white', | |
outline: 'none', | |
width: '100%', | |
}], | |
['.qf-input-field', { | |
background: DIM_GRAY_LIGHT, | |
padding: '3px', | |
height: '1.5em', | |
display: 'flex', | |
alignItems: 'center', | |
borderRadius: '4px', | |
marginBottom: '6px', | |
}], | |
['.qf-form button', { | |
border: 'none', | |
height: '1.5em', | |
marginBottom: '6px', | |
background: 'none', | |
color: 'white', | |
}], | |
['.qf-form button:hover', { | |
color: DIM_YELLOW, | |
cursor: 'pointer', | |
}] | |
]); | |
} | |
// ======================= Filter Functions ====================== | |
function setFilter(str, combine) { | |
// console.log("filter set", str); | |
let finput = document.querySelector('.filter-input') | |
let oldVal = finput.value; | |
let newVal = combine ? oldVal + ' ' + (str || '') : (str || ''); | |
finput.value = newVal; | |
// send a change event to trigger filtering | |
var evt = new Event('input', {'bubbles': true, 'cancelable': false}); | |
finput.dispatchEvent(evt); | |
} | |
//if (typeof filters === 'undefined') { | |
let filters = []; | |
//} | |
const defaultFilters = [{ | |
name:'clear', | |
filter: '' | |
}]; | |
function resetFilters() { | |
filters = defaultFilters; | |
} | |
function loadFilters() { | |
let filterCookie = window.Cookies.getJSON('qfFilters'); | |
filters = filterCookie ? filterCookie : defaultFilters; | |
} | |
function addFilter(name, filter, regenList) { | |
let idx = filters.findIndex(f => f.name === name); | |
if (idx >= 0) { | |
filters[idx].filter = filter; | |
} | |
else { | |
filters.push({name, filter}); | |
} | |
window.Cookies.set('qfFilters', filters, {expires: 180}); | |
if (regenList) { | |
generateList(true); | |
} | |
} | |
function removeFilter(name) { | |
console.log('remove', name); | |
filters = filters.filter(f => f.name !== name); | |
window.Cookies.set('qfFilters', filters, {expires: 180}); | |
generateList(true); | |
} | |
function newFormFilter(e) { | |
e.preventDefault(); | |
let name = document.qfNewForm.qfName.value; | |
//check for dups | |
let filter = document.qfNewForm.qfQuery.value; | |
if (name && filter && !(name in filters)) { | |
addFilter(name, filter, true); | |
} | |
} | |
// ================= Build DOM Elements ====================== | |
function generateSpacer() { | |
// build a spacer to adjust for newer style where search bar fills space | |
// personal preference that I like it smaller with emptyness between | |
// header-right and header-left | |
let spacer = document.createElement('div'); | |
spacer.className = 'spacer'; | |
spacer.style = 'flex: 1'; | |
var headerSection = document.querySelector('.header-right'); | |
headerSection.insertAdjacentElement('afterbegin', spacer); | |
} | |
function generateDropdown() { | |
if (document.querySelector('.qf-dropdown')) { | |
document.querySelector('.qf-dropdown').remove(); | |
} | |
let dropdown = document.createElement('div'); | |
dropdown.className = 'qf-dropdown'; | |
//build title | |
let title = document.createElement('div'); | |
title.textContent = 'Quick Filters'; | |
title.className = 'qf-title'; | |
dropdown.appendChild(title); | |
dropdown.addEventListener('click', function(e) { | |
e.stopPropagation(); | |
var elem = document.querySelector('.qf-dropdown'); | |
var list = elem.querySelector('.qf-list'); | |
if (list.classList.contains('active')) { | |
list.classList.remove('active'); | |
} else { | |
list.classList.add('active'); | |
} | |
}); | |
var headerSection = document.querySelector('.header-right'); | |
headerSection.insertAdjacentElement('afterbegin', dropdown); | |
} | |
function generateList(open) { | |
if (document.querySelector('.qf-list')) { | |
document.querySelector('.qf-list').remove(); | |
} | |
//start list | |
let list = document.createElement('div'); | |
list.className = 'qf-list'; | |
if (open) { | |
list.classList.add('active'); | |
} | |
//list items from filters | |
filters.forEach(function(filter) { | |
let itemName = document.createElement('div'); | |
itemName.className = 'qf-item-name'; | |
itemName.innerText = filter.name; | |
let item = document.createElement('div'); | |
item.className = 'qf-item'; | |
item.appendChild(itemName); | |
item.dataset.name = filter.name; | |
item.dataset.filter = filter.filter; | |
item.addEventListener('click', function() { | |
setFilter(this.dataset.filter); | |
}); | |
if (filter.name !== 'clear') { | |
let del = document.createElement('span'); | |
del.className = 'qf-item-icon qf-delete'; | |
del.innerText = 'x'; | |
del.addEventListener('click', function(e) { | |
e.stopPropagation(); | |
removeFilter(this.parentNode.dataset.name); | |
}); | |
let combine = document.createElement ('span'); | |
combine.className = 'qf-item-icon qf-combine'; | |
combine.innerText = '&'; | |
combine.addEventListener('click', function(e) { | |
e.stopPropagation(); | |
setFilter(this.parentNode.dataset.filter, true); | |
}); | |
item.appendChild(combine); | |
item.appendChild(del); | |
} | |
list.appendChild(item); | |
}); | |
//new form | |
let form = generateNewFilterForm(); | |
let formWrap = document.createElement('div'); | |
formWrap.addEventListener("click", function(e) { | |
e.stopPropagation(); | |
}); | |
formWrap.className = 'qf-form'; | |
formWrap.appendChild(form); | |
list.appendChild(formWrap); | |
document.querySelector('.qf-dropdown').appendChild(list); | |
} | |
function generateNewFilterForm() { | |
let form = document.createElement('form'); | |
form.name = "qfNewForm"; | |
form.id = "qf-new-form"; | |
form.addEventListener("submit", newFormFilter); | |
//let inputWrap = document.createElement('div'); | |
//inputWrap.className = "qf-input-wrap"; | |
let name = document.createElement('input'); | |
name.id = "qf-name"; | |
name.name = "qfName"; | |
name.type = "text"; | |
name.placeholder = "Filter Name..."; | |
name.autocomplete = "off"; | |
let nameField = document.createElement('div'); | |
nameField.className = 'qf-input-field'; | |
nameField.appendChild(name); | |
//inputWrap.appendChild(name); | |
let query = document.createElement('input'); | |
query.id = "qf-query"; | |
query.name = "qfQuery"; | |
query.type = "text"; | |
query.placeholder = "Filter Query..."; | |
query.autocomplete = "off"; | |
let queryField = document.createElement('div'); | |
queryField.className = 'qf-input-field'; | |
queryField.appendChild(query); | |
//inputWrap.appendChild(query); | |
form.appendChild(nameField); | |
form.appendChild(queryField); | |
//let btnWrap = document.createElement('div'); | |
//btnWrap.className = "qf-btn-wrap"; | |
let btn = document.createElement('button'); | |
btn.type = "submit"; | |
btn.innerText = "+"; | |
//btnWrap.appendChild(btn); | |
form.appendChild(btn); | |
document.body.appendChild(form); | |
return form; | |
} | |
// =================== Initilize ================= | |
function init() { | |
createStylesheet(); | |
loadFilters(); | |
//close if click out of dropdown | |
document.body.addEventListener('click', function(e) { | |
var elem = document.querySelector('.qf-dropdown'); | |
var list = elem.querySelector('.qf-list'); | |
list.classList.remove('active'); | |
}); | |
generateDropdown(); | |
generateList(); | |
generateSpacer(); | |
console.log('DIM Quickfilters init finished'); | |
} | |
window.onload = init; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment