Feb 2022  

  Documentation for  wsurvey.sortTable.js 

wsurvey.sortTable.js is javascript library used to easily enable user chosen sorting of an existing html table.
It is part of, and requires, the wsurvey library (and jQuery).

Features:
  
   * Automatically places "toggleable" sort-this-column buttons -- for all or for a selected set of columns -- in a chosen "header row".
     These buttons can be automatically highlighted when its column is sorted.
   * Sorting uses the value in a column -- but all rows are sorted.
   * Values can be the actual text value, or use an attribute of the cell's elements.
   * A selected set of columns can be "frozen" -- they won't ever be sorted (their original order is maintained)
   * Secondary, tertiary, etc. sorting supported. For example, a secondary sort maintain the order created by a primary sort,
     with additional sorting of primary-sort columns having the same value
   * Automatic background coloring of every other (or every third, fourth, etc) rows
   * An "external" header row -- with clickable buttons -- can be created. When placed outside of the table, this will
     NOT move even as the user scrolls down the rows of a long table
   * All cells in a column  can be shrunken, and unshrunken, by the user with a simple button click
   * Several extra function faciltate:
      o Marking of "groups" using the same border -- where a "group" is a set of contiguous cells with the same value.
      o Resizing of column widths -- on a per column basis.
      o Toggle highlighting of all cells in a sorted column
      o Toggle view of headers

wsurvey_sortTable_test.html (in the examples\ directory) contains a demo.

For an alternative, that converts an javascript "associative array" into a table -- and that uses <divs> (not a <table>)  -- see wsurvey.arrayToHtml.js

::: Setup :::

 1) Include in your html file something like:
       <script type="text/javascript" src="jquery-3.6.0.min.js"></script>
       <script type="text/javascript" src="wsurvey.utils1.js"></script>
       <script type="text/javascript" src="wsurvey.sortTable.js"></script>
 
 2)  Assume the table you want to make sortable has an id of "myTable".

     Call (say, from an onLoad function)
             wsurvey.sortTable.init('myTable')

 If you just want a basic sort, that's all you have to do.
 Each cell in the top row  will have a button that will sort on this column when clicked -- toggling between ascending and descending sort.

However there are a lot of options that can be specified -- including options that enable multi-level (primary and secondary sorts).

   These are readily enabled by calling:
        wsurvey.sortTable_init('myTable',options)
     where options is an associative array with a number of options

  Or,
     You can specify several options (over riding the options in the options argument) on a column specific basis by including
     attributes in the cells (the <td> or <th>) of the header row of the table (by default, the header row is the top row)

The options are described below!

