r86088 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r86087‎ | r86088 | r86089 >
Date:21:47, 14 April 2011
Author:diebuche
Status:resolved (Comments)
Tags:
Comment:
Completely rewritten table sorting script.
Fixes Bug 8028, Bug 8115, Bug 15406, Bug 17141, Bug 8732

1. Sites can specify custom collations.
The script accepts an object "tableSorterCollation" which contains a lookup
table, how specific characters should be treated.
For example, after setting "tableSorterCollation={'ä':'ae', 'ß':'ss'};" in the
site's common.js any string containing an ä or Ä will be sorted as if it were a
'ae'.

2. Table rows can be forced to use a specific data type.
By setting class="sort-{Parsername}", the row will be parsed with the specified
algorithm. class="sort-date" would force date sorting etc.
The following parsers are available: text, IPAddress, number, url, currency,
date, isoDate, usLongDate, time

3. Execution time is reduced by half or more.

Sorting a 935 row * 8 columns table:

Browser Before After
-------- ------ -----
Chrome 10 90ms 42ms
Safari 5 115ms 48ms
Firefox 4 412ms 87ms
IE8 720ms 115ms

4. Based on the content language and the mdy vs dmy preference, the parser can
understand dates such as "17. März '11". wgMonthNames=[] and
wgMonthNamesShort=[]
in the content language and the mdy vs dmy preference are exported to js; A
table containing the following dates would be sorted correctly:
17. Jan. 01
23 Feb 1992
9.02.05
13 November 2001
14 Oktober '76

Was tested in ie6-8, chrome, safari 5, ff3 & ff4
Modified paths:
  • /trunk/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php (modified) (history)
  • /trunk/phase3/resources/Resources.php (modified) (history)
  • /trunk/phase3/resources/jquery/jquery.tablesorter.js (added) (history)
  • /trunk/phase3/resources/mediawiki.util/mediawiki.util.js (modified) (history)
  • /trunk/phase3/skins/common/images/sort_both.gif (added) (history)
  • /trunk/phase3/skins/common/images/sort_down.gif (modified) (history)
  • /trunk/phase3/skins/common/images/sort_up.gif (modified) (history)
  • /trunk/phase3/skins/common/shared.css (modified) (history)
  • /trunk/phase3/skins/common/wikibits.js (modified) (history)

Diff [purge]

Index: trunk/phase3/skins/common/shared.css
@@ -567,11 +567,6 @@
568568 background-repeat: no-repeat;
569569 }
570570
571 -/* Sort arrows added by SortableTables */
572 -a.sortheader {
573 - margin: 0 0.3em;
574 -}
575 -
576571 /* Localised ordered list numbering for some languages */
577572 ol:lang(bcc) li,
578573 ol:lang(bqi) li,
@@ -652,3 +647,17 @@
653648 right: 10px;
654649 background-position: 0% 100%;
655650 }
 651+/* Table Sorting */
 652+th.headerSort {
 653+ background-image: url(images/sort_both.gif);
 654+ cursor: pointer;
 655+ background-repeat: no-repeat;
 656+ background-position: center right;
 657+ padding-right: 21px;
 658+}
 659+th.headerSortUp {
 660+ background-image: url(images/sort_up.gif);
 661+}
 662+th.headerSortDown {
 663+ background-image: url(images/sort_down.gif);
 664+}
\ No newline at end of file
Index: trunk/phase3/skins/common/wikibits.js
@@ -981,7 +981,7 @@
982982 updateTooltipAccessKeys( null );
983983 setupCheckboxShiftClick();
984984
985 - jQuery( document ).ready( sortables_init );
 985+ //jQuery( document ).ready( sortables_init );
986986
987987 // Run any added-on functions
988988 for ( var i = 0; i < onloadFuncts.length; i++ ) {
Index: trunk/phase3/skins/common/images/sort_up.gif
Cannot display: file marked as a binary type.
svn:mime-type = image/gif
Index: trunk/phase3/skins/common/images/sort_down.gif
Cannot display: file marked as a binary type.
svn:mime-type = image/gif
Index: trunk/phase3/skins/common/images/sort_both.gif
Cannot display: file marked as a binary type.
svn:mime-type = image/gif
Property changes on: trunk/phase3/skins/common/images/sort_both.gif
___________________________________________________________________
Added: svn:mime-type
989989 + image/gif
Index: trunk/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php
@@ -54,6 +54,17 @@
5555 );
5656 $mainPage = Title::newMainPage();
5757
 58+ #$localDateFormats = $wgContLang->getDateFormats();
 59+ #$localPreferedFormat = $localDateFormats[$wgContLang->getDefaultDateFormat().' date'];
 60+
 61+ $monthNames = array('');
 62+ $monthNamesShort = array('');
 63+ for ($i=1; $i < 13; $i++) {
 64+ $monthNames[]=$wgContLang->getMonthName($i);
 65+ $monthNamesShort[]=$wgContLang->getMonthAbbreviation($i);
 66+ }
 67+
 68+ #$localPreferedFormat = $localDateFormats['dmy date'];
