Subversion Repositories ALCASAR

Rev

Go to most recent revision | Details | Last modification | View Log

Rev Author Line No. Line
2787 rexy 1
/*
2
  SortTable
3
  version 2+2014.12.25_fix-noinit-noforeach
4
  7th April 2007
5
  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
6
 
7
  25th December 2014
8
  Fixed behavior of multiple sortable tables in same page. 
9
  Redefinition of sortfwdind and sortrevind icons.
10
  Mieczyslaw Nalewaj (namiltd@users.sourceforge.net)
11
 
12
  19th November 2015
13
  Disabled autostart after loading.
14
  You have to run sorttable.init() at the beginning of the code,
15
  or makeSortable for each table.
16
  Mieczyslaw Nalewaj (namiltd@users.sourceforge.net)
17
 
18
  Instructions:
19
  Download this file
20
  Add <script src="sorttable.js"></script> to your HTML
21
  Add class="sortable" to any table you'd like to make sortable
22
  Click on the headers to sort
23
 
24
  Thanks to many, many people for contributions and suggestions.
25
  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
26
  This basically means: do what you want with it.
27
*/
28
 
29
 
30
sorttable = {
31
  init: function() {
32
    // quit if this function has already been called
33
    if (arguments.callee.done) return;
34
    // flag this function so we don't do the same thing twice
35
    arguments.callee.done = true;
36
 
37
    if (!document.createElement || !document.getElementsByTagName) return;
38
 
39
    sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;
40
 
41
    allTables=document.getElementsByTagName('table');
42
    for (var i=0; i < allTables.length; i++) {
43
      if (allTables[i].className.search(/\bsortable\b/) != -1) {
44
        sorttable.makeSortable(allTables[i]);
45
      }
46
    }
47
  },
48
 
49
  makeSortable: function(table) {
50
    if (table.getElementsByTagName('thead').length == 0) {
51
      // table doesn't have a tHead. Since it should have, create one and
52
      // put the first table row in it.
53
      the = document.createElement('thead');
54
      the.appendChild(table.rows[0]);
55
      table.insertBefore(the,table.firstChild);
56
    }
57
    // Safari doesn't support table.tHead, sigh
58
    if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];
59
 
60
    if (table.tHead.rows.length != 1) return; // can't cope with two header rows
61
 
62
    // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
63
    // "total" rows, for example). This is B&R, since what you're supposed
64
    // to do is put them in a tfoot. So, if there are sortbottom rows,
65
    // for backwards compatibility, move them to tfoot (creating it if needed).
66
    sortbottomrows = [];
67
    for (var i=0; i<table.rows.length; i++) {
68
      if (table.rows[i].className.search(/\bsortbottom\b/) != -1) {
69
        sortbottomrows[sortbottomrows.length] = table.rows[i];
70
      }
71
    }
72
    if (sortbottomrows) {
73
      if (table.tFoot == null) {
74
        // table doesn't have a tfoot. Create one.
75
        tfo = document.createElement('tfoot');
76
        table.appendChild(tfo);
77
      }
78
      for (var i=0; i<sortbottomrows.length; i++) {
79
        tfo.appendChild(sortbottomrows[i]);
80
      }
81
      delete sortbottomrows;
82
    }
83
 
84
    // work through each column and calculate its type
85
    headrow = table.tHead.rows[0].cells;
86
    for (var i=0; i<headrow.length; i++) {
87
      // manually override the type with a sorttable_type attribute
88
      if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
89
        mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
90
        if (mtch) { override = mtch[1]; }
91
	      if (mtch && typeof sorttable["sort_"+override] == 'function') {
92
	        headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
93
	      } else {
94
	        headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
95
	      }
96
	      // make it clickable to sort
97
	      headrow[i].sorttable_columnindex = i;
98
	      headrow[i].sorttable_tbody = table.tBodies[0];
99
	      dean_addEvent(headrow[i],"click", sorttable.innerSortFunction = function(e) {
100
 
101
          if (this.className.search(/\bsorttable_sorted\b/) != -1) {
102
            // if we're already sorted by this column, just
103
            // reverse the table, which is quicker
104
            sorttable.reverse(this.sorttable_tbody);
105
            this.className = this.className.replace('sorttable_sorted',
106
                                                    'sorttable_sorted_reverse');
107
            this.removeChild(document.getElementById('sorttable_sortfwdind'+$(this).parent().parent().parent()[0].id));
108
            sortrevind = document.createElement('span');
109
            sortrevind.id = "sorttable_sortrevind"+$(this).parent().parent().parent()[0].id;
110
            sortrevind.innerHTML = '&nbsp;&#x25B2;';
111
            this.appendChild(sortrevind);
112
            return;
113
          }
114
          if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) {
115
            // if we're already sorted by this column in reverse, just
116
            // re-reverse the table, which is quicker
117
            sorttable.reverse(this.sorttable_tbody);
118
            this.className = this.className.replace('sorttable_sorted_reverse',
119
                                                    'sorttable_sorted');
120
            this.removeChild(document.getElementById('sorttable_sortrevind'+$(this).parent().parent().parent()[0].id));
121
            sortfwdind = document.createElement('span');
122
            sortfwdind.id = "sorttable_sortfwdind"+$(this).parent().parent().parent()[0].id;
123
            sortfwdind.innerHTML = '&nbsp;&#x25BC;';
124
            this.appendChild(sortfwdind);
125
            return;
126
          }
127
 
128
          // remove sorttable_sorted classes
129
          theadrow = this.parentNode;
130
          for (var i=0; i < theadrow.childNodes.length; i++) {
131
            if (theadrow.childNodes[i].nodeType == 1) { // an element
132
              theadrow.childNodes[i].className = theadrow.childNodes[i].className.replace('sorttable_sorted_reverse','');
133
              theadrow.childNodes[i].className = theadrow.childNodes[i].className.replace('sorttable_sorted','');
134
            }
135
          }
136
 
137
          sortfwdind = document.getElementById('sorttable_sortfwdind'+$(this).parent().parent().parent()[0].id);
138
          if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
139
          sortrevind = document.getElementById('sorttable_sortrevind'+$(this).parent().parent().parent()[0].id);
140
          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
141
 
142
          this.className += ' sorttable_sorted';
143
          sortfwdind = document.createElement('span');
144
          sortfwdind.id = "sorttable_sortfwdind"+$(this).parent().parent().parent()[0].id;
145
          sortfwdind.innerHTML = '&nbsp;&#x25BC;';
146
          this.appendChild(sortfwdind);
147
 
148
	        // build an array to sort. This is a Schwartzian transform thing,
149
	        // i.e., we "decorate" each row with the actual sort key,
150
	        // sort based on the sort keys, and then put the rows back in order
151
	        // which is a lot faster because you only do getInnerText once per row
152
	        row_array = [];
153
	        col = this.sorttable_columnindex;
154
	        rows = this.sorttable_tbody.rows;
155
	        for (var j=0; j<rows.length; j++) {
156
	          row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
157
	        }
158
	        /* If you want a stable sort, uncomment the following line */
159
	        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
160
	        /* and comment out this one */
161
	        row_array.sort(this.sorttable_sortfunction);
162
 
163
	        tb = this.sorttable_tbody;
164
	        for (var j=0; j<row_array.length; j++) {
165
	          tb.appendChild(row_array[j][1]);
166
	        }
167
 
168
	        delete row_array;
169
	      });
170
	    }
