new natural sort algorithm

This commit is contained in:
Rob Garrison 2012-05-04 20:42:04 -05:00
parent ebf50c950a
commit 1ac4778369
7 changed files with 187 additions and 88 deletions

Binary file not shown.

View File

@ -148,7 +148,7 @@
<div class="next-up"> <div class="next-up">
<hr /> <hr />
Next up: <a href="example-option-digits.html">Dealing with digits! &rsaquo;&rsaquo;</a> Next up: <a href="example-multiple-tbodies.html">Sorting with Multiple Tbodies &rsaquo;&rsaquo;</a>
</div> </div>
</div> </div>

View File

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery plugin: Tablesorter 2.0 - Sorting with Multiple Tbodies</title>
<!-- jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<!-- Demo stuff -->
<link rel="stylesheet" href="css/jq.css">
<script src="js/chili/jquery.chili-2.2.js"></script>
<script src="js/chili/recipes.js"></script>
<script src="js/docs.js"></script>
<!-- Tablesorter: required -->
<link rel="stylesheet" href="../css/blue/style.css">
<script src="../js/jquery.tablesorter.js"></script>
<script id="js">$(function() {
$("table").tablesorter({
cssInfoBlock : "tablesorter-no-sort"
});
});</script>
</head>
<body>
<div id="banner">
<h1>table<em>sorter</em></h1>
<h2>Sorting with Multiple Tbodies</h2>
<h3>Flexible client-side table sorting</h3>
<a href="index.html">Back to documentation</a>
</div>
<div id="main">
<h1>Demo</h1>
<div id="demo"><table class="sortable">
<thead>
<tr><th>Name</th><th>A-head</th><th>B-head</th><th>C-head</th></tr>
</thead>
<tfoot>
<tr><th>Name</th><th>A-foot</th><th>B-foot</th><th>C-foot</th></tr>
</tfoot>
<tbody>
<tr><td>DDD</td><td>r1c1</td><td>r1c2</td><td>r1c3</td></tr>
<tr><td>AAA</td><td>r2c1</td><td>r2c2</td><td>r2c3</td></tr>
<tr><td>CCC</td><td>r3c1</td><td>r3c2</td><td>r3c3</td></tr>
<tr><td>BBB</td><td>r4c1</td><td>r4c2</td><td>r4c3</td></tr>
</tbody>
<!-- add "tablesorter-no-sort" class to skip sorting of this tbody -->
<tbody class="tablesorter-no-sort">
<tr><th colspan="4">summary info for the first group</th></tr>
<tr><th colspan="4">Another row referring to the first group</th></tr>
</tbody>
<tbody>
<tr><td>Zorro</td><td>r5c1</td><td>r5c2</td><td>r5c3</td></tr>
<tr><td>Caleb</td><td>r6c1</td><td>r6c2</td><td>r6c3</td></tr>
<tr><td>Momo</td><td>r7c1</td><td>r7c2</td><td>r7c3</td></tr>
<tr><td>Wolfie</td><td>r8c1</td><td>r8c2</td><td>r8c3</td></tr>
</tbody>
<tbody class="tablesorter-no-sort">
<tr><th colspan="4">summary info for the second group</th></tr>
</tbody>
<tbody>
<tr><td>Ulysses</td><td>r9c1</td><td>r9c2</td><td>r9c3</td></tr>
<tr><td>Penelope</td><td>r10c1</td><td>r10c2</td><td>r10c3</td></tr>
<tr><td>Edvald</td><td>r11c1</td><td>r11c2</td><td>r11c3</td></tr>
<tr><td>Jan</td><td>r12c1</td><td>r12c2</td><td>r12c3</td></tr>
</tbody>
<tbody class="tablesorter-no-sort">
<tr><th colspan="4">summary info for the last group</th></tr>
</tbody>
</table></div>
<h1>Javascript</h1>
<div id="javascript">
<pre class="js"></pre>
</div>
<h1>HTML</h1>
<div id="html">
<pre class="html"></pre>
</div>
<div class="next-up">
<hr />
Next up: <a href="example-option-digits.html">Dealing with digits! &rsaquo;&rsaquo;</a>
</div>
</div>
</body>
</html>

View File

