Attachment 'common.js.patch'
Download 1 diff -r dc854677e9bd wiki/htdocs/common/js/common.js
2 --- a/wiki/htdocs/common/js/common.js Wed Jan 07 01:41:18 2009 +0100
3 +++ b/wiki/htdocs/common/js/common.js Thu Jan 08 12:31:58 2009 +0100
4 @@ -389,3 +389,497 @@
5 return getElementsByClassName(className, tag, elm);
6 };
7
8 +/*
9 + SortTable
10 + version 2
11 + 7th April 2007
12 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
13 +
14 + Instructions:
15 + Download this file
16 + Add <script src="sorttable.js"></script> to your HTML
17 + Add class="sortable" to any table you'd like to make sortable
18 + Click on the headers to sort
19 +
20 + Thanks to many, many people for contributions and suggestions.
21 + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
22 + This basically means: do what you want with it.
23 +*/
24 +
25 +
26 +var stIsIE = /*@cc_on!@*/false;
27 +
28 +sorttable = {
29 + init: function() {
30 + // quit if this function has already been called
31 + if (arguments.callee.done) return;
32 + // flag this function so we don't do the same thing twice
33 + arguments.callee.done = true;
34 + // kill the timer
35 + if (_timer) clearInterval(_timer);
36 +
37 + if (!document.createElement || !document.getElementsByTagName) return;
38 +
39 + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;
40 +
41 + forEach(document.getElementsByTagName('table'), function(table) {
42 + if (table.className.search(/\bsortable\b/) != -1) {
43 + sorttable.makeSortable(table);
44 + }
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", 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'));
108 + sortrevind = document.createElement('span');
109 + sortrevind.id = "sorttable_sortrevind";
110 + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴';
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'));
121 + sortfwdind = document.createElement('span');
122 + sortfwdind.id = "sorttable_sortfwdind";
123 + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾';
124 + this.appendChild(sortfwdind);
125 + return;
126 + }
127 +
128 + // remove sorttable_sorted classes
129 + theadrow = this.parentNode;
130 + forEach(theadrow.childNodes, function(cell) {
131 + if (cell.nodeType == 1) { // an element
132 + cell.className = cell.className.replace('sorttable_sorted_reverse','');
133 + cell.className = cell.className.replace('sorttable_sorted','');
134 + }
135 + });
136 + sortfwdind = document.getElementById('sorttable_sortfwdind');
137 + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
138 + sortrevind = document.getElementById('sorttable_sortrevind');
139 + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
140 +
141 + this.className += ' sorttable_sorted';
142 + sortfwdind = document.createElement('span');
143 + sortfwdind.id = "sorttable_sortfwdind";
144 + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾';
145 + this.appendChild(sortfwdind);
146 +
147 + // build an array to sort. This is a Schwartzian transform thing,
148 + // i.e., we "decorate" each row with the actual sort key,
149 + // sort based on the sort keys, and then put the rows back in order
150 + // which is a lot faster because you only do getInnerText once per row
151 + row_array = [];
152 + col = this.sorttable_columnindex;
153 + rows = this.sorttable_tbody.rows;
154 + for (var j=0; j<rows.length; j++) {
155 + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
156 + }
157 + /* If you want a stable sort, uncomment the following line */
158 + //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
159 + /* and comment out this one */
160 + row_array.sort(this.sorttable_sortfunction);
161 +
162 + tb = this.sorttable_tbody;
163 + for (var j=0; j<row_array.length; j++) {
164 + tb.appendChild(row_array[j][1]);
165 + }
166 +
167 + delete row_array;
168 + });
169 + }
170 + }
171 + },
172 +
173 + guessType: function(table, column) {
174 + // guess the type of a column based on its first non-blank row
175 + sortfn = sorttable.sort_alpha;
176 + for (var i=0; i<table.tBodies[0].rows.length; i++) {
177 + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);
178 + if (text != '') {
179 + if (text.match(/^-?[£$?]?[\d,.]+%?$/)) {
180 + return sorttable.sort_numeric;
181 + }
182 + // check for a date: dd/mm/yyyy or dd/mm/yy
183 + // can have / or . or - as separator
184 + // can be mm/dd as well
185 + possdate = text.match(sorttable.DATE_RE)
186 + if (possdate) {
187 + // looks like a date
188 + first = parseInt(possdate[1]);
189 + second = parseInt(possdate[2]);
190 + if (first > 12) {
191 + // definitely dd/mm
192 + return sorttable.sort_ddmm;
193 + } else if (second > 12) {
194 + return sorttable.sort_mmdd;
195 + } else {
196 + // looks like a date, but we can't tell which, so assume
197 + // that it's dd/mm (English imperialism!) and keep looking
198 + sortfn = sorttable.sort_ddmm;
199 + }
200 + }
201 + }
202 + }
203 + return sortfn;
204 + },
205 +
206 + getInnerText: function(node) {
207 + // gets the text we want to use for sorting for a cell.
208 + // strips leading and trailing whitespace.
209 + // this is *not* a generic getInnerText function; it's special to sorttable.
210 + // for example, you can override the cell text with a customkey attribute.
211 + // it also gets .value for <input> fields.
212 +
213 + hasInputs = (typeof node.getElementsByTagName == 'function') &&
214 + node.getElementsByTagName('input').length;
215 +
216 + if (node.getAttribute("sorttable_customkey") != null) {
217 + return node.getAttribute("sorttable_customkey");
218 + }
219 + else if (typeof node.textContent != 'undefined' && !hasInputs) {
220 + return node.textContent.replace(/^\s+|\s+$/g, '');
221 + }
222 + else if (typeof node.innerText != 'undefined' && !hasInputs) {
223 + return node.innerText.replace(/^\s+|\s+$/g, '');
224 + }
225 + else if (typeof node.text != 'undefined' && !hasInputs) {
226 + return node.text.replace(/^\s+|\s+$/g, '');
227 + }
228 + else {
229 + switch (node.nodeType) {
230 + case 3:
231 + if (node.nodeName.toLowerCase() == 'input') {
232 + return node.value.replace(/^\s+|\s+$/g, '');
233 + }
234 + case 4:
235 + return node.nodeValue.replace(/^\s+|\s+$/g, '');
236 + break;
237 + case 1:
238 + case 11:
239 + var innerText = '';
240 + for (var i = 0; i < node.childNodes.length; i++) {
241 + innerText += sorttable.getInnerText(node.childNodes[i]);
242 + }
243 + return innerText.replace(/^\s+|\s+$/g, '');
244 + break;
245 + default:
246 + return '';
247 + }
248 + }
249 + },
250 +
251 + reverse: function(tbody) {
252 + // reverse the rows in a tbody
253 + newrows = [];
254 + for (var i=0; i<tbody.rows.length; i++) {
255 + newrows[newrows.length] = tbody.rows[i];
256 + }
257 + for (var i=newrows.length-1; i>=0; i--) {
258 + tbody.appendChild(newrows[i]);
259 + }
260 + delete newrows;
261 + },
262 +
263 + /* sort functions
264 + each sort function takes two parameters, a and b
265 + you are comparing a[0] and b[0] */
266 + sort_numeric: function(a,b) {
267 + aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));
268 + if (isNaN(aa)) aa = 0;
269 + bb = parseFloat(b[0].replace(/[^0-9.-]/g,''));
270 + if (isNaN(bb)) bb = 0;
271 + return aa-bb;
272 + },
273 + sort_alpha: function(a,b) {
274 + if (a[0]==b[0]) return 0;
275 + if (a[0]<b[0]) return -1;
276 + return 1;
277 + },
278 + sort_ddmm: function(a,b) {
279 + mtch = a[0].match(sorttable.DATE_RE);
280 + y = mtch[3]; m = mtch[2]; d = mtch[1];
281 + if (m.length == 1) m = '0'+m;
282 + if (d.length == 1) d = '0'+d;
283 + dt1 = y+m+d;
284 + mtch = b[0].match(sorttable.DATE_RE);
285 + y = mtch[3]; m = mtch[2]; d = mtch[1];
286 + if (m.length == 1) m = '0'+m;
287 + if (d.length == 1) d = '0'+d;
288 + dt2 = y+m+d;
289 + if (dt1==dt2) return 0;
290 + if (dt1<dt2) return -1;
291 + return 1;
292 + },
293 + sort_mmdd: function(a,b) {
294 + mtch = a[0].match(sorttable.DATE_RE);
295 + y = mtch[3]; d = mtch[2]; m = mtch[1];
296 + if (m.length == 1) m = '0'+m;
297 + if (d.length == 1) d = '0'+d;
298 + dt1 = y+m+d;
299 + mtch = b[0].match(sorttable.DATE_RE);
300 + y = mtch[3]; d = mtch[2]; m = mtch[1];
301 + if (m.length == 1) m = '0'+m;
302 + if (d.length == 1) d = '0'+d;
303 + dt2 = y+m+d;
304 + if (dt1==dt2) return 0;
305 + if (dt1<dt2) return -1;
306 + return 1;
307 + },
308 +
309 + shaker_sort: function(list, comp_func) {
310 + // A stable sort function to allow multi-level sorting of data
311 + // see: http://en.wikipedia.org/wiki/Cocktail_sort
312 + // thanks to Joseph Nahmias
313 + var b = 0;
314 + var t = list.length - 1;
315 + var swap = true;
316 +
317 + while(swap) {
318 + swap = false;
319 + for(var i = b; i < t; ++i) {
320 + if ( comp_func(list[i], list[i+1]) > 0 ) {
321 + var q = list[i]; list[i] = list[i+1]; list[i+1] = q;
322 + swap = true;
323 + }
324 + } // for
325 + t--;
326 +
327 + if (!swap) break;
328 +
329 + for(var i = t; i > b; --i) {
330 + if ( comp_func(list[i], list[i-1]) < 0 ) {
331 + var q = list[i]; list[i] = list[i-1]; list[i-1] = q;
332 + swap = true;
333 + }
334 + } // for
335 + b++;
336 +
337 + } // while(swap)
338 + }
339 +}
340 +
341 +/* ******************************************************************
342 + Supporting functions: bundled here to avoid depending on a library
343 + ****************************************************************** */
344 +
345 +// Dean Edwards/Matthias Miller/John Resig
346 +
347 +/* for Mozilla/Opera9 */
348 +if (document.addEventListener) {
349 + document.addEventListener("DOMContentLoaded", sorttable.init, false);
350 +}
351 +
352 +/* for Internet Explorer */
353 +/*@cc_on @*/
354 +/*@if (@_win32)
355 + document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
356 + var script = document.getElementById("__ie_onload");
357 + script.onreadystatechange = function() {
358 + if (this.readyState == "complete") {
359 + sorttable.init(); // call the onload handler
360 + }
361 + };
362 +/*@end @*/
363 +
364 +/* for Safari */
365 +if (/WebKit/i.test(navigator.userAgent)) { // sniff
366 + var _timer = setInterval(function() {
367 + if (/loaded|complete/.test(document.readyState)) {
368 + sorttable.init(); // call the onload handler
369 + }
370 + }, 10);
371 +}
372 +
373 +/* for other browsers */
374 +//window.onload = sorttable.init;
375 +
376 +// written by Dean Edwards, 2005
377 +// with input from Tino Zijdel, Matthias Miller, Diego Perini
378 +
379 +// http://dean.edwards.name/weblog/2005/10/add-event/
380 +
381 +function dean_addEvent(element, type, handler) {
382 + if (element.addEventListener) {
383 + element.addEventListener(type, handler, false);
384 + } else {
385 + // assign each event handler a unique ID
386 + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;
387 + // create a hash table of event types for the element
388 + if (!element.events) element.events = {};
389 + // create a hash table of event handlers for each element/event pair
390 + var handlers = element.events[type];
391 + if (!handlers) {
392 + handlers = element.events[type] = {};
393 + // store the existing event handler (if there is one)
394 + if (element["on" + type]) {
395 + handlers[0] = element["on" + type];
396 + }
397 + }
398 + // store the event handler in the hash table
399 + handlers[handler.$$guid] = handler;
400 + // assign a global event handler to do all the work
401 + element["on" + type] = handleEvent;
402 + }
403 +};
404 +// a counter used to create unique IDs
405 +dean_addEvent.guid = 1;
406 +
407 +function removeEvent(element, type, handler) {
408 + if (element.removeEventListener) {
409 + element.removeEventListener(type, handler, false);
410 + } else {
411 + // delete the event handler from the hash table
412 + if (element.events && element.events[type]) {
413 + delete element.events[type][handler.$$guid];
414 + }
415 + }
416 +};
417 +
418 +function handleEvent(event) {
419 + var returnValue = true;
420 + // grab the event object (IE uses a global event object)
421 + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
422 + // get a reference to the hash table of event handlers
423 + var handlers = this.events[event.type];
424 + // execute each event handler
425 + for (var i in handlers) {
426 + this.$$handleEvent = handlers[i];
427 + if (this.$$handleEvent(event) === false) {
428 + returnValue = false;
429 + }
430 + }
431 + return returnValue;
432 +};
433 +
434 +function fixEvent(event) {
435 + // add W3C standard event methods
436 + event.preventDefault = fixEvent.preventDefault;
437 + event.stopPropagation = fixEvent.stopPropagation;
438 + return event;
439 +};
440 +fixEvent.preventDefault = function() {
441 + this.returnValue = false;
442 +};
443 +fixEvent.stopPropagation = function() {
444 + this.cancelBubble = true;
445 +}
446 +
447 +// Dean's forEach: http://dean.edwards.name/base/forEach.js
448 +/*
449 + forEach, version 1.0
450 + Copyright 2006, Dean Edwards
451 + License: http://www.opensource.org/licenses/mit-license.php
452 +*/
453 +
454 +// array-like enumeration
455 +if (!Array.forEach) { // mozilla already supports this
456 + Array.forEach = function(array, block, context) {
457 + for (var i = 0; i < array.length; i++) {
458 + block.call(context, array[i], i, array);
459 + }
460 + };
461 +}
462 +
463 +// generic enumeration
464 +Function.prototype.forEach = function(object, block, context) {
465 + for (var key in object) {
466 + if (typeof this.prototype[key] == "undefined") {
467 + block.call(context, object[key], key, object);
468 + }
469 + }
470 +};
471 +
472 +// character enumeration
473 +String.forEach = function(string, block, context) {
474 + Array.forEach(string.split(""), function(chr, index) {
475 + block.call(context, chr, index, string);
476 + });
477 +};
478 +
479 +// globally resolve forEach enumeration
480 +var forEach = function(object, block, context) {
481 + if (object) {
482 + var resolve = Object; // default
483 + if (object instanceof Function) {
484 + // functions have a "length" property
485 + resolve = Function;
486 + } else if (object.forEach instanceof Function) {
487 + // the object implements a custom forEach method so use that
488 + object.forEach(block, context);
489 + return;
490 + } else if (typeof object == "string") {
491 + // the object is a string
492 + resolve = String;
493 + } else if (typeof object.length == "number") {
494 + // the object is array-like
495 + resolve = Array;
496 + }
497 + resolve.forEach(object, block, context);
498 + }
499 +};
500 +
501 +
502
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.