::: Helpful hint: the data-_sortUse attribute :::

  What if your table cells contain HTML, or contain transformations of raw values that are not easily sorted?
  To ensure that sorting is done on appropriate values, you can include a data-_sortUse attribute, whose value is used for sorting
    This attribute can be included in a cells <td> (or <th>) element.
    If there is no such sortUse attribute in the <td> (or <th>), all of its children are searched for an element with this attribute.
    The first match (if any) is used. If none are found - the .text() value of the cell is used (which may yield odd results when the 
    cell has html, etc.)

    Actually, the following is looked for (in the <td>, or in it children) -- stopping after the first success.
    (in parenthesis is the default, if sortUse is not specified in the options argument).

      1) data-aws_sortTable_sortUse   ("data-_aws_sortTable__sortUse" -- note the double underscore)
      2) data-_sortUse                 ("data-_sortUse")
      3)  sortUse                      ('_sortUse")


::: Function list  :::

   wsurvey.sortTable.init : initialize an existing table -- add "sort buttons" to the top (header cell of a column)

   wsurvey.sortTable.status  : return an array of messages describing the sort status of the table
   wsurvey.sortTable.highlight  : highlight the header cells of sorted columns (using bgcolor)
   wsurvey.sortTable.rowColors(aid,rowColors)  :: setup and add a class that yields alternating row colors
   wsurvey.sortTable.recentSortInfo(athis,aid)  ::return a string of descriptive information on what this sort button
                                             (that was clicked) did (or would) do
   wsurvey.sortTable.copyHeaderRow(aid,targetId) ::   clone the header row to a container not in the table -- it will have active sort buttons
   wsurvey.sortTable.unShrinkAll(aid) :: unshrink shrunken columns
   wsurvey.sortTable.markGroups : mark "contiguous groups " (cells next to each other with same value) with the same cell border color
   wsurvey.sortTable.setColWidths   : set the column widths
   wsurvey.sortTable.attr  : read an attribute (checks for several variances)

   wsuvey.sortTable.sortCol : not typically used  -- it is what the "sort buttons" call.
          Maybe useful for advanced programmers who want to bypass wsurvey.sortTable.init.

::: Description of functions :::

::: :::   wsurvey.sortTable.init   ::: :::

 wsurvey.sortTable.init(tableid,options:

    tableId : string (it can start with a '#') of the id of the table to sort
             Or, a jquery object pointing to the table

   options: an associative array of options specify sorting options. All of them are optional (defaults are used if not specified).

  Several of these options can be overidden on a column specific basis; via attributes in the headerRow (typically the
  top row) td and th  cells. These are: sortNumeric, sortAsc, iconClass, ascIcon, desIcon, asc2Icon, des2Icon

 Valid options are (default in parenthesis). Note that the options are case insensitive.
   'ascIcon' ('&uarr;') :  The icon used in the column header ..   click on it for ascending sort
   'asc2Icon' ('') :   If specified, the icon used in the column header .. a  click on it for secondary ascending sort
   'desIcon' (&darr;) :  The icon used in the column header .. click on it for descending sort.
   'des2Icon' ('') :  If specified, the icon used in the column header .. click on it for descending secondary sort.

        If asc2Icon and des2Icon are NOT specified, secondary sorts buttons are not displayed (though this can be overridden
        on a column specific basis). In contrast, if   ascIcon and desIcon are not specified -- the default values are used.

        If asc2Icon and des2Icon equals "1", default values are used (&Uarr; and &Darr;  respectively)

   'endRow'  : (''): Ending row.  
                     startRow and endRow are modified so that 0 <=startRow <=endRow <=#rows
                     A value of 0, or '', means "sort all rows"
                     A value <0 means "do not sort this many rows from bottom".
                        Thus: -2 means "do not sort the final 2 rows"

   'freezeCols': ([]) :  An array of columns (0 for first column) to NOT move. The values in this columns will stay in their same row and column
                       of the table. freezeCols are also skipCols --- they won't have a sort button added.
                       If freezeCols is a scalar number, just that column is frozen.    

                       Notes
                         * The first column is 0
                         * These column numbers should refer to column position BEFORE   rowNumber column(s) are added
                           (see rowNumberClass and rowNumberSortableClass)!

   'headerRow' : (0)  Row to write the sort buttons to.
                      Caution: If not less than the startRow, the row with the sort buttons could move around.
                      Hint: to place sort buttons at bottom of table:
                              headerRow:1111111111  // (a large number)
                              endRow:-1             // do not sort last row of table

                      Note: the header row of the table will have a ws_sorttable_headerrow="headerRow" attribute added to its <tr>
                      (where the "headerrow" is the value of the headerRow option (0 if not specified)

   'iconCurrentClass' ('wsurvey_sortTable_iconCurrentClass') :   If not '', add (using .toggleClass) this class onto the currently 
                      selected "primary sort" button.
                      As new columns are chosen, the corresponding button is highlighed using this class (and the class is removed
                      from the previously highlighted "primary sort" column button).
                      If iconCurrentClass is NOT specified, wsurvey_sortTable_iconCurrenClass is used -- a class created by wsurvey.sortTable.init
                      that specifies a a light shadow and dotted blue border.
                      If iconCurrentClass='', then NO special class is used (there is no highlighting of selected columns).

   'icon2CurrentClass' ('wsurvey_sortTable_iconCurrent2Class') :   If not '', add this class to currently selected "secondary sort" buttons

   'iconClass':('')  : If not '', a CSS class to add to all the sort buttons
                      If iconClass is NOT specified, wsurvey_sortTable_iconClass is used -- a class created by wsurvey.sortTable.init  --
                      that specifies a small oval button with a blue font.
                      If iconClass='', then NO special class is used (the browser's default buttons are displayed).

   'rowNumberClass': ('')  : If not '', a CSS class to use in an added "row number" column.
                       If not specified, is '', or '0': a row number column is NOT added.
                       Otherwise, it is added as the left most column,

                       Notes:
                         * If rowNumberClass='1', wsurvey_sortTable_rowNumberClass (a hard coded class) is used.
                         * an added rowNumberClass column is "frozen" (it won't and can't be sorted).
                         * Each <td> in this column will have a <span>, using rowNumberClass as a class, displaying the row number (top row=0)

   'rowNumberSortableClass': ('')  : If not '', a CSS class to use in an added "sortable row number" column.
                       If not specified, is '', or '0': a row number column is NOT added.
                       Otherwise, it is added as the left most column, but after a rowNumberClass column.

                       Notes:
                         * If rowNumberSortableClass='1', wsurvey_sortTable_rowNumberSortableClass (a hard coded class) is used.
                         * an added rowNumberClass column is "sortable" -- it is meant to show where the original rows are after sorting
                            In Contrast: rowNumberClass is frozen -- rowNumberClass is supposed to be show where you are in the
                                         currently viewed table
                         * Each <td> in this column will have a <span>, using rowNumberSortableClass as a class, displaying the
                           original row number (top row=0)
                         * The rowNumberSortable column can be primary sorted. But, to save display space, it can NOT be a secondary sort.
                           And it can not be shrunken.


  'rowNumberSortableIcon' : (&Oscr;') : The icon used in the header cell of the rowNumberSortable column.
       This is ignored if rowNumberSortableClass='' or 0 (or if rowNumberSortableClass is not specified).
       If rowNumberSortableIcon=1, the default (&Oscr;#) is used. But any other value is used as is (including "" and "0")


   'preCall': ([])  : A array of function names (as strings). See below for details
   'postCall': ([])  : A array of function names (as strings). See below for details

   shrinkColIcon' ('')  :   If not '', and not '0' : add a " shink this column" icon, using  shrinkColIcon as the button value
                   If '1', '#9003;' is used (leftarrow with X in box) -- using the iconClass.
                   Clicking these buttons will shrink the cells in this column to about 12px.

                   After shrinking, clicking on the shrunken header cell will restore the width

                   Notes:
                      * "external header row" columns will not be shrunk
                      * problems might arise if there are other buttons (not the ones created by wsurvey.sortTable.init) in the header cell --
                        clicking on the cell might be interpreted as clicking on one of these other buttons

   'skipCols': ([]) :  An array of columns (0 for first column) to NOT add sort buttons to.
                    They will BE moved when some other column is sorted, but you won't be able to sort on this column's values.
                    If skipCols is a scalar number, just that column is skipped.     Note that the first column is 0

                    Note: these column numbers should refer to column position BEFORE a rowNumber (or rowNumberSortable) column is added!

   'sortAsc':  (1)  :  Sort direction to start. 0=descending, 1=ascending

   'sortUse' : ('_sortUse') : An attribute to look for in the <td> (or <th>),or in any element in a td or th. Use its value when sorting
               See the "hint" above for details. It is better practice to use '-data_sortUse' (but '_sortUse' will work).

   'startRow': (1)  : Starting row. All rows before are not sorted. The top row is row 0  (so 1 means "do not sort top row"
                     and 0 means "sort all rows)).
                    Warning: if you sort all rows (start at 0), the sort button moves around!
                             Or if you select a headerRow <= the startRow!
                    You can prevent this by appropriate choices of headerRow, startRow and endRow (such as by using the default).

   'sortNumeric' (-1) 0:alphabetic, 1: numeric, -1: numeric if possible (if a pair being evaluted both have numeric values).

 return 'message' if an error (i.e.; no tr in aid, or no th or td in first tr).
        'nCols ' if okay (nCols is number of buttons added. Could be zero if all cols are skipped)
        Use isNaN on return (or typeof()='string) to see if there was an error.

 Hints:
   *  You can specify a short description (no html) in a desc attribute in a columns <td> (or <th>).
      This is used by a few of the utility functions (such as  wsurvey.sortTable.copyHeaderRow and wsurvey.sortTable.status)
      If you have a complex header (with HTML or images), specifying a desc is highly recommened.

   * If you specify a sortUse in the <td> (or <th>) element, and in an element in the <td> -- the version in the <td> is used.
        Example: <td data-_sortUse="21"><span data-_sortUse="55" title="Age"><b>Age</b></span></td> --   21 is used when sorting

   * To have no seperate ascending and descending icons, but just a column that when clicked will do a sort 
     (toggling between ascending and descending)
       a) In the header row (by default, top row of the column), leave its <th> empty.
       b) Specify ascIcon and desIcon to be the same thing. 
         For example:
          Example: <th ascIcon="Size" desIcon="Size"></th>

  *  If you do NOT specify a sortUse, and the contents of a column's <td>'s have html -- the results may be unpredictable.
     (the .text() method of jQuery is used to extract a text value).

   * The secondary sorts (asc2Icon and des2Icon) are used to perform secondary sorts -- a second sort to reolve ties in
     the primary sort.
     If these are not specified, no secondary sort buttons are displayed.
     If clicked and there hasn't been a primary sort yet, its the same as clicking a primary sort button.
     Examples: options['asc2Icon']='&Uarr;'  options['des2Icon']='&Darr;' ;
     
     Actually, any number of "secondary" sorts (i.e; 3rd and 4th etcs) can be performed (by clicking columns in the 
     desired order: pimary first, seconday second, etc).

  * Clicking a primary sort button clears all prior sorts -- just that column is sorted on.
     Secondary sort buttons are cumulative -- so clicking on a secondary sort add this sort instruction after all existing sort instructions.
     Clicking a secondary sort button on column already sorted (either as primary or secondary) will move that column sort position to be last,
     regardless of whether or not it previously was a primary or secondary sort.
     That means the highest "secondary" sort become the primary sort!


preCall and postCall
   You specify one or more (or none) javascript functions to call BEFORE a column sort occurs, or afterwards.
   preCall and postCall should be an array of function names (as strings), or a single function name (as a string)

   Each function in preCall is called BEFORE a sort is attempted.
   Each function in postCall is called AFTER as sort is completed

   In both cases, the event that calle the sortd (i.e.; the button that was pushed) is supplied as an argument

   You can pull attributes out of this event using the wsurvey.sortTable.attr function.
   For example, if a preCall of "checkThis" is specified:

     function checkThis(evt) {
         let evt2=$(evt.target);
         let origCol=wsurvey.sortTable.attr(evt2,'origcolnumber');
         ...
      }

   origcolnumber is the original (before possible addition of a row number column) column number this button refers to.
   In contrast, 'colnumber' is the column in the currently displayed table
   These will be the same if   rowColClass='0'

   Hint: to get a bunch of table specific attributes (as set by wsurvey.sortTable.init)), use wsurvey.sortTable.status()

   These functions should return 1 or false.
     * For preCall : a return of false means "stop processing". Do not call any more preCall functions, and do NOT attempt sort
     * For postCall :  a return of false means "stop processing". Do not call any more postCall functions

 Example:
     postCall can be used to call the wsurvey.sortTable.rowColors function (to reshade rows)
     preCall can be used to confirm whether or not this column should be sorted

     The example (wsurvey_sortTableTest.html) contains a few examples of preCall and postCall functions.

* FOR ADVANCED PROGRAMMERS ONLY

     Instead of using wsurvey.sortTable.init -- you can hand code "sort buttons" into your table.
      -- You must specify  a call to wsurvey.sortTable.sortCol (i.e. onClick="wsurvey.sortTable.sortCol(this)"
      -- You must include name="ws_sortTable_button"
     --  You should specify several attributes. In particular: ascIcon, and desIcon.

   In addition, the <table> should have the following attributes set (if not, defaults are used).
   ws_sorttable_startrow="n" (default is 1) and ws_sorttable_endrow="n" (default is all rows in table
   And optionally:
     * ws_sorttable_freeze="k1,k2,.." (default is "") -- csv of "frozen" columns
     * ws_sorttable_precalls and and ws_sorttable_postCalls as: "" or "func1,func2,..."

   There are other attributes (in the <table> and in the buttons) that should be set. Look at the code,
   inspect the html produced by _init, and good luck.

::: :::  wsurvey.sortTable.status  ::: :::

  Return an array of messages describing the sort status of a table

  messageList=wsurvey.sortTable.status(tableId,asData)

  By default, asData=0.

  If asData!=1, messageList is a several element array. Each row contains a message about the "sorting status" of the table.
    You can create a list, or a table, or whatever from these messages.
    Example:
       [0]: "Sort rows 1 to 7"
       [1]: "Sorted columns (primary, secondary,..):
           <span style="margin:4px 3px 4px 6px;padding:5px;background-color:">3</span>
           <span style="margin:4px 3px 4px 6px;padding:5px;background-color:">2</span> "
       [2]: "Sorted order  (primary, secondary,..): ascending  | ascending "
       [3]: "Sorted type  (primary, secondary,..): Numeric | Automatic"
       [4]: "Frozen columns: 0"

   If header cells have been highlighted, [1] could be:
       [1]:: "Sorted columns (primary, secondary,..):
             <span style="margin:4px 3px 4px 6px;padding:5px;background-color:048efa">3</span>
             <span style="margin:4px 3px 4px 6px;padding:5px;background-color:cyan">2</span> "

  If asData==1, an associative array containing the above information is returned.
    Example.
      sortList:  array(1): {
          [0]:  number: 4
       }
       sortAscending:  array(1): {
          [0]:  number: 1
       }
       sortNumeric:  array(1): {
          [0]:  number: 1
       }
       colDescs:  array(7): {
          [0]:  string(1): "#"
          [1]:  string(0): ""
          [2]:  string(13): "This is Col 0"
          [3]:  string(4): "col1"
          [4]:  string(24): "Col2 for  more funYes!!!"
          [5]:  string(0): ""
          [6]:  string(23): "Col4
             origOrder"
       }
       colWidths:  array(7): {
          [0]:  number: 119.483
          [1]:  number: 119.483
          [2]:  number: 119.483
          [3]:  number: 119.483
          [4]:  number: 119.483
          [5]:  number: 119.483
          [6]:  number: 119.483
       }
       colScales:  array(7): {
          [0]:  number: 1
          [1]:  number: 1
          [2]:  number: 1
          [3]:  number: 1
          [4]:  number: 1
          [5]:  number: 1
          [6]:  number: 1
       }
       colWidthsPct:  array(7): {
          [0]:  number: 0.14285714285714288
          [1]:  number: 0.14285714285714288
          [2]:  number: 0.14285714285714288
          [3]:  number: 0.14285714285714288
          [4]:  number: 0.14285714285714288
          [5]:  number: 0.14285714285714288
          [6]:  number: 0.14285714285714288
       }
       origTableWidth:  number: 1004.536666

       freezeCols:  array(2): {
          [0]:  number: 0
          [1]:  number: 2
       }
       startRow:  number: 1
       endRow:  number: 12
       sortUse:  string(8): "_sortUse"


   If the table has not been sorted: sortList, sortAscending, and sortNumeric will be []

::: ::: wsurvey.sortTable.highlight ::: :::

  Highlight all the cells of sorted columns (using a bgcolor in the <td> or <th> of the header cell)

    wsurvey.sortTable.highlight(tableId,colorList)

  where colorlist is an array of colors.

  * If not specified, 3 colors ('048efa','cyan','04fab7') are used.

 *  if Colorlist =[],   unhiglight all highlighted cells.

 Note the use of bgcolor, rather than adding a class.  This overrides cell colors specifed via wsurvey.sortTable.rowColors!


::: :::  wsurvey.sortTable.rowColors  ::

 Setup and add a class that yields alternating row colors

    wsurvey.sortTable.rowColors(aid,rowColors)

 rowColors should be an array (or csv string) of colors to use in rows.
    [0] is used in the top row
    [1],..,[n] are used in repetitivel

    Example: rowColors=['gray','cyan','lime'];
      Top row (row 0) : bgColor="gray"
      Row 1 : background-color='cyan'
      Row 2 : background-color='lime';
      Row 3 : background-color='cyan'
      Row 4 : ....

   Note: a   tr:nth-child(2n+i) (i is 1+ index in rowColors) is used in the CSS style applied to the table.
         This can have odd impacts of there are more than 3 colors specified.

         If  wsurvey.sortTable.rowColors is called from a button inside of a table, the parent table will be found (assuming
         it was initialized with wsurvey.sortTable.init).  Thus, you can use something like this in a header row:
           <input type="button" value="RowColors" onClick="wsurvey.sortTable.rowColors(this)" title="shade table rows with alternating colors">

::: :::  wsurvey.sortTable.recentSortInfo  ::: :::

Returns a string containing a description of what the sort was (our would be) -- given a pointer to a sort button.
This is designed to be used as the backend of a postCall function.

   wsurvey.sortTable.recentSortInfo(athis,whereId)

   athis : the event (i.e.; as provided by the button click's call to wsurvey.sortTable.sortCol)
   whereId : optional. An id (string), or  jquery or dom object. The message string will be written there
   
   Example of a message string: 'Sort (Ascending /Numeric ) of column 2'

   Calling example (assuming that postCall=['myShowSortInfo'];
   function myShowSortInfo(evt) {
      wsurvey.sortTable.recentSortInfo(evt,'#showSortInfoSpan');
      return true;
   }


::: :::  wsurvey.sortTable.copyHeaderRow ::: ::: /

 Copy the header row to an "external header row". This should be a container in the table (i.e.; something that is always visible).

 The buttons will work -- and they will be updated if the original buttons are clicked!

 This allows one to scroll through a long table, while always being able to view the headers (say, in a container just above the table).

   wsurvey.sortTable.copyHeaderRow(aid,targetId,targetAddClass)

     aid: id (or jquery object) of the table
     targetId: where to place the "external header row".
     targetAddClass : class to add to the "one row table" added to targetId

 Notes:
   *  If targetId is not specified, or is false, a jquery object containing the html is returned (which can be copied to a container by the caller).
      25 March 2021: This has not been tested -- used with caution.
      
   *  The "desc" attribute in the <td> (or <th>) of the header row is used as the text of the cells in this always viewable row. This can
      be useful if the actual column header text is long, has html or images, or is otherwise complicated.
      
    * To assure that cells line up,  a new 1 row table is created; and is then added to the targetId.
      By default (if targetAddClass is not specified, or is false) the classes in aid are copied to this 1 row table.
      THIS CAN CAUSE PROBLEMS -- say, if you have a fixed height (to allow for scrolling) in the css atributes for the aid table.

      Specifying targetAddClass can resolve this.  For example, you could use mostly the same attributes in the targetAddClass
      class -- with the dangerous ones (such as height) removed.
      
      A short cut is to use these css styles...
        In the aid table
         display:block
      In the targetId container (to which the one row table is inserted)
         height: 3em
         overflow:hidden
      This shortcut is not highly recommended (i.e.; it can have funny impacts on placement of borders).


::: ::: wsurvey.sortTable.unShrinkAll ::: :::

  Unshrink shrunken columns (restore to original size).

  wsurvey.sortTable.unShrinkAll(aid)

     aid: id (or jquery object) of the table
     targetId: where to place the "external header row".

  If this aid is not a ws_sortTable initialized table, an error message string is returned.
  Otherwise, an integer value -- the number of columns unshrunken (0 if no columns were unshrunken)


 ::: :::  wsurvey.sortTable.sortCol_extractVals ::: :::

 Extract values from the current state of the table. Thus, if this is called after sorting, the values will be listed in the sorted order

     tableVals=wsurvey.sortTable.sortCol_extractVals(erows,useCols,startRowUse,endRow,sortUse,defVal) {


  where
     erows: the rows of the table.
             Example:   etable=$('#myTable');
                        erows=etable.find('tr');
     useCols : array of column numbers (within the table) to extract.
                   If [],  extract columns 0,1,..., # of cells in startRow.
                   Note: "cell" is synonymous with a  <td>..</td> or <th>...</th>
                   If a column number doesn't exist (in a give row), the defVal is used.
                   If defVal === false (the default), use the row number

                   Short cuts:
                       [] or '' : all columns in the startRow row. This may not be the same as in other rows!
                       n  : Integer value -- just column n
                      'i,j,k'  : CSV of column numbers

                   Note: see shortcut below on what happens if useCols is not specified!

     startRow : row (in erows) to start at. 0 is first row.
                 If not specified, or false, use the initially specified startRow (or 0 if not available)
     endRow :  row to end at (inclusive)
                If not specified, or false, use the initially specified endRow (or final row if not available)
               tableVals will start at [0] -- so tableVals[0] refers to values pulled from erows[startRow]
     sortUse : method of pulling values from the table
                '0' : pull the .text() of each table cell
                '1'  : pull the .html() of each table cell
           attributeName :  a) look for an attribute with this name in the <td> (or <th>) of the table cell.
                            b) if found, use its value
                            c) if not found, search for any element with this attribute (for example, an non-displayed span)
                               within this <td> (or <th>)
                            d) If any such elements are found, use the attribute value from the first one
                            e) If no such elements are found, use the .text() (same as '0')
           If sortUse not specified, false, or '': sortUse='_sortUse'
           
           Note: as described in the "Helpful Hint: the sortUse attribute" -- several variants of sortUse are searche for.

     defVal : value to use if a non existent column is refered to
               If not specified, or false, use the row number

     The matrix returned will be from [0] to [endRow-startRow-1). [0] contains values from erows[startRow],
     endRow-startRow-1 refers to values from erows[endRow]

  ShortCut:

     tableVals=wsurvey.sortTable.sortCol_extractVals(aid)

  where aid is an id (or a jquery object) pointing to a table that has been processed with wsurvey.sortTable.init
  If aid is not such a table, [] is returned

  This will
    * extract the rows (<tr>) from aid
    * the currently sorted columns,
    * between the intially specified start and end row
    * Use the sortUse attribute initially specified
    * use the "row number" as the attribute



::: :::  wsurvey.sortTable.markGroups ::: :::

Mark cells, or rows, in a group.

     wsurvey.sortTable.markGroups(aid,level,colors,borderThickness) {

   aid: id (or jquery object) of the table
   level: What sort secondary sort level. 0=primary, 1=first secondary, etc
   colors: array (or csv) of colors to used in marking (as border or background
   borderThickness :  thickness of border (in pixels) used to mark a group's cells

   This REQUIRES that the table be sorted (by a prior call to wsurvey.sortTable.sortCol)-- so that "same valued" cells
   (in a column) are adjacent.  These cells will be marked using the colors specified in colors -- with cycling back to the start
   if there are more different unique values (in the column) than rows in colors

   Marking is done by adding an "inset" border around each cell, and by changing the background color
   The border thickness is set  borderThickness -- as the pixel size of the border line.
   If not specifeid, or not a number, or < 1; 4 (px) is used.

   Note that when rows are shaded using wsurvey.sortTable.rowColors; or a column is shaded with one color using wsurvey.sortTable.highlight,
   these background colors will be overwritten. However, the border will not!

   Special values of colors :
   
   These values are converted to some built in palettes
     'none' : remove marking
     'mixed15': 15 variety of colors
     'gold10' :  10 gold shaded colors
     'pink10' :  10 pink shaded colors
     'greenPurple10' : 10 colors shading from green to purple
     'mixed10': 10 variety of colors
     'cyan5' : 5 cyans shaded colors
     'green5' : 5 green shaded colors
     'mixed5' : 5 variety colors

   If you use a numeric value (say, 6): then the first six colors of 'mixed15' are used.


::: ::: wsurvey.sortTable.setColWidths  ::: :::

  Resize all the columns in a wsurvey.sortTable.init initialized table.

    wsurvey.sortTable.setColWidths(aid,scalers)

     aid: id (or jquery object) of the table
     scalers: how much to scale each column by.

   Scalers is a scaling factor applied to the original cell widths. Thus, 1.0 means "resize to original width"

    scalers can have several formats

        * a number   : all column widths are scaked  by this amount -- newWidth= originalWidth * scalers
            Example: 1.2 = increase original widths by 20%
                     1.0  = reset to original width

        * a csv : the scalar value in the csv is used with the corresponding column
          Note that a ', ,'  means "1.0". If not enough values in the csv, 1.0 is used.
          Thus, columns 6 and above in the table will be reset to their original widths
          Example:  '0.5, 0.6, 1.0, , 2.0  ' --  col3 (with ', ,') is reset to its original width, as are colums > 4_

        * a sparse matrix: the indices are column numbers (starting at 0), the values are the scalars.
           If a column is not specified, a value of 1.0 is used
          Example: {0:0.5, 1:0.6,   4: 2.0}  (note that there is no need to specify  2:1, since the default is 1.0)

  Note: the current scale factors are returned by wsurvey.sortTable.status



Contact and legal

  Feb 2022
  Daniel Hellerstein
  danielh@crosslink.net
  http://www.wsurvey.org/distrib, or  https://github.com/dHellerstein 

    wsurvey.sortTable  is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    wsurvey.sortTable  is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    If you did not receive a copy of the GNU General Public License,
    see <http://www.gnu.org/licenses/>.

