From: Tim Starling Date: Tue, 28 Oct 2008 08:07:00 +0000 (+0000) Subject: In sortable tables: X-Git-Tag: 1.31.0-rc.0~44544 X-Git-Url: http://git.cyclocoop.org/%24href?a=commitdiff_plain;h=1d897ce4eac93ee4416e4c39e543ca24a41a0f4e;p=lhc%2Fweb%2Fwiklou.git In sortable tables: * (bug 8063) Use the content language digit transform table. * Don't recognise C-style hexadecimal notation as a number, that feature never actually worked. * Be more forgiving about things that look like numbers but turn out not to be. Sort them stringwise. * Optimised ts_resortTable by having it calculate the sort keys at the start, and then using a single trivial comparison function. There are potentially many more comparisons than rows. Observed factor of 2 speedup. * Use RegExp.test() instead of String.match() when a true/false value is desired, as recommended by the Mozilla reference --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 3c7a093ab9..c5b3032025 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -289,6 +289,7 @@ The following extensions are migrated into MediaWiki 1.14: JavaScript is disabled. * (bug 4253) Recentchanges IRC messages no longer include title in diff URLs * Allow '0' to be an accesskey. +* (bug 8063) Use language-dependent sorting in client-side sortable tables === API changes in 1.14 === diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index ea3c851eea..76b4071fb6 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -1399,7 +1399,7 @@ $wgCacheEpoch = '20030516000000'; * to ensure that client-side caches don't keep obsolete copies of global * styles. */ -$wgStyleVersion = '182'; +$wgStyleVersion = '183'; # Server-side caching: diff --git a/includes/Skin.php b/includes/Skin.php index b1336ee8c6..0fe06cc021 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -341,6 +341,18 @@ class Skin extends Linker { $ns = $wgTitle->getNamespace(); $nsname = isset( $wgCanonicalNamespaceNames[ $ns ] ) ? $wgCanonicalNamespaceNames[ $ns ] : $wgTitle->getNsText(); + $separatorTransTable = $wgContLang->separatorTransformTable(); + $separatorTransTable = $separatorTransTable ? $separatorTransTable : array(); + $compactSeparatorTransTable = array( + implode( "\t", array_keys( $separatorTransTable ) ), + implode( "\t", $separatorTransTable ), + ); + $digitTransTable = $wgContLang->digitTransformTable(); + $digitTransTable = $digitTransTable ? $digitTransTable : array(); + $compactDigitTransTable = array( + implode( "\t", array_keys( $digitTransTable ) ), + implode( "\t", $digitTransTable ), + ); $vars = array( 'skin' => $data['skinname'], @@ -368,6 +380,8 @@ class Skin extends Linker { 'wgVersion' => $wgVersion, 'wgEnableAPI' => $wgEnableAPI, 'wgEnableWriteAPI' => $wgEnableWriteAPI, + 'wgSeparatorTransformTable' => $compactSeparatorTransTable, + 'wgDigitTransformTable' => $compactDigitTransTable, ); if( $wgUseAjax && $wgEnableMWSuggest && !$wgUser->getOption( 'disablesuggest', false )){ diff --git a/skins/common/wikibits.js b/skins/common/wikibits.js index 62ef574842..5e7905f019 100644 --- a/skins/common/wikibits.js +++ b/skins/common/wikibits.js @@ -503,6 +503,8 @@ var ts_image_down = "sort_down.gif"; var ts_image_none = "sort_none.gif"; var ts_europeandate = wgContentLanguage != "en"; // The non-American-inclined can change to "true" var ts_alternate_row_colors = false; +var ts_number_transform_table = null; +var ts_number_regex = null; function sortables_init() { var idnum = 0; @@ -575,9 +577,14 @@ function ts_resortTable(lnk) { table = table.parentNode; if (!table) return; - // Work out a type for the column if (table.rows.length <= 1) return; + // Generate the number transform table if it's not done already + if (ts_number_transform_table == null) { + ts_initTransformTable(); + } + + // Work out a type for the column // Skip the first row if that's where the headings are var rowStart = (table.tHead && table.tHead.rows.length > 0 ? 0 : 1); @@ -590,22 +597,21 @@ function ts_resortTable(lnk) { } } - var sortfn = ts_sort_caseinsensitive; - if (itm.match(/^\d\d[\/. -][a-zA-Z]{3}[\/. -]\d\d\d\d$/)) - sortfn = ts_sort_date; - else if (itm.match(/^\d\d[\/.-]\d\d[\/.-]\d\d\d\d$/)) - sortfn = ts_sort_date; - else if (itm.match(/^\d\d[\/.-]\d\d[\/.-]\d\d$/)) - sortfn = ts_sort_date; + // TODO: bug 8226, localised date formats + var sortfn = ts_sort_generic; + var preprocessor = ts_toLowerCase; + if (/^\d\d[\/. -][a-zA-Z]{3}[\/. -]\d\d\d\d$/.test(itm)) { + preprocessor = ts_dateToSortKey; + } else if (/^\d\d[\/.-]\d\d[\/.-]\d\d\d\d$/.test(itm)) { + preprocessor = ts_dateToSortKey; + } else if (/^\d\d[\/.-]\d\d[\/.-]\d\d$/.test(itm)) { + preprocessor = ts_dateToSortKey; // pound dollar euro yen currency cents - else if (itm.match(/(^[\u00a3$\u20ac\u00a4\u00a5]|\u00a2$)/)) - sortfn = ts_sort_currency; - // We allow a trailing percent sign, which we just strip. This works fine - // if percents and regular numbers aren't being mixed. - else if (itm.match(/^[+-]?\d[\d,]*(\.[\d,]*)?([eE][+-]?\d[\d,]*)?\%?$/) || - itm.match(/^[+-]?\.\d[\d,]*([eE][+-]?\d[\d,]*)?\%?$/) || - itm.match(/^0x[\da-f]+$/i)) - sortfn = ts_sort_numeric; + } else if (/(^[\u00a3$\u20ac\u00a4\u00a5]|\u00a2$)/.test(itm)) { + preprocessor = ts_currencyToSortKey; + } else if (ts_number_regex.test(itm)) { + preprocessor = ts_parseFloat; + } var reverse = (span.getAttribute("sortdir") == 'down'); @@ -614,8 +620,9 @@ function ts_resortTable(lnk) { var row = table.rows[j]; var keyText = ts_getInnerText(row.cells[column]); var oldIndex = (reverse ? -j : j); + var preprocessed = preprocessor( keyText ); - newRows[newRows.length] = new Array(row, keyText, oldIndex); + newRows[newRows.length] = new Array(row, preprocessed, oldIndex); } newRows.sort(sortfn); @@ -654,6 +661,63 @@ function ts_resortTable(lnk) { } } +function ts_initTransformTable() { + if ( typeof wgSeparatorTransformTable == "undefined" + || ( wgSeparatorTransformTable[0] == '' && wgDigitTransformTable[2] == '' ) ) + { + digitClass = "[0-9,.]"; + ts_number_transform_table = false; + } else { + ts_number_transform_table = {}; + // Unpack the transform table + // Separators + ascii = wgSeparatorTransformTable[0].split("\t"); + localised = wgSeparatorTransformTable[1].split("\t"); + for ( var i = 0; i < ascii.length; i++ ) { + ts_number_transform_table[localised[i]] = ascii[i]; + } + // Digits + ascii = wgDigitTransformTable[0].split("\t"); + localised = wgDigitTransformTable[1].split("\t"); + for ( var i = 0; i < ascii.length; i++ ) { + ts_number_transform_table[localised[i]] = ascii[i]; + } + + // Construct regex for number identification + digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '\\.']; + maxDigitLength = 1; + for ( var digit in ts_number_transform_table ) { + // Escape regex metacharacters + digits.push( + digit.replace( /[\\\\$\*\+\?\.\(\)\|\{\}\[\]\-]/, + function( s ) { return '\\' + s; } ) + ); + if (digit.length > maxDigitLength) { + maxDigitLength = digit.length; + } + } + if ( maxDigitLength > 1 ) { + digitClass = '[' + digits.join( '', digits ) + ']'; + } else { + 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_number_regex = new RegExp( + "^(" + + "[+-]?[0-9][0-9,]*(\\.[0-9,]*)?(E[+-]?[0-9][0-9,]*)?" + // Fortran-style scientific + "|" + + "[+-]?" + digitClass + "+%?" + // Generic localised + ")$", "i" + ); +} + +function ts_toLowerCase( s ) { + return s.toLowerCase(); +} + function ts_dateToSortKey(date) { // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX if (date.length == 11) { @@ -695,38 +759,34 @@ function ts_dateToSortKey(date) { return "00000000"; } -function ts_parseFloat(num) { - if (!num) return 0; - num = parseFloat(num.replace(/,/g, "")); - return (isNaN(num) ? 0 : num); -} - -function ts_sort_date(a,b) { - var aa = ts_dateToSortKey(a[1]); - var bb = ts_dateToSortKey(b[1]); - return (aa < bb ? -1 : aa > bb ? 1 : a[2] - b[2]); -} - -function ts_sort_currency(a,b) { - var aa = ts_parseFloat(a[1].replace(/[^0-9.,]/g,'')); - var bb = ts_parseFloat(b[1].replace(/[^0-9.,]/g,'')); - return (aa != bb ? aa - bb : a[2] - b[2]); -} +function ts_parseFloat( s ) { + if ( !s ) { + return 0; + } + if (ts_number_transform_table != false) { + var newNum = '', c; + + for ( var p = 0; p < s.length; p++ ) { + c = s.charAt( p ); + if (c in ts_number_transform_table) { + newNum += ts_number_transform_table[c]; + } else { + newNum += c; + } + } + s = newNum; + } -function ts_sort_numeric(a,b) { - var aa = ts_parseFloat(a[1]); - var bb = ts_parseFloat(b[1]); - return (aa != bb ? aa - bb : a[2] - b[2]); + num = parseFloat(s.replace(/,/g, "")); + return (isNaN(num) ? s : num); } -function ts_sort_caseinsensitive(a,b) { - var aa = a[1].toLowerCase(); - var bb = b[1].toLowerCase(); - return (aa < bb ? -1 : aa > bb ? 1 : a[2] - b[2]); +function ts_currencyToSortKey( s ) { + return ts_parseFloat(s.replace(/[^0-9.,]/g,'')); } -function ts_sort_default(a,b) { - return (a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : a[2] - b[2]); +function ts_sort_generic(a, b) { + return a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : a[2] - b[2]; } function ts_alternate(table) {