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