@@ -65,11 +65,13 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
65
65
// Doesn't infer dates with delimiter "."; as could capture semantic version numbers.
66
66
const dmyRegex = / ^ ( \d \d ? ) [ / - ] ( \d \d ? ) [ / - ] ( ( \d \d ) ? \d \d ) / ;
67
67
const ymdRegex = / ^ ( \d \d \d \d ) [ / - ] ( \d \d ? ) [ / - ] ( \d \d ? ) / ;
68
+ const numericRegex = / ^ (?: \( \d + (?: \. \d + ) ? \) | - ? \d + (?: \. \d + ) ? ) $ / ;
68
69
const inferableClasses = {
69
70
runtime : { regexp : runtimeRegex , class : "runtime-sort" , count : 0 } ,
70
71
filesize : { regexp : fileSizeRegex , class : "file-size-sort" , count : 0 } ,
71
72
dmyDates : { regexp : dmyRegex , class : "dates-dmy-sort" , count : 0 } ,
72
73
ymdDates : { regexp : ymdRegex , class : "dates-ymd-sort" , count : 0 } ,
74
+ numericRegex : { regexp : numericRegex , class : "numeric-sort" , count : 0 } ,
73
75
} ;
74
76
let classNameAdded = false ;
75
77
let regexNotFoundCount = 0 ;
@@ -105,28 +107,44 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
105
107
}
106
108
107
109
function makeTableSortable ( sortableTable ) {
108
- const tableBody = getTableBody ( sortableTable ) ;
109
- const tableHead = sortableTable . querySelector ( "thead" ) ;
110
- const tableHeadHeaders = tableHead . querySelectorAll ( "th" ) ;
111
- const tableRows = tableBody . querySelectorAll ( "tr" ) ;
110
+ const table = {
111
+ body : getTableBody ( sortableTable ) ,
112
+ head : sortableTable . querySelector ( "thead" ) ,
113
+ } ;
114
+ table . headers = table . head . querySelectorAll ( "th" ) ;
115
+ table . rows = table . body . querySelectorAll ( "tr" ) ;
116
+
117
+ let columnIndexesClicked = [ ] ;
112
118
113
119
const isNoSortClassInference =
114
120
sortableTable . classList . contains ( "no-class-infer" ) ;
115
121
116
- for ( let [ columnIndex , th ] of tableHeadHeaders . entries ( ) ) {
122
+ for ( let [ columnIndex , th ] of table . headers . entries ( ) ) {
117
123
if ( ! th . classList . contains ( "disable-sort" ) ) {
118
124
th . style . cursor = "pointer" ;
119
125
if ( ! isNoSortClassInference ) {
120
- inferSortClasses ( tableRows , columnIndex , th ) ;
126
+ inferSortClasses ( table . rows , columnIndex , th ) ;
121
127
}
122
- makeEachColumnSortable ( th , columnIndex , tableBody , sortableTable ) ;
128
+ makeEachColumnSortable (
129
+ th ,
130
+ columnIndex ,
131
+ table ,
132
+ sortableTable ,
133
+ columnIndexesClicked
134
+ ) ;
123
135
}
124
136
}
125
137
}
126
138
127
- function makeEachColumnSortable ( th , columnIndex , tableBody , sortableTable ) {
139
+ function makeEachColumnSortable (
140
+ th ,
141
+ columnIndex ,
142
+ table ,
143
+ sortableTable ,
144
+ columnIndexesClicked
145
+ ) {
128
146
const desc = th . classList . contains ( "order-by-desc" ) ;
129
- let tableArrows = sortableTable . classList . contains ( "table-arrows" ) ;
147
+ const tableArrows = sortableTable . classList . contains ( "table-arrows" ) ;
130
148
const [ arrowUp , arrowDown ] = [ " ▲" , " ▼" ] ;
131
149
const fillValue = "!X!Y!Z!" ;
132
150
@@ -249,9 +267,7 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
249
267
}
250
268
}
251
269
252
- let [ timesClickedColumn , columnIndexesClicked ] = [ 0 , [ ] ] ;
253
-
254
- function rememberSort ( timesClickedColumn , columnIndexesClicked ) {
270
+ function rememberSort ( ) {
255
271
// if user clicked different column from first column reset times clicked.
256
272
columnIndexesClicked . push ( columnIndex ) ;
257
273
if ( timesClickedColumn === 1 && columnIndexesClicked . length > 1 ) {
@@ -260,14 +276,15 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
260
276
const secondLastColumnClicked =
261
277
columnIndexesClicked [ columnIndexesClicked . length - 2 ] ;
262
278
if ( lastColumnClicked !== secondLastColumnClicked ) {
263
- timesClickedColumn = 0 ;
264
279
columnIndexesClicked . shift ( ) ;
280
+ timesClickedColumn = 0 ;
265
281
}
266
282
}
283
+ return timesClickedColumn ;
267
284
}
268
285
269
- function getColSpanData ( sortableTable , column ) {
270
- sortableTable . querySelectorAll ( "th" ) . forEach ( ( th , index ) => {
286
+ function getColSpanData ( headers , column ) {
287
+ headers . forEach ( ( th , index ) => {
271
288
column . span [ index ] = th . colSpan ;
272
289
if ( index === 0 ) column . spanSum [ index ] = th . colSpan ;
273
290
else column . spanSum [ index ] = column . spanSum [ index - 1 ] + th . colSpan ;
@@ -285,16 +302,7 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
285
302
}
286
303
287
304
function getTableData ( tableProperties ) {
288
- const {
289
- tableRows,
290
- column,
291
- isFileSize,
292
- isTimeSort,
293
- isSortDateDayMonthYear,
294
- isSortDateMonthDayYear,
295
- isSortDateYearMonthDay,
296
- isDataAttribute,
297
- } = tableProperties ;
305
+ const { tableRows, column, hasThClass, isSortDates } = tableProperties ;
298
306
for ( let [ i , tr ] of tableRows . entries ( ) ) {
299
307
let tdTextContent = getColumn (
300
308
tr ,
@@ -305,17 +313,17 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
305
313
tdTextContent = "" ;
306
314
}
307
315
if ( tdTextContent . trim ( ) !== "" ) {
308
- if ( isFileSize ) {
316
+ if ( hasThClass . fileSize ) {
309
317
fileSizeColumnTextAndRow [ column . toBeSorted [ i ] ] = tr . outerHTML ;
310
318
}
311
319
// These classes already handle pushing to column and setting the tr html.
312
320
if (
313
- ! isFileSize &&
314
- ! isDataAttribute &&
315
- ! isTimeSort &&
316
- ! isSortDateDayMonthYear &&
317
- ! isSortDateYearMonthDay &&
318
- ! isSortDateMonthDayYear
321
+ ! hasThClass . fileSize &&
322
+ ! hasThClass . dataSort &&
323
+ ! hasThClass . runtime &&
324
+ ! isSortDates . dayMonthYear &&
325
+ ! isSortDates . yearMonthDay &&
326
+ ! isSortDates . monthDayYear
319
327
) {
320
328
column . toBeSorted . push ( `${ tdTextContent } #${ i } ` ) ;
321
329
columnIndexAndTableRow [ `${ tdTextContent } #${ i } ` ] = tr . outerHTML ;
@@ -329,17 +337,48 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
329
337
330
338
const isPunctSort = th . classList . contains ( "punct-sort" ) ;
331
339
const isAlphaSort = th . classList . contains ( "alpha-sort" ) ;
340
+ const isNumericSort = th . classList . contains ( "numeric-sort" ) ;
341
+
342
+ function parseNumberFromString ( str ) {
343
+ let num ;
344
+ str = str . slice ( 0 , str . indexOf ( "#" ) ) ;
345
+ if ( str . match ( / ^ \( ( \d + (?: \. \d + ) ? ) \) $ / ) ) {
346
+ num = - 1 * Number ( str . slice ( 1 , - 1 ) ) ;
347
+ } else {
348
+ num = Number ( str ) ;
349
+ }
350
+ return num ;
351
+ }
352
+
353
+ function strLocaleCompare ( str1 , str2 ) {
354
+ return str1 . localeCompare (
355
+ str2 ,
356
+ navigator . languages [ 0 ] || navigator . language ,
357
+ { numeric : ! isAlphaSort , ignorePunctuation : ! isPunctSort }
358
+ ) ;
359
+ }
360
+
361
+ function handleNumbers ( str1 , str2 ) {
362
+ let num1 , num2 ;
363
+ num1 = parseNumberFromString ( str1 ) ;
364
+ num2 = parseNumberFromString ( str2 ) ;
365
+
366
+ if ( ! isNaN ( num1 ) && ! isNaN ( num2 ) ) {
367
+ return num1 - num2 ;
368
+ } else {
369
+ return strLocaleCompare ( str1 , str2 ) ;
370
+ }
371
+ }
372
+
332
373
function sortAscending ( a , b ) {
333
374
if ( a . includes ( `${ fillValue } #` ) ) {
334
375
return 1 ;
335
376
} else if ( b . includes ( `${ fillValue } #` ) ) {
336
377
return - 1 ;
378
+ } else if ( isNumericSort ) {
379
+ return handleNumbers ( a , b ) ;
337
380
} else {
338
- return a . localeCompare (
339
- b ,
340
- navigator . languages [ 0 ] || navigator . language ,
341
- { numeric : ! isAlphaSort , ignorePunctuation : ! isPunctSort }
342
- ) ;
381
+ return strLocaleCompare ( a , b ) ;
343
382
}
344
383
}
345
384
@@ -391,9 +430,9 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
391
430
}
392
431
393
432
function updateTable ( tableProperties ) {
394
- const { tableRows, column, isFileSize } = tableProperties ;
433
+ const { tableRows, column, hasThClass } = tableProperties ;
395
434
for ( let [ i , tr ] of tableRows . entries ( ) ) {
396
- if ( isFileSize ) {
435
+ if ( hasThClass . fileSize ) {
397
436
tr . innerHTML = fileSizeColumnTextAndRow [ column . toBeSorted [ i ] ] ;
398
437
let fileSizeInBytesHTML = tr
399
438
. querySelectorAll ( "td" )
@@ -426,71 +465,70 @@ function tableSortJs(testingTableSortJS = false, domDocumentWindow = document) {
426
465
}
427
466
tr . querySelectorAll ( "td" ) . item ( columnIndex ) . innerHTML =
428
467
fileSizeInBytesHTML ;
429
- } else if ( ! isFileSize ) {
468
+ } else if ( ! hasThClass . fileSize ) {
430
469
tr . outerHTML = columnIndexAndTableRow [ column . toBeSorted [ i ] ] ;
431
470
}
432
471
}
433
472
}
434
473
474
+ let timesClickedColumn = 0 ;
435
475
th . addEventListener ( "click" , function ( ) {
436
- timesClickedColumn += 1 ;
437
476
const column = {
438
- // column used for sorting; better name?
439
477
toBeSorted : [ ] ,
440
478
span : { } ,
441
479
spanSum : { } ,
442
480
} ;
443
481
444
- const visibleTableRows = Array . prototype . filter . call (
445
- tableBody . querySelectorAll ( "tr" ) ,
482
+ table . visibleRows = Array . prototype . filter . call (
483
+ table . body . querySelectorAll ( "tr" ) ,
446
484
( tr ) => {
447
485
return tr . style . display !== "none" ;
448
486
}
449
487
) ;
450
488
451
- getColSpanData ( sortableTable , column ) ;
489
+ getColSpanData ( table . headers , column ) ;
452
490
453
- const isDataAttribute = th . classList . contains ( "data -sort" ) ;
454
- if ( isDataAttribute ) {
455
- sortDataAttributes ( visibleTableRows , column ) ;
491
+ const isRememberSort = sortableTable . classList . contains ( "remember -sort" ) ;
492
+ if ( ! isRememberSort ) {
493
+ timesClickedColumn = rememberSort ( ) ;
456
494
}
495
+ timesClickedColumn += 1 ;
457
496
458
- const isFileSize = th . classList . contains ( "file-size-sort" ) ;
459
- if ( isFileSize ) {
460
- sortFileSize ( visibleTableRows , column ) ;
461
- }
497
+ const hasThClass = {
498
+ dataSort : th . classList . contains ( "data-sort" ) ,
499
+ fileSize : th . classList . contains ( "file-size-sort" ) ,
500
+ runtime : th . classList . contains ( "runtime-sort" ) ,
501
+ } ;
462
502
463
- const isTimeSort = th . classList . contains ( "runtime-sort" ) ;
464
- if ( isTimeSort ) {
465
- sortByRuntime ( visibleTableRows , column ) ;
503
+ if ( hasThClass . dataSort ) {
504
+ sortDataAttributes ( table . visibleRows , column ) ;
466
505
}
467
-
468
- const isSortDateDayMonthYear = th . classList . contains ( "dates-dmy-sort" ) ;
469
- const isSortDateMonthDayYear = th . classList . contains ( "dates-mdy-sort" ) ;
470
- const isSortDateYearMonthDay = th . classList . contains ( "dates-ymd-sort" ) ;
471
- // pick mdy first to override the inferred default class which is dmy.
472
- if ( isSortDateMonthDayYear ) {
473
- sortDates ( "mdy" , visibleTableRows , column ) ;
474
- } else if ( isSortDateYearMonthDay ) {
475
- sortDates ( "ymd" , visibleTableRows , column ) ;
476
- } else if ( isSortDateDayMonthYear ) {
477
- sortDates ( "dmy" , visibleTableRows , column ) ;
506
+ if ( hasThClass . fileSize ) {
507
+ sortFileSize ( table . visibleRows , column ) ;
508
+ }
509
+ if ( hasThClass . runtime ) {
510
+ sortByRuntime ( table . visibleRows , column ) ;
478
511
}
479
512
480
- const isRememberSort = sortableTable . classList . contains ( "remember-sort" ) ;
481
- if ( ! isRememberSort ) {
482
- rememberSort ( timesClickedColumn , columnIndexesClicked ) ;
513
+ const isSortDates = {
514
+ dayMonthYear : th . classList . contains ( "dates-dmy-sort" ) ,
515
+ monthDayYear : th . classList . contains ( "dates-mdy-sort" ) ,
516
+ yearMonthDay : th . classList . contains ( "dates-ymd-sort" ) ,
517
+ } ;
518
+ // pick mdy first to override the inferred default class which is dmy.
519
+ if ( isSortDates . monthDayYear ) {
520
+ sortDates ( "mdy" , table . visibleRows , column ) ;
521
+ } else if ( isSortDates . yearMonthDay ) {
522
+ sortDates ( "ymd" , table . visibleRows , column ) ;
523
+ } else if ( isSortDates . dayMonthYear ) {
524
+ sortDates ( "dmy" , table . visibleRows , column ) ;
483
525
}
484
526
485
527
const tableProperties = {
486
- tableRows : visibleTableRows ,
528
+ tableRows : table . visibleRows ,
487
529
column,
488
- isFileSize,
489
- isSortDateDayMonthYear,
490
- isSortDateMonthDayYear,
491
- isSortDateYearMonthDay,
492
- isDataAttribute,
493
- isTimeSort,
530
+ hasThClass,
531
+ isSortDates,
494
532
} ;
495
533
getTableData ( tableProperties ) ;
496
534
updateTable ( tableProperties ) ;
0 commit comments