171
    }
172
  },
173
 
174
  guessType: function(table, column) {
175
    // guess the type of a column based on its first non-blank row
176
    sortfn = sorttable.sort_alpha;
177
    for (var i=0; i<table.tBodies[0].rows.length; i++) {
178
      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);
179
      if (text != '') {
180
        if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) {
181
          return sorttable.sort_numeric;
182
        }
183
        // check for a date: dd/mm/yyyy or dd/mm/yy
184
        // can have / or . or - as separator
185
        // can be mm/dd as well
186
        possdate = text.match(sorttable.DATE_RE);
187
        if (possdate) {
188
          // looks like a date
189
          first = parseInt(possdate[1]);
190
          second = parseInt(possdate[2]);
191
          if (first > 12) {
192
            // definitely dd/mm
193
            return sorttable.sort_ddmm;
194
          } else if (second > 12) {
195
            return sorttable.sort_mmdd;
196
          } else {
197
            // looks like a date, but we can't tell which, so assume
198
            // that it's dd/mm (English imperialism!) and keep looking
199
            sortfn = sorttable.sort_ddmm;
200
          }
201
        }
202
      }
203
    }
204
    return sortfn;
205
  },
206
 
207
  getInnerText: function(node) {
208
    // gets the text we want to use for sorting for a cell.
209
    // strips leading and trailing whitespace.
210
    // this is *not* a generic getInnerText function; it's special to sorttable.
211
    // for example, you can override the cell text with a customkey attribute.
212
    // it also gets .value for <input> fields.
213
 
214
    if (!node) return "";
215
 
216
    hasInputs = (typeof node.getElementsByTagName == 'function') &&
217
                 node.getElementsByTagName('input').length;
218
 
219
    if (node.getAttribute("sorttable_customkey") != null) {
220
      return node.getAttribute("sorttable_customkey");
221
    }
222
    else if (typeof node.textContent != 'undefined' && !hasInputs) {
223
      return node.textContent.replace(/^\s+|\s+$/g, '');
224
    }
225
    else if (typeof node.innerText != 'undefined' && !hasInputs) {
226
      return node.innerText.replace(/^\s+|\s+$/g, '');
227
    }
228
    else if (typeof node.text != 'undefined' && !hasInputs) {
229
      return node.text.replace(/^\s+|\s+$/g, '');
230
    }
231
    else {
232
      switch (node.nodeType) {
233
        case 3:
234
          if (node.nodeName.toLowerCase() == 'input') {
235
            return node.value.replace(/^\s+|\s+$/g, '');
236
          }
237
        case 4:
238
          return node.nodeValue.replace(/^\s+|\s+$/g, '');
239
          break;
240
        case 1:
241
        case 11:
242
          var innerText = '';
243
          for (var i = 0; i < node.childNodes.length; i++) {
244
            innerText += sorttable.getInnerText(node.childNodes[i]);
245
          }
246
          return innerText.replace(/^\s+|\s+$/g, '');
247
          break;
248
        default:
249
          return '';
250
      }
251
    }
252
  },
