3 of 3 found this page helpful

TsTable: Sortable, Paginated, and Zebrified Tables + flexible table generator

    Please discuss this template in this forum discussion.

    Table of Contents

    Introduction

    This template creates sortable, filterable tables using the jQuery "Tablesorter" extension plus a whole pile of additional Dekiscript and Javascript.  Key to this template are:

    1. Ease of use: while there are many options, all have intelligent defaults and enable the template to be used very simply if desired.  Extensive error checking is provided to eliminate as many debugging black holes as possible
    2. Flexibility: this template has an almost comical level of flexibility and user-customizability.  Fortunately, you don't need to use it all all the time, but it's there when you want it.
    3. Table generator: in addition to converting existing tables to sortable tables, this template includes a rather powerful table generator which lets you spit out tables from Dekiscript data structures without need to muck about with any HTML

    In general, this template has different virtues from BlakeH's version using Datatables.  I encourage you to look at both.  As for me, I use this all the time on my wiki, and hope it works for you.

    This template requires Lyons (9.02.x).

    History

    Version Date Author Description
    1.24 11/22/2010 neilw Added "nodata" option
    1.23 8/13/2010 neilw Cleaned up the stylesheet a bit to remove redundant and inefficient selectors
    1.22 7/26/2010 neilw Updated sort:false operation; now applies table styling when sorting is disabled.  Still no filter or pager for the moment.
    1.21 7/19/2010 neilw Updated tablesorter script to properly recognize "0" as a number.  The tablesorter javascript file has been renamed to tstable.tablesorter.min.js, and the URL in the template has been updated accordingly.
    1.20 7/14/2010 neilw
    • Added global "sort" option (OPTIONS)
    • Fleshed out "sorter" field (COLUMNS)
    1.18 2/23/2010 neilw Added UNSAFECONTENT permissions self-check
    1.17 2/12/2010 neilw Improved table header HTML for improved reliability
    1.16 10/16/2009 neilw One more CSS tweak to make IE7 happy.  Good grief!
    1.15 10/12/2009 neilw Cleaned up CSS and table header spacing to work more consistently in all browsers (esp. IE)
    1.14 10/9/2009 neilw Fixed a bug in 1.13 (sigh)
    1.13 10/8/2009 neilw Put in a spacer for blank cells, to improve display on some browsers
    1.12 09/25/2009 neilw Added support for data as list of lists to enable CSV example
    1.11 08/25/2009 neilw Fixed bug with pager and fewer than 10 rows in table
    1.10 07/29/2009 neilw Added pager support (doesn't yet work with filters)
    1.00 07/27/2009 neilw First published version

    How do I install it?

    NOTE: You must have UNSAFECONTENT permissions to install this template successfully.

    These instructions will help you install the template'TSTable' on your wiki quickly and correctly.

    Perform the following steps:

    1. Copy the template source code:
      1. If the code in the "HTML Source" area below is not already visible and selected, click the "View" button to display and select it.
      2. Copy it to your clipboard (CTRL-C).
    2. Create a new template on your wiki:
      1. Select the Tools->Templates menu item on your wiki
      2. Click "New Page" button. This will open the editor on a new page with the title "Template:Page Title".
      3. Replace "Page Title" with the name of the template.
    3. Paste in the source code:
      1. In the editor, click the "View->Source" menu item to view the HTML source of the page.
      2. Select all text (CTRL-A).
      3. Paste in the template source code from the clipboard (CTRL-V).
    4. Click "Save" button.

    HTML Source for Template:TSTable

    Additional Installation

    1. Install the Javascript and image files (attached to this page at the bottom) somewhere your wiki can access them, and set the appropriate pointer variables at the top of the script.  If you want, you can simply leave the script as-is, pointing to the files in the location on this page, but there is no guarantee that they will always stay in this location.  Note: these are customized versions of the tablesorter and pager scripts.
    2. If desired, customize the default color scheme in the Template script.

    A quick note about the examples on this page

    For all the examples on this page, the code is shown before the working example.  The code is shown with the syntax extension, and looks like this:

    TSTable()

    This means that the actual code on the page should be enclosed in a Dekiscript block.  If you want to copy the code from this page, then use the same procedure as described in steps 2-4 above.

    How do I use it?

    Let's get this out of the way right now: this template is a bit of a beast.  It has a zillion options, some of which are complex, and can take some time to figure out.  However, I have tried very hard to adhere to the following principle:

    Simple things should be simple, and complex things should be possible

    This means that, if I've done my job correctly, you'll be able to get up and running with it quickly and easily, and can ease your way into the more esoteric stuff at your own pace.  So don't be put off by the long and gruesome argument specifications; that's needed as reference but you don't need to try to absorb it all at once.  Peruse it briefly, and then head down to the tutorial to step through the functionality.

    A few quick examples

    Before we delve into the gory details, let's just have a few quick examples to show what kinds of things we can do, and how simple it is to do simple things.

    First, we have "apply" mode.  This means we have an existing table on the page (maybe hand-edited, maybe generated by another template), and want to make it sortable.  Just call TSTable() right before the table in question:

    TSTable()
    text numbers dates
    this  9 July 22, 2009
    is  -1  August 6, 2009
     text 11  April 1, 2010

     

    which then becomes:

    text numbers dates
    this  9 July 22, 2009
    is  -1  August 6, 2009
     text 11  April 1, 2010

     

    Clicking in the header of each column sorts that column, just like you'd expect.  Click again and the sort order is reversed.  Hold shift and click multiple columns to sort on multiple columns.  Also note that the sorter correctly handles numbers in the middle column, and dates in the third column (data format support is limited, more on that later).  It also cleans up leading space from some of the entries.

    Next, let's try a very simple example of "generate" mode, where we'll create a sortable table from data we'll supply to the template, in this case a wiki data structure.   While we're at it, we'll enable the pager:

    TSTable {
        options: { pager:true },
        columns: ["Title", "Viewcount"],
        data: wiki.getpage("en/docs/DekiScript/Reference").subpages
    }

    This produces:

    TitleViewcount
    DekiScript Syntax178
    DekiScript Operators124
    Wiki Functions and Variables506
    DekiScript Remote Function Calls122
    DekiScript Statements156
    DekiScript Literals211
    HTML Page Composition Model170
    DekiScript HTML/XML Statements240
    DekiScript Functions and Variables543
    JavaScript Events & Messages (JEM)590
    XPath97

    Finally, a more fancy table generation, using more options and a filter: 

    TSTable {
        options: { zebra:true, width:"100%", rowstyle:"(#$.comments ? 'font-weight:bold' : '')" },
        columns: [
            { key:"Title", width:"20%" },
            { key:"Path", sorter:false },
            { key:"ViewCount", width:"10%", initial:1 },
            { key:"(#$.comments)", title:"#Comments", width:10% },
            { key:"(var c=$.comments; #c ? c[#c-1].author.name .. ' (' .. date.format(c[#c-1].date,'MM/dd/yyyy') .. ')' : '')",
                title:"Last comment", width:"15%" }
        ],
        data: wiki.getpage("en/docs/DekiScript/Reference").subpages,
        filter: [ [ "all items", "1" ], [ "items with comments", "#$.comments>0" ], [ "items without comments", "#$.comments==0" ] ]
    }

    Becomes:

    Show:

    TitlePathViewCount#CommentsLast comment
    DekiScript Syntaxen/docs/DekiScript/Reference/DekiScript_Syntax1780 
    DekiScript Operatorsen/docs/DekiScript/Reference/DekiScript_Operators1242klumsy (06/11/2010)
    Wiki Functions and Variablesen/docs/DekiScript/Reference/Wiki_Functions_and_Variables5062SteveB (04/28/2010)
    DekiScript Remote Function Callsen/docs/DekiScript/Reference/DekiScript_Remote_Function_Calls1220 
    DekiScript Statementsen/docs/DekiScript/Reference/DekiScript_Statements1560 
    DekiScript Literalsen/docs/DekiScript/Reference/DekiScript_Literals2110 
    HTML Page Composition Modelen/docs/DekiScript/Reference/HTML_Page_Composition_Model1700 
    DekiScript HTML/XML Statementsen/docs/DekiScript/Reference/DekiScript_HTML//XML_Statements2403wovenlander (09/12/2010)
    DekiScript Functions and Variablesen/docs/DekiScript/Reference/DekiScript_Functions_and_Variables5430 
    JavaScript Events & Messages (JEM)en/docs/DekiScript/Reference/JEM5902SteveB (10/19/2009)
    XPathen/docs/DekiScript/Reference/XPath971muddyboots (04/29/2010)

    So that's what we can do.  If this looks interesting to you, then read on.

    Arguments

    TSTable takes four optional arguments, but each one is a world unto itself, and will be described separately below.

    Name
    Type
    Default
    Description
    options map? various defaults; see docs This argument specifies options that apply to the entire table. See below for more details.
    columns list? use "apply" mode (see docs) This argument specifies the characteristics of the table columns.  It only applies to "Generate mode" (see docs below).  Each list element may be either a map, allowing for more complex specification, or a string, for quick and dirty tables.  Examples of each form are provided below.
    data

    list of maps?

    or

    map of maps?

    or

    list of lists?

    use "apply" mode (see docs)

    Data to use when generating the table.  If data is not specified, then "apply" mode will be used, and the template will find the next table on the page and convert it to sortable.  If data is specified, then TSTable will generate a newe table using this data.

    Normally, this will be a list of maps, with each map containing the data for one table row.  If a map is provided, though, TSTable will take map.values() of the provided map and therefore convert it into a list.  This makes it convenient to provide certain wiki data structures which are implemented as maps but can work well here (e.g., page.subpages).  See the examples!

    If data is a list of lists, then each element of each list will be used in order.  See the "CSV Splitting example".

    filter list of lists? no filter This arg allows you to specify a custom filter for the table.  The filter is implemented as a pull-down, with user-defined options.  The first option is always the default, and will be enabled when the page is first displayed.

    Options

    This is a map containing global options for the table.  Here are available elements in the map:

    Name
    Type
    Mode Default
    Description
    id str? apply find next table Find the table with specified ID attribute and apply.  If not specified, find the next table on the page after the TSTable() call.
    generate generate ID Apply the given ID to the generated table.  If not specified, TSTable() will generate a unique ID
    width str? generate none specify width of generated table.  If not specified, browser will auto-size the table.
    zebra bool? either false Apply zebra-striping to the table.  Color of the zebra stripe is customizable in the script in the "zebra_color" variable.
    rowstyle str? generate no styling This arg allows you to specify a style to apply to each data row of the table.  If the string is surrounded by parentheses, it will be evaluated as a Dekiscript expression for each row (see docs below)
    initial list of lists? either no initial sorting This specifies the "sortlist" argument to the tablesorter extension, and overrides "initial" elements specified in the "columns" argument.  Use this to specify a list of columns on which to sort the table when first displaying the page.
    pager bool? generate false

    Enables the pager, which is useful for large tables.  Initially, the table will display 10 records per page.  If there are fewer than 10 items in the table, the pager will not appear.

    At present, the pager cannot be used with the filter.

    sort bool? either true This globally enables or disables sorting on the whole table.  If sort=false, you can just use TSTable as a table generator.
    nodata str? generate "(no data)" Text to display if there are no data rows in the table

    Columns

    This argument is only used in "generate" mode.

    This is a list containing specifications for the table columns.  Each element of the list may be either a map or a string.  If it's a string, then TSTable() assumes the string is actually the "key" element.  The table below specifies the available elements for each column map:

    Name
    Type
    Default
    Description
    key str   This is the sole required element, and specifies the element of the row map in the data argument containing the cell contents.  If the string is surrounded by parentheses, then it will instead be evaluated as a Dekiscript expression applied to the row data (see docs below).
    title str? key Title of the column.  If not specified, the value of "key" will be used instead.  Note that "key" is not case-sensitive, so you can capitalize it as desired to produce a good-looking title.
    width str? none Specify width of the column.  If not specified, browser will auto-size the column.
    style str? no styling This arg allows you to specify a style to apply to this column of the table.  If the string is surrounded by parentheses, it will be evaluated as a Dekiscript expression for each row (see docs below)
    initial num? column not initially selected as sort column

    Sort on this column when first displaying the table:

    0 = ascending order
    1 = descending order

    sorter bool? or str? true

    By default, each column is sortable.  TSTable figures out how to sort based on analyzing the first row of data.  Usually it does a good enough job, but not always.  Using the "sorter" option, you can force the type of sort to use, or else disable sorting for the column altogether.  Note string values are not case sensitive.

    • Set to false to disable sortability on this column.
    • Set to one of the following strings to enforce a particular sorting type:
      • "text" : sort as text (default)
      • "digit" : sort numerically
      • "currency" : sort numerically, ignore leading currency symbol
      • "ipAddress" : sort as IP address (ddd.ddd.ddd.ddd)
      • "url" : sort as URL (ignore protocol prefix)
      • "isoDate" : sort as ISO formatted date (yyyy-M-d; separator may be "-" or "/")
      • "percent" : sort numerically; ignore trailing "%"
      • "usLongDate" : sort as date (MMM dd, (yyyy | 'yy) [hh:mm:ss (AM|PM))
      • "shortDate" : sort as date (MM-dd-(yyyy|yy))
      • "time" : sort as time of day (hh:mm (am|pm))

    Data

    This argument is only used in "generate" mode.

    This is a list of maps containing the data for the table.  If a map is provided, TSTable will automatically convert it to a list by applying map.values() to it.  This makes it convenient to pass wiki data structures, which are frequently maps of maps (e.g., page.subpages).  We'll show this in the tutorial.

    Each list element contains a map of data for one row of the table.  The values for each column are extracted according to the "key" element of the corresponding "columns" argument (see above).  For instance, if the first column specifies key:"uri", then the first column of row n of the table will be equal to data[n].uri.  This all will become much more clear in the examples.

    Normally, cell styles will be applied according to the "style" elements in the "columns" argument.  However, if you want to you can pre-calculate the style for any particular column by putting it in the data with the key stylec, where c = the column number, starting from zero.  In other words, if the row data has the element style1:"background-color:#FF0000", then the second column of that row (remember, we count from zero) will have a red background.

    Filter

    This argument is only used in "generate" mode, and will not work if the pager is enabled.

    If specified, this defines a filter that can be used to show only desired table rows.  The argument is a list of lists, each corresponding to an option on the filter pull-down control.  The first is always the default.  Each list has two elements:

    Name Type
    Description
    Option name str This is the name of the option that will be displayed in the pull-down
    Expression str This is a Dekiscript expression that will be applied to each row of the table.  If it evaluates to "true", then that row will be displayed when this option is selected.  Because this string is always evaluated as Dekiscript, it need not be enclosed in parentheses, but it won't hurt if it is.

    Evaluated Dekiscript expressions are described more below.  In the meantime, here's a simple example of a filter:

    [ [ "show all items", "true" ], [ "show nothing", "false" ] ]

    In this example, the Dekiscript expressions are as simple as they can get.   The first element is commonly used as the default, "show all" option.  The second is not very useful!

    Tutorial

    Please see the TsTable() tutorial to really learn and understand how this all works.  This is the same tutorial that used to be right here, just moved to its own page.

    Template Code

    // UNSAFECONTENT PERMISSION CHECK
    // This code checks if the template is properly installed for unsafe content execution,
    // and may be removed if this check is not desired.  If you leave this here, add your new code
    // in a new DekiScript block below this one.
    var chkunsafe = wiki.inclusions()[-1];
    if (!wiki.pagepermissions(chkunsafe.id, chkunsafe.author.id).unsafecontent)
      <div style="color:red; width:75%; padding:5px; border:1px solid red;">
        "WARNING: The page '"..chkunsafe.path.."' must be re-saved by a user with UNSAFECONTENT permission in order to work correctly. ";
        <a href="http://developer.mindtouch.com/en/kb/Using_templates_which_require_UNSAFECONTENT_permission"> "See this" </a>;
        " for more info."; 
      </div>;
    // TsTable template, by neilw, 2009
    // Versions
    //    1.00    7/27/2009   neilw    First published version
    //    1.10    7/29/2009   neilw    Added pagination support (doesn't work with filter yet)
    //    1.11    8/25/2009   neilw    Fixed bug with pager and fewer than 10 rows
    //    1.12    9/25/2009   neilw    Added support for data as list of lists
    //    1.13    10/8/2009   neilw    Put in a spacer for empty table cells
    //    1.14    10/9/2009   neilw    Fixed a bug introduced in 1.13 (sigh)
    //    1.15    10/12/2009  neilw    Cleaned up CSS, especially for IE
    //    1.16    10/13/2009  neilw    Added default filter option, reduced spurious error alerts
    //    1.17    2/12/2010   neilw    Improved table header HTML for improved reliability
    //    1.18    2/23/2010   neilw    Added UNSAFECONTENT permission check
    //    1.20    7/14/2010   neilw    Added full support for "sorter" option in columns
    //                                 Added "nosort" option for table
    //    1.21    7/19/2010   neilw    Now properly recognize "0" as a number (new tablesorter script, new name!!!)
    //    1.22    7/26/2010   neilw    Improved "sort:false" option
    //    1.23    8/13/2010   neilw    Cleaned up stylesheet a *little*
    //    1.24    11/22/2010  neilw    Added "nodata" option
    
    // LOCAL SETTINGS
    
    //This template requires the following files to be installed as specified below:
    var tablesorter_uri      = "http://developer.mindtouch.com/@api/deki/files/6272/=tstable.tablesorter.min.js";
    var tablesorter_bg_uri   = "http://developer.mindtouch.com/@api/deki/files/4627/=tablesorter_bg.gif";
    var tablesorter_asc_uri  = "http://developer.mindtouch.com/@api/deki/files/4626/=tablesorter_asc.gif";
    var tablesorter_desc_uri = "http://developer.mindtouch.com/@api/deki/files/4624/=tablesorter_desc.gif";
    
    var pager_uri            = "http://developer.mindtouch.com/@api/deki/files/4651/=jquery.tablesorter.pager.js";
    var pager_first_uri      = "http://developer.mindtouch.com/@api/deki/files/4648/=pager_first.png";
    var pager_prev_uri       = "http://developer.mindtouch.com/@api/deki/files/4647/=pager_prev.png";
    var pager_next_uri       = "http://developer.mindtouch.com/@api/deki/files/4650/=pager_next.png";
    var pager_last_uri       = "http://developer.mindtouch.com/@api/deki/files/4649/=pager_last.png";
    
    // The following variables define important styles for this template:
    var thead_unsel_color = "#D0E0E0";  // Color of unselected header cell
    var thead_sel_color   = "#8DBDD8";  // Color of selected header cell
    var zebra_color       = "#ECECF0";  // Color of odd rows when "zebra" is specified
    
    // USAGE: template.TsTable(options, columns, data)
    //    "columns": Optional array of column specifications, one for each table column. Each column spec can be either a
    //        simple string or a map.  This argument only applies if "data" is also specified (GENERATE mode).  If it's a
    //        string, then it specifies the key and title for the column data.  If it's a map, then the possible fields are:
    //            key: keyname for the map element containing the cell data.  THIS IS THE ONE AND ONLY FIELD THAT IS
    //                REQUIRED.  If surrounded by parentheses, will be evaluated as a Dekiscript expression to provided
    //                the cell value (see docs).  
    //            title: Title for the column.  If not specified, will use "key".
    //            width: width of column
    //            style: style to apply to this cell.  If surrounded by parentheses, will be evalauted as a Dekiscript
    //                expression (see docs)
    //            initial: set this column as initial sort column; 0 ==> ascending, 1 ==> descending
    //            sorter: control sortability on this column (this is case sensitive!):
    //                false ==> disable sorting on this column
    //                "text" ==> sort column as text
    //                "digit" ==> sort column as numbers
    //                "currency" ==> sort column as currency values (ignore leading currency symbol)
    //                "ipAddress" ==> sort as IP addresses
    //                "url" ==> sort as URL; ignore protocol prefix
    //                "isoDate" ==> sort as ISO formatted date (yyyy-M-d; separator may be "-" or "/")
    //                "percent" ==> sort as percentage (ignore trailing "%")
    //                "usLongDate" ==> sort as date (MMM dd, (yyyy | 'yy) [hh:mm:ss (AM|PM))
    //                "usShortDate" ==> sort as date (MM-dd-(yyyy|yy)
    //                "time" ==> sort as time (hh:mm (am|pm))
    //            date: TBD
    //    "options": Optional map containing an assortment of options that apply to the whole table:
    //        id: ID attribute of the table element.  If we're applying tablesorter to an existing table, the template
    //            will look for a table with this name.  If we're generating a table, its ID will be set to this value
    //        width: width of the entire table
    //        initial: initial sort order for the table.  This is the "sortlist" parameter for the tablesorter extension.
    //        rowstyle: style applied to each row.  If surrounded by parentheses, will be evaluated as a Dekiscript
    //            expression (see docs)
    //        zebra: set to true to enable zebra-striping of the table row
    //        pager: enable the table pager (doesn't work with filters yet!)
    //        sort: enable or disable sorting on the entire table (default:true)
    //        nodata: message to be printed when there is no data (default:  "(no data)")
    //    "data": array of maps or lists containing data for each row of the table.  The "columns" argument specified in the "key"
    //        element which element of the map will be used to supply data for this cell.  Additional elements may be
    //        present in the map for purposes of style or content evaluation (see docs), or it will be ignored.  If a data row is
    //        a list, then the elements will be used in order.
    //    "filter": array of 2-element arrays, each specifying optional filters that may be applied to the table.
    //        The first sub-array element specifies the name of the filter, which will be displayed in the
    //        <select> widget.  The second element is Dekiscript code which is applied to each row in the table;
    //        if the code evaluates to "true" then the row will be displayed when that option is selected.
    //        The first selection, and the default, is always "show all items", so you don't need to specify
    //        that one.  See docs for more details.
    
    /**** Misc "globals" ****/
    var options_arg = $1 ?? $options ?? {};
    var columns_arg = $0 ?? $columns;
    var data_arg    = $2 ?? $data;
    if (data_arg is map)
        let data_arg = map.values(data_arg);
    var filter_arg  = $3 ?? $filter;
    var apply = (data_arg == nil); // if no data, modify existing table (APPLY mode)
    
    var eval_re = "(^\\((.*)\\)$)";    // regular expression to detect "eval"
    var errors = [];     // list of error messages
    
    /**** Argument Processing ****/ 
    //
    // "columns" arg
    //
    var c_series = [];
    var columns = [];    // processed list of column specifications
    var headers = {};    // "headers" arg for tablesorter
    var sortlist = [];   // "sortlist" arg for tablesorter
    if (columns_arg == nil) {
        if (!apply) let errors ..= [ "must specify COLUMNS when DATA is also specified ('generate' mode)" ];
    }
    else {
        let c_series = num.series(0, #columns_arg - 1);
        if (apply) let errors ..= [ "WARNING: ignoring COLUMNS in 'apply' mode" ];
        else if (columns_arg is not list) let errors ..= [ 'COLUMNS: must be a list' ];
        else foreach (var i in c_series) {
            var c = columns_arg[i];
            if (c is str)
                let c = { key:c };
        // check for bogus extra fields
            foreach (var k in map.keys(c))
                if (!list.contains([ "title","key","width","sorter","initial","style" ], //"date","type","hide" ]
                    string.tolower(k))) let errors ..= [ "COLUMNS["..i.."]: unknown field: '"..k.."'" ];
        // "key" field
            if (c.key == nil) {
                let errors ..= [ "COLUMNS["..i.."]: no KEY supplied" ];
                let c ..= { key:"xxx" };
            }
            else if (string.match(c.key, eval_re)) let c ..= { eval_key:true };
        // "title" field
            if (c.title == nil) let c ..= { title:c.key };
        // "sorter" field
            if (c.sorter is bool || c.sorter is str) {
                var sorterOptions = [ "text", "digit", "currency", "ipAddress", "url", "isoDate",
                    "percent", "usLongDate", "shortDate", "time", "metadata" ];
                if (c.sorter is str && !list.contains(sorterOptions, c.sorter))
                    let errors ..= [ "COLUMNS["..i.."]: invalid SORTER option '"..c.sorter.."'" ];
                else
                    let headers ..= { (i):{ sorter:c.sorter } };
            }
        // "initial" field
            if (c.initial != nil) {
                if (!string.match(c.initial, "(^[01]$)")) let errors ..= [ "COLUMNS["..i.."]: INITIAL must be 0 or 1" ];
                else let sortlist ..= [ [i,c.initial] ];
            }
        // "style" field
            if (c.style != nil && string.match(c.style, eval_re)) let c ..= { eval_style:true };
        // Build columns
            let columns ..= [ c ];
        }
    }
    //
    // "options" arg
    //
    
    // check for bogus extra fields
    foreach (var k in map.keys(options_arg))
        if (!list.contains(["id","width","zebra","initial","rowstyle","pager","sort","nodata" ], string.tolower(k)))
            let errors ..= [ 'OPTIONS: unknown option: ' .. k ];
    // simple fields
    var tname    = options_arg.id?? (apply ? nil : @tname);
    var find     = (tname == nil);
    if (find) let tname = @tname; 
    var width    = options_arg.width;
    var widgets  = options_arg.zebra == true ? [ 'zebra' ] : [];
    let sortlist = options_arg.initial ?? sortlist;
    var pager    = options_arg.pager ?? false;
    var perpage  = [ 10, 25, 50, 100, 250, 500 ];
    var enableSorting   = options_arg.sort ?? true;
    var noData = options_arg.nodata;
    if (noData is not str) let noData = "(no data)";
    
    // "rowstyle" field
    if (options_arg.rowstyle != nil && string.match(options_arg.rowstyle, eval_re))
        let options_arg ..= { eval_rowstyle:true };
    
    //
    // "data" and "filter" arg
    //
    var filter = [];
    var filter_default = -1;
    var filter_code = [];
    if (filter_arg) {
        if (filter_arg is not list) let errors ..= [ "expected FILTER arg to be a list" ];
        else if (pager)             let errors ..= [ "filter won't work when pager is enabled (disabling filter)" ];
        else {
            let filter = [ ];
            let  filter_default = 0;
            let filter_code = [ ];
            foreach (var i in num.series(0,#filter_arg-1)) {
                var f = filter_arg[i];
                if (f is not list)
                    let errors ..= [ "FILTER: option "..i.." is not a list" ];
                else {
                    if (#f != 2 && !(#f == 3 && f[2] is bool))
                    let errors ..= [ "FILTER: option "..i.." is not a list of two elementsvalid" ];
                    
                else { 
                    let filter ..= [ f[0] ];                 
                    let filter_code ..= [ f[1] ];
                        if (f[2]) let filter_default = i;
                    }
                }
            }
        }
    }
    
    var space = web.html("&nbsp;");
    var data = [];       // processed data array
    if (!apply) {
        foreach (var r in data_arg) {
            if (r is list) let r = { (i):r[i] foreach var i in num.series(0,#r-1) };
            if (r is map) {
                let class = [];
                let r ..= { rowstyle:(options_arg.eval_rowstyle ?
                    list.apply([r],options_arg.rowstyle)[0] : options_arg.rowstyle) };
                foreach (var i in c_series) {
                    var c = columns[i];
                    if (c.eval_key || r[c.key] != nil)
                        let r ..= { (i): (c.eval_key ? list.apply([r],c.key)[0] : r[c.key]) };
                }
                foreach (var i in c_series) {
                    var c = columns[i];
                    if (c.style) let r ..= { ("style"..i): (c.eval_style ? list.apply([r],c.style)[0] : c.style ) };
                }
                if (#filter_code)
                    foreach (var i in num.series(0,#filter_code-1))
                        if (list.apply([r],filter_code[i])[0])
                            let class ..= [ "tsf"..i ];
                let data ..= [ r..{ class:string.join(class," ") } ];
            }
            else
                let errors ..= [ "DATA: item ".. __index .. " is not a map or list" ];
        }
    }
    else if (filter_arg)
        let errors ..= [ "WARNING: ignoring FILTER arg in 'apply' mode" ];
    
    //
    // Output XML
    //
    <html>
    // Scripts and stylesheets go in the head
    <head>
    <script type="text/javascript" src=(tablesorter_uri)></script>;
    <script type="text/javascript"> var garbage = 0; </script>;
    <script type="text/javascript" src=(pager_uri)></script>;
    
    <script type="text/javascript">"
    // Call main tablesorter function
    DekiWiki.$(document).ready(function($) {
        tstableApply($, "..json.emit(apply)..","..json.emit(find)..","..json.emit(@id)..","..
            json.emit(tname)..","..json.emit(headers)..","..json.emit(sortlist)..","..
            json.emit(widgets)..","..json.emit(filter_default)..","..json.emit(pager)..","..json.emit(enableSorting)..");
    });
    "</script>;
    <script type="text/javascript">"
    // Apply tablesorter to table
    function tstableApply($, apply, find, id, name, headers, sortlist, widgets, filter, pager, enable) {
    // Find the table
        var $table;
        if (!find)
            $table = $('table#'+name);
        else {
            var pid = 'p#' + id;
            var $nodes = $(pid).nextAll();
            for (var i = 0; i < $nodes.length; i++) {
                $table = $nodes.eq(i);
                if ($table.is('table')) break;
                $table = $table.find('table:first');
                if ($table.length) break;
            }
            if (!$table.length) {
                alert(\"ERROR: TsTable: can't find table\"); 
                return;
            }
            $table.attr({'id':name, 'border':'0', 'cellspacing':'0px', 'cellpadding':'3px'});
        }
    // set 'tablesorter' class if necessary
        if (!$table.hasClass('tablesorter')) $table.addClass('tablesorter');
    // Copy header row to 'thead' element if necessary
        var $header = $table.find('thead tr');
        if (!$header.length) {
            var $ohr = $table.find('tr:eq(0)');
            if (!$ohr.length) return;	// table is empty, bail out
            $table.prepend('<thead><tr>'+$ohr.html()+'</tr></thead>');
            $ohr.remove();
            $header = $table.find('thead tr');
        }
    // Convert header <td> to <th> if necessary
        if (apply) {
            var hdrhtml = '';
            $header.children().each(function() { hdrhtml += '<th>'+$(this).html()+'</th>'; });
            $header.html(hdrhtml);
        }
    // Convert format of each header element to reserve room for arrow
        $header.children().each(function() {
            $(this).css('padding-right','20px');
        });
    // Clean up leading and trailing space
        $table.find('td,th').each(function() {
     	$(this).find('p:first-child').css({'margin-top':'0px','padding-top':'0px'});
    	$(this).find('p:last-child').css({'margin-bottom':'0px','padding-bottom':'0px'});
            var html = $(this).html();
            html = html.replace(/^(\\s*(\\&nbsp;)*(\\<br ?\\/?\\>)*)*/,'');
            html = html.replace( /(\\s*(\\&nbsp;)*(\\<br ?\\/?\\>)*)*$/,'');
            $(this).html(html);
        });
    // If we're not going to enable sorting, get out
        if (!enable) return;
    // Install filter
        if (filter >= 0) {
            var selectid = 'select#tsf' + id;
            $(selectid).change(function() {
                var $options = $(this).find('option:selected');
                $table.find('tbody.tsf > tr').each(function() {
                    var show = false;
                    var $row = $(this);
                    $options.each(function() { show |= $row.hasClass($(this).attr('value')); } );
                    if (show)    $row.show();
                    else         $row.hide();
                });
                $table.trigger('applyWidgets');
                return(false);
            });
            $(selectid).find('option').each(function(i) { $(this).attr('selected',i==filter?'selected':''); });
            $(selectid).change();
        }
    // Now activate tablesorter
        $table.tablesorter({ 
            headers: headers, sortList: sortlist, widgets: widgets,
            cssHeader: 'tsHeader', cssAsc: 'tsHeaderSortUp', cssDesc: 'tsHeaderSortDown'
        });
        if (pager) {
            var pagerid = '#'+id+'pager';
            $(pagerid).find('option').each(function(i) { $(this).attr('selected',i==0?'selected':''); });
            $table.tablesorterPager({container: $('#'+id+'pager'), positionFixed:false});
        }
    }
    "</script>;
    
    // Styles
    var tid = "table.tablesorter#"..tname;
    <style type="text/css">"
    .tablesorter {
        border-collapse: separate;
        border-left: solid #808080 1px;
        border-top:  solid #808080 1px;
    }
    .tablesorter > thead > tr > th, .tablesorter > tbody > tr > td, .tablesorter > tfoot > tr > td {
        border-right:  solid #808080 1px;
        border-bottom: solid #808080 1px;
    }
    .tablesorter > thead > tr > th, .tablesorter > tfoot > tr > th {
        background-color: "..thead_unsel_color..";
        font-weight: bold;
    }
    .tsHeader {
            background-image: url("..tablesorter_bg_uri..");
            background-repeat: no-repeat;
            background-position: center right;
            cursor: pointer;
    }
    .tablesorter > tbody tr.odd    { background-color:"..zebra_color.."; }
    .tsHeader.tsHeaderSortUp   {
        background-image: url("..tablesorter_asc_uri..");
        background-color: "..thead_sel_color..";
    }
    .tsHeader.tsHeaderSortDown {
        background-image: url("..tablesorter_desc_uri..");
        background-color: "..thead_sel_color..";
    }
    "</style>;
    </head>
    
    // Generate table HTML in the body
    <body>
    // Error messages
        if (#errors)
          <div style="color:red; font-weight:bold;"> "TSTABLE ERRORS:";
            <ul> foreach (var e in errors) <li> e </li>; </ul>
          </div>;
    // Insert marker if applying
        if (apply) { <p id=(@id) style="display:none;" /><div />; }
    // Else generate table
        else {
          if (#filter) {
            <p><span>"Show: "</span><select id=("tsf"..@id)>
                foreach (var i in num.series(0,#filter-1))
                    <option value=("tsf"..i) selected=(i==0?"selected":"")> filter[i] </option>;
            </select></p>;
          }  
          <table class="tablesorter" id=(tname) width=(width) cellpadding="3px" cellspacing="0px">
        // Header
            <thead><tr> foreach (var c in columns) <th width=(c.width)>c.title</th>; </tr></thead>
        // Body
            <tbody class="tsf">
                foreach (var r in data) <tr style=(r.rowstyle) class=(r.class)>
                    foreach (var i in c_series) <td style=(r["style"..i])> (r[i]==nil || r[i]=="") ? space : r[i] </td>;
                </tr>;
                if (!#data) <tr><td colspan=(#columns)> noData </td></tr>;
            </tbody>
            if (pager) {
              <tfoot style=(#data > perpage[0] ? "" : "display:none")>
               <tr bgcolor=(thead_unsel_color)><td id=(@id .. "pager") colspan=(#columns) align="center">
                <img class="first" style="cursor:pointer" src=(pager_first_uri) />
                <img class="prev" style="cursor:pointer" src=(pager_prev_uri) />
                <img src="/skins/common/icons/icon-trans.gif" width="8" height="8" />
                <span class="pagedisplay"> " " </span>
                <img src="/skins/common/icons/icon-trans.gif" width="8" height="8" />
                <img class="next" style="cursor:pointer" src=(pager_next_uri) />
                <img class="last" style="cursor:pointer" src=(pager_last_uri) />
                <select class="pagesize" style="float:right">
                  foreach (var pp in perpage) {
                      <option value=(pp) selected=(__index == 0 ? "selected" : "")> (pp>#data ? "all":pp) </option>;
                      if (pp > #data) break;
                  }
    	    </select>
              </td></tr></tfoot>;
            }
          </table>;
        }
    </body>
    </html>;
    Was this page helpful?
    Tag page

    Files 10

    FileVersionSizeModified 
    Viewing 15 of 41 comments: view all
    Got it:
    { key:'(<input type="button" value="Details" />)', title:"Last comment" }
    Thanks.
    Posted 16:17, 8 Jan 2010
    I would like to use TSTable to drop every page on my current wiki into a sortable, filterable table. The problem I have so far is that I cannot figure out how to use wiki.tree instead of wiki.getpage...
    Any thoughts?
    Posted 15:05, 28 Jan 2010
    @imattdotnet
    Use wiki.getsearch("type:wiki", 10000) to get the list of pages (replace 10000 with whatever is the maximum you want to allow). You may want to pre-sort the list before giving it to TsTable, which you can do either inside wiki.getsearch() or with a list.sort() afterwards. Keep in mind that if the number of pages is very large this could take a while!
    Posted 16:48, 28 Jan 2010
    Hello. I'm a bit new at inserting copied code into my pages so this may be something I did wrong. However, when I copied the code (expanded, view source "Notepadish" version), it didn't work well. My version had a bunch of issues where it tried to process the commented out lines. FYI, I am running IE8 and using a pre-release version of MindTouch. An example of the problems I am having include the less-than + SELECT + greater-than phrase in line 61 (spelled out to ensure it isn't handled as a tag as mine did). This area of the code was only visible in the SOURCE editor even though it is enclosed in a set of PRE class="script" tags. Has anyone else found a similar problem? As I mentioned, I am running a prerelease version (commercial that is having some development done on it) and I believe I heard or read somewhere that it was dealing with special characters differently (escaping??). Like I said, I'm new to this world, so this could just be noob error.
    Update: After trying to fix the mess manually, I recopied the code from here, pasted it into a text editor (Notepad++), selected all and copied it from there, and then successfully pasted it into the dekiscript section of my template page. Live and learn... FYI, even after doing this, I still ended up with some extra pre tags that needed to be cleaned out. edited 00:04, 6 Feb 2010
    Posted 21:44, 5 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    When I use wiki.getsearch("type:wiki", 10000) to get the whole list of pages, some functions doesn't work, like { key:"(web.link($.uri,$.title))" } or { key:"(date.format($.date,'yyyy/MM/dd'))" }. Do you have some ideas? Thanks a lot!
    Posted 09:36, 24 Feb 2010
    @jc_silver
    I've answered your question (once :)) on the forums; please continue the discussion there.
    Posted 17:40, 24 Feb 2010
    Have somebody the same problem, when you disable your internet connection, that you aren't able to edit any page in deki wiki?

    Seems to be a problem with the javascript files on developer.mindtocuh.com.
    Please make a exclusion if you don't have a internet connection. So you can edit pages in intranet.
    Ty
    Posted 01:13, 28 Feb 2012
    Viewing 15 of 41 comments: view all
    You must login to post a comment.

    Copyright © 2011 MindTouch, Inc. Powered by