Permalink
Browse files

Refactor HtmlTable.Sort's sort.

+ Refactor of HtmlTable.Sort
+ Removes internal reference to zebra (that was gross)
+ Fixes Broken tests -- all tests now pass
+ Adds spec for Sort Parsers
+ Corrects Sort Behavior for string sort (toLowerCase)
  https://mootools.lighthouseapp.com/projects/24057/tickets/261-htmltablesort-behaviour-with-string-sort
+ Adds shared style endpoint to HtmlTable to .Zebra class
+ Remove Infinite loop in class.refactor
  • Loading branch information...
1 parent bbbf50b commit 5a40b1c3df528d4d14b387c60db8c12fdc8761d9 @fat fat committed with anutron Nov 27, 2010
View
4 Source/Class/Class.Refactor.js
@@ -27,10 +27,10 @@ Class.refactor = function(original, refactors){
Object.each(refactors, function(item, name){
var origin = original.prototype[name];
if (origin && origin.$origin) origin = origin.$origin;
- if (origin && typeof item == 'function'){
+ if (typeof item == 'function'){
original.implement(name, function(){
var old = this.previous;
- this.previous = origin;
+ this.previous = origin || function(){};
@arian
MooTools member
arian added a line comment Mar 30, 2011

This introduced this bug: https://mootools.lighthouseapp.com/projects/24057/tickets/554-classrefactor-bug-in-1311#ticket-554-2

Also now every method is wrapped by this function, which has less performance than directly implementing it. The advantage however is that every method has a .previous method, although that's not very useful yet if it does nothing, but can be useful when you do multiple refactors i guess...

@arian
MooTools member
arian added a line comment Mar 30, 2011

even nicer would be to do:

origin = (origin && origin.$origin) || origin || function(){};

at line 29

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
var value = item.apply(this, arguments);
this.previous = old;
return value;
View
163 Source/Interface/HtmlTable.Sort.js
@@ -12,6 +12,7 @@ license: MIT-style license
authors:
- Harald Kirschner
- Aaron Newton
+ - Jacob Thornton
requires:
- Core/Hash
@@ -87,7 +88,7 @@ HtmlTable = Class.refactor(HtmlTable, {
this.sortSpans.push(sortSpan);
var parser = parsers[index],
- cancel;
+ cancel;
switch (typeOf(parser)){
case 'function': parser = {convert: parser}; cancel = true; break;
case 'string': parser = parser; cancel = true; break;
@@ -121,100 +122,110 @@ HtmlTable = Class.refactor(HtmlTable, {
return false;
},
- sort: function(index, reverse, pre){
- if (!this.head) return;
- var classCellSort = this.options.classCellSort;
- var classGroup = this.options.classGroup,
- classGroupHead = this.options.classGroupHead;
+ setSortedState: function(index, reverse){
+ if (reverse != null) this.sorted.reverse = reverse;
+ else if (this.sorted.index == index) this.sorted.reverse = !this.sorted.reverse;
+ else this.sorted.reverse = this.sorted.index == null;
- if (!pre){
- if (index != null){
- if (this.sorted.index == index){
- this.sorted.reverse = !(this.sorted.reverse);
- } else {
- if (this.sorted.index != null){
- this.sorted.reverse = false;
- this.head.cells[this.sorted.index].removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev);
- } else {
- this.sorted.reverse = true;
- }
- this.sorted.index = index;
- }
- } else {
- index = this.sorted.index;
- }
+ if (index != null) this.sorted.index = index;
+ },
+
+ setHeadSort: function(sorted){
+ var head = document.id(this.head.cells[this.sorted.index]);
+ if (!head) return;
+ if (sorted){
+ head.addClass(this.options.classHeadSort);
+ if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev);
+ else head.removeClass(this.options.classHeadSortRev);
+ }
+ else {
+ head.removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev);
+ }
+ },
+
+ setRowSort: function(data, pre){
+ var count = data.length,
+ body = this.body,
+ group,
+ rowIndex;
+
+ while (count){
+ var item = data[--count],
+ position = item.position,
+ row = body.rows[position];
- if (reverse != null) this.sorted.reverse = reverse;
+ if (row.disabled) continue;
+ if (!pre){
+ group = this.setGroupSort(group, row, item);
+ this.setRowStyle(row, count);
+ }
+ body.appendChild(row);
- var head = document.id(this.head.cells[index]);
- if (head){
- head.addClass(this.options.classHeadSort);
- if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev);
- else head.removeClass(this.options.classHeadSortRev);
+ for (rowIndex = 0; rowIndex < count; rowIndex++) {
+ if (data[rowIndex].position > position) data[rowIndex].position--;
}
+ };
+ },
+
+ setRowStyle: function(row, i){
+ this.previous(row, i);
+ row.cells[this.sorted.index].addClass(this.options.classCellSort);
+ },
+
+ setGroupSort: function(group, row, item){
+ if (group == item.value) row.removeClass(this.options.classGroupHead).addClass(this.options.classGroup);
+ else row.removeClass(this.options.classGroup).addClass(this.options.classGroupHead);
+ return item.value;
+ },
+
+ getParser: function(){
+ var parser = this.parsers[this.sorted.index];
+ return typeOf(parser) == 'string' ? HtmlTable.Parsers[parser] : parser;
+ },
- this.body.getElements('td').removeClass(this.options.classCellSort);
+ sort: function(index, reverse, pre){
+ if (!this.head) return;
+
+ if (!pre){
+ this.clearSort();
+ this.setSortedState(index, reverse);
+ this.setHeadSort(true);
}
- var parser = this.parsers[index];
- if (typeOf(parser) == 'string') parser = HtmlTable.Parsers[parser];
+ var parser = this.getParser();
if (!parser) return;
if (!Browser.ie){
var rel = this.body.getParent();
this.body.dispose();
}
- var data = Array.map(this.body.rows, function(row, i){
- var value = parser.convert.call(document.id(row.cells[index]));
-
- return {
- position: i,
- value: value,
- toString: function(){
- return value.toString();
- }
- };
- }, this);
- data.reverse(true);
-
- data.sort(function(a, b){
+ var data = this.parseData(parser).sort(function(a, b){
if (a.value === b.value) return 0;
return a.value > b.value ? 1 : -1;
});
- if (!this.sorted.reverse) data.reverse(true);
-
- var i = data.length, body = this.body;
- var j, position, entry, group;
-
- while (i){
- var item = data[--i];
- position = item.position;
- var row = body.rows[position];
- if (row.disabled) continue;
-
- if (!pre){
- if (group === item.value){
- row.removeClass(classGroupHead).addClass(classGroup);
- } else {
- group = item.value;
- row.removeClass(classGroup).addClass(classGroupHead);
- }
- if (this.options.zebra) this.zebra(row, i);
-
- row.cells[index].addClass(classCellSort);
- }
+ if (this.sorted.reverse == (this.parsers[this.sorted.index] == 'input-checked')) data.reverse(true);
+ this.setRowSort(data, pre);
+
+ if (rel) rel.grab(this.body);
+
+ return this.fireEvent('sort', [this.body, this.sorted.index]);
+ },
- body.appendChild(row);
- for (j = 0; j < i; j++){
- if (data[j].position > position) data[j].position--;
- }
- };
- data = null;
- if (rel) rel.grab(body);
+ parseData: function(parser){
+ return Array.map(this.body.rows, function(row, i){
+ var value = parser.convert.call(document.id(row.cells[this.sorted.index]));
+ return {
+ position: i,
+ value: value
+ };
+ }, this);
+ },
- return this.fireEvent('sort', [body, index]);
+ clearSort: function(){
+ this.setHeadSort(false);
+ this.body.getElements('td').removeClass(this.options.classCellSort);
},
reSort: function(){
@@ -296,7 +307,7 @@ HtmlTable.Parsers = {
'string': {
match: null,
convert: function(){
- return this.get('text').stripTags();
+ return this.get('text').stripTags().toLowerCase();
}
},
'title': {
View
5 Source/Interface/HtmlTable.Zebra.js
@@ -39,6 +39,11 @@ HtmlTable = Class.refactor(HtmlTable, {
Array.each(this.body.rows, this.zebra, this);
},
+ setRowStyle: function(row, i){
+ if (this.previous) this.previous(row, i);
+ this.zebra(row, i);
+ },
+
zebra: function(row, i){
return row[((i % 2) ? 'remove' : 'add')+'Class'](this.options.classZebra);
},
View
128 Specs/1.3/Interface/HtmlTable.Sort.js
@@ -0,0 +1,128 @@
+describe('HtmlTable.Sort', function(){
+
+ describe('HtmlTable.Parsers', function(){
+
+ var sortedTable = function(type, data){
+ var table = new HtmlTable({
+ sortable: true,
+ headers: ['col'],
+ parsers: [type],
+ rows: data.map(function(item){return [item]}).shuffle()
+ });
+
+ return Array.map(table.sort(0, false).body.rows, function(item){
+ return item.cells[0].get('text') || item.cells[0].getElement('input').get('value')
+ });
+ }
+
+ describe('date', function(){
+
+ it('should sort on date', function(){
+ expect(sortedTable('date', ['1/2/08', '2/4/10', '3/4/10'])).toEqual(['1/2/08', '2/4/10', '3/4/10']);
+ });
+
+ it('should accept multiple date types', function(){
+ expect(sortedTable('date', ['1/2/08', 'Jan 5 2010', '01/08/2010'])).toEqual(['1/2/08', 'Jan 5 2010', '01/08/2010']);
+ });
+
+ });
+
+ describe('input-', function(){
+
+ var data = [
+ new Element('input', {type: 'checkbox', checked: true, value: 'a'}),
+ new Element('input', {type: 'checkbox', checked: true, value: 'b'}),
+ new Element('input', {type: 'checkbox', checked: false, value: 'c'}),
+ new Element('input', {type: 'checkobx', checked: false, value: 'd'})
+ ];
+
+ describe('input-checked', function(){
+
+ it('should sort by checked inputs', function(){
+ var result = sortedTable('input-checked', data);
+ expect(result[0]).not.toEqual('d');
+ expect(result[0]).not.toEqual('c');
+ expect(result[3]).not.toEqual('b');
+ expect(result[3]).not.toEqual('a');
+ });
+
+ });
+
+ describe('input-value', function(){
+
+ it('should sort by input value', function(){
+ expect(sortedTable('input-value', data)).toEqual(['a', 'b', 'c', 'd']);
+ });
+
+ });
+
+ });
+
+ describe('number', function(){
+
+ it('should sort a list numerically', function(){
+ expect(sortedTable('number', [1, 2, 3])).toEqual(['1','2','3']);
+ });
+
+ it('should accept numbers as strings', function(){
+ expect(sortedTable('number', ['1', 2, '3'])).toEqual(['1','2','3']);
+ });
+
+ it('should not sort floats according to value', function(){
+ expect(sortedTable('number', ['.03', '.2', '1'])).not.toEqual(['.03', '.2', '1']);
+ });
+
+ });
+
+ describe('numberLax', function(){
+
+ it('should sort a alphanumerical list numerically', function(){
+ expect(sortedTable('number', ['1a', '4b', '12c'])).toEqual(['1a','4b','12c']);
+ });
+
+ });
+
+ describe('float', function(){
+
+ it('should correctly sort floats according to value', function(){
+ expect(sortedTable('float', ['.03', '.2', '1'])).toEqual(['.03', '.2', '1']);
+ });
+
+ });
+
+ describe('floatLax', function(){
+
+ it('should correctly sort alpha-floats according to value', function(){
+ expect(sortedTable('float', ['.03a', '.2b', '1c'])).toEqual(['.03a', '.2b', '1c']);
+ });
+
+ });
+
+ describe('string', function(){
+
+ it('should sort a list alphabetically', function(){
+ expect(sortedTable('string', ['a','b','c'])).toEqual(['a','b','c']);
+ });
+
+ it('should not be case sensitive', function(){
+ expect(sortedTable('string', ['A','b','C'])).toEqual(['A','b','C']);
+ });
+
+ });
+
+ describe('title', function(){
+
+ it('should sort a list alphabetically by title', function(){
+ var data = [
+ new Element('div', {title: 'a', text: 'a'}),
+ new Element('div', {title: 'b', text: 'b'}),
+ new Element('div', {title: 'c', text: 'c'})
+ ];
+ expect(sortedTable('string', data)).toEqual(['a','b','c']);
+ });
+
+ });
+
+ });
+
+});
View
4 Specs/Configuration.js
@@ -55,7 +55,7 @@ Configuration.sets = {
'Element/Element.Forms', 'Element/Element.Measure', 'Element/Elements.From', 'Element/Element.Shortcuts',
'Element/Element.Event.Pseudos', 'Element/Element.Event.Pseudos.Keys', 'Element/Element.Delegation',
'Types/URI', 'Types/URI.Relative',
- 'Interface/Keyboard',
+ 'Interface/Keyboard', 'Interface/HtmlTable.Sort',
'Request/Request.JSONP',
'Utilities/Hash.Cookie', 'Utilities/Assets'
]
@@ -173,6 +173,8 @@ Configuration.source = {
'Types/URI.Relative',
'Interface/Keyboard',
+ 'Interface/HtmlTable',
+ 'Interface/HtmlTable.Sort',
'Request/Request.JSONP',
View
5 Tests/Interface/HtmlTable_(sortable).html
@@ -69,10 +69,10 @@
<tr><td>10-12-2010</td></tr>
</tbody>
</table>
-
<script src="/depender/build?require=More/HtmlTable.Sort"></script>
<script>
new HtmlTable($('hidden_sorts'), {
+ parsers: ['number', 'number'],
sortable: true
});
new HtmlTable($('date_sort_w_empty'), {
@@ -89,7 +89,6 @@
t.push(['plumbs', 'purple']);
t.push(['grapes', 'green']);
-
var data = [
{"id":56,"timezone":"Europe/Amsterdam","name":"Amsterdam","geolat":52.3789,"geolong":4.90067},
{"id":46,"timezone":"America/New_York","name":"Atlanta","geolat":33.7525,"geolong":-84.3888},
@@ -149,7 +148,6 @@
});
$(myTable).inject($('mt-content'));
-
headers = ["hostname","model","rack","expire"];
rows = [
@@ -182,5 +180,4 @@
sortable: true,
});
myTable.inject($('mt-content'));
-
</script>
View
93 Tests/Interface/HtmlTable_(zebra).html
@@ -0,0 +1,93 @@
+<style>
+table {
+ border: solid #777;
+ border-width: 0px 0px 1px 1px;
+ margin: 10px 0px;
+}
+thead {
+ background: #777;
+ border-bottom: 1px solid #333;
+ color: #fff;
+}
+tr.table-tr-odd {
+ background: #ccc;
+}
+td, th {
+ padding: 4px;
+ border-right: 1px solid #777;
+ border-top: 1px solid #777;
+}
+th, th:hover {
+ cursor: pointer;
+ width: 100px;
+}
+th.table-th-sort {
+ color: #000;
+ background: #fff url(/asset/more/bullet_arrow_down.png) no-repeat right 3px;
+}
+th.table-th-sort-rev {
+ background: #fff url(/asset/more/bullet_arrow_up.png) no-repeat right 3px;
+}
+</style>
+<script src="/depender/build?require=More/HtmlTable.Sort,More/HtmlTable.Zebra"></script>
+<script>
+var data = [
+ {"id":56,"timezone":"Europe/Amsterdam","name":"Amsterdam","geolat":52.3789,"geolong":4.90067},
+ {"id":46,"timezone":"America/New_York","name":"Atlanta","geolat":33.7525,"geolong":-84.3888},
+ {"id":42,"timezone":"America/Chicago","name":"Austin","geolat":30.2669,"geolong":-97.7428},
+ {"id":63,"timezone":"America/New_York","name":"Baltimore","geolat":39.294255,"geolong":-76.614275},
+ {"id":24,"timezone":"America/New_York","name":"Boston","geolat":42.3583,"geolong":-71.0603},
+ {"id":32,"timezone":"America/Chicago","name":"Chicago","geolat":41.8858,"geolong":-87.6181},
+ {"id":64,"timezone":"America/New_York","name":"Cleveland","geolat":41.499819,"geolong":-81.693716},
+ {"id":43,"timezone":"America/Chicago","name":"Dallas / Fort Worth","geolat":32.7887,"geolong":-96.7676},
+ {"id":25,"timezone":"America/Denver","name":"Denver","geolat":39.734,"geolong":-105.026},
+ {"id":47,"timezone":"America/New_York","name":"Detroit","geolat":42.3333,"geolong":-83.0484},
+ {"id":48,"timezone":"America/Chicago","name":"Houston","geolat":29.7594,"geolong":-95.3594},
+ {"id":66,"timezone":"America/New_York","name":"Indianapolis","geolat":39.767016,"geolong":-86.156255},
+ {"id":65,"timezone":"America/Chicago","name":"Kansas City","geolat":39.090431,"geolong":-94.583644},
+ {"id":49,"timezone":"America/Los_Angeles","name":"Las Vegas","geolat":36.1721,"geolong":-115.122},
+ {"id":61,"timezone":"Europe/London","name":"London","geolat":51.50714,"geolong":-0.126171},
+ {"id":34,"timezone":"America/Los_Angeles","name":"Los Angeles","geolat":34.0443,"geolong":-118.251},
+ {"id":39,"timezone":"America/New_York","name":"Miami","geolat":25.7323,"geolong":-80.2436},
+ {"id":67,"timezone":"America/Chicago","name":"Milwaukee","geolat":43.038902,"geolong":-87.906474},
+ {"id":51,"timezone":"America/Chicago","name":"Minneapolis / St. Paul","geolat":44.9609,"geolong":-93.2642},
+ {"id":70,"timezone":"America/New_York","name":"Montreal","geolat":45.545447,"geolong":-73.639076},
+ {"id":52,"timezone":"America/Chicago","name":"New Orleans","geolat":29.9544,"geolong":-90.075},
+ {"id":22,"timezone":"America/New_York","name":"New York City","geolat":40.7255,"geolong":-73.9983},
+ {"id":72,"timezone":"America/Chicago","name":"Omaha","geolat":41.254006,"geolong":-95.999258},
+ {"id":33,"timezone":"America/New_York","name":"Philadelphia","geolat":39.8694,"geolong":-75.2731},
+ {"id":53,"timezone":"America/Phoenix","name":"Phoenix","geolat":33.4483,"geolong":-112.073},
+ {"id":60,"timezone":"America/New_York","name":"Pittsburgh","geolat":40.4405,"geolong":-79.9961},
+ {"id":37,"timezone":"America/Los_Angeles","name":"Portland","geolat":45.527,"geolong":-122.685},
+ {"id":57,"timezone":"America/New_York","name":"Raleigh / Durham","geolat":35.7797,"geolong":-78.6434},
+ {"id":73,"timezone":"America/New_York","name":"Richmond","geolat":37.542979,"geolong":-77.469092},
+ {"id":71,"timezone":"America/Denver","name":"Salt Lake City","geolat":40.760779,"geolong":-111.891047},
+ {"id":68,"timezone":"America/Chicago","name":"San Antonio","geolat":29.424122,"geolong":-98.493628},
+ {"id":38,"timezone":"America/Los_Angeles","name":"San Diego","geolat":32.7153,"geolong":-117.156},
+ {"id":23,"timezone":"America/Los_Angeles","name":"San Francisco","geolat":37.7587,"geolong":-122.433},
+ {"id":41,"timezone":"America/Los_Angeles","name":"Seattle","geolat":47.6036,"geolong":-122.326},
+ {"id":62,"timezone":"America/Chicago","name":"St. Louis","geolat":38.627491,"geolong":-90.198417},
+ {"id":69,"timezone":"America/New_York","name":"Toronto","geolat":43.670233,"geolong":-79.386755},
+ {"id":59,"timezone":"America/Vancouver","name":"Vancouver","geolat":49.259515,"geolong":-123.103867},
+ {"id":31,"timezone":"America/New_York","name":"Washington, DC","geolat":38.8964,"geolong":-77.0447}
+];
+var myArray = data.map(function(item){
+ return [item.id, item.name, item.timezone, item.geolat, item.geolong];
+});
+
+var myTable = new HtmlTable({
+
+ properties : {
+ border : 1,
+ cellspacing : 0,
+ cellpadding : 5
+ },
+ zebra: true,
+ rows : myArray,
+ headers : ['ID', 'TimeZone', 'Name', 'GEO Latitude', 'GEO Longitude'],
+ sortable : true,
+ parsers : ['number', 'string', 'string', 'float', 'float']
+
+});
+$(myTable).inject($('mt-content'));
+</script>

0 comments on commit 5a40b1c

Please sign in to comment.