253
 
254
  reverse: function(tbody) {
255
    // reverse the rows in a tbody
256
    newrows = [];
257
    for (var i=0; i<tbody.rows.length; i++) {
258
      newrows[newrows.length] = tbody.rows[i];
259
    }
260
    for (var i=newrows.length-1; i>=0; i--) {
261
       tbody.appendChild(newrows[i]);
262
    }
263
    delete newrows;
264
  },
265
 
266
  /* sort functions
267
     each sort function takes two parameters, a and b
268
     you are comparing a[0] and b[0] */
269
  sort_numeric: function(a,b) {
270
    aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));
271
    if (isNaN(aa)) aa = 0;
272
    bb = parseFloat(b[0].replace(/[^0-9.-]/g,''));
273
    if (isNaN(bb)) bb = 0;
274
    return aa-bb;
275
  },
276
  sort_alpha: function(a,b) {
277
    if (a[0]==b[0]) return 0;
278
    if (a[0]<b[0]) return -1;
279
    return 1;
280
  },
281
  sort_ddmm: function(a,b) {
282
    mtch = a[0].match(sorttable.DATE_RE);
283
    y = mtch[3]; m = mtch[2]; d = mtch[1];
284
    if (m.length == 1) m = '0'+m;
285
    if (d.length == 1) d = '0'+d;
286
    dt1 = y+m+d;
287
    mtch = b[0].match(sorttable.DATE_RE);
288
    y = mtch[3]; m = mtch[2]; d = mtch[1];
289
    if (m.length == 1) m = '0'+m;
290
    if (d.length == 1) d = '0'+d;
291
    dt2 = y+m+d;
292
    if (dt1==dt2) return 0;
293
    if (dt1<dt2) return -1;
294
    return 1;
295
  },