5869 // Build list of variables
5970 $vars = array(
6071 'wgLoadScript' => $wgLoadScript,
@@ -73,6 +84,9 @@
7485 'wgVersion' => $wgVersion,
7586 'wgEnableAPI' => $wgEnableAPI,
7687 'wgEnableWriteAPI' => $wgEnableWriteAPI,
 88+ 'wgDefaultDateFormat' => $wgContLang->getDefaultDateFormat(),
 89+ 'wgMonthNames' => $monthNames,
 90+ 'wgMonthNamesShort' => $monthNamesShort,
7791 'wgSeparatorTransformTable' => $compactSeparatorTransTable,
7892 'wgDigitTransformTable' => $compactDigitTransTable,
7993 'wgMainPageTitle' => $mainPage ? $mainPage->getPrefixedText() : null,
Index: trunk/phase3/resources/jquery/jquery.tablesorter.js
@@ -0,0 +1,937 @@
 2+/*
 3+ *
 4+ * TableSorter for MediaWiki
 5+ *
 6+ * Written 2011 Leo Koppelkamm
 7+ * Based on tablesorter.com plugin, written (c) 2007 Christian Bach.
 8+ *
 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+ * @option String cssHeader ( optional ) A string of the class name to be appended
 22+ * to sortable tr elements in the thead of the table. Default value:
 23+ * "header"
 24+ *
 25+ * @option String cssAsc ( optional ) A string of the class name to be appended to
 26+ * sortable tr elements in the thead on a ascending sort. Default value:
 27+ * "headerSortUp"
 28+ *
 29+ * @option String cssDesc ( optional ) A string of the class name to be appended
 30+ * to sortable tr elements in the thead on a descending sort. Default
 31+ * value: "headerSortDown"
 32+ *
 33+ * @option String sortInitialOrder ( optional ) A string of the inital sorting
 34+ * order can be asc or desc. Default value: "asc"
 35+ *
 36+ * @option String sortMultisortKey ( optional ) A string of the multi-column sort
 37+ * key. Default value: "shiftKey"
 38+ *
 39+ * @option Boolean sortLocaleCompare ( optional ) Boolean flag indicating whatever
 40+ * to use String.localeCampare method or not. Set to false.
 41+ *
 42+ * @option Boolean cancelSelection ( optional ) Boolean flag indicating if
 43+ * tablesorter should cancel selection of the table headers text.
 44+ * Default value: true
 45+ *
 46+ * @option Boolean debug ( optional ) Boolean flag indicating if tablesorter
 47+ * should display debuging information usefull for development.
 48+ *
 49+ * @type jQuery
 50+ *
 51+ * @name tablesorter
 52+ *
 53+ * @cat Plugins/Tablesorter
 54+ *
 55+ * @author Christian Bach/christian.bach@polyester.se
 56+ */
 57+
 58+( function ($) {
 59+ $.extend( {
 60+ tablesorter: new
 61+
 62+ function () {
 63+
 64+ var parsers = [];
 65+
 66+ this.defaults = {
 67+ cssHeader: "headerSort",
 68+ cssAsc: "headerSortUp",
 69+ cssDesc: "headerSortDown",
 70+ cssChildRow: "expand-child",
 71+ sortInitialOrder: "asc",
 72+ sortMultiSortKey: "shiftKey",
 73+ sortLocaleCompare: false,
 74+ parsers: {},
 75+ widgets: [],
 76+ headers: {},
 77+ cancelSelection: true,
 78+ sortList: [],
 79+ headerList: [],
 80+ selectorHeaders: 'thead tr:eq(0) th',
 81+ debug: false
 82+ };
 83+
 84+ /* debuging utils */
 85+ //
 86+ // function benchmark( s, d ) {
 87+ // alert( s + "," + ( new Date().getTime() - d.getTime() ) + "ms" );
 88+ // }
 89+ //
 90+ // this.benchmark = benchmark;
 91+ //
 92+ //
 93+ /* parsers utils */
 94+
 95+ function buildParserCache( table, $headers ) {
 96+ var rows = table.tBodies[0].rows;
 97+
 98+ if ( rows[0] ) {
 99+
 100+ var list = [],
 101+ cells = rows[0].cells,
 102+ l = cells.length;
 103+
 104+ for ( var i = 0; i < l; i++ ) {
 105+
 106+ if ( $headers.eq(i).is( '[class*="sort-"]' ) ) {
 107+ p = getParserById($headers.eq(i).attr('class').replace(/.*?sort-(.*?) .*/, '$1'));
 108+ } else {
 109+ p = detectParserForColumn( table, rows, -1, i );
 110+ }
 111+ // if ( table.config.debug ) {
 112+ // console.log( "column:" + i + " parser:" + p.id + "\n" );
 113+ // }
 114+ list.push(p);
 115+ }
 116+ }
 117+ return list;
 118+ }
 119+
 120+ function detectParserForColumn( table, rows, rowIndex, cellIndex ) {
 121+ var l = parsers.length,
 122+ node = false,
 123+ nodeValue = false,
 124+ keepLooking = true;
 125+ while ( nodeValue == '' && keepLooking ) {
 126+ rowIndex++;
 127+ if ( rows[rowIndex] ) {
 128+ node = getNodeFromRowAndCellIndex( rows, rowIndex, cellIndex );
 129+ nodeValue = trimAndGetNodeText( table.config, node );
 130+ // if ( table.config.debug ) {
 131+ // console.log( 'Checking if value was empty on row:' + rowIndex );
 132+ // }
 133+ } else {
 134+ keepLooking = false;
 135+ }
 136+ }
 137+ for ( var i = 1; i < l; i++ ) {
 138+ if ( parsers[i].is( nodeValue, table, node ) ) {
 139+ return parsers[i];
 140+ }
 141+ }
 142+ // 0 is always the generic parser ( text )
 143+ return parsers[0];
 144+ }
 145+
 146+ function getNodeFromRowAndCellIndex( rows, rowIndex, cellIndex ) {
 147+ return rows[rowIndex].cells[cellIndex];
 148+ }
 149+
 150+ function trimAndGetNodeText( config, node ) {
 151+ return $.trim( getElementText( config, node ) );
 152+ }
 153+
 154+ function getParserById( name ) {
 155+ var l = parsers.length;
 156+ for ( var i = 0; i < l; i++ ) {
 157+ if ( parsers[i].id.toLowerCase() == name.toLowerCase() ) {
 158+ return parsers[i];
 159+ }
 160+ }
 161+ return false;
 162+ }
 163+
 164+ /* utils */
 165+
 166+ function buildCache( table ) {
 167+
 168+ // if ( table.config.debug ) {
 169+ // var cacheTime = new Date();
 170+ // }
 171+ var totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0,
 172+ totalCells = ( table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length ) || 0,
 173+ parsers = table.config.parsers,
 174+ cache = {
 175+ row: [],
 176+ normalized: []
 177+ };
 178+
 179+ for ( var i = 0; i < totalRows; ++i ) {
 180+
 181+ // Add the table data to main data array
 182+ var c = $( table.tBodies[0].rows[i] ),
 183+ cols = [];
 184+
 185+ // if this is a child row, add it to the last row's children and
 186+ // continue to the next row
 187+ if ( c.hasClass( table.config.cssChildRow ) ) {
 188+ cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add(c);
 189+ // go to the next for loop
 190+ continue;
 191+ }
 192+
 193+ cache.row.push(c);
 194+
 195+ for ( var j = 0; j < totalCells; ++j ) {
 196+ cols.push( parsers[j].format( getElementText( table.config, c[0].cells[j] ), table, c[0].cells[j] ) );
 197+ }
 198+
 199+ cols.push( cache.normalized.length ); // add position for rowCache
 200+ cache.normalized.push( cols );
 201+ cols = null;
 202+ }
 203+
 204+ // if ( table.config.debug ) {
 205+ // benchmark( "Building cache for " + totalRows + " rows:", cacheTime );
 206+ // }
 207+ return cache;
 208+ }
 209+
 210+ function getElementText( config, node ) {
 211+ return $( node ).text();
 212+ }
 213+
 214+ function appendToTable( table, cache ) {
 215+
 216+ // if ( table.config.debug ) {
 217+ // var appendTime = new Date()
 218+ // }
 219+ var c = cache,
 220+ r = c.row,
 221+ n = c.normalized,
 222+ totalRows = n.length,
 223+ checkCell = (n[0].length - 1),
 224+ tableBody = $( table.tBodies[0] ),
 225+ fragment = document.createDocumentFragment();
 226+
 227+ for ( var i = 0; i < totalRows; i++ ) {
 228+ var pos = n[i][checkCell];
 229+
 230+ var l = r[pos].length;
 231+
 232+ for ( var j = 0; j < l; j++ ) {
 233+ fragment.appendChild( r[pos][j] );
 234+ }
 235+
 236+ }
 237+ tableBody[0].appendChild( fragment );
 238+ // if ( table.config.debug ) {
 239+ // benchmark( "Rebuilt table:", appendTime );
 240+ // }
 241+ }
 242+
 243+ function buildHeaders( table ) {
 244+
 245+ // if ( table.config.debug ) {
 246+ // var time = new Date();
 247+ // }
 248+ //var header_index = computeTableHeaderCellIndexes( table );
 249+ var realCellIndex = 0;
 250+
 251+ $tableHeaders = $( table.config.selectorHeaders, table ).each( function ( index ) {
 252+ //var normalIndex = allCells.index( this );
 253+ //var realCellIndex = 0;
 254+ this.column = realCellIndex;
 255+
 256+ var colspan = this.colspan;
 257+ colspan = colspan ? parseInt( colspan ) : 1;
 258+ realCellIndex += colspan;
 259+
 260+ //this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
 261+ this.order = 0;
 262+ this.count = 0;
 263+
 264+ if ( $( this ).is( '.unsortable' ) ) this.sortDisabled = true;
 265+
 266+ if ( !this.sortDisabled ) {
 267+ var $th = $( this ).addClass( table.config.cssHeader );
 268+ //if ( table.config.onRenderHeader ) table.config.onRenderHeader.apply($th);
 269+ }
 270+
 271+ // add cell to headerList
 272+ table.config.headerList[index] = this;
 273+ } );
 274+
 275+ // if ( table.config.debug ) {
 276+ // benchmark( "Built headers:", time );
 277+ // console.log( $tableHeaders );
 278+ // }
 279+ //
 280+ return $tableHeaders;
 281+
 282+ }
 283+
 284+ // // from:
 285+ // // http://www.javascripttoolbox.com/lib/table/examples.php
 286+ // // http://www.javascripttoolbox.com/temp/table_cellindex.html
 287+ //
 288+ // function computeTableHeaderCellIndexes(t) {
 289+ // var matrix = [];
 290+ // var lookup = {};
 291+ // var thead = t.getElementsByTagName( 'THEAD' )[0];
 292+ // var trs = thead.getElementsByTagName( 'TR' );
 293+ //
 294+ // for ( var i = 0; i < trs.length; i++ ) {
 295+ // var cells = trs[i].cells;
 296+ // for ( var j = 0; j < cells.length; j++ ) {
 297+ // var c = cells[j];
 298+ //
 299+ // var rowIndex = c.parentNode.rowIndex;
 300+ // var cellId = rowIndex + "-" + c.cellIndex;
 301+ // var rowSpan = c.rowSpan || 1;
 302+ // var colSpan = c.colSpan || 1;
 303+ // var firstAvailCol;
 304+ // if ( typeof( matrix[rowIndex] ) == "undefined" ) {
 305+ // matrix[rowIndex] = [];
 306+ // }
 307+ // // Find first available column in the first row
 308+ // for ( var k = 0; k < matrix[rowIndex].length + 1; k++ ) {
 309+ // if ( typeof( matrix[rowIndex][k] ) == "undefined" ) {
 310+ // firstAvailCol = k;
 311+ // break;
 312+ // }
 313+ // }
 314+ // lookup[cellId] = firstAvailCol;
 315+ // for ( var k = rowIndex; k < rowIndex + rowSpan; k++ ) {
 316+ // if ( typeof( matrix[k] ) == "undefined" ) {
 317+ // matrix[k] = [];
 318+ // }
 319+ // var matrixrow = matrix[k];
 320+ // for ( var l = firstAvailCol; l < firstAvailCol + colSpan; l++ ) {
 321+ // matrixrow[l] = "x";
 322+ // }
 323+ // }
 324+ // }
 325+ // }
 326+ // return lookup;
 327+ // }
 328+ // function checkCellColSpan( table, rows, row ) {
 329+ // var arr = [],
 330+ // r = table.tHead.rows,
 331+ // c = r[row].cells;
 332+ //
 333+ // for ( var i = 0; i < c.length; i++ ) {
 334+ // var cell = c[i];
 335+ //
 336+ // if ( cell.colSpan > 1 ) {
 337+ // arr = arr.concat( checkCellColSpan( table, headerArr, row++ ) );
 338+ // } else {
 339+ // if ( table.tHead.length == 1 || ( cell.rowSpan > 1 || !r[row + 1] ) ) {
 340+ // arr.push( cell );
 341+ // }
 342+ // // headerArr[row] = ( i+row );
 343+ // }
 344+ // }
 345+ // return arr;
 346+ // }
 347+ //
 348+ // function checkHeaderOptions( table, i ) {
 349+ // if ( ( table.config.headers[i] ) && ( table.config.headers[i].sorter === false ) ) {
 350+ // return true;
 351+ // }
 352+ // return false;
 353+ // }
 354+ // function formatSortingOrder(v) {
 355+ // if ( typeof(v) != "Number" ) {
 356+ // return ( v.toLowerCase() == "desc" ) ? 1 : 0;
 357+ // } else {
 358+ // return ( v == 1 ) ? 1 : 0;
 359+ // }
 360+ // }
 361+
 362+ function isValueInArray( v, a ) {
 363+ var l = a.length;
 364+ for ( var i = 0; i < l; i++ ) {
 365+ if ( a[i][0] == v ) {
 366+ return true;
 367+ }
 368+ }
 369+ return false;
 370+ }
 371+
 372+ function setHeadersCss( table, $headers, list, css ) {
 373+ // remove all header information
 374+ $headers.removeClass( css[0] ).removeClass( css[1] );
 375+
 376+ var h = [];
 377+ $headers.each( function ( offset ) {
 378+ if ( !this.sortDisabled ) {
 379+ h[this.column] = $( this );
 380+ }
 381+ } );
 382+
 383+ var l = list.length;
 384+ for ( var i = 0; i < l; i++ ) {
 385+ h[list[i][0]].addClass( css[list[i][1]] );
 386+ }
 387+ }
 388+
 389+ // function updateHeaderSortCount( table, sortList ) {
 390+ // var c = table.config,
 391+ // l = sortList.length;
 392+ // for ( var i = 0; i < l; i++ ) {
 393+ // var s = sortList[i],
 394+ // o = c.headerList[s[0]];
 395+ // o.count = s[1];
 396+ // o.count++;
 397+ // }
 398+ // }
 399+ /* sorting methods */
 400+
 401+ function multisort( table, sortList, cache ) {
 402+ // if ( table.config.debug ) {
 403+ // var sortTime = new Date();
 404+ // }
 405+ var dynamicExp = "var sortWrapper = function(a,b) {",
 406+ l = sortList.length;
 407+
 408+ // TODO: inline functions.
 409+ for ( var i = 0; i < l; i++ ) {
 410+
 411+ var c = sortList[i][0];
 412+ var order = sortList[i][1];
 413+ var s = "";
 414+ if ( table.config.parsers[c].type == "text" ) {
 415+ if ( order == 0 ) {
 416+ s = makeSortFunction( "text", "asc", c );
 417+ } else {
 418+ s = makeSortFunction( "text", "desc", c );
 419+ }
 420+ } else {
 421+ if ( order == 0 ) {
 422+ s = makeSortFunction( "numeric", "asc", c );
 423+ } else {
 424+ s = makeSortFunction( "numeric", "desc", c );
 425+ }
 426+ }
 427+ var e = "e" + i;
 428+
 429+ dynamicExp += "var " + e + " = " + s; // + "(a[" + c + "],b[" + c + "]); ";
 430+ dynamicExp += "if(" + e + ") { return " + e + "; } ";
 431+ dynamicExp += "else { ";
 432+ }
 433+
 434+ // if value is the same keep original order
 435+ var orgOrderCol = cache.normalized[0].length - 1;
 436+ dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
 437+
 438+ for ( var i = 0; i < l; i++ ) {
 439+ dynamicExp += "}; ";
 440+ }
 441+
 442+ dynamicExp += "return 0; ";
 443+ dynamicExp += "}; ";
 444+
 445+ // if ( table.config.debug ) {
 446+ // benchmark( "Evaling expression:" + dynamicExp, new Date() );
 447+ // }
 448+ eval( dynamicExp );
 449+ cache.normalized.sort( sortWrapper );
 450+
 451+ // if ( table.config.debug ) {
 452+ // benchmark( "Sorting on " + sortList.toString() + " and dir " + order + " time:", sortTime );
 453+ // }
 454+ return cache;
 455+ }
 456+
 457+ function makeSortFunction( type, direction, index ) {
 458+ var a = "a[" + index + "]",
 459+ b = "b[" + index + "]";
 460+ if (type == 'text' && direction == 'asc') {
 461+ return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + a + " < " + b + ") ? -1 : 1 )));";
 462+ } else if (type == 'text' && direction == 'desc') {
 463+ return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + b + " < " + a + ") ? -1 : 1 )));";
 464+ } else if (type == 'numeric' && direction == 'asc') {
 465+ return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + a + " - " + b + "));";
 466+ } else if (type == 'numeric' && direction == 'desc') {
 467+ return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + b + " - " + a + "));";
 468+ }
 469+ }
 470+
 471+ function makeSortText(i) {
 472+ return "((a[" + i + "] < b[" + i + "]) ? -1 : ((a[" + i + "] > b[" + i + "]) ? 1 : 0));";
 473+ }
 474+
 475+ function makeSortTextDesc(i) {
 476+ return "((b[" + i + "] < a[" + i + "]) ? -1 : ((b[" + i + "] > a[" + i + "]) ? 1 : 0));";
 477+ }
 478+
 479+ function makeSortNumeric(i) {
 480+ return "a[" + i + "]-b[" + i + "];";
 481+ }
 482+
 483+ function makeSortNumericDesc(i) {
 484+ return "b[" + i + "]-a[" + i + "];";
 485+ }
 486+
 487+ function sortText( a, b ) {
 488+ if ( table.config.sortLocaleCompare ) return a.localeCompare(b);
 489+ return ((a < b) ? -1 : ((a > b) ? 1 : 0));
 490+ }
 491+
 492+ function sortTextDesc( a, b ) {
 493+ if ( table.config.sortLocaleCompare ) return b.localeCompare(a);
 494+ return ((b < a) ? -1 : ((b > a) ? 1 : 0));
 495+ }
 496+
 497+ function sortNumeric( a, b ) {
 498+ return a - b;
 499+ }
 500+
 501+ function sortNumericDesc( a, b ) {
 502+ return b - a;
 503+ }
 504+
 505+ function buildTransformTable() {
 506+ var digits = '0123456789,.'.split('');
 507+
 508+ if ( typeof wgSeparatorTransformTable == 'undefined' || ( wgSeparatorTransformTable[0] == '' && wgDigitTransformTable[2] == '' ) ) {
 509+ ts.transformTable = false;
 510+ } else {
 511+ ts.transformTable = {};
 512+
 513+ // Unpack the transform table
 514+ var ascii = wgSeparatorTransformTable[0].split( "\t" ).concat( wgDigitTransformTable[0].split( "\t" ) );
 515+ var localised = wgSeparatorTransformTable[1].split( "\t" ).concat( wgDigitTransformTable[1].split( "\t" ) );
 516+
 517+ // Construct regex for number identification
 518+ for ( var i = 0; i < ascii.length; i++ ) {
 519+ ts.transformTable[localised[i]] = ascii[i];
 520+ digits.push( $.escapeRE( localised[i] ) );
 521+ }
 522+ }
 523+ var digitClass = '[' + digits.join( '', digits ) + ']';
 524+
 525+ // We allow a trailing percent sign, which we just strip. This works fine
 526+ // if percents and regular numbers aren't being mixed.
 527+ ts.numberRegex = new RegExp("^(" + "[-+\u2212]?[0-9][0-9,]*(\\.[0-9,]*)?(E[-+\u2212]?[0-9][0-9,]*)?" + // Fortran-style scientific
 528+ "|" + "[-+\u2212]?" + digitClass + "+%?" + // Generic localised
 529+ ")$", "i");
 530+ }
 531+
 532+ function buildDateTable() {
 533+ var r = '';
 534+ ts.monthNames = [
 535+ [],
 536+ []
 537+ ];
 538+ ts.dateRegex = [];
 539+
 540+ for ( i = 1; i < 13; i++ ) {
 541+ ts.monthNames[0][i] = wgMonthNames[i].toLowerCase();
 542+ ts.monthNames[1][i] = wgMonthNamesShort[i].toLowerCase().replace( '.', '' );
 543+ r += $.escapeRE( ts.monthNames[0][i] ) + '|';
 544+ r += $.escapeRE( ts.monthNames[1][i] ) + '|';
 545+ }
 546+
 547+ //Remove trailing pipe
 548+ r = r.slice( 0, -1 );
 549+
 550+ //Build RegEx
 551+ //Any date formated with . , ' - or /
 552+ ts.dateRegex[0] = new RegExp(/^\s*\d{1,2}[\,\.\-\/'\s]*\d{1,2}[\,\.\-\/'\s]*\d{2,4}\s*?/i);
 553+
 554+ //Written Month name, dmy
 555+ ts.dateRegex[1] = new RegExp('^\\s*\\d{1,2}[\\,\\.\\-\\/\'\\s]*(' + r + ')' + '[\\,\\.\\-\\/\'\\s]*\\d{2,4}\\s*$', 'i');
 556+
 557+ //Written Month name, mdy
 558+ ts.dateRegex[2] = new RegExp('^\\s*(' + r + ')' + '[\\,\\.\\-\\/\'\\s]*\\d{1,2}[\\,\\.\\-\\/\'\\s]*\\d{2,4}\\s*$', 'i');
 559+
 560+ }
 561+
 562+ function buildCollationTable() {
 563+ if ( typeof tableSorterCollation == "object" ) {
 564+ ts.collationRegex = [];
 565+
 566+ //Build array of key names
 567+ for ( var key in tableSorterCollation ) {
 568+ if ( tableSorterCollation.hasOwnProperty(key) ) { //to be safe
 569+ ts.collationRegex.push(key);
 570+ }
 571+ }
 572+ ts.collationRegex = new RegExp( '[' + ts.collationRegex.join('') + ']', 'ig' );
 573+ }
 574+ }
 575+
 576+ function cacheRegexs() {
 577+ ts.rgx = {
 578+ IPAddress: [new RegExp(/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/)],
 579+ currency: [new RegExp(/^[£$€?.]/), new RegExp(/[£$€]/g)],
 580+ url: [new RegExp(/^(https?|ftp|file):\/\/$/), new RegExp(/(https?|ftp|file):\/\//)],
 581+ isoDate: [new RegExp(/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/)],
 582+ 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)))$/)],
 583+ time: [new RegExp(/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/)]
 584+ };
 585+ } /* public methods */
 586+ this.construct = function ( settings ) {
 587+ return this.each( function () {
 588+ // if no thead or tbody quit.
 589+ if ( !this.tHead || !this.tBodies ) return;
 590+ // declare
 591+ var $this, $document, $headers, cache, config, shiftDown = 0,
 592+ sortOrder;
 593+
 594+ // new blank config object
 595+ this.config = {};
 596+ // merge and extend.
 597+ config = $.extend( this.config, $.tablesorter.defaults, settings );
 598+
 599+ // store common expression for speed
 600+ $this = $( this );
 601+ // save the settings where they read
 602+ $.data( this, "tablesorter", config );
 603+ // build headers
 604+ $headers = buildHeaders( this );
 605+ // Grab and process locale settings
 606+ buildTransformTable();
 607+ buildDateTable();
 608+ buildCollationTable();
 609+
 610+ //Precaching regexps can bring 10 fold
 611+ //performance improvements in some browsers
 612+ cacheRegexs();
 613+
 614+ // try to auto detect column type, and store in tables config
 615+ this.config.parsers = buildParserCache( this, $headers );
 616+ // build the cache for the tbody cells
 617+ cache = buildCache( this );
 618+ // get the css class names, could be done else where.
 619+ var sortCSS = [config.cssDesc, config.cssAsc];
 620+ // apply event handling to headers
 621+ // this is to big, perhaps break it out?
 622+ $headers.click(
 623+
 624+ function (e) {
 625+ //var clickTime= new Date();
 626+ var totalRows = ( $this[0].tBodies[0] && $this[0].tBodies[0].rows.length ) || 0;
 627+ if ( !this.sortDisabled && totalRows > 0 ) {
 628+ // Only call sortStart if sorting is
 629+ // enabled.
 630+ //$this.trigger( "sortStart" );
 631+ // store exp, for speed
 632+ var $cell = $( this );
 633+ // get current column index
 634+ var i = this.column;
 635+ // get current column sort order
 636+ this.order = this.count % 2;
 637+ this.count++;
 638+ // user only whants to sort on one
 639+ // column
 640+ if ( !e[config.sortMultiSortKey] ) {
 641+ // flush the sort list
 642+ config.sortList = [];
 643+ // add column to sort list
 644+ config.sortList.push( [i, this.order] );
 645+ // multi column sorting
 646+ } else {
 647+ // the user has clicked on an all
 648+ // ready sortet column.
 649+ if ( isValueInArray( i, config.sortList ) ) {
 650+ // revers the sorting direction
 651+ // for all tables.
 652+ for ( var j = 0; j < config.sortList.length; j++ ) {
 653+ var s = config.sortList[j],
 654+ o = config.headerList[s[0]];
 655+ if ( s[0] == i ) {
 656+ o.count = s[1];
 657+ o.count++;
 658+ s[1] = o.count % 2;
 659+ }
 660+ }
 661+ } else {
 662+ // add column to sort list array
 663+ config.sortList.push( [i, this.order] );
 664+ }
 665+ }
 666+ setTimeout( function () {
 667+ // set css for headers
 668+ setHeadersCss( $this[0], $headers, config.sortList, sortCSS );
 669+ appendToTable(
 670+ $this[0], multisort(
 671+ $this[0], config.sortList, cache ) );
 672+ //benchmark( "Sorting " + totalRows + " rows:", clickTime );
 673+ }, 1 );
 674+ // stop normal event by returning false
 675+ return false;
 676+ }
 677+ // cancel selection
 678+ } ).mousedown( function () {
 679+ if ( config.cancelSelection ) {
 680+ this.onselectstart = function () {
 681+ return false;
 682+ };
 683+ return false;
 684+ }
 685+ } );
 686+ // apply easy methods that trigger binded events
 687+ //Can't think of any use for these in a mw context
 688+ // $this.bind( "update", function () {
 689+ // var me = this;
 690+ // setTimeout( function () {
 691+ // // rebuild parsers.
 692+ // me.config.parsers = buildParserCache(
 693+ // me, $headers );
 694+ // // rebuild the cache map
 695+ // cache = buildCache(me);
 696+ // }, 1 );
 697+ // } ).bind( "updateCell", function ( e, cell ) {
 698+ // var config = this.config;
 699+ // // get position from the dom.
 700+ // var pos = [( cell.parentNode.rowIndex - 1 ), cell.cellIndex];
 701+ // // update cache
 702+ // cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format(
 703+ // getElementText( config, cell ), cell );
 704+ // } ).bind( "sorton", function ( e, list ) {
 705+ // $( this ).trigger( "sortStart" );
 706+ // config.sortList = list;
 707+ // // update and store the sortlist
 708+ // var sortList = config.sortList;
 709+ // // update header count index
 710+ // updateHeaderSortCount( this, sortList );
 711+ // // set css for headers
 712+ // setHeadersCss( this, $headers, sortList, sortCSS );
 713+ // // sort the table and append it to the dom
 714+ // appendToTable( this, multisort( this, sortList, cache ) );
 715+ // } ).bind( "appendCache", function () {
 716+ // appendToTable( this, cache );
 717+ // } );
 718+ } );
 719+ };
 720+ this.addParser = function ( parser ) {
 721+ var l = parsers.length,
 722+ a = true;
 723+ for ( var i = 0; i < l; i++ ) {
 724+ if ( parsers[i].id.toLowerCase() == parser.id.toLowerCase() ) {
 725+ a = false;
 726+ }
 727+ }
 728+ if (a) {
 729+ parsers.push( parser );
 730+ }
 731+ };
 732+ this.formatDigit = function (s) {
 733+ if ( ts.transformTable != false ) {
 734+ var out = '',
 735+ c;
 736+ for ( var p = 0; p < s.length; p++ ) {
 737+ c = s.charAt(p);
 738+ if ( c in ts.transformTable ) {
 739+ out += ts.transformTable[c];
 740+ } else {
 741+ out += c;
 742+ }
 743+ }
 744+ s = out;
 745+ }
 746+ var i = parseFloat( s.replace(/[, ]/g, '').replace( "\u2212", '-' ) );
 747+ return ( isNaN(i)) ? 0 : i;
 748+ };
 749+ this.formatFloat = function (s) {
 750+ var i = parseFloat(s);
 751+ return ( isNaN(i)) ? 0 : i;
 752+ };
 753+ this.formatInt = function (s) {
 754+ var i = parseInt(s);
 755+ return ( isNaN(i)) ? 0 : i;
 756+ };
 757+ this.clearTableBody = function ( table ) {
 758+ if ( $.browser.msie ) {
 759+ function empty() {
 760+ while ( this.firstChild )
 761+ this.removeChild( this.firstChild );
 762+ }
 763+ empty.apply( table.tBodies[0] );
 764+ } else {
 765+ table.tBodies[0].innerHTML = "";
 766+ }
 767+ };
 768+ }
 769+ } );
 770+
 771+ // extend plugin scope
 772+ $.fn.extend( {
 773+ tablesorter: $.tablesorter.construct
 774+ } );
 775+
 776+ // make shortcut
 777+ var ts = $.tablesorter;
 778+
 779+ // add default parsers
 780+ ts.addParser( {
 781+ id: "text",
 782+ is: function (s) {
 783+ return true;
 784+ },
 785+ format: function (s) {
 786+ s = $.trim( s.toLowerCase() );
 787+ if ( ts.collationRegex ) {
 788+ var tsc = tableSorterCollation;
 789+ s = s.replace( ts.collationRegex, function ( match ) {
 790+ var r = tsc[match] ? tsc[match] : tsc[match.toUpperCase()];
 791+ return r.toLowerCase();
 792+ } );
 793+ }
 794+ return s;
 795+ },
 796+ type: "text"
 797+ } );
 798+
 799+ ts.addParser( {
 800+ id: "IPAddress",
 801+ is: function (s) {
 802+ return ts.rgx.IPAddress[0].test(s);
 803+ },
 804+ format: function (s) {
 805+ var a = s.split("."),
 806+ r = "",
 807+ l = a.length;
 808+ for ( var i = 0; i < l; i++ ) {
 809+ var item = a[i];
 810+ if ( item.length == 2 ) {
 811+ r += "0" + item;
 812+ } else {
 813+ r += item;
 814+ }
 815+ }
 816+ return $.tablesorter.formatFloat(r);
 817+ },
 818+ type: "numeric"
 819+ } );
 820+
 821+ ts.addParser( {
 822+ id: "number",
 823+ is: function ( s, table ) {
 824+ return $.tablesorter.numberRegex.test( $.trim(s ));
 825+ },
 826+ format: function (s) {
 827+ return $.tablesorter.formatDigit(s);
 828+ },
 829+ type: "numeric"
 830+ } );
 831+
 832+ ts.addParser( {
 833+ id: "currency",
 834+ is: function (s) {
 835+ return ts.rgx.currency[0].test(s);
 836+ },
 837+ format: function (s) {
 838+ return $.tablesorter.formatDigit( s.replace( ts.rgx.currency[1], "" ) );
 839+ },
 840+ type: "numeric"
 841+ } );
 842+
 843+ ts.addParser( {
 844+ id: "url",
 845+ is: function (s) {
 846+ return ts.rgx.url[0].test(s);
 847+ },
 848+ format: function (s) {
 849+ return $.trim( s.replace( ts.rgx.url[1], '' ) );
 850+ },
 851+ type: "text"
 852+ } );
 853+
 854+ ts.addParser( {
 855+ id: "isoDate",
 856+ is: function (s) {
 857+ return ts.rgx.isoDate[0].test(s);
 858+ },
 859+ format: function (s) {
 860+ return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(
 861+ new RegExp(/-/g), "/")).getTime() : "0");
 862+ },
 863+ type: "numeric"
 864+ } );
 865+
 866+ ts.addParser( {
 867+ id: "usLongDate",
 868+ is: function (s) {
 869+ return ts.rgx.usLongDate[0].test(s);
 870+ },
 871+ format: function (s) {
 872+ return $.tablesorter.formatFloat( new Date(s).getTime() );
 873+ },
 874+ type: "numeric"
 875+ } );
 876+
 877+ ts.addParser( {
 878+ id: "date",
 879+ is: function (s) {
 880+ return ( ts.dateRegex[0].test(s) || ts.dateRegex[1].test(s) || ts.dateRegex[2].test(s ));
 881+ },
 882+ format: function ( s, table ) {
 883+ s = s.toLowerCase();
 884+
 885+ for ( i = 1, j = 0; i < 13 && j < 2; i++ ) {
 886+ s = s.replace( ts.monthNames[j][i], i );
 887+ if ( i == 12 ) {
 888+ j++;
 889+ i = 0;
 890+ }
 891+ }
 892+
 893+ s = s.replace(/[\-\.\,' ]/g, "/");
 894+
 895+ //Replace double slashes
 896+ s = s.replace(/\/\//g, "/");
 897+ s = s.replace(/\/\//g, "/");
 898+ s = s.split('/');
 899+
 900+ //Pad Month and Day
 901+ if ( s[0].length == 1 ) s[0] = "0" + s[0];
 902+ if ( s[1].length == 1 ) s[1] = "0" + s[1];
 903+
 904+ if ( !s[2] ) {
 905+ //Fix yearless dates
 906+ s[2] = 2000;
 907+ } else if ( ( y = parseInt( s[2] ) ) < 100 ) {
 908+ //Guestimate years without centuries
 909+ if ( y < 30 ) {
 910+ s[2] = 2000 + y;
 911+ } else {
 912+ s[2] = 1900 + y;
 913+ }
 914+ }
 915+ //Resort array depending on preferences
 916+ if ( wgDefaultDateFormat == "mdy" ) {
 917+ s.push( s.shift() );
 918+ s.push( s.shift() );
 919+ } else if ( wgDefaultDateFormat == "dmy" ) {
 920+ var d = s.shift();
 921+ s.push( s.shift() );
 922+ s.push(d);
 923+ }
 924+ return parseInt( s.join('') );
 925+ },
 926+ type: "numeric"
 927+ } );
 928+ ts.addParser( {
 929+ id: "time",
 930+ is: function (s) {
 931+ return ts.rgx.time[0].test(s);
 932+ },
 933+ format: function (s) {
 934+ return $.tablesorter.formatFloat( new Date( "2000/01/01 " + s ).getTime() );
 935+ },
 936+ type: "numeric"
 937+ } );
 938+} )( jQuery );
\ No newline at end of file
Property changes on: trunk/phase3/resources/jquery/jquery.tablesorter.js
___________________________________________________________________
Added: svn:eol-style
1939 + native
Index: trunk/phase3/resources/mediawiki.util/mediawiki.util.js
@@ -57,6 +57,9 @@
5858 /* Enable CheckboxShiftClick */
5959 $( 'input[type=checkbox]:not(.noshiftselect)' ).checkboxShiftClick();
6060
 61+ /* Enable Tablesorting */
 62+ $( 'table.sortable' ).tablesorter();
 63+
6164 /* Emulate placeholder if not supported by browser */
6265 if ( !( 'placeholder' in document.createElement( 'input' ) ) ) {
6366 $( 'input[placeholder]' ).placeholder();
Index: trunk/phase3/resources/Resources.php
@@ -129,6 +129,9 @@
130130 'jquery.tabIndex' => array(
131131 'scripts' => 'resources/jquery/jquery.tabIndex.js',
132132 ),
 133+ 'jquery.tablesorter' => array(
 134+ 'scripts' => 'resources/jquery/jquery.tablesorter.js'
 135+ ),
133136 'jquery.textSelection' => array(
134137 'scripts' => 'resources/jquery/jquery.textSelection.js',
135138 ),
@@ -395,6 +398,7 @@
396399 'jquery.messageBox',
397400 'jquery.makeCollapsible',
398401 'jquery.placeholder',
 402+ 'jquery.tablesorter',
399403 ),
400404 'debugScripts' => 'resources/mediawiki.util/mediawiki.util.test.js',
401405 ),

Follow-up revisions

RevisionCommit summaryAuthorDate
r86108Followup ro r86088: Use data-sort-type instead of classes to specify the pars...diebuche08:23, 15 April 2011
r86305Followup r86088 per CR: Move month array builder into language; use mw.config...diebuche12:54, 18 April 2011
r86337r86088: Get rid of eval by implemting a MergeSort algorithm. It's a few ms sl...diebuche19:20, 18 April 2011
r87243Make jquery.tablesorter more resilient by checking multiple cells before assu...diebuche11:31, 2 May 2011
r87244Tablesorter: use mw.config.get() to access globals; force mdy for english con...diebuche11:55, 2 May 2011
r87322Tablesorter: Add a title attribute to sort arrows ( Bug 21453 )diebuche11:44, 3 May 2011
r90595Add initial QUnit JS test cases for jquery.tablesorter -- NOTE THERE ARE IN F...brion19:30, 22 June 2011
r90612Fix tablesorting bug that caused weird interferences between two tables; Make...diebuche21:54, 22 June 2011
r90613Followup r90595: Tweak tablesorter tests so they run on r86088 version of jqu...brion21:56, 22 June 2011
r90619Followup r87243: qunit test cases for bug 28775 (German-style date sorting)brion22:24, 22 June 2011
r90630Followup r86088, r87244, r90612: fix jquery.tablesorter for null collation ta...brion23:19, 22 June 2011
r90637Followup r86088: test cases and a correction for bug 17141 (IPv4 address sort...brion00:37, 23 June 2011
r90666Fix global var leaking that was making collationsort fail on Safari 5...hartman17:12, 23 June 2011
r90678Switch jquery.sortable to use mw-sortable and mw-unsortable classes, to be in...hartman21:26, 23 June 2011
r91207r86088: Adding legacy support for .sortbottom & a test for it per CRdiebuche19:40, 30 June 2011
r105560basic tests for bug 8115hashar16:27, 8 December 2011

Comments

#Comment by DieBuche (talk | contribs)   21:48, 14 April 2011

Weird something stripped the whitespace in the bench table Browser Before After


------ -----

Chrome 10 90ms 42ms Safari 5 115ms 48ms Firefox 4 412ms 87ms IE8 720ms 115ms

#Comment by DieBuche (talk | contribs)   21:49, 14 April 2011

That's not any better. *wishes for edit button*

#Comment by Catrope (talk | contribs)   22:16, 14 April 2011

Provisionally tagging revert1.18; this is a rewrite that we'll probably want to defer to 1.19, assuming 1.18 will be branched in the next few days.

#Comment by Nikerabbit (talk | contribs)   05:52, 15 April 2011

You should remove all old code that is no longer in use. Otherwise it will just confuse people later.

-	jQuery( document ).ready( sortables_init );
+	//jQuery( document ).ready( sortables_init );
By setting class="sort-{Parsername}", the row will be parsed with the specified

Should use the data- parameters like the data-sort-value, which btw I don't see supported here!

And you didn't close those bugs?

#Comment by DieBuche (talk | contribs)   06:57, 15 April 2011

I somehow had the feeling that data stuff would be stripped. I was hesitant to close them because of the revert1.18 tag, will it only be reverted in the future REL1.18 branch?

#Comment by Nikerabbit (talk | contribs)   08:11, 15 April 2011

It means that if it works, it will stay in trunk and future releases, but it will not go into the 1.18 release. data- attributes has been allowed for a while already.

#Comment by DieBuche (talk | contribs)   08:33, 15 April 2011

Ok, old code is removed & and I added support for data-sort-value & moved the other one to data-sort-type

#Comment by He7d3r (talk | contribs)   13:04, 15 April 2011

Isn't tableSorterCollation something that should go into mw.config.get( ... )?

#Comment by Catrope (talk | contribs)   15:06, 15 April 2011
+		#$localDateFormats = $wgContLang->getDateFormats();
+		#$localPreferedFormat = $localDateFormats[$wgContLang->getDefaultDateFormat().' date'];
+

Please don't introduce commented-out code. There's another commented-out line a few lines down.

		
+		$monthNames = array('');
+		$monthNamesShort = array('');
+		for ($i=1; $i < 13; $i++) { 
+			$monthNames[]=$wgContLang->getMonthName($i);
+			$monthNamesShort[]=$wgContLang->getMonthAbbreviation($i);
+		}

This does not conform to whitespace conventions. Also, this array building code should be moved into the Language class so you can just call e.g. $wgContLang->getMonthNames()

#Comment by Bawolff (talk | contribs)   20:24, 16 April 2011

Is the eval really necessary?

#Comment by DieBuche (talk | contribs)   08:33, 17 April 2011

I'm not fond of it either, but it's necessary in order to use Array.sort ( http://www.w3schools.com/jsref/jsref_sort.asp ) Array.sort accepts a sorter function, and there no way to build this fn w/o eval.

#Comment by Brion VIBBER (talk | contribs)   19:59, 22 June 2011

There are some serious problems with this -- I've added a few initial qunit tests on trunk in r90595 and can confirm the failures I'm seeing manually:

  • numeric sort doesn't work (values of different magnitudes are somehow sorting wrong)
  • interference between tables? sort one table by name, then another and you often find that it sorts totally incorrectly.
#Comment by Brion VIBBER (talk | contribs)   21:33, 22 June 2011

Numeric sort seems to be being broken by the date detection: numbers like '1234', '1234.5' etc match this 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);
#Comment by Brion VIBBER (talk | contribs)   21:53, 22 June 2011

It looks like these regressions are actually showing up on r86337 and later, but not on r86088 or r86305 (which both test clean once I adjust the test to set global month vars).

#Comment by DieBuche (talk | contribs)   22:01, 22 June 2011

Fixed in r90612

#Comment by Brion VIBBER (talk | contribs)   22:12, 22 June 2011

Wow, thanks for the quick fix! I'd only just narrowed it down to that commit and it would have taken me a while to figure out all the scary sort bits. ;)

This resolves known actual bugs with the new sorting; should just need some more test cases for the fixed regressions.

#Comment by TheDJ (talk | contribs)   21:01, 23 June 2011

Since we have mw-collapsible and mw-collopsed now... Should we also change sortable and unsortable to mw-sortable and mw-unsortable ????

Will be a lot of work to replace all that in the existing wikicode, but it's something to consider I think.

#Comment by TheDJ (talk | contribs)   10:15, 28 June 2011

FYI, this breaks sortbottom behavior, because we have no tfoot support in wikitables yet. See also: bug 4740

#Comment by TheDJ (talk | contribs)   18:49, 30 June 2011

I was incorrect, we do have tfoot support apparently in 1.18 (r85922), but all content in Wikipedia currently assumes 'sortbottom'. I've been testing a few tables that use sortbottom, and i think that about 50% will break when we deploy this, because they don't use ! on all cells in their tablerow to indicate that this 'sortbottom' row is a footer.

- We now have better syntax - The old syntax is broken

So the question is: - Should we also support the old syntax in order to prevent drama ? - Should we explain up front to people what they need to fix in the articles in order to make it working again ?

#Comment by Brion VIBBER (talk | contribs)   18:52, 30 June 2011
  • Should we also support the old syntax in order to prevent drama ?
    • Yes we should.
#Comment by DieBuche (talk | contribs)   19:41, 30 June 2011

It's wrapping sortbottoms inside tfoot's as of r91207

#Comment by TheDJ (talk | contribs)   19:58, 30 June 2011

OK, as far as I can tell all known issues should now be fixed. Great work Diebuche !!!

#Comment by TheDJ (talk | contribs)   13:24, 14 August 2011

Marking as resolved, I know of no further issues with this rewrite.

Status & tagging log