From 84859059016345e67efbd6033ea8a85ec584f7d3 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Fri, 1 Jul 2016 19:26:17 +0200 Subject: [PATCH] Clean sort code. --- src/_h5ai/private/conf/options.json | 4 +- src/_h5ai/public/js/lib/ext/sort.js | 64 +++++++--------------- src/_h5ai/public/js/lib/ext/tree.js | 2 +- src/_h5ai/public/js/lib/util/naturalCmp.js | 59 ++++++++++---------- 4 files changed, 51 insertions(+), 78 deletions(-) diff --git a/src/_h5ai/private/conf/options.json b/src/_h5ai/private/conf/options.json index 38d9c8d7..8b9ad279 100644 --- a/src/_h5ai/private/conf/options.json +++ b/src/_h5ai/private/conf/options.json @@ -345,7 +345,7 @@ "column": 0, "reverse": false, "ignorecase": true, - "natural": false, + "natural": true, "folders": 0 }, @@ -390,7 +390,7 @@ "enabled": true, "show": true, "maxSubfolders": 50, - "naturalSort": false, + "naturalSort": true, "ignorecase": true } } diff --git a/src/_h5ai/public/js/lib/ext/sort.js b/src/_h5ai/public/js/lib/ext/sort.js index b2ba4240..62c3393e 100644 --- a/src/_h5ai/public/js/lib/ext/sort.js +++ b/src/_h5ai/public/js/lib/ext/sort.js @@ -13,51 +13,25 @@ const settings = Object.assign({ folders: 0 }, allsettings.sort); const storekey = 'ext/sort'; -const template = 'sort order'; +const template = `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 $item = dom(item); +const cmpFn = (prop, reverse, ignorecase, natural) => { + return (el1, el2) => { + const item1 = el1._item; + const item2 = el2._item; - if ($item.hasCls('folder-parent')) { - 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); + let res = getTypeOrder(item1) - getTypeOrder(item2); if (res !== 0) { return res; } - val1 = getValue(item1); - val2 = getValue(item2); + let val1 = item1[prop]; + let val2 = item2[prop]; if (isNaN(val1) || isNaN(val2)) { val1 = String(val1); @@ -77,14 +51,14 @@ const cmpFn = (getValue, reverse, ignorecase, natural) => { const sortItems = (column, reverse) => { const $headers = dom('#items li.header a'); 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}); $headers.rmCls('ascending').rmCls('descending'); $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 = () => { @@ -101,11 +75,11 @@ const addToggles = () => { each(columnClasses, (cls, idx) => { const pos = idx === '0' ? 'app' : 'pre'; $header - .find('a.' + cls)[pos](template) - .on('click', ev => { - sortItems(idx, dom(ev.currentTarget).hasCls('ascending')); - ev.preventDefault(); - }); + .find('a.' + cls)[pos](template) + .on('click', ev => { + sortItems(idx, dom(ev.currentTarget).hasCls('ascending')); + ev.preventDefault(); + }); }); }; diff --git a/src/_h5ai/public/js/lib/ext/tree.js b/src/_h5ai/public/js/lib/ext/tree.js index 076203a8..a9b0fbdd 100644 --- a/src/_h5ai/public/js/lib/ext/tree.js +++ b/src/_h5ai/public/js/lib/ext/tree.js @@ -66,7 +66,7 @@ const cmpItems = (item1, item2) => { val2 = val2.toLowerCase(); } - return settings.natural ? naturalCmp(val1, val2) : regularCmp(val1, val2); + return settings.naturalSort ? naturalCmp(val1, val2) : regularCmp(val1, val2); }; const update = item => { diff --git a/src/_h5ai/public/js/lib/util/naturalCmp.js b/src/_h5ai/public/js/lib/util/naturalCmp.js index 8b6731bb..888e23a8 100644 --- a/src/_h5ai/public/js/lib/util/naturalCmp.js +++ b/src/_h5ai/public/js/lib/util/naturalCmp.js @@ -3,58 +3,57 @@ // Modified to make it work with h5ai - -const re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi; -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 reToken = /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/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 reHex = /^0x[0-9a-f]+$/i; const reLeadingZero = /^0/; /* eslint-disable complexity */ -const naturalCmp = (val1, val2) => { +const naturalCmp = (a, b) => { // convert all to strings strip whitespace - const x = String(val1).replace(reStrip, ''); - const y = String(val2).replace(reStrip, ''); - // 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; + const x = String(a).trim(); + const y = String(b).trim(); - let oFxNcL; - let oFyNcL; + // chunk/tokenize + 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 - if (yD) { - if (xD < yD) { + const xDate = parseInt(x.match(reHex), 16) || xTokens.length !== 1 && x.match(reDate) && Date.parse(x); + const yDate = parseInt(y.match(reHex), 16) || xDate && y.match(reDate) && Date.parse(y) || null; + if (yDate) { + if (xDate < yDate) { return -1; - } else if (xD > yD) { + } + if (xDate > yDate) { return 1; } } // 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) - oFxNcL = !(xN[cLoc] || '').match(reLeadingZero) && parseFloat(xN[cLoc]) || xN[cLoc] || 0; - oFyNcL = !(yN[cLoc] || '').match(reLeadingZero) && parseFloat(yN[cLoc]) || yN[cLoc] || 0; + let xToken = !(xTokens[idx] || '').match(reLeadingZero) && parseFloat(xTokens[idx]) || xTokens[idx] || 0; + let yToken = !(yTokens[idx] || '').match(reLeadingZero) && parseFloat(yTokens[idx]) || yTokens[idx] || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) - if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { - return isNaN(oFxNcL) ? 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 (isNaN(xToken) !== isNaN(yToken)) { + return isNaN(xToken) ? 1 : -1; } - 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; } - if (oFxNcL > oFyNcL) { + if (xToken > yToken) { return 1; } } - return 0; }; /* eslint-enable */