296
  sort_mmdd: function(a,b) {
297
    mtch = a[0].match(sorttable.DATE_RE);
298
    y = mtch[3]; d = mtch[2]; m = mtch[1];
299
    if (m.length == 1) m = '0'+m;
300
    if (d.length == 1) d = '0'+d;
301
    dt1 = y+m+d;
302
    mtch = b[0].match(sorttable.DATE_RE);
303
    y = mtch[3]; d = mtch[2]; m = mtch[1];
304
    if (m.length == 1) m = '0'+m;
305
    if (d.length == 1) d = '0'+d;
306
    dt2 = y+m+d;
307
    if (dt1==dt2) return 0;
308
    if (dt1<dt2) return -1;
309
    return 1;
310
  },
311
 
312
  shaker_sort: function(list, comp_func) {
313
    // A stable sort function to allow multi-level sorting of data
314
    // see: http://en.wikipedia.org/wiki/Cocktail_sort
315
    // thanks to Joseph Nahmias
316
    var b = 0;
317
    var t = list.length - 1;
318
    var swap = true;
319
 
320
    while(swap) {
321
        swap = false;
322
        for(var i = b; i < t; ++i) {
323
            if ( comp_func(list[i], list[i+1]) > 0 ) {
324
                var q = list[i]; list[i] = list[i+1]; list[i+1] = q;
325
                swap = true;
326
            }
327
        } // for
328
        t--;
329
 
330
        if (!swap) break;
331
 
332
        for(var i = t; i > b; --i) {
333
            if ( comp_func(list[i], list[i-1]) < 0 ) {
334
                var q = list[i]; list[i] = list[i-1]; list[i-1] = q;
335
                swap = true;
336
            }
337
        } // for
338
        b++;
339
 
340
    } // while(swap)
341
  }
342
};
343
 
344
// written by Dean Edwards, 2005
345
// with input from Tino Zijdel, Matthias Miller, Diego Perini
346
 
347
// http://dean.edwards.name/weblog/2005/10/add-event/
348
 
349
function dean_addEvent(element, type, handler) {
350
	if (element.addEventListener) {
351
		element.addEventListener(type, handler, false);
352
	} else {
353
		// assign each event handler a unique ID
354
		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;
355
		// create a hash table of event types for the element
356
		if (!element.events) element.events = {};
357
		// create a hash table of event handlers for each element/event pair
358
		var handlers = element.events[type];
359
		if (!handlers) {
360
			handlers = element.events[type] = {};
361
			// store the existing event handler (if there is one)
362
			if (element["on" + type]) {
363
				handlers[0] = element["on" + type];
364
			}
365
		}
366
		// store the event handler in the hash table
367
		handlers[handler.$$guid] = handler;
368
		// assign a global event handler to do all the work
369
		element["on" + type] = handleEvent;
370
	}
371
};
372
// a counter used to create unique IDs
373
dean_addEvent.guid = 1;
374
 
375
function removeEvent(element, type, handler) {
376
	if (element.removeEventListener) {
377
		element.removeEventListener(type, handler, false);
378
	} else {
379
		// delete the event handler from the hash table
380
		if (element.events && element.events[type]) {
381
			delete element.events[type][handler.$$guid];
382
		}
383
	}
384
};
385
 
386
function handleEvent(event) {
387
	var returnValue = true;
388
	// grab the event object (IE uses a global event object)
389
	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
390
	// get a reference to the hash table of event handlers
391
	var handlers = this.events[event.type];
392
	// execute each event handler
393
	for (var i in handlers) {
394
		this.$$handleEvent = handlers[i];
395
		if (this.$$handleEvent(event) === false) {
396
			returnValue = false;
397
		}
398
	}
399
	return returnValue;
400
};
401
 
402
function fixEvent(event) {
403
	// add W3C standard event methods
404
	event.preventDefault = fixEvent.preventDefault;
405
	event.stopPropagation = fixEvent.stopPropagation;
406
	return event;
407
};
408
fixEvent.preventDefault = function() {
409
	this.returnValue = false;
410
};
411
fixEvent.stopPropagation = function() {
412
  this.cancelBubble = true;
413
};