Modularize sorter
This commit is contained in:
parent
aa17234894
commit
2646d910ab
|
@ -1,5 +1,5 @@
|
|||
/* global installed messageBox */
|
||||
/* global updateSort */
|
||||
/* global sorter */
|
||||
'use strict';
|
||||
|
||||
const filtersSelector = {
|
||||
|
@ -156,7 +156,7 @@ function filterOnChange({target: el, forceRefilter}) {
|
|||
if (installed) {
|
||||
reapplyFilter();
|
||||
}
|
||||
debounce(updateSort);
|
||||
debounce(sorter().updateSort);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/* global checkUpdate, handleUpdateInstalled */
|
||||
/* global objectDiff */
|
||||
/* global configDialog */
|
||||
/* global sortInit, sortStyles, updateSort */
|
||||
/* global sorter */
|
||||
'use strict';
|
||||
|
||||
let installed;
|
||||
|
@ -85,7 +85,7 @@ function initGlobalEvents() {
|
|||
|
||||
// N.B. triggers existing onchange listeners
|
||||
setupLivePrefs();
|
||||
sortInit();
|
||||
sorter().sortInit();
|
||||
|
||||
$$('[id^="manage.newUI"]')
|
||||
.forEach(el => (el.oninput = (el.onchange = switchUI)));
|
||||
|
@ -108,7 +108,7 @@ function initGlobalEvents() {
|
|||
|
||||
|
||||
function showStyles(styles = []) {
|
||||
const sorted = sortStyles({
|
||||
const sorted = sorter().sortStyles({
|
||||
parser: 'style',
|
||||
styles: styles.map(style => ({
|
||||
style,
|
||||
|
@ -459,7 +459,7 @@ function handleUpdate(style, {reason, method} = {}) {
|
|||
handleUpdateInstalled(entry, reason);
|
||||
}
|
||||
filterAndAppend({entry});
|
||||
debounce(updateSort);
|
||||
debounce(sorter().updateSort);
|
||||
if (!entry.matches('.hidden') && reason !== 'import') {
|
||||
animateElement(entry);
|
||||
scrollElementIntoView(entry);
|
||||
|
@ -568,18 +568,6 @@ function switchUI({styleOnly} = {}) {
|
|||
}
|
||||
|
||||
|
||||
function updateStripes() {
|
||||
let index = 0;
|
||||
[...installed.children].forEach(entry => {
|
||||
const list = entry.classList;
|
||||
if (!list.contains('hidden')) {
|
||||
list.add(index % 2 ? 'odd' : 'even');
|
||||
list.remove(index++ % 2 ? 'even' : 'odd');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function rememberScrollPosition() {
|
||||
history.replaceState({scrollY: window.scrollY}, document.title);
|
||||
}
|
||||
|
|
374
manage/sort.js
374
manage/sort.js
|
@ -2,195 +2,211 @@
|
|||
/* global messageBox */
|
||||
'use strict';
|
||||
|
||||
const sorterType = {
|
||||
alpha: (a, b) => (a < b ? -1 : a === b ? 0 : 1),
|
||||
number: (a, b) => a - b
|
||||
};
|
||||
var sorter = (() => {
|
||||
|
||||
const tagData = {
|
||||
title: {
|
||||
text: t('genericTitle'),
|
||||
parse: {
|
||||
style: ({name}) => name,
|
||||
entry: entry => entry.styleNameLowerCase,
|
||||
},
|
||||
sorter: sorterType.alpha
|
||||
},
|
||||
usercss: {
|
||||
text: 'Usercss',
|
||||
parse: {
|
||||
style: ({style}) => (style.usercssData ? 0 : 1),
|
||||
entry: entry => (entry.classList.contains('usercss') ? 0 : 1)
|
||||
},
|
||||
sorter: sorterType.number
|
||||
},
|
||||
disabled: {
|
||||
text: '', // added as either "enabled" or "disabled" by the addSortOptions function
|
||||
parse: {
|
||||
style: ({style}) => (style.enabled ? 1 : 0),
|
||||
entry: entry => (entry.classList.contains('enabled') ? 1 : 0)
|
||||
},
|
||||
sorter: sorterType.number
|
||||
},
|
||||
dateInstalled: {
|
||||
text: t('dateInstalled'),
|
||||
parse: {
|
||||
style: ({style}) => style.installDate,
|
||||
entry: entry => entry.dataset.installdate
|
||||
},
|
||||
sorter: sorterType.number
|
||||
},
|
||||
dateUpdated: {
|
||||
text: t('dateUpdated'),
|
||||
parse: {
|
||||
style: ({style}) => style.updateDate,
|
||||
entry: entry => entry.dataset.updatedate
|
||||
},
|
||||
sorter: sorterType.number
|
||||
}
|
||||
};
|
||||
|
||||
// Adding (assumed) most commonly used ('title,asc' should always be first)
|
||||
// whitespace before & after the comma is ignored
|
||||
const sortSelectOptions = [
|
||||
'{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',
|
||||
'{groupDesc}',
|
||||
'title,desc',
|
||||
'usercss,asc, title,desc',
|
||||
'usercss,desc, title,desc',
|
||||
'disabled,desc, title,desc',
|
||||
'disabled,desc, usercss,asc, title,desc'
|
||||
];
|
||||
|
||||
const sortByRegex = /\s*,\s*/;
|
||||
|
||||
function addSortOptions() {
|
||||
let container;
|
||||
const select = $('#sort-select');
|
||||
const renderBin = document.createDocumentFragment();
|
||||
const option = $create('option');
|
||||
const optgroup = $create('optgroup');
|
||||
const meta = {
|
||||
desc: ' \uea4d',
|
||||
enabled: t('genericEnabledLabel'),
|
||||
disabled: t('genericDisabledLabel'),
|
||||
dateNew: ` (${t('sortDateNewestFirst')})`,
|
||||
dateOld: ` (${t('sortDateOldestFirst')})`,
|
||||
labelFirst: ` (${t('sortLabelFirst')})`,
|
||||
labelLast: ` (${t('sortLabelLast')})`,
|
||||
groupAsc: t('sortLabelTitleAsc'),
|
||||
groupDesc: t('sortLabelTitleDesc')
|
||||
const sorterType = {
|
||||
alpha: (a, b) => (a < b ? -1 : a === b ? 0 : 1),
|
||||
number: (a, b) => a - b
|
||||
};
|
||||
const optgroupRegex = /\{\w+\}/;
|
||||
sortSelectOptions.forEach(sort => {
|
||||
if (optgroupRegex.test(sort)) {
|
||||
if (container) {
|
||||
renderBin.appendChild(container);
|
||||
|
||||
const tagData = {
|
||||
title: {
|
||||
text: t('genericTitle'),
|
||||
parse: {
|
||||
style: ({name}) => name,
|
||||
entry: entry => entry.styleNameLowerCase,
|
||||
},
|
||||
sorter: sorterType.alpha
|
||||
},
|
||||
usercss: {
|
||||
text: 'Usercss',
|
||||
parse: {
|
||||
style: ({style}) => (style.usercssData ? 0 : 1),
|
||||
entry: entry => (entry.classList.contains('usercss') ? 0 : 1)
|
||||
},
|
||||
sorter: sorterType.number
|
||||
},
|
||||
disabled: {
|
||||
text: '', // added as either "enabled" or "disabled" by the addSortOptions function
|
||||
parse: {
|
||||
style: ({style}) => (style.enabled ? 1 : 0),
|
||||
entry: entry => (entry.classList.contains('enabled') ? 1 : 0)
|
||||
},
|
||||
sorter: sorterType.number
|
||||
},
|
||||
dateInstalled: {
|
||||
text: t('dateInstalled'),
|
||||
parse: {
|
||||
style: ({style}) => style.installDate,
|
||||
entry: entry => entry.dataset.installdate
|
||||
},
|
||||
sorter: sorterType.number
|
||||
},
|
||||
dateUpdated: {
|
||||
text: t('dateUpdated'),
|
||||
parse: {
|
||||
style: ({style}) => style.updateDate,
|
||||
entry: entry => entry.dataset.updatedate
|
||||
},
|
||||
sorter: sorterType.number
|
||||
}
|
||||
};
|
||||
|
||||
// Adding (assumed) most commonly used ('title,asc' should always be first)
|
||||
// whitespace before & after the comma is ignored
|
||||
const sortSelectOptions = [
|
||||
'{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',
|
||||
'{groupDesc}',
|
||||
'title,desc',
|
||||
'usercss,asc, title,desc',
|
||||
'usercss,desc, title,desc',
|
||||
'disabled,desc, title,desc',
|
||||
'disabled,desc, usercss,asc, title,desc'
|
||||
];
|
||||
|
||||
const sortByRegex = /\s*,\s*/;
|
||||
|
||||
function addSortOptions() {
|
||||
let container;
|
||||
const select = $('#sort-select');
|
||||
const renderBin = document.createDocumentFragment();
|
||||
const option = $create('option');
|
||||
const optgroup = $create('optgroup');
|
||||
const meta = {
|
||||
desc: ' \uea4d',
|
||||
enabled: t('genericEnabledLabel'),
|
||||
disabled: t('genericDisabledLabel'),
|
||||
dateNew: ` (${t('sortDateNewestFirst')})`,
|
||||
dateOld: ` (${t('sortDateOldestFirst')})`,
|
||||
labelFirst: ` (${t('sortLabelFirst')})`,
|
||||
labelLast: ` (${t('sortLabelLast')})`,
|
||||
groupAsc: t('sortLabelTitleAsc'),
|
||||
groupDesc: t('sortLabelTitleDesc')
|
||||
};
|
||||
const optgroupRegex = /\{\w+\}/;
|
||||
sortSelectOptions.forEach(sort => {
|
||||
if (optgroupRegex.test(sort)) {
|
||||
if (container) {
|
||||
renderBin.appendChild(container);
|
||||
}
|
||||
container = optgroup.cloneNode();
|
||||
container.label = meta[sort.substring(1, sort.length - 1)];
|
||||
return;
|
||||
}
|
||||
container = optgroup.cloneNode();
|
||||
container.label = meta[sort.substring(1, sort.length - 1)];
|
||||
return;
|
||||
let lastTag = '';
|
||||
const opt = option.cloneNode();
|
||||
opt.textContent = sort.split(sortByRegex).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'] + meta['labelFirst'];
|
||||
if (lastTag !== 'title') return acc + meta[val === 'desc' ? 'labelLast' : 'labelFirst'];
|
||||
return acc + (meta[val] || '');
|
||||
}, '');
|
||||
opt.value = sort;
|
||||
container.appendChild(opt);
|
||||
});
|
||||
renderBin.appendChild(container);
|
||||
select.appendChild(renderBin);
|
||||
select.value = prefs.get('manage.newUI.sort');
|
||||
}
|
||||
|
||||
function sortStyles({styles, parser}) {
|
||||
if (!styles) {
|
||||
styles = [...installed.children];
|
||||
parser = 'entry';
|
||||
} else {
|
||||
parser = 'style';
|
||||
}
|
||||
let lastTag = '';
|
||||
const opt = option.cloneNode();
|
||||
opt.textContent = sort.split(sortByRegex).reduce((acc, val) => {
|
||||
if (tagData[val]) {
|
||||
lastTag = val;
|
||||
return acc + (acc !== '' ? ' + ' : '') + tagData[val].text;
|
||||
const sortBy = prefs.get('manage.newUI.sort').split(sortByRegex); // 'title,asc'
|
||||
const len = sortBy.length;
|
||||
return styles.sort((a, b) => {
|
||||
let types, direction;
|
||||
let result = 0;
|
||||
let indx = 0;
|
||||
// multi-sort
|
||||
while (result === 0 && indx < len) {
|
||||
types = tagData[sortBy[indx++]];
|
||||
direction = sortBy[indx++] === 'asc' ? 1 : -1;
|
||||
result = types.sorter(types.parse[parser](a), types.parse[parser](b)) * direction;
|
||||
}
|
||||
if (lastTag.indexOf('date') > -1) return acc + meta[val === 'desc' ? 'dateNew' : 'dateOld'];
|
||||
if (lastTag === 'disabled') return acc + meta[val === 'desc' ? 'enabled' : 'disabled'] + meta['labelFirst'];
|
||||
if (lastTag !== 'title') return acc + meta[val === 'desc' ? 'labelLast' : 'labelFirst'];
|
||||
return acc + (meta[val] || '');
|
||||
}, '');
|
||||
opt.value = sort;
|
||||
container.appendChild(opt);
|
||||
});
|
||||
renderBin.appendChild(container);
|
||||
select.appendChild(renderBin);
|
||||
select.value = prefs.get('manage.newUI.sort');
|
||||
}
|
||||
|
||||
function sortStyles({styles, parser}) {
|
||||
if (!styles) {
|
||||
styles = [...installed.children];
|
||||
parser = 'entry';
|
||||
} else {
|
||||
parser = 'style';
|
||||
return result;
|
||||
});
|
||||
}
|
||||
const sortBy = prefs.get('manage.newUI.sort').split(sortByRegex); // 'title,asc'
|
||||
const len = sortBy.length;
|
||||
return styles.sort((a, b) => {
|
||||
let types, direction;
|
||||
let result = 0;
|
||||
let indx = 0;
|
||||
// multi-sort
|
||||
while (result === 0 && indx < len) {
|
||||
types = tagData[sortBy[indx++]];
|
||||
direction = sortBy[indx++] === 'asc' ? 1 : -1;
|
||||
result = types.sorter(types.parse[parser](a), types.parse[parser](b)) * direction;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function manageSort(event) {
|
||||
event.preventDefault();
|
||||
prefs.set('manage.newUI.sort', this.value);
|
||||
debounce(updateSort);
|
||||
}
|
||||
|
||||
function updateSort() {
|
||||
const renderBin = document.createDocumentFragment();
|
||||
const entries = sortStyles({parser: 'entry'});
|
||||
const isDiffSort = [...installed.children].find((entry, index) => entry.id !== entries[index].id);
|
||||
let index = 0;
|
||||
function moveEntries() {
|
||||
const t0 = performance.now();
|
||||
let moved = 0;
|
||||
while (
|
||||
index < entries.length &&
|
||||
(++moved < 10 || performance.now() - t0 < 10)
|
||||
) {
|
||||
renderBin.appendChild(entries[index++]);
|
||||
function updateSort() {
|
||||
const renderBin = document.createDocumentFragment();
|
||||
const entries = sortStyles({parser: 'entry'});
|
||||
const isDiffSort = [...installed.children].find((entry, index) => entry.id !== entries[index].id);
|
||||
let index = 0;
|
||||
function moveEntries() {
|
||||
const t0 = performance.now();
|
||||
let moved = 0;
|
||||
while (
|
||||
index < entries.length &&
|
||||
(++moved < 10 || performance.now() - t0 < 10)
|
||||
) {
|
||||
renderBin.appendChild(entries[index++]);
|
||||
}
|
||||
if (index < entries.length) {
|
||||
requestAnimationFrame(moveEntries);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (index < entries.length) {
|
||||
requestAnimationFrame(moveEntries);
|
||||
return;
|
||||
if (isDiffSort !== undefined) {
|
||||
moveEntries();
|
||||
installed.appendChild(renderBin);
|
||||
updateStripes();
|
||||
}
|
||||
}
|
||||
if (isDiffSort !== undefined) {
|
||||
moveEntries();
|
||||
installed.appendChild(renderBin);
|
||||
updateStripes();
|
||||
|
||||
function manageSort(event) {
|
||||
event.preventDefault();
|
||||
prefs.set('manage.newUI.sort', this.value);
|
||||
debounce(updateSort);
|
||||
}
|
||||
}
|
||||
|
||||
function showSortHelp(event) {
|
||||
event.preventDefault();
|
||||
messageBox({
|
||||
className: 'help-text',
|
||||
title: t('sortStylesHelpTitle'),
|
||||
contents:
|
||||
$create('div',
|
||||
t('sortStylesHelp').split('\n').map(line =>
|
||||
$create('p', line))),
|
||||
buttons: [t('confirmOK')],
|
||||
});
|
||||
}
|
||||
function showSortHelp(event) {
|
||||
event.preventDefault();
|
||||
messageBox({
|
||||
className: 'help-text',
|
||||
title: t('sortStylesHelpTitle'),
|
||||
contents:
|
||||
$create('div',
|
||||
t('sortStylesHelp').split('\n').map(line =>
|
||||
$create('p', line))),
|
||||
buttons: [t('confirmOK')],
|
||||
});
|
||||
}
|
||||
|
||||
function sortInit() {
|
||||
$('#sort-select').addEventListener('change', manageSort);
|
||||
$('#sorter-help').onclick = showSortHelp;
|
||||
addSortOptions();
|
||||
}
|
||||
function sortInit() {
|
||||
$('#sort-select').addEventListener('change', manageSort);
|
||||
$('#sorter-help').onclick = showSortHelp;
|
||||
addSortOptions();
|
||||
}
|
||||
|
||||
function updateStripes() {
|
||||
let index = 0;
|
||||
[...installed.children].forEach(entry => {
|
||||
const list = entry.classList;
|
||||
if (!list.contains('hidden')) {
|
||||
list.add(index % 2 ? 'odd' : 'even');
|
||||
list.remove(index++ % 2 ? 'even' : 'odd');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {sortInit, updateSort, sortStyles, updateStripes};
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* global messageBox */
|
||||
/* global ENTRY_ID_PREFIX, newUI */
|
||||
/* global filtersSelector, filterAndAppend, updateSort */
|
||||
/* global filtersSelector, filterAndAppend, sorter */
|
||||
'use strict';
|
||||
|
||||
onDOMready().then(() => {
|
||||
|
@ -144,7 +144,7 @@ function reportUpdateState(state, style, details) {
|
|||
}
|
||||
if (filtersSelector.hide) {
|
||||
filterAndAppend({entry});
|
||||
debounce(updateSort);
|
||||
debounce(sorter().updateSort);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user