1 // Copyright (c) 2005 spinelz.org (http://script.spinelz.org/)
3 // Permission is hereby granted, free of charge, to any person obtaining
4 // a copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to
8 // permit persons to whom the Software is furnished to do so, subject to
9 // the following conditions:
11 // The above copyright notice and this permission notice shall be
12 // included in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 var SortableTable = Class.create();
24 SortableTable.classNames = {
25 header: 'sortableTable_header',
26 title: 'sortableTable_title',
27 empty: 'sortableTable_empty',
28 down: 'sortableTable_down',
29 up: 'sortableTable_up',
30 mark: 'sortableTable_mark',
31 thead: 'sortableTable_thead',
32 tbody: 'sortableTable_tbody'
35 SortableTable.prototype = {
37 initialize: function(element) {
38 this.element = $(element);
39 Element.setStyle(this.element, {visibility: 'hidden'});
41 var options = Object.extend({
44 }, arguments[1] || {});
46 var customCss = CssUtil.appendPrefix(options.cssPrefix, SortableTable.classNames);
47 this.classNames = new CssUtil([SortableTable.classNames, customCss]);
49 this.sortType = options.sortType;
51 this.currentOrder = 'default';
52 this.defaultOrder = new Array();
53 for (var i = 1; i < this.element.rows.length; i++) {
54 this.defaultOrder[i - 1] = this.element.rows[i];
58 Element.setStyle(this.element, {visibility: 'visible'});
62 thead = this.element.tHead;
63 this.classNames.addClassNames(thead, 'thead');
64 tbody = thead.nextSibling;
65 while ((tbody.nodeType != 1) || (tbody.tagName.toLowerCase() != 'tbody')) {
66 tbody = tbody.nextSibling;
68 this.classNames.addClassNames(tbody, 'tbody');
69 var rows = this.element.rows[0];
72 for (var i = 0; i < rows.cells.length; i++) {
74 var cell = rows.cells[i];
75 cell.style.cursor = 'pointer';
77 Element.cleanWhitespace(cell);
78 var title = Builder.node('DIV', $A(cell.childNodes));
79 this.classNames.addClassNames(title, 'title');
81 var img = Builder.node('DIV');
82 this.classNames.addClassNames(img, 'mark');
83 this.classNames.addClassNames(img, 'empty');
85 var header = Builder.node('DIV', [title, img]);
86 this.classNames.addClassNames(header, 'header');
87 cell.appendChild(header);
89 var titleWidth = title.offsetWidth;
90 var imgWidth = img.offsetWidth;
92 title.style.width = (titleWidth + imgWidth) + 'px';
93 Event.observe(rows.cells[i], 'click', this.sortTable.bindAsEventListener(this));
97 sortTable: function(event) {
98 var cell = Event.element(event);
100 if (cell.tagName.toUpperCase() != 'TD' && cell.tagName.toUpperCase() != 'TH') {
101 cell = Element.getParentByTagName(['TD','TH'], cell);
104 var tmpColumn = cell.cellIndex;
105 if (this.targetColumn != tmpColumn) {
106 this.currentOrder = 'default';
108 this.targetColumn = tmpColumn;
110 var newRows = new Array();
111 for (var i = 1; i < this.element.rows.length; i++) {
112 newRows[i - 1] = this.element.rows[i];
114 if (newRows.length < 1) return;
116 if (this.currentOrder == 'default') {
117 newRows.sort(this.getSortFunc());
118 this.currentOrder = 'asc';
119 } else if (this.currentOrder == 'asc') {
120 newRows = newRows.reverse();
121 this.currentOrder = 'desc';
122 } else if (this.currentOrder == 'desc') {
123 newRows = this.defaultOrder;
124 this.currentOrder = 'default';
127 for (var i = 0; i < newRows.length; i++) {
128 this.element.tBodies[0].appendChild(newRows[i]);
134 mark: function(cell) {
135 var images = document.getElementsByClassName(SortableTable.classNames.mark, this.element);
136 var targetImg = document.getElementsByClassName(SortableTable.classNames.mark, cell)[0];
138 for (var i = 0; i < images.length; i++) {
139 var parent = images[i].parentNode;
140 var title = document.getElementsByClassName(SortableTable.classNames.title, parent)[0];
141 var titleWidth = title.offsetWidth;
143 if (targetImg == images[i]) {
145 var imgWidth = targetImg.offsetWidth;
147 if (this.currentOrder == 'asc') {
148 this.classNames.addClassNames(targetImg, 'down');
149 this.classNames.removeClassNames(targetImg, 'empty');
150 if (!document.all) title.style.width = (titleWidth - imgWidth) + 'px';
152 } else if (this.currentOrder == 'desc') {
153 this.classNames.addClassNames(targetImg, 'up');
154 this.classNames.removeClassNames(targetImg, 'down');
156 } else if (this.currentOrder == 'default') {
157 this.classNames.addClassNames(targetImg, 'empty');
158 this.classNames.removeClassNames(targetImg, 'up');
159 if (!document.all) title.style.width = (titleWidth + imgWidth) + 'px';
164 if (Element.hasClassName(images[i], SortableTable.classNames.empty))
167 else if (Element.hasClassName(images[i], SortableTable.classNames.down))
168 this.classNames.removeClassNames(images[i], 'down');
170 else if (Element.hasClassName(images[i], SortableTable.classNames.up))
171 this.classNames.removeClassNames(images[i], 'up');
173 var imgWidth = targetImg.offsetWidth;
174 this.classNames.addClassNames(images[i], 'empty');
175 if (!document.all) title.style.width = (titleWidth + imgWidth) + 'px';
180 getSortFunc: function() {
181 if (!this.sortType || !this.sortType[this.targetColumn])
182 return SortFunction.string(this);
184 var type = this.getSortType();
186 if (!this.sortType || !type) {
187 return SortFunction.string(this);
188 } else if (type == SortFunction.numeric) {
189 return SortFunction.number(this);
192 return SortFunction.date(this);
195 getSortType: function() {
196 return this.sortType[this.targetColumn];
200 var SortFunction = Class.create();
204 mmddyyyy: 'mmddyyyy',
206 yyyymmdd: 'yyyymmdd',
208 ddmmyyyy: 'ddmmyyyy',
211 date: function(grid) {
212 return function(fst, snd) {
213 var aValue = Element.collectTextNodes(fst.cells[grid.targetColumn]);
214 var bValue = Element.collectTextNodes(snd.cells[grid.targetColumn]);
217 var date1 = SortFunction.getDateString(aValue, grid.getSortType());
218 var date2 = SortFunction.getDateString(bValue, grid.getSortType());
220 if (date1 == date2) return 0;
221 if (date1 < date2) return -1;
227 number: function(grid) {
228 return function(fst, snd) {
229 var aValue = parseFloat(Element.collectTextNodes(fst.cells[grid.targetColumn]));
230 if (isNaN(aValue)) aValue = 0;
231 var bValue = parseFloat(Element.collectTextNodes(snd.cells[grid.targetColumn]));
232 if (isNaN(bValue)) bValue = 0;
234 return aValue - bValue;
238 string: function(grid) {
239 return function(fst, snd) {
240 var aValue = Element.collectTextNodes(fst.cells[grid.targetColumn]);
241 var bValue = Element.collectTextNodes(snd.cells[grid.targetColumn]);
242 if (aValue == bValue) return 0;
243 if (aValue < bValue) return -1;
248 getDateString: function(date, type) {
249 var array = date.split('/');
251 if ((type == SortFunction.mmddyyyy) ||
252 (type == SortFunction.mmddyy)) {
253 var newArray = new Array();
254 newArray.push(array[2]);
255 newArray.push(array[0]);
256 newArray.push(array[1]);
257 } else if ((type == SortFunction.ddmmyyyy) ||
258 (type == SortFunction.ddmmyy)) {
259 var newArray = new Array();
260 newArray.push(array[2]);
261 newArray.push(array[1]);
262 newArray.push(array[0]);
267 return newArray.join();