Skip to content

Instantly share code, notes, and snippets.

@jjspace
Last active August 14, 2020 13:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jjspace/b9dec89b1aa68ee9356270b6507bc27c to your computer and use it in GitHub Desktop.
Save jjspace/b9dec89b1aa68ee9356270b6507bc27c to your computer and use it in GitHub Desktop.
DIM Quickfilters
// ==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