PageRenderTime 138ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/files/tablesorter/2.0.5b/jquery.tablesorter.js

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