--- untitled.txt (saved version) +++ (current document) @@ -1,11 +1,10 @@ /* * - * TableSorter 2.0 - Client-side table sorting with ease! - * Version 2.0.5b - * @requires jQuery v1.2.3 + * TableSorter for MediaWiki * - * Copyright (c) 2007 Christian Bach - * Examples and docs at: http://tablesorter.com + * Written 2011 Leo Koppelkamm + * Based on tablesorter.com plugin, written (c) 2007 Christian Bach. + * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html @@ -17,26 +16,7 @@ * * @example $('table').tablesorter(); * @desc Create a simple tablesorter interface. - * - * @example $('table').tablesorter({ sortList:[[0,0],[1,0]] }); - * @desc Create a tablesorter interface and sort on the first and secound column column headers. - * - * @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } }); - * - * @desc Create a tablesorter interface and disableing the first and second column headers. - * - * - * @example $('table').tablesorter({ headers: { 0: {sorter:"integer"}, 1: {sorter:"currency"} } }); - * - * @desc Create a tablesorter interface and set a column parser for the first - * and second column. - * - * - * @param Object - * settings An object literal containing key/value pairs to provide - * optional settings. - * - * + * * @option String cssHeader (optional) A string of the class name to be appended * to sortable tr elements in the thead of the table. Default value: * "header" @@ -54,37 +34,10 @@ * * @option String sortMultisortKey (optional) A string of the multi-column sort * key. Default value: "shiftKey" - * - * @option String textExtraction (optional) A string of the text-extraction - * method to use. For complex html structures inside td cell set this - * option to "complex", on large tables the complex option can be slow. - * Default value: "simple" - * - * @option Object headers (optional) An array containing the forces sorting - * rules. This option let's you specify a default sorting rule. Default - * value: null - * - * @option Array sortList (optional) An array containing the forces sorting - * rules. This option let's you specify a default sorting rule. Default - * value: null - * - * @option Array sortForce (optional) An array containing forced sorting rules. - * This option let's you specify a default sorting rule, which is - * prepended to user-selected rules. Default value: null - * + * * @option Boolean sortLocaleCompare (optional) Boolean flag indicating whatever - * to use String.localeCampare method or not. Default set to true. - * - * - * @option Array sortAppend (optional) An array containing forced sorting rules. - * This option let's you specify a default sorting rule, which is - * appended to user-selected rules. Default value: null - * - * @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter - * should apply fixed widths to the table columns. This is usefull when - * using the pager companion plugin. This options requires the dimension - * jquery plugin. Default value: false - * + * to use String.localeCampare method or not. Set to false. + * * @option Boolean cancelSelection (optional) Boolean flag indicating if * tablesorter should cancel selection of the table headers text. * Default value: true @@ -107,62 +60,38 @@ function () { - var parsers = [], - widgets = []; + var parsers = []; this.defaults = { - cssHeader: "header", + cssHeader: "headerSort", cssAsc: "headerSortUp", cssDesc: "headerSortDown", cssChildRow: "expand-child", sortInitialOrder: "asc", sortMultiSortKey: "shiftKey", - sortForce: null, - sortAppend: null, - sortLocaleCompare: true, - textExtraction: "simple", + sortLocaleCompare: false, parsers: {}, widgets: [], - widgetZebra: { - css: ["even", "odd"] - }, headers: {}, - widthFixed: false, cancelSelection: true, sortList: [], headerList: [], - dateFormat: "us", - decimal: '/\.|\,/g', - onRenderHeader: null, selectorHeaders: 'thead th', debug: false }; /* debuging utils */ - - function benchmark(s, d) { - log(s + "," + (new Date().getTime() - d.getTime()) + "ms"); - } - - this.benchmark = benchmark; - - function log(s) { - if (typeof console != "undefined" && typeof console.debug != "undefined") { - console.log(s); - } else { - alert(s); - } - } - + // + // function benchmark(s, d) { + // alert(s + "," + (new Date().getTime() - d.getTime()) + "ms"); + // } + // + // this.benchmark = benchmark; + // + // /* parsers utils */ function buildParserCache(table, $headers) { - - if (table.config.debug) { - var parsersDebug = ""; - } - - if (table.tBodies.length == 0) return; // In the case of empty tables var rows = table.tBodies[0].rows; if (rows[0]) { @@ -173,35 +102,20 @@ for (var i = 0; i < l; i++) { - var p = false; - - if ($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter)) { - - p = getParserById($($headers[i]).metadata().sorter); - - } else if ((table.config.headers[i] && table.config.headers[i].sorter)) { - - p = getParserById(table.config.headers[i].sorter); - } - if (!p) { - + if ($headers.eq(i).is('[class*="sort-"]')) { + p = getParserById($headers.eq(i).attr('class').replace(/.*?sort-(.*?) .*/, '$1')); + } else { p = detectParserForColumn(table, rows, -1, i); } - if (table.config.debug) { - parsersDebug += "column:" + i + " parser:" + p.id + "\n"; - } - + // if (table.config.debug) { + // console.log("column:" + i + " parser:" + p.id + "\n"); + // } list.push(p); } } - - if (table.config.debug) { - log(parsersDebug); - } - return list; - }; + } function detectParserForColumn(table, rows, rowIndex, cellIndex) { var l = parsers.length, @@ -213,9 +127,9 @@ if (rows[rowIndex]) { node = getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex); nodeValue = trimAndGetNodeText(table.config, node); - if (table.config.debug) { - log('Checking if value was empty on row:' + rowIndex); - } + // if (table.config.debug) { + // console.log('Checking if value was empty on row:' + rowIndex); + // } } else { keepLooking = false; } @@ -251,10 +165,9 @@ function buildCache(table) { - if (table.config.debug) { - var cacheTime = new Date(); - } - + // if (table.config.debug) { + // var cacheTime = new Date(); + // } var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0, totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0, parsers = table.config.parsers, @@ -265,7 +178,7 @@ for (var i = 0; i < totalRows; ++i) { - /** Add the table data to main data array */ + // Add the table data to main data array var c = $(table.tBodies[0].rows[i]), cols = []; @@ -286,248 +199,165 @@ cols.push(cache.normalized.length); // add position for rowCache cache.normalized.push(cols); cols = null; - }; - - if (table.config.debug) { - benchmark("Building cache for " + totalRows + " rows:", cacheTime); } + // if (table.config.debug) { + // benchmark("Building cache for " + totalRows + " rows:", cacheTime); + // } return cache; - }; + } function getElementText(config, node) { - - var text = ""; - - if (!node) return ""; - - if (!config.supportsTextContent) config.supportsTextContent = node.textContent || false; - - if (config.textExtraction == "simple") { - if (config.supportsTextContent) { - text = node.textContent; - } else { - if (node.childNodes[0] && node.childNodes[0].hasChildNodes()) { - text = node.childNodes[0].innerHTML; - } else { - text = node.innerHTML; - } - } - } else { - if (typeof(config.textExtraction) == "function") { - text = config.textExtraction(node); - } else { - text = $(node).text(); - } - } - return text; + return $(node).text(); } function appendToTable(table, cache) { - if (table.config.debug) { - var appendTime = new Date() - } - + // if (table.config.debug) { + // var appendTime = new Date() + // } var c = cache, r = c.row, n = c.normalized, totalRows = n.length, checkCell = (n[0].length - 1), tableBody = $(table.tBodies[0]), - rows = []; - + fragment = document.createDocumentFragment(); for (var i = 0; i < totalRows; i++) { var pos = n[i][checkCell]; - rows.push(r[pos]); - - if (!table.config.appender) { - - //var o = ; - var l = r[pos].length; - for (var j = 0; j < l; j++) { - tableBody[0].appendChild(r[pos][j]); - } + var l = r[pos].length; - // + for (var j = 0; j < l; j++) { + fragment.appendChild(r[pos][j]); } - } - - - if (table.config.appender) { - - table.config.appender(table, rows); } - - rows = null; - - if (table.config.debug) { - benchmark("Rebuilt table:", appendTime); - } - - // apply table widgets - applyWidget(table); - - // trigger sortend - setTimeout(function () { - $(table).trigger("sortEnd"); - }, 0); - - }; + tableBody[0].appendChild(fragment); + // if (table.config.debug) { + // benchmark("Rebuilt table:", appendTime); + // } + } function buildHeaders(table) { - if (table.config.debug) { - var time = new Date(); - } - - var meta = ($.metadata) ? true : false; - - var header_index = computeTableHeaderCellIndexes(table); + // if (table.config.debug) { + // var time = new Date(); + // } + //var header_index = computeTableHeaderCellIndexes(table); + var realCellIndex = 0; $tableHeaders = $(table.config.selectorHeaders, table).each(function (index) { + //var normalIndex = allCells.index(this); + //var realCellIndex = 0; + this.column = realCellIndex; + + var colspan = this.colspan; + colspan = colspan ? parseInt(colspan) : 1; + realCellIndex += colspan; + + //this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex]; + this.order = 0; + this.count = 0; - this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex]; - // this.column = index; - this.order = formatSortingOrder(table.config.sortInitialOrder); - - - this.count = this.order; - - if (checkHeaderMetadata(this) || checkHeaderOptions(table, index)) this.sortDisabled = true; - if (checkHeaderOptionsSortingLocked(table, index)) this.order = this.lockedOrder = checkHeaderOptionsSortingLocked(table, index); + if ($(this).is('.unsortable')) this.sortDisabled = true; if (!this.sortDisabled) { var $th = $(this).addClass(table.config.cssHeader); - if (table.config.onRenderHeader) table.config.onRenderHeader.apply($th); + //if (table.config.onRenderHeader) table.config.onRenderHeader.apply($th); } // add cell to headerList table.config.headerList[index] = this; }); - if (table.config.debug) { - benchmark("Built headers:", time); - log($tableHeaders); - } - + // if (table.config.debug) { + // benchmark("Built headers:", time); + // console.log($tableHeaders); + // } + // return $tableHeaders; - }; - - // from: - // http://www.javascripttoolbox.com/lib/table/examples.php - // http://www.javascripttoolbox.com/temp/table_cellindex.html - - function computeTableHeaderCellIndexes(t) { - var matrix = []; - var lookup = {}; - var thead = t.getElementsByTagName('THEAD')[0]; - var trs = thead.getElementsByTagName('TR'); - - for (var i = 0; i < trs.length; i++) { - var cells = trs[i].cells; - for (var j = 0; j < cells.length; j++) { - var c = cells[j]; - - var rowIndex = c.parentNode.rowIndex; - var cellId = rowIndex + "-" + c.cellIndex; - var rowSpan = c.rowSpan || 1; - var colSpan = c.colSpan || 1 - var firstAvailCol; - if (typeof(matrix[rowIndex]) == "undefined") { - matrix[rowIndex] = []; - } - // Find first available column in the first row - for (var k = 0; k < matrix[rowIndex].length + 1; k++) { - if (typeof(matrix[rowIndex][k]) == "undefined") { - firstAvailCol = k; - break; - } - } - lookup[cellId] = firstAvailCol; - for (var k = rowIndex; k < rowIndex + rowSpan; k++) { - if (typeof(matrix[k]) == "undefined") { - matrix[k] = []; - } - var matrixrow = matrix[k]; - for (var l = firstAvailCol; l < firstAvailCol + colSpan; l++) { - matrixrow[l] = "x"; - } - } - } - } - return lookup; } - function checkCellColSpan(table, rows, row) { - var arr = [], - r = table.tHead.rows, - c = r[row].cells; - - for (var i = 0; i < c.length; i++) { - var cell = c[i]; - - if (cell.colSpan > 1) { - arr = arr.concat(checkCellColSpan(table, headerArr, row++)); - } else { - if (table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row + 1])) { - arr.push(cell); - } - // headerArr[row] = (i+row); - } - } - return arr; - }; - - function checkHeaderMetadata(cell) { - if (($.metadata) && ($(cell).metadata().sorter === false)) { - return true; - }; - return false; - } - - function checkHeaderOptions(table, i) { - if ((table.config.headers[i]) && (table.config.headers[i].sorter === false)) { - return true; - }; - return false; - } - - function checkHeaderOptionsSortingLocked(table, i) { - if ((table.config.headers[i]) && (table.config.headers[i].lockedOrder)) return table.config.headers[i].lockedOrder; - return false; - } - - function applyWidget(table) { - var c = table.config.widgets; - var l = c.length; - for (var i = 0; i < l; i++) { - - getWidgetById(c[i]).format(table); - } - - } - - function getWidgetById(name) { - var l = widgets.length; - for (var i = 0; i < l; i++) { - if (widgets[i].id.toLowerCase() == name.toLowerCase()) { - return widgets[i]; - } - } - }; - - function formatSortingOrder(v) { - if (typeof(v) != "Number") { - return (v.toLowerCase() == "desc") ? 1 : 0; - } else { - return (v == 1) ? 1 : 0; - } - } + // // from: + // // http://www.javascripttoolbox.com/lib/table/examples.php + // // http://www.javascripttoolbox.com/temp/table_cellindex.html + // + // function computeTableHeaderCellIndexes(t) { + // var matrix = []; + // var lookup = {}; + // var thead = t.getElementsByTagName('THEAD')[0]; + // var trs = thead.getElementsByTagName('TR'); + // + // for (var i = 0; i < trs.length; i++) { + // var cells = trs[i].cells; + // for (var j = 0; j < cells.length; j++) { + // var c = cells[j]; + // + // var rowIndex = c.parentNode.rowIndex; + // var cellId = rowIndex + "-" + c.cellIndex; + // var rowSpan = c.rowSpan || 1; + // var colSpan = c.colSpan || 1; + // var firstAvailCol; + // if (typeof(matrix[rowIndex]) == "undefined") { + // matrix[rowIndex] = []; + // } + // // Find first available column in the first row + // for (var k = 0; k < matrix[rowIndex].length + 1; k++) { + // if (typeof(matrix[rowIndex][k]) == "undefined") { + // firstAvailCol = k; + // break; + // } + // } + // lookup[cellId] = firstAvailCol; + // for (var k = rowIndex; k < rowIndex + rowSpan; k++) { + // if (typeof(matrix[k]) == "undefined") { + // matrix[k] = []; + // } + // var matrixrow = matrix[k]; + // for (var l = firstAvailCol; l < firstAvailCol + colSpan; l++) { + // matrixrow[l] = "x"; + // } + // } + // } + // } + // return lookup; + // } + // function checkCellColSpan(table, rows, row) { + // var arr = [], + // r = table.tHead.rows, + // c = r[row].cells; + // + // for (var i = 0; i < c.length; i++) { + // var cell = c[i]; + // + // if (cell.colSpan > 1) { + // arr = arr.concat(checkCellColSpan(table, headerArr, row++)); + // } else { + // if (table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row + 1])) { + // arr.push(cell); + // } + // // headerArr[row] = (i+row); + // } + // } + // return arr; + // } + // + // function checkHeaderOptions(table, i) { + // if ((table.config.headers[i]) && (table.config.headers[i].sorter === false)) { + // return true; + // } + // return false; + // } + // function formatSortingOrder(v) { + // if (typeof(v) != "Number") { + // return (v.toLowerCase() == "desc") ? 1 : 0; + // } else { + // return (v == 1) ? 1 : 0; + // } + // } function isValueInArray(v, a) { var l = a.length; @@ -556,36 +386,22 @@ } } - function fixColumnWidth(table, $headers) { - var c = table.config; - if (c.widthFixed) { - var colgroup = $(''); - $("tr:first td", table.tBodies[0]).each(function () { - colgroup.append($('').css('width', $(this).width())); - }); - $(table).prepend(colgroup); - }; - } - - function updateHeaderSortCount(table, sortList) { - var c = table.config, - l = sortList.length; - for (var i = 0; i < l; i++) { - var s = sortList[i], - o = c.headerList[s[0]]; - o.count = s[1]; - o.count++; - } - } - + // function updateHeaderSortCount(table, sortList) { + // var c = table.config, + // l = sortList.length; + // for (var i = 0; i < l; i++) { + // var s = sortList[i], + // o = c.headerList[s[0]]; + // o.count = s[1]; + // o.count++; + // } + // } /* sorting methods */ function multisort(table, sortList, cache) { - - if (table.config.debug) { - var sortTime = new Date(); - } - + // if (table.config.debug) { + // var sortTime = new Date(); + // } var dynamicExp = "var sortWrapper = function(a,b) {", l = sortList.length; @@ -610,7 +426,7 @@ } - // if value is the same keep orignal order + // if value is the same keep original order var orgOrderCol = cache.normalized[0].length - 1; dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];"; @@ -621,20 +437,18 @@ dynamicExp += "return 0; "; dynamicExp += "}; "; - if (table.config.debug) { - benchmark("Evaling expression:" + dynamicExp, new Date()); - } - + // if (table.config.debug) { + // benchmark("Evaling expression:" + dynamicExp, new Date()); + // } eval(dynamicExp); cache.normalized.sort(sortWrapper); - if (table.config.debug) { - benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time:", sortTime); - } - + // if (table.config.debug) { + // benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time:", sortTime); + // } return cache; - }; + } function makeSortFunction(type, direction, index) { var a = "a[" + index + "]", @@ -648,45 +462,124 @@ } else if (type == 'numeric' && direction == 'desc') { return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + b + " - " + a + "));"; } - }; + } function makeSortText(i) { return "((a[" + i + "] < b[" + i + "]) ? -1 : ((a[" + i + "] > b[" + i + "]) ? 1 : 0));"; - }; + } function makeSortTextDesc(i) { return "((b[" + i + "] < a[" + i + "]) ? -1 : ((b[" + i + "] > a[" + i + "]) ? 1 : 0));"; - }; + } function makeSortNumeric(i) { return "a[" + i + "]-b[" + i + "];"; - }; + } function makeSortNumericDesc(i) { return "b[" + i + "]-a[" + i + "];"; - }; + } function sortText(a, b) { if (table.config.sortLocaleCompare) return a.localeCompare(b); return ((a < b) ? -1 : ((a > b) ? 1 : 0)); - }; + } function sortTextDesc(a, b) { if (table.config.sortLocaleCompare) return b.localeCompare(a); return ((b < a) ? -1 : ((b > a) ? 1 : 0)); - }; + } function sortNumeric(a, b) { return a - b; - }; + } function sortNumericDesc(a, b) { return b - a; - }; + } function getCachedSortType(parsers, i) { return parsers[i].type; - }; /* public methods */ + } + + function buildTransformTable() { + var digits = '0123456789,.'.split(''); + + if (typeof wgSeparatorTransformTable == 'undefined' || (wgSeparatorTransformTable[0] == '' && wgDigitTransformTable[2] == '')) { + ts.transformTable = false; + } else { + ts.transformTable = {}; + + // Unpack the transform table + var ascii = wgSeparatorTransformTable[0].split("\t").concat(wgDigitTransformTable[0].split("\t")); + var localised = wgSeparatorTransformTable[1].split("\t").concat(wgDigitTransformTable[1].split("\t")); + + // Construct regex for number identification + for (var i = 0; i < ascii.length; i++) { + ts.transformTable[localised[i]] = ascii[i]; + digits.push($.escapeRE(localised[i])); + } + } + var digitClass = '[' + digits.join('', digits) + ']'; + + // We allow a trailing percent sign, which we just strip. This works fine + // if percents and regular numbers aren't being mixed. + ts.numberRegex = new RegExp("^(" + "[-+\u2212]?[0-9][0-9,]*(\\.[0-9,]*)?(E[-+\u2212]?[0-9][0-9,]*)?" + // Fortran-style scientific + "|" + "[-+\u2212]?" + digitClass + "+%?" + // Generic localised + ")$", "i"); + } + + function buildDateTable() { + var r = ''; + ts.monthNames = [[],[]]; + ts.dateRegex = []; + + for (i = 1; i < 13; i++) { + ts.monthNames[0][i] = wgMonthNames[i].toLowerCase(); + ts.monthNames[1][i] = wgMonthNamesShort[i].toLowerCase().replace('.', ''); + r += $.escapeRE(ts.monthNames[0][i]) + '|'; + r += $.escapeRE(ts.monthNames[1][i]) + '|'; + } + + //Remove trailing pipe + r = r.slice(0, -1); + + //Build RegEx + //Any date formated with . , ' - or / + ts.dateRegex[0] = new RegExp(/^\s*\d{1,2}[\,\.\-\/'\s]*\d{1,2}[\,\.\-\/'\s]*\d{2,4}\s*?/i); + + //Written Month name, dmy + ts.dateRegex[1] = new RegExp('^\\s*\\d{1,2}[\\,\\.\\-\\/\'\\s]*(' + r + ')' + '[\\,\\.\\-\\/\'\\s]*\\d{2,4}\\s*$', 'i'); + + //Written Month name, mdy + ts.dateRegex[2] = new RegExp('^\\s*(' + r + ')' + '[\\,\\.\\-\\/\'\\s]*\\d{1,2}[\\,\\.\\-\\/\'\\s]*\\d{2,4}\\s*$', 'i'); + + } + + function buildCollationTable() { + if (typeof tableSorterCollation == "object") { + ts.collationRegex = []; + + //Build array of key names + for (var key in tableSorterCollation) { + if (tableSorterCollation.hasOwnProperty(key)) { //to be safe + ts.collationRegex.push(key); + } + } + ts.collationRegex = new RegExp('[' + ts.collationRegex.join('') + ']', 'ig'); + } + } + + function cacheRegexs() { + ts.rgx = { + IPAddress: [new RegExp(/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/)], + currency: [new RegExp(/^[£$€?.]/), new RegExp(/[£$€]/g)], + url: [new RegExp(/^(https?|ftp|file):\/\/$/), new RegExp(/(https?|ftp|file):\/\//)], + isoDate: [new RegExp(/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/)], + usLongDate: [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)))$/)], + time: [new RegExp(/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/)] + }; + } /* public methods */ this.construct = function (settings) { return this.each(function () { // if no thead or tbody quit. @@ -694,29 +587,35 @@ // declare var $this, $document, $headers, cache, config, shiftDown = 0, sortOrder; - // new blank config object - this.config = {}; // merge and extend. - config = $.extend(this.config, $.tablesorter.defaults, settings); + config = this.config = $.tablesorter.defaults; // store common expression for speed $this = $(this); // save the settings where they read $.data(this, "tablesorter", config); // build headers $headers = buildHeaders(this); + // Grab and process locale settings + buildTransformTable(); + buildDateTable(); + buildCollationTable(); + + //Precaching regexps can bring 10 fold + //performance improvements in some browsers + cacheRegexs(); + // try to auto detect column type, and store in tables config this.config.parsers = buildParserCache(this, $headers); // build the cache for the tbody cells cache = buildCache(this); // get the css class names, could be done else where. var sortCSS = [config.cssDesc, config.cssAsc]; - // fixate columns if the users supplies the fixedWidth option - fixColumnWidth(this); // apply event handling to headers // this is to big, perhaps break it out? $headers.click( function (e) { + //var clickTime= new Date(); var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0; if (!this.sortDisabled && totalRows > 0) { // Only call sortStart if sorting is @@ -727,23 +626,13 @@ // get current column index var i = this.column; // get current column sort order - this.order = this.count++ % 2; - // always sort on the locked order. - if (this.lockedOrder) this.order = this.lockedOrder; - + this.order = this.count % 2; + this.count++; // user only whants to sort on one // column if (!e[config.sortMultiSortKey]) { // flush the sort list config.sortList = []; - if (config.sortForce != null) { - var a = config.sortForce; - for (var j = 0; j < a.length; j++) { - if (a[j][0] != i) { - config.sortList.push(a[j]); - } - } - } // add column to sort list config.sortList.push([i, this.order]); // multi column sorting @@ -766,13 +655,14 @@ // add column to sort list array config.sortList.push([i, this.order]); } - }; + } setTimeout(function () { // set css for headers setHeadersCss($this[0], $headers, config.sortList, sortCSS); appendToTable( $this[0], multisort( $this[0], config.sortList, cache)); + //benchmark("Sorting " + totalRows + " rows:", clickTime); }, 1); // stop normal event by returning false return false; @@ -781,56 +671,43 @@ }).mousedown(function () { if (config.cancelSelection) { this.onselectstart = function () { - return false + return false; }; return false; } }); // apply easy methods that trigger binded events - $this.bind("update", function () { - var me = this; - setTimeout(function () { - // rebuild parsers. - me.config.parsers = buildParserCache( - me, $headers); - // rebuild the cache map - cache = buildCache(me); - }, 1); - }).bind("updateCell", function (e, cell) { - var config = this.config; - // get position from the dom. - var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex]; - // update cache - cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format( - getElementText(config, cell), cell); - }).bind("sorton", function (e, list) { - $(this).trigger("sortStart"); - config.sortList = list; - // update and store the sortlist - var sortList = config.sortList; - // update header count index - updateHeaderSortCount(this, sortList); - // set css for headers - setHeadersCss(this, $headers, sortList, sortCSS); - // sort the table and append it to the dom - appendToTable(this, multisort(this, sortList, cache)); - }).bind("appendCache", function () { - appendToTable(this, cache); - }).bind("applyWidgetId", function (e, id) { - getWidgetById(id).format(this); - }).bind("applyWidgets", function () { - // apply widgets - applyWidget(this); - }); - if ($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) { - config.sortList = $(this).metadata().sortlist; - } - // if user has supplied a sort list to constructor. - if (config.sortList.length > 0) { - $this.trigger("sorton", [config.sortList]); - } - // apply widgets - applyWidget(this); + //Can't think of any use for these in a mw context + // $this.bind("update", function () { + // var me = this; + // setTimeout(function () { + // // rebuild parsers. + // me.config.parsers = buildParserCache( + // me, $headers); + // // rebuild the cache map + // cache = buildCache(me); + // }, 1); + // }).bind("updateCell", function (e, cell) { + // var config = this.config; + // // get position from the dom. + // var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex]; + // // update cache + // cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format( + // getElementText(config, cell), cell); + // }).bind("sorton", function (e, list) { + // $(this).trigger("sortStart"); + // config.sortList = list; + // // update and store the sortlist + // var sortList = config.sortList; + // // update header count index + // updateHeaderSortCount(this, sortList); + // // set css for headers + // setHeadersCss(this, $headers, sortList, sortCSS); + // // sort the table and append it to the dom + // appendToTable(this, multisort(this, sortList, cache)); + // }).bind("appendCache", function () { + // appendToTable(this, cache); + // }); }); }; this.addParser = function (parser) { @@ -843,10 +720,24 @@ } if (a) { parsers.push(parser); - }; + } }; - this.addWidget = function (widget) { - widgets.push(widget); + this.formatDigit = function (s) { + if (ts.transformTable != false) { + var out = '', + c; + for (var p = 0; p < s.length; p++) { + c = s.charAt(p); + if (c in ts.transformTable) { + out += ts.transformTable[c]; + } else { + out += c; + } + } + s = out; + } + var i = parseFloat(s.replace(/[, ]/g, '').replace("\u2212", '-')); + return (isNaN(i)) ? 0 : i; }; this.formatFloat = function (s) { var i = parseFloat(s); @@ -856,10 +747,6 @@ var i = parseInt(s); return (isNaN(i)) ? 0 : i; }; - this.isDigit = function (s, config) { - // replace all an wanted chars and match. - return /^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g, ''))); - }; this.clearTableBody = function (table) { if ($.browser.msie) { function empty() { @@ -889,38 +776,23 @@ return true; }, format: function (s) { - return $.trim(s.toLocaleLowerCase()); + s = $.trim(s.toLowerCase()); + if (ts.collationRegex) { + var tsc = tableSorterCollation; + s = s.replace(ts.collationRegex, function (match) { + var r = tsc[match] ? tsc[match] : tsc[match.toUpperCase()]; + return r.toLowerCase(); + }); + } + return s; }, type: "text" }); ts.addParser({ - id: "digit", - is: function (s, table) { - var c = table.config; - return $.tablesorter.isDigit(s, c); - }, - format: function (s) { - return $.tablesorter.formatFloat(s); - }, - type: "numeric" - }); - - ts.addParser({ - id: "currency", + id: "IPAddress", is: function (s) { - return /^[£$€?.]/.test(s); - }, - format: function (s) { - return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g), "")); - }, - type: "numeric" - }); - - ts.addParser({ - id: "ipAddress", - is: function (s) { - return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s); + return ts.rgx.IPAddress[0].test(s); }, format: function (s) { var a = s.split("."), @@ -940,35 +812,46 @@ }); ts.addParser({ - id: "url", - is: function (s) { - return /^(https?|ftp|file):\/\/$/.test(s); + id: "number", + is: function (s, table) { + return $.tablesorter.numberRegex.test($.trim(s)); }, format: function (s) { - return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//), '')); + return $.tablesorter.formatDigit(s); }, - type: "text" + type: "numeric" }); ts.addParser({ - id: "isoDate", + id: "currency", is: function (s) { - return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s); + return ts.rgx.currency[0].test(s); }, format: function (s) { - return $.tablesorter.formatFloat((s != "") ? new Date(s.replace( - new RegExp(/-/g), "/")).getTime() : "0"); + return $.tablesorter.formatDigit(s.replace(ts.rgx.currency[1], "")); }, type: "numeric" }); ts.addParser({ - id: "percent", + id: "url", is: function (s) { - return /\%$/.test($.trim(s)); + return ts.rgx.url[0].test(s); }, format: function (s) { - return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g), "")); + return $.trim(s.replace(ts.rgx.url[1], '')); + }, + type: "text" + }); + + ts.addParser({ + id: "isoDate", + is: function (s) { + return ts.rgx.isoDate[0].test(s); + }, + format: function (s) { + return $.tablesorter.formatFloat((s != "") ? new Date(s.replace( + new RegExp(/-/g), "/")).getTime() : "0"); }, type: "numeric" }); @@ -976,7 +859,7 @@ ts.addParser({ id: "usLongDate", is: function (s) { - 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)))$/)); + return ts.rgx.usLongDate[0].test(s); }, format: function (s) { return $.tablesorter.formatFloat(new Date(s).getTime()); @@ -985,71 +868,64 @@ }); ts.addParser({ - id: "shortDate", + id: "date", is: function (s) { - return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s); + return (ts.dateRegex[0].test(s) || ts.dateRegex[1].test(s) || ts.dateRegex[2].test(s)); }, format: function (s, table) { - var c = table.config; - s = s.replace(/\-/g, "/"); - if (c.dateFormat == "us") { - // reformat the string in ISO format - s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2"); - } else if (c.dateFormat == "uk") { - // reformat the string in ISO format - s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1"); - } else if (c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") { - s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3"); + s = s.toLowerCase(); + + for (i = 1, j = 0; i < 13 && j < 2; i++) { + s = s.replace(ts.monthNames[j][i], i); + if (i == 12) { + j++; + i = 0; + } } - return $.tablesorter.formatFloat(new Date(s).getTime()); + + s = s.replace(/[\-\.\,' ]/g, "/"); + + //Replace double slashes + s = s.replace(/\/\//g, "/"); + s = s.replace(/\/\//g, "/"); + s = s.split('/'); + + //Pad Month and Day + if (s[0].length == 1) s[0] = "0" + s[0]; + if (s[1].length == 1) s[1] = "0" + s[1]; + + if (!s[2]) { + //Fix yearless dates + s[2] = 2000; + } else if ((y = parseInt(s[2])) < 100) { + //Guestimate years without centuries + if (y < 30) { + s[2] = 2000 + y; + } else { + s[2] = 1900 + y; + } + } + //Resort array depending on preferences + if (wgDefaultDateFormat == "mdy") { + s.push(s.shift()); + s.push(s.shift()); + } else if (wgDefaultDateFormat == "dmy") { + var d = s.shift(); + s.push(s.shift()); + s.push(d); + } + return parseInt(s.join('')); }, type: "numeric" }); ts.addParser({ id: "time", is: function (s) { - return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s); + return ts.rgx.time[0].test(s); }, format: function (s) { return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime()); }, type: "numeric" }); - ts.addParser({ - id: "metadata", - is: function (s) { - return false; - }, - format: function (s, table, cell) { - var c = table.config, - p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName; - return $(cell).metadata()[p]; - }, - type: "numeric" - }); - // add default widgets - ts.addWidget({ - id: "zebra", - format: function (table) { - if (table.config.debug) { - var time = new Date(); - } - var $tr, row = -1, - odd; - // loop through the visible rows - $("tr:visible", table.tBodies[0]).each(function (i) { - $tr = $(this); - // style children rows the same way the parent - // row was styled - if (!$tr.hasClass(table.config.cssChildRow)) row++; - odd = (row % 2 == 0); - $tr.removeClass( - table.config.widgetZebra.css[odd ? 0 : 1]).addClass( - table.config.widgetZebra.css[odd ? 1 : 0]) - }); - if (table.config.debug) { - $.tablesorter.benchmark("Applying Zebra widget", time); - } - } - }); })(jQuery); \ No newline at end of file