Commit 0bb25f6f authored by Elias Villiger's avatar Elias Villiger
Browse files

Feature #1261 - Tablesorter, incl. saved sort, combined column sort, filters, pagination

parent e5bb6217
Pipeline #863 passed with stage
in 1 minute and 47 seconds
...@@ -132,7 +132,8 @@ module.exports = function (grunt) { ...@@ -132,7 +132,8 @@ module.exports = function (grunt) {
{ {
cwd: 'node_modules/tablesorter/dist/js/', cwd: 'node_modules/tablesorter/dist/js/',
src: [ src: [
'jquery.tablesorter.min.js' 'jquery.tablesorter.combined.min.js',
'extras/jquery.tablesorter.pager.min.js'
], ],
expand: true, expand: true,
dest: typo3_js, dest: typo3_js,
...@@ -141,9 +142,8 @@ module.exports = function (grunt) { ...@@ -141,9 +142,8 @@ module.exports = function (grunt) {
{ {
cwd: 'node_modules/tablesorter/dist/js/', cwd: 'node_modules/tablesorter/dist/js/',
src: [ src: [
'jquery.tablesorter.min.js', 'jquery.tablesorter.combined.min.js',
'jquery.tablesorter.widgets.min.js', 'extras/jquery.tablesorter.pager.min.js'
'widgets/widget-grouping.min.js'
], ],
expand: true, expand: true,
dest: 'js/', dest: 'js/',
...@@ -517,7 +517,7 @@ module.exports = function (grunt) { ...@@ -517,7 +517,7 @@ module.exports = function (grunt) {
"extension/Resources/Public/Css/qfq-bs.css": "less/qfq-bs.css.less", "extension/Resources/Public/Css/qfq-bs.css": "less/qfq-bs.css.less",
"extension/Resources/Public/Css/qfq-letter.css": "less/qfq-letter.css.less", "extension/Resources/Public/Css/qfq-letter.css": "less/qfq-letter.css.less",
"extension/Resources/Public/Css/qfq-plain.css": "less/qfq-plain.css.less", "extension/Resources/Public/Css/qfq-plain.css": "less/qfq-plain.css.less",
"extension/Resources/Public/Css/bs-tablesorter.css": "less/bs-tablesorter.less" "extension/Resources/Public/Css/tablesorter-bootstrap.css": "less/tablesorter-bootstrap.less"
}, },
options: { options: {
compress: true compress: true
...@@ -528,7 +528,7 @@ module.exports = function (grunt) { ...@@ -528,7 +528,7 @@ module.exports = function (grunt) {
"css/qfq-bs.css": "less/qfq-bs.css.less", "css/qfq-bs.css": "less/qfq-bs.css.less",
"css/qfq-letter.css": "less/qfq-letter.css.less", "css/qfq-letter.css": "less/qfq-letter.css.less",
"css/qfq-plain.css": "less/qfq-plain.css.less", "css/qfq-plain.css": "less/qfq-plain.css.less",
"css/bs-tablesorter.css": "less/bs-tablesorter.less" "css/tablesorter-bootstrap.css": "less/tablesorter-bootstrap.less"
}, },
options: { options: {
banner: "/* Change qfq-bs.css.less, not qfq-bs.css */" banner: "/* Change qfq-bs.css.less, not qfq-bs.css */"
......
...@@ -203,6 +203,9 @@ Setup CSS & JS ...@@ -203,6 +203,9 @@ Setup CSS & JS
file3 = typo3conf/ext/qfq/Resources/Public/Css/jqx.base.css file3 = typo3conf/ext/qfq/Resources/Public/Css/jqx.base.css
file4 = typo3conf/ext/qfq/Resources/Public/Css/jqx.bootstrap.css file4 = typo3conf/ext/qfq/Resources/Public/Css/jqx.bootstrap.css
file5 = typo3conf/ext/qfq/Resources/Public/Css/qfq-bs.css file5 = typo3conf/ext/qfq/Resources/Public/Css/qfq-bs.css
# Needed for tablesorter
file6 = typo3conf/ext/qfq/Resources/Public/Css/tablesorter-bootstrap.css
} }
page.includeJS { page.includeJS {
...@@ -218,7 +221,11 @@ Setup CSS & JS ...@@ -218,7 +221,11 @@ Setup CSS & JS
# Only needed in case FormElement 'annotate' is used. # Only needed in case FormElement 'annotate' is used.
file10 = typo3conf/ext/qfq/Resources/Public/JavaScript/fabric.min.js file10 = typo3conf/ext/qfq/Resources/Public/JavaScript/fabric.min.js
file11 = typo3conf/ext/qfq/Resources/Public/JavaScript/qfq.fabric.min.js file11 = typo3conf/ext/qfq/Resources/Public/JavaScript/qfq.fabric.min.js
# Needed for tablesorter
file12 = typo3conf/ext/qfq/Resources/Public/JavaScript/jquery.tablesorter.combined.min.js
file13 = typo3conf/ext/qfq/Resources/Public/JavaScript/jquery.tablesorter.pager.min.js
} }
...@@ -5671,7 +5678,7 @@ Easily create Email links. ...@@ -5671,7 +5678,7 @@ Easily create Email links.
|<emailaddress>| The email address where the link should point to. |none | |<emailaddress>| The email address where the link should point to. |none |
+--------------+----------------------------------------------------------------------------------------+-------------+ +--------------+----------------------------------------------------------------------------------------+-------------+
|<linktext> | The text that should be displayed on the website and be linked to the email address. |none | |<linktext> | The text that should be displayed on the website and be linked to the email address. |none |
| | This will typically be the name of the recipient. If this parameter is omitted, | | | | | | This will typically be the name of the recipient. If this parameter is omitted, | |
| | the email address will be displayed as link text. | | | | the email address will be displayed as link text. | |
+--------------+----------------------------------------------------------------------------------------+-------------+ +--------------+----------------------------------------------------------------------------------------+-------------+
...@@ -6747,6 +6754,75 @@ E.g.:: ...@@ -6747,6 +6754,75 @@ E.g.::
10.sql = SELECT "p:home&r=0|t:Home|c:qfq-100 qfq-left" AS _pagev 10.sql = SELECT "p:home&r=0|t:Home|c:qfq-100 qfq-left" AS _pagev
Tablesorter
-----------
QFQ includes a third-party client-side table sorter. tablesorter 2.31.0 is used, see official docs here: https://mottie.github.io/tablesorter/docs/index.html
To turn any table into a sortable table, use this simple setup:
* Ensure that your QFQ installation is importing the appropriate js/css files, see setup-css-js_.
* Add the `class="tablesorter"` to your `<table>` element.
* Make sure your `<table>` has a `<thead>` and `<tbody>`.
In addition to the *tablesorter* class, you can add a *tablesorter-filter* and/or *tablesorter-pager* to add
filter options and pagination, respectively. Please note that filtering and/or paging is only supported in combination
with sorting.
Example: ::
10 {
sql = SELECT id, CONCAT('form&form=person&r=', id) AS _Pagee, lastName FROM person
head = <table class="table tablesorter tablesorter-filter tablesorter-pager">
<thead><tr><th>Id</th><th data-sorter="false">Edit</th><th>Name</th></tr></thead><tbody>
tail = </tbody></table>
rbeg = <tr>
rend = </tr>
fbeg = <td>
fend = </td>
}
Please note that if you want to customize certain aspects of the sorter, filter, or pager, you may have to add the
appropriate javascript code for yourself (inside a `$(document).ready()`). If you do so, do not use
the above *tablesorter* classes since the QFQ javascript code could then interfere with your javascript code.
The default configuration is: ::
tablesorter() - without filter:
{
theme: "bootstrap",
widthFixed: true,
headerTemplate: '{content} {icon}',
dateFormat: 'ddmmyyyy',
widgets: [ "uitheme", "saveSort"]
}
tablesorter() - with filter:
{
theme: "bootstrap",
widthFixed: true,
headerTemplate: '{content} {icon}',
dateFormat: 'ddmmyyyy',
widgets: [ "uitheme", "filter", "saveSort"],
widgetOptions: {
filter_reset: ".reset",
filter_cssFilter: "form-control"
}
}
tablesorterPager():
{
container: $("#qfq-pager-{{num}}"),
cssGoto: ".pagenum",
removeRows: false,
output: '{startRow} - {endRow} / {filteredRows}' });
}
For tables with the *tablesorter-pager* class, the paging navigation is dynamically injected upon loading the document;
`{{num}}` is a sequential number.
.. _monitor: .. _monitor:
Monitor Monitor
......
/**
* @author Elias Villiger <elias.villiger@uzh.ch>
*/
/* global $ */
/* global EventEmitter */
/* @depend QfqEvents.js */
$(document).ready( function () {
$.tablesorter.themes.bootstrap.table = "";
$('.tablesorter:not(.tablesorter-filter)').tablesorter({
theme: "bootstrap",
widthFixed: true,
headerTemplate: '{content} {icon}',
dateFormat: 'ddmmyyyy',
widgets: [ "uitheme", "saveSort"]
} );
$('.tablesorter-filter').tablesorter({
theme: "bootstrap",
widthFixed: true,
headerTemplate: '{content} {icon}',
dateFormat: 'ddmmyyyy',
widgets: [ "uitheme", "filter", "saveSort"],
widgetOptions: {
filter_reset: ".reset",
filter_cssFilter: "form-control"
} } );
$('.tablesorter-pager').each(function(i) {
var pagerId = "qfq-pager-" + i;
var pagerHtml = '<div id="' + pagerId + '" class="form-inline" style="margin-top:-20px; margin-bottom:20px;">' +
'<div class="btn-group btn-group-sm" role="group">' +
'<button type="button" class="btn btn-default first"><span class="glyphicon glyphicon-step-backward"></span></button>' +
'<button type="button" class="btn btn-default prev"><span class="glyphicon glyphicon-backward"></span></button>' +
'</div>' +
'<span class="pagedisplay"></span>' +
'<div class="btn-group btn-group-sm" role="group">' +
'<button type="button" class="btn btn-default next"><span class="glyphicon glyphicon-forward"></span></button>' +
'<button type="button" class="btn btn-default last"><span class="glyphicon glyphicon-step-forward"></span></button>' +
'</div>' +
'<select class="form-control input-sm pagesize" title="Select page size">' +
'<option selected="selected" value="10">10</option>' +
'<option value="25">25</option>' +
'<option value="50">50</option>' +
'<option value="100">100</option>' +
'<option value="all">All Rows</option>' +
'</select>' +
'<select class="form-control input-sm pagenum" title="Select page number"></select>' +
'</div>';
$(pagerHtml).insertAfter($(this));
$(this).tablesorterPager({
container: $("#" + pagerId),
cssGoto: ".pagenum",
removeRows: false,
output: '{startRow} - {endRow} / {filteredRows}' });
});
} );
\ No newline at end of file
/* Tablesorter Custom LESS Theme by Rob Garrison
To create your own theme, modify the code below and run it through
a LESS compiler, like this one: http://leafo.net/lessphp/editor.html
or download less.js from http://lesscss.org/
Test out these custom less files live
Basic Theme : http://codepen.io/Mottie/pen/eqBbn
Bootstrap : http://codepen.io/Mottie/pen/Ltzpi
Metro Style : http://codepen.io/Mottie/pen/gCslk
*/
/*** theme ***/
@theme : tablesorter-bootstrap;
/*** fonts ***/
@tableHeaderFont : inherit;
@tableBodyFont : inherit;
/*** color definitions ***/
/* for best results, only change the hue (120),
leave the saturation (60%) and luminosity (75%) alone
pick the color from here: http://hslpicker.com/#99E699 */
@headerBackground : white;
@borderAndBackground : #cdcdcd;
@overallBorder : inherit;
@headerTextColor : #000;
@bodyBackground : #fff;
@bodyTextColor : #000;
@headerAsc : darken(spin(@headerBackground, 5), 10%);
/* darken(@headerBackground, 10%); */
@headerDesc : lighten(spin(@headerBackground, -5), 10%);
/* desaturate(@headerAsc, 5%); */
@captionBackground : #fff;
/* it might be best to match the document body background color here */
@errorBackground : #e6bf99;
/* ajax error message (added to thead) */
@filterCellBackground : #eee;
@filterElementTextColor: #333;
@filterElementBkgd : #fff;
@filterElementBorder : 1px solid #bbb;
@filterTransitionTime : 0.1s;
@filterRowHiddenHeight : 4px;
/* becomes height using padding (so it's divided by 2) */
@overallPadding : inherit;
/* 20px should be slightly wider than the icon width to avoid overlap */
@headerPadding : inherit;
/* url(icons/loading.gif); */
@processingIcon : url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=');
/* zebra striping */
.allRows {
background-color: @bodyBackground;
color: @bodyTextColor;
}
.evenRows {
background-color: lighten(@headerBackground, 40%);
color: @bodyTextColor;
}
.oddRows {
background-color: lighten(@headerBackground, 20%);
}
/* hovered rows */
.oddHovered {
background-color: desaturate(@headerBackground, 60%);
color: @bodyTextColor;
}
.evenHovered {
background-color: lighten(desaturate(@headerBackground, 60%), 10%);
color: @bodyTextColor;
}
/* Columns widget */
@primaryOdd : spin(@headerBackground, 10);
/* saturate( darken( desaturate(@headerBackground, 10%), 10% ), 30%); */
@primaryEven : lighten(@primaryOdd, 10%);
@secondaryOdd : @primaryEven;
@secondaryEven : lighten(@primaryEven, 5%);
@tertiaryOdd : @secondaryEven;
@tertiaryEven : lighten(@secondaryEven, 5%);
/* Filter widget transition */
.filterWidgetTransition {
-webkit-transition: line-height @filterTransitionTime ease;
-moz-transition: line-height @filterTransitionTime ease;
-o-transition: line-height @filterTransitionTime ease;
transition: line-height @filterTransitionTime ease;
}
/*** Arrows ***/
@arrowPosition : right 5px center;
/* black */
@unsortedBlack : url(data:image/gif;base64,R0lGODlhFQAJAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==);
@sortAscBlack : url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7);
@sortDescBlack : url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7);
/* white */
@unsortedWhite : url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==);
@sortAscWhite : url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7);
@sortDescWhite : url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7);
/* automatically choose the correct arrow/text color */
.headerText (@a) when (lightness(@a) >= 50%) {
color: @headerTextColor;
}
.headerText (@a) when (lightness(@a) < 50%) {
color: lighten(@headerTextColor, 90%);
}
.unsorted (@a) when (lightness(@a) >= 50%) {
background-image: @unsortedBlack;
}
.unsorted (@a) when (lightness(@a) < 50%) {
background-image: @unsortedWhite;
}
.sortAsc (@a) when (lightness(@a) >= 50%) {
background-image: @sortAscBlack;
}
.sortAsc (@a) when (lightness(@a) < 50%) {
background-image: @sortAscWhite;
}
.sortDesc (@a) when (lightness(@a) >= 50%) {
background-image: @sortDescBlack;
}
.sortDesc (@a) when (lightness(@a) < 50%) {
background-image: @sortDescWhite;
}
/* variable theme name - requires less.js 1.3+;
or just replace (!".@{theme}") with the contents of @theme
*/
.@{theme} {
/* style header */
.tablesorter-header {
.unsorted(@headerBackground);
background-repeat: no-repeat;
background-position: @arrowPosition;
cursor: pointer;
}
.tablesorter-header.sorter-false {
background-image: none;
cursor: default;
}
.tablesorter-headerAsc {
background-color: @headerAsc;
.sortAsc(@headerBackground);
}
.tablesorter-headerDesc {
background-color: @headerDesc;
.sortDesc(@headerBackground);
}
/* tfoot */
tfoot .tablesorter-headerAsc,
tfoot .tablesorter-headerDesc {
/* remove sort arrows from footer */
background-image: none;
}
/* optional disabled input styling */
.disabled {
opacity: 0.5;
filter: alpha(opacity=50);
cursor: not-allowed;
}
/* body */
tbody {
td {
.allRows;
}
/* Zebra Widget - row alternating colors */
tr.odd > td {
.oddRows;
}
tr.even > td {
.evenRows;
}
tr.group-hidden {
display: none;
}
tr.group-header {
td {
i {
display: inline-block;
width: 0;
height: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid #888;
border-right: 4px solid #888;
border-left: 4px solid transparent;
margin-right: 7px;
user-select: none;
-moz-user-select: none;
}
}
}
tr.group-header.collapsed {
td {
i {
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid #888;
border-right: 0;
margin-right: 10px;
}
}
}
}
/* hovered row colors
you'll need to add additional lines for
rows with more than 2 child rows
*/
tbody > tr.hover td,
tbody > tr:hover td,
tbody > tr:hover + tr.tablesorter-childRow > td,
tbody > tr:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td,
tbody > tr.even.hover > td,
tbody > tr.even:hover > td,
tbody > tr.even:hover + tr.tablesorter-childRow > td,
tbody > tr.even:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td {
.evenHovered;
}
tbody > tr.odd.hover > td,
tbody > tr.odd:hover > td,
tbody > tr.odd:hover + tr.tablesorter-childRow > td,
tbody > tr.odd:hover + tr.tablesorter-childRow + tr.tablesorter-childRow > td {
.oddHovered;
}
/* table processing indicator - indeterminate spinner */
.tablesorter-processing {
background-image: @processingIcon;
background-position: center center;
background-repeat: no-repeat;
}
/* Column Widget - column sort colors */
tr.odd td.primary {
background-color: @primaryOdd;
}
td.primary, tr.even td.primary {
background-color: @primaryEven;
}
tr.odd td.secondary {
background-color: @secondaryOdd;
}
td.secondary, tr.even td.secondary {
background-color: @secondaryEven;
}
tr.odd td.tertiary {
background-color: @tertiaryOdd;
}
td.tertiary, tr.even td.tertiary {
background-color: @tertiaryEven;
}
/* caption (non-theme matching) */
caption {
background-color: @captionBackground;
}
/* filter widget */
.tablesorter-filter-row input,
.tablesorter-filter-row select {
width: 98%;
height: auto;
margin: 0;
padding: @overallPadding;
color: @filterElementTextColor;
background-color: @filterElementBkgd;
border: @filterElementBorder;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
.filterWidgetTransition;
}
.tablesorter-filter-row {
background-color: @filterCellBackground;
}
.tablesorter-filter-row td {
text-align: center;
background-color: @filterCellBackground;
line-height: normal;
text-align: center; /* center the input */
.filterWidgetTransition;
}
/* hidden filter row */
.tablesorter-filter-row.hideme td {
padding: @filterRowHiddenHeight / 2;
margin: 0;
line-height: 0;
cursor: pointer;
}
.tablesorter-filter-row.hideme * {
height: 1px;
min-height: 0;
border: 0;
padding: 0;
margin: 0;
/* don't use visibility: hidden because it disables tabbing */
opacity: 0;
filter: alpha(opacity=0);
}
/* rows hidden by filtering (needed for child rows) */
.filtered {
display: none;
}
/* ajax error row */
.tablesorter-errorRow td {
text-align: center;
cursor: pointer;
background-color: @errorBackground;
}
}
/* Based on Tablesorter Custom Bootstrap v3 LESS Theme by Rob Garrison
Modified into minimal bootstrap theme by Elias Villiger
To create your own theme, modify the code below and run it through
a LESS compiler, like this one: http://leafo.net/lessphp/editor.html
or download less.js from http://lesscss.org/
Test out these customization files live
Basic LESS Theme : http://codepen.io/Mottie/pen/eqBbn
Bootstrap LESS : http://codepen.io/Mottie/pen/Ltzpi
Metro LESS Style : http://codepen.io/Mottie/pen/gCslk
Basic SCSS : http://codepen.io/Mottie/pen/LbXdNR
*/
/*** theme ***/
@theme : tablesorter-bootstrap;
/*** fonts ***/
//@tableHeaderFont : 14px bold Arial, Sans-serif;
//@tableBodyFont : 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
/*** color definitions ***/
/* for best results, only change the hue (240),
leave the saturation (60%) and luminosity (80%) alone
pick the color from here: http://hslpicker.com/#99E699 */
//@headerBackground : hsl(240, 60%, 80%);
//@borderAndBackground : #cdcdcd;
//@overallBorder : @borderAndBackground 1px solid;
//@headerTextColor : #000;
//
//@bodyBackground : #fff;
//@bodyTextColor : #000;
//@headerAsc : darken(spin(@headerBackground, 5), 10%); /* darken(@headerBackground, 10%); */
//@headerDesc : lighten(spin(@headerBackground, -5), 10%); /* desaturate(@headerAsc, 5%); */
@captionBackground : #fff; /* it might be best to match the document body background color here */
@errorBackground : #e6bf99; /* ajax error message (added to thead) */
//@filterCellBackground : #eee;
//@filterElementTextColor: #333;
//@filterElementBkgd : #fff;
//@filterElementBorder : 1px solid #bbb;
@filterTransitionTime : 0.1s;
@filterRowHiddenHeight : 4px; /* becomes height using padding (so it's divided by 2) */
//@overallPadding : 4px;
/* 20px should be slightly wider than the icon width to avoid overlap */
@headerPadding : 0px 18px 0px 0px;
@headerMargin : 0 0 18px;
/* url(icons/loading.gif); */
@processingIcon : url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=');
/* zebra striping */
//.allRows {
// background-color: @bodyBackground;
// color: @bodyTextColor;
//}
//.evenRows {