|
1 /* |
|
2 * |
|
3 * TableSorter 2.0 - Client-side table sorting with ease! |
|
4 * Version 2.0.3 |
|
5 * @requires jQuery v1.2.3 |
|
6 * |
|
7 * Copyright (c) 2007 Christian Bach |
|
8 * Examples and docs at: http://tablesorter.com |
|
9 * Dual licensed under the MIT and GPL licenses: |
|
10 * http://www.opensource.org/licenses/mit-license.php |
|
11 * http://www.gnu.org/licenses/gpl.html |
|
12 * |
|
13 */ |
|
14 /** |
|
15 * |
|
16 * @description Create a sortable table with multi-column sorting capabilitys |
|
17 * |
|
18 * @example $('table').tablesorter(); |
|
19 * @desc Create a simple tablesorter interface. |
|
20 * |
|
21 * @example $('table').tablesorter({ sortList:[[0,0],[1,0]] }); |
|
22 * @desc Create a tablesorter interface and sort on the first and secound column in ascending order. |
|
23 * |
|
24 * @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } }); |
|
25 * @desc Create a tablesorter interface and disableing the first and secound column headers. |
|
26 * |
|
27 * @example $('table').tablesorter({ 0: {sorter:"integer"}, 1: {sorter:"currency"} }); |
|
28 * @desc Create a tablesorter interface and set a column parser for the first and secound column. |
|
29 * |
|
30 * |
|
31 * @param Object settings An object literal containing key/value pairs to provide optional settings. |
|
32 * |
|
33 * @option String cssHeader (optional) A string of the class name to be appended to sortable tr elements in the thead of the table. |
|
34 * Default value: "header" |
|
35 * |
|
36 * @option String cssAsc (optional) A string of the class name to be appended to sortable tr elements in the thead on a ascending sort. |
|
37 * Default value: "headerSortUp" |
|
38 * |
|
39 * @option String cssDesc (optional) A string of the class name to be appended to sortable tr elements in the thead on a descending sort. |
|
40 * Default value: "headerSortDown" |
|
41 * |
|
42 * @option String sortInitialOrder (optional) A string of the inital sorting order can be asc or desc. |
|
43 * Default value: "asc" |
|
44 * |
|
45 * @option String sortMultisortKey (optional) A string of the multi-column sort key. |
|
46 * Default value: "shiftKey" |
|
47 * |
|
48 * @option String textExtraction (optional) A string of the text-extraction method to use. |
|
49 * For complex html structures inside td cell set this option to "complex", |
|
50 * on large tables the complex option can be slow. |
|
51 * Default value: "simple" |
|
52 * |
|
53 * @option Object headers (optional) An array containing the forces sorting rules. |
|
54 * This option let's you specify a default sorting rule. |
|
55 * Default value: null |
|
56 * |
|
57 * @option Array sortList (optional) An array containing the forces sorting rules. |
|
58 * This option let's you specify a default sorting rule. |
|
59 * Default value: null |
|
60 * |
|
61 * @option Array sortForce (optional) An array containing forced sorting rules. |
|
62 * This option let's you specify a default sorting rule, which is prepended to user-selected rules. |
|
63 * Default value: null |
|
64 * |
|
65 * @option Array sortAppend (optional) An array containing forced sorting rules. |
|
66 * This option let's you specify a default sorting rule, which is appended to user-selected rules. |
|
67 * Default value: null |
|
68 * |
|
69 * @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter should apply fixed widths to the table columns. |
|
70 * This is usefull when using the pager companion plugin. |
|
71 * This options requires the dimension jquery plugin. |
|
72 * Default value: false |
|
73 * |
|
74 * @option Boolean cancelSelection (optional) Boolean flag indicating if tablesorter should cancel selection of the table headers text. |
|
75 * Default value: true |
|
76 * |
|
77 * @option Boolean debug (optional) Boolean flag indicating if tablesorter should display debuging information usefull for development. |
|
78 * |
|
79 * @type jQuery |
|
80 * |
|
81 * @name tablesorter |
|
82 * |
|
83 * @cat Plugins/Tablesorter |
|
84 * |
|
85 * @author Christian Bach/christian.bach@polyester.se |
|
86 */ |
|
87 |
|
88 var Sortable = {}; |
|
89 |
|
90 (function($) { |
|
91 $.extend({ |
|
92 tablesorter: new function() { |
|
93 var parsers = [], widgets = []; |
|
94 |
|
95 this.defaults = { |
|
96 cssHeader: "header", |
|
97 cssAsc: "headerSortUp", |
|
98 cssDesc: "headerSortDown", |
|
99 sortInitialOrder: "asc", |
|
100 sortMultiSortKey: "shiftKey", |
|
101 sortForce: null, |
|
102 sortAppend: null, |
|
103 textExtraction: "simple", |
|
104 parsers: {}, |
|
105 widgets: [], |
|
106 widgetZebra: {css: ["even","odd"]}, |
|
107 headers: {}, |
|
108 widthFixed: false, |
|
109 cancelSelection: true, |
|
110 sortList: [], |
|
111 headerList: [], |
|
112 dateFormat: "us", |
|
113 decimal: '.', |
|
114 debug: false |
|
115 }; |
|
116 |
|
117 /* debuging utils */ |
|
118 function benchmark(s,d) { |
|
119 log(s + "," + (new Date().getTime() - d.getTime()) + "ms"); |
|
120 } |
|
121 |
|
122 this.benchmark = benchmark; |
|
123 |
|
124 function log(s) { |
|
125 if (typeof console != "undefined" && typeof console.debug != "undefined") { |
|
126 console.log(s); |
|
127 } else { |
|
128 alert(s); |
|
129 } |
|
130 } |
|
131 |
|
132 /* parsers utils */ |
|
133 function buildParserCache(table,$headers) { |
|
134 |
|
135 if(table.config.debug) { var parsersDebug = ""; } |
|
136 |
|
137 var rows = table.tBodies[0].rows; |
|
138 |
|
139 if(table.tBodies[0].rows[0]) { |
|
140 |
|
141 var list = [], cells = rows[0].cells, l = cells.length; |
|
142 |
|
143 for (var i=0;i < l; i++) { |
|
144 var p = false; |
|
145 |
|
146 if($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter) ) { |
|
147 |
|
148 p = getParserById($($headers[i]).metadata().sorter); |
|
149 |
|
150 } else if((table.config.headers[i] && table.config.headers[i].sorter)) { |
|
151 |
|
152 p = getParserById(table.config.headers[i].sorter); |
|
153 } |
|
154 if(!p) { |
|
155 p = detectParserForColumn(table,cells[i]); |
|
156 } |
|
157 |
|
158 if(table.config.debug) { parsersDebug += "column:" + i + " parser:" +p.id + "\n"; } |
|
159 |
|
160 list.push(p); |
|
161 } |
|
162 } |
|
163 |
|
164 if(table.config.debug) { log(parsersDebug); } |
|
165 |
|
166 return list; |
|
167 }; |
|
168 |
|
169 function detectParserForColumn(table,node) { |
|
170 var l = parsers.length; |
|
171 for(var i=1; i < l; i++) { |
|
172 if(parsers[i].is($.trim(getElementText(table.config,node)),table,node)) { |
|
173 return parsers[i]; |
|
174 } |
|
175 } |
|
176 // 0 is always the generic parser (text) |
|
177 return parsers[0]; |
|
178 } |
|
179 |
|
180 function getParserById(name) { |
|
181 var l = parsers.length; |
|
182 for(var i=0; i < l; i++) { |
|
183 if(parsers[i].id.toLowerCase() == name.toLowerCase()) { |
|
184 return parsers[i]; |
|
185 } |
|
186 } |
|
187 return false; |
|
188 } |
|
189 |
|
190 /* utils */ |
|
191 function buildCache(table) { |
|
192 |
|
193 if(table.config.debug) { var cacheTime = new Date(); } |
|
194 |
|
195 |
|
196 var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0, |
|
197 totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0, |
|
198 parsers = table.config.parsers, |
|
199 cache = {row: [], normalized: []}; |
|
200 |
|
201 for (var i=0;i < totalRows; ++i) { |
|
202 |
|
203 /** Add the table data to main data array */ |
|
204 var c = table.tBodies[0].rows[i], cols = []; |
|
205 |
|
206 cache.row.push($(c)); |
|
207 |
|
208 for(var j=0; j < totalCells; ++j) { |
|
209 cols.push(parsers[j].format(getElementText(table.config,c.cells[j]),table,c.cells[j])); |
|
210 } |
|
211 |
|
212 cols.push(i); // add position for rowCache |
|
213 cache.normalized.push(cols); |
|
214 cols = null; |
|
215 }; |
|
216 |
|
217 if(table.config.debug) { benchmark("Building cache for " + totalRows + " rows:", cacheTime); } |
|
218 |
|
219 return cache; |
|
220 }; |
|
221 |
|
222 function getElementText(config,node) { |
|
223 |
|
224 if(!node) return ""; |
|
225 |
|
226 var t = ""; |
|
227 |
|
228 if(config.textExtraction == "simple") { |
|
229 if(node.childNodes[0] && node.childNodes[0].hasChildNodes()) { |
|
230 t = node.childNodes[0].innerHTML; |
|
231 } else { |
|
232 t = node.innerHTML; |
|
233 } |
|
234 } else { |
|
235 if(typeof(config.textExtraction) == "function") { |
|
236 t = config.textExtraction(node); |
|
237 } else { |
|
238 t = $(node).text(); |
|
239 } |
|
240 } |
|
241 return t; |
|
242 } |
|
243 |
|
244 function appendToTable(table,cache) { |
|
245 |
|
246 if(table.config.debug) {var appendTime = new Date()} |
|
247 |
|
248 var c = cache, |
|
249 r = c.row, |
|
250 n= c.normalized, |
|
251 totalRows = n.length, |
|
252 checkCell = (n[0].length-1), |
|
253 tableBody = $(table.tBodies[0]), |
|
254 rows = []; |
|
255 |
|
256 for (var i=0;i < totalRows; i++) { |
|
257 rows.push(r[n[i][checkCell]]); |
|
258 if(!table.config.appender) { |
|
259 |
|
260 var o = r[n[i][checkCell]]; |
|
261 var l = o.length; |
|
262 for(var j=0; j < l; j++) { |
|
263 |
|
264 tableBody[0].appendChild(o[j]); |
|
265 |
|
266 } |
|
267 |
|
268 //tableBody.append(r[n[i][checkCell]]); |
|
269 } |
|
270 } |
|
271 |
|
272 if(table.config.appender) { |
|
273 |
|
274 table.config.appender(table,rows); |
|
275 } |
|
276 |
|
277 rows = null; |
|
278 |
|
279 if(table.config.debug) { benchmark("Rebuilt table:", appendTime); } |
|
280 |
|
281 //apply table widgets |
|
282 applyWidget(table); |
|
283 |
|
284 // trigger sortend |
|
285 setTimeout(function() { |
|
286 $(table).trigger("sortEnd"); |
|
287 },0); |
|
288 |
|
289 }; |
|
290 |
|
291 function buildHeaders(table) { |
|
292 |
|
293 if(table.config.debug) { var time = new Date(); } |
|
294 |
|
295 var meta = ($.metadata) ? true : false, tableHeadersRows = []; |
|
296 |
|
297 for(var i = 0; i < table.tHead.rows.length; i++) { tableHeadersRows[i]=0; }; |
|
298 |
|
299 $tableHeaders = $("thead th",table); |
|
300 |
|
301 $tableHeaders.each(function(index) { |
|
302 |
|
303 this.count = 0; |
|
304 this.column = index; |
|
305 this.order = formatSortingOrder(table.config.sortInitialOrder); |
|
306 |
|
307 if(checkHeaderMetadata(this) || checkHeaderOptions(table,index)) this.sortDisabled = true; |
|
308 |
|
309 if(!this.sortDisabled) { |
|
310 $(this).addClass(table.config.cssHeader); |
|
311 } |
|
312 |
|
313 // add cell to headerList |
|
314 table.config.headerList[index]= this; |
|
315 }); |
|
316 |
|
317 if(table.config.debug) { benchmark("Built headers:", time); log($tableHeaders); } |
|
318 |
|
319 return $tableHeaders; |
|
320 |
|
321 }; |
|
322 |
|
323 function checkCellColSpan(table, rows, row) { |
|
324 var arr = [], r = table.tHead.rows, c = r[row].cells; |
|
325 |
|
326 for(var i=0; i < c.length; i++) { |
|
327 var cell = c[i]; |
|
328 |
|
329 if ( cell.colSpan > 1) { |
|
330 arr = arr.concat(checkCellColSpan(table, headerArr,row++)); |
|
331 } else { |
|
332 if(table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row+1])) { |
|
333 arr.push(cell); |
|
334 } |
|
335 //headerArr[row] = (i+row); |
|
336 } |
|
337 } |
|
338 return arr; |
|
339 }; |
|
340 |
|
341 function checkHeaderMetadata(cell) { |
|
342 if(($.metadata) && ($(cell).metadata().sorter === false)) { return true; }; |
|
343 return false; |
|
344 } |
|
345 |
|
346 function checkHeaderOptions(table,i) { |
|
347 if((table.config.headers[i]) && (table.config.headers[i].sorter === false)) { return true; }; |
|
348 return false; |
|
349 } |
|
350 |
|
351 function applyWidget(table) { |
|
352 var c = table.config.widgets; |
|
353 var l = c.length; |
|
354 for(var i=0; i < l; i++) { |
|
355 |
|
356 getWidgetById(c[i]).format(table); |
|
357 } |
|
358 |
|
359 } |
|
360 |
|
361 function getWidgetById(name) { |
|
362 var l = widgets.length; |
|
363 for(var i=0; i < l; i++) { |
|
364 if(widgets[i].id.toLowerCase() == name.toLowerCase() ) { |
|
365 return widgets[i]; |
|
366 } |
|
367 } |
|
368 }; |
|
369 |
|
370 function formatSortingOrder(v) { |
|
371 |
|
372 if(typeof(v) != "Number") { |
|
373 i = (v.toLowerCase() == "desc") ? 1 : 0; |
|
374 } else { |
|
375 i = (v == (0 || 1)) ? v : 0; |
|
376 } |
|
377 return i; |
|
378 } |
|
379 |
|
380 function isValueInArray(v, a) { |
|
381 var l = a.length; |
|
382 for(var i=0; i < l; i++) { |
|
383 if(a[i][0] == v) { |
|
384 return true; |
|
385 } |
|
386 } |
|
387 return false; |
|
388 } |
|
389 |
|
390 function setHeadersCss(table,$headers, list, css) { |
|
391 // remove all header information |
|
392 $headers.removeClass(css[0]).removeClass(css[1]); |
|
393 |
|
394 var h = []; |
|
395 $headers.each(function(offset) { |
|
396 if(!this.sortDisabled) { |
|
397 h[this.column] = $(this); |
|
398 } |
|
399 }); |
|
400 |
|
401 var l = list.length; |
|
402 for(var i=0; i < l; i++) { |
|
403 h[list[i][0]].addClass(css[list[i][1]]); |
|
404 } |
|
405 } |
|
406 |
|
407 function fixColumnWidth(table,$headers) { |
|
408 var c = table.config; |
|
409 if(c.widthFixed) { |
|
410 var colgroup = $('<colgroup>'); |
|
411 $("tr:first td",table.tBodies[0]).each(function() { |
|
412 colgroup.append($('<col>').css('width',$(this).width())); |
|
413 }); |
|
414 $(table).prepend(colgroup); |
|
415 }; |
|
416 } |
|
417 |
|
418 function updateHeaderSortCount(table,sortList) { |
|
419 var c = table.config, l = sortList.length; |
|
420 for(var i=0; i < l; i++) { |
|
421 var s = sortList[i], o = c.headerList[s[0]]; |
|
422 o.count = s[1]; |
|
423 o.count++; |
|
424 } |
|
425 } |
|
426 |
|
427 /* sorting methods */ |
|
428 function multisort(table,sortList,cache) { |
|
429 |
|
430 if(table.config.debug) { var sortTime = new Date(); } |
|
431 |
|
432 var dynamicExp = "var sortWrapper = function(a,b) {", l = sortList.length; |
|
433 |
|
434 for(var i=0; i < l; i++) { |
|
435 |
|
436 var c = sortList[i][0]; |
|
437 var order = sortList[i][1]; |
|
438 var s = (getCachedSortType(table.config.parsers,c) == "text") ? ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ? "sortNumeric" : "sortNumericDesc"); |
|
439 var e = "e" + i; |
|
440 dynamicExp += "var " + e + " = " + s + "(a[" + c + "],b[" + c + "]); "; |
|
441 dynamicExp += "if(" + e + ") { return " + e + "; } "; |
|
442 dynamicExp += "else { "; |
|
443 } |
|
444 |
|
445 // if value is the same keep orignal order |
|
446 var orgOrderCol = cache.normalized[0].length - 1; |
|
447 dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];"; |
|
448 |
|
449 for(var i=0; i < l; i++) { |
|
450 dynamicExp += "}; "; |
|
451 } |
|
452 |
|
453 dynamicExp += "return 0; "; |
|
454 dynamicExp += "}; "; |
|
455 |
|
456 eval(dynamicExp); |
|
457 |
|
458 cache.normalized.sort(sortWrapper); |
|
459 |
|
460 if(table.config.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order+ " time:", sortTime); } |
|
461 |
|
462 return cache; |
|
463 }; |
|
464 |
|
465 function sortText(a,b) { |
|
466 return ((a < b) ? -1 : ((a > b) ? 1 : 0)); |
|
467 }; |
|
468 |
|
469 function sortTextDesc(a,b) { |
|
470 return ((b < a) ? -1 : ((b > a) ? 1 : 0)); |
|
471 }; |
|
472 |
|
473 function sortNumeric(a,b) { |
|
474 return a-b; |
|
475 }; |
|
476 |
|
477 function sortNumericDesc(a,b) { |
|
478 return b-a; |
|
479 }; |
|
480 |
|
481 function getCachedSortType(parsers,i) { |
|
482 return parsers[i].type; |
|
483 }; |
|
484 |
|
485 /* public methods */ |
|
486 this.construct = function(settings) { |
|
487 |
|
488 return this.each(function() { |
|
489 |
|
490 if(!this.tHead || !this.tBodies) return; |
|
491 |
|
492 var $this, $document,$headers, cache, config, shiftDown = 0, sortOrder; |
|
493 |
|
494 this.config = {}; |
|
495 |
|
496 config = $.extend(this.config, $.tablesorter.defaults, settings); |
|
497 |
|
498 // store common expression for speed |
|
499 $this = $(this); |
|
500 |
|
501 // build headers |
|
502 $headers = buildHeaders(this); |
|
503 |
|
504 // try to auto detect column type, and store in tables config |
|
505 this.config.parsers = buildParserCache(this,$headers); |
|
506 |
|
507 // build the cache for the tbody cells |
|
508 cache = buildCache(this); |
|
509 |
|
510 // get the css class names, could be done else where. |
|
511 var sortCSS = [config.cssDesc,config.cssAsc]; |
|
512 |
|
513 // fixate columns if the users supplies the fixedWidth option |
|
514 fixColumnWidth(this); |
|
515 |
|
516 // apply event handling to headers |
|
517 // this is to big, perhaps break it out? |
|
518 $headers.click(function(e) { |
|
519 |
|
520 $this.trigger("sortStart"); |
|
521 |
|
522 var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0; |
|
523 |
|
524 if(!this.sortDisabled && totalRows > 0) { |
|
525 |
|
526 |
|
527 // store exp, for speed |
|
528 var $cell = $(this); |
|
529 |
|
530 // get current column index |
|
531 var i = this.column; |
|
532 |
|
533 // get current column sort order |
|
534 this.order = this.count++ % 2; |
|
535 |
|
536 // user only whants to sort on one column |
|
537 if(!e[config.sortMultiSortKey]) { |
|
538 |
|
539 // flush the sort list |
|
540 config.sortList = []; |
|
541 |
|
542 if(config.sortForce != null) { |
|
543 var a = config.sortForce; |
|
544 for(var j=0; j < a.length; j++) { |
|
545 if(a[j][0] != i) { |
|
546 config.sortList.push(a[j]); |
|
547 } |
|
548 } |
|
549 } |
|
550 |
|
551 // add column to sort list |
|
552 config.sortList.push([i,this.order]); |
|
553 |
|
554 // multi column sorting |
|
555 } else { |
|
556 // the user has clicked on an all ready sortet column. |
|
557 if(isValueInArray(i,config.sortList)) { |
|
558 |
|
559 // revers the sorting direction for all tables. |
|
560 for(var j=0; j < config.sortList.length; j++) { |
|
561 var s = config.sortList[j], o = config.headerList[s[0]]; |
|
562 if(s[0] == i) { |
|
563 o.count = s[1]; |
|
564 o.count++; |
|
565 s[1] = o.count % 2; |
|
566 } |
|
567 } |
|
568 } else { |
|
569 // add column to sort list array |
|
570 config.sortList.push([i,this.order]); |
|
571 } |
|
572 }; |
|
573 setTimeout(function() { |
|
574 //set css for headers |
|
575 setHeadersCss($this[0],$headers,config.sortList,sortCSS); |
|
576 appendToTable($this[0],multisort($this[0],config.sortList,cache)); |
|
577 },1); |
|
578 // stop normal event by returning false |
|
579 return false; |
|
580 } |
|
581 // cancel selection |
|
582 }).mousedown(function() { |
|
583 if(config.cancelSelection) { |
|
584 this.onselectstart = function() {return false}; |
|
585 return false; |
|
586 } |
|
587 }); |
|
588 |
|
589 // apply easy methods that trigger binded events |
|
590 $this.bind("update",function() { |
|
591 |
|
592 // rebuild parsers. |
|
593 this.config.parsers = buildParserCache(this,$headers); |
|
594 |
|
595 // rebuild the cache map |
|
596 cache = buildCache(this); |
|
597 |
|
598 }).bind("sorton",function(e,list) { |
|
599 |
|
600 $(this).trigger("sortStart"); |
|
601 |
|
602 config.sortList = list; |
|
603 |
|
604 // update and store the sortlist |
|
605 var sortList = config.sortList; |
|
606 |
|
607 // update header count index |
|
608 updateHeaderSortCount(this,sortList); |
|
609 |
|
610 //set css for headers |
|
611 setHeadersCss(this,$headers,sortList,sortCSS); |
|
612 |
|
613 |
|
614 // sort the table and append it to the dom |
|
615 appendToTable(this,multisort(this,sortList,cache)); |
|
616 |
|
617 }).bind("appendCache",function() { |
|
618 |
|
619 appendToTable(this,cache); |
|
620 |
|
621 }).bind("applyWidgetId",function(e,id) { |
|
622 |
|
623 getWidgetById(id).format(this); |
|
624 |
|
625 }).bind("applyWidgets",function() { |
|
626 // apply widgets |
|
627 applyWidget(this); |
|
628 }); |
|
629 |
|
630 if($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) { |
|
631 config.sortList = $(this).metadata().sortlist; |
|
632 } |
|
633 // if user has supplied a sort list to constructor. |
|
634 if(config.sortList.length > 0) { |
|
635 $this.trigger("sorton",[config.sortList]); |
|
636 } |
|
637 |
|
638 // apply widgets |
|
639 applyWidget(this); |
|
640 }); |
|
641 }; |
|
642 |
|
643 this.addParser = function(parser) { |
|
644 var l = parsers.length, a = true; |
|
645 for(var i=0; i < l; i++) { |
|
646 if(parsers[i].id.toLowerCase() == parser.id.toLowerCase()) { |
|
647 a = false; |
|
648 } |
|
649 } |
|
650 if(a) { parsers.push(parser); }; |
|
651 }; |
|
652 |
|
653 this.addWidget = function(widget) { |
|
654 widgets.push(widget); |
|
655 }; |
|
656 |
|
657 this.formatFloat = function(s) { |
|
658 var i = parseFloat(s); |
|
659 return (isNaN(i)) ? 0 : i; |
|
660 }; |
|
661 this.formatInt = function(s) { |
|
662 var i = parseInt(s); |
|
663 return (isNaN(i)) ? 0 : i; |
|
664 }; |
|
665 |
|
666 this.isDigit = function(s,config) { |
|
667 var DECIMAL = '\\' + config.decimal; |
|
668 var exp = '/(^[+]?0(' + DECIMAL +'0+)?$)|(^([-+]?[1-9][0-9]*)$)|(^([-+]?((0?|[1-9][0-9]*)' + DECIMAL +'(0*[1-9][0-9]*)))$)|(^[-+]?[1-9]+[0-9]*' + DECIMAL +'0+$)/'; |
|
669 return RegExp(exp).test($.trim(s)); |
|
670 }; |
|
671 |
|
672 this.clearTableBody = function(table) { |
|
673 if($.browser.msie) { |
|
674 function empty() { |
|
675 while ( this.firstChild ) this.removeChild( this.firstChild ); |
|
676 } |
|
677 empty.apply(table.tBodies[0]); |
|
678 } else { |
|
679 table.tBodies[0].innerHTML = ""; |
|
680 } |
|
681 }; |
|
682 } |
|
683 }); |
|
684 |
|
685 // extend plugin scope |
|
686 $.fn.extend({ |
|
687 tablesorter: $.tablesorter.construct |
|
688 }); |
|
689 |
|
690 var ts = $.tablesorter; |
|
691 |
|
692 // add default parsers |
|
693 ts.addParser({ |
|
694 id: "text", |
|
695 is: function(s) { |
|
696 return true; |
|
697 }, |
|
698 format: function(s) { |
|
699 return $.trim(s.toLowerCase()); |
|
700 }, |
|
701 type: "text" |
|
702 }); |
|
703 |
|
704 |
|
705 ts.addParser({ |
|
706 id: "json", |
|
707 is: function(s) { |
|
708 return s.startsWith('json:'); |
|
709 }, |
|
710 format: function(s,table,cell) { |
|
711 return evalJSON(s.slice(5)); |
|
712 }, |
|
713 type: "text" |
|
714 }); |
|
715 |
|
716 ts.addParser({ |
|
717 id: "digit", |
|
718 is: function(s,table) { |
|
719 var c = table.config; |
|
720 return $.tablesorter.isDigit(s,c); |
|
721 }, |
|
722 format: function(s) { |
|
723 return $.tablesorter.formatFloat(s); |
|
724 }, |
|
725 type: "numeric" |
|
726 }); |
|
727 |
|
728 ts.addParser({ |
|
729 id: "currency", |
|
730 is: function(s) { |
|
731 return /^[£$€?.]/.test(s); |
|
732 }, |
|
733 format: function(s) { |
|
734 return $.tablesorter.formatFloat(s.replace(new RegExp(/[^0-9.]/g),"")); |
|
735 }, |
|
736 type: "numeric" |
|
737 }); |
|
738 |
|
739 ts.addParser({ |
|
740 id: "ipAddress", |
|
741 is: function(s) { |
|
742 return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s); |
|
743 }, |
|
744 format: function(s) { |
|
745 var a = s.split("."), r = "", l = a.length; |
|
746 for(var i = 0; i < l; i++) { |
|
747 var item = a[i]; |
|
748 if(item.length == 2) { |
|
749 r += "0" + item; |
|
750 } else { |
|
751 r += item; |
|
752 } |
|
753 } |
|
754 return $.tablesorter.formatFloat(r); |
|
755 }, |
|
756 type: "numeric" |
|
757 }); |
|
758 |
|
759 ts.addParser({ |
|
760 id: "url", |
|
761 is: function(s) { |
|
762 return /^(https?|ftp|file):\/\/$/.test(s); |
|
763 }, |
|
764 format: function(s) { |
|
765 return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),'')); |
|
766 }, |
|
767 type: "text" |
|
768 }); |
|
769 |
|
770 ts.addParser({ |
|
771 id: "isoDate", |
|
772 is: function(s) { |
|
773 return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s); |
|
774 }, |
|
775 format: function(s) { |
|
776 return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(new RegExp(/-/g),"/")).getTime() : "0"); |
|
777 }, |
|
778 type: "numeric" |
|
779 }); |
|
780 |
|
781 ts.addParser({ |
|
782 id: "percent", |
|
783 is: function(s) { |
|
784 return /\%$/.test($.trim(s)); |
|
785 }, |
|
786 format: function(s) { |
|
787 return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),"")); |
|
788 }, |
|
789 type: "numeric" |
|
790 }); |
|
791 |
|
792 ts.addParser({ |
|
793 id: "usLongDate", |
|
794 is: function(s) { |
|
795 return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/)); //' |
|
796 }, |
|
797 format: function(s) { |
|
798 return $.tablesorter.formatFloat(new Date(s).getTime()); |
|
799 }, |
|
800 type: "numeric" |
|
801 }); |
|
802 |
|
803 ts.addParser({ |
|
804 id: "shortDate", |
|
805 is: function(s) { |
|
806 return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s); |
|
807 }, |
|
808 format: function(s,table) { |
|
809 var c = table.config; |
|
810 s = s.replace(/\-/g,"/"); |
|
811 if(c.dateFormat == "us") { |
|
812 // reformat the string in ISO format |
|
813 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2"); |
|
814 } else if(c.dateFormat == "uk") { |
|
815 //reformat the string in ISO format |
|
816 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1"); |
|
817 } else if(c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") { |
|
818 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3"); |
|
819 } |
|
820 return $.tablesorter.formatFloat(new Date(s).getTime()); |
|
821 }, |
|
822 type: "numeric" |
|
823 }); |
|
824 |
|
825 ts.addParser({ |
|
826 id: "time", |
|
827 is: function(s) { |
|
828 return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s); |
|
829 }, |
|
830 format: function(s) { |
|
831 return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime()); |
|
832 }, |
|
833 type: "numeric" |
|
834 }); |
|
835 |
|
836 |
|
837 ts.addParser({ |
|
838 id: "metadata", |
|
839 is: function(s) { |
|
840 return false; |
|
841 }, |
|
842 format: function(s,table,cell) { |
|
843 var c = table.config, p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName; |
|
844 return $(cell).metadata()[p]; |
|
845 }, |
|
846 type: "numeric" |
|
847 }); |
|
848 |
|
849 |
|
850 // add default widgets |
|
851 ts.addWidget({ |
|
852 id: "zebra", |
|
853 format: function(table) { |
|
854 if(table.config.debug) { var time = new Date(); } |
|
855 $("tr:visible",table.tBodies[0]) |
|
856 .filter(':even') |
|
857 .removeClass(table.config.widgetZebra.css[1]).addClass(table.config.widgetZebra.css[0]) |
|
858 .end().filter(':odd') |
|
859 .removeClass(table.config.widgetZebra.css[0]).addClass(table.config.widgetZebra.css[1]); |
|
860 if(table.config.debug) { $.tablesorter.benchmark("Applying Zebra widget", time); } |
|
861 } |
|
862 }); |
|
863 })(jQuery); |
|
864 |
|
865 |
|
866 function cubicwebSortValueExtraction(node){ |
|
867 return jQuery(node).attr('cubicweb:sortvalue'); |
|
868 } |
|
869 |
|
870 Sortable.sortTables = function() { |
|
871 jQuery("table.listing").tablesorter({textExtraction: cubicwebSortValueExtraction}); |
|
872 } |
|
873 |
|
874 jQuery(document).ready(Sortable.sortTables); |