PageSourceSearch

https://simonstl.com/covid/sort-table.js

js simonstl.com collected 2026-09-24 17:45:25 UTC 10,037 bytes, 299 lines download raw bytes

1/**
2 * sort-table.js
3 * A pure JavaScript (no dependencies) solution to make HTML
4 *  Tables sortable
5 *
6 * Copyright (c) 2013 Tyler Uebele
7 * Released under the MIT license.  See included LICENSE.txt
8 *  or http://opensource.org/licenses/MIT
9 *
10 * latest version available at https://github.com/tyleruebele/sort-table
11 */
12
13/**
14 * Sort the rows in a HTML Table
15 *
16 * @param Table The Table DOM object
17 * @param col   The zero-based column number by which to sort
18 * @param dir   Optional. The sort direction; pass 1 for asc; -1 for desc
19 * @returns void
20 */
21function sortTable(Table, col, dir) {
22    var sortClass, i;
23
24    // get previous sort column
25    sortTable.sortCol = -1;
26    sortClass = Table.className.match(/js-sort-\d+/);
27    if (null != sortClass) {
28        sortTable.sortCol = sortClass[0].replace(/js-sort-/, '');
29        Table.className = Table.className.replace(new RegExp(' ?' + sortClass[0] + '\\b'), '');
30    }
31    // If sort column was not passed, use previous
32    if ('undefined' === typeof col) {
33        col = sortTable.sortCol;
34    }
35
36    if ('undefined' !== typeof dir) {
37        // Accept -1 or 'desc' for descending.  All else is ascending
38        sortTable.sortDir = dir == -1 || dir == 'desc' ? -1 : 1;
39    } else {
40        // sort direction was not passed, use opposite of previous
41        sortClass = Table.className.match(/js-sort-(a|de)sc/);
42        if (null != sortClass && sortTable.sortCol == col) {
43            sortTable.sortDir = 'js-sort-asc' == sortClass[0] ? -1 : 1;
44        } else {
45            sortTable.sortDir = 1;
46        }
47    }
48    Table.className = Table.className.replace(/ ?js-sort-(a|de)sc/g, '');
49
50    // update sort column
51    Table.className += ' js-sort-' + col;
52    sortTable.sortCol = col;
53
54    // update sort direction
55    Table.className += ' js-sort-' + (sortTable.sortDir == -1 ? 'desc' : 'asc');
56
57    // get sort type
58    if (col < Table.tHead.rows[Table.tHead.rows.length - 1].cells.length) {
59        sortClass = Table.tHead.rows[Table.tHead.rows.length - 1].cells[col].className.match(/js-sort-[-\w]+/);
60    }
61    // Improved support for colspan'd headers
62    for (i = 0; i < Table.tHead.rows[Table.tHead.rows.length - 1].cells.length; i++) {
63        if (col == Table.tHead.rows[Table.tHead.rows.length - 1].cells[i].getAttribute('data-js-sort-colNum')) {
64            sortClass = Table.tHead.rows[Table.tHead.rows.length - 1].cells[i].className.match(/js-sort-[-\w]+/);
65        }
66    }
67    if (null != sortClass) {
68        sortTable.sortFunc = sortClass[0].replace(/js-sort-/, '');
69    } else {
70        sortTable.sortFunc = 'string';
71    }
72    // Set the headers for the active column to have the decorative class
73    Table.querySelectorAll('.js-sort-active').forEach(function(Node) {
74        Node.className = Node.className.replace(/ ?js-sort-active\b/, '');
75    });
76    Table.querySelectorAll('[data-js-sort-colNum="' + col + '"]:not(:empty)').forEach(function(Node) {
77        Node.className += ' js-sort-active';
78    });
79
80    // sort!
81    var rows = [],
82        TBody = Table.tBodies[0];
83
84    for (i = 0; i < TBody.rows.length; i++) {
85        rows[i] = TBody.rows[i];
86    }
87    if ('none' != sortTable.sortFunc) {
88        rows.sort(sortTable.compareRow);
89    }
90
91    while (TBody.firstChild) {
92        TBody.removeChild(TBody.firstChild);
93    }
94    for (i = 0; i < rows.length; i++) {
95        TBody.appendChild(rows[i]);
96    }
97}
98
99/**
100 * Compare two table rows based on current settings
101 *
102 * @param RowA A TR DOM object
103 * @param RowB A TR DOM object
104 * @returns {number} 1 if RowA is greater, -1 if RowB, 0 if equal
105 */
106sortTable.compareRow = function(RowA, RowB) {
107    var valA, valB;
108    if ('function' != typeof sortTable[sortTable.sortFunc]) {
109        sortTable.sortFunc = 'string';
110    }
111    valA = sortTable[sortTable.sortFunc](RowA.cells[sortTable.sortCol]);
112    valB = sortTable[sortTable.sortFunc](RowB.cells[sortTable.sortCol]);
113
114    return valA == valB ? 0 : sortTable.sortDir * (valA > valB ? 1 : -1);
115};
116
117/**
118 * Strip all HTML, no exceptions
119 * @param html
120 * @returns {string}
121 */
122sortTable.stripTags = function(html) {
123    return html.replace(/<\/?[a-z][a-z0-9]*\b[^>]*>/gi, '');
124};
125
126/**
127 * Helper function that converts a table cell (TD) to a comparable value
128 * Converts innerHTML to a timestamp, 0 for invalid dates
129 *
130 * @param Cell A TD DOM object
131 * @returns {Number}
132 */
133sortTable.date = function(Cell) {
134    // If okDate library is available, Use it for advanced Date processing
135    if (typeof okDate !== 'undefined') {
136        var kDate = okDate(sortTable.stripTags(Cell.innerHTML));
137        return kDate ? kDate.getTime() : 0;
138    } else {
139        return (new Date(sortTable.stripTags(Cell.innerHTML))).getTime() || 0;
140    }
141};
142
143/**
144 * Helper function that converts a table cell (TD) to a comparable value
145 * Converts innerHTML to a JS Number object
146 *
147 * @param Cell A TD DOM object
148 * @returns {Number}
149 */
150sortTable.number = function(Cell) {
151    return Number(sortTable.stripTags(Cell.innerHTML).replace(/[^-\d.]/g, ''));
152};
153
154/**
155 * Helper function that converts a table cell (TD) to a comparable value
156 * Converts innerHTML to a lower case string for insensitive compare
157 *
158 * @param Cell A TD DOM object
159 * @returns {String}
160 */
161sortTable.string = function(Cell) {
162    return sortTable.stripTags(Cell.innerHTML).toLowerCase();
163};
164
165/**
166 * Helper function that converts a table cell (TD) to a comparable value
167 *
168 * @param Cell A TD DOM object
169 * @returns {String}
170 */
171sortTable.raw = function(Cell) {
172    return Cell.innerHTML;
173};
174
175/**
176 * Helper function that converts a table cell (TD) to a comparable value
177 * Captures the last space-delimited token from innerHTML
178 *
179 * @param Cell A TD DOM object
180 * @returns {String}
181 */
182sortTable.last = function(Cell) {
183    return sortTable.stripTags(Cell.innerHTML).split(' ').pop().toLowerCase();
184};
185
186/**
187 * Helper function that converts a table cell (TD) to a comparable value
188 * Captures the value of the first childNode
189 *
190 * @param Cell A TD DOM object
191 * @returns {String}
192 */
193sortTable.input = function(Cell) {
194    for (var i = 0; i < Cell.children.length; i++) {
195        if ('object' == typeof Cell.children[i]
196            && 'undefined' != typeof Cell.children[i].value
197        ) {
198            return Cell.children[i].value.toLowerCase();
199        }
200    }
201
202    return sortTable.string(Cell);
203};
204
205/**
206 * Helper function that prevents sorting by always returning null
207 *
208 * @param Cell A TD DOM object
209 * @returns null
210 */
211sortTable.none = function(Cell) {
212    return null;
213};
214
215/**
216 * Return the click handler appropriate to the specified Table and column
217 *
218 * @param Table Table to sort
219 * @param col   Column to sort by
220 * @returns {Function} Click Handler
221 */
222sortTable.getClickHandler = function(Table, col) {
223    return function() {
224        sortTable(Table, col);
225    };
226};
227
228/**
229 * Attach sortTable() calls to table header cells' onclick events
230 * If the table(s) do not have a THead node, one will be created around the
231 *  first row
232 */
233sortTable.init = function() {
234    var THead, Tables, Handler;
235    if (document.querySelectorAll) {
236        Tables = document.querySelectorAll('table.js-sort-table');
237    } else {
238        Tables = document.getElementsByTagName('table');
239    }
240
241    for (var i = 0; i < Tables.length; i++) {
242        // Because IE<8 doesn't support querySelectorAll, skip unclassed tables
243        if (!document.querySelectorAll && null === Tables[i].className.match(/\bjs-sort-table\b/)) {
244            continue;
245        }
246
247        // Prevent repeat processing
248        if (Tables[i].attributes['data-js-sort-table']) {
249            continue;
250        }
251
252        // Ensure table has a tHead element
253        if (!Tables[i].tHead) {
254            THead = document.createElement('thead');
255            THead.appendChild(Tables[i].rows[0]);
256            Tables[i].insertBefore(THead, Tables[i].children[0]);
257        } else {
258            THead = Tables[i].tHead;
259        }
260
261        // Attach click events to table header
262        for (var rowNum = 0; rowNum < THead.rows.length; rowNum++) {
263            for (var cellNum = 0, colNum = 0; cellNum < THead.rows[rowNum].cells.length; cellNum++) {
264                // Skip headers marked "js-sort-none"
265                if (THead.rows[rowNum].cells[cellNum].className.match(/\bjs-sort-none\b/)) {
266                    continue;
267                }
268                // Define which column the header should invoke sorting for
269                THead.rows[rowNum].cells[cellNum].setAttribute('data-js-sort-colNum', colNum);
270                Handler = sortTable.getClickHandler(Tables[i], colNum);
271                window.addEventListener
272                    ? THead.rows[rowNum].cells[cellNum].addEventListener('click', Handler)
273                    : window.attachEvent && THead.rows[rowNum].cells[cellNum].attachEvent('onclick', Handler);
274                colNum += THead.rows[rowNum].cells[cellNum].colSpan;
275            }
276        }
277
278        // Mark table as processed
279        Tables[i].setAttribute('data-js-sort-table', 'true')
280    }
281
282    // Add default styles as the first style in head so they can be easily overwritten by user styles
283    var element = document.createElement('style');
284    document.head.insertBefore(element, document.head.childNodes[0]);
285    var sheet = element.sheet;
286    sheet.insertRule('table.js-sort-table.js-sort-asc thead tr > .js-sort-active:not(.js-sort-none):after {content: "\\25b2";font-size: 0.7em;padding-left: 3px;line-height: 0.7em;}', 0);
287    sheet.insertRule('table.js-sort-table.js-sort-desc thead tr > .js-sort-active:not(.js-sort-none):after {content: "\\25bc";font-size: 0.7em;padding-left: 3px;line-height: 0.7em;}', 0);
288};
289
290// Run sortTable.init() when the page loads
291window.addEventListener
292    ? window.addEventListener('load', sortTable.init, false)
293    : window.attachEvent && window.attachEvent('onload', sortTable.init)
294    ;
295
296// Shim for IE11's lack of NodeList.prototype.forEach
297if (typeof NodeList.prototype.forEach !== "function") {
298    NodeList.prototype.forEach = Array.prototype.forEach;
299}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.