1;(function() { 2 function Tablesort(el, options) { 3 if (!(this instanceof Tablesort)) return new Tablesort(el, options); 4 5 if (!el || el.tagName !== 'TABLE') { 6 throw new Error('Element must be a table'); 7 } 8 this.init(el, options || {}); 9 } 10 11 var sortOptions = []; 12 13 var createEvent = function(name) { 14 var evt; 15 16 if (!window.CustomEvent || typeof window.CustomEvent !== 'function') { 17 evt = document.createEvent('CustomEvent'); 18 evt.initCustomEvent(name, false, false, undefined); 19 } else { 20 evt = new CustomEvent(name); 21 } 22 23 return evt; 24 }; 25 26 var getInnerText = function(el) { 27 return el.getAttribute('data-sort') || el.textContent || el.innerText || ''; 28 }; 29 30 // Default sort method if no better sort method is found 31 var caseInsensitiveSort = function(a, b) { 32 a = a.trim().toLowerCase(); 33 b = b.trim().toLowerCase(); 34 35 if (a === b) return 0; 36 if (a < b) return 1; 37 38 return -1; 39 }; 40 41 var getCellByKey = function(cells, key) { 42 return [].slice.call(cells).find(function(cell) { 43 return cell.getAttribute('data-sort-column-key') === key; 44 }); 45 }; 46 47 // Stable sort function 48 // If two elements are equal under the original sort function, 49 // then there relative order is reversed 50 var stabilize = function(sort, antiStabilize) { 51 return function(a, b) { 52 var unstableResult = sort(a.td, b.td); 53 54 if (unstableResult === 0) { 55 if (antiStabilize) return b.index - a.index; 56 return a.index - b.index; 57 } 58 59 return unstableResult; 60 }; 61 }; 62 63 Tablesort.extend = function(name, pattern, sort) { 64 if (typeof pattern !== 'function' || typeof sort !== 'function') { 65 throw new Error('Pattern and sort must be a function'); 66 } 67 68 sortOptions.push({ 69 name: name, 70 pattern: pattern, 71 sort: sort 72 }); 73 }; 74 75 Tablesort.prototype = { 76 77 init: function(el, options) { 78 var that = this, 79 firstRow, 80 defaultSort, 81 i, 82 cell; 83 84 that.table = el; 85 that.thead = false; 86 that.options = options; 87 88 if (el.rows && el.rows.length > 0) { 89 if (el.tHead && el.tHead.rows.length > 0) { 90 for (i = 0; i < el.tHead.rows.length; i++) { 91 if (el.tHead.rows[i].getAttribute('data-sort-method') === 'thead') { 92 firstRow = el.tHead.rows[i]; 93 break; 94 } 95 } 96 if (!firstRow) { 97 firstRow = el.tHead.rows[el.tHead.rows.length - 1]; 98 } 99 that.thead = true; 100 } else { 101 firstRow = el.rows[0]; 102 } 103 } 104 105 if (!firstRow) return; 106 107 var onClick = function() { 108 if (that.current && that.current !== this) { 109 that.current.removeAttribute('aria-sort'); 110 } 111 112 that.current = this; 113 that.sortTable(this); 114 }; 115 116 // Assume first row is the header and attach a click handler to each. 117 for (i = 0; i < firstRow.cells.length; i++) { 118 cell = firstRow.cells[i]; 119 cell.setAttribute('role','columnheader'); 120 if (cell.getAttribute('data-sort-method') !== 'none') { 121 cell.tabindex = 0; 122 cell.addEventListener('click', onClick, false); 123 124 if (cell.getAttribute('data-sort-default') !== null) { 125 defaultSort = cell; 126 } 127 } 128 } 129 130 if (defaultSort) { 131 that.current = defaultSort; 132 that.sortTable(defaultSort); 133 } 134 }, 135 136 sortTable: function(header, update) { 137 var that = this, 138 columnKey = header.getAttribute('data-sort-column-key'), 139 column = header.cellIndex, 140 sortFunction = caseInsensitiveSort, 141 item = '', 142 items = [], 143 i = that.thead ? 0 : 1, 144 sortMethod = header.getAttribute('data-sort-method'), 145 sortOrder = header.getAttribute('aria-sort'); 146 147 that.table.dispatchEvent(createEvent('beforeSort')); 148 149 // If updating an existing sort, direction should remain unchanged. 150 if (!update) { 151 if (sortOrder === 'ascending') { 152 sortOrder = 'descending'; 153 } else if (sortOrder === 'descending') { 154 sortOrder = 'ascending'; 155 } else { 156 sortOrder = that.options.descending ? 'descending' : 'ascending'; 157 } 158 159 header.setAttribute('aria-sort', sortOrder); 160 } 161 162 if (that.table.rows.length < 2) return; 163 164 // If we force a sort method, it is not necessary to check rows 165 if (!sortMethod) { 166 var cell; 167 while (items.length < 3 && i < that.table.tBodies[0].rows.length) { 168 if(columnKey) { 169 cell = getCellByKey(that.table.tBodies[0].rows[i].cells, columnKey); 170 } else { 171 cell = that.table.tBodies[0].rows[i].cells[column]; 172 } 173 174 // Treat missing cells as empty cells 175 item = cell ? getInnerText(cell) : ""; 176 177 item = item.trim(); 178 179 if (item.length > 0) { 180 items.push(item); 181 } 182 183 i++; 184 } 185 186 if (!items) return; 187 } 188 189 for (i = 0; i < sortOptions.length; i++) { 190 item = sortOptions[i]; 191 192 if (sortMethod) { 193 if (item.name === sortMethod) { 194 sortFunction = item.sort; 195 break; 196 } 197 } else if (items.every(item.pattern)) { 198 sortFunction = item.sort; 199 break; 200 } 201 } 202 203 that.col = column; 204 205 for (i = 0; i < that.table.tBodies.length; i++) { 206 var newRows = [], 207 noSorts = {}, 208 j, 209 totalRows = 0, 210 noSortsSoFar = 0; 211 212 if (that.table.tBodies[i].rows.length < 2) continue; 213 214 for (j = 0; j < that.table.tBodies[i].rows.length; j++) { 215 var cell; 216 217 item = that.table.tBodies[i].rows[j]; 218 if (item.getAttribute('data-sort-method') === 'none') { 219 // keep no-sorts in separate list to be able to insert 220 // them back at their original position later 221 noSorts[totalRows] = item; 222 } else { 223 if (columnKey) { 224 cell = getCellByKey(item.cells, columnKey); 225 } else { 226 cell = item.cells[that.col]; 227 } 228 // Save the index for stable sorting 229 newRows.push({ 230 tr: item, 231 td: cell ? getInnerText(cell) : '', 232 index: totalRows 233 }); 234 } 235 totalRows++; 236 } 237 // Before we append should we reverse the new array or not? 238 // If we reverse, the sort needs to be `anti-stable` so that 239 // the double negatives cancel out 240 if (sortOrder === 'descending') { 241 newRows.sort(stabilize(sortFunction, true)); 242 } else { 243 newRows.sort(stabilize(sortFunction, false)); 244 newRows.reverse(); 245 } 246 247 // append rows that already exist rather than creating new ones 248 for (j = 0; j < totalRows; j++) { 249 if (noSorts[j]) { 250 // We have a no-sort row for this position, insert it here. 251 item = noSorts[j]; 252 noSortsSoFar++; 253 } else { 254 item = newRows[j - noSortsSoFar].tr; 255 } 256 257 // appendChild(x) moves x if already present somewhere else in the DOM 258 that.table.tBodies[i].appendChild(item); 259 } 260 } 261 262 that.table.dispatchEvent(createEvent('afterSort')); 263 }, 264 265 refresh: function() { 266 if (this.current !== undefined) { 267 this.sortTable(this.current, true); 268 } 269 } 270 }; 271 272 if (typeof module !== 'undefined' && module.exports) { 273 module.exports = Tablesort; 274 } else { 275 window.Tablesort = Tablesort; 276 } 277})();
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.