xref: /plugin/sortablejs/script.js (revision 22eb40c487b267ee2b9febff281753ea58339c0d)
1/*
2 based on code from http://www.kryogenix.org/code/browser/sorttable/ by Stuart Langridge
3 (distributed under the condisions of MIT licence from http://www.kryogenix.org/code/browser/licence.html)
4 see
5 2007-2016 by oiv (Otto Vainio at otto@valjakko.net)
6 2016-? by vaxquis AKA FyiurAmron (spamove@gmail.com)
7 */
8
9//
10
11var stIsIE = /*@cc_on!@*/false;
12//var tableid = 0;
13
14var sorttable = {
15    reinit: function () {
16        arguments.callee.done = true;
17
18        if ( !document.createElement || !document.getElementsByTagName ) {
19            return;
20        }
21
22        var elems = document.getElementsByTagName( "table" );
23        var elem;
24        for( var i = 0; i < elems.length; i++ ) {
25            elem = elems[i];
26            if ( jQuery(elem).hasClass("sortable") ) {
27                sorttable.makeSortable( elem );
28            }
29        }
30        elems = document.getElementsByTagName( "div" );
31        for( var i = 0; i < elems.length; i++ ) {
32            elem = elems[i];
33            if ( jQuery(elem).hasClass("sortable") ) {
34                sorttable.makeSortableDiv( elem );
35            }
36        }
37    },
38    init: function () {
39        if ( arguments.callee.done ) {
40            return;
41        }
42        sorttable.reinit();
43    },
44    makeSortableDiv: function ( div ) {
45        var childTables = div.getElementsByTagName( "table" );
46        var elem;
47        for( var i = 0; i < childTables.length; i++ ) {
48            elem = childTables[i];
49            var colid = div.className;
50            var patt1 = /\bcol_\d_[a-z]+/gi;
51            var overs = [];
52            if ( colid.search( patt1 ) !== -1 ) {
53                var overrides = colid.match( patt1 );
54                for( var i = 0; i < overrides.length; i++ ) {
55                    var entry = overrides[i];
56                    if ( entry !== "" ) {
57                        try {
58                            var tmp = entry.split( "_" );
59                            var ind = tmp[1];
60                            var val = tmp[2];
61                            overs[ind] = val;
62                        } catch( e ) {
63                        }
64                    }
65                }
66                colid = colid.replace( patt1, '' );
67            }
68            var patt2 = /\bsortbottom_?\d?/gi;
69            var bottoms = 0;
70            if ( colid.search( patt2 ) !== -1 ) {
71                var bs = colid.match( patt2 );
72                try {
73                    var tmp = bs[0].split( "_" );
74                    //var ind = tmp[1];
75                    var val = 1;
76                    if ( tmp.length > 1 ) {
77                        val = tmp[1];
78                    }
79                    bottoms = val;
80                } catch( e ) {
81                }
82            }
83            var patt2ph = /\bthreephase/gi;
84            var ph2 = true;
85            if ( colid.search( patt2ph ) !== -1 ) {
86                ph2 = false;
87            }
88
89            sorttable.makeSortable( elem, overs, bottoms, ph2 );
90            var pattdefault = /\bsortr?\d\d?/gi;
91            if ( colid.search( pattdefault ) !== -1 ) {
92                var mi = colid.match( pattdefault );
93                colid = mi[0].replace( 'sort', '' );
94                if ( colid !== '' ) {
95                    colid = colid.trim();
96                }
97                var revs = false;
98                if ( colid.search( /\br/ ) !== -1 ) {
99                    revs = true;
100                    colid = colid.replace( 'r', '' );
101                }
102                sorttable.defaultSort( elem, colid, revs );
103            }
104        }
105    },
106    defaultSort: function ( table, colid, revs ) {
107//    theadrow = table.tHead.rows[0].cells;
108        var havetHead = table.tHead;
109        var sindex = 1;
110        if ( havetHead ) {
111            sindex = 0;
112        }
113        var theadrow = table.rows[0].cells;
114        colid--;
115        var colname = "col" + colid;
116        // remove sorttable_sorted classes
117        var thiscell = false;
118        for( var i = 0; i < theadrow.length; i++ ) {
119            var cell = theadrow[i];
120            var colclass = cell.className;
121            var classname = colclass.split( " " );
122//       if (cell.className==colname)
123            if ( classname[0] === colname ) {
124                thiscell = cell;
125            }
126        }
127        if ( thiscell === false ) {
128            return;
129        }
130        // build an array to sort. This is a Schwartzian transform thing,
131        // i.e., we "decorate" each row with the actual sort key,
132        // sort based on the sort keys, and then put the rows back in order
133        // which is a lot faster because you only do getInnerText once per row
134        var row_array = [];
135        var col = thiscell.sorttable_columnindex;
136        var rows = thiscell.sorttable_tbody.rows;
137        for( var j = sindex; j < rows.length; j++ ) {
138            row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]];
139        }
140        /* If you want a stable sort, uncomment the following line */
141        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
142        /* and comment out this one */
143        row_array.sort( thiscell.sorttable_sortfunction );
144
145        var tb = thiscell.sorttable_tbody;
146        for( var jj = 0; jj < row_array.length; jj++ ) {
147            tb.appendChild( row_array[jj][1] );
148        }
149
150        if ( revs ) {
151            sorttable.reverse( thiscell.sorttable_tbody, sindex );
152            jQuery(thiscell).addClass( "sorttable_sorted_reverse" );
153        } else {
154            jQuery(thiscell).addClass( "sorttable_sorted" );
155        }
156    },
157    makeSortable: function ( table, overrides, bottoms, ph2 ) {
158//    tableid++;
159        /*
160         if (table.getElementsByTagName('thead').length === 0) {
161         // table doesn't have a tHead. Since it should have, create one and
162         // put the first table row in it.
163         the = document.createElement('thead');
164         the.appendChild(table.rows[0]);
165         table.insertBefore(the,table.firstChild);
166         }
167         */
168        // Safari doesn't support table.tHead, sigh
169        /*
170         if (table.tHead === null) {table.tHead = table.getElementsByTagName('thead')[0];}
171
172         if (table.tHead.rows.length != 1) {return;} // can't cope with two header rows
173         */
174//    table.tHead.className += ' tableid'+tableid;
175
176        // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
177        // "total" rows, for example). This is B&R, since what you're supposed
178        // to do is put them in a tfoot. So, if there are sortbottom rows,
179        // for backwards compatibility, move them to tfoot (creating it if needed).
180
181        var sortbottomrows = [];
182        if ( bottoms > 0 ) {
183            var frombottom = table.rows.length - bottoms;
184            for( var i = table.rows.length - 1; i >= frombottom; i-- ) {
185//      if (bottoms<frombottom) {
186                sortbottomrows[sortbottomrows.length] = table.rows[i];
187//      }
188//      frombottom++;
189            }
190            if ( sortbottomrows ) {
191                var tfo;
192                if ( table.tFoot === null ) {
193                    // table doesn't have a tfoot. Create one.
194                    tfo = document.createElement( 'tfoot' );
195                    table.appendChild( tfo );
196                }
197                for( var ii = sortbottomrows.length - 1; ii >= 0; ii-- ) {
198                    tfo.appendChild( sortbottomrows[ii] );
199                }
200                //delete sortbottomrows;
201            }
202        }
203        // work through each column and calculate its type
204        var havetHead = table.tHead;
205        var sindex = 1;
206        if ( havetHead ) {
207            sindex = 0;
208        }
209        var headrow = table.rows[0].cells;
210
211        for( var i = 0; i < headrow.length; i++ ) {
212            // manually override the type with a sorttable_type attribute
213            var colOptions = "";
214            if ( overrides[i + 1] )
215            {
216                colOptions = overrides[i + 1];
217            }
218            if ( !colOptions.match( /\bnosort\b/ ) ) { // skip this col
219                var mtch = colOptions.match( /\b[a-z0-9]+\b/ );
220                var override;
221                if ( mtch ) {
222                    override = mtch[0];
223                }
224                if ( mtch && typeof sorttable["sort_" + override] === 'function' ) {
225                    headrow[i].sorttable_sortfunction = sorttable["sort_" + override];
226                } else {
227                    headrow[i].sorttable_sortfunction = sorttable.guessType( table, i );
228                }
229                /*
230                 if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
231                 mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
232                 if (mtch) { override = mtch[1]; }
233                 if (mtch && typeof sorttable["sort_"+override] == 'function') {
234                 headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
235                 } else {
236                 headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
237                 }
238                 */
239                // make it clickable to sort
240                headrow[i].sorttable_columnindex = i;
241                headrow[i].sorttable_tbody = table.tBodies[0];
242                headrow[i].sindex = sindex;
243
244                jQuery( headrow[i] ).click( function () {
245                    var theadrow = this.parentNode;
246                    var jqt = jQuery( this );
247                    if ( jqt.hasClass( "sorttable_sorted" ) ) {
248                        // if we're already sorted by this column, just reverse the table
249                        sorttable.reverse( this.sorttable_tbody, this.sindex );
250                        jqt.removeClass( "sorttable_sorted" );
251                        jqt.addClass( "sorttable_sorted_reverse" );
252                        return;
253                    }
254                    if ( jqt.hasClass( "sorttable_sorted_reverse" ) ) {
255                        if ( !ph2 ) {
256                            sorttable.original_order( this.sorttable_tbody, this.sindex );
257                            var list = theadrow.childNodes;
258                            for( var i = 0; i < list.length; i++ ) {
259                                var cell = list[i];
260                                if ( cell.nodeType === 1 ) { // an element
261                                    var cc = jQuery( cell );
262                                    cc.removeClass( "sorttable_sorted" );
263                                    cc.removeClass( "sorttable_sorted_reverse" );
264                                }
265                            }
266                            return;
267                        } else {
268                            // if we're already sorted by this column in reverse, just re-reverse the table
269                            sorttable.reverse( this.sorttable_tbody, this.sindex );
270                            jqt.removeClass( "sorttable_sorted_reverse" );
271                            jqt.addClass( "sorttable_sorted" );
272                            return;
273                        }
274                    }
275
276                    // remove sorttable_sorted classes
277                    var list = theadrow.childNodes;
278                    for( var i = 0; i < list.length; i++ ) {
279                        var cell = list[i];
280                        if ( cell.nodeType === 1 ) { // an element
281                            var cc = jQuery( cell );
282                            cc.removeClass( "sorttable_sorted" );
283                            cc.removeClass( "sorttable_sorted_reverse" );
284                        }
285                    }
286                    jqt.addClass( "sorttable_sorted" );
287
288                    // build an array to sort. This is a Schwartzian transform thing,
289                    // i.e., we "decorate" each row with the actual sort key,
290                    // sort based on the sort keys, and then put the rows back in order
291                    // which is a lot faster because you only do getInnerText once per row
292                    var row_array = [];
293                    var col = this.sorttable_columnindex;
294                    var rows = this.sorttable_tbody.rows;
295                    sindex = this.sindex;
296                    for( var j = sindex; j < rows.length; j++ ) {
297                        row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]];
298                    }
299                    /* If you want a stable sort, uncomment the following line */
300                    //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
301                    /* and comment out this one */
302                    row_array.sort( this.sorttable_sortfunction );
303
304                    var tb = this.sorttable_tbody;
305                    for( var j3 = 0; j3 < row_array.length; j3++ ) {
306                        tb.appendChild( row_array[j3][1] );
307                    }
308
309                    //delete row_array;
310                } );
311            }
312        }
313    },
314    guessType: function ( table, column ) {
315        // guess the type of a column based on its first non-blank row
316        var textCnt = 0;
317        var numCnt = 0;
318        var dateCnt = 0;
319        var ipCnt = 0;
320
321        for( var i = 0; i < table.tBodies[0].rows.length; i++ ) {
322            var text = sorttable.getInnerText( table.tBodies[0].rows[i].cells[column] );
323            if ( text !== "" ) {
324                if ( text.match( /^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$/ ) ) {
325                    ipCnt++;
326                } else if ( text.match( /^[\-\+]?.?\d*[\d,.]?\d+.?$/ ) ) {
327                    numCnt++;
328                } else if ( !isNaN( new Date( text ).getTime() ) ) {
329                    dateCnt++;
330                } else { // not a date (nor IP nor number)
331                    textCnt++;
332                }
333            }
334        }
335        if ( textCnt > numCnt && textCnt > ipCnt && textCnt > dateCnt )
336            return sorttable.sort_alpha;
337        if ( numCnt > ipCnt && numCnt > dateCnt )
338            return sorttable.sort_numeric;
339        if ( ipCnt > dateCnt )
340            return sorttable.sort_ipaddr;
341        return sorttable.sort_date;
342    },
343    getInnerText: function ( node ) {
344        // gets the text we want to use for sorting for a cell.
345        // strips leading and trailing whitespace.
346        // this is *not* a generic getInnerText function; it's special to sorttable.
347        // for example, you can override the cell text with a customkey attribute.
348        // it also gets .value for <input> fields.
349        if ( !node ) {
350            return '';
351        }
352        var hasInputs = ( typeof node.getElementsByTagName === "function" ) &&
353                node.getElementsByTagName( "input" ).length;
354        if ( node.getAttribute( "sorttable_customkey" ) !== null ) {
355            return node.getAttribute( "sorttable_customkey" );
356        } else if ( typeof node.textContent !== "undefined" && !hasInputs ) {
357            return node.textContent.replace( /^\s+|\s+$/g, '' );
358        } else if ( typeof node.innerText !== "undefined" && !hasInputs ) {
359            return node.innerText.replace( /^\s+|\s+$/g, '' );
360        } else if ( typeof node.text !== "undefined" && !hasInputs ) {
361            return node.text.replace( /^\s+|\s+$/g, '' );
362        } else {
363            switch ( node.nodeType ) {
364                case 3:
365                    return ( node.nodeName.toLowerCase() === "input" ) ? node.value.replace( /^\s+|\s+$/g, '' ) : '';
366                case 4:
367                    return node.nodeValue.replace( /^\s+|\s+$/g, '' );
368                case 1:
369                case 11:
370                    var innerText = '';
371                    for( var i = 0; i < node.childNodes.length; i++ ) {
372                        innerText += sorttable.getInnerText( node.childNodes[i] );
373                    }
374                    return innerText.replace( /^\s+|\s+$/g, '' );
375                default:
376                    return '';
377            }
378        }
379    },
380    reverse: function ( tbody, sindex ) {
381        // reverse the rows in a tbody
382        var newrows = [];
383        for( var i = sindex; i < tbody.rows.length; i++ ) {
384            newrows[newrows.length] = tbody.rows[i];
385        }
386        for( var i = newrows.length - 1; i >= 0; i-- ) {
387            tbody.appendChild( newrows[i] );
388        }
389        //delete newrows;
390    },
391    original_order: function ( tbody, isindex ) {
392        // build an array to sort. This is a Schwartzian transform thing,
393        // i.e., we "decorate" each row with the actual sort key,
394        // sort based on the sort keys, and then put the rows back in order
395        // which is a lot faster because you only do getInnerText once per row
396        var row_array = [];
397        var rows = tbody.rows;
398        var sindex = isindex;
399        for( var j = sindex; j < rows.length; j++ ) {
400            row_array[row_array.length] = [rows[j].className, rows[j]];
401        }
402        /* If you want a stable sort, uncomment the following line */
403        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
404        /* and comment out this one */
405        row_array.sort( sorttable.sort_alpha );
406
407        var tb = tbody;
408        for( var j3 = 0; j3 < row_array.length; j3++ ) {
409            tb.appendChild( row_array[j3][1] );
410        }
411
412        //delete row_array;
413    },
414    /* sort functions
415     each sort function takes two parameters, a and b
416     you are comparing a[0] and b[0] */
417    sort_ipaddr: function ( a, b ) {
418        var aa = a[0].split( ".", 4 );
419        var bb = b[0].split( ".", 4 );
420        var resulta = aa[0] * 0x1000000 + aa[1] * 0x10000 + aa[2] * 0x100 + aa[3] * 1;
421        var resultb = bb[0] * 0x1000000 + bb[1] * 0x10000 + bb[2] * 0x100 + bb[3] * 1;
422        return resulta - resultb;
423    },
424    sort_numeric: function ( a, b ) {
425        if ( a[0] === "" ) {
426            return -1;
427        }
428        if ( b[0] === "" ) {
429            return 1;
430        }
431        var aa = parseFloat( a[0].replace( ",", "." ).replace( /[^0-9.\-]/g, "" ) );
432        if ( isNaN( aa ) ) {
433            aa = Number.NEGATIVE_INFINITY;
434        }
435        var bb = parseFloat( b[0].replace( ",", "." ).replace( /[^0-9.\-]/g, "" ) );
436        if ( isNaN( bb ) ) {
437            bb = Number.NEGATIVE_INFINITY;
438        }
439        return aa - bb;
440    },
441    sort_alpha: function ( a, b ) {
442        return a[0].localeCompare( b[0] );
443    },
444    sort_date: function ( a, b ) {
445        var aa = new Date( a[0] ), bb = new Date( b[0] );
446        return ( aa > bb ) - ( aa < bb );
447    },
448    shaker_sort: function ( list, comp_func ) {
449        // A stable sort function to allow multi-level sorting of data
450        // see: http://en.wikipedia.org/wiki/Cocktail_sort
451        // thanks to Joseph Nahmias
452        var b = 0;
453        var t = list.length - 1;
454        var swap = true;
455        var q;
456
457        while( swap ) {
458            swap = false;
459            for( var i = b; i < t; ++i ) {
460                if ( comp_func( list[i], list[i + 1] ) > 0 ) {
461                    q = list[i];
462                    list[i] = list[i + 1];
463                    list[i + 1] = q;
464                    swap = true;
465                }
466            } // for
467            t--;
468
469            if ( !swap ) {
470                break;
471            }
472
473            for( var i = t; i > b; --i ) {
474                if ( comp_func( list[i], list[i - 1] ) < 0 ) {
475                    q = list[i];
476                    list[i] = list[i - 1];
477                    list[i - 1] = q;
478                    swap = true;
479                }
480            } // for
481            b++;
482
483        } // while(swap)
484    }
485
486};
487
488if ( typeof ( window.addEvent ) !== "undefined" ) {
489    window.addEvent( window, "load", sorttable.init );
490} else {
491    jQuery( function () {
492        sorttable.init();
493    } );
494}
495