Clean sort code.

This commit is contained in:
Lars Jung 2016-07-01 19:26:17 +02:00
parent 73e74bb887
commit 8485905901
4 changed files with 51 additions and 78 deletions

View file

@ -345,7 +345,7 @@
"column": 0, "column": 0,
"reverse": false, "reverse": false,
"ignorecase": true, "ignorecase": true,
"natural": false, "natural": true,
"folders": 0 "folders": 0
}, },
@ -390,7 +390,7 @@
"enabled": true, "enabled": true,
"show": true, "show": true,
"maxSubfolders": 50, "maxSubfolders": 50,
"naturalSort": false, "naturalSort": true,
"ignorecase": true "ignorecase": true
} }
} }

View file

@ -13,51 +13,25 @@ const settings = Object.assign({
folders: 0 folders: 0
}, allsettings.sort); }, allsettings.sort);
const storekey = 'ext/sort'; const storekey = 'ext/sort';
const template = '<img src="' + resource.image('sort') + '" class="sort" alt="sort order"/>'; const template = `<img src="${resource.image('sort')}" class="sort" alt="sort order"/>`;
const getTypeOrder = item => item.isFolder() ? settings.folders : 1;
const columnProps = {0: 'label', 1: 'time', 2: 'size'};
const columnClasses = {0: 'label', 1: 'date', 2: 'size'};
const getType = item => { const cmpFn = (prop, reverse, ignorecase, natural) => {
const $item = dom(item); return (el1, el2) => {
const item1 = el1._item;
const item2 = el2._item;
if ($item.hasCls('folder-parent')) { let res = getTypeOrder(item1) - getTypeOrder(item2);
return 0;
}
if ($item.hasCls('folder')) {
if (settings.folders === 1) {
return 2;
} else if (settings.folders === 2) {
return 3;
}
return 1;
}
return 2;
};
const columnGetters = {
0: el => el._item.label,
1: el => el._item.time,
2: el => el._item.size
};
const columnClasses = {
0: 'label',
1: 'date',
2: 'size'
};
const cmpFn = (getValue, reverse, ignorecase, natural) => {
return (item1, item2) => {
let res;
let val1;
let val2;
res = getType(item1) - getType(item2);
if (res !== 0) { if (res !== 0) {
return res; return res;
} }
val1 = getValue(item1); let val1 = item1[prop];
val2 = getValue(item2); let val2 = item2[prop];
if (isNaN(val1) || isNaN(val2)) { if (isNaN(val1) || isNaN(val2)) {
val1 = String(val1); val1 = String(val1);
@ -77,14 +51,14 @@ const cmpFn = (getValue, reverse, ignorecase, natural) => {
const sortItems = (column, reverse) => { const sortItems = (column, reverse) => {
const $headers = dom('#items li.header a'); const $headers = dom('#items li.header a');
const $header = dom('#items li.header a.' + columnClasses[column]); const $header = dom('#items li.header a.' + columnClasses[column]);
const fn = cmpFn(columnGetters[column], reverse, settings.ignorecase, column === 0 && settings.natural); const fn = cmpFn(columnProps[column], reverse, settings.ignorecase, settings.natural);
store.put(storekey, {column, reverse}); store.put(storekey, {column, reverse});
$headers.rmCls('ascending').rmCls('descending'); $headers.rmCls('ascending').rmCls('descending');
$header.addCls(reverse ? 'descending' : 'ascending'); $header.addCls(reverse ? 'descending' : 'ascending');
dom(toArray(dom('#items .item')).sort(fn)).appTo('#items'); dom(toArray(dom('#items .item:not(.folder-parent)')).sort(fn)).appTo('#items');
}; };
const onContentChanged = () => { const onContentChanged = () => {
@ -101,11 +75,11 @@ const addToggles = () => {
each(columnClasses, (cls, idx) => { each(columnClasses, (cls, idx) => {
const pos = idx === '0' ? 'app' : 'pre'; const pos = idx === '0' ? 'app' : 'pre';
$header $header
.find('a.' + cls)[pos](template) .find('a.' + cls)[pos](template)
.on('click', ev => { .on('click', ev => {
sortItems(idx, dom(ev.currentTarget).hasCls('ascending')); sortItems(idx, dom(ev.currentTarget).hasCls('ascending'));
ev.preventDefault(); ev.preventDefault();
}); });
}); });
}; };

View file

@ -66,7 +66,7 @@ const cmpItems = (item1, item2) => {
val2 = val2.toLowerCase(); val2 = val2.toLowerCase();
} }
return settings.natural ? naturalCmp(val1, val2) : regularCmp(val1, val2); return settings.naturalSort ? naturalCmp(val1, val2) : regularCmp(val1, val2);
}; };
const update = item => { const update = item => {

View file

@ -3,58 +3,57 @@
// Modified to make it work with h5ai // Modified to make it work with h5ai
const reToken = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi;
const re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi; const reDate = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
const reStrip = /(^[ ]*|[ ]*$)/g;
const dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/;
const reHex = /^0x[0-9a-f]+$/i; const reHex = /^0x[0-9a-f]+$/i;
const reLeadingZero = /^0/; const reLeadingZero = /^0/;
/* eslint-disable complexity */ /* eslint-disable complexity */
const naturalCmp = (val1, val2) => { const naturalCmp = (a, b) => {
// convert all to strings strip whitespace // convert all to strings strip whitespace
const x = String(val1).replace(reStrip, ''); const x = String(a).trim();
const y = String(val2).replace(reStrip, ''); const y = String(b).trim();
// chunk/tokenize
const xN = x.replace(re, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
const yN = y.replace(re, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
// numeric, hex or date detection
const xD = parseInt(x.match(reHex), 10) || xN.length !== 1 && x.match(dre) && Date.parse(x);
const yD = parseInt(y.match(reHex), 10) || xD && y.match(dre) && Date.parse(y) || null;
let oFxNcL; // chunk/tokenize
let oFyNcL; const xTokens = x.replace(reToken, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
const yTokens = y.replace(reToken, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
// first try and sort Hex codes or Dates // first try and sort Hex codes or Dates
if (yD) { const xDate = parseInt(x.match(reHex), 16) || xTokens.length !== 1 && x.match(reDate) && Date.parse(x);
if (xD < yD) { const yDate = parseInt(y.match(reHex), 16) || xDate && y.match(reDate) && Date.parse(y) || null;
if (yDate) {
if (xDate < yDate) {
return -1; return -1;
} else if (xD > yD) { }
if (xDate > yDate) {
return 1; return 1;
} }
} }
// natural sorting through split numeric strings and default strings // natural sorting through split numeric strings and default strings
for (let cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc += 1) { for (let idx = 0, len = Math.max(xTokens.length, yTokens.length); idx < len; idx += 1) {
// find floats not starting with '0', string or 0 if not defined (Clint Priest) // find floats not starting with '0', string or 0 if not defined (Clint Priest)
oFxNcL = !(xN[cLoc] || '').match(reLeadingZero) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; let xToken = !(xTokens[idx] || '').match(reLeadingZero) && parseFloat(xTokens[idx]) || xTokens[idx] || 0;
oFyNcL = !(yN[cLoc] || '').match(reLeadingZero) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; let yToken = !(yTokens[idx] || '').match(reLeadingZero) && parseFloat(yTokens[idx]) || yTokens[idx] || 0;
// handle numeric vs string comparison - number < string - (Kyle Adams) // handle numeric vs string comparison - number < string - (Kyle Adams)
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { if (isNaN(xToken) !== isNaN(yToken)) {
return isNaN(oFxNcL) ? 1 : -1; return isNaN(xToken) ? 1 : -1;
} else if (typeof oFxNcL !== typeof oFyNcL) {
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
oFxNcL = String(oFxNcL);
oFxNcL = String(oFxNcL);
} }
if (oFxNcL < oFyNcL) {
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
if (typeof xToken !== typeof yToken) {
xToken = String(xToken);
yToken = String(yToken);
}
if (xToken < yToken) {
return -1; return -1;
} }
if (oFxNcL > oFyNcL) { if (xToken > yToken) {
return 1; return 1;
} }
} }
return 0; return 0;
}; };
/* eslint-enable */ /* eslint-enable */