227 lines
6.4 KiB
JavaScript
227 lines
6.4 KiB
JavaScript
/* global $ $create dom messageBoxProxy */// dom.js
|
|
/* global installed */// manage.js
|
|
/* global prefs */
|
|
/* global t */// localization.js
|
|
'use strict';
|
|
|
|
const sorter = (() => {
|
|
|
|
const COL_MIN = 300; // same as options.html
|
|
const COL_MAX = 9999; // same as options.html
|
|
const COL_PROP = '--columns';
|
|
|
|
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: v => v.styleNameLC,
|
|
sorter: sorterType.alpha,
|
|
},
|
|
usercss: {
|
|
text: 'Usercss',
|
|
parse: v => v.styleMeta.usercssData ? 0 : 1,
|
|
sorter: sorterType.number,
|
|
},
|
|
disabled: {
|
|
text: '', // added as either "enabled" or "disabled" by the addOptions function
|
|
parse: v => v.styleMeta.enabled ? 1 : 0,
|
|
sorter: sorterType.number,
|
|
},
|
|
dateInstalled: {
|
|
text: t('dateInstalled'),
|
|
parse: v => v.styleMeta.installDate,
|
|
sorter: sorterType.number,
|
|
},
|
|
dateUpdated: {
|
|
text: t('dateUpdated'),
|
|
parse: ({styleMeta: s}) => s.updateDate || s.installDate,
|
|
sorter: sorterType.number,
|
|
},
|
|
size: {
|
|
text: t('genericSize'),
|
|
parse: v => v.styleSize,
|
|
sorter: sorterType.number,
|
|
},
|
|
};
|
|
|
|
// Adding (assumed) most commonly used ('title,asc' should always be first)
|
|
// whitespace before & after the comma is ignored
|
|
const selectOptions = [
|
|
'{groupAsc}',
|
|
'title,asc',
|
|
'dateInstalled,desc, title,asc',
|
|
'dateInstalled,asc, title,asc',
|
|
'dateUpdated,desc, title,asc',
|
|
'dateUpdated,asc, title,asc',
|
|
'usercss,asc, title,asc',
|
|
'usercss,desc, title,asc',
|
|
'disabled,asc, title,asc',
|
|
'disabled,desc, title,asc',
|
|
'disabled,desc, usercss,asc, title,asc',
|
|
'size,desc, title,asc',
|
|
'{groupDesc}',
|
|
'title,desc',
|
|
'usercss,asc, title,desc',
|
|
'usercss,desc, title,desc',
|
|
'disabled,desc, title,desc',
|
|
'disabled,desc, usercss,asc, title,desc',
|
|
];
|
|
const splitRegex = /\s*,\s*/;
|
|
const ID = 'manage.newUI.sort';
|
|
const getPref = () => prefs.get(ID) || prefs.defaults[ID];
|
|
|
|
let columns = 1;
|
|
let minWidth;
|
|
|
|
function init() {
|
|
prefs.subscribe(ID, sorter.update);
|
|
$('#sorter-help').onclick = showHelp;
|
|
addOptions();
|
|
prefs.subscribe('manage.minColumnWidth', updateColumnWidth, {runNow: true});
|
|
}
|
|
|
|
function addOptions() {
|
|
let container;
|
|
const select = $('#' + ID);
|
|
const renderBin = document.createDocumentFragment();
|
|
const option = $create('option');
|
|
const optgroup = $create('optgroup');
|
|
const meta = {
|
|
desc: ' \u21E9',
|
|
enabled: t('genericEnabledLabel'),
|
|
disabled: t('genericDisabledLabel'),
|
|
dateNew: ` (${t('sortDateNewestFirst')})`,
|
|
dateOld: ` (${t('sortDateOldestFirst')})`,
|
|
groupAsc: t('sortLabelTitleAsc'),
|
|
groupDesc: t('sortLabelTitleDesc'),
|
|
};
|
|
selectOptions.forEach(sort => {
|
|
if (/{\w+}/.test(sort)) {
|
|
if (container) {
|
|
renderBin.appendChild(container);
|
|
}
|
|
container = optgroup.cloneNode();
|
|
container.label = meta[sort.substring(1, sort.length - 1)];
|
|
return;
|
|
}
|
|
let lastTag = '';
|
|
const opt = option.cloneNode();
|
|
opt.textContent = sort.split(splitRegex).reduce((acc, val) => {
|
|
if (tagData[val]) {
|
|
lastTag = val;
|
|
return acc + (acc !== '' ? ' + ' : '') + tagData[val].text;
|
|
}
|
|
if (lastTag.indexOf('date') > -1) return acc + meta[val === 'desc' ? 'dateNew' : 'dateOld'];
|
|
if (lastTag === 'disabled') return acc + meta[val === 'desc' ? 'enabled' : 'disabled'];
|
|
return acc + (meta[val] || '');
|
|
}, '');
|
|
opt.value = sort;
|
|
container.appendChild(opt);
|
|
});
|
|
renderBin.appendChild(container);
|
|
select.appendChild(renderBin);
|
|
select.value = getPref();
|
|
}
|
|
|
|
return {
|
|
|
|
init,
|
|
|
|
sort(styles) {
|
|
const sortBy = getPref().split(splitRegex);
|
|
const len = sortBy.length;
|
|
return styles.sort((a, b) => {
|
|
let types, direction;
|
|
let result = 0;
|
|
let index = 0;
|
|
// multi-sort
|
|
while (result === 0 && index < len) {
|
|
types = tagData[sortBy[index++]];
|
|
direction = sortBy[index++] === 'asc' ? 1 : -1;
|
|
result = types.sorter(types.parse(a), types.parse(b)) * direction;
|
|
}
|
|
return result;
|
|
});
|
|
},
|
|
|
|
update() {
|
|
if (!installed) return;
|
|
const current = [...installed.children];
|
|
const sorted = sorter.sort([...current]);
|
|
if (current.some((el, i) => el !== sorted[i])) {
|
|
installed.append(...sorted);
|
|
}
|
|
sorter.updateStripes();
|
|
},
|
|
|
|
updateStripes({onlyWhenColumnsChanged} = {}) {
|
|
if (onlyWhenColumnsChanged && !updateColumnCount()) return;
|
|
let index = 0;
|
|
let isOdd = false;
|
|
const flipRows = columns % 2 === 0;
|
|
for (const {classList} of installed.children) {
|
|
if (classList.contains('hidden')) continue;
|
|
classList.toggle('odd', isOdd);
|
|
classList.toggle('even', !isOdd);
|
|
if (flipRows && ++index >= columns) {
|
|
index = 0;
|
|
} else {
|
|
isOdd = !isOdd;
|
|
}
|
|
}
|
|
},
|
|
};
|
|
|
|
function updateColumnCount() {
|
|
const useStyle = [].some.call($.root.children,
|
|
el => el.tagName === 'STYLE' && el.textContent.includes(COL_PROP + ':'));
|
|
const v = useStyle ? Math.max(1, getComputedStyle($.root).getPropertyValue(COL_PROP) >> 0)
|
|
: minWidth ? onResize()
|
|
: columns;
|
|
if (columns !== v) {
|
|
columns = v;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function updateColumnWidth(_, val) {
|
|
minWidth = Math.max(val, COL_MIN);
|
|
if (val < COL_MAX) {
|
|
window.on('resize', onResize);
|
|
} else {
|
|
window.off('resize', onResize);
|
|
$.root.style.removeProperty(COL_PROP);
|
|
}
|
|
sorter.updateStripes({onlyWhenColumnsChanged: true});
|
|
}
|
|
|
|
function onResize(evt) {
|
|
const c = Math.max(1, (window.innerWidth - dom.HWval) / minWidth >> 0);
|
|
if (columns !== c) {
|
|
$.root.style.setProperty(COL_PROP, c);
|
|
if (evt) {
|
|
columns = c;
|
|
sorter.updateStripes();
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
async function showHelp(event) {
|
|
event.preventDefault();
|
|
messageBoxProxy.show({
|
|
className: 'help-text center-dialog',
|
|
title: t('sortStylesHelpTitle'),
|
|
contents:
|
|
$create('div',
|
|
t('sortStylesHelp').split('\n').map(line =>
|
|
$create('p', line))),
|
|
buttons: [t('confirmOK')],
|
|
});
|
|
}
|
|
})();
|