bugfix for ticket #540
[plewww.git] / plekit / tablesort / customsort.js
1 /*
2     sortEnglishDateTime
3     -----------------------
4
5     This function sorts English dateTime vaues such as:
6
7     1st January 2003, 23:32:01
8     23/03/1972 à 10:22:22
9     1970/13/03 at 23:22:01
10     
11     The function is "safe" i.e. non-dateTime data (like the word "Unknown") can be passed in and is sorted properly.
12 */
13 var sortEnglishDateTime = fdTableSort.sortNumeric;
14
15 function sortEnglishDateTimePrepareData(tdNode, innerText) {
16         // You can localise the function here
17         var months = ['january','february','march','april','may','june','july','august','september','october','november','december','jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'];
18
19         // Lowercase the text
20         var aa = innerText.toLowerCase();
21
22         // Replace the longhand months with an integer equivalent
23         for(var i = 0; i < months.length; i++) {
24                 aa = aa.replace(months[i], (i+13)%12);
25         };
26
27         // Replace multiple spaces and anything that is not valid in the parsing of the date, then trim
28         aa = aa.replace(/\s+/g, " ").replace(/([^\d\s\/-:.])/g, "").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
29
30         // No timestamp at the end, then return -1
31         if(aa.search(/(\d){2}:(\d){2}(:(\d){2})?$/) == -1) { return -1; };
32
33         // Grab the timestamp
34         var timestamp = aa.match(/(\d){2}:(\d){2}(:(\d){2})?$/)[0].replace(/:/g, "");
35
36         // Make the timestamp 6 characters by default
37         if(timestamp.length == 4) { timestamp += "00"; };
38
39         // Remove it from the string to assist the date parser, then trim
40         aa = aa.replace(/(\d){2}:(\d){2}(:(\d){2})?$/, "").replace(/\s\s*$/, '');
41
42         // If you want the parser to favour the parsing of European dd/mm/yyyy dates then leave this set to "true"
43         // If you want the parser to favour the parsing of American mm/dd/yyyy dates then set to "false"
44         var favourDMY = true;
45
46         // If you have a regular expression you wish to add, add the Object to the end of the array
47         var dateTest = [
48                        { regExp:/^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/, d:3, m:1, y:5 },  // mdy
49                        { regExp:/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, d:1, m:3, y:5 },  // dmy
50                        { regExp:/^(\d\d\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/, d:5, m:3, y:1 }      // ymd
51                        ];
52
53         var start,y,m,d;
54         var cnt = 0;
55         var numFormats = dateTest.length;
56         while(cnt < numFormats) {
57                start = (cnt + (favourDMY ? numFormats + 1 : numFormats)) % numFormats;
58                if(aa.match(dateTest[start].regExp)) {
59                        res = aa.match(dateTest[start].regExp);
60                        y = res[dateTest[start].y];
61                        m = res[dateTest[start].m];
62                        d = res[dateTest[start].d];
63                        if(m.length == 1) m = "0" + String(m);
64                        if(d.length == 1) d = "0" + String(d);
65                        if(y.length != 4) y = (parseInt(y) < 50) ? "20" + String(y) : "19" + String(y);
66
67                        return y+String(m)+d+String(timestamp);
68                };
69                cnt++;
70         };
71         return -1;
72 };
73
74 /*
75     sortAlphaNumeric
76     -----------------------
77
78     This function sorts alphaNumeric values e.g. 1, e, 1a, -23c, 54z
79     
80     Notice how the prepareData function actually returns an Array i.e. you are not limited
81     in the type of data you return to the tableSort script.
82 */
83 function sortAlphaNumericPrepareData(tdNode, innerText){
84         var aa = innerText.toLowerCase().replace(" ", "");
85         var reg = /((\-|\+)?(\s+)?[0-9]+\.([0-9]+)?|(\-|\+)?(\s+)?(\.)?[0-9]+)([a-z]+)/;
86
87         if(reg.test(aa)) {
88                 var aaP = aa.match(reg);
89                 return [aaP[1], aaP[8]];
90         };
91
92         // Return an array
93         return isNaN(aa) ? ["",aa] : [aa,""];
94 }
95
96 function sortAlphaNumeric(a, b){
97         // Get the previously prepared array
98         var aa = a[fdTableSort.pos];
99         var bb = b[fdTableSort.pos];
100
101         // If they are equal then return 0
102         if(aa[0] == bb[0] && aa[1] == bb[1]) { return 0; };
103
104         // Check numeric parts if not equal
105         if(aa[0] != bb[0]) {
106                 if(aa[0] != "" && bb[0] != "") { return aa[0] - bb[0]; };
107                 if(aa[0] == "" && bb[0] != "") { return -1; };
108                 return 1;
109         };
110         
111         // Check alpha parts if numeric parts equal
112         if(aa[1] == bb[1]) return 0;
113         if(aa[1] < bb[1])  return -1;
114         return 1;
115 }
116
117 /*
118     sortDutchCurrencyValues
119     -----------------------
120
121     This function sorts Dutch currency values (of the type 100.000,00)
122     The function is "safe" i.e. non-currency data (like the word "Unknown") can be passed in and is sorted properly.
123 */
124 var sortDutchCurrencyValues = fdTableSort.sortNumeric;
125
126 function sortDutchCurrencyValuesPrepareData(tdNode, innerText) {
127         innerText = parseInt(innerText.replace(/[^0-9\.,]+/g, "").replace(/\./g,"").replace(",","."));
128         return isNaN(innerText) ? "" : innerText;
129 }
130
131 /*
132    sortByTwelveHourTimestamp
133    -------------------------
134
135    This custom sort function sorts 12 hour timestamps of an hour/minute nature.
136    The hour/minute dividor can be a full-stop or a colon and it correctly calculates that 12.30am is before 1am etc
137    The am/pm part can be written in lower or uppercase and can optionally contain full-stops e.g.
138
139    am, a.m, a.m., AM, A.M etc
140
141    Additionally, the values "12 midnight" and "12 noon" are also handled correctly.
142
143    The question remains... does "12p.m." mean "midnight" or "12 noon"? I've decided here that it's 12 noon.
144
145    The function is "safe" i.e. non-timestamp data (like the word "Unknown") can be passed in and is sorted properly.
146 */
147 var sortByTwelveHourTimestamp = fdTableSort.sortNumeric;
148
149 function sortByTwelveHourTimestampPrepareData(tdNode, innerText) {
150         tmp = innerText
151         innerText = innerText.replace(":",".");
152
153         // Check for the special cases of "12 noon" or "12 midnight"
154         if(innerText.search(/12([\s]*)?noon/i) != -1) return "12.00";
155         if(innerText.search(/12([\s]*)?midnight/i) != -1) return "24.00";
156
157         var regExpPM = /^([0-9]{1,2}).([0-9]{2})([\s]*)?(p[\.]?m)/i;
158         var regExpAM = /^([0-9]{1,2}).([0-9]{2})([\s]*)?(a[\.]?m)/i;
159
160         if(innerText.search(regExpPM) != -1) {
161                 var bits = innerText.match(regExpPM);
162                 if(parseInt(bits[1]) < 12) { bits[1] = parseInt(bits[1]) + 12; }
163         } else if(innerText.search(regExpAM) != -1) {
164                 var bits = innerText.match(regExpAM);
165                 if(bits[1] == "12") { bits[1] = "00"; }
166         } else return "";
167
168         if(bits[2].length < 2) { bits[2] = "0" + String(bits[2]); }
169
170         innerText = bits[1] + "." + String(bits[2]);
171
172         return isNaN(innerText) ? "" : innerText;
173 }
174 /*
175    sortEnglishLonghandDateFormat
176    -----------------------------
177
178    This custom sort function sorts dates of the format:
179
180    "12th April, 2006" or "12 April 2006" or "12-4-2006" or "12 April" or "12 4" or "12 Apr 2006" etc
181
182    The function expects dates to be in the format day/month/year. Should no year be stipulated,
183    the function treats the year as being the current year.
184
185    The function is "safe" i.e. non-date data (like the word "Unknown") can be passed in and is sorted properly.
186 */
187 var sortEnglishLonghandDateFormat = fdTableSort.sortNumeric;
188
189 function sortEnglishLonghandDateFormatPrepareData(tdNode, innerText) {
190         var months = ['january','february','march','april','may','june','july','august','september','october','november','december'];
191
192         var aa = innerText.toLowerCase();
193
194         // Replace the longhand months with an integer equivalent
195         for(var i = 0; i < 12; i++) {
196                 aa = aa.replace(months[i], i+1).replace(months[i].substring(0,3), i+1);
197         }
198
199         // If there are still alpha characters then return -1
200         if(aa.search(/a-z/) != -1) return -1;
201
202         // Replace multiple spaces and anything that is not numeric
203         aa = aa.replace(/\s+/g, " ").replace(/[^\d\s]/g, "");
204
205         // If were left with nothing then return -1
206         if(aa.replace(" ", "") == "") return -1;
207
208         // Split on the (now) single spaces
209         aa = aa.split(" ");
210
211         // If something has gone terribly wrong then return -1
212         if(aa.length < 2) return -1;
213
214         // If no year stipulated, then add this year as default
215         if(aa.length == 2) {
216                 aa[2] = String(new Date().getFullYear());
217         }
218
219         // Equalise the day and month
220         if(aa[0].length < 2) aa[0] = "0" + String(aa[0]);
221         if(aa[1].length < 2) aa[1] = "0" + String(aa[1]);
222
223         // Deal with Y2K issues
224         if(aa[2].length != 4) {
225                 aa[2] = (parseInt(aa[2]) < 50) ? '20' + aa[2] : '19' + aa[2];
226         }
227
228         // YMD (can be used as integer during comparison)
229         return aa[2] + String(aa[1]) + aa[0];
230 }
231 /*
232    sortIPAddress
233    -------------
234
235    This custom sort function correctly sorts IP addresses i.e. it checks all of the address parts and not just the first.
236
237    The function is "safe" i.e. non-IP address data (like the word "Unknown") can be passed in and is sorted properly.
238 */
239 var sortIPAddress = fdTableSort.sortNumeric;
240
241 function sortIPAddressPrepareData(tdNode, innerText) {
242         // Get the innerText of the TR nodes
243         var aa = innerText;
244
245         // Remove spaces
246         aa = aa.replace(" ","");
247
248         // If not an IP address then return -1
249         if(aa.search(/^([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3}).([0-9]{1,3})$/) == -1) return -1;
250
251         // Split on the "."
252         aa = aa.split(".");
253
254         // If we don't have 4 parts then return -1
255         if(aa.length != 4) return -1;
256
257         var retVal = "";
258
259         // Make all the parts an equal length and create a master integer
260         for(var i = 0; i < 4; i++) {
261                 retVal += (String(aa[i]).length < 3) ? "0000".substr(0, 3 - String(aa[i]).length) + String(aa[i]) : aa[i];
262         }
263
264         return retVal;
265 }
266 /*
267    sortScientificNotation
268    ----------------------
269
270    This custom sort function sorts numbers stipulated in scientific notation
271
272    The function is "safe" i.e. data like the word "Unknown" can be passed in and is sorted properly.
273
274    N.B. The only way I can think to really sort scientific notation is to convert
275         it to a floating point number and then perform the sort on that. If you can think of
276         an easier/better way then please let me know.
277 */
278 var sortScientificNotation = fdTableSort.sortNumeric;
279
280 function sortScientificNotationPrepareData(tdNode, innerText) {
281         var aa = innerText;
282
283         var floatRegExp = /((\-|\+)?(\s+)?[0-9]+\.([0-9]+)?|(\-|\+)?(\s+)?(\.)?[0-9]+)/g;
284
285         aa = aa.match(floatRegExp);
286
287         if(!aa || aa.length != 2) return "";
288
289         var f1 = parseFloat(aa[0].replace(" ",""))*Math.pow(10,parseFloat(aa[1].replace(" ","")));
290         return isNaN(f1) ? "" : f1;
291 }
292
293 /*
294         sortImage
295         ---------
296
297         This is the function called in order to sort the data previously prepared by the function
298         "sortImagePrepareData". It does a basic case sensitive comparison on the data using the
299         tableSort's in-built sortText method.
300 */
301 var sortImage = fdTableSort.sortText;
302
303 /*
304         This is the function used to prepare i.e. parse data, to be used during the sort
305         of the images within the last table.
306
307         In this case, we are checking to see if the TD node has any child nodes that are
308         images and, if an image exists, return it's "src" attribute.
309         If no image exists, then we return an empty string.
310
311         The "prepareData" functions are passed the actual TD node and also the TD node inner text
312         which means you are free to check for child nodes etc and are not just limited to
313         sorting on the TD node's inner text.
314
315         The prepareData functions are not required (only your bespoke sort function is required)
316         and only called by the script should they exist.
317 */
318 function sortImagePrepareData(td, innerText) {
319         var img = td.getElementsByTagName('img');
320         return img.length ? img[0].src: "";
321 }
322
323 /*
324         sortFileSize
325         ------------
326
327         1 Byte = 8 Bit
328         1 Kilobyte = 1024 Bytes
329         1 Megabyte = 1048576 Bytes
330         1 Gigabyte = 1073741824 Bytes
331 */
332 var sortFileSize = fdTableSort.sortNumeric;
333
334 function sortFileSizePrepareData(td, innerText) {
335         var regExp = /(kb|mb|gb)/i;
336
337         var type = innerText.search(regExp) != -1 ? innerText.match(regExp)[0] : "";
338
339         switch (type.toLowerCase()) {
340                 case "kb" :
341                         mult = 1024;
342                         break;
343                 case "mb" :
344                         mult = 1048576;
345                         break;
346                 case "gb" :
347                         mult = 1073741824;
348                         break;
349                 default :
350                         mult = 1;
351         };
352
353         innerText = parseFloat(innerText.replace(/[^0-9\.\-]/g,''));
354
355         return isNaN(innerText) ? "" : innerText * mult;
356 };
357
358 var sortBandwidth = fdTableSort.sortNumeric;
359
360 function sortBandwidthPrepareData(td, innerText) {
361         var regExp = /(kbps|mbps|gbps)/i;
362
363         var type = innerText.search(regExp) != -1 ? innerText.match(regExp)[0] : "";
364
365         switch (type.toLowerCase()) {
366                 case "kbps" :
367                         mult = 1000;
368                         break;
369                 case "mb" :
370                         mult = 1000000;
371                         break;
372                 case "gb" :
373                         mult = 1000000000;
374                         break;
375                 default :
376                         mult = 1;
377         };
378
379         innerText = parseFloat(innerText.replace(/[^0-9\.\-]/g,''));
380
381         return isNaN(innerText) ? "" : innerText * mult;
382 };