2018-12-02 06:14:59 +00:00
|
|
|
/* global installed t $ prefs */
|
2018-11-07 06:09:29 +00:00
|
|
|
/* exported sorter */
|
2017-12-23 00:11:46 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const sorter = (() => {
|
|
|
|
|
2018-12-02 06:14:59 +00:00
|
|
|
// Set up for only one column
|
|
|
|
const defaultSort = 'title,asc';
|
|
|
|
|
|
|
|
const sortOrder = [
|
|
|
|
'asc',
|
|
|
|
'desc',
|
|
|
|
'' // unsorted
|
|
|
|
];
|
|
|
|
|
2017-12-23 00:11:46 +00:00
|
|
|
const sorterType = {
|
|
|
|
alpha: (a, b) => a < b ? -1 : a === b ? 0 : 1,
|
|
|
|
number: (a, b) => (a || 0) - (b || 0),
|
|
|
|
};
|
|
|
|
|
|
|
|
const tagData = {
|
|
|
|
title: {
|
|
|
|
text: t('genericTitle'),
|
|
|
|
parse: ({name}) => name,
|
|
|
|
sorter: sorterType.alpha
|
|
|
|
},
|
2018-12-02 06:14:59 +00:00
|
|
|
order: {
|
|
|
|
text: '#',
|
2018-12-03 03:06:05 +00:00
|
|
|
parse: ({style}) => style.injectionOrder || style.id,
|
2018-12-02 06:14:59 +00:00
|
|
|
sorter: sorterType.number,
|
|
|
|
},
|
2017-12-23 00:11:46 +00:00
|
|
|
usercss: {
|
|
|
|
text: 'Usercss',
|
|
|
|
parse: ({style}) => style.usercssData ? 0 : 1,
|
|
|
|
sorter: sorterType.number
|
|
|
|
},
|
|
|
|
disabled: {
|
2018-12-02 06:14:59 +00:00
|
|
|
text: t('genericDisabledLabel'), // added as either "enabled" or "disabled" by the addOptions function
|
|
|
|
parse: ({style}) => style.enabled ? 0 : 1,
|
|
|
|
sorter: sorterType.number
|
|
|
|
},
|
|
|
|
version: {
|
|
|
|
text: '#',
|
|
|
|
parse: ({style}) => (style.usercssData && style.usercssData.version || '')
|
|
|
|
.split(/[.-]/)
|
|
|
|
.splice(0, 3) // ignore extra labels; e.g. 1.2.3-beta.1
|
|
|
|
.map(n => n ? n.padStart(4, '0') : '')
|
|
|
|
.join(''),
|
2017-12-23 00:11:46 +00:00
|
|
|
sorter: sorterType.number
|
|
|
|
},
|
|
|
|
dateInstalled: {
|
|
|
|
text: t('dateInstalled'),
|
2018-12-02 06:14:59 +00:00
|
|
|
parse: ({style}) => style.installDate || '',
|
2017-12-23 00:11:46 +00:00
|
|
|
sorter: sorterType.number
|
|
|
|
},
|
|
|
|
dateUpdated: {
|
|
|
|
text: t('dateUpdated'),
|
2018-12-02 06:14:59 +00:00
|
|
|
parse: ({style}) => style.updateDate || '',
|
2017-12-23 00:11:46 +00:00
|
|
|
sorter: sorterType.number
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const splitRegex = /\s*,\s*/;
|
2018-12-02 06:14:59 +00:00
|
|
|
const whitespace = /\s+/g;
|
2017-12-23 00:11:46 +00:00
|
|
|
|
2018-05-15 13:24:28 +00:00
|
|
|
let columns = 1;
|
2018-12-02 06:14:59 +00:00
|
|
|
let lastSort;
|
2017-12-23 00:11:46 +00:00
|
|
|
|
|
|
|
function sort({styles}) {
|
2018-12-02 06:14:59 +00:00
|
|
|
let sortBy = prefs.get('manage.newUI.sort').replace(whitespace, '');
|
|
|
|
if (lastSort === sortBy) {
|
|
|
|
return styles;
|
|
|
|
}
|
|
|
|
sortBy = sortBy.split(splitRegex);
|
|
|
|
if (sortBy.join('') === '') {
|
|
|
|
sortBy = defaultSort.split(splitRegex);
|
|
|
|
}
|
|
|
|
updateHeaders(sortBy);
|
2017-12-23 00:11:46 +00:00
|
|
|
const len = sortBy.length;
|
|
|
|
return styles.sort((a, b) => {
|
2018-12-02 06:14:59 +00:00
|
|
|
let types, direction, x, y;
|
2017-12-23 00:11:46 +00:00
|
|
|
let result = 0;
|
|
|
|
let index = 0;
|
|
|
|
// multi-sort
|
|
|
|
while (result === 0 && index < len) {
|
|
|
|
types = tagData[sortBy[index++]];
|
2018-12-02 06:14:59 +00:00
|
|
|
x = types.parse(a);
|
|
|
|
y = types.parse(b);
|
|
|
|
// sort empty values to the bottom
|
|
|
|
if (x === '') {
|
|
|
|
result = 1;
|
|
|
|
} else if (y === '') {
|
|
|
|
result = -1;
|
|
|
|
} else {
|
|
|
|
direction = sortBy[index++] === 'asc' ? 1 : -1;
|
|
|
|
result = types.sorter(x, y) * direction;
|
|
|
|
}
|
2017-12-23 00:11:46 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-12-02 06:14:59 +00:00
|
|
|
// Update default sort on init & when all other columns are unsorted
|
|
|
|
function updateHeaders(sortBy) {
|
|
|
|
let header, sortDir;
|
|
|
|
let i = 0;
|
|
|
|
const len = sortBy.length;
|
|
|
|
while (i < len) {
|
|
|
|
header = $(`.entry-header [data-type="${sortBy[i++]}"]`);
|
|
|
|
sortDir = sortBy[i++];
|
|
|
|
if (header) {
|
|
|
|
header.dataset.sortDir = sortDir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateSort(event) {
|
|
|
|
const sortables = $$('.entry-header .sortable');
|
|
|
|
const elm = event.target;
|
|
|
|
// default sort column only allows asc, desc; not unsorted
|
|
|
|
const len = sortOrder.length - (elm.dataset.type === defaultSort.split(splitRegex)[0] ? 1 : 0);
|
|
|
|
let index = (sortOrder.indexOf(elm.dataset.sortDir) + 1) % len;
|
|
|
|
// shift key for multi-column sorting
|
|
|
|
if (!event.shiftKey) {
|
|
|
|
sortables.forEach(el => {
|
|
|
|
el.dataset.sortDir = '';
|
|
|
|
el.dataset.timestamp = '';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
elm.dataset.sortDir = sortOrder[index];
|
|
|
|
elm.dataset.timestamp = Date.now();
|
|
|
|
|
|
|
|
const newSort = sortables
|
|
|
|
.filter(el => el.dataset.sortDir !== '')
|
|
|
|
.reduce((acc, el) => {
|
|
|
|
const {sortDir, type, timestamp = new Date()} = el.dataset;
|
|
|
|
if (sortDir) {
|
|
|
|
acc.push({sortDir, type, timestamp: parseFloat(timestamp)});
|
|
|
|
}
|
|
|
|
return acc;
|
|
|
|
}, [])
|
|
|
|
.sort((a, b) => a.timestamp - b.timestamp)
|
|
|
|
.reduce((acc, item) => {
|
|
|
|
acc = acc.concat(item.type, item.sortDir);
|
|
|
|
return acc;
|
|
|
|
}, [])
|
|
|
|
.join(',');
|
|
|
|
prefs.set('manage.newUI.sort', newSort || defaultSort);
|
|
|
|
}
|
|
|
|
|
2017-12-23 00:11:46 +00:00
|
|
|
function update() {
|
|
|
|
if (!installed) return;
|
|
|
|
const current = [...installed.children];
|
2018-11-27 23:50:11 +00:00
|
|
|
current.shift(); // remove header
|
2017-12-23 00:11:46 +00:00
|
|
|
const sorted = sort({
|
|
|
|
styles: current.map(entry => ({
|
|
|
|
entry,
|
|
|
|
name: entry.styleNameLowerCase + '\n' + entry.styleMeta.name,
|
2018-01-01 17:02:49 +00:00
|
|
|
style: entry.styleMeta,
|
2017-12-23 00:11:46 +00:00
|
|
|
}))
|
|
|
|
});
|
|
|
|
if (current.some((entry, index) => entry !== sorted[index].entry)) {
|
|
|
|
const renderBin = document.createDocumentFragment();
|
|
|
|
sorted.forEach(({entry}) => renderBin.appendChild(entry));
|
|
|
|
installed.appendChild(renderBin);
|
|
|
|
}
|
2018-05-15 11:56:12 +00:00
|
|
|
updateStripes();
|
2017-12-23 00:11:46 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 13:24:28 +00:00
|
|
|
function updateStripes({onlyWhenColumnsChanged} = {}) {
|
|
|
|
if (onlyWhenColumnsChanged && !updateColumnCount()) return;
|
2017-12-23 00:11:46 +00:00
|
|
|
let index = 0;
|
2018-05-15 13:24:28 +00:00
|
|
|
let isOdd = false;
|
|
|
|
const flipRows = columns % 2 === 0;
|
|
|
|
for (const {classList} of installed.children) {
|
2018-11-25 23:35:54 +00:00
|
|
|
if (classList.contains('hidden') || classList.contains('entry-header')) continue;
|
2018-05-15 13:24:28 +00:00
|
|
|
classList.toggle('odd', isOdd);
|
|
|
|
classList.toggle('even', !isOdd);
|
|
|
|
if (flipRows && ++index >= columns) {
|
|
|
|
index = 0;
|
|
|
|
} else {
|
|
|
|
isOdd = !isOdd;
|
2017-12-23 00:11:46 +00:00
|
|
|
}
|
2018-05-15 13:24:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateColumnCount() {
|
|
|
|
let newValue = 1;
|
|
|
|
for (let el = document.documentElement.lastElementChild;
|
|
|
|
el.localName === 'style';
|
|
|
|
el = el.previousElementSibling) {
|
|
|
|
if (el.textContent.includes('--columns:')) {
|
|
|
|
newValue = Math.max(1, getComputedStyle(document.documentElement).getPropertyValue('--columns') | 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (columns !== newValue) {
|
|
|
|
columns = newValue;
|
|
|
|
return true;
|
|
|
|
}
|
2017-12-23 00:11:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function init() {
|
|
|
|
prefs.subscribe(['manage.newUI.sort'], update);
|
2018-12-02 06:14:59 +00:00
|
|
|
// addOptions();
|
2018-05-15 13:24:28 +00:00
|
|
|
updateColumnCount();
|
2017-12-23 00:11:46 +00:00
|
|
|
}
|
|
|
|
|
2018-12-02 06:14:59 +00:00
|
|
|
return {init, update, sort, updateSort, updateStripes};
|
2017-12-23 00:11:46 +00:00
|
|
|
})();
|