xref: /plugin/sortablejs/script.js (revision a45f1e335ef6fae6c5976a603f6549dec0e47271)
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
9var stIsIE = /*@cc_on!@*/false;
10//var tableid = 0;
11
12var sorttable = {
13    reinit: function () {
14        arguments.callee.done = true;
15
16        if ( !document.createElement || !document.getElementsByTagName ) {
17            return;
18        }
19
20        var elems = document.getElementsByTagName( "table" );
21        var elem;
22        for( var i = 0; i < elems.length; i++ ) {
23            elem = elems[i];
24            if ( jQuery(elem).hasClass("sortable") ) {
25                sorttable.makeSortable( elem );
26            }
27        }
28        elems = document.getElementsByTagName( "div" );
29        for( var i = 0; i < elems.length; i++ ) {
30            elem = elems[i];
31            if ( jQuery(elem).hasClass("sortable") ) {
32                sorttable.makeSortableDiv( elem );
33            }
34        }
35    },
36    init: function () {
37        if ( arguments.callee.done ) {
38            return;
39        }
40        sorttable.reinit();
41    },
42    makeSortableDiv: function ( div ) {
43        var childTables = div.getElementsByTagName( "table" );
44        var elem;
45        for( var i = 0; i < childTables.length; i++ ) {
46            elem = childTables[i];
47            var colid = div.className;
48            var patt1 = /\bcol_\d_[a-z]+/gi;
49            var overs = [];
50            if ( colid.search( patt1 ) !== -1 ) {
51                var overrides = colid.match( patt1 );
52                for( var i = 0; i < overrides.length; i++ ) {
53                    var entry = overrides[i];
54                    if ( entry !== "" ) {
55                        try {
56                            var tmp = entry.split( "_" );
57                            var ind = tmp[1];
58                            var val = tmp[2];
59                            overs[ind] = val;
60                        } catch( e ) {
61                        }
62                    }
63                }
64                colid = colid.replace( patt1, '' );
65            }
66            var patt2 = /\bsortbottom_?\d?/gi;
67            var bottoms = 0;
68            if ( colid.search( patt2 ) !== -1 ) {
69                var bs = colid.match( patt2 );
70                try {
71                    var tmp = bs[0].split( "_" );
72                    //var ind = tmp[1];
73                    var val = 1;
74                    if ( tmp.length > 1 ) {
75                        val = tmp[1];
76                    }
77                    bottoms = val;
78                } catch( e ) {
79                }
80            }
81            var patt2ph = /\bthreephase/gi;
82            var ph2 = true;
83            if ( colid.search( patt2ph ) !== -1 ) {
84                ph2 = false;
85            }
86
87            sorttable.makeSortable( elem, overs, bottoms, ph2 );
88            var pattdefault = /\bsortr?\d\d?/gi;
89            if ( colid.search( pattdefault ) !== -1 ) {
90                var mi = colid.match( pattdefault );
91                colid = mi[0].replace( 'sort', '' );
92                if ( colid !== '' ) {
93                    colid = colid.trim();
94                }
95                var revs = false;
96                if ( colid.search( /\br/ ) !== -1 ) {
97                    revs = true;
98                    colid = colid.replace( 'r', '' );
99                }
100                sorttable.defaultSort( elem, colid, revs );
101            }
102        }
103    },
104    defaultSort: function ( table, colid, revs ) {
105//    theadrow = table.tHead.rows[0].cells;
106        var havetHead = table.tHead;
107        var sindex = 1;
108        if ( havetHead ) {
109            sindex = 0;
110        }
111        var theadrow = table.rows[0].cells;
112        colid--;
113        var colname = "col" + colid;
114        // remove sorttable_sorted classes
115        var thiscell = false;
116        for( var i = 0; i < theadrow.length; i++ ) {
117            var cell = theadrow[i];
118            var colclass = cell.className;
119            var classname = colclass.split( " " );
120//       if (cell.className==colname)
121            if ( classname[0] === colname ) {
122                thiscell = cell;
123            }
124        }
125        if ( thiscell === false ) {
126            return;
127        }
128        // build an array to sort. This is a Schwartzian transform thing,
129        // i.e., we "decorate" each row with the actual sort key,
130        // sort based on the sort keys, and then put the rows back in order
131        // which is a lot faster because you only do getInnerText once per row
132        var row_array = [];
133        var col = thiscell.sorttable_columnindex;
134        var rows = thiscell.sorttable_tbody.rows;
135        for( var j = sindex; j < rows.length; j++ ) {
136            row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]];
137        }
138        /* If you want a stable sort, uncomment the following line */
139        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
140        /* and comment out this one */
141        row_array.sort( thiscell.sorttable_sortfunction );
142
143        var tb = thiscell.sorttable_tbody;
144        for( var jj = 0; jj < row_array.length; jj++ ) {
145            tb.appendChild( row_array[jj][1] );
146        }
147
148        if ( revs ) {
149            sorttable.reverse( thiscell.sorttable_tbody, sindex );
150            jQuery(thiscell).addClass( "sorttable_sorted_reverse" );
151        } else {
152            jQuery(thiscell).addClass( "sorttable_sorted" );
153        }
154    },
155    makeSortable: function ( table, overrides, bottoms, ph2 ) {
156//    tableid++;
157        /*
158         if (table.getElementsByTagName('thead').length === 0) {
159         // table doesn't have a tHead. Since it should have, create one and
160         // put the first table row in it.
161         the = document.createElement('thead');
162         the.appendChild(table.rows[0]);
163         table.insertBefore(the,table.firstChild);
164         }
165         */
166        // Safari doesn't support table.tHead, sigh
167        /*
168         if (table.tHead === null) {table.tHead = table.getElementsByTagName('thead')[0];}
169
170         if (table.tHead.rows.length != 1) {return;} // can't cope with two header rows
171         */
172//    table.tHead.className += ' tableid'+tableid;
173
174        // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
175        // "total" rows, for example). This is B&R, since what you're supposed
176        // to do is put them in a tfoot. So, if there are sortbottom rows,
177        // for backwards compatibility, move them to tfoot (creating it if needed).
178
179        var sortbottomrows = [];
180        if ( bottoms > 0 ) {
181            var frombottom = table.rows.length - bottoms;
182            for( var i = table.rows.length - 1; i >= frombottom; i-- ) {
183//      if (bottoms<frombottom) {
184                sortbottomrows[sortbottomrows.length] = table.rows[i];
185//      }
186//      frombottom++;
187            }
188            if ( sortbottomrows ) {
189                var tfo;
190                if ( table.tFoot === null ) {
191                    // table doesn't have a tfoot. Create one.
192                    tfo = document.createElement( 'tfoot' );
193                    table.appendChild( tfo );
194                }
195                for( var ii = sortbottomrows.length - 1; ii >= 0; ii-- ) {
196                    tfo.appendChild( sortbottomrows[ii] );
197                }
198                //delete sortbottomrows;
199            }
200        }
201        // work through each column and calculate its type
202        var havetHead = table.tHead;
203        var sindex = 1;
204        if ( havetHead ) {
205            sindex = 0;
206        }
207        var headrow = table.rows[0].cells;
208
209        for( var i = 0; i < headrow.length; i++ ) {
210            // manually override the type with a sorttable_type attribute
211            var colOptions = "";
212            if ( overrides[i + 1] )
213            {
214                colOptions = overrides[i + 1];
215            }
216            if ( colOptions.match( /\bnosort\b/ ) ) {
217                jQuery(headrow[i]).addClass("sorttable_nosort");
218            } else { // 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