From f4038c1f00fe1e88ef2cfcf07f4c49dfae85a1a3 Mon Sep 17 00:00:00 2001 From: LeeWannacott Date: Fri, 26 May 2023 22:09:02 +1200 Subject: [PATCH] NPM version 1.17.0; tables with multiple tbodies; cells-sort class; fix for tbody being null. --- README.md | 1 - browser-extensions/chrome/manifest.json | 2 +- browser-extensions/chrome/table-sort-js.zip | Bin 7626 -> 7927 bytes browser-extensions/chrome/table-sort.js | 722 ++++++++++--------- browser-extensions/firefox/manifest.json | 2 +- browser-extensions/firefox/table-sort-js.zip | Bin 7626 -> 7927 bytes browser-extensions/firefox/table-sort.js | 722 ++++++++++--------- npm/README.md | 30 +- npm/package.json | 2 +- npm/table-sort.js | 722 ++++++++++--------- public/docs/about.html | 3 +- public/docs/demo.html | 323 +++++---- public/docs/development.html | 3 +- public/docs/html5.html | 2 +- public/table-sort.js | 9 +- test/table.js | 2 +- 16 files changed, 1385 insertions(+), 1160 deletions(-) diff --git a/README.md b/README.md index 10648b7..37ca0b9 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ Or Minified (smaller size, but harder to debug!): Example on how to use table-sort-js with [HTML](https://leewannacott.github.io/table-sort-js/docs/html5.html) - - Option 2: Install from npm: ```javascript diff --git a/browser-extensions/chrome/manifest.json b/browser-extensions/chrome/manifest.json index aa5aedc..251c72e 100644 --- a/browser-extensions/chrome/manifest.json +++ b/browser-extensions/chrome/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "author": "Lee Wannacott", "name": "table-sort-js", - "version": "1.16.0", + "version": "1.17.0", "description": "Makes tables sortable using table-sort-js: https://github.com/LeeWannacott/table-sort-js", "icons": { "48": "icons/t.png" }, "browser_action": { diff --git a/browser-extensions/chrome/table-sort-js.zip b/browser-extensions/chrome/table-sort-js.zip index ed4be8a96b9f67af750e445b881dbc790871aa93..43adff3708281417e174a414943312174383dab9 100644 GIT binary patch delta 5035 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 B!ioR@ delta 4732 zcmV-?5`*pcJ<2<Q)g0||+s5%fpW;M&8qlIZN$F%JRAi52#fcls_E<{O zlx7+TIEt|G!Qep4tjyQwgY04c@&(TpOIhyA+Uy}jMNgGY}@uMDmd zy2tXO+jjrQzzP$3oJRJe1L>!qSWs8(7 zg7AHCO%>|R&5aKa-Z73{J+=_fXqqdGgwmUugI{ru^U-Snp5+OAdN2~A_MT^~43cEr zdsgIGNwa87zF}bzFG|9HitxC}N~6rlDqj}l`^)neq<0Eh;L@VTVlfF~NW;l5!FC1xZ5bET>-+vF%Om=a zG>?`EWu3wg2v<$949hsr*cj}40-}aVz?d#%ALZg-8H)EsM3VS_J;lK^fWHWW4*~sY z5{K_eo`C|Wz#wxPM6_TCjez(h^?Q#-z20n@amE7h0v`T_c_n3KoLyhap$l?MWq90GPh(CYB)qcBHKD^O&*P z1HK3E^lAq-XS**X5jlTVdZe!3KlZ%csfmw~kkc0_?6pV#V$;IZ({x0^E|wGL=`hGj8poCUek}@ zt)pMOx6$r=@Z>Fff}c|Pi4C4S9K}Z(Y8_HE+=;(`zxVZYcYrvjpMHR!`1I2`{J@{@ z;0OL(!cTlUs7Z}RH&MnzCYSAg$hvQO5-iV_-;ff035hG7*g9Nt6Nc50yQGqj&ht+s$7#7(>h^D-}=0g31-(gB!# z-Mrs&E^vF6kSe%gaC9(!e5{!1k?;xv?|U;c zN#VHP@xVcF%X9@ujOegBQ%?&pGSH!%GZX=)7S2umXj6GlS2&kfSMPwZ58s$)yZu0a z7g?&F8*Tw~ZSV;Ynfk)aM-opaE@DowNoKUbEJ!Eb0p_PT4Nqe|AUm*)Wy>TnNf2|| zm~9*xkKF=uSKHmyLD#+JCls)QK&z<mhbW5J??l?0vQS>R-Q7IL9W(?4{2o)WAS5#ydA^06~`!M&WeP8F9uew zP|#%oPhqzGMIL96YV^&RvE!b$6*Zj67dJ&S`)V!}Q6haCgmV#fq)!^Fcc?E(jiwHS zSo(lrlokTg04G}fr9AdQyWG=*rvJidSZSA}OAKANF?IBpbD6|!P zQdDbWv#W|iHsA;*r3oMWaWN!+`~&uqur{Cc$$$R!Z^#(%@9+Jol_n>D4U#2>kp9l! zc7ERZ`%b@desnQDt3Ulo7gD(w%vIELC(*kg1pmj)$m?J6>JEy-z9xuSk;<*?z9fHK z$ZA6@)Y|;0KS<7klM+_r)v|O|8;YG;-!b@qF*Q;b*NPTFDbg8SMxy_JAso&ti<*tk z7D0BgOpWgYACVa?HHY_6XnmjH&}DwgcK{msizSZgri@7X#*1MPy@T>U|(wk66{M2tmI$^huVmQ`?G{w*BX&U6O?a1 zLFxA+&TqBAB{RB5QF(8#t1F*Nk>}_RMUY9~a3puhu)i=bfiS&|2(0G%96jcl&=9sm{352xU?FY+6IT0Dg{<28U&-N|gf zo8Z7yM`>SKm57ftip|y4=&^KBl#$gzXX8>kUA+;tao1WaiyJkxa{21)6{M#8RD=ZL#GvjBkSLC@%_5EklxM^2p#TXSu5@%C_P@MwnA&yxCRk(q>i zp?O9zh5FASD^K!kk2VSsXC4Y6m-hjmF6P(XQqlA$xslLh%!iej2U% zes`OPeYPE-GbSsk zn{jwW1B|*+u!7$y5T193UXh?&@^b&ak#_=1{B_O_hD3ZgtUr9oS=X_6C?nrcMi+N@ zL1I`PR^-&jFPrhE)$5v|1Z3eFsrAE^#bs=t!I4FO9_&;B>PHOH$X%Rh=fK|~_wqe~ik>n;_Jobp5^$8XtJ9zQA8R;n; zs--y-JS}NqZWl}rMrO0zh1X`bvmys!FOow7I4++8lQab3X9guO1WYC$)1in=!q z*#!1PJ+GV(p?*R?A;*sm6U)7B!kUIiPx9XuRajRs%`9QF>#qq@>i+@O-#I~a7g%>}+fbsjud zd$TsRbwfdb`yvkmyc3gxvM3UNJK)njbX}{2hmg|i@S0GFr0*M9@FBhqAPMkcQCu&B zYl_!eu}RW=qT}7x_qWCvF{B&8o_U?+1?6tJ3{ZRypxjhVeb7E*5sroGSj%)PkANre z`%vi?$LeDkmscGO4A{di*hA&6>NY~~5)X->cz^;!eLu9kM;Z12(iu~KM2E8|taL^DNsa)NW%AT8y5kZ`YrYG$=1He`Iiu9XK`khjzT#diU4a~i(4OD9hC%e zLHTprRh^e7@IQw_o3}7DJ#2G*Lkh92lT^7n_ZQa;n%M$^n(#w=!*E1YJ=5%ttqOGQ zwq{$a#8y0Bh_N*8ww$ePTQ71QZ8%_dQkU0XoU~6|qxRxi0tmhc3I3bRItHo4r|;M|n8g+w(&G1yQE5Vdi$H?}`tYy;1Zkq0(5N9M zcx(O)6dHMlgEe`0Tk97Ly*~ZbqSly%X1(Th|Hl1z{7>%+>7RL$Uz2O=N!?plTWG2e z^+ewoYdu`L!%HE0pxiViXQuAzX*Hs;Y+T4Fu-*C9hV81pmD9FILyi|j(YGHUf??}{ z!c4tBKUac()h_os&)(6(nv9kSNU_FbGF7I*w(6MNuFa8)WvUOZWvwV~pMQv1Ty1i| z1UB+u52l zR#mvxd5dxJU!J-g*$t>S+QHyfSG((zM60D#Ro6Oyxs5rRc_?WfU^-EDkFlOLW}u}o zx+l~<*>PJ}3jwMrD5KOXqZP|~gHJ=1FBG<58t)Mfnm%V`Ott*F4%@Dc>z=9^+SKM2 z8CmoG^nEB-6SWCjw?#KlwwzQQ#s^`7So&K8_?6JiLN){H=xV+D+i5x z+mOtEE$|+o8T@iMIB? z!;!#$_~n*iGvGB%qBniqClM_i@E*$_;Gi41g#+IH+KPkeR#vrRZX|ViGj`CmS_JC5 zcJ`<$AvKqV@jDMYYoM+guR22=XfMOGz1zco|HYQZi(DKU{I8C_;#DP3RCOpO|Lp~S z%yv;?MZs?;g?YMweVyWcye>HYN*Law{y_UZ!MBuGpZPz+q2GaOqk^(SU0n6nBv>(91io7v{rj{i 1) { + // Why index 1?; I don't remember return sortableTable.querySelectorAll("tbody")[1]; } else { - return sortableTable.querySelector("tbody"); + return sortableTable.querySelectorAll("tbody"); } } else { - return sortableTable.querySelector("tbody"); + // if or exists below the browser will make + return sortableTable.querySelectorAll("tbody"); } } @@ -108,389 +109,455 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { function makeTableSortable(sortableTable) { const table = { - body: getTableBody(sortableTable), - head: sortableTable.querySelector("thead"), + bodies: getTableBodies(sortableTable), + theads: sortableTable.querySelectorAll("thead"), + rows: [], + headers: [], }; - table.headers = table.head.querySelectorAll("th"); - table.rows = table.body.querySelectorAll("tr"); - - let columnIndexesClicked = []; + for (let index of table.bodies.keys()) { + if (table.bodies.item(index) == null) { + return; + } + table.headers.push(table.theads.item(index).querySelectorAll("th")); + table.rows.push(table.bodies.item(index).querySelectorAll("tr")); + } - const isNoSortClassInference = - sortableTable.classList.contains("no-class-infer"); + table.hasClass = { + noClassInfer: sortableTable.classList.contains("no-class-infer"), + cellsSort: sortableTable.classList.contains("cells-sort"), + tableArrows: sortableTable.classList.contains("table-arrows"), + rememberSort: sortableTable.classList.contains("remember-sort"), + }; - for (let [columnIndex, th] of table.headers.entries()) { - if (!th.classList.contains("disable-sort")) { - th.style.cursor = "pointer"; - if (!isNoSortClassInference) { - inferSortClasses(table.rows, columnIndex, th); + for ( + let headerIndex = 0; + headerIndex < table.theads.length; + headerIndex++ + ) { + let columnIndexesClicked = []; + for (let [columnIndex, th] of table.headers[headerIndex].entries()) { + if (!th.classList.contains("disable-sort")) { + th.style.cursor = "pointer"; + if (!table.hasClass.noClassInfer) { + inferSortClasses(table.rows[headerIndex], columnIndex, th); + } + makeEachColumnSortable( + th, + headerIndex, + columnIndex, + table, + columnIndexesClicked + ); } - makeEachColumnSortable( - th, - columnIndex, - table, - sortableTable, - columnIndexesClicked - ); } } } - function makeEachColumnSortable( - th, - columnIndex, - table, - sortableTable, - columnIndexesClicked - ) { - const desc = th.classList.contains("order-by-desc"); - const tableArrows = sortableTable.classList.contains("table-arrows"); - const [arrowUp, arrowDown] = [" ▲", " ▼"]; - const fillValue = "!X!Y!Z!"; - - if (desc && tableArrows) { - th.insertAdjacentText("beforeend", arrowDown); - } else if (tableArrows) { - th.insertAdjacentText("beforeend", arrowUp); + function cellsOrRows(table, tr) { + if (table.hasClass.cellsSort) { + return tr.innerHTML; + } else { + return tr.outerHTML; } + } - function sortDataAttributes(tableRows, column) { - for (let [i, tr] of tableRows.entries()) { - let dataAttributeTd = getColumn(tr, column.spanSum, column.span).dataset - .sort; - column.toBeSorted.push(`${dataAttributeTd}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; - } + function sortDataAttributes(table, column) { + for (let [i, tr] of table.visibleRows.entries()) { + let dataAttributeTd = column.getColumn(tr, column.spanSum, column.span) + .dataset.sort; + column.toBeSorted.push(`${dataAttributeTd}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } - function sortFileSize(tableRows, column) { - let unitToMultiplier = { - b: 1, - kb: 1000, - kib: 2 ** 10, - mb: 1e6, - mib: 2 ** 20, - gb: 1e9, - gib: 2 ** 30, - tb: 1e12, - tib: 2 ** 40, - }; - const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i; - for (let [i, tr] of tableRows.entries()) { - let fileSizeTd = tr - .querySelectorAll("td") - .item(columnIndex).textContent; - let match = fileSizeTd.match(numberWithUnitType); - if (match) { - let number = parseFloat(match[1]); - let unit = match[2].toLowerCase(); - let multiplier = unitToMultiplier[unit]; - column.toBeSorted.push(`${number * multiplier}#${i}`); - } else { - column.toBeSorted.push(`${fillValue}#${i}`); - } + function sortFileSize(table, column, columnIndex) { + let unitToMultiplier = { + b: 1, + kb: 1000, + kib: 2 ** 10, + mb: 1e6, + mib: 2 ** 20, + gb: 1e9, + gib: 2 ** 30, + tb: 1e12, + tib: 2 ** 40, + }; + const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i; + for (let [i, tr] of table.visibleRows.entries()) { + let fileSizeTd = tr.querySelectorAll("td").item(columnIndex).textContent; + let match = fileSizeTd.match(numberWithUnitType); + if (match) { + let number = parseFloat(match[1]); + let unit = match[2].toLowerCase(); + let multiplier = unitToMultiplier[unit]; + column.toBeSorted.push(`${number * multiplier}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } } + } - function sortByRuntime(tableRows, column) { - try { - for (let [i, tr] of tableRows.entries()) { - const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i; - let columnOfTd = ""; - // TODO: github actions runtime didn't like textContent, tests didn't like innerText? - if (testingTableSortJS) { - columnOfTd = getColumn(tr, column.spanSum, column.span).textContent; - } else { - columnOfTd = getColumn(tr, column.spanSum, column.span).innerText; - } - let match = columnOfTd.match(regexMinutesAndSeconds); - let [minutesInSeconds, hours, seconds] = [0, 0, 0]; - let timeinSeconds = columnOfTd; - if (match) { - const regexHours = match[1]; - if (regexHours) { - hours = Number(regexHours.replace("h", "")) * 60 * 60; - } - const regexMinutes = match[2]; - if (regexMinutes) { - minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60; - } - const regexSeconds = match[3]; - if (regexSeconds) { - seconds = Number(regexSeconds.replace("s", "")); + function sortDates(datesFormat, table, column) { + try { + for (let [i, tr] of table.visibleRows.entries()) { + let columnOfTd, datesRegex; + if (datesFormat === "mdy" || datesFormat === "dmy") { + datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/; + } else if (datesFormat === "ymd") { + datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/; + } + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + let match = columnOfTd.match(datesRegex); + let [years, days, months] = [0, 0, 0]; + let numberToSort = columnOfTd; + if (match) { + const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]]; + if (regPos1 && regPos2 && regPos3) { + if (datesFormat === "mdy") { + [months, days, years] = [regPos1, regPos2, regPos3]; + } else if (datesFormat === "ymd") { + [years, months, days] = [regPos1, regPos2, regPos3]; + } else { + [days, months, years] = [regPos1, regPos2, regPos3]; } - timeinSeconds = hours + minutesInSeconds + seconds; } - column.toBeSorted.push(`${timeinSeconds}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; + numberToSort = Number( + years + + String(months).padStart(2, "0") + + String(days).padStart(2, "0") + ); } - } catch (e) { - console.log(e); + column.toBeSorted.push(`${numberToSort}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } catch (e) { + console.log(e); } + } - function sortDates(datesFormat, tableRows, column) { - try { - for (let [i, tr] of tableRows.entries()) { - let columnOfTd, datesRegex; - if (datesFormat === "mdy" || datesFormat === "dmy") { - datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/; - } else if (datesFormat === "ymd") { - datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/; + function sortByRuntime(table, column) { + try { + for (let [i, tr] of table.visibleRows.entries()) { + const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i; + let columnOfTd = ""; + // TODO: github actions runtime didn't like textContent, tests didn't like innerText? + if (testingTableSortJS) { + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + } else { + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).innerText; + } + let match = columnOfTd.match(regexMinutesAndSeconds); + let [minutesInSeconds, hours, seconds] = [0, 0, 0]; + let timeinSeconds = columnOfTd; + if (match) { + const regexHours = match[1]; + if (regexHours) { + hours = Number(regexHours.replace("h", "")) * 60 * 60; } - columnOfTd = getColumn(tr, column.spanSum, column.span).textContent; - let match = columnOfTd.match(datesRegex); - let [years, days, months] = [0, 0, 0]; - let numberToSort = columnOfTd; - if (match) { - const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]]; - if (regPos1 && regPos2 && regPos3) { - if (datesFormat === "mdy") { - [months, days, years] = [regPos1, regPos2, regPos3]; - } else if (datesFormat === "ymd") { - [years, months, days] = [regPos1, regPos2, regPos3]; - } else { - [days, months, years] = [regPos1, regPos2, regPos3]; - } - } - numberToSort = Number( - years + - String(months).padStart(2, "0") + - String(days).padStart(2, "0") - ); + const regexMinutes = match[2]; + if (regexMinutes) { + minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60; + } + const regexSeconds = match[3]; + if (regexSeconds) { + seconds = Number(regexSeconds.replace("s", "")); } - column.toBeSorted.push(`${numberToSort}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; + timeinSeconds = hours + minutesInSeconds + seconds; } - } catch (e) { - console.log(e); + column.toBeSorted.push(`${timeinSeconds}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } catch (e) { + console.log(e); } + } - function rememberSort() { - // if user clicked different column from first column reset times clicked. - columnIndexesClicked.push(columnIndex); - if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) { - const lastColumnClicked = - columnIndexesClicked[columnIndexesClicked.length - 1]; - const secondLastColumnClicked = - columnIndexesClicked[columnIndexesClicked.length - 2]; - if (lastColumnClicked !== secondLastColumnClicked) { - columnIndexesClicked.shift(); - timesClickedColumn = 0; + function getTableData(tableProperties, timesClickedColumn) { + const { + table, + tableRows, + fillValue, + column, + th, + hasThClass, + isSortDates, + desc, + arrow, + } = tableProperties; + for (let [i, tr] of tableRows.entries()) { + let tdTextContent = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + if (tdTextContent.length === 0) { + tdTextContent = ""; + } + if (tdTextContent.trim() !== "") { + if ( + !hasThClass.fileSize && + !hasThClass.dataSort && + !hasThClass.runtime && + !hasThClass.filesize && + !isSortDates.dayMonthYear && + !isSortDates.yearMonthDay && + !isSortDates.monthDayYear + ) { + column.toBeSorted.push(`${tdTextContent}#${i}`); + columnIndexAndTableRow[`${tdTextContent}#${i}`] = cellsOrRows( + table, + tr + ); } + } else { + // Fill in blank table cells dict key with filler value. + column.toBeSorted.push(`${fillValue}#${i}`); + columnIndexAndTableRow[`${fillValue}#${i}`] = cellsOrRows(table, tr); } - return timesClickedColumn; } - function getColSpanData(headers, column) { - headers.forEach((th, index) => { - column.span[index] = th.colSpan; - if (index === 0) column.spanSum[index] = th.colSpan; - else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan; - }); - } + const isPunctSort = th.classList.contains("punct-sort"); + const isAlphaSort = th.classList.contains("alpha-sort"); + const isNumericSort = th.classList.contains("numeric-sort"); - function getColumn(tr, colSpanSum, colSpanData) { - return tr - .querySelectorAll("td") - .item( - colSpanData[columnIndex] === 1 - ? colSpanSum[columnIndex] - 1 - : colSpanSum[columnIndex] - colSpanData[columnIndex] - ); + function parseNumberFromString(str) { + let num; + str = str.slice(0, str.indexOf("#")); + if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) { + num = -1 * Number(str.slice(1, -1)); + } else { + num = Number(str); + } + return num; } - function getTableData(tableProperties) { - const { tableRows, column, hasThClass, isSortDates } = tableProperties; - for (let [i, tr] of tableRows.entries()) { - let tdTextContent = getColumn( - tr, - column.spanSum, - column.span - ).textContent; - if (tdTextContent.length === 0) { - tdTextContent = ""; - } - if (tdTextContent.trim() !== "") { - if (hasThClass.fileSize) { - fileSizeColumnTextAndRow[column.toBeSorted[i]] = tr.outerHTML; - } - // These classes already handle pushing to column and setting the tr html. - if ( - !hasThClass.fileSize && - !hasThClass.dataSort && - !hasThClass.runtime && - !isSortDates.dayMonthYear && - !isSortDates.yearMonthDay && - !isSortDates.monthDayYear - ) { - column.toBeSorted.push(`${tdTextContent}#${i}`); - columnIndexAndTableRow[`${tdTextContent}#${i}`] = tr.outerHTML; - } - } else { - // Fill in blank table cells dict key with filler value. - column.toBeSorted.push(`${fillValue}#${i}`); - columnIndexAndTableRow[`${fillValue}#${i}`] = tr.outerHTML; - } - } + function strLocaleCompare(str1, str2) { + return str1.localeCompare( + str2, + navigator.languages[0] || navigator.language, + { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort } + ); + } - const isPunctSort = th.classList.contains("punct-sort"); - const isAlphaSort = th.classList.contains("alpha-sort"); - const isNumericSort = th.classList.contains("numeric-sort"); + function handleNumbers(str1, str2) { + let num1, num2; + num1 = parseNumberFromString(str1); + num2 = parseNumberFromString(str2); - function parseNumberFromString(str) { - let num; - str = str.slice(0, str.indexOf("#")); - if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) { - num = -1 * Number(str.slice(1, -1)); - } else { - num = Number(str); - } - return num; + if (!isNaN(num1) && !isNaN(num2)) { + return num1 - num2; + } else { + return strLocaleCompare(str1, str2); } + } - function strLocaleCompare(str1, str2) { - return str1.localeCompare( - str2, - navigator.languages[0] || navigator.language, - { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort } - ); + function sortAscending(a, b) { + if (a.includes(`${fillValue}#`)) { + return 1; + } else if (b.includes(`${fillValue}#`)) { + return -1; + } else if (isNumericSort) { + return handleNumbers(a, b); + } else { + return strLocaleCompare(a, b); } + } - function handleNumbers(str1, str2) { - let num1, num2; - num1 = parseNumberFromString(str1); - num2 = parseNumberFromString(str2); + function sortDescending(a, b) { + return sortAscending(b, a); + } - if (!isNaN(num1) && !isNaN(num2)) { - return num1 - num2; - } else { - return strLocaleCompare(str1, str2); - } - } + function clearArrows(arrowUp = "▲", arrowDown = "▼") { + th.innerHTML = th.innerHTML.replace(arrowUp, ""); + th.innerHTML = th.innerHTML.replace(arrowDown, ""); + } - function sortAscending(a, b) { - if (a.includes(`${fillValue}#`)) { - return 1; - } else if (b.includes(`${fillValue}#`)) { - return -1; - } else if (isNumericSort) { - return handleNumbers(a, b); - } else { - return strLocaleCompare(a, b); - } - } + if (column.toBeSorted[0] === undefined) { + return; + } - function sortDescending(a, b) { - return sortAscending(b, a); + function changeTableArrow(arrowDirection) { + if (table.hasClass.tableArrows) { + clearArrows(arrow.up, arrow.down); + th.insertAdjacentText("beforeend", arrowDirection); } + } - function clearArrows(arrowUp = "▲", arrowDown = "▼") { - th.innerHTML = th.innerHTML.replace(arrowUp, ""); - th.innerHTML = th.innerHTML.replace(arrowDown, ""); - } + function sortColumn(sortDirection) { + column.toBeSorted.sort(sortDirection, { + numeric: !isAlphaSort, + ignorePunctuation: !isPunctSort, + }); + } - if (column.toBeSorted[0] === undefined) { - return; + if (timesClickedColumn === 1) { + if (desc) { + changeTableArrow(arrow.down); + sortColumn(sortDescending); + } else { + changeTableArrow(arrow.up); + sortColumn(sortAscending); } - - function changeTableArrow(arrowDirection) { - if (tableArrows) { - clearArrows(arrowUp, arrowDown); - th.insertAdjacentText("beforeend", arrowDirection); - } + } else if (timesClickedColumn === 2) { + timesClickedColumn = 0; + if (desc) { + changeTableArrow(arrow.up); + sortColumn(sortAscending); + } else { + changeTableArrow(arrow.down); + sortColumn(sortDescending); } + } + return timesClickedColumn; + } - function sortColumn(sortDirection) { - column.toBeSorted.sort(sortDirection, { - numeric: !isAlphaSort, - ignorePunctuation: !isPunctSort, - }); + function updateFilesize(i, table, tr, column, columnIndex) { + if (table.hasClass.cellsSort) { + tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; + } else { + // We do this to sort rows rather than cells: + const template = document.createElement("template"); + template.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; + tr = template.content.firstChild; + } + let fileSizeInBytesHTML = column.getColumn( + tr, + column.spanSum, + column.span + ).outerHTML; + const fileSizeInBytesText = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + + const fileSize = column.toBeSorted[i].replace(/#[0-9]*/, ""); + let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"]; + let replaced = false; + for (let i = 0; i < prefixes.length; ++i) { + let nextPrefixMultiplier = 2 ** (10 * (i + 1)); + if (fileSize < nextPrefixMultiplier) { + let prefixMultiplier = 2 ** (10 * i); + fileSizeInBytesHTML = fileSizeInBytesHTML.replace( + fileSizeInBytesText, + `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B` + ); + replaced = true; + break; } + } + if (!replaced) { + fileSizeInBytesHTML = fileSizeInBytesHTML.replace( + fileSizeInBytesText, + "NaN" + ); + } + tr.querySelectorAll("td").item(columnIndex).innerHTML = fileSizeInBytesHTML; + return table.hasClass.cellsSort ? tr.innerHTML : tr.outerHTML; + } - if (timesClickedColumn === 1) { - if (desc) { - changeTableArrow(arrowDown); - sortColumn(sortDescending); + function updateTable(tableProperties) { + const { column, table, columnIndex, hasThClass } = tableProperties; + for (let [i, tr] of table.visibleRows.entries()) { + if (hasThClass.fileSize) { + if (table.hasClass.cellsSort) { + tr.innerHTML = updateFilesize(i, table, tr, column, columnIndex); } else { - changeTableArrow(arrowUp); - sortColumn(sortAscending); + tr.outerHTML = updateFilesize(i, table, tr, column, columnIndex); } - } else if (timesClickedColumn === 2) { - timesClickedColumn = 0; - if (desc) { - changeTableArrow(arrowUp); - sortColumn(sortAscending); + } else if (!hasThClass.fileSize) { + if (table.hasClass.cellsSort) { + tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; } else { - changeTableArrow(arrowDown); - sortColumn(sortDescending); + tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; } } } + } - function updateTable(tableProperties) { - const { tableRows, column, hasThClass } = tableProperties; - for (let [i, tr] of tableRows.entries()) { - if (hasThClass.fileSize) { - tr.innerHTML = fileSizeColumnTextAndRow[column.toBeSorted[i]]; - let fileSizeInBytesHTML = tr - .querySelectorAll("td") - .item(columnIndex).innerHTML; - const fileSizeInBytesText = tr - .querySelectorAll("td") - .item(columnIndex).textContent; - // Remove the unique identifyer for duplicate values(#number). - column.toBeSorted[i] = column.toBeSorted[i].replace(/#[0-9]*/, ""); - const fileSize = parseFloat(column.toBeSorted[i]); - let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"]; - let replaced = false; - for (let i = 0; i < prefixes.length; ++i) { - let nextPrefixMultiplier = 2 ** (10 * (i + 1)); - if (fileSize < nextPrefixMultiplier) { - let prefixMultiplier = 2 ** (10 * i); - fileSizeInBytesHTML = fileSizeInBytesHTML.replace( - fileSizeInBytesText, - `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B` - ); - replaced = true; - break; - } - } - if (!replaced) { - fileSizeInBytesHTML = fileSizeInBytesHTML.replace( - fileSizeInBytesText, - "NaN" - ); - } - tr.querySelectorAll("td").item(columnIndex).innerHTML = - fileSizeInBytesHTML; - } else if (!hasThClass.fileSize) { - tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; - } + function getColSpanData(headers, column) { + headers.forEach((th, index) => { + column.span[index] = th.colSpan; + if (index === 0) column.spanSum[index] = th.colSpan; + else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan; + }); + } + + function rememberSort(columnIndexesClicked, timesClickedColumn, columnIndex) { + // if user clicked different column from first column reset times clicked. + columnIndexesClicked.push(columnIndex); + if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) { + const lastColumnClicked = + columnIndexesClicked[columnIndexesClicked.length - 1]; + const secondLastColumnClicked = + columnIndexesClicked[columnIndexesClicked.length - 2]; + if (lastColumnClicked !== secondLastColumnClicked) { + columnIndexesClicked.shift(); + timesClickedColumn = 0; } } + return timesClickedColumn; + } + + function makeEachColumnSortable( + th, + headerIndex, + columnIndex, + table, + columnIndexesClicked + ) { + const desc = th.classList.contains("order-by-desc"); + const arrow = { up: " ▲", down: " ▼" }; + const fillValue = "!X!Y!Z!"; + + if (desc && table.hasClass.tableArrows) { + th.insertAdjacentText("beforeend", arrow.down); + } else if (table.hasClass.tableArrows) { + th.insertAdjacentText("beforeend", arrow.up); + } let timesClickedColumn = 0; + const column = { + getColumn: function getColumn(tr, colSpanSum, colSpanData) { + return tr + .querySelectorAll("td") + .item( + colSpanData[columnIndex] === 1 + ? colSpanSum[columnIndex] - 1 + : colSpanSum[columnIndex] - colSpanData[columnIndex] + ); + }, + }; th.addEventListener("click", function () { - const column = { - toBeSorted: [], - span: {}, - spanSum: {}, - }; + column.toBeSorted = []; + column.span = {}; + column.spanSum = {}; + getColSpanData(table.headers[headerIndex], column); table.visibleRows = Array.prototype.filter.call( - table.body.querySelectorAll("tr"), + table.bodies.item(headerIndex).querySelectorAll("tr"), (tr) => { return tr.style.display !== "none"; } ); - getColSpanData(table.headers, column); - - const isRememberSort = sortableTable.classList.contains("remember-sort"); - if (!isRememberSort) { - timesClickedColumn = rememberSort(); + if (!table.hasClass.rememberSort) { + timesClickedColumn = rememberSort( + columnIndexesClicked, + timesClickedColumn, + columnIndex + ); } timesClickedColumn += 1; @@ -501,13 +568,13 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { }; if (hasThClass.dataSort) { - sortDataAttributes(table.visibleRows, column); + sortDataAttributes(table, column); } if (hasThClass.fileSize) { - sortFileSize(table.visibleRows, column); + sortFileSize(table, column, columnIndex, fillValue); } if (hasThClass.runtime) { - sortByRuntime(table.visibleRows, column); + sortByRuntime(table, column); } const isSortDates = { @@ -517,20 +584,27 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { }; // pick mdy first to override the inferred default class which is dmy. if (isSortDates.monthDayYear) { - sortDates("mdy", table.visibleRows, column); + sortDates("mdy", table, column); } else if (isSortDates.yearMonthDay) { - sortDates("ymd", table.visibleRows, column); + sortDates("ymd", table, column); } else if (isSortDates.dayMonthYear) { - sortDates("dmy", table.visibleRows, column); + sortDates("dmy", table, column); } const tableProperties = { + table, tableRows: table.visibleRows, + fillValue, column, + columnIndex, + th, hasThClass, isSortDates, + desc, + timesClickedColumn, + arrow, }; - getTableData(tableProperties); + timesClickedColumn = getTableData(tableProperties, timesClickedColumn); updateTable(tableProperties); }); diff --git a/browser-extensions/firefox/manifest.json b/browser-extensions/firefox/manifest.json index 5dbff19..5efa850 100644 --- a/browser-extensions/firefox/manifest.json +++ b/browser-extensions/firefox/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "author": "Lee Wannacott", "name": "table-sort-js", - "version": "1.16.0", + "version": "1.17.0", "description": "Makes tables sortable using table-sort-js: https://github.com/LeeWannacott/table-sort-js", "icons": { "48": "icons/t.png" }, "browser_action": { diff --git a/browser-extensions/firefox/table-sort-js.zip b/browser-extensions/firefox/table-sort-js.zip index fc637c64e2a765fecffa7395cb7dda730fab1c9a..91c195dc2eb5f299e96cbc064181e788a6ab42e1 100644 GIT binary patch delta 5035 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 B#4P{- delta 4732 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 1) { + // Why index 1?; I don't remember return sortableTable.querySelectorAll("tbody")[1]; } else { - return sortableTable.querySelector("tbody"); + return sortableTable.querySelectorAll("tbody"); } } else { - return sortableTable.querySelector("tbody"); + // if or exists below the browser will make + return sortableTable.querySelectorAll("tbody"); } } @@ -108,389 +109,455 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { function makeTableSortable(sortableTable) { const table = { - body: getTableBody(sortableTable), - head: sortableTable.querySelector("thead"), + bodies: getTableBodies(sortableTable), + theads: sortableTable.querySelectorAll("thead"), + rows: [], + headers: [], }; - table.headers = table.head.querySelectorAll("th"); - table.rows = table.body.querySelectorAll("tr"); - - let columnIndexesClicked = []; + for (let index of table.bodies.keys()) { + if (table.bodies.item(index) == null) { + return; + } + table.headers.push(table.theads.item(index).querySelectorAll("th")); + table.rows.push(table.bodies.item(index).querySelectorAll("tr")); + } - const isNoSortClassInference = - sortableTable.classList.contains("no-class-infer"); + table.hasClass = { + noClassInfer: sortableTable.classList.contains("no-class-infer"), + cellsSort: sortableTable.classList.contains("cells-sort"), + tableArrows: sortableTable.classList.contains("table-arrows"), + rememberSort: sortableTable.classList.contains("remember-sort"), + }; - for (let [columnIndex, th] of table.headers.entries()) { - if (!th.classList.contains("disable-sort")) { - th.style.cursor = "pointer"; - if (!isNoSortClassInference) { - inferSortClasses(table.rows, columnIndex, th); + for ( + let headerIndex = 0; + headerIndex < table.theads.length; + headerIndex++ + ) { + let columnIndexesClicked = []; + for (let [columnIndex, th] of table.headers[headerIndex].entries()) { + if (!th.classList.contains("disable-sort")) { + th.style.cursor = "pointer"; + if (!table.hasClass.noClassInfer) { + inferSortClasses(table.rows[headerIndex], columnIndex, th); + } + makeEachColumnSortable( + th, + headerIndex, + columnIndex, + table, + columnIndexesClicked + ); } - makeEachColumnSortable( - th, - columnIndex, - table, - sortableTable, - columnIndexesClicked - ); } } } - function makeEachColumnSortable( - th, - columnIndex, - table, - sortableTable, - columnIndexesClicked - ) { - const desc = th.classList.contains("order-by-desc"); - const tableArrows = sortableTable.classList.contains("table-arrows"); - const [arrowUp, arrowDown] = [" ▲", " ▼"]; - const fillValue = "!X!Y!Z!"; - - if (desc && tableArrows) { - th.insertAdjacentText("beforeend", arrowDown); - } else if (tableArrows) { - th.insertAdjacentText("beforeend", arrowUp); + function cellsOrRows(table, tr) { + if (table.hasClass.cellsSort) { + return tr.innerHTML; + } else { + return tr.outerHTML; } + } - function sortDataAttributes(tableRows, column) { - for (let [i, tr] of tableRows.entries()) { - let dataAttributeTd = getColumn(tr, column.spanSum, column.span).dataset - .sort; - column.toBeSorted.push(`${dataAttributeTd}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; - } + function sortDataAttributes(table, column) { + for (let [i, tr] of table.visibleRows.entries()) { + let dataAttributeTd = column.getColumn(tr, column.spanSum, column.span) + .dataset.sort; + column.toBeSorted.push(`${dataAttributeTd}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } - function sortFileSize(tableRows, column) { - let unitToMultiplier = { - b: 1, - kb: 1000, - kib: 2 ** 10, - mb: 1e6, - mib: 2 ** 20, - gb: 1e9, - gib: 2 ** 30, - tb: 1e12, - tib: 2 ** 40, - }; - const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i; - for (let [i, tr] of tableRows.entries()) { - let fileSizeTd = tr - .querySelectorAll("td") - .item(columnIndex).textContent; - let match = fileSizeTd.match(numberWithUnitType); - if (match) { - let number = parseFloat(match[1]); - let unit = match[2].toLowerCase(); - let multiplier = unitToMultiplier[unit]; - column.toBeSorted.push(`${number * multiplier}#${i}`); - } else { - column.toBeSorted.push(`${fillValue}#${i}`); - } + function sortFileSize(table, column, columnIndex) { + let unitToMultiplier = { + b: 1, + kb: 1000, + kib: 2 ** 10, + mb: 1e6, + mib: 2 ** 20, + gb: 1e9, + gib: 2 ** 30, + tb: 1e12, + tib: 2 ** 40, + }; + const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i; + for (let [i, tr] of table.visibleRows.entries()) { + let fileSizeTd = tr.querySelectorAll("td").item(columnIndex).textContent; + let match = fileSizeTd.match(numberWithUnitType); + if (match) { + let number = parseFloat(match[1]); + let unit = match[2].toLowerCase(); + let multiplier = unitToMultiplier[unit]; + column.toBeSorted.push(`${number * multiplier}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } } + } - function sortByRuntime(tableRows, column) { - try { - for (let [i, tr] of tableRows.entries()) { - const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i; - let columnOfTd = ""; - // TODO: github actions runtime didn't like textContent, tests didn't like innerText? - if (testingTableSortJS) { - columnOfTd = getColumn(tr, column.spanSum, column.span).textContent; - } else { - columnOfTd = getColumn(tr, column.spanSum, column.span).innerText; - } - let match = columnOfTd.match(regexMinutesAndSeconds); - let [minutesInSeconds, hours, seconds] = [0, 0, 0]; - let timeinSeconds = columnOfTd; - if (match) { - const regexHours = match[1]; - if (regexHours) { - hours = Number(regexHours.replace("h", "")) * 60 * 60; - } - const regexMinutes = match[2]; - if (regexMinutes) { - minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60; - } - const regexSeconds = match[3]; - if (regexSeconds) { - seconds = Number(regexSeconds.replace("s", "")); + function sortDates(datesFormat, table, column) { + try { + for (let [i, tr] of table.visibleRows.entries()) { + let columnOfTd, datesRegex; + if (datesFormat === "mdy" || datesFormat === "dmy") { + datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/; + } else if (datesFormat === "ymd") { + datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/; + } + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + let match = columnOfTd.match(datesRegex); + let [years, days, months] = [0, 0, 0]; + let numberToSort = columnOfTd; + if (match) { + const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]]; + if (regPos1 && regPos2 && regPos3) { + if (datesFormat === "mdy") { + [months, days, years] = [regPos1, regPos2, regPos3]; + } else if (datesFormat === "ymd") { + [years, months, days] = [regPos1, regPos2, regPos3]; + } else { + [days, months, years] = [regPos1, regPos2, regPos3]; } - timeinSeconds = hours + minutesInSeconds + seconds; } - column.toBeSorted.push(`${timeinSeconds}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; + numberToSort = Number( + years + + String(months).padStart(2, "0") + + String(days).padStart(2, "0") + ); } - } catch (e) { - console.log(e); + column.toBeSorted.push(`${numberToSort}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } catch (e) { + console.log(e); } + } - function sortDates(datesFormat, tableRows, column) { - try { - for (let [i, tr] of tableRows.entries()) { - let columnOfTd, datesRegex; - if (datesFormat === "mdy" || datesFormat === "dmy") { - datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/; - } else if (datesFormat === "ymd") { - datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/; + function sortByRuntime(table, column) { + try { + for (let [i, tr] of table.visibleRows.entries()) { + const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i; + let columnOfTd = ""; + // TODO: github actions runtime didn't like textContent, tests didn't like innerText? + if (testingTableSortJS) { + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + } else { + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).innerText; + } + let match = columnOfTd.match(regexMinutesAndSeconds); + let [minutesInSeconds, hours, seconds] = [0, 0, 0]; + let timeinSeconds = columnOfTd; + if (match) { + const regexHours = match[1]; + if (regexHours) { + hours = Number(regexHours.replace("h", "")) * 60 * 60; } - columnOfTd = getColumn(tr, column.spanSum, column.span).textContent; - let match = columnOfTd.match(datesRegex); - let [years, days, months] = [0, 0, 0]; - let numberToSort = columnOfTd; - if (match) { - const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]]; - if (regPos1 && regPos2 && regPos3) { - if (datesFormat === "mdy") { - [months, days, years] = [regPos1, regPos2, regPos3]; - } else if (datesFormat === "ymd") { - [years, months, days] = [regPos1, regPos2, regPos3]; - } else { - [days, months, years] = [regPos1, regPos2, regPos3]; - } - } - numberToSort = Number( - years + - String(months).padStart(2, "0") + - String(days).padStart(2, "0") - ); + const regexMinutes = match[2]; + if (regexMinutes) { + minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60; + } + const regexSeconds = match[3]; + if (regexSeconds) { + seconds = Number(regexSeconds.replace("s", "")); } - column.toBeSorted.push(`${numberToSort}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; + timeinSeconds = hours + minutesInSeconds + seconds; } - } catch (e) { - console.log(e); + column.toBeSorted.push(`${timeinSeconds}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } catch (e) { + console.log(e); } + } - function rememberSort() { - // if user clicked different column from first column reset times clicked. - columnIndexesClicked.push(columnIndex); - if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) { - const lastColumnClicked = - columnIndexesClicked[columnIndexesClicked.length - 1]; - const secondLastColumnClicked = - columnIndexesClicked[columnIndexesClicked.length - 2]; - if (lastColumnClicked !== secondLastColumnClicked) { - columnIndexesClicked.shift(); - timesClickedColumn = 0; + function getTableData(tableProperties, timesClickedColumn) { + const { + table, + tableRows, + fillValue, + column, + th, + hasThClass, + isSortDates, + desc, + arrow, + } = tableProperties; + for (let [i, tr] of tableRows.entries()) { + let tdTextContent = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + if (tdTextContent.length === 0) { + tdTextContent = ""; + } + if (tdTextContent.trim() !== "") { + if ( + !hasThClass.fileSize && + !hasThClass.dataSort && + !hasThClass.runtime && + !hasThClass.filesize && + !isSortDates.dayMonthYear && + !isSortDates.yearMonthDay && + !isSortDates.monthDayYear + ) { + column.toBeSorted.push(`${tdTextContent}#${i}`); + columnIndexAndTableRow[`${tdTextContent}#${i}`] = cellsOrRows( + table, + tr + ); } + } else { + // Fill in blank table cells dict key with filler value. + column.toBeSorted.push(`${fillValue}#${i}`); + columnIndexAndTableRow[`${fillValue}#${i}`] = cellsOrRows(table, tr); } - return timesClickedColumn; } - function getColSpanData(headers, column) { - headers.forEach((th, index) => { - column.span[index] = th.colSpan; - if (index === 0) column.spanSum[index] = th.colSpan; - else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan; - }); - } + const isPunctSort = th.classList.contains("punct-sort"); + const isAlphaSort = th.classList.contains("alpha-sort"); + const isNumericSort = th.classList.contains("numeric-sort"); - function getColumn(tr, colSpanSum, colSpanData) { - return tr - .querySelectorAll("td") - .item( - colSpanData[columnIndex] === 1 - ? colSpanSum[columnIndex] - 1 - : colSpanSum[columnIndex] - colSpanData[columnIndex] - ); + function parseNumberFromString(str) { + let num; + str = str.slice(0, str.indexOf("#")); + if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) { + num = -1 * Number(str.slice(1, -1)); + } else { + num = Number(str); + } + return num; } - function getTableData(tableProperties) { - const { tableRows, column, hasThClass, isSortDates } = tableProperties; - for (let [i, tr] of tableRows.entries()) { - let tdTextContent = getColumn( - tr, - column.spanSum, - column.span - ).textContent; - if (tdTextContent.length === 0) { - tdTextContent = ""; - } - if (tdTextContent.trim() !== "") { - if (hasThClass.fileSize) { - fileSizeColumnTextAndRow[column.toBeSorted[i]] = tr.outerHTML; - } - // These classes already handle pushing to column and setting the tr html. - if ( - !hasThClass.fileSize && - !hasThClass.dataSort && - !hasThClass.runtime && - !isSortDates.dayMonthYear && - !isSortDates.yearMonthDay && - !isSortDates.monthDayYear - ) { - column.toBeSorted.push(`${tdTextContent}#${i}`); - columnIndexAndTableRow[`${tdTextContent}#${i}`] = tr.outerHTML; - } - } else { - // Fill in blank table cells dict key with filler value. - column.toBeSorted.push(`${fillValue}#${i}`); - columnIndexAndTableRow[`${fillValue}#${i}`] = tr.outerHTML; - } - } + function strLocaleCompare(str1, str2) { + return str1.localeCompare( + str2, + navigator.languages[0] || navigator.language, + { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort } + ); + } - const isPunctSort = th.classList.contains("punct-sort"); - const isAlphaSort = th.classList.contains("alpha-sort"); - const isNumericSort = th.classList.contains("numeric-sort"); + function handleNumbers(str1, str2) { + let num1, num2; + num1 = parseNumberFromString(str1); + num2 = parseNumberFromString(str2); - function parseNumberFromString(str) { - let num; - str = str.slice(0, str.indexOf("#")); - if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) { - num = -1 * Number(str.slice(1, -1)); - } else { - num = Number(str); - } - return num; + if (!isNaN(num1) && !isNaN(num2)) { + return num1 - num2; + } else { + return strLocaleCompare(str1, str2); } + } - function strLocaleCompare(str1, str2) { - return str1.localeCompare( - str2, - navigator.languages[0] || navigator.language, - { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort } - ); + function sortAscending(a, b) { + if (a.includes(`${fillValue}#`)) { + return 1; + } else if (b.includes(`${fillValue}#`)) { + return -1; + } else if (isNumericSort) { + return handleNumbers(a, b); + } else { + return strLocaleCompare(a, b); } + } - function handleNumbers(str1, str2) { - let num1, num2; - num1 = parseNumberFromString(str1); - num2 = parseNumberFromString(str2); + function sortDescending(a, b) { + return sortAscending(b, a); + } - if (!isNaN(num1) && !isNaN(num2)) { - return num1 - num2; - } else { - return strLocaleCompare(str1, str2); - } - } + function clearArrows(arrowUp = "▲", arrowDown = "▼") { + th.innerHTML = th.innerHTML.replace(arrowUp, ""); + th.innerHTML = th.innerHTML.replace(arrowDown, ""); + } - function sortAscending(a, b) { - if (a.includes(`${fillValue}#`)) { - return 1; - } else if (b.includes(`${fillValue}#`)) { - return -1; - } else if (isNumericSort) { - return handleNumbers(a, b); - } else { - return strLocaleCompare(a, b); - } - } + if (column.toBeSorted[0] === undefined) { + return; + } - function sortDescending(a, b) { - return sortAscending(b, a); + function changeTableArrow(arrowDirection) { + if (table.hasClass.tableArrows) { + clearArrows(arrow.up, arrow.down); + th.insertAdjacentText("beforeend", arrowDirection); } + } - function clearArrows(arrowUp = "▲", arrowDown = "▼") { - th.innerHTML = th.innerHTML.replace(arrowUp, ""); - th.innerHTML = th.innerHTML.replace(arrowDown, ""); - } + function sortColumn(sortDirection) { + column.toBeSorted.sort(sortDirection, { + numeric: !isAlphaSort, + ignorePunctuation: !isPunctSort, + }); + } - if (column.toBeSorted[0] === undefined) { - return; + if (timesClickedColumn === 1) { + if (desc) { + changeTableArrow(arrow.down); + sortColumn(sortDescending); + } else { + changeTableArrow(arrow.up); + sortColumn(sortAscending); } - - function changeTableArrow(arrowDirection) { - if (tableArrows) { - clearArrows(arrowUp, arrowDown); - th.insertAdjacentText("beforeend", arrowDirection); - } + } else if (timesClickedColumn === 2) { + timesClickedColumn = 0; + if (desc) { + changeTableArrow(arrow.up); + sortColumn(sortAscending); + } else { + changeTableArrow(arrow.down); + sortColumn(sortDescending); } + } + return timesClickedColumn; + } - function sortColumn(sortDirection) { - column.toBeSorted.sort(sortDirection, { - numeric: !isAlphaSort, - ignorePunctuation: !isPunctSort, - }); + function updateFilesize(i, table, tr, column, columnIndex) { + if (table.hasClass.cellsSort) { + tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; + } else { + // We do this to sort rows rather than cells: + const template = document.createElement("template"); + template.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; + tr = template.content.firstChild; + } + let fileSizeInBytesHTML = column.getColumn( + tr, + column.spanSum, + column.span + ).outerHTML; + const fileSizeInBytesText = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + + const fileSize = column.toBeSorted[i].replace(/#[0-9]*/, ""); + let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"]; + let replaced = false; + for (let i = 0; i < prefixes.length; ++i) { + let nextPrefixMultiplier = 2 ** (10 * (i + 1)); + if (fileSize < nextPrefixMultiplier) { + let prefixMultiplier = 2 ** (10 * i); + fileSizeInBytesHTML = fileSizeInBytesHTML.replace( + fileSizeInBytesText, + `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B` + ); + replaced = true; + break; } + } + if (!replaced) { + fileSizeInBytesHTML = fileSizeInBytesHTML.replace( + fileSizeInBytesText, + "NaN" + ); + } + tr.querySelectorAll("td").item(columnIndex).innerHTML = fileSizeInBytesHTML; + return table.hasClass.cellsSort ? tr.innerHTML : tr.outerHTML; + } - if (timesClickedColumn === 1) { - if (desc) { - changeTableArrow(arrowDown); - sortColumn(sortDescending); + function updateTable(tableProperties) { + const { column, table, columnIndex, hasThClass } = tableProperties; + for (let [i, tr] of table.visibleRows.entries()) { + if (hasThClass.fileSize) { + if (table.hasClass.cellsSort) { + tr.innerHTML = updateFilesize(i, table, tr, column, columnIndex); } else { - changeTableArrow(arrowUp); - sortColumn(sortAscending); + tr.outerHTML = updateFilesize(i, table, tr, column, columnIndex); } - } else if (timesClickedColumn === 2) { - timesClickedColumn = 0; - if (desc) { - changeTableArrow(arrowUp); - sortColumn(sortAscending); + } else if (!hasThClass.fileSize) { + if (table.hasClass.cellsSort) { + tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; } else { - changeTableArrow(arrowDown); - sortColumn(sortDescending); + tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; } } } + } - function updateTable(tableProperties) { - const { tableRows, column, hasThClass } = tableProperties; - for (let [i, tr] of tableRows.entries()) { - if (hasThClass.fileSize) { - tr.innerHTML = fileSizeColumnTextAndRow[column.toBeSorted[i]]; - let fileSizeInBytesHTML = tr - .querySelectorAll("td") - .item(columnIndex).innerHTML; - const fileSizeInBytesText = tr - .querySelectorAll("td") - .item(columnIndex).textContent; - // Remove the unique identifyer for duplicate values(#number). - column.toBeSorted[i] = column.toBeSorted[i].replace(/#[0-9]*/, ""); - const fileSize = parseFloat(column.toBeSorted[i]); - let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"]; - let replaced = false; - for (let i = 0; i < prefixes.length; ++i) { - let nextPrefixMultiplier = 2 ** (10 * (i + 1)); - if (fileSize < nextPrefixMultiplier) { - let prefixMultiplier = 2 ** (10 * i); - fileSizeInBytesHTML = fileSizeInBytesHTML.replace( - fileSizeInBytesText, - `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B` - ); - replaced = true; - break; - } - } - if (!replaced) { - fileSizeInBytesHTML = fileSizeInBytesHTML.replace( - fileSizeInBytesText, - "NaN" - ); - } - tr.querySelectorAll("td").item(columnIndex).innerHTML = - fileSizeInBytesHTML; - } else if (!hasThClass.fileSize) { - tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; - } + function getColSpanData(headers, column) { + headers.forEach((th, index) => { + column.span[index] = th.colSpan; + if (index === 0) column.spanSum[index] = th.colSpan; + else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan; + }); + } + + function rememberSort(columnIndexesClicked, timesClickedColumn, columnIndex) { + // if user clicked different column from first column reset times clicked. + columnIndexesClicked.push(columnIndex); + if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) { + const lastColumnClicked = + columnIndexesClicked[columnIndexesClicked.length - 1]; + const secondLastColumnClicked = + columnIndexesClicked[columnIndexesClicked.length - 2]; + if (lastColumnClicked !== secondLastColumnClicked) { + columnIndexesClicked.shift(); + timesClickedColumn = 0; } } + return timesClickedColumn; + } + + function makeEachColumnSortable( + th, + headerIndex, + columnIndex, + table, + columnIndexesClicked + ) { + const desc = th.classList.contains("order-by-desc"); + const arrow = { up: " ▲", down: " ▼" }; + const fillValue = "!X!Y!Z!"; + + if (desc && table.hasClass.tableArrows) { + th.insertAdjacentText("beforeend", arrow.down); + } else if (table.hasClass.tableArrows) { + th.insertAdjacentText("beforeend", arrow.up); + } let timesClickedColumn = 0; + const column = { + getColumn: function getColumn(tr, colSpanSum, colSpanData) { + return tr + .querySelectorAll("td") + .item( + colSpanData[columnIndex] === 1 + ? colSpanSum[columnIndex] - 1 + : colSpanSum[columnIndex] - colSpanData[columnIndex] + ); + }, + }; th.addEventListener("click", function () { - const column = { - toBeSorted: [], - span: {}, - spanSum: {}, - }; + column.toBeSorted = []; + column.span = {}; + column.spanSum = {}; + getColSpanData(table.headers[headerIndex], column); table.visibleRows = Array.prototype.filter.call( - table.body.querySelectorAll("tr"), + table.bodies.item(headerIndex).querySelectorAll("tr"), (tr) => { return tr.style.display !== "none"; } ); - getColSpanData(table.headers, column); - - const isRememberSort = sortableTable.classList.contains("remember-sort"); - if (!isRememberSort) { - timesClickedColumn = rememberSort(); + if (!table.hasClass.rememberSort) { + timesClickedColumn = rememberSort( + columnIndexesClicked, + timesClickedColumn, + columnIndex + ); } timesClickedColumn += 1; @@ -501,13 +568,13 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { }; if (hasThClass.dataSort) { - sortDataAttributes(table.visibleRows, column); + sortDataAttributes(table, column); } if (hasThClass.fileSize) { - sortFileSize(table.visibleRows, column); + sortFileSize(table, column, columnIndex, fillValue); } if (hasThClass.runtime) { - sortByRuntime(table.visibleRows, column); + sortByRuntime(table, column); } const isSortDates = { @@ -517,20 +584,27 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { }; // pick mdy first to override the inferred default class which is dmy. if (isSortDates.monthDayYear) { - sortDates("mdy", table.visibleRows, column); + sortDates("mdy", table, column); } else if (isSortDates.yearMonthDay) { - sortDates("ymd", table.visibleRows, column); + sortDates("ymd", table, column); } else if (isSortDates.dayMonthYear) { - sortDates("dmy", table.visibleRows, column); + sortDates("dmy", table, column); } const tableProperties = { + table, tableRows: table.visibleRows, + fillValue, column, + columnIndex, + th, hasThClass, isSortDates, + desc, + timesClickedColumn, + arrow, }; - getTableData(tableProperties); + timesClickedColumn = getTableData(tableProperties, timesClickedColumn); updateTable(tableProperties); }); diff --git a/npm/README.md b/npm/README.md index 6e0d26f..37ca0b9 100644 --- a/npm/README.md +++ b/npm/README.md @@ -3,6 +3,7 @@ [![jsDeliver downloads](https://data.jsdelivr.com/v1/package/npm/table-sort-js/badge)](https://www.jsdelivr.com/package/npm/table-sort-js) ![repo size](https://img.shields.io/github/repo-size/leewannacott/table-sort-js) ![MIT licence](https://img.shields.io/github/license/LeeWannacott/table-sort-js) +[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) ![build status](https://img.shields.io/github/actions/workflow/status/leewannacott/table-sort-js/jest.yml?branch=master) # TABLE-SORT-JS. @@ -17,39 +18,31 @@ ## Install instructions. -- Option 1: Install from npm: +- Option 1: Load as script from a Content Delivery Network (CDN): ```javascript -npm install table-sort-js + ``` +Or Minified (smaller size, but harder to debug!): + ```javascript -import tableSort from "table-sort-js/table-sort.js"; + ``` -Examples on using table-sort-js with frontend frameworks such as [React.js](https://leewannacott.github.io/table-sort-js/docs/react.html) and [Vue.js](https://leewannacott.github.io/table-sort-js/docs/vue.html) +Example on how to use table-sort-js with [HTML](https://leewannacott.github.io/table-sort-js/docs/html5.html) -- Option 2: Load as script from a Content Delivery Network (CDN): +- Option 2: Install from npm: ```javascript - +npm install table-sort-js ``` -Or Minified (smaller size, but harder to debug!): - ```javascript - +import tableSort from "table-sort-js/table-sort.js"; ``` -Refer to the documenation for examples on how to use table-sort-js with [HTML](https://leewannacott.github.io/table-sort-js/docs/html5.html) - -- Option 3: Download [table-sort.js](https://cdn.jsdelivr.net/npm/table-sort-js@latest/table-sort.js) (Select save as.), or download a [minified version](https://cdn.jsdelivr.net/npm/table-sort-js@latest/table-sort.min.js) (~5kB) - -Then rename and add the following script before your HTML table: - -```html - -``` +Examples on using table-sort-js with frontend frameworks such as [React.js](https://leewannacott.github.io/table-sort-js/docs/react.html) and [Vue.js](https://leewannacott.github.io/table-sort-js/docs/vue.html) ## To make tables sortable: @@ -64,6 +57,7 @@ Then rename and add the following script before your HTML table: | "no-class-infer" | Turns off inference for adding sort classes automatically i.e (file-size-sort, runtime-sort, dates-dmy-sort). | | "table-arrows" | Display ascending or descending triangles. | | "remember-sort" | If clicking on different columns remembers sort of the original column. | +| "cells-sort" | sort cells (td) rather than table rows (tr); useful for keeping table rows with classes/attributes in place. |
diff --git a/npm/package.json b/npm/package.json index 4c8819d..4c9ce6b 100644 --- a/npm/package.json +++ b/npm/package.json @@ -1,6 +1,6 @@ { "name": "table-sort-js", - "version": "1.16.0", + "version": "1.17.0", "description": "A JavaScript client-side HTML table sorting library with no dependencies required.", "license": "MIT", "repository": "LeeWannacott/table-sort-js", diff --git a/npm/table-sort.js b/npm/table-sort.js index 4b19ea0..a9a7e31 100644 --- a/npm/table-sort.js +++ b/npm/table-sort.js @@ -28,7 +28,6 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { const [getTagTable] = getHTMLTables(); const columnIndexAndTableRow = {}; - const fileSizeColumnTextAndRow = {}; for (let table of getTagTable) { if (table.classList.contains("table-sort")) { makeTableSortable(table); @@ -46,16 +45,18 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { sortableTable.insertBefore(createTableHead, sortableTable.firstChild); } - function getTableBody(sortableTable) { + function getTableBodies(sortableTable) { if (sortableTable.getElementsByTagName("thead").length === 0) { createMissingTableHead(sortableTable); if (sortableTable.querySelectorAll("tbody").length > 1) { + // Why index 1?; I don't remember return sortableTable.querySelectorAll("tbody")[1]; } else { - return sortableTable.querySelector("tbody"); + return sortableTable.querySelectorAll("tbody"); } } else { - return sortableTable.querySelector("tbody"); + // if or exists below the browser will make + return sortableTable.querySelectorAll("tbody"); } } @@ -108,389 +109,455 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { function makeTableSortable(sortableTable) { const table = { - body: getTableBody(sortableTable), - head: sortableTable.querySelector("thead"), + bodies: getTableBodies(sortableTable), + theads: sortableTable.querySelectorAll("thead"), + rows: [], + headers: [], }; - table.headers = table.head.querySelectorAll("th"); - table.rows = table.body.querySelectorAll("tr"); - - let columnIndexesClicked = []; + for (let index of table.bodies.keys()) { + if (table.bodies.item(index) == null) { + return; + } + table.headers.push(table.theads.item(index).querySelectorAll("th")); + table.rows.push(table.bodies.item(index).querySelectorAll("tr")); + } - const isNoSortClassInference = - sortableTable.classList.contains("no-class-infer"); + table.hasClass = { + noClassInfer: sortableTable.classList.contains("no-class-infer"), + cellsSort: sortableTable.classList.contains("cells-sort"), + tableArrows: sortableTable.classList.contains("table-arrows"), + rememberSort: sortableTable.classList.contains("remember-sort"), + }; - for (let [columnIndex, th] of table.headers.entries()) { - if (!th.classList.contains("disable-sort")) { - th.style.cursor = "pointer"; - if (!isNoSortClassInference) { - inferSortClasses(table.rows, columnIndex, th); + for ( + let headerIndex = 0; + headerIndex < table.theads.length; + headerIndex++ + ) { + let columnIndexesClicked = []; + for (let [columnIndex, th] of table.headers[headerIndex].entries()) { + if (!th.classList.contains("disable-sort")) { + th.style.cursor = "pointer"; + if (!table.hasClass.noClassInfer) { + inferSortClasses(table.rows[headerIndex], columnIndex, th); + } + makeEachColumnSortable( + th, + headerIndex, + columnIndex, + table, + columnIndexesClicked + ); } - makeEachColumnSortable( - th, - columnIndex, - table, - sortableTable, - columnIndexesClicked - ); } } } - function makeEachColumnSortable( - th, - columnIndex, - table, - sortableTable, - columnIndexesClicked - ) { - const desc = th.classList.contains("order-by-desc"); - const tableArrows = sortableTable.classList.contains("table-arrows"); - const [arrowUp, arrowDown] = [" ▲", " ▼"]; - const fillValue = "!X!Y!Z!"; - - if (desc && tableArrows) { - th.insertAdjacentText("beforeend", arrowDown); - } else if (tableArrows) { - th.insertAdjacentText("beforeend", arrowUp); + function cellsOrRows(table, tr) { + if (table.hasClass.cellsSort) { + return tr.innerHTML; + } else { + return tr.outerHTML; } + } - function sortDataAttributes(tableRows, column) { - for (let [i, tr] of tableRows.entries()) { - let dataAttributeTd = getColumn(tr, column.spanSum, column.span).dataset - .sort; - column.toBeSorted.push(`${dataAttributeTd}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; - } + function sortDataAttributes(table, column) { + for (let [i, tr] of table.visibleRows.entries()) { + let dataAttributeTd = column.getColumn(tr, column.spanSum, column.span) + .dataset.sort; + column.toBeSorted.push(`${dataAttributeTd}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } - function sortFileSize(tableRows, column) { - let unitToMultiplier = { - b: 1, - kb: 1000, - kib: 2 ** 10, - mb: 1e6, - mib: 2 ** 20, - gb: 1e9, - gib: 2 ** 30, - tb: 1e12, - tib: 2 ** 40, - }; - const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i; - for (let [i, tr] of tableRows.entries()) { - let fileSizeTd = tr - .querySelectorAll("td") - .item(columnIndex).textContent; - let match = fileSizeTd.match(numberWithUnitType); - if (match) { - let number = parseFloat(match[1]); - let unit = match[2].toLowerCase(); - let multiplier = unitToMultiplier[unit]; - column.toBeSorted.push(`${number * multiplier}#${i}`); - } else { - column.toBeSorted.push(`${fillValue}#${i}`); - } + function sortFileSize(table, column, columnIndex) { + let unitToMultiplier = { + b: 1, + kb: 1000, + kib: 2 ** 10, + mb: 1e6, + mib: 2 ** 20, + gb: 1e9, + gib: 2 ** 30, + tb: 1e12, + tib: 2 ** 40, + }; + const numberWithUnitType = /([.0-9]+)\s?(B|KB|KiB|MB|MiB|GB|GiB|TB|TiB)/i; + for (let [i, tr] of table.visibleRows.entries()) { + let fileSizeTd = tr.querySelectorAll("td").item(columnIndex).textContent; + let match = fileSizeTd.match(numberWithUnitType); + if (match) { + let number = parseFloat(match[1]); + let unit = match[2].toLowerCase(); + let multiplier = unitToMultiplier[unit]; + column.toBeSorted.push(`${number * multiplier}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } } + } - function sortByRuntime(tableRows, column) { - try { - for (let [i, tr] of tableRows.entries()) { - const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i; - let columnOfTd = ""; - // TODO: github actions runtime didn't like textContent, tests didn't like innerText? - if (testingTableSortJS) { - columnOfTd = getColumn(tr, column.spanSum, column.span).textContent; - } else { - columnOfTd = getColumn(tr, column.spanSum, column.span).innerText; - } - let match = columnOfTd.match(regexMinutesAndSeconds); - let [minutesInSeconds, hours, seconds] = [0, 0, 0]; - let timeinSeconds = columnOfTd; - if (match) { - const regexHours = match[1]; - if (regexHours) { - hours = Number(regexHours.replace("h", "")) * 60 * 60; - } - const regexMinutes = match[2]; - if (regexMinutes) { - minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60; - } - const regexSeconds = match[3]; - if (regexSeconds) { - seconds = Number(regexSeconds.replace("s", "")); + function sortDates(datesFormat, table, column) { + try { + for (let [i, tr] of table.visibleRows.entries()) { + let columnOfTd, datesRegex; + if (datesFormat === "mdy" || datesFormat === "dmy") { + datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/; + } else if (datesFormat === "ymd") { + datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/; + } + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + let match = columnOfTd.match(datesRegex); + let [years, days, months] = [0, 0, 0]; + let numberToSort = columnOfTd; + if (match) { + const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]]; + if (regPos1 && regPos2 && regPos3) { + if (datesFormat === "mdy") { + [months, days, years] = [regPos1, regPos2, regPos3]; + } else if (datesFormat === "ymd") { + [years, months, days] = [regPos1, regPos2, regPos3]; + } else { + [days, months, years] = [regPos1, regPos2, regPos3]; } - timeinSeconds = hours + minutesInSeconds + seconds; } - column.toBeSorted.push(`${timeinSeconds}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; + numberToSort = Number( + years + + String(months).padStart(2, "0") + + String(days).padStart(2, "0") + ); } - } catch (e) { - console.log(e); + column.toBeSorted.push(`${numberToSort}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } catch (e) { + console.log(e); } + } - function sortDates(datesFormat, tableRows, column) { - try { - for (let [i, tr] of tableRows.entries()) { - let columnOfTd, datesRegex; - if (datesFormat === "mdy" || datesFormat === "dmy") { - datesRegex = /^(\d\d?)[./-](\d\d?)[./-]((\d\d)?\d\d)/; - } else if (datesFormat === "ymd") { - datesRegex = /^(\d\d\d\d)[./-](\d\d?)[./-](\d\d?)/; + function sortByRuntime(table, column) { + try { + for (let [i, tr] of table.visibleRows.entries()) { + const regexMinutesAndSeconds = /^(\d+h)?\s?(\d+m)?\s?(\d+s)?$/i; + let columnOfTd = ""; + // TODO: github actions runtime didn't like textContent, tests didn't like innerText? + if (testingTableSortJS) { + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + } else { + columnOfTd = column.getColumn( + tr, + column.spanSum, + column.span + ).innerText; + } + let match = columnOfTd.match(regexMinutesAndSeconds); + let [minutesInSeconds, hours, seconds] = [0, 0, 0]; + let timeinSeconds = columnOfTd; + if (match) { + const regexHours = match[1]; + if (regexHours) { + hours = Number(regexHours.replace("h", "")) * 60 * 60; } - columnOfTd = getColumn(tr, column.spanSum, column.span).textContent; - let match = columnOfTd.match(datesRegex); - let [years, days, months] = [0, 0, 0]; - let numberToSort = columnOfTd; - if (match) { - const [regPos1, regPos2, regPos3] = [match[1], match[2], match[3]]; - if (regPos1 && regPos2 && regPos3) { - if (datesFormat === "mdy") { - [months, days, years] = [regPos1, regPos2, regPos3]; - } else if (datesFormat === "ymd") { - [years, months, days] = [regPos1, regPos2, regPos3]; - } else { - [days, months, years] = [regPos1, regPos2, regPos3]; - } - } - numberToSort = Number( - years + - String(months).padStart(2, "0") + - String(days).padStart(2, "0") - ); + const regexMinutes = match[2]; + if (regexMinutes) { + minutesInSeconds = Number(regexMinutes.replace("m", "")) * 60; + } + const regexSeconds = match[3]; + if (regexSeconds) { + seconds = Number(regexSeconds.replace("s", "")); } - column.toBeSorted.push(`${numberToSort}#${i}`); - columnIndexAndTableRow[column.toBeSorted[i]] = tr.outerHTML; + timeinSeconds = hours + minutesInSeconds + seconds; } - } catch (e) { - console.log(e); + column.toBeSorted.push(`${timeinSeconds}#${i}`); + columnIndexAndTableRow[column.toBeSorted[i]] = cellsOrRows(table, tr); } + } catch (e) { + console.log(e); } + } - function rememberSort() { - // if user clicked different column from first column reset times clicked. - columnIndexesClicked.push(columnIndex); - if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) { - const lastColumnClicked = - columnIndexesClicked[columnIndexesClicked.length - 1]; - const secondLastColumnClicked = - columnIndexesClicked[columnIndexesClicked.length - 2]; - if (lastColumnClicked !== secondLastColumnClicked) { - columnIndexesClicked.shift(); - timesClickedColumn = 0; + function getTableData(tableProperties, timesClickedColumn) { + const { + table, + tableRows, + fillValue, + column, + th, + hasThClass, + isSortDates, + desc, + arrow, + } = tableProperties; + for (let [i, tr] of tableRows.entries()) { + let tdTextContent = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + if (tdTextContent.length === 0) { + tdTextContent = ""; + } + if (tdTextContent.trim() !== "") { + if ( + !hasThClass.fileSize && + !hasThClass.dataSort && + !hasThClass.runtime && + !hasThClass.filesize && + !isSortDates.dayMonthYear && + !isSortDates.yearMonthDay && + !isSortDates.monthDayYear + ) { + column.toBeSorted.push(`${tdTextContent}#${i}`); + columnIndexAndTableRow[`${tdTextContent}#${i}`] = cellsOrRows( + table, + tr + ); } + } else { + // Fill in blank table cells dict key with filler value. + column.toBeSorted.push(`${fillValue}#${i}`); + columnIndexAndTableRow[`${fillValue}#${i}`] = cellsOrRows(table, tr); } - return timesClickedColumn; } - function getColSpanData(headers, column) { - headers.forEach((th, index) => { - column.span[index] = th.colSpan; - if (index === 0) column.spanSum[index] = th.colSpan; - else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan; - }); - } + const isPunctSort = th.classList.contains("punct-sort"); + const isAlphaSort = th.classList.contains("alpha-sort"); + const isNumericSort = th.classList.contains("numeric-sort"); - function getColumn(tr, colSpanSum, colSpanData) { - return tr - .querySelectorAll("td") - .item( - colSpanData[columnIndex] === 1 - ? colSpanSum[columnIndex] - 1 - : colSpanSum[columnIndex] - colSpanData[columnIndex] - ); + function parseNumberFromString(str) { + let num; + str = str.slice(0, str.indexOf("#")); + if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) { + num = -1 * Number(str.slice(1, -1)); + } else { + num = Number(str); + } + return num; } - function getTableData(tableProperties) { - const { tableRows, column, hasThClass, isSortDates } = tableProperties; - for (let [i, tr] of tableRows.entries()) { - let tdTextContent = getColumn( - tr, - column.spanSum, - column.span - ).textContent; - if (tdTextContent.length === 0) { - tdTextContent = ""; - } - if (tdTextContent.trim() !== "") { - if (hasThClass.fileSize) { - fileSizeColumnTextAndRow[column.toBeSorted[i]] = tr.outerHTML; - } - // These classes already handle pushing to column and setting the tr html. - if ( - !hasThClass.fileSize && - !hasThClass.dataSort && - !hasThClass.runtime && - !isSortDates.dayMonthYear && - !isSortDates.yearMonthDay && - !isSortDates.monthDayYear - ) { - column.toBeSorted.push(`${tdTextContent}#${i}`); - columnIndexAndTableRow[`${tdTextContent}#${i}`] = tr.outerHTML; - } - } else { - // Fill in blank table cells dict key with filler value. - column.toBeSorted.push(`${fillValue}#${i}`); - columnIndexAndTableRow[`${fillValue}#${i}`] = tr.outerHTML; - } - } + function strLocaleCompare(str1, str2) { + return str1.localeCompare( + str2, + navigator.languages[0] || navigator.language, + { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort } + ); + } - const isPunctSort = th.classList.contains("punct-sort"); - const isAlphaSort = th.classList.contains("alpha-sort"); - const isNumericSort = th.classList.contains("numeric-sort"); + function handleNumbers(str1, str2) { + let num1, num2; + num1 = parseNumberFromString(str1); + num2 = parseNumberFromString(str2); - function parseNumberFromString(str) { - let num; - str = str.slice(0, str.indexOf("#")); - if (str.match(/^\((\d+(?:\.\d+)?)\)$/)) { - num = -1 * Number(str.slice(1, -1)); - } else { - num = Number(str); - } - return num; + if (!isNaN(num1) && !isNaN(num2)) { + return num1 - num2; + } else { + return strLocaleCompare(str1, str2); } + } - function strLocaleCompare(str1, str2) { - return str1.localeCompare( - str2, - navigator.languages[0] || navigator.language, - { numeric: !isAlphaSort, ignorePunctuation: !isPunctSort } - ); + function sortAscending(a, b) { + if (a.includes(`${fillValue}#`)) { + return 1; + } else if (b.includes(`${fillValue}#`)) { + return -1; + } else if (isNumericSort) { + return handleNumbers(a, b); + } else { + return strLocaleCompare(a, b); } + } - function handleNumbers(str1, str2) { - let num1, num2; - num1 = parseNumberFromString(str1); - num2 = parseNumberFromString(str2); + function sortDescending(a, b) { + return sortAscending(b, a); + } - if (!isNaN(num1) && !isNaN(num2)) { - return num1 - num2; - } else { - return strLocaleCompare(str1, str2); - } - } + function clearArrows(arrowUp = "▲", arrowDown = "▼") { + th.innerHTML = th.innerHTML.replace(arrowUp, ""); + th.innerHTML = th.innerHTML.replace(arrowDown, ""); + } - function sortAscending(a, b) { - if (a.includes(`${fillValue}#`)) { - return 1; - } else if (b.includes(`${fillValue}#`)) { - return -1; - } else if (isNumericSort) { - return handleNumbers(a, b); - } else { - return strLocaleCompare(a, b); - } - } + if (column.toBeSorted[0] === undefined) { + return; + } - function sortDescending(a, b) { - return sortAscending(b, a); + function changeTableArrow(arrowDirection) { + if (table.hasClass.tableArrows) { + clearArrows(arrow.up, arrow.down); + th.insertAdjacentText("beforeend", arrowDirection); } + } - function clearArrows(arrowUp = "▲", arrowDown = "▼") { - th.innerHTML = th.innerHTML.replace(arrowUp, ""); - th.innerHTML = th.innerHTML.replace(arrowDown, ""); - } + function sortColumn(sortDirection) { + column.toBeSorted.sort(sortDirection, { + numeric: !isAlphaSort, + ignorePunctuation: !isPunctSort, + }); + } - if (column.toBeSorted[0] === undefined) { - return; + if (timesClickedColumn === 1) { + if (desc) { + changeTableArrow(arrow.down); + sortColumn(sortDescending); + } else { + changeTableArrow(arrow.up); + sortColumn(sortAscending); } - - function changeTableArrow(arrowDirection) { - if (tableArrows) { - clearArrows(arrowUp, arrowDown); - th.insertAdjacentText("beforeend", arrowDirection); - } + } else if (timesClickedColumn === 2) { + timesClickedColumn = 0; + if (desc) { + changeTableArrow(arrow.up); + sortColumn(sortAscending); + } else { + changeTableArrow(arrow.down); + sortColumn(sortDescending); } + } + return timesClickedColumn; + } - function sortColumn(sortDirection) { - column.toBeSorted.sort(sortDirection, { - numeric: !isAlphaSort, - ignorePunctuation: !isPunctSort, - }); + function updateFilesize(i, table, tr, column, columnIndex) { + if (table.hasClass.cellsSort) { + tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; + } else { + // We do this to sort rows rather than cells: + const template = document.createElement("template"); + template.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; + tr = template.content.firstChild; + } + let fileSizeInBytesHTML = column.getColumn( + tr, + column.spanSum, + column.span + ).outerHTML; + const fileSizeInBytesText = column.getColumn( + tr, + column.spanSum, + column.span + ).textContent; + + const fileSize = column.toBeSorted[i].replace(/#[0-9]*/, ""); + let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"]; + let replaced = false; + for (let i = 0; i < prefixes.length; ++i) { + let nextPrefixMultiplier = 2 ** (10 * (i + 1)); + if (fileSize < nextPrefixMultiplier) { + let prefixMultiplier = 2 ** (10 * i); + fileSizeInBytesHTML = fileSizeInBytesHTML.replace( + fileSizeInBytesText, + `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B` + ); + replaced = true; + break; } + } + if (!replaced) { + fileSizeInBytesHTML = fileSizeInBytesHTML.replace( + fileSizeInBytesText, + "NaN" + ); + } + tr.querySelectorAll("td").item(columnIndex).innerHTML = fileSizeInBytesHTML; + return table.hasClass.cellsSort ? tr.innerHTML : tr.outerHTML; + } - if (timesClickedColumn === 1) { - if (desc) { - changeTableArrow(arrowDown); - sortColumn(sortDescending); + function updateTable(tableProperties) { + const { column, table, columnIndex, hasThClass } = tableProperties; + for (let [i, tr] of table.visibleRows.entries()) { + if (hasThClass.fileSize) { + if (table.hasClass.cellsSort) { + tr.innerHTML = updateFilesize(i, table, tr, column, columnIndex); } else { - changeTableArrow(arrowUp); - sortColumn(sortAscending); + tr.outerHTML = updateFilesize(i, table, tr, column, columnIndex); } - } else if (timesClickedColumn === 2) { - timesClickedColumn = 0; - if (desc) { - changeTableArrow(arrowUp); - sortColumn(sortAscending); + } else if (!hasThClass.fileSize) { + if (table.hasClass.cellsSort) { + tr.innerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; } else { - changeTableArrow(arrowDown); - sortColumn(sortDescending); + tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; } } } + } - function updateTable(tableProperties) { - const { tableRows, column, hasThClass } = tableProperties; - for (let [i, tr] of tableRows.entries()) { - if (hasThClass.fileSize) { - tr.innerHTML = fileSizeColumnTextAndRow[column.toBeSorted[i]]; - let fileSizeInBytesHTML = tr - .querySelectorAll("td") - .item(columnIndex).innerHTML; - const fileSizeInBytesText = tr - .querySelectorAll("td") - .item(columnIndex).textContent; - // Remove the unique identifyer for duplicate values(#number). - column.toBeSorted[i] = column.toBeSorted[i].replace(/#[0-9]*/, ""); - const fileSize = parseFloat(column.toBeSorted[i]); - let prefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi"]; - let replaced = false; - for (let i = 0; i < prefixes.length; ++i) { - let nextPrefixMultiplier = 2 ** (10 * (i + 1)); - if (fileSize < nextPrefixMultiplier) { - let prefixMultiplier = 2 ** (10 * i); - fileSizeInBytesHTML = fileSizeInBytesHTML.replace( - fileSizeInBytesText, - `${(fileSize / prefixMultiplier).toFixed(2)} ${prefixes[i]}B` - ); - replaced = true; - break; - } - } - if (!replaced) { - fileSizeInBytesHTML = fileSizeInBytesHTML.replace( - fileSizeInBytesText, - "NaN" - ); - } - tr.querySelectorAll("td").item(columnIndex).innerHTML = - fileSizeInBytesHTML; - } else if (!hasThClass.fileSize) { - tr.outerHTML = columnIndexAndTableRow[column.toBeSorted[i]]; - } + function getColSpanData(headers, column) { + headers.forEach((th, index) => { + column.span[index] = th.colSpan; + if (index === 0) column.spanSum[index] = th.colSpan; + else column.spanSum[index] = column.spanSum[index - 1] + th.colSpan; + }); + } + + function rememberSort(columnIndexesClicked, timesClickedColumn, columnIndex) { + // if user clicked different column from first column reset times clicked. + columnIndexesClicked.push(columnIndex); + if (timesClickedColumn === 1 && columnIndexesClicked.length > 1) { + const lastColumnClicked = + columnIndexesClicked[columnIndexesClicked.length - 1]; + const secondLastColumnClicked = + columnIndexesClicked[columnIndexesClicked.length - 2]; + if (lastColumnClicked !== secondLastColumnClicked) { + columnIndexesClicked.shift(); + timesClickedColumn = 0; } } + return timesClickedColumn; + } + + function makeEachColumnSortable( + th, + headerIndex, + columnIndex, + table, + columnIndexesClicked + ) { + const desc = th.classList.contains("order-by-desc"); + const arrow = { up: " ▲", down: " ▼" }; + const fillValue = "!X!Y!Z!"; + + if (desc && table.hasClass.tableArrows) { + th.insertAdjacentText("beforeend", arrow.down); + } else if (table.hasClass.tableArrows) { + th.insertAdjacentText("beforeend", arrow.up); + } let timesClickedColumn = 0; + const column = { + getColumn: function getColumn(tr, colSpanSum, colSpanData) { + return tr + .querySelectorAll("td") + .item( + colSpanData[columnIndex] === 1 + ? colSpanSum[columnIndex] - 1 + : colSpanSum[columnIndex] - colSpanData[columnIndex] + ); + }, + }; th.addEventListener("click", function () { - const column = { - toBeSorted: [], - span: {}, - spanSum: {}, - }; + column.toBeSorted = []; + column.span = {}; + column.spanSum = {}; + getColSpanData(table.headers[headerIndex], column); table.visibleRows = Array.prototype.filter.call( - table.body.querySelectorAll("tr"), + table.bodies.item(headerIndex).querySelectorAll("tr"), (tr) => { return tr.style.display !== "none"; } ); - getColSpanData(table.headers, column); - - const isRememberSort = sortableTable.classList.contains("remember-sort"); - if (!isRememberSort) { - timesClickedColumn = rememberSort(); + if (!table.hasClass.rememberSort) { + timesClickedColumn = rememberSort( + columnIndexesClicked, + timesClickedColumn, + columnIndex + ); } timesClickedColumn += 1; @@ -501,13 +568,13 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { }; if (hasThClass.dataSort) { - sortDataAttributes(table.visibleRows, column); + sortDataAttributes(table, column); } if (hasThClass.fileSize) { - sortFileSize(table.visibleRows, column); + sortFileSize(table, column, columnIndex, fillValue); } if (hasThClass.runtime) { - sortByRuntime(table.visibleRows, column); + sortByRuntime(table, column); } const isSortDates = { @@ -517,20 +584,27 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) { }; // pick mdy first to override the inferred default class which is dmy. if (isSortDates.monthDayYear) { - sortDates("mdy", table.visibleRows, column); + sortDates("mdy", table, column); } else if (isSortDates.yearMonthDay) { - sortDates("ymd", table.visibleRows, column); + sortDates("ymd", table, column); } else if (isSortDates.dayMonthYear) { - sortDates("dmy", table.visibleRows, column); + sortDates("dmy", table, column); } const tableProperties = { + table, tableRows: table.visibleRows, + fillValue, column, + columnIndex, + th, hasThClass, isSortDates, + desc, + timesClickedColumn, + arrow, }; - getTableData(tableProperties); + timesClickedColumn = getTableData(tableProperties, timesClickedColumn); updateTable(tableProperties); }); diff --git a/public/docs/about.html b/public/docs/about.html index d2f10e5..e089683 100644 --- a/public/docs/about.html +++ b/public/docs/about.html @@ -72,7 +72,8 @@

Objectives of table-sort-js:

  • Having no dependencies keeps the library size down and avoids the - left-pad fiasco. (for example jquery which datatables relies on is around 80KB). + left-pad fiasco. (for example jquery which datatables relies on is + around 80KB).
  • Be versatile; sorts dates, numbers (in a natural order), diff --git a/public/docs/demo.html b/public/docs/demo.html index 1f4c92f..d3e3b24 100644 --- a/public/docs/demo.html +++ b/public/docs/demo.html @@ -61,167 +61,172 @@

    table-sort-js

    -
    Demo tables showing off what table-sort-js can sort - and features.
    +
    + Demo tables showing off what table-sort-js can sort and features. +
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Last NameFirst NameBirth DateEmployee IDDepartmentRuntimeFile Sizedata-sort daysdates in dd/mm/yyyy
    FranklinBenjamin1706-1-171k-level1h 1m 17s10bTuesday17/6/1978
    da VinciZarlo1452-4-15130001m 45s192038998987021bWednesday18/10/2027
    StathamJason1967-7-26HR11m 40s134809bFriday4/9/2008
    MichealAngelo1958-8-2154Marketing29s30980980bThursday2/3/1879
    Ben1994/9/23134Marketing41s902938402398bMonday8/6/1978
    -
    -

    Demo of colspan and data-sort class to sort fractions:

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    CategoryShowOverallOn Our DatesFirst Sold Out
    ComedyShow 118/2572%3/475%1999-07-30
    MusicShow 26/1060%3/475%1999-08-04
    TheatreShow 37/1547%3/475%1999-07-19
    ComedyShow 410/1567%2/367%1999-07-19
    ComedyShow 59/1275%1/250%1999-07-29
    ComedyShow 616/2467%2/450%1999-07-26
    ComedyShow 716/2662%2/450%2022-07-31
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Last NameFirst NameBirth DateEmployee IDDepartmentRuntimeFile Sizedata-sort daysdates in dd/mm/yyyy
    FranklinBenjamin1706-1-171k-level1h 1m 17s10bTuesday17/6/1978
    da VinciZarlo1452-4-15130001m 45s192038998987021bWednesday18/10/2027
    StathamJason1967-7-26HR11m 40s134809bFriday4/9/2008
    MichealAngelo1958-8-2154Marketing29s30980980bThursday2/3/1879
    Ben1994/9/23134Marketing41s902938402398bMonday8/6/1978
    +
    +

    + Demo of colspan and data-sort class to sort fractions: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CategoryShowOverallOn Our DatesFirst Sold Out
    ComedyShow 118/2572%3/475%1999-07-30
    MusicShow 26/1060%3/475%1999-08-04
    TheatreShow 37/1547%3/475%1999-07-19
    ComedyShow 410/1567%2/367%1999-07-19
    ComedyShow 59/1275%1/250%1999-07-29
    ComedyShow 616/2467%2/450%1999-07-26
    ComedyShow 716/2662%2/450%2022-07-31
    diff --git a/public/docs/development.html b/public/docs/development.html index 2abf4bb..12a7c9a 100644 --- a/public/docs/development.html +++ b/public/docs/development.html @@ -102,7 +102,8 @@

    How to contribute to table-sort-js:

  • note: table-sort-js currently uses node version 16; I would - recommend using node version manager (nvm) and running the command nvm 16 to select node version 16. + recommend using node version manager (nvm) and running the command + nvm 16 to select node version 16.
  • Testing table-sort-js: npm run test (currently uses Jest diff --git a/public/docs/html5.html b/public/docs/html5.html index aab4210..da7031e 100644 --- a/public/docs/html5.html +++ b/public/docs/html5.html @@ -65,7 +65,7 @@

    table-sort-js

    Example of how to use table-sort-js with basic HTML.
    -
    
    +        
    
     <script src="https://cdn.jsdelivr.net/npm/table-sort-js/table-sort.js"></script>
     <table class="table-sort">
       <thead>
    diff --git a/public/table-sort.js b/public/table-sort.js
    index dbf75bd..a9a7e31 100644
    --- a/public/table-sort.js
    +++ b/public/table-sort.js
    @@ -129,9 +129,12 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
           rememberSort: sortableTable.classList.contains("remember-sort"),
         };
     
    -
    -    for ( let headerIndex = 0; headerIndex < table.theads.length; headerIndex++) {
    -    let columnIndexesClicked = [];
    +    for (
    +      let headerIndex = 0;
    +      headerIndex < table.theads.length;
    +      headerIndex++
    +    ) {
    +      let columnIndexesClicked = [];
           for (let [columnIndex, th] of table.headers[headerIndex].entries()) {
             if (!th.classList.contains("disable-sort")) {
               th.style.cursor = "pointer";
    diff --git a/test/table.js b/test/table.js
    index bd40711..0ce6eb3 100644
    --- a/test/table.js
    +++ b/test/table.js
    @@ -105,7 +105,7 @@ function createTestTable(
       for (let [i, tr] of tableRows.entries()) {
         if (tr.style.display !== "none") {
           for (let i = 0; i < numberOfTableColumns; i++)
    -        if (props.tableTags === "cells-sort" || props.tableTags ==="tr-sort") {
    +        if (props.tableTags === "cells-sort" || props.tableTags === "tr-sort") {
               testIfSortedList[`col${i}`].push(tr.outerHTML);
             } else {
               testIfSortedList[`col${i}`].push(