1/*----------------------------------------------------------------------------\ 2| Sortable Table 1.12+02 | 3|-----------------------------------------------------------------------------| 4| Created by Erik Arvidsson | 5| (http://webfx.eae.net/contact.html#erik) | 6| For WebFX (http://webfx.eae.net/) | 7|-----------------------------------------------------------------------------| 8| A DOM 1 based script that allows an ordinary HTML table to be sortable. | 9|-----------------------------------------------------------------------------| 10| Copyright (c) 1998 - 2006 Erik Arvidsson | 11|-----------------------------------------------------------------------------| 12| Licensed under the Apache License, Version 2.0 (the "License"); you may not | 13| use this file except in compliance with the License. You may obtain a copy | 14| of the License at http://www.apache.org/licenses/LICENSE-2.0 | 15| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 16| Unless required by applicable law or agreed to in writing, software | 17| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 18| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 19| License for the specific language governing permissions and limitations | 20| under the License. | 21|-----------------------------------------------------------------------------| 22| 2003-01-10 | First version | 23| 2003-01-19 | Minor changes to the date parsing | 24| 2003-01-28 | JScript 5.0 fixes (no support for 'in' operator) | 25| 2003-02-01 | Sloppy typo like error fixed in getInnerText | 26| 2003-07-04 | Added workaround for IE cellIndex bug. | 27| 2003-11-09 | The bDescending argument to sort was not correctly working | 28| | Using onclick DOM0 event if no support for addEventListener | 29| | or attachEvent | 30| 2004-01-13 | Adding addSortType and removeSortType which makes it a lot | 31| | easier to add new, custom sort types. | 32| 2004-01-27 | Switch to use descending = false as the default sort order. | 33| | Change defaultDescending to suit your needs. | 34| 2004-03-14 | Improved sort type None look and feel a bit | 35| 2004-08-26 | Made the handling of tBody and tHead more flexible. Now you | 36| | can use another tHead or no tHead, and you can chose some | 37| | other tBody. | 38| 2006-04-25 | Changed license to Apache Software License 2.0 | 39|-----------------------------------------------------------------------------| 40| 2005-10-06 | condition check (&& c.firstChild.nodeType != 3) [email protected] | 41| 2005-10-15 | multiple tHead (and related tBody) rows support [email protected] | 42| 2006-09-05 | sortTypes existing check / safari support [email protected] | 43|-----------------------------------------------------------------------------| 44| Created 2003-01-10 | All changes are in the log above. | Updated 2006-09-05 | 45\----------------------------------------------------------------------------*/ 46 47 48function SortableTable(oTable, oSortTypes) { 49 50 this.sortTypes = oSortTypes || []; 51 52 this.sortColumn = null; 53 this.sortRow = null; 54 this.descending = null; 55 56 var oThis = this; 57 this._headerOnclick = function (e) { 58 oThis.headerOnclick(e); 59 }; 60 61 if (oTable) { 62 this.setTable( oTable ); 63 this.document = oTable.ownerDocument || oTable.document; 64 } 65 else { 66 this.document = document; 67 } 68 69 70 // only IE needs this 71 var win = this.document.defaultView || this.document.parentWindow; 72 this._onunload = function () { 73 oThis.destroy(); 74 }; 75 if (win && typeof win.attachEvent != "undefined") { 76 win.attachEvent("onunload", this._onunload); 77 } 78} 79 80SortableTable.gecko = navigator.product == "Gecko"; 81SortableTable.safari = (navigator.userAgent.indexOf("Safari") != -1); 82SortableTable.msie = /msie/i.test(navigator.userAgent); 83// Mozilla is faster when doing the DOM manipulations on 84// an orphaned element. MSIE is not 85SortableTable.removeBeforeSort = SortableTable.gecko; 86 87SortableTable.prototype.onsort = function () {}; 88 89// default sort order. true ->
89 descending, false -> ascending 90SortableTable.prototype.defaultDescending = false; 91 92// shared between all instances. This is intentional to allow external files 93// to modify the prototype 94SortableTable.prototype._sortTypeInfo = {}; 95 96SortableTable.prototype.setTable = function (oTable) { 97 if ( this.tHead ) 98 this.uninitHeader(); 99 this.element = oTable; 100 this.setTHead( oTable.tHead ); 101 this.setTBody( oTable.tBodies[0] ); 102}; 103 104SortableTable.prototype.setTHead = function (oTHead) { 105 if (this.tHead && this.tHead != oTHead ) 106 this.uninitHeader(); 107 this.tHead = oTHead; 108 this.step = oTHead.rows.length; 109 this.initHeader( this.sortTypes ); 110}; 111 112SortableTable.prototype.setTBody = function (oTBody) { 113 this.tBody = oTBody; 114}; 115 116SortableTable.prototype.setSortTypes = function ( oSortTypes ) { 117 if ( this.tHead ) 118 this.uninitHeader(); 119 this.sortTypes = oSortTypes || []; 120 if ( this.tHead ) 121 this.initHeader( this.sortTypes ); 122}; 123 124// adds arrow containers and events 125// also binds sort type to the header cells so that reordering columns does 126// not break the sort types 127SortableTable.prototype.initHeader = function (oSortTypes) { 128 if (!this.tHead) return; 129 130 this.sortTypes = new Array(this.tHead.rows.length); 131 this.head 132 var index = 0; 133 134 var doc = this.tHead.ownerDocument || this.tHead.document; 135 var sortTypes = oSortTypes || []; 136 137 for (var row = 0; row < this.tHead.rows.length; row++) { 138 var cells = this.tHead.rows[row].cells; 139 this.sortTypes[row] = new Array(cells.length); 140 for (var column = 0; column < cells.length; column++) { 141 var c = cells[column]; 142 var sortType = sortTypes[index++] || 'None'; 143 this.sortTypes[row][column] = sortType; 144 if (sortType == 'None') { continue; } 145 146 if (c.firstChild.nodeName == 'A') { c.firstChild.href='javascript:void(0)'; } 147 148 var img = doc.createElement('IMG'); 149 img.src = 'image/blank.png'; 150 c.appendChild(img); 151 152 if (typeof c.addEventListener != "undefined") { 153 c.addEventListener("click", this._headerOnclick, false); 154 } else if (typeof c.attachEvent != "undefined") { 155 c.attachEvent("onclick", this._headerOnclick); 156 } else { 157 c.onclick = this._headerOnclick; 158 } 159 } 160 } 161 this.updateHeaderArrows(); 162}; 163 164// remove arrows and events 165SortableTable.prototype.uninitHeader = function () { 166 if (! this.tHead) { return; } 167 for (var row = 0; row < this.tHead.rows.length; row++) { 168 var cells = this.tHead.rows[row].cells; 169 for (var column = 0; column < cells.length; column++) { 170 var c = cells[column]; 171 if (this.getSortType(row, column) == 'None') { continue; } 172 c.removeChild(c.lastChild); 173 if (typeof c.removeEventListener != "undefined") { 174 c.removeEventListener("click", this._headerOnclick, false); 175 } else if (typeof c.detachEvent != "undefined") { 176 c.detachEvent("onclick", this._headerOnclick); 177 } 178 } 179 } 180}; 181 182SortableTable.prototype.updateHeaderArrows = function () { 183 if (! this.tHead) { return; } 184 for (var row = 0; row < this.tHead.rows.length; row++) { 185 var cells = this.tHead.rows[row].cells; 186 for (var column = 0; column < cells.length; column++) { 187 var c = cells[column]; 188 if (this.getSortType(row, column) == 'None') { continue; } 189 var img = c.lastChild; 190 if (row == this.sortRow && column == this.sortColumn) { 191 img.className = "sort-arrow " + (this.descending ? "descending" : "ascending"); 192 } else { 193 img.className = "sort-arrow"; 194 } 195 } 196 } 197}; 198 199SortableTable.prototype.headerOnclick = function (e) { 200 // find TD element 201 var cell = e.target || e.srcElement; 202 while (cell.nodeName != "TD" && cell.nodeName != "TH") { cell = cell.parentNode; } 203 var row = cell.parentNode; 204 while (row.nodeName != 'TR') { row = row.parentNode; } 205 206 var row = row.rowIndex; 207 var column = (SortableTable.msie || SortableTable.safari) 208 ? SortableTable.getCellIndex(cell) 209 : cell.cellIndex; 210 this.sort(row, column); 211}; 212 213// IE returns wrong cellIndex when columns are hidden 214SortableTable.getCellIndex = function (cell) { 215 var cells = cell.parentNode.childNodes; 216 for (var column = 0; cells[column] != cell && column < cells.length; column++) 217 ; 218 return column; 219}; 220 221SortableTable.prototype.getSortType = function (row, column) { 222 if (! this.sortTypes[row]) { return "None"; } 223 return this.sortTypes[row][column] || "String"; 224}; 225 226// only nRow, nColumn is required 227// if bDescending is left out the old value is taken into account 228// if sSortType is left out the sort type is found from the sortTypes array 229 230SortableTable.prototype.sort = function (nRow, nColumn, bDescending, sSortType) { 231 if (! this.tBody) { return; }
232 if (sSortType == null) { sSortType = this.getSortType(nRow, nColumn); } 233 234 // exit if None 235 if (sSortType == 'None') { return; } 236 237 if (bDescending == null) { 238 this.descending = (this.sortRow == nRow && this.sortColumn == nColumn) 239 ? ! this.descending : this.defaultDescending; 240 } else { 241 this.descending = bDescending; 242 } 243 244 this.sortRow = nRow; 245 this.sortColumn = nColumn; 246 247 if (typeof this.onbeforesort == "function") { 248 this.onbeforesort(); 249 } 250 251 var f = this.getSortFunction(sSortType, nRow, nColumn); 252 var a = this.getCache(sSortType, nRow, nColumn); 253 var tBody = this.tBody; 254 255 a.sort(f); 256 257 if (this.descending) { 258 a.reverse(); 259 } 260 261 if (SortableTable.removeBeforeSort) { 262 // remove from doc 263 var nextSibling = tBody.nextSibling; 264 var p = tBody.parentNode; 265 p.removeChild(tBody); 266 } 267 268 // insert in the new order 269 for (var i = 0; i < a.length; i++) { 270 for (var j = 0; j < a[i].elements.length; j++) { 271 tBody.appendChild(a[i].elements[j]); 272 } 273 } 274 275 if (SortableTable.removeBeforeSort) { 276 // insert into doc 277 p.insertBefore(tBody, nextSibling); 278 } 279 280 this.updateHeaderArrows(); 281 282 this.destroyCache(a); 283 284 if (typeof this.onsort == "function") { 285 this.onsort(); 286 } 287}; 288 289SortableTable.prototype.asyncSort = function (nRow, nColumn, bDescending, sSortType) { 290 var oThis = this; 291 this._asyncsort = function () { 292 oThis.sort(nRow, nColumn, bDescending, sSortType); 293 }; 294 window.setTimeout(this._asyncsort, 1); 295}; 296 297SortableTable.prototype.getCache = function (sType, nRow, nColumn) { 298 if (!this.tBody) { return []; } 299 var rows = this.tBody.rows; 300 var a = new Array; 301 for (var i = 0; i < rows.length; i += this.step) { 302 var r = []; 303 for (var j = 0; j < this.step; j++) { r.push(rows[i + j]); } 304 a.push({ 305 value: this.getRowValue(r[nRow], sType, nColumn), 306 elements: r 307 }); 308 }; 309 return a; 310}; 311 312SortableTable.prototype.destroyCache = function (oArray) { 313 var l = oArray.length; 314 for (var i = 0; i < l; i++) { 315 oArray[i].value = null; 316 oArray[i].elements = null; 317 oArray[i] = null; 318 } 319}; 320 321SortableTable.prototype.getRowValue = function (oRow, sType, nColumn) { 322 // if we have defined a custom getRowValue use that 323 if (this._sortTypeInfo[sType] && this._sortTypeInfo[sType].getRowValue) 324 return this._sortTypeInfo[sType].getRowValue(oRow, nColumn); 325 326 var s; 327 var c = oRow.cells[nColumn]; 328 // add condition (&& c.firstChild.nodeType != 3) 2005-10-06 [email protected] 329 if (c.firstChild && c.firstChild.nodeType != 3) { c = c.firstChild; } 330 if (typeof c.innerText != "undefined") 331 s = c.innerText; 332 else 333 s = SortableTable.getInnerText(c); 334 return this.getValueFromString(s, sType); 335}; 336 337SortableTable.getInnerText = function (oNode) { 338 var s = ""; 339 var cs = oNode.childNodes; 340 var l = cs.length; 341 for (var i = 0; i < l; i++) { 342 switch (cs[i].nodeType) { 343 case 1: //ELEMENT_NODE 344 s += SortableTable.getInnerText(cs[i]); 345 break; 346 case 3: //TEXT_NODE 347 s += cs[i].nodeValue; 348 break; 349 } 350 } 351 return s; 352}; 353 354SortableTable.prototype.getValueFromString = function (sText, sType) { 355 if (this._sortTypeInfo[sType]) 356 return this._sortTypeInfo[sType].getValueFromString( sText ); 357 return sText; 358 /* 359 switch (sType) { 360 case "Number": 361 return Number(sText); 362 case "CaseInsensitiveString": 363 return sText.toUpperCase(); 364 case "Date": 365 var parts = sText.split("-"); 366 var d = new Date(0); 367 d.setFullYear(parts[0]); 368 d.setDate(parts[2]); 369 d.setMonth(parts[1] - 1); 370 return d.valueOf(); 371 } 372 return sText; 373 */ 374 }; 375 376SortableTable.prototype.getSortFunction = function (sType, nRow, nColumn) { 377 if (this._sortTypeInfo[sType]) 378 return this._sortTypeInfo[sType].compare; 379 return SortableTable.basicCompare; 380}; 381 382SortableTable.prototype.destroy = function () { 383 this.uninitHeader(); 384 var win = this.document.parentWindow; 385 if (win && typeof win.detachEvent != "undefined") { // only IE needs this 386 win.detachEvent("onunload", this._onunload); 387 } 388 this._onunload = null; 389 this.element = null; 390 this.tHead = null; 391 this.step = null; 392 this.tBody = null; 393 this.document = null; 394 this._headerOnclick = null; 395 this.sortTypes = null; 396 this._asyncsort = null; 397 this.onsort = null; 398}; 399 400// Adds a sort type to all instance of SortableTable
401// sType : String - the identifier of the sort type 402// fGetValueFromString : function ( s : string ) : T - A function that takes a 403// string and casts it to a desired format. If left out the string is just 404// returned 405// fCompareFunction : function ( n1 : T, n2 : T ) : Number - A normal JS sort 406// compare function. Takes two values and compares them. If left out less than, 407// <, compare is used 408// fGetRowValue : function( oRow : HTMLTRElement, nColumn : int ) : T - A function 409// that takes the row and the column index and returns the value used to compare. 410// If left out then the innerText is first taken for the cell and then the 411// fGetValueFromString is used to convert that string the desired value and type 412 413SortableTable.prototype.addSortType = function (sType, fGetValueFromString, fCompareFunction, fGetRowValue) { 414 this._sortTypeInfo[sType] = { 415 type: sType, 416 getValueFromString: fGetValueFromString || SortableTable.idFunction, 417 compare: fCompareFunction || SortableTable.basicCompare, 418 getRowValue: fGetRowValue 419 }; 420}; 421 422// this removes the sort type from all instances of SortableTable 423SortableTable.prototype.removeSortType = function (sType) { 424 delete this._sortTypeInfo[sType]; 425}; 426 427SortableTable.basicCompare = function compare(n1, n2) { 428 if (n1.value < n2.value) 429 return -1; 430 if (n2.value < n1.value) 431 return 1; 432 return 0; 433}; 434 435SortableTable.idFunction = function (x) { 436 return x; 437}; 438 439SortableTable.toUpperCase = function (s) { 440 return s.toUpperCase(); 441}; 442 443SortableTable.toDate = function (s) { 444 var parts = s.split("-"); 445 var d = new Date(0); 446 d.setFullYear(parts[0]); 447 d.setDate(parts[2]); 448 d.setMonth(parts[1] - 1); 449 return d.valueOf(); 450}; 451 452 453// add sort types 454SortableTable.prototype.addSortType("Number", Number); 455SortableTable.prototype.addSortType("CaseInsensitiveString", SortableTable.toUpperCase); 456SortableTable.prototype.addSortType("Date", SortableTable.toDate); 457SortableTable.prototype.addSortType("String"); 458// None is a special case
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.