├── README.md
├── plugin.info.txt
├── screen.css
├── script.js
└── syntax.php
/README.md:
--------------------------------------------------------------------------------
1 | sortablejs
2 | ==========
3 |
4 | JavaScript-based (client-side) DokuWiki table sorting plugin. In semi-maintenance mode now - only bugfixes and feature requests via PRs are handled.
5 |
6 | DokuWiki homepage at https://www.dokuwiki.org/plugin:sortablejs
7 |
8 | For server-side sorting (required for DW2PDF etc.) or more robust sort facilities, use @samwilson 's dokuwiki sorter (based on https://github.com/Mottie/tablesorter): https://github.com/samwilson/dokuwiki_sortablejs
9 |
10 | Cross-plugin compatibility is outside the scope of this repo.
11 |
12 | If reporting bugs, please provide a valid reproduction scenario for any non-trivial case.
13 |
--------------------------------------------------------------------------------
/plugin.info.txt:
--------------------------------------------------------------------------------
1 | base sortablejs
2 | author vaxquis
3 | email spamove@gmail.com
4 | date 2023-04-20
5 | name sortablejs
6 | desc Allow sorting tables by columns
7 | url https://github.com/FyiurAmron/sortablejs
8 |
--------------------------------------------------------------------------------
/screen.css:
--------------------------------------------------------------------------------
1 | div.sortable thead tr.row0 th:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort):after {
2 | content: " \25B4\25BE"
3 | }
4 |
5 | div.sortable thead tr.row0 th.sorttable_sorted:after {
6 | content: " \25BE"
7 | }
8 |
9 | div.sortable thead tr.row0 th.sorttable_sorted_reverse:after {
10 | content: " \25B4"
11 | }
12 |
--------------------------------------------------------------------------------
/script.js:
--------------------------------------------------------------------------------
1 | /**
2 | Based on code from http://www.kryogenix.org/code/browser/sorttable/ by Stuart Langridge
3 | (distributed under the conditions of MIT licence from http://www.kryogenix.org/code/browser/licence.html).
4 | Includes open-source contributions from other people
5 | (see https://github.com/FyiurAmron/sortablejs/graphs/contributors for more details).
6 | Maintainers:
7 | 2007-2016 oiv (Otto Vainio at otto@valjakko.net)
8 | 2016-? vaxquis AKA FyiurAmron (spamove@gmail.com)
9 | */
10 |
11 | var stIsIE = /*@cc_on!@*/false;
12 | //var tableid = 0;
13 |
14 | var 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 | var havetHead = table.tHead;
108 | var sindex = 1;
109 | if ( havetHead ) {
110 | sindex = 0;
111 | }
112 | var theadrow = table.rows[0].cells;
113 | colid--;
114 | var colname = "col" + colid;
115 | // remove sorttable_sorted classes
116 | var thiscell = false;
117 | for( var i = 0; i < theadrow.length; i++ ) {
118 | var cell = theadrow[i];
119 | var colclass = cell.className;
120 | var classname = colclass.split( " " );
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 | // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
157 | // "total" rows, for example). This is B&R, since what you're supposed
158 | // to do is put them in a tfoot. So, if there are sortbottom rows,
159 | // for backwards compatibility, move them to tfoot (creating it if needed).
160 |
161 | var sortbottomrows = [];
162 | if ( bottoms > 0 ) {
163 | var frombottom = table.rows.length - bottoms;
164 | for( var i = table.rows.length - 1; i >= frombottom; i-- ) {
165 | sortbottomrows[sortbottomrows.length] = table.rows[i];
166 | }
167 | if ( sortbottomrows ) {
168 | var tfo;
169 | if ( table.tFoot === null ) {
170 | // table doesn't have a tfoot. Create one.
171 | tfo = document.createElement( 'tfoot' );
172 | table.appendChild( tfo );
173 | }
174 | for( var ii = sortbottomrows.length - 1; ii >= 0; ii-- ) {
175 | tfo.appendChild( sortbottomrows[ii] );
176 | }
177 | //delete sortbottomrows;
178 | }
179 | }
180 | // work through each column and calculate its type
181 | var havetHead = table.tHead;
182 | var sindex = 1;
183 | if ( havetHead ) {
184 | sindex = 0;
185 | }
186 | var headrow = table.rows[0].cells;
187 |
188 | for( var i = 0; i < headrow.length; i++ ) {
189 | // manually override the type with a sorttable_type attribute
190 | var colOptions = "";
191 | if ( overrides[i + 1] )
192 | {
193 | colOptions = overrides[i + 1];
194 | }
195 | if ( colOptions.match( /\bnosort\b/ ) ) {
196 | jQuery(headrow[i]).addClass("sorttable_nosort");
197 | } else { // skip this col
198 | var mtch = colOptions.match( /\b[a-z0-9]+\b/ );
199 | var override;
200 | if ( mtch ) {
201 | override = mtch[0];
202 | }
203 | if ( mtch && typeof sorttable["sort_" + override] === 'function' ) {
204 | headrow[i].sorttable_sortfunction = sorttable["sort_" + override];
205 | } else {
206 | headrow[i].sorttable_sortfunction = sorttable.guessType( table, i );
207 | }
208 |
209 | // make it clickable to sort
210 | headrow[i].sorttable_columnindex = i;
211 | headrow[i].sorttable_tbody = table.tBodies[0];
212 | headrow[i].sindex = sindex;
213 |
214 | jQuery( headrow[i] ).click( function () {
215 | var theadrow = this.parentNode;
216 | var jqt = jQuery( this );
217 | if ( jqt.hasClass( "sorttable_sorted" ) ) {
218 | // if we're already sorted by this column, just reverse the table
219 | sorttable.reverse( this.sorttable_tbody, this.sindex );
220 | jqt.removeClass( "sorttable_sorted" );
221 | jqt.addClass( "sorttable_sorted_reverse" );
222 | return;
223 | }
224 | if ( jqt.hasClass( "sorttable_sorted_reverse" ) ) {
225 | if ( !ph2 ) {
226 | sorttable.original_order( this.sorttable_tbody, this.sindex );
227 | var list = theadrow.childNodes;
228 | for( var i = 0; i < list.length; i++ ) {
229 | var cell = list[i];
230 | if ( cell.nodeType === 1 ) { // an element
231 | var cc = jQuery( cell );
232 | cc.removeClass( "sorttable_sorted" );
233 | cc.removeClass( "sorttable_sorted_reverse" );
234 | }
235 | }
236 | return;
237 | } else {
238 | // if we're already sorted by this column in reverse, just re-reverse the table
239 | sorttable.reverse( this.sorttable_tbody, this.sindex );
240 | jqt.removeClass( "sorttable_sorted_reverse" );
241 | jqt.addClass( "sorttable_sorted" );
242 | return;
243 | }
244 | }
245 |
246 | // remove sorttable_sorted classes
247 | var list = theadrow.childNodes;
248 | for( var i = 0; i < list.length; i++ ) {
249 | var cell = list[i];
250 | if ( cell.nodeType === 1 ) { // an element
251 | var cc = jQuery( cell );
252 | cc.removeClass( "sorttable_sorted" );
253 | cc.removeClass( "sorttable_sorted_reverse" );
254 | }
255 | }
256 | jqt.addClass( "sorttable_sorted" );
257 |
258 | // build an array to sort. This is a Schwartzian transform thing,
259 | // i.e., we "decorate" each row with the actual sort key,
260 | // sort based on the sort keys, and then put the rows back in order
261 | // which is a lot faster because you only do getInnerText once per row
262 | var row_array = [];
263 | var col = this.sorttable_columnindex;
264 | var rows = this.sorttable_tbody.rows;
265 | sindex = this.sindex;
266 | for( var j = sindex; j < rows.length; j++ ) {
267 | row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]];
268 | }
269 | /* If you want a stable sort, uncomment the following line */
270 | //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
271 | /* and comment out this one */
272 | row_array.sort( this.sorttable_sortfunction );
273 |
274 | var tb = this.sorttable_tbody;
275 | for( var j3 = 0; j3 < row_array.length; j3++ ) {
276 | tb.appendChild( row_array[j3][1] );
277 | }
278 |
279 | //delete row_array;
280 | } );
281 | }
282 | }
283 | },
284 | guessType: function ( table, column ) {
285 | // guess the type of a column based on its first non-blank row
286 | var textCnt = 0;
287 | var numCnt = 0;
288 | var dateCnt = 0;
289 | var ipCnt = 0;
290 |
291 | for( var i = 0; i < table.tBodies[0].rows.length; i++ ) {
292 | var text = sorttable.getInnerText( table.tBodies[0].rows[i].cells[column] );
293 | if ( text !== "" ) {
294 | 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])$/ ) ) {
295 | ipCnt++;
296 | } else if ( text.match( /^[\-\+]?.?\d*[\d,.]?\d+.?$/ ) ) {
297 | numCnt++;
298 | } else if ( !isNaN( new Date( text ).getTime() ) ) {
299 | dateCnt++;
300 | } else { // not a date (nor IP nor number)
301 | textCnt++;
302 | }
303 | }
304 | }
305 | if ( textCnt > numCnt && textCnt > ipCnt && textCnt > dateCnt )
306 | return sorttable.sort_alpha;
307 | if ( numCnt > ipCnt && numCnt > dateCnt )
308 | return sorttable.sort_numeric;
309 | if ( ipCnt > dateCnt )
310 | return sorttable.sort_ipaddr;
311 | return sorttable.sort_date;
312 | },
313 | getInnerText: function ( node ) {
314 | // gets the text we want to use for sorting for a cell.
315 | // strips leading and trailing whitespace.
316 | // this is *not* a generic getInnerText function; it's special to sorttable.
317 | // for example, you can override the cell text with a customkey attribute.
318 | // it also gets .value for fields.
319 | if ( !node ) {
320 | return '';
321 | }
322 | var hasInputs = ( typeof node.getElementsByTagName === "function" ) &&
323 | node.getElementsByTagName( "input" ).length;
324 | if ( node.getAttribute( "sorttable_customkey" ) !== null ) {
325 | return node.getAttribute( "sorttable_customkey" );
326 | } else if ( typeof node.textContent !== "undefined" && !hasInputs ) {
327 | return node.textContent.replace( /^\s+|\s+$/g, '' );
328 | } else if ( typeof node.innerText !== "undefined" && !hasInputs ) {
329 | return node.innerText.replace( /^\s+|\s+$/g, '' );
330 | } else if ( typeof node.text !== "undefined" && !hasInputs ) {
331 | return node.text.replace( /^\s+|\s+$/g, '' );
332 | } else {
333 | switch ( node.nodeType ) {
334 | case 3:
335 | return ( node.nodeName.toLowerCase() === "input" ) ? node.value.replace( /^\s+|\s+$/g, '' ) : '';
336 | case 4:
337 | return node.nodeValue.replace( /^\s+|\s+$/g, '' );
338 | case 1:
339 | case 11:
340 | var innerText = '';
341 | for( var i = 0; i < node.childNodes.length; i++ ) {
342 | innerText += sorttable.getInnerText( node.childNodes[i] );
343 | }
344 | return innerText.replace( /^\s+|\s+$/g, '' );
345 | default:
346 | return '';
347 | }
348 | }
349 | },
350 | reverse: function ( tbody, sindex ) {
351 | // reverse the rows in a tbody
352 | var newrows = [];
353 | for( var i = sindex; i < tbody.rows.length; i++ ) {
354 | newrows[newrows.length] = tbody.rows[i];
355 | }
356 | for( var i = newrows.length - 1; i >= 0; i-- ) {
357 | tbody.appendChild( newrows[i] );
358 | }
359 | //delete newrows;
360 | },
361 | original_order: function ( tbody, isindex ) {
362 | // build an array to sort. This is a Schwartzian transform thing,
363 | // i.e., we "decorate" each row with the actual sort key,
364 | // sort based on the sort keys, and then put the rows back in order
365 | // which is a lot faster because you only do getInnerText once per row
366 | var row_array = [];
367 | var rows = tbody.rows;
368 | var sindex = isindex;
369 | for( var j = sindex; j < rows.length; j++ ) {
370 | row_array[row_array.length] = [rows[j].className, rows[j]];
371 | }
372 | /* If you want a stable sort, uncomment the following line */
373 | //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
374 | /* and comment out this one */
375 | row_array.sort( sorttable.sort_alpha );
376 |
377 | var tb = tbody;
378 | for( var j3 = 0; j3 < row_array.length; j3++ ) {
379 | tb.appendChild( row_array[j3][1] );
380 | }
381 |
382 | //delete row_array;
383 | },
384 | /* sort functions
385 | each sort function takes two parameters, a and b
386 | you are comparing a[0] and b[0] */
387 | sort_ipaddr: function ( a, b ) {
388 | var aa = a[0].split( ".", 4 );
389 | var bb = b[0].split( ".", 4 );
390 | var resulta = aa[0] * 0x1000000 + aa[1] * 0x10000 + aa[2] * 0x100 + aa[3] * 1;
391 | var resultb = bb[0] * 0x1000000 + bb[1] * 0x10000 + bb[2] * 0x100 + bb[3] * 1;
392 | return resulta - resultb;
393 | },
394 | sort_numeric: function ( a, b ) {
395 | if ( a[0] === "" ) {
396 | return -1;
397 | }
398 | if ( b[0] === "" ) {
399 | return 1;
400 | }
401 | var aa = parseFloat( a[0].replace( ",", "." ).replace( /[^0-9.\-]/g, "" ) );
402 | if ( isNaN( aa ) ) {
403 | aa = Number.NEGATIVE_INFINITY;
404 | }
405 | var bb = parseFloat( b[0].replace( ",", "." ).replace( /[^0-9.\-]/g, "" ) );
406 | if ( isNaN( bb ) ) {
407 | bb = Number.NEGATIVE_INFINITY;
408 | }
409 | return aa - bb;
410 | },
411 | sort_alpha: function ( a, b ) {
412 | return a[0].localeCompare( b[0] );
413 | },
414 | sort_date: function ( a, b ) {
415 | var aa = new Date( a[0] ), bb = new Date( b[0] );
416 | return ( aa > bb ) - ( aa < bb );
417 | },
418 | shaker_sort: function ( list, comp_func ) {
419 | // A stable sort function to allow multi-level sorting of data
420 | // see: http://en.wikipedia.org/wiki/Cocktail_sort
421 | // thanks to Joseph Nahmias
422 | var b = 0;
423 | var t = list.length - 1;
424 | var swap = true;
425 | var q;
426 |
427 | while( swap ) {
428 | swap = false;
429 | for( var i = b; i < t; ++i ) {
430 | if ( comp_func( list[i], list[i + 1] ) > 0 ) {
431 | q = list[i];
432 | list[i] = list[i + 1];
433 | list[i + 1] = q;
434 | swap = true;
435 | }
436 | } // for
437 | t--;
438 |
439 | if ( !swap ) {
440 | break;
441 | }
442 |
443 | for( var i = t; i > b; --i ) {
444 | if ( comp_func( list[i], list[i - 1] ) < 0 ) {
445 | q = list[i];
446 | list[i] = list[i - 1];
447 | list[i - 1] = q;
448 | swap = true;
449 | }
450 | } // for
451 | b++;
452 |
453 | } // while(swap)
454 | }
455 |
456 | };
457 |
458 | if ( typeof ( window.addEvent ) !== "undefined" ) {
459 | window.addEvent( window, "load", sorttable.init );
460 | } else {
461 | jQuery( function () {
462 | sorttable.init();
463 | } );
464 | }
465 |
--------------------------------------------------------------------------------
/syntax.php:
--------------------------------------------------------------------------------
1 | Lexer->addEntryPattern( '