@ -294,6 +294,7 @@
<li><a href="example-option-sort-key.html">Change the default multi-sorting key</a></li> <li><a href="example-option-sort-key.html">Change the default multi-sorting key</a></li>
<li><a href="example-option-custom-sort.html">Custom sort script</a> <span class="tip"><em>New!</em></span> v2.2</li> <li><a href="example-option-custom-sort.html">Custom sort script</a> <span class="tip"><em>New!</em></span> v2.2</li>
<li><a href="example-locale-sort.html">Sorting Accented Characters</a> <span class="tip"><em>New!</em></span> v2.2</li> <li><a href="example-locale-sort.html">Sorting Accented Characters</a> <span class="tip"><em>New!</em></span> v2.2</li>
<li><a href="example-multiple-tbodies.html">Sorting with Multiple Tbodies</a> <span class="tip"><em>New!</em></span> v2.2</li>
</ul> </ul>
<h4>Parsers / Extracting Content</h4> <h4>Parsers / Extracting Content</h4>

View File

@ -1,5 +1,5 @@
/*! /*!
* TableSorter 2.2.1 - Client-side table sorting with ease! * TableSorter 2.2.2 - Client-side table sorting with ease!
* @requires jQuery v1.2.6+ * @requires jQuery v1.2.6+
* *
* Copyright (c) 2007 Christian Bach * Copyright (c) 2007 Christian Bach
@ -18,7 +18,7 @@
$.extend({ $.extend({
tablesorter: new function() { tablesorter: new function() {
this.version = "2.2.1"; this.version = "2.2.2";
var parsers = [], widgets = [], tbl; var parsers = [], widgets = [], tbl;
this.defaults = { this.defaults = {
@ -26,7 +26,7 @@
cssAsc: "tablesorter-headerSortUp", cssAsc: "tablesorter-headerSortUp",
cssDesc: "tablesorter-headerSortDown", cssDesc: "tablesorter-headerSortDown",
cssChildRow: "expand-child", cssChildRow: "expand-child",
cssInfoBlock: "tablesorter-infoOnly", cssInfoBlock: "tablesorter-infoOnly", // don't sort tbody with this class name
sortInitialOrder: "asc", sortInitialOrder: "asc",
sortMultiSortKey: "shiftKey", sortMultiSortKey: "shiftKey",
sortForce: null, sortForce: null,
@ -224,27 +224,29 @@
} }
for (k = 0; k < b.length; k++) { for (k = 0; k < b.length; k++) {
tc.cache[k] = { row: [], normalized: [] }; tc.cache[k] = { row: [], normalized: [] };
totalRows = (b[k] && b[k].rows.length) || 0; // ignore tbodies with class name from css.cssInfoBlock
totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0; if (!$(b[k]).hasClass(tc.cssInfoBlock)) {
totalRows = (b[k] && b[k].rows.length) || 0;
for (i = 0; i < totalRows; ++i) { totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0;
/** Add the table data to main data array */ for (i = 0; i < totalRows; ++i) {
c = $(b[k].rows[i]); /** Add the table data to main data array */
cols = []; c = $(b[k].rows[i]);
// if this is a child row, add it to the last row's children and continue to the next row cols = [];
if (c.hasClass(tc.cssChildRow)) { // if this is a child row, add it to the last row's children and continue to the next row
tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c); if (c.hasClass(tc.cssChildRow)) {
// go to the next for loop tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c);
continue; // go to the next for loop
continue;
}
tc.cache[k].row.push(c);
for (j = 0; j < totalCells; ++j) {
t = trimAndGetNodeText(tc, c[0].cells[j], j);
// don't bother parsing if the string is empty - previously parsing would change it to zero
cols.push( parsers[j].format(t, table, c[0].cells[j], j) );
}
cols.push(tc.cache[k].normalized.length); // add position for rowCache
tc.cache[k].normalized.push(cols);
} }
tc.cache[k].row.push(c);
for (j = 0; j < totalCells; ++j) {
t = trimAndGetNodeText(tc, c[0].cells[j], j);
// don't bother parsing if the string is empty - previously parsing would change it to zero
cols.push( parsers[j].format(t, table, c[0].cells[j], j) );
}
cols.push(tc.cache[k].normalized.length); // add position for rowCache
tc.cache[k].normalized.push(cols);
} }
} }
if (tc.debug) { if (tc.debug) {
@ -287,23 +289,25 @@
appendTime = new Date(); appendTime = new Date();
} }
for (k = 0; k < b.length; k++) { for (k = 0; k < b.length; k++) {
f = document.createDocumentFragment(); if (!$(b[k]).hasClass(c.cssInfoBlock)){
r = c.cache[k].row; f = document.createDocumentFragment();
n = c.cache[k].normalized; r = c.cache[k].row;
totalRows = n.length; n = c.cache[k].normalized;
checkCell = totalRows ? (n[0].length - 1) : 0; totalRows = n.length;
for (i = 0; i < totalRows; i++) { checkCell = totalRows ? (n[0].length - 1) : 0;
pos = n[i][checkCell]; for (i = 0; i < totalRows; i++) {
rows.push(r[pos]); pos = n[i][checkCell];
// removeRows used by the pager plugin rows.push(r[pos]);
if (!c.appender || !c.removeRows) { // removeRows used by the pager plugin
l = r[pos].length; if (!c.appender || !c.removeRows) {
for (j = 0; j < l; j++) { l = r[pos].length;
f.appendChild(r[pos][j]); for (j = 0; j < l; j++) {
f.appendChild(r[pos][j]);
}
} }
} }
table.tBodies[k].appendChild(f);
} }
table.tBodies[k].appendChild(f);
} }
if (c.appender) { if (c.appender) {
c.appender(table, rows); c.appender(table, rows);
@ -417,24 +421,6 @@
return $tableHeaders; return $tableHeaders;
} }
// Part of original tablesorter - not even called.
function checkCellColSpan(table, rows, row) {
var i, cell, arr = [],
r = table.tHead.rows,
c = r[row].cells;
for (i = 0; i < c.length; i++) {
cell = c[i];
if (cell.colSpan > 1) {
arr = arr.concat(checkCellColSpan(table, rows, row++)); // what is headerArr?
} else {
if (table.tHead.length === 1 || (cell.rowSpan > 1 || !r[row + 1])) {
arr.push(cell);
}
}
}
return arr;
}
function isValueInArray(v, a) { function isValueInArray(v, a) {
var i, l = a.length; var i, l = a.length;
for (i = 0; i < l; i++) { for (i = 0; i < l; i++) {
@ -535,34 +521,40 @@
// Natural sort modified from: http://www.webdeveloper.com/forum/showthread.php?t=107909 // Natural sort modified from: http://www.webdeveloper.com/forum/showthread.php?t=107909
function sortText(a, b, col) { function sortText(a, b, col) {
if (a === b) { return 0; } if (a === b) { return 0; }
var c = tbl[0].config, cnt = 0, L, t, x, e = c.string[ (c.empties[col] || c.emptyTo ) ]; var c = tbl[0].config, e = c.string[ (c.empties[col] || c.emptyTo ) ],
r = $.tablesorter.regex, xN, xD, yN, yD, xF, yF, i, mx;
if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : -e || -1; } if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : -e || -1; }
if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : e || 1; } if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : e || 1; }
if (typeof c.textSorter === 'function') { return c.textSorter(a, b); } if (typeof c.textSorter === 'function') { return c.textSorter(a, b); }
// if (c.sortLocaleCompare) { return a.localeCompare(b); } // natural sort - https://github.com/overset/javascript-natural-sort
try { // chunk/tokenize
x = /^(\.)?\d/; xN = a.replace(r[0], '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
L = Math.min(a.length, b.length) + 1; yN = b.replace(r[0], '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0');
while (cnt < L && a.charAt(cnt) === b.charAt(cnt) && x.test(b.substring(cnt)) === false && x.test(a.substring(cnt)) === false) { cnt++; } // numeric, hex or date detection
a = a.substring(cnt); xD = parseInt(a.match(r[2])) || (xN.length !== 1 && a.match(r[1]) && Date.parse(a));
b = b.substring(cnt); yD = parseInt(b.match(r[2])) || (xD && b.match(r[1]) && Date.parse(b)) || null;
if (x.test(a) || x.test(b)) { // first try and sort Hex codes or Dates
if (x.test(a) === false) { if (yD) {
return (a) ? 1 : -1; if ( xD < yD ) { return -1; }
} else if (x.test(b) === false) { if ( xD > yD ) { return 1; }
return (b) ? -1 : 1;
} else {
t = parseFloat(a) - parseFloat(b);
if (t !== 0) { return t; } else { t = a.search(/[^\.\d]/); }
if (t === -1) { t = b.search(/[^\.\d]/); }
a = a.substring(t);
b = b.substring(t);
}
}
return (a > b) ? 1 : -1;
} catch (er) {
return 0;
} }
mx = Math.max(xN.length, yN.length);
// natural sorting through split numeric strings and default strings
for (i = 0; i < mx; i++) {
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
xF = (!(xN[i] || '').match(r[3]) && parseFloat(xN[i])) || xN[i] || 0;
yF = (!(yN[i] || '').match(r[3]) && parseFloat(yN[i])) || yN[i] || 0;
// handle numeric vs string comparison - number < string - (Kyle Adams)
if (isNaN(xF) !== isNaN(yF)) { return (isNaN(xF)) ? 1 : -1; }
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
if (typeof xF !== typeof yF) {
xF += '';
yF += '';
}
if (xF < yF) { return -1; }
if (xF > yF) { return 1; }
}
return 0;
} }
function sortTextDesc(a, b, col) { function sortTextDesc(a, b, col) {
@ -571,7 +563,6 @@
if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : e || 1; } if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : e || 1; }
if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : -e || -1; } if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : -e || -1; }
if (typeof c.textSorter === 'function') { return c.textSorter(b, a); } if (typeof c.textSorter === 'function') { return c.textSorter(b, a); }
// if (c.sortLocaleCompare) { return b.localeCompare(a); }
return sortText(b, a); return sortText(b, a);
} }
@ -836,6 +827,13 @@
// replace all unwanted chars and match. // replace all unwanted chars and match.
return (/^[\-+(]?\d*[)]?$/).test($.trim(s.replace(/[,.'\s]/g, ''))); return (/^[\-+(]?\d*[)]?$/).test($.trim(s.replace(/[,.'\s]/g, '')));
}; };
// regex used in natural sort
this.regex = [
/(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?$|^0x[0-9a-f]+$|[0-9]+)/gi, // chunk/tokenize numbers & letters
/(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, //date
/^0x[0-9a-f]+$/i, // hex
/^0/ // leading zeros
];
// used when replacing accented characters during sorting // used when replacing accented characters during sorting
this.characterEquivalents = { this.characterEquivalents = {
"a" : "\u00e1\u00e0\u00e2\u00e3\u00e4", // áàâãä "a" : "\u00e1\u00e0\u00e2\u00e3\u00e4", // áàâãä
@ -1030,7 +1028,7 @@
ts.addWidget({ ts.addWidget({
id: "zebra", id: "zebra",
format: function(table) { format: function(table) {
var $tr, row, even, time, k, var $tr, $r, row, even, time, k,
c = table.config, c = table.config,
child = c.cssChildRow, child = c.cssChildRow,
b = table.tBodies, b = table.tBodies,
@ -1047,11 +1045,11 @@
$tr = $(b[k]).filter(':not(' + c.cssInfoBlock + ')').find('tr:visible:not(.' + c.cssInfoBlock + ')'); $tr = $(b[k]).filter(':not(' + c.cssInfoBlock + ')').find('tr:visible:not(.' + c.cssInfoBlock + ')');
if ($tr.length > 1) { if ($tr.length > 1) {
$tr.each(function() { $tr.each(function() {
$tr = $(this); $r = $(this);
// style children rows the same way the parent row was styled // style children rows the same way the parent row was styled
if (!$tr.hasClass(child)) { row++; } if (!$r.hasClass(child)) { row++; }
even = (row % 2 === 0); even = (row % 2 === 0);
$tr $r
.removeClass(css[even ? 1 : 0]) .removeClass(css[even ? 1 : 0])
.addClass(css[even ? 0 : 1]); .addClass(css[even ? 0 : 1]);
}); });

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{ {
"name": "tablesorter", "name": "tablesorter",
"version": "2.2.1", "version": "2.2.2",
"title": "tablesorter", "title": "tablesorter",
"author": { "author": {
"name": "Christian Bach", "name": "Christian Bach",