summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-12-01 20:42:54 +0100
committerDaniel Baumann <daniel@debian.org>2024-12-01 22:18:37 +0100
commit5622eceb23cb593322e7c0494323fab8efdb288e (patch)
tree661058f3088debcd764dda2addd7f208669132aa
parentAdding sortablejs makefile. (diff)
downloaddokuwiki-plugins-extra-5622eceb23cb593322e7c0494323fab8efdb288e.tar.xz
dokuwiki-plugins-extra-5622eceb23cb593322e7c0494323fab8efdb288e.zip
Adding sortablejs version 2023-04-20 (ef9a13e).
Signed-off-by: Daniel Baumann <daniel@debian.org>
-rw-r--r--plugins/55/sortablejs/README.md12
-rw-r--r--plugins/55/sortablejs/plugin.info.txt7
-rw-r--r--plugins/55/sortablejs/screen.css11
-rw-r--r--plugins/55/sortablejs/script.js464
-rw-r--r--plugins/55/sortablejs/syntax.php157
5 files changed, 651 insertions, 0 deletions
diff --git a/plugins/55/sortablejs/README.md b/plugins/55/sortablejs/README.md
new file mode 100644
index 0000000..4a74654
--- /dev/null
+++ b/plugins/55/sortablejs/README.md
@@ -0,0 +1,12 @@
+sortablejs
+==========
+
+JavaScript-based (client-side) DokuWiki table sorting plugin. In semi-maintenance mode now - only bugfixes and feature requests via PRs are handled.
+
+DokuWiki homepage at https://www.dokuwiki.org/plugin:sortablejs
+
+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
+
+Cross-plugin compatibility is outside the scope of this repo.
+
+If reporting bugs, please provide a valid reproduction scenario for any non-trivial case.
diff --git a/plugins/55/sortablejs/plugin.info.txt b/plugins/55/sortablejs/plugin.info.txt
new file mode 100644
index 0000000..a154473
--- /dev/null
+++ b/plugins/55/sortablejs/plugin.info.txt
@@ -0,0 +1,7 @@
+base sortablejs
+author vaxquis
+email spamove@gmail.com
+date 2023-04-20
+name sortablejs
+desc Allow sorting tables by columns
+url https://github.com/FyiurAmron/sortablejs
diff --git a/plugins/55/sortablejs/screen.css b/plugins/55/sortablejs/screen.css
new file mode 100644
index 0000000..53289e7
--- /dev/null
+++ b/plugins/55/sortablejs/screen.css
@@ -0,0 +1,11 @@
+div.sortable thead tr.row0 th:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort):after {
+ content: " \25B4\25BE"
+}
+
+div.sortable thead tr.row0 th.sorttable_sorted:after {
+ content: " \25BE"
+}
+
+div.sortable thead tr.row0 th.sorttable_sorted_reverse:after {
+ content: " \25B4"
+}
diff --git a/plugins/55/sortablejs/script.js b/plugins/55/sortablejs/script.js
new file mode 100644
index 0000000..f9a85cb
--- /dev/null
+++ b/plugins/55/sortablejs/script.js
@@ -0,0 +1,464 @@
+/**
+ Based on code from http://www.kryogenix.org/code/browser/sorttable/ by Stuart Langridge
+ (distributed under the conditions of MIT licence from http://www.kryogenix.org/code/browser/licence.html).
+ Includes open-source contributions from other people
+ (see https://github.com/FyiurAmron/sortablejs/graphs/contributors for more details).
+ Maintainers:
+ 2007-2016 oiv (Otto Vainio at otto@valjakko.net)
+ 2016-? vaxquis AKA FyiurAmron (spamove@gmail.com)
+ */
+
+var stIsIE = /*@cc_on!@*/false;
+//var tableid = 0;
+
+var sorttable = {
+ reinit: function () {
+ arguments.callee.done = true;
+
+ if ( !document.createElement || !document.getElementsByTagName ) {
+ return;
+ }
+
+ var elems = document.getElementsByTagName( "table" );
+ var elem;
+ for( var i = 0; i < elems.length; i++ ) {
+ elem = elems[i];
+ if ( jQuery(elem).hasClass("sortable") ) {
+ sorttable.makeSortable( elem );
+ }
+ }
+ elems = document.getElementsByTagName( "div" );
+ for( var i = 0; i < elems.length; i++ ) {
+ elem = elems[i];
+ if ( jQuery(elem).hasClass("sortable") ) {
+ sorttable.makeSortableDiv( elem );
+ }
+ }
+ },
+ init: function () {
+ if ( arguments.callee.done ) {
+ return;
+ }
+ sorttable.reinit();
+ },
+ makeSortableDiv: function ( div ) {
+ var childTables = div.getElementsByTagName( "table" );
+ var elem;
+ for( var i = 0; i < childTables.length; i++ ) {
+ elem = childTables[i];
+ var colid = div.className;
+ var patt1 = /\bcol_\d+_[a-z]+/gi;
+ var overs = [];
+ if ( colid.search( patt1 ) !== -1 ) {
+ var overrides = colid.match( patt1 );
+ for( var i = 0; i < overrides.length; i++ ) {
+ var entry = overrides[i];
+ if ( entry !== "" ) {
+ try {
+ var tmp = entry.split( "_" );
+ var ind = tmp[1];
+ var val = tmp[2];
+ overs[ind] = val;
+ } catch( e ) {
+ }
+ }
+ }
+ colid = colid.replace( patt1, '' );
+ }
+ var patt2 = /\bsortbottom_?\d?/gi;
+ var bottoms = 0;
+ if ( colid.search( patt2 ) !== -1 ) {
+ var bs = colid.match( patt2 );
+ try {
+ var tmp = bs[0].split( "_" );
+ //var ind = tmp[1];
+ var val = 1;
+ if ( tmp.length > 1 ) {
+ val = tmp[1];
+ }
+ bottoms = val;
+ } catch( e ) {
+ }
+ }
+ var patt2ph = /\bthreephase/gi;
+ var ph2 = true;
+ if ( colid.search( patt2ph ) !== -1 ) {
+ ph2 = false;
+ }
+
+ sorttable.makeSortable( elem, overs, bottoms, ph2 );
+ var pattdefault = /\bsortr?\d\d?/gi;
+ if ( colid.search( pattdefault ) !== -1 ) {
+ var mi = colid.match( pattdefault );
+ colid = mi[0].replace( 'sort', '' );
+ if ( colid !== '' ) {
+ colid = colid.trim();
+ }
+ var revs = false;
+ if ( colid.search( /\br/ ) !== -1 ) {
+ revs = true;
+ colid = colid.replace( 'r', '' );
+ }
+ sorttable.defaultSort( elem, colid, revs );
+ }
+ }
+ },
+ defaultSort: function ( table, colid, revs ) {
+ var havetHead = table.tHead;
+ var sindex = 1;
+ if ( havetHead ) {
+ sindex = 0;
+ }
+ var theadrow = table.rows[0].cells;
+ colid--;
+ var colname = "col" + colid;
+ // remove sorttable_sorted classes
+ var thiscell = false;
+ for( var i = 0; i < theadrow.length; i++ ) {
+ var cell = theadrow[i];
+ var colclass = cell.className;
+ var classname = colclass.split( " " );
+ if ( classname[0] === colname ) {
+ thiscell = cell;
+ }
+ }
+ if ( thiscell === false ) {
+ return;
+ }
+ // build an array to sort. This is a Schwartzian transform thing,
+ // i.e., we "decorate" each row with the actual sort key,
+ // sort based on the sort keys, and then put the rows back in order
+ // which is a lot faster because you only do getInnerText once per row
+ var row_array = [];
+ var col = thiscell.sorttable_columnindex;
+ var rows = thiscell.sorttable_tbody.rows;
+ for( var j = sindex; j < rows.length; j++ ) {
+ row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]];
+ }
+ /* If you want a stable sort, uncomment the following line */
+ //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
+ /* and comment out this one */
+ row_array.sort( thiscell.sorttable_sortfunction );
+
+ var tb = thiscell.sorttable_tbody;
+ for( var jj = 0; jj < row_array.length; jj++ ) {
+ tb.appendChild( row_array[jj][1] );
+ }
+
+ if ( revs ) {
+ sorttable.reverse( thiscell.sorttable_tbody, sindex );
+ jQuery(thiscell).addClass( "sorttable_sorted_reverse" );
+ } else {
+ jQuery(thiscell).addClass( "sorttable_sorted" );
+ }
+ },
+ makeSortable: function ( table, overrides, bottoms, ph2 ) {
+ // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
+ // "total" rows, for example). This is B&R, since what you're supposed
+ // to do is put them in a tfoot. So, if there are sortbottom rows,
+ // for backwards compatibility, move them to tfoot (creating it if needed).
+
+ var sortbottomrows = [];
+ if ( bottoms > 0 ) {
+ var frombottom = table.rows.length - bottoms;
+ for( var i = table.rows.length - 1; i >= frombottom; i-- ) {
+ sortbottomrows[sortbottomrows.length] = table.rows[i];
+ }
+ if ( sortbottomrows ) {
+ var tfo;
+ if ( table.tFoot === null ) {
+ // table doesn't have a tfoot. Create one.
+ tfo = document.createElement( 'tfoot' );
+ table.appendChild( tfo );
+ }
+ for( var ii = sortbottomrows.length - 1; ii >= 0; ii-- ) {
+ tfo.appendChild( sortbottomrows[ii] );
+ }
+ //delete sortbottomrows;
+ }
+ }
+ // work through each column and calculate its type
+ var havetHead = table.tHead;
+ var sindex = 1;
+ if ( havetHead ) {
+ sindex = 0;
+ }
+ var headrow = table.rows[0].cells;
+
+ for( var i = 0; i < headrow.length; i++ ) {
+ // manually override the type with a sorttable_type attribute
+ var colOptions = "";
+ if ( overrides[i + 1] )
+ {
+ colOptions = overrides[i + 1];
+ }
+ if ( colOptions.match( /\bnosort\b/ ) ) {
+ jQuery(headrow[i]).addClass("sorttable_nosort");
+ } else { // skip this col
+ var mtch = colOptions.match( /\b[a-z0-9]+\b/ );
+ var override;
+ if ( mtch ) {
+ override = mtch[0];
+ }
+ if ( mtch && typeof sorttable["sort_" + override] === 'function' ) {
+ headrow[i].sorttable_sortfunction = sorttable["sort_" + override];
+ } else {
+ headrow[i].sorttable_sortfunction = sorttable.guessType( table, i );
+ }
+
+ // make it clickable to sort
+ headrow[i].sorttable_columnindex = i;
+ headrow[i].sorttable_tbody = table.tBodies[0];
+ headrow[i].sindex = sindex;
+
+ jQuery( headrow[i] ).click( function () {
+ var theadrow = this.parentNode;
+ var jqt = jQuery( this );
+ if ( jqt.hasClass( "sorttable_sorted" ) ) {
+ // if we're already sorted by this column, just reverse the table
+ sorttable.reverse( this.sorttable_tbody, this.sindex );
+ jqt.removeClass( "sorttable_sorted" );
+ jqt.addClass( "sorttable_sorted_reverse" );
+ return;
+ }
+ if ( jqt.hasClass( "sorttable_sorted_reverse" ) ) {
+ if ( !ph2 ) {
+ sorttable.original_order( this.sorttable_tbody, this.sindex );
+ var list = theadrow.childNodes;
+ for( var i = 0; i < list.length; i++ ) {
+ var cell = list[i];
+ if ( cell.nodeType === 1 ) { // an element
+ var cc = jQuery( cell );
+ cc.removeClass( "sorttable_sorted" );
+ cc.removeClass( "sorttable_sorted_reverse" );
+ }
+ }
+ return;
+ } else {
+ // if we're already sorted by this column in reverse, just re-reverse the table
+ sorttable.reverse( this.sorttable_tbody, this.sindex );
+ jqt.removeClass( "sorttable_sorted_reverse" );
+ jqt.addClass( "sorttable_sorted" );
+ return;
+ }
+ }
+
+ // remove sorttable_sorted classes
+ var list = theadrow.childNodes;
+ for( var i = 0; i < list.length; i++ ) {
+ var cell = list[i];
+ if ( cell.nodeType === 1 ) { // an element
+ var cc = jQuery( cell );
+ cc.removeClass( "sorttable_sorted" );
+ cc.removeClass( "sorttable_sorted_reverse" );
+ }
+ }
+ jqt.addClass( "sorttable_sorted" );
+
+ // build an array to sort. This is a Schwartzian transform thing,
+ // i.e., we "decorate" each row with the actual sort key,
+ // sort based on the sort keys, and then put the rows back in order
+ // which is a lot faster because you only do getInnerText once per row
+ var row_array = [];
+ var col = this.sorttable_columnindex;
+ var rows = this.sorttable_tbody.rows;
+ sindex = this.sindex;
+ for( var j = sindex; j < rows.length; j++ ) {
+ row_array[row_array.length] = [sorttable.getInnerText( rows[j].cells[col] ), rows[j]];
+ }
+ /* If you want a stable sort, uncomment the following line */
+ //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
+ /* and comment out this one */
+ row_array.sort( this.sorttable_sortfunction );
+
+ var tb = this.sorttable_tbody;
+ for( var j3 = 0; j3 < row_array.length; j3++ ) {
+ tb.appendChild( row_array[j3][1] );
+ }
+
+ //delete row_array;
+ } );
+ }
+ }
+ },
+ guessType: function ( table, column ) {
+ // guess the type of a column based on its first non-blank row
+ var textCnt = 0;
+ var numCnt = 0;
+ var dateCnt = 0;
+ var ipCnt = 0;
+
+ for( var i = 0; i < table.tBodies[0].rows.length; i++ ) {
+ var text = sorttable.getInnerText( table.tBodies[0].rows[i].cells[column] );
+ if ( text !== "" ) {
+ 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])$/ ) ) {
+ ipCnt++;
+ } else if ( text.match( /^[\-\+]?.?\d*[\d,.]?\d+.?$/ ) ) {
+ numCnt++;
+ } else if ( !isNaN( new Date( text ).getTime() ) ) {
+ dateCnt++;
+ } else { // not a date (nor IP nor number)
+ textCnt++;
+ }
+ }
+ }
+ if ( textCnt > numCnt && textCnt > ipCnt && textCnt > dateCnt )
+ return sorttable.sort_alpha;
+ if ( numCnt > ipCnt && numCnt > dateCnt )
+ return sorttable.sort_numeric;
+ if ( ipCnt > dateCnt )
+ return sorttable.sort_ipaddr;
+ return sorttable.sort_date;
+ },
+ getInnerText: function ( node ) {
+ // gets the text we want to use for sorting for a cell.
+ // strips leading and trailing whitespace.
+ // this is *not* a generic getInnerText function; it's special to sorttable.
+ // for example, you can override the cell text with a customkey attribute.
+ // it also gets .value for <input> fields.
+ if ( !node ) {
+ return '';
+ }
+ var hasInputs = ( typeof node.getElementsByTagName === "function" ) &&
+ node.getElementsByTagName( "input" ).length;
+ if ( node.getAttribute( "sorttable_customkey" ) !== null ) {
+ return node.getAttribute( "sorttable_customkey" );
+ } else if ( typeof node.textContent !== "undefined" && !hasInputs ) {
+ return node.textContent.replace( /^\s+|\s+$/g, '' );
+ } else if ( typeof node.innerText !== "undefined" && !hasInputs ) {
+ return node.innerText.replace( /^\s+|\s+$/g, '' );
+ } else if ( typeof node.text !== "undefined" && !hasInputs ) {
+ return node.text.replace( /^\s+|\s+$/g, '' );
+ } else {
+ switch ( node.nodeType ) {
+ case 3:
+ return ( node.nodeName.toLowerCase() === "input" ) ? node.value.replace( /^\s+|\s+$/g, '' ) : '';
+ case 4:
+ return node.nodeValue.replace( /^\s+|\s+$/g, '' );
+ case 1:
+ case 11:
+ var innerText = '';
+ for( var i = 0; i < node.childNodes.length; i++ ) {
+ innerText += sorttable.getInnerText( node.childNodes[i] );
+ }
+ return innerText.replace( /^\s+|\s+$/g, '' );
+ default:
+ return '';
+ }
+ }
+ },
+ reverse: function ( tbody, sindex ) {
+ // reverse the rows in a tbody
+ var newrows = [];
+ for( var i = sindex; i < tbody.rows.length; i++ ) {
+ newrows[newrows.length] = tbody.rows[i];
+ }
+ for( var i = newrows.length - 1; i >= 0; i-- ) {
+ tbody.appendChild( newrows[i] );
+ }
+ //delete newrows;
+ },
+ original_order: function ( tbody, isindex ) {
+ // build an array to sort. This is a Schwartzian transform thing,
+ // i.e., we "decorate" each row with the actual sort key,
+ // sort based on the sort keys, and then put the rows back in order
+ // which is a lot faster because you only do getInnerText once per row
+ var row_array = [];
+ var rows = tbody.rows;
+ var sindex = isindex;
+ for( var j = sindex; j < rows.length; j++ ) {
+ row_array[row_array.length] = [rows[j].className, rows[j]];
+ }
+ /* If you want a stable sort, uncomment the following line */
+ //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
+ /* and comment out this one */
+ row_array.sort( sorttable.sort_alpha );
+
+ var tb = tbody;
+ for( var j3 = 0; j3 < row_array.length; j3++ ) {
+ tb.appendChild( row_array[j3][1] );
+ }
+
+ //delete row_array;
+ },
+ /* sort functions
+ each sort function takes two parameters, a and b
+ you are comparing a[0] and b[0] */
+ sort_ipaddr: function ( a, b ) {
+ var aa = a[0].split( ".", 4 );
+ var bb = b[0].split( ".", 4 );
+ var resulta = aa[0] * 0x1000000 + aa[1] * 0x10000 + aa[2] * 0x100 + aa[3] * 1;
+ var resultb = bb[0] * 0x1000000 + bb[1] * 0x10000 + bb[2] * 0x100 + bb[3] * 1;
+ return resulta - resultb;
+ },
+ sort_numeric: function ( a, b ) {
+ if ( a[0] === "" ) {
+ return -1;
+ }
+ if ( b[0] === "" ) {
+ return 1;
+ }
+ var aa = parseFloat( a[0].replace( ",", "." ).replace( /[^0-9.\-]/g, "" ) );
+ if ( isNaN( aa ) ) {
+ aa = Number.NEGATIVE_INFINITY;
+ }
+ var bb = parseFloat( b[0].replace( ",", "." ).replace( /[^0-9.\-]/g, "" ) );
+ if ( isNaN( bb ) ) {
+ bb = Number.NEGATIVE_INFINITY;
+ }
+ return aa - bb;
+ },
+ sort_alpha: function ( a, b ) {
+ return a[0].localeCompare( b[0] );
+ },
+ sort_date: function ( a, b ) {
+ var aa = new Date( a[0] ), bb = new Date( b[0] );
+ return ( aa > bb ) - ( aa < bb );
+ },
+ shaker_sort: function ( list, comp_func ) {
+ // A stable sort function to allow multi-level sorting of data
+ // see: http://en.wikipedia.org/wiki/Cocktail_sort
+ // thanks to Joseph Nahmias
+ var b = 0;
+ var t = list.length - 1;
+ var swap = true;
+ var q;
+
+ while( swap ) {
+ swap = false;
+ for( var i = b; i < t; ++i ) {
+ if ( comp_func( list[i], list[i + 1] ) > 0 ) {
+ q = list[i];
+ list[i] = list[i + 1];
+ list[i + 1] = q;
+ swap = true;
+ }
+ } // for
+ t--;
+
+ if ( !swap ) {
+ break;
+ }
+
+ for( var i = t; i > b; --i ) {
+ if ( comp_func( list[i], list[i - 1] ) < 0 ) {
+ q = list[i];
+ list[i] = list[i - 1];
+ list[i - 1] = q;
+ swap = true;
+ }
+ } // for
+ b++;
+
+ } // while(swap)
+ }
+
+};
+
+if ( typeof ( window.addEvent ) !== "undefined" ) {
+ window.addEvent( window, "load", sorttable.init );
+} else {
+ jQuery( function () {
+ sorttable.init();
+ } );
+}
diff --git a/plugins/55/sortablejs/syntax.php b/plugins/55/sortablejs/syntax.php
new file mode 100644
index 0000000..145c925
--- /dev/null
+++ b/plugins/55/sortablejs/syntax.php
@@ -0,0 +1,157 @@
+<?php
+/**
+ Based on code from http://www.kryogenix.org/code/browser/sorttable/ by Stuart Langridge
+ (distributed under the conditions of MIT licence from http://www.kryogenix.org/code/browser/licence.html).
+ Includes open-source contributions from other people
+ (see https://github.com/FyiurAmron/sortablejs/graphs/contributors for more details).
+ Maintainers:
+ 2007-2016 oiv (Otto Vainio at otto@valjakko.net)
+ 2016-? vaxquis AKA FyiurAmron (spamove@gmail.com)
+ */
+
+// must be run within Dokuwiki
+if ( !defined( 'DOKU_INC' ) )
+ die();
+//
+class syntax_plugin_sortablejs extends DokuWiki_Syntax_Plugin {
+
+ function getType() {
+ return 'container';
+ }
+
+ function getPType() {
+ return 'block';
+ }
+
+ function getSort() {
+ return 371;
+ }
+
+ function getAllowedTypes() {
+ return array( 'container', 'formatting', 'substition' );
+ }
+
+ function connectTo( $mode ) {
+ $this->Lexer->addEntryPattern( '<sortable[^>]*>(?=.*?</sortable>)', $mode, 'plugin_sortablejs' );
+ }
+
+ function postConnect() {
+ $this->Lexer->addExitPattern( '</sortable>', 'plugin_sortablejs' );
+ }
+
+ function handle( $match, $state, $pos, Doku_Handler $handler ) {
+
+ switch ( $state ) {
+ case DOKU_LEXER_ENTER :
+ $match = substr( $match, 9, -1 );
+ $match = trim( $match );
+ $scl = "";
+ if ( strlen( $match ) > 0 ) {
+ $scl = $this->__validateOptions( $match );
+ }
+ return array( $state, $scl );
+ case DOKU_LEXER_UNMATCHED :
+ return array( $state, $match );
+ case DOKU_LEXER_EXIT :
+ return array( $state, "" );
+ }
+ return array();
+ }
+
+ function render( $mode, Doku_Renderer $renderer, $data ) {
+ list($state, $match) = $data;
+ if ( $mode == 'xhtml' ) {
+ switch ( $state ) {
+ case DOKU_LEXER_ENTER :
+ $renderer->doc .= "<div class=\"sortable$match\">";
+ break;
+ case DOKU_LEXER_UNMATCHED :
+ $instructions = p_get_instructions( $match );
+ foreach( $instructions as $instruction ) {
+ call_user_func_array( array( &$renderer, $instruction[0] ), $instruction[1] );
+ }
+
+ break;
+ case DOKU_LEXER_EXIT :
+ $renderer->doc .= "</div>";
+ break;
+ }
+ return true;
+ } else if ( $mode == 'odt' ) {
+ switch ( $state ) {
+ case DOKU_LEXER_ENTER :
+ // In ODT, tables must not be inside a paragraph. Make sure we
+ // closed any opened paragraph
+ $renderer->p_close();
+ break;
+ case DOKU_LEXER_UNMATCHED :
+ $instructions = array_slice( p_get_instructions( $match ), 1, -1 );
+ foreach( $instructions as $instruction ) {
+ call_user_func_array( array( &$renderer, $instruction[0] ), $instruction[1] );
+ }
+ break;
+ case DOKU_LEXER_EXIT :
+ //$renderer->p_open();
+ // DO NOT re-open the paragraph, it would cause an error if the table is the last content on a page
+ break;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ function __validateOptions( $opts ) {
+ if ( empty( $opts ) ) {
+ return "";
+ }
+ $ret = "";
+ $oa = explode( " ", $opts );
+ foreach( $oa as $opt ) {
+ $explodedOption = explode( "=", $opt );
+ $c = $explodedOption[0];
+ $v = $explodedOption[1] ?? null;
+ if ( $c == "sumrow" ) {
+ $c = $v;
+ $v = "sumrow";
+ if ( $c == "" ) {
+ $c = "1";
+ }
+ } else if ( $c == "3phase" ) {
+ $v = $c;
+ $c = "";
+ }
+ if ( $v != null ) {
+ $cmpr = $v;
+ } else {
+ if ( preg_match( '/r?\d*/', $c, $matches ) ) {
+ $cmpr = 'sort';
+ }
+ }
+ switch ( $cmpr ) {
+ case '3phase':
+ $ret .= " threephase";
+ break;
+ case 'nosort':
+ $ret .= " col_".$c."_nosort";
+ break;
+ case 'numeric':
+ $ret .= " col_".$c."_numeric";
+ break;
+ case 'date':
+ $ret .= " col_".$c."_date";
+ break;
+ case 'alpha':
+ case 'text':
+ $ret .= " col_".$c."_alpha";
+ break;
+ case 'sort':
+ $ret .= ' sort'.$opt;
+ break;
+ case 'sumrow':
+ $ret .= ' sortbottom_'.$c;
+ break;
+ }
+ }
+ return $ret;
+ }
+}