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.