This commit is contained in:
2024-04-27 09:23:34 +02:00
commit 11e713ca6f
11884 changed files with 3263371 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
/*
* Metadata - jQuery plugin for parsing metadata from elements
*
* Copyright (c) 2006 John Resig, Yehuda Katz, Jörn Zaefferer, Paul McLanahan
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
/**
* Sets the type of metadata to use. Metadata is encoded in JSON, and each property
* in the JSON will become a property of the element itself.
*
* There are three supported types of metadata storage:
*
* attr: Inside an attribute. The name parameter indicates *which* attribute.
*
* class: Inside the class attribute, wrapped in curly braces: { }
*
* elem: Inside a child element (e.g. a script tag). The
* name parameter indicates *which* element.
*
* The metadata for an element is loaded the first time the element is accessed via jQuery.
*
* As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
* matched by expr, then redefine the metadata type and run another $(expr) for other elements.
*
* @name $.metadata.setType
*
* @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
* @before $.metadata.setType("class")
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
* @desc Reads metadata from the class attribute
*
* @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
* @before $.metadata.setType("attr", "data")
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
* @desc Reads metadata from a "data" attribute
*
* @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
* @before $.metadata.setType("elem", "script")
* @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
* @desc Reads metadata from a nested script element
*
* @param String type The encoding type
* @param String name The name of the attribute to be used to get metadata (optional)
* @cat Plugins/Metadata
* @descr Sets the type of encoding to be used when loading metadata for the first time
* @type undefined
* @see metadata()
*/
(function($) {
$.extend({
metadata : {
defaults : {
type: 'class',
name: 'metadata',
cre: /(\{.*\})/,
single: 'metadata'
},
setType: function( type, name ){
this.defaults.type = type;
this.defaults.name = name;
},
get: function( elem, opts ){
var data, m, e, attr,
settings = $.extend({},this.defaults,opts);
// check for empty string in single property
if ( !settings.single.length ) { settings.single = 'metadata'; }
data = $.data(elem, settings.single);
// returned cached data if it already exists
if ( data ) { return data; }
data = "{}";
if ( settings.type === "class" ) {
m = settings.cre.exec( elem.className );
if ( m ) { data = m[1]; }
} else if ( settings.type === "elem" ) {
if( !elem.getElementsByTagName ) { return undefined; }
e = elem.getElementsByTagName(settings.name);
if ( e.length ) { data = $.trim(e[0].innerHTML); }
} else if ( elem.getAttribute !== undefined ) {
attr = elem.getAttribute( settings.name );
if ( attr ) { data = attr; }
}
if ( data.indexOf( '{' ) <0 ) { data = "{" + data + "}"; }
data = eval("(" + data + ")");
$.data( elem, settings.single, data );
return data;
}
}
});
/**
* Returns the metadata object for the first member of the jQuery object.
*
* @name metadata
* @descr Returns element's metadata object
* @param Object opts An object contianing settings to override the defaults
* @type jQuery
* @cat Plugins/Metadata
*/
$.fn.metadata = function( opts ){
return $.metadata.get( this[0], opts );
};
})(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,76 @@
/*
*
* StaticRow widget for jQuery TableSorter 2.0
* Version 1.0
*
* Copyright (c) 2011 Nils Luxton
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
*/
$.tablesorter.addWidget({
// Give the new Widget an ID to be used in the tablesorter() call, as follows:
// $('#myElement').tablesorter({ widgets: ['zebra','staticRow'] });
id: 'staticRow',
// "Format" is run on all widgets once when the tablesorter has finished initialising,
// and then again every time a sort has finished.
format: function(table) {
// Use a property of the function to determine
// whether this is the first run of "Format"
// (i.e. is this the table's default starting position,
// or has it been sorted?)
if (typeof $(table).data('hasSorted') == 'undefined')
{
$(table).data('hasSorted', true); // This will force us into the "else" block the next time "Format" is run
// "Index" the static rows, saving their current (starting)
// position in the table inside a data() param on the
// <tr> element itself for later use.
$('tbody .static', table).each(function() {
$(this).data('tableindex', $(this).index());
});
}
else
{
// Loop the static rows, moving them to their
// original "indexed" position, and keep doing
// this until no more re-shuffling needs doing
var hasShuffled = true;
while (hasShuffled)
{
hasShuffled = false;
$('tbody .static', table).each(function() {
var targetIndex = $(this).data('tableindex');
if (targetIndex != $(this).index())
{
hasShuffled = true;
var thisRow = $(this).detach();
var numRows = $('tbody tr', table).length;
// Are we trying to be the last row?
if (targetIndex >= numRows)
{
thisRow.appendTo($('tbody', table));
}
// Are we trying to be the first row?
else if (targetIndex == 0)
{
thisRow.prependTo($('tbody', table));
}
// No, we want to be somewhere in the middle!
else
{
thisRow.insertBefore($('tbody tr:eq(' + targetIndex + ')', table));
}
}
});
}
}
}
});

View File

@@ -0,0 +1 @@
$.tablesorter.addWidget({id:"staticRow",format:function(a){if(typeof $(a).data("hasSorted")=="undefined"){$(a).data("hasSorted",true);$("tbody .static",a).each(function(){$(this).data("tableindex",$(this).index())})}else{var b=true;while(b){b=false;$("tbody .static",a).each(function(){var c=$(this).data("tableindex");if(c!=$(this).index()){b=true;var d=$(this).detach();var e=$("tbody tr",a).length;if(c>=e){d.appendTo($("tbody",a))}else if(c==0){d.prependTo($("tbody",a))}else{d.insertBefore($("tbody tr:eq("+c+")",a))}}})}}}})

View File

@@ -0,0 +1,929 @@
/*! Filter widget formatter functions - updated 10/10/2013
* requires: tableSorter 2.7.7+ and jQuery 1.4.3+
*
* uiSpinner (jQuery UI spinner)
* uiSlider (jQuery UI slider)
* uiRange (jQuery UI range slider)
* uiDateCompare (jQuery UI datepicker+compare selector; 1 input)
* uiDatepicker (jQuery UI datepicker; 2 inputs, filter range)
* html5Number (spinner+compare selector)
* html5Range (slider)
* html5Color (color)
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
$.tablesorter = $.tablesorter || {};
$.tablesorter.filterFormatter = {
/**********************\
jQuery UI Spinner
\**********************/
uiSpinner: function($cell, indx, spinnerDef) {
var o = $.extend({
min : 0,
max : 100,
step : 1,
value : 1,
delayed : true,
addToggle : true,
disabled : false,
exactMatch : true,
compare : ''
}, spinnerDef ),
// Add a hidden input to hold the range values
$input = $('<input class="filter" type="hidden">')
.appendTo($cell)
// hidden filter update (.tsfilter) namespace trigger by filter widget
.on('change.tsfilter', function(){
updateSpinner({ value: this.value, delayed: false });
}),
$shcell = [],
c = $cell.closest('table')[0].config,
// this function updates the hidden input and adds the current values to the header cell text
updateSpinner = function(ui) {
var chkd = true, state,
// ui is not undefined on create
v = ui && ui.value && $.tablesorter.utility.formatFloat((ui.value + '').replace(/[><=]/g,'')) || $cell.find('.spinner').val() || o.value;
if (o.addToggle) {
chkd = $cell.find('.toggle').is(':checked');
}
state = o.disabled || !chkd ? 'disable' : 'enable';
$cell.find('.filter')
// add equal to the beginning, so we filter exact numbers
.val( chkd ? (o.compare ? o.compare : o.exactMatch ? '=' : '') + v : '' )
.trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end()
.find('.spinner').spinner(state).val(v);
// update sticky header cell
if ($shcell.length) {
$shcell.find('.spinner').spinner(state).val(v);
if (o.addToggle) {
$shcell.find('.toggle')[0].checked = chkd;
}
}
};
// add callbacks; preserve added callbacks
o.oldcreate = o.create;
o.oldspin = o.spin;
o.create = function(event, ui) {
updateSpinner(); // ui is an empty object on create
if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); }
};
o.spin = function(event, ui) {
updateSpinner(ui);
if (typeof o.oldspin === 'function') { o.oldspin(event, ui); }
};
if (o.addToggle) {
$('<div class="button"><input id="uispinnerbutton' + indx + '" type="checkbox" class="toggle" /><label for="uispinnerbutton' + indx + '"></label></div>')
.appendTo($cell)
.find('.toggle')
.on('change', function(){
updateSpinner();
});
}
// make sure we use parsed data
$cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed');
// add a jQuery UI spinner!
$('<input class="spinner spinner' + indx + '" />')
.val(o.value)
.appendTo($cell)
.spinner(o)
.on('change keyup', function(e){
updateSpinner();
});
// has sticky headers?
c.$table.on('stickyHeadersInit', function(){
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
if (o.addToggle) {
$('<div class="button"><input id="stickyuispinnerbutton' + indx + '" type="checkbox" class="toggle" /><label for="stickyuispinnerbutton' + indx + '"></label></div>')
.appendTo($shcell)
.find('.toggle')
.on('change', function(){
$cell.find('.toggle')[0].checked = this.checked;
updateSpinner();
});
}
// add a jQuery UI spinner!
$('<input class="spinner spinner' + indx + '" />')
.val(o.value)
.appendTo($shcell)
.spinner(o)
.on('change keyup', function(e){
$cell.find('.spinner').val( this.value );
updateSpinner();
});
});
// on reset
c.$table.on('filterReset', function(){
// turn off the toggle checkbox
if (o.addToggle) {
$cell.find('.toggle')[0].checked = false;
}
updateSpinner();
});
updateSpinner();
return $input;
},
/**********************\
jQuery UI Slider
\**********************/
uiSlider: function($cell, indx, sliderDef) {
var o = $.extend({
value : 0,
min : 0,
max : 100,
step : 1,
range : "min",
delayed : true,
valueToHeader : false,
exactMatch : true,
compare : '',
allText : 'all'
}, sliderDef ),
// Add a hidden input to hold the range values
$input = $('<input class="filter" type="hidden">')
.appendTo($cell)
// hidden filter update (.tsfilter) namespace trigger by filter widget
.on('change.tsfilter', function(){
updateSlider({ value: this.value });
}),
$shcell = [],
c = $cell.closest('table')[0].config,
// this function updates the hidden input and adds the current values to the header cell text
updateSlider = function(ui) {
// ui is not undefined on create
var v = typeof ui !== "undefined" ? $.tablesorter.utility.formatFloat((ui.value + '').replace(/[><=]/g,'')) || o.min : o.value,
val = o.compare ? v : v === o.min ? o.allText : v,
result = o.compare + val;
if (o.valueToHeader) {
// add range indication to the header cell above!
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')');
} else {
// add values to the handle data-value attribute so the css tooltip will work properly
$cell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result);
}
// update the hidden input;
// ****** ADD AN EQUAL SIGN TO THE BEGINNING! <- this makes the slide exactly match the number ******
// when the value is at the minimum, clear the hidden input so all rows will be seen
$cell.find('.filter')
.val( ( o.compare ? o.compare + v : v === o.min ? '' : (o.exactMatch ? '=' : '') + v ) )
.trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end()
.find('.slider').slider('value', v);
// update sticky header cell
if ($shcell.length) {
$shcell.find('.slider').slider('value', v);
if (o.valueToHeader) {
$shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(' (' + result + ')');
} else {
$shcell.find('.ui-slider-handle').addClass('value-popup').attr('data-value', result);
}
}
};
$cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed');
// add span to header for value - only works if the line in the updateSlider() function is also un-commented out
if (o.valueToHeader) {
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curvalue" />');
}
// add callbacks; preserve added callbacks
o.oldcreate = o.create;
o.oldslide = o.slide;
o.create = function(event, ui) {
updateSlider(); // ui is an empty object on create
if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); }
};
o.slide = function(event, ui) {
updateSlider(ui);
if (typeof o.oldslide === 'function') { o.oldslide(event, ui); }
};
// add a jQuery UI slider!
$('<div class="slider slider' + indx + '"/>')
.appendTo($cell)
.slider(o);
// on reset
c.$table.on('filterReset', function(){
$cell.find('.slider').slider('value', o.value);
updateSlider();
});
// has sticky headers?
c.$table.on('stickyHeadersInit', function(){
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
// add a jQuery UI slider!
$('<div class="slider slider' + indx + '"/>')
.val(o.value)
.appendTo($shcell)
.slider(o)
.on('change keyup', function(e){
$cell.find('.slider').val( this.value );
updateSlider();
});
});
return $input;
},
/*************************\
jQuery UI Range Slider (2 handles)
\*************************/
uiRange: function($cell, indx, rangeDef) {
var o = $.extend({
values : [0, 100],
min : 0,
max : 100,
range : true,
delayed : true,
valueToHeader : false
}, rangeDef ),
// Add a hidden input to hold the range values
$input = $('<input class="filter" type="hidden">')
.appendTo($cell)
// hidden filter update (.tsfilter) namespace trigger by filter widget
.on('change.tsfilter', function(){
var v = this.value.split(' - ');
if (this.value === '') { v = [ o.min, o.max ]; }
if (v && v[1]) {
updateUiRange({ values: v, delay: false });
}
}),
$shcell = [],
c = $cell.closest('table')[0].config,
// this function updates the hidden input and adds the current values to the header cell text
updateUiRange = function(ui) {
// ui.values are undefined for some reason on create
var val = ui && ui.values || o.values,
result = val[0] + ' - ' + val[1],
// make range an empty string if entire range is covered so the filter row will hide (if set)
range = val[0] === o.min && val[1] === o.max ? '' : result;
if (o.valueToHeader) {
// add range indication to the header cell above (if not using the css method)!
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')');
} else {
// add values to the handle data-value attribute so the css tooltip will work properly
$cell.find('.ui-slider-handle')
.addClass('value-popup')
.eq(0).attr('data-value', val[0]).end() // adding value to data attribute
.eq(1).attr('data-value', val[1]); // value popup shown via css
}
// update the hidden input
$cell.find('.filter').val(range)
.trigger('search', ui && typeof ui.delayed === 'boolean' ? ui.delayed : o.delayed).end()
.find('.range').slider('values', val);
// update sticky header cell
if ($shcell.length) {
$shcell.find('.range').slider('values', val);
if (o.valueToHeader) {
$shcell.closest('thead').find('th[data-column=' + indx + ']').find('.currange').html(' (' + result + ')');
} else {
$shcell.find('.ui-slider-handle')
.addClass('value-popup')
.eq(0).attr('data-value', val[0]).end() // adding value to data attribute
.eq(1).attr('data-value', val[1]); // value popup shown via css
}
}
};
$cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed');
// add span to header for value - only works if the line in the updateUiRange() function is also un-commented out
if (o.valueToHeader) {
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="currange"/>');
}
// add callbacks; preserve added callbacks
o.oldcreate = o.create;
o.oldslide = o.slide;
// add a jQuery UI range slider!
o.create = function(event, ui) {
updateUiRange(); // ui is an empty object on create
if (typeof o.oldcreate === 'function') { o.oldcreate(event, ui); }
};
o.slide = function(event, ui) {
updateUiRange(ui);
if (typeof o.oldslide === 'function') { o.oldslide(event, ui); }
};
$('<div class="range range' + indx +'"/>')
.appendTo($cell)
.slider(o);
// on reset
c.$table.on('filterReset', function(){
$cell.find('.range').slider('values', o.values);
updateUiRange();
});
// has sticky headers?
c.$table.on('stickyHeadersInit', function(){
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
// add a jQuery UI slider!
$('<div class="range range' + indx + '"/>')
.val(o.value)
.appendTo($shcell)
.slider(o)
.on('change keyup', function(e){
$cell.find('.range').val( this.value );
updateUiRange();
});
});
// return the hidden input so the filter widget has a reference to it
return $input;
},
/*************************\
jQuery UI Datepicker compare (1 input)
\*************************/
uiDateCompare: function($cell, indx, defDate) {
var o = $.extend({
defaultDate : '',
cellText : '',
changeMonth : true,
changeYear : true,
numberOfMonths : 1,
compare : '',
compareOptions : false
}, defDate),
$hdr = $cell.closest('thead').find('th[data-column=' + indx + ']'),
// Add a hidden input to hold the range values
$input = $('<input class="dateCompare" type="hidden">')
.appendTo($cell)
// hidden filter update (.tsfilter) namespace trigger by filter widget
.on('change.tsfilter', function(){
var v = this.value;
if (v) {
o.onClose(v);
}
}),
t, l, $shcell = [],
c = $cell.closest('table')[0].config,
// this function updates the hidden input
updateCompare = function(v) {
var date = new Date($cell.find('.date').datepicker('getDate')).getTime();
$cell.find('.compare').val(v);
$cell.find('.dateCompare')
// add equal to the beginning, so we filter exact numbers
.val(v + date)
.trigger('search', o.delayed).end();
// update sticky header cell
if ($shcell.length) {
$shcell.find('.compare').val(v);
}
};
// make sure we're using parsed dates in the search
$hdr.addClass('filter-parsed');
// Add date range picker
if (o.compareOptions) {
l = '<select class="compare">';
for(var myOption in o.compareOptions) {
l += '<option value="' + myOption + '"';
if (myOption === o.compare)
l += ' selected="selected"';
l += '>' + myOption + '</option>';
}
l += '</select>';
$cell.append(l)
.find('.compare')
.on('change', function(){
updateCompare($(this).val());
});
} else if (o.cellText) {
l = '<label>' + o.cellText + '</label>';
$cell.append(l);
}
t = '<input type="text" class="date date' + indx +
'" placeholder="' + ($hdr.data('placeholder') || $hdr.attr('data-placeholder') || '') + '" />';
$(t).appendTo($cell);
// add callbacks; preserve added callbacks
o.oldonClose = o.onClose;
o.onClose = function( selectedDate, ui ) {
var date = new Date($cell.find('.date').datepicker('getDate')).getTime() || '';
var compare = ( $cell.find('.compare').val() || o.compare);
$cell
// update hidden input
.find('.dateCompare').val( compare + date )
.trigger('search').end()
.find('.date')
.datepicker('setDate', selectedDate);
// update sticky header cell
if ($shcell.length) {
$shcell.find('.date').datepicker('setDate', selectedDate);
}
if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); }
};
$cell.find('.date').datepicker(o);
if (o.filterDate) {
$cell.find('.date').datepicker('setDate', o.filterDate);
}
// on reset
c.$table.on('filterReset', function(){
$cell.find('.date').val('').datepicker('option', 'currentText', '' );
if ($shcell.length) {
$shcell.find('.date').val('').datepicker('option', 'currentText', '' );
}
});
// has sticky headers?
c.$table.on('stickyHeadersInit', function(){
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
if (o.compareOptions) {
$shcell.append(l)
.find('.compare')
.on('change', function(){
updateCompare($(this).val());
});
} else if (o.cellText) {
$shcell.append(l);
}
// add a jQuery datepicker!
$shcell
.append(t)
.find('.date')
.datepicker(o);
});
// return the hidden input so the filter widget has a reference to it
return $input.val( o.defaultDate ? o.defaultDate : '' );
},
/*************************\
jQuery UI Datepicker (2 inputs)
\*************************/
uiDatepicker: function($cell, indx, defDate) {
var o = $.extend({
from : '',
to : '',
textFrom : 'from',
textTo : 'to',
changeMonth : true,
changeYear : true,
numberOfMonths : 1
}, defDate),
t, closeTo, closeFrom, $shcell = [],
// Add a hidden input to hold the range values
$input = $('<input class="dateRange" type="hidden">')
.appendTo($cell)
// hidden filter update (.tsfilter) namespace trigger by filter widget
.on('change.tsfilter', function(){
var v = this.value;
if (v.match(' - ')) {
v = v.split(' - ');
$cell.find('.dateTo').val(v[1]);
closeFrom(v[0]);
} else if (v.match('>=')) {
closeFrom( v.replace('>=', '') );
} else if (v.match('<=')) {
closeTo( v.replace('<=', '') );
}
}),
c = $cell.closest('table')[0].config;
// make sure we're using parsed dates in the search
$cell.closest('thead').find('th[data-column=' + indx + ']').addClass('filter-parsed');
// Add date range picker
t = '<label>' + o.textFrom + '</label><input type="text" class="dateFrom" /><label>' + o.textTo + '</label><input type="text" class="dateTo" />';
$(t).appendTo($cell);
// add callbacks; preserve added callbacks
o.oldonClose = o.onClose;
var localfrom = o.defaultDate = o.from || o.defaultDate;
closeFrom = o.onClose = function( selectedDate, ui ) {
var from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '',
to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '',
range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : '');
$cell
.find('.dateRange').val(range)
.trigger('search').end()
.find('.dateTo').datepicker('option', 'minDate', selectedDate ).end()
.find('.dateFrom').val(selectedDate);
// update sticky header cell
if ($shcell.length) {
$shcell
.find('.dateTo').datepicker('option', 'minDate', selectedDate ).end()
.find('.dateFrom').val(selectedDate);
}
if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); }
};
$cell.find('.dateFrom').datepicker(o);
o.defaultDate = o.to || '+7d'; // set to date +7 days from today (if not defined)
closeTo = o.onClose = function( selectedDate, ui ) {
var from = new Date( $cell.find('.dateFrom').datepicker('getDate') ).getTime() || '',
to = new Date( $cell.find('.dateTo').datepicker('getDate') ).getTime() || '',
range = from ? ( to ? from + ' - ' + to : '>=' + from ) : (to ? '<=' + to : '');
$cell
.find('.dateRange').val(range)
.trigger('search').end()
.find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end()
.find('.dateTo').val(selectedDate);
// update sticky header cell
if ($shcell.length) {
$shcell
.find('.dateFrom').datepicker('option', 'maxDate', selectedDate ).end()
.find('.dateTo').val(selectedDate);
}
if (typeof o.oldonClose === 'function') { o.oldonClose(selectedDate, ui); }
};
$cell.find('.dateTo').datepicker(o);
// has sticky headers?
c.$table.on('stickyHeadersInit', function(){
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
$shcell.append(t);
// add a jQuery datepicker!
o.onClose = closeTo;
$shcell.find('.dateTo').datepicker(o);
o.defaultDate = localfrom;
o.onClose = closeFrom;
$shcell.find('.dateFrom').datepicker(o);
});
// on reset
$cell.closest('table').on('filterReset', function(){
$cell.find('.dateFrom, .dateTo').val('').datepicker('option', 'currentText', '' );
if ($shcell.length) {
$shcell.find('.dateFrom, .dateTo').val('').datepicker('option', 'currentText', '' );
}
});
// return the hidden input so the filter widget has a reference to it
return $input.val( o.from ? ( o.to ? o.from + ' - ' + o.to : '>=' + o.from ) : (o.to ? '<=' + o.to : '') );
},
/**********************\
HTML5 Number (spinner)
\**********************/
html5Number : function($cell, indx, def5Num) {
var t, o = $.extend({
value : 0,
min : 0,
max : 100,
step : 1,
delayed : true,
disabled : false,
addToggle : true,
exactMatch : true,
compare : '',
compareOptions : false,
skipTest: false
}, def5Num),
// test browser for HTML5 range support
$number = $('<input type="number" style="visibility:hidden;" value="test">').appendTo($cell),
// test if HTML5 number is supported - from Modernizr
numberSupported = o.skipTest || $number.attr('type') === 'number' && $number.val() !== 'test',
t, l, $shcell = [],
c = $cell.closest('table')[0].config,
updateCompare = function(v) {
var number = $cell.find('.number').val();
$cell.find('.compare').val(v);
$cell.find('input[type=hidden]')
// add equal to the beginning, so we filter exact numbers
.val(v + number)
.trigger('search', o.delayed).end();
// update sticky header cell
if ($shcell.length) {
$shcell.find('.compare').val(v);
}
},
updateNumber = function(v, delayed){
var chkd = o.addToggle ? $cell.find('.toggle').is(':checked') : true;
var compare = ( $cell.find('.compare').val() || o.compare);
$cell.find('input[type=hidden]')
// add equal to the beginning, so we filter exact numbers
.val( !o.addToggle || chkd ? (o.compare ? o.compare : o.exactMatch ? '=' : '') + v : '' )
.val( !o.addToggle || chkd ? compare + v : '' )
.trigger('search', delayed ? delayed : o.delayed).end()
.find('.number').val(v);
if ($cell.find('.number').length) {
$cell.find('.number')[0].disabled = (o.disabled || !chkd);
}
// update sticky header cell
if ($shcell.length) {
$shcell.find('.number').val(v)[0].disabled = (o.disabled || !chkd);
if (o.addToggle) {
$shcell.find('.toggle')[0].checked = chkd;
}
}
};
$number.remove();
if (numberSupported) {
l = o.addToggle ? '<div class="button"><input id="html5button' + indx + '" type="checkbox" class="toggle" /><label for="html5button' + indx + '"></label></div>' : '';
}
if (o.compareOptions) {
l = '<select class="compare">';
for(var myOption in o.compareOptions) {
l += '<option value="' + myOption + '"';
if (myOption === o.compare)
l += ' selected="selected"';
l += '>' + myOption + '</option>';
}
l += '</select>';
$cell.append(l)
.find('.compare')
.on('change', function(){
updateCompare($(this).val());
});
} else {
if (l)
$cell.append(l);
}
if (numberSupported) {
t = '<input class="number" type="number" min="' + o.min + '" max="' + o.max + '" value="' +
o.value + '" step="' + o.step + '" />';
// add HTML5 number (spinner)
$cell
.append(t + '<input type="hidden" />')
.find('.toggle, .number').on('change', function(){
updateNumber( $cell.find('.number').val() );
})
.closest('thead').find('th[data-column=' + indx + ']')
.addClass('filter-parsed') // get exact numbers from column
// on reset
.closest('table').on('filterReset', function(){
// turn off the toggle checkbox
if (o.addToggle) {
$cell.find('.toggle')[0].checked = false;
if ($shcell.length) {
$shcell.find('.toggle')[0].checked = false;
}
}
updateNumber( $cell.find('.number').val() );
});
// hidden filter update (.tsfilter) namespace trigger by filter widget
// FIXME TheSin, Not sure why but this breaks updates
// Commenting out till it's fixed.
//$cell.find('input[type=hidden]').on('change.tsfilter', function(){
// updateNumber( this.value );
//});
// has sticky headers?
c.$table.on('stickyHeadersInit', function(){
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
if (o.compareOptions) {
$shcell.append(l)
.find('.compare')
.on('change', function(){
updateCompare($(this).val());
});
} else {
$shcell.append(l);
}
$shcell
.append(t)
.find('.toggle, .number').on('change', function(){
updateNumber( $shcell.find('.number').val() );
});
updateNumber( $cell.find('.number').val() );
});
updateNumber( $cell.find('.number').val() );
}
return numberSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
},
/**********************\
HTML5 range slider
\**********************/
html5Range : function($cell, indx, def5Range) {
var t, o = $.extend({
value : 0,
min : 0,
max : 100,
step : 1,
delayed : true,
valueToHeader : true,
exactMatch : true,
compare : '',
allText : 'all',
skipTest : false
}, def5Range),
// test browser for HTML5 range support
$range = $('<input type="range" style="visibility:hidden;" value="test">').appendTo($cell),
// test if HTML5 range is supported - from Modernizr (but I left out the method to detect in Safari 2-4)
// see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/inputtypes.js
rangeSupported = o.skipTest || $range.attr('type') === 'range' && $range.val() !== 'test',
$shcell = [],
c = $cell.closest('table')[0].config,
updateRange = function(v, delayed){
/*jshint eqeqeq:false */
v = (v + '').replace(/[<>=]/g,'') || o.min; // hidden input changes may include compare symbols
var t = ' (' + (o.compare ? o.compare + v : v == o.min ? o.allText : v) + ')';
$cell.find('input[type=hidden]')
// add equal to the beginning, so we filter exact numbers
.val( ( o.compare ? o.compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) ) )
//( val == o.min ? '' : val + (o.exactMatch ? '=' : ''))
.trigger('search', delayed ? delayed : o.delayed).end()
.find('.range').val(v);
// or add current value to the header cell, if desired
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t);
// update sticky header cell
if ($shcell.length) {
$shcell.find('.range').val(v);
$shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curvalue').html(t);
}
};
$range.remove();
if (rangeSupported) {
// add HTML5 range
$cell
.html('<input type="hidden"><input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />')
.closest('thead').find('th[data-column=' + indx + ']')
.addClass('filter-parsed') // get exact numbers from column
// add span to header for the current slider value
.find('.tablesorter-header-inner').append('<span class="curvalue" />');
$cell.find('.range').on('change', function(){
updateRange( this.value );
});
// hidden filter update (.tsfilter) namespace trigger by filter widget
$cell.find('input[type=hidden]').on('change.tsfilter', function(){
/*jshint eqeqeq:false */
var v = this.value;
if (v !== this.lastValue) {
this.lastValue = ( o.compare ? o.compare + v : ( v == o.min ? '' : ( o.exactMatch ? '=' : '' ) + v ) );
this.value = this.lastValue;
updateRange( v );
}
});
// has sticky headers?
c.$table.on('stickyHeadersInit', function(){
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx).empty();
$shcell
.html('<input class="range" type="range" min="' + o.min + '" max="' + o.max + '" value="' + o.value + '" />')
.find('.range').on('change', function(){
updateRange( $shcell.find('.range').val() );
});
updateRange( $cell.find('.range').val() );
});
// on reset
$cell.closest('table').on('filterReset', function(){
// just turn off the colorpicker
updateRange(o.value);
});
updateRange( $cell.find('.range').val() );
}
return rangeSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
},
/**********************\
HTML5 Color picker
\**********************/
html5Color: function($cell, indx, defColor) {
var t, o = $.extend({
value : '#000000',
disabled : false,
addToggle : true,
exactMatch : true,
valueToHeader : false,
skipTest : false
}, defColor),
// Add a hidden input to hold the range values
$color = $('<input type="color" style="visibility:hidden;" value="test">').appendTo($cell),
// test if HTML5 color is supported - from Modernizr
colorSupported = o.skipTest || $color.attr('type') === 'color' && $color.val() !== 'test',
$shcell = [],
c = $cell.closest('table')[0].config,
updateColor = function(v){
v = v || o.value;
var chkd = true,
t = ' (' + v + ')';
if (o.addToggle) {
chkd = $cell.find('.toggle').is(':checked');
}
if ($cell.find('.colorpicker').length) {
$cell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd);
}
$cell.find('input[type=hidden]')
.val( chkd ? v + (o.exactMatch ? '=' : '') : '' )
.trigger('search');
if (o.valueToHeader) {
// add current color to the header cell
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t);
} else {
// current color to span in cell
$cell.find('.currentColor').html(t);
}
// update sticky header cell
if ($shcell.length) {
$shcell.find('.colorpicker').val(v)[0].disabled = (o.disabled || !chkd);
if (o.addToggle) {
$shcell.find('.toggle')[0].checked = chkd;
}
if (o.valueToHeader) {
// add current color to the header cell
$shcell.closest('thead').find('th[data-column=' + indx + ']').find('.curcolor').html(t);
} else {
// current color to span in cell
$shcell.find('.currentColor').html(t);
}
}
};
$color.remove();
if (colorSupported) {
// add HTML5 color picker
t = '<div class="color-controls-wrapper">';
t += o.addToggle ? '<div class="button"><input id="colorbutton' + indx + '" type="checkbox" class="toggle" /><label for="colorbutton' + indx + '"></label></div>' : '';
t += '<input type="hidden"><input class="colorpicker" type="color" />';
t += (o.valueToHeader ? '' : '<span class="currentColor">(#000000)</span>') + '</div>';
$cell.html(t);
// add span to header for the current color value - only works if the line in the updateColor() function is also un-commented out
if (o.valueToHeader) {
$cell.closest('thead').find('th[data-column=' + indx + ']').find('.tablesorter-header-inner').append('<span class="curcolor" />');
}
$cell.find('.toggle, .colorpicker').on('change', function(){
updateColor( $cell.find('.colorpicker').val() );
});
// hidden filter update (.tsfilter) namespace trigger by filter widget
$cell.find('input[type=hidden]').on('change.tsfilter', function(){
updateColor( this.value );
});
// on reset
$cell.closest('table').on('filterReset', function(){
// just turn off the colorpicker
$cell.find('.toggle')[0].checked = false;
updateColor( $cell.find('.colorpicker').val() );
});
// has sticky headers?
c.$table.on('stickyHeadersInit', function(){
$shcell = c.widgetOptions.$sticky.find('.tablesorter-filter-row').children().eq(indx);
$shcell
.html(t)
.find('.toggle, .colorpicker').on('change', function(){
updateColor( $shcell.find('.colorpicker').val() );
});
updateColor( $shcell.find('.colorpicker').val() );
});
updateColor( o.value );
}
return colorSupported ? $cell.find('input[type="hidden"]') : $('<input type="search">');
}
};
})(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,34 @@
/*! ISO-8601 date parser
* This parser will work with dates in ISO8601 format
* 2013-02-18T18:18:44+00:00
* Written by Sean Ellingham :https://github.com/seanellingham
* See https://github.com/Mottie/tablesorter/issues/247
*/
/*global jQuery: false */
;(function($){
"use strict";
var iso8601date = /^([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?$/;
$.tablesorter.parser.add({
id : 'iso8601date',
is : function(s) {
return s.match(iso8601date);
},
format : function(s) {
var result = s.match(iso8601date);
if (result) {
var date = new Date(result[1], 0, 1);
if (result[3]) { date.setMonth(result[3] - 1); }
if (result[5]) { date.setDate(result[5]); }
if (result[7]) { date.setHours(result[7]); }
if (result[8]) { date.setMinutes(result[8]); }
if (result[10]) { date.setSeconds(result[10]); }
if (result[12]) { date.setMilliseconds(Number('0.' + result[12]) * 1000); }
return date;
}
return 0;
},
type : 'numeric'
});
})(jQuery);

View File

@@ -0,0 +1,33 @@
/*! Month parser
* Demo: http://jsfiddle.net/Mottie/abkNM/477/
*/
/*jshint jquery:true */
;(function($){
"use strict";
var ts = $.tablesorter;
ts.dates = $.extend({}, ts.dates, {
// *** modify this array to change match the language ***
monthCased : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
});
ts.dates.monthLower = ts.dates.monthCased.join(',').toLocaleLowerCase().split(',');
ts.parser.add({
id: "month",
is: function(){
return false;
},
format: function(s, table) {
var j = -1, c = table.config;
s = c.ignoreCase ? s.toLocaleLowerCase() : s;
$.each(ts.dates[ 'month' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){
if (j < 0 && s.match(v)) { j = i; }
});
// return s (original string) if there isn't a match
// (non-weekdays will sort separately and empty cells will sort as expected)
return j < 0 ? s : j;
},
type: "numeric"
});
})(jQuery);

View File

@@ -0,0 +1,71 @@
/*! Two digit year parser
* Demo: http://jsfiddle.net/Mottie/abkNM/427/
*/
/*jshint jquery:true */
;(function($){
"use strict";
var ts = $.tablesorter,
// Make the date be within +/- range of the 2 digit year
// so if the current year is 2020, and the 2 digit year is 80 (2080 - 2020 > 50), it becomes 1980
// if the 2 digit year is 50 (2050 - 2020 < 50), then it becomes 2050.
range = 50;
ts.regex.date_xxxxyy = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{2})/;
ts.regex.date_yyxxxx = /(\d{2})[\/\s](\d{1,2})[\/\s](\d{1,2})/;
ts.utility.formatDate = function(s, regex, format){
s = s
// replace separators
.replace(/\s+/g," ").replace(/[-.,]/g, "/")
// reformat xx/xx/xx to mm/dd/19yy;
.replace(regex, format);
var d = new Date(s),
y = d.getFullYear(),
now = new Date().getFullYear();
// if date > 50 years old (set range), add 100 years
// this will work when people start using "50" and mean "2050"
while (now - y > range) {
y += 100;
}
return d.setFullYear(y);
};
ts.parser.add({
id: "ddmmyy",
is: function() {
return false;
},
format: function(s) {
// reformat dd/mm/yy to mm/dd/19yy;
return ts.utility.formatDate(s, ts.regex.date_xxxxyy, "$2/$1/19$3");
},
type: "numeric"
});
ts.parser.add({
id: "mmddyy",
is: function() {
return false;
},
format: function(s) {
// reformat mm/dd/yy to mm/dd/19yy
return ts.utility.formatDate(s, ts.regex.date_xxxxyy, "$1/$2/19$3");
},
type: "numeric"
});
ts.parser.add({
id: "yymmdd",
is: function() {
return false;
},
format: function(s) {
// reformat yy/mm/dd to mm/dd/19yy
return ts.utility.formatDate(s, ts.regex.date_yyxxxx, "$2/$3/19$1");
},
type: "numeric"
});
})(jQuery);

View File

@@ -0,0 +1,33 @@
/*! Weekday parser
* Demo: http://jsfiddle.net/Mottie/abkNM/477/
*/
/*jshint jquery:true */
;(function($){
"use strict";
var ts = $.tablesorter;
ts.dates = $.extend({}, ts.dates, {
// *** modify this array to change match the language ***
weekdayCased : [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
});
ts.dates.weekdayLower = ts.dates.weekdayCased.join(',').toLocaleLowerCase().split(',');
ts.parser.add({
id: "weekday",
is: function(){
return false;
},
format: function(s, table) {
var j = -1, c = table.config;
s = c.ignoreCase ? s.toLocaleLowerCase() : s;
$.each(ts.dates[ 'weekday' + (c.ignoreCase ? 'Lower' : 'Cased') ], function(i,v){
if (j < 0 && s.match(v)) { j = i; }
});
// return s (original string) if there isn't a match
// (non-weekdays will sort separately and empty cells will sort as expected)
return j < 0 ? s : j;
},
type: "numeric"
});
})(jQuery);

View File

@@ -0,0 +1,36 @@
/*!
* Extract dates using popular natural language date parsers
*/
/*jshint jquery:true */
;(function($){
"use strict";
/*! Sugar (http://sugarjs.com/dates#comparing_dates)
* demo: http://jsfiddle.net/Mottie/abkNM/551/
*/
$.tablesorter.parser.add({
id: "sugar",
is: function() {
return false;
},
format: function(s) {
return Date.create ? Date.create(s).getTime() || s : new Date(s).getTime() || s;
},
type: "numeric"
});
/*! Datejs (http://www.datejs.com/)
* demo: http://jsfiddle.net/Mottie/abkNM/550/
*/
$.tablesorter.parser.add({
id: "datejs",
is: function() {
return false;
},
format: function(s) {
return Date.parse && Date.parse(s) || s;
},
type: "numeric"
});
})(jQuery);

View File

@@ -0,0 +1,63 @@
/*! Distance parser
* This parser will parser numbers like 5'10" (5 foot 10 inches)
* and 31½ into sortable values.
* Demo: http://jsfiddle.net/Mottie/abkNM/154/
*/
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter;
ts.regex.fractions = /[\u215b\u215c\u215d\u215e\u00bc\u00bd\u00be]/g;
ts.utility.processFractions = function(n, table) {
if (n) {
var t, p = 0;
n = $.trim(n.replace(/\"/,''));
// look for a space in the first part of the number: "10 3/4" and save the "10"
if (/\s/.test(n)) {
p = ts.utility.formatFloat(n.split(' ')[0], table);
// remove stuff to the left of the space
n = $.trim(n.substring(n.indexOf(' '), n.length));
}
// look for a "/" to calculate fractions
if (/\//g.test(n)) {
t = n.split('/');
// turn 3/4 into .75; make sure we don't divide by zero
n = p + parseInt(t[0], 10) / parseInt(t[1] || 1, 10);
// look for fraction symbols
} else if (ts.regex.fractions.test(n)) {
n = p + n.replace(ts.regex.fractions, function(m){
return {
'\u215b' : '.125', // 1/8
'\u215c' : '.375', // 3/8
'\u215d' : '.625', // 5/8
'\u215e' : '.875', // 7/8
'\u00bc' : '.25', // 1/4
'\u00bd' : '.5', // 1/2
'\u00be' : '.75' // 3/4
}[m];
});
}
}
return n || 0;
};
ts.parser.add({
id: 'distance',
is: function() {
// return false so this parser is not auto detected
return false;
},
format: function(s, table) {
if (s === '') { return ''; }
// look for feet symbol = '
// very generic test to catch 1.1', 1 1/2' and 1½'
var d = (/^\s*\S*(\s+\S+)?\s*\'/.test(s)) ? s.split("'") : [0,s],
f = ts.utility.processFractions(d[0], table), // feet
i = ts.utility.processFractions(d[1], table); // inches
return (/[\'\"]/).test(s) ? parseFloat(f) + (parseFloat(i)/12 || 0) : parseFloat(f) + parseFloat(i);
},
type: 'numeric'
});
})(jQuery);

View File

@@ -0,0 +1,74 @@
/*! File Type parser
* When a file type extension is found, the equivalent name is
* prefixed into the parsed data, so sorting occurs in groups
*/
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter,
// basic list from http://en.wikipedia.org/wiki/List_of_file_formats
// To add a custom equivalent, define:
// $.tablesorter.utility.fileTypes.equivalents['xx'] = "A|B|C";
fileTypes = ts.utility.fileTypes = {
// divides filetype extensions in the equivalent list below
separator : '|',
equivalents : {
"3D Image" : "3dm|3ds|dwg|max|obj",
"Audio" : "aif|aac|ape|flac|la|m4a|mid|midi|mp2|mp3|ogg|ra|raw|rm|wav|wma",
"Compressed" : "7z|bin|cab|cbr|gz|gzip|iso|lha|lz|rar|tar|tgz|zip|zipx|zoo",
"Database" : "csv|dat|db|dbf|json|ldb|mdb|myd|pdb|sql|tsv|wdb|wmdb|xlr|xls|xlsx|xml",
"Development" : "asm|c|class|cls|cpp|cc|cs|cxx|cbp|cs|dba|fla|h|java|lua|pl|py|pyc|pyo|sh|sln|r|rb|vb",
"Document" : "doc|docx|odt|ott|pages|pdf|rtf|tex|wpd|wps|wrd|wri",
"Executable" : "apk|app|com|exe|gadget|lnk|msi",
"Fonts" : "eot|fnt|fon|otf|ttf|woff",
"Icons" : "ani|cur|icns|ico",
"Images" : "bmp|gif|jpg|jpeg|jpe|jp2|pic|png|psd|tga|tif|tiff|wmf|webp",
"Presentation" : "pps|ppt",
"Published" : "chp|epub|lit|pub|ppp|fm|mobi",
"Script" : "as|bat|cgi|cmd|jar|js|lua|scpt|scptd|sh|vbs|vb|wsf",
"Styles" : "css|less|sass",
"Text" : "info|log|md|markdown|nfo|tex|text|txt",
"Vectors" : "awg|ai|eps|cdr|ps|svg",
"Video" : "asf|avi|flv|m4v|mkv|mov|mp4|mpe|mpeg|mpg|ogg|rm|rv|swf|vob|wmv",
"Web" : "asp|aspx|cer|cfm|htm|html|php|url|xhtml"
}
};
ts.parser.add({
id: 'filetype',
is: function() {
return false;
},
format: function(s, table) {
var t,
c = table.config,
wo = c.widgetOptions,
i = s.lastIndexOf('.'),
sep = fileTypes.separator,
m = fileTypes.matching,
types = fileTypes.equivalents;
if (!m) {
// make a string to "quick" match the existing equivalents
var t = [];
$.each(types, function(i,v){
t.push(v);
});
m = fileTypes.matching = sep + t.join(sep) + sep;
}
if (i >= 0) {
t = sep + s.substring(i + 1, s.length) + sep;
if (m.indexOf(t) >= 0) {
for (i in types) {
if ((sep + types[i] + sep).indexOf(t) >= 0) {
return i + (wo.group_separator ? wo.group_separator : '-') + s;
}
}
}
}
return s;
},
type: 'text'
});
})(jQuery);

View File

@@ -0,0 +1,48 @@
/*! Title parser
* This parser will remove "The", "A" and "An" from the beginning of a book
* or movie title, so it sorts by the second word or number
* Demo: http://jsfiddle.net/Mottie/abkNM/5/
*/
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter;
// basic list from http://en.wikipedia.org/wiki/Article_%28grammar%29
ts.ignoreArticles = {
"en" : "the, a, an",
"de" : "der, die, das, des, dem, den, ein, eine, einer, eines, einem, einen",
"nl" : "de, het, de, een",
"es" : "el, la, lo, los, las, un, una, unos, unas",
"pt" : "o, a, os, as, um, uma, uns, umas",
"fr" : "le, la, l'_, les, un, une, des",
"it" : "il, lo, la, l'_, i, gli, le, un', uno, una, un",
"hu" : "a, az, egy"
};
// To add a custom parser, define:
// $.tablesorter.ignoreArticles['xx'] = "A, B, C";
// and then set the language id 'xx' in the headers option
// ignoreArticles : 'xx'
ts.parser.add({
id: 'ignoreArticles',
is: function() {
return false;
},
format: function(s, table, cell, cellIndex) {
var c = table.config, art, lang;
if ( !(c.headers && c.headers[cellIndex] && c.headers[cellIndex].ignoreArticlesRegex) ) {
// initialize - save regex in c.headers[cellIndex].ignoreArticles
if (!c.headers) { c.headers = {}; }
if (!c.headers[cellIndex]) { c.headers[cellIndex] = {}; }
lang = ts.utility.getData(c.$headers.eq(cellIndex), c.headers[cellIndex], 'ignoreArticles');
art = (ts.ignoreArticles[lang] || "the, a, an" ) + "";
c.headers[cellIndex].ignoreArticlesRegex = new RegExp('^(' + $.trim( art.split(/\s*\,\s*/).join('\\s|') + "\\s" ).replace("_\\s","") + ')', 'i');
}
return (s || '').replace(c.headers[cellIndex].ignoreArticlesRegex, '');
},
type: 'text'
});
})(jQuery);

View File

@@ -0,0 +1,78 @@
/*! input & select parsers for jQuery 1.7+ & tablesorter 2.7.11+
* Demo: http://mottie.github.com/tablesorter/docs/example-widget-grouping.html
*/
/*jshint browser: true, jquery:true, unused:false */
;(function($){
"use strict";
var resort = true, // resort table after update
updateServer = function(event, $table, $input){
// do something here to update your server, if needed
// event = change event object
// $table = jQuery object of the table that was just updated
// $input = jQuery object of the input or select that was modified
};
// Custom parser for parsing input values
// updated dynamically using the "change" function below
$.tablesorter.parser.add({
id: "inputs",
is: function(){
return false;
},
format: function(s, table, cell) {
return $(cell).find('input').val() || s;
},
type: "text"
});
// Custom parser for including checkbox status if using the grouping widget
// updated dynamically using the "change" function below
$.tablesorter.parser.add({
id: "checkbox",
is: function(){
return false;
},
format: function(s, table, cell) {
// using plain language here because this is what is shown in the group headers
// change it as desired
var $c = $(cell).find('input');
return $c.length ? $c.is(':checked') ? 'checked' : 'unchecked' : s;
},
type: "text"
});
// Custom parser which returns the currently selected options
// updated dynamically using the "change" function below
$.tablesorter.parser.add({
id: "select",
is: function(){
return false;
},
format: function(s, table, cell) {
return $(cell).find('select').val() || s;
},
type: "text"
});
// update select and all input types in the tablesorter cache when the change event fires.
// This method only works with jQuery 1.7+
// you can change it to use delegate (v1.4.3+) or live (v1.3+) as desired
// if this code interferes somehow, target the specific table $('#mytable'), instead of $('table')
$(window).load(function(){
// this flag prevents the updateCell event from being spammed
// it happens when you modify input text and hit enter
var alreadyUpdating = false;
$('table').find('tbody').on('change', 'select, input', function(e){
if (!alreadyUpdating) {
var $tar = $(e.target),
$table = $tar.closest('table');
alreadyUpdating = true;
$table.trigger('updateCell', [ $tar.closest('td'), resort ]);
updateServer(e, $table, $tar);
setTimeout(function(){ alreadyUpdating = false; }, 10);
}
});
});
})(jQuery);

View File

@@ -0,0 +1,76 @@
/*! IPv6 Address parser (WIP)
* IPv6 Address (ffff:0000:0000:0000:0000:0000:0000:0000)
* needs to support short versions like "::8" or "1:2::7:8"
* and "::00:192.168.10.184" (embedded IPv4 address)
* see http://www.intermapper.com/support/tools/IPV6-Validator.aspx
*/
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter;
$.extend( ts.regex, {}, {
ipv4Validate : /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/,
ipv4Extract : /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/,
// simplified regex from http://www.intermapper.com/support/tools/IPV6-Validator.aspx
// (specifically from http://download.dartware.com/thirdparty/ipv6validator.js)
ipv6Validate : /^\s*((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/i
});
ts.parser.add({
id: "ipv6Address",
is: function(s) {
return ts.regex.ipv6Validate.test(s);
},
format: function(address, table) {
// code modified from http://forrst.com/posts/JS_Expand_Abbreviated_IPv6_Addresses-1OR
var i, t, sides, groups, groupsPresent,
hex = table ? (typeof table === "boolean" ? table : table && table.config.ipv6HexFormat || false) : false,
fullAddress = '',
expandedAddress = '',
validGroupCount = 8,
validGroupSize = 4;
// remove any extra spaces
address = address.replace(/\s*/g, '');
// look for embedded ipv4
if (ts.regex.ipv4Validate.test(address)) {
groups = address.match(ts.regex.ipv4Extract);
t = '';
for (i = 1; i < groups.length; i++){
t += ('00' + (parseInt(groups[i], 10).toString(16)) ).slice(-2) + ( i === 2 ? ':' : '' );
}
address = address.replace( ts.regex.ipv4Extract, t );
}
if (address.indexOf("::") == -1) {
// All eight groups are present
fullAddress = address;
} else {
// Consecutive groups of zeroes have been collapsed with "::".
sides = address.split("::");
groupsPresent = 0;
for (i = 0; i < sides.length; i++) {
groupsPresent += sides[i].split(":").length;
}
fullAddress += sides[0] + ":";
for (i = 0; i < validGroupCount - groupsPresent; i++) {
fullAddress += "0000:";
}
fullAddress += sides[1];
}
groups = fullAddress.split(":");
for (i = 0; i < validGroupCount; i++) {
// it's fastest & easiest for tablesorter to sort decimal values (vs hex)
groups[i] = hex ? ('0000' + groups[i]).slice(-4) :
('00000' + (parseInt(groups[i], 16) || 0)).slice(-5);
expandedAddress += ( i != validGroupCount-1) ? groups[i] + ':' : groups[i];
}
return hex ? expandedAddress : expandedAddress.replace(/:/g, '');
},
// uses natural sort hex compare
type: "numeric"
});
})(window.jQuery || window.Zepto);

View File

@@ -0,0 +1,77 @@
/*! Metric parser
* Demo: http://jsfiddle.net/Mottie/abkNM/382/
* Set the metric name in the header (defaults to "m|meter"), e.g.
* <th data-metric-name="b|byte">HDD Size</th>
* <th data-metric-name="m|meter">Distance</th>
*/
/*jshint jquery:true */
;(function($){
"use strict";
var prefixes = {
// "prefix" : [ base 10, base 2 ]
// skipping IEEE 1541 defined prefixes: kibibyte, mebibyte, etc, for now.
"Y|Yotta|yotta" : [ 1e24, Math.pow(1024, 8) ], // 1024^8
"Z|Zetta|zetta" : [ 1e21, Math.pow(1024, 7) ], // 1024^7
"E|Exa|exa" : [ 1e18, Math.pow(1024, 6) ], // 1024^6
"P|Peta|peta" : [ 1e15, Math.pow(1024, 5) ], // 1024^5
"T|Tera|tera" : [ 1e12, Math.pow(1024, 4) ], // 1024^4
"G|Giga|giga" : [ 1e9, Math.pow(1024, 3) ], // 1024^3
"M|Mega|mega" : [ 1e6, Math.pow(1024, 2) ], // 1024^2
"k|Kilo|kilo" : [ 1e3, 1024 ], // 1024
// prefixes below here are rarely, if ever, used in binary
"h|hecto" : [ 1e2, 1e2 ],
"da|deka" : [ 1e1, 1e1 ],
"d|deci" : [ 1e-1, 1e-1 ],
"c|centi" : [ 1e-2, 1e-2],
"m|milli" : [ 1e-3, 1e-3 ],
"µ|micro" : [ 1e-6, 1e-6 ],
"n|nano" : [ 1e-9, 1e-9 ],
"p|pico" : [ 1e-12, 1e-12 ],
"f|femto" : [ 1e-15, 1e-15 ],
"a|atto" : [ 1e-18, 1e-18 ],
"z|zepto" : [ 1e-21, 1e-21 ],
"y|yocto" : [ 1e-24, 1e-24 ]
},
// the \\d+ will not catch digits with spaces, commas or decimals; so use the value from n instead
RegLong = "(\\d+)(\\s+)?([Zz]etta|[Ee]xa|[Pp]eta|[Tt]era|[Gg]iga|[Mm]ega|kilo|hecto|deka|deci|centi|milli|micro|nano|pico|femto|atto|zepto|yocto)(",
RegAbbr = "(\\d+)(\\s+)?(Z|E|P|T|G|M|k|h|da|d|c|m|µ|n|p|f|a|z|y)(";
$.tablesorter.addParser({
id: 'metric',
is: function() {
return false;
},
format: function(s, table, cell, cellIndex) {
var v = 'm|meter',
b, t,
// process number here to get a numerical format (us or eu)
n = $.tablesorter.utility.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table),
$t = table.config.$headers.filter('[data-column="' + cellIndex + '"]'),
m = $t.data('metric');
if (!m) {
// stored values
t = ($t.attr('data-metric-name') || v).split('|');
m = [ t[1] || t[0].substring(1), t[0] ];
m[2] = new RegExp(RegLong + m[0] + "|" + m[1] + ")");
m[3] = new RegExp(RegAbbr + m[1] + ")");
$t.data('metric', m);
}
// find match to full name or abbreviation
t = s.match(m[2]) || s.match(m[3]);
if (t) {
for (v in prefixes) {
if (t[3].match(v)) {
// exception when using binary prefix
// change base for binary use
b = /^[b|bit|byte|o|octet]/.test(t[4]) ? 1 : 0;
return n * prefixes[v][b];
}
}
}
return n;
},
type: 'numeric'
});
})(jQuery);

View File

@@ -0,0 +1,441 @@
/*! Build Table widget * by Rob Garrison */
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter = $.tablesorter || {},
// build a table from data (requires existing <table> tag)
// data.header contains an array of header titles
// data.rows contains an array of rows which contains an array of cells
bt = ts.build.table = function(tar, c){
// add table if one doesn't exist
var $tbl = tar.tagName === 'TABLE' ? $(tar) : $('<table>').appendTo(tar),
table = $tbl[0],
wo = c.widgetOptions = $.extend( true, {}, bt.defaults, c.widgetOptions ),
p = wo.build_processing,
typ = wo.build_type,
d = wo.build_source || c.data,
// determine type: html, json, array, csv, object
runType = function(d){
var t = $.type(d),
jq = d instanceof jQuery;
// run any processing if set
if ( typeof p === 'function' ) { d = p(d, wo); }
// store processed data in table.config.data
c.data = d;
// String (html or unprocessed json) or jQuery object
if ( jq || t === 'string' ) {
// look for </tr> closing tag, then we have an HTML string
if ( jq || /<\s*\/tr\s*>/.test(d) ) {
return bt.html( table, d, wo );
}
try {
d = $.parseJSON(d);
if (d) {
// valid JSON!
return bt.object( table, d, wo );
}
} catch(ignore) {}
// fall through in case it's a csv string
}
// Array
if (t === 'array' || t === 'string' || typ === 'array' || typ === 'csv') {
// build table using an array (csv & array combined script)
return bt.csv( table, d, wo );
}
// if we got here, it's an object, or nothing
return bt.object( table, d, wo );
};
// store config
table.config = c;
// even if wo.build_type is undefined, we can try to figure out the type
if ( !ts.build.table.hasOwnProperty(typ) && typ !== '' ) {
if (c.debug) { ts.log('aborting build table widget, incorrect build type'); }
return false;
}
if ( d instanceof jQuery ) {
// get data from within a jQuery object (csv)
runType( $.trim( d.html() ) );
} else if ( d && ( d.hasOwnProperty('url') || typ === 'json' ) ) {
// load data via ajax
$.ajax( wo.build_source )
.done(function(data) {
runType(data);
})
.fail(function( jqXHR, textStatus, errorThrown) {
if (c.debug) { ts.log('aborting build table widget, failed ajax load'); }
$tbl.html('<tr><td class="error">' + jqXHR.status + ' ' + textStatus + '</td></tr>');
});
} else {
runType(d);
}
};
bt.defaults = {
// *** build widget core ***
build_type : '', // array, csv, object, json, html
build_source : '', // array, object, jQuery Object or ajaxObject { url: '', dataType: 'json' },
build_processing : null, // function that returns a useable build_type (e.g. string to array)
build_complete : 'tablesorter-build-complete', // triggered event when build completes
// *** CSV & Array ***
build_headers : {
rows : 1, // Number of header rows from the csv
classes : [], // Header classes to apply to cells
text : [], // Header cell text
widths : [] // set header cell widths (set in colgroup)
},
build_footers : {
rows : 1, // Number of header rows from the csv
classes : [], // Footer classes to apply to cells
text : [] // Footer cell text
},
build_numbers : {
addColumn : false, // include row numbering column?
sortable : false // make column sortable?
},
// *** CSV only options ***
build_csvStartLine : 0, // line within the csv to start adding to table
build_csvSeparator : ",", // csv separator
// *** build object options ***
build_objectRowKey : 'rows', // object key containing table rows
build_objectCellKey : 'cells', // object key containing table cells (within the rows object)
build_objectHeaderKey : 'headers', // object key containing table headers
build_objectFooterKey : 'footers' // object key containing table footers
};
bt.create = {
colgroup : function(widths) {
var t = '';
// add colgroup if widths set
if (widths && widths.length) {
t += '<colgroup>';
$.each(widths, function(i, w){
t += '<col' + ( w ? ' style="width:' + w + '"' : '' ) + '>';
});
t += '</colgroup>';
}
return t;
},
// d = cell data; typ = 'th' or 'td'; first = save widths from first header row only
cell : function(d, wo, typ, col, first){
var j, $td,
$col = first ? $('<col>') : '',
cls = wo.build_headers.classes,
cw = wo.build_headers.widths;
// d is just an array
if (/string|number/.test(typeof d)) {
// add classes from options, but not text
$td = $('<' + typ + (cls && cls[col] ? ' class="' + cls[col] + '"' : '') + '>' + d + '</' + typ + '>');
// get widths from options (only from first row)
if (first && cw && cw[col]) {
$col.width(cw[col] || '');
}
} else {
// assume we have an object
$td = $('<' + typ + '>');
for (j in d) {
if (d.hasOwnProperty(j)){
if (j === 'text' || j === 'html') {
$td[j]( d[j] );
} else if (first && j === 'width') {
// set column width, but only from first row
$col.width(d[j] || '');
} else {
$td.attr(j, d[j]);
}
}
}
}
return [ $td, $col ];
},
// h1 = header text from data
header : function(h1, wo){
var h2 = wo.build_headers.text,
cls = wo.build_headers.classes,
t = '<tr>' + (wo.build_numbers.addColumn ? '<th' + (wo.build_numbers.sortable ? '' :
' class="sorter-false"') + '>' + wo.build_numbers.addColumn + '</th>' : '');
$.each(h1, function(i, h) {
t += '<th' + (cls && cls[i] ? ' class="' + cls[i] + '"' : '') + '>' +
(h2 && h2[i] ? h2[i] : h) + '</th>';
});
return t + '</tr>';
},
rows : function(items, txt, c, wo, num, ftr){
var h = (ftr ? 'th' : 'td'),
t = '<tr>' + (wo.build_numbers.addColumn ? '<' + h + '>' + (ftr ? '' : num) + '</' + h + '>' : '');
$.each(items, function(i, item) {
t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' +
(ftr && txt && txt.length && txt[i] ? txt[i] : item) + '</' + h + '>';
});
return t + '</tr>';
}
};
bt.complete = function(table, wo){
$(table).trigger(wo.build_complete);
ts.setup(table, table.config);
};
/* ==== Array example ====
[
[ "header1", "header2", ... "headerN" ],
[ "row1cell1", "row1cell2", ... "row1cellN" ],
[ "row2cell1", "row2cell2", ... "row2cellN" ],
...
[ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
]
*/
bt.array = function(table, data, wo) {
return bt.csv(table, data, wo);
};
/* ==== CSV example ====
ID, Name, Age, Date
A42b, Parker, 28, "Jul 6, 2006 8:14 AM"
A255, Hood, 33, "Dec 10, 2002 5:14 AM"
A33, Kent, 18, "Jan 12, 2003 11:14 AM"
A1, Franklin, 45, "Jan 18, 2001 9:12 AM"
A102, Evans, 22, "Jan 18, 2007 9:12 AM"
A42a, Everet, 22, "Jan 18, 2007 9:12 AM"
ID, Name, Age, Date
*/
// Adapted & modified from csvToTable.js by Steve Sobel
// MIT license: https://code.google.com/p/jquerycsvtotable/
bt.csv = function(table, data, wo) {
var c, h,
csv = wo.build_type === 'csv' || typeof data === 'string',
$t = $(table),
lines = csv ? data.replace('\r','').split('\n') : data,
len = lines.length,
printedLines = 0,
infooter = false,
r = wo.build_headers.rows + (csv ? wo.build_csvStartLine : 0),
f = wo.build_footers.rows,
headerCount = 0,
error = '',
items,
tableHTML = bt.create.colgroup( wo.build_headers.widths ) + '<thead>';
$.each(lines, function(n, line) {
if ( n >= len - f ) { infooter = true; }
// build header
if ( (csv ? n >= wo.build_csvStartLine : true) && ( n < r ) ) {
h = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line;
headerCount = h.length;
tableHTML += bt.create.header(h, wo);
} else if ( n >= r ) {
// build tbody & tfoot rows
if (n === r) {
tableHTML += '</thead><tbody>';
}
items = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line;
if (infooter && f > 0) {
tableHTML += (n === len - f ? '</tbody><tfoot>' : '') +
(n === len ? '</tfoot>' : '');
}
if (items.length > 1) {
printedLines++;
if ( items.length !== headerCount ) {
error += 'error on line ' + n + ': Item count (' + items.length +
') does not match header count (' + headerCount + ') \n';
}
c = infooter ? wo.build_footers.classes : '';
tableHTML += bt.create.rows(items, wo.build_footers.text, c, wo, printedLines, infooter);
}
}
});
tableHTML += (f > 0 ? '' : '</tbody>');
if (error) {
$t.html(error);
} else {
$t.html(tableHTML);
bt.complete(table, wo);
}
};
// CSV Parser by Brian Huisman (http://www.greywyvern.com/?post=258)
bt.splitCSV = function(str, sep) {
var x, tl,
thisCSV = $.trim(str).split(sep = sep || ",");
for ( x = thisCSV.length - 1; x >= 0; x-- ) {
if ( thisCSV[x].replace(/\"\s+$/, '"').charAt(thisCSV[x].length - 1) === '"' ) {
if ( (tl = thisCSV[x].replace(/^\s+\"/, '"')).length > 1 && tl.charAt(0) === '"' ) {
thisCSV[x] = thisCSV[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
} else if (x) {
thisCSV.splice(x - 1, 2, [thisCSV[x - 1], thisCSV[x]].join(sep));
} else {
thisCSV = thisCSV.shift().split(sep).concat(thisCSV);
}
} else {
thisCSV[x].replace(/""/g, '"');
}
}
return thisCSV;
};
// data may be a jQuery object after processing
bt.html = function(table, data, wo) {
var $t = $(table);
if ( data instanceof jQuery ) {
$t.empty().append(data);
} else {
$t.html(data);
}
bt.complete(table, wo);
};
/* ==== Object example ====
data : {
headers : [
[
{ text: 'First Name', class: 'fname', width: '20%' }, // row 1 cell 1
'Last Name',
{ text: 'Age', class: 'age', 'data-sorter' : false },
'Total',
{ text: 'Discount', class : 'sorter-false' },
{ text: 'Date', class : 'date' } // row 1 cell 6
]
],
footers : 'clone', // clone headers or assign array like headers
rows : [
// TBODY 1
[ 'Peter', 'Parker', 28, '$9.99', '20%', 'Jul 6, 2006 8:14 AM' ], // row 1
[ 'John', 'Hood', 33, '$19.99', '25%', 'Dec 10, 2002 5:14 AM' ], // row 2
[ 'Clark', 'Kent', 18, '$15.89', '44%', 'Jan 12, 2003 11:14 AM' ], // row 3
// TBODY 2
{ newTbody: true, class: 'tablesorter-infoOnly' },
{ cells : [ { text: 'Info Row', colSpan: 6 } ] }, // row 4
// TBODY 3
{ newTbody: true },
[ 'Bruce', 'Evans', 22, '$13.19', '11%', 'Jan 18, 2007 9:12 AM' ], // row 5
[ 'Brice', 'Almighty', 45, '$153.19', '44%', 'Jan 18, 2001 9:12 AM' ], // row 6
{ class: 'specialRow', // row 7
cells: [
{ text: 'Fred', class: 'fname' },
{ text: 'Smith', class: 'lname' },
{ text: 18, class: 'age', 'data-info': 'fake ID!, he is really 16' },
{ text: '$22.44', class: 'total' },
{ text: '8%', class: 'discount' },
{ text: 'Aug 20, 2012 10:15 AM', class: 'date' }
],
'data-info' : 'This row likes turtles'
}
]
}
*/
bt.object = function(table, data, wo) {
// "rows"
var j, l, t, $c, $t, $tb, $tr,
c = table.config,
kh = wo.build_objectHeaderKey,
kr = wo.build_objectRowKey,
h = data.hasOwnProperty(kh) && !$.isEmptyObject(data.kh) ? data.kh : data.hasOwnProperty('headers') ? data.headers : false,
r = data.hasOwnProperty(kr) && !$.isEmptyObject(data.kr) ? data.kr : data.hasOwnProperty('rows') ? data.rows : false;
if (!h || !r || h.length === 0 || r.length === 0) {
if (c.debug) { ts.log('aborting build table widget, missing data for object build'); }
return false;
}
$c = $('<colgroup>');
$t = $('<table><thead/></table>');
// Build thead
// h = [ ['headerRow1Cell1', 'headerRow1Cell2', ... 'headerRow1CellN' ], ['headerRow2Cell1', ... ] ]
// or h = [ [ { text: 'firstCell', class: 'fc', width: '20%' }, ..., { text: 'last Cell' } ], [ /* second row */ ] ]
$.each(h, function(i, d){
$tr = $('<tr>').appendTo( $t.find('thead') );
l = d.length; // header row
for ( j = 0; j < l; j++ ) {
// cell(cellData, widgetOptions, 'th', first row)
t = bt.create.cell(d[j], wo, 'th', j, i === 0);
if (t[0] && t[0].length) { t[0].appendTo( $tr ); } // add cell
if (i === 0 && t[1]) { t[1].appendTo( $c ); } // add col to colgroup
}
});
if ($c.find('col[style]').length) {
// add colgroup if it contains col elements
$t.prepend( $c );
}
$tb = $('<tbody>');
// Build tbody
$.each(r, function(i, d){
t = $.type(d) === 'object';
// add new tbody
if (t && d.newTbody) {
$tb = $('<tbody>').appendTo( $t );
for (j in d) {
if (d.hasOwnProperty(j) && j !== 'newTbody'){
$tb.attr(j, d[j]);
}
}
} else {
if (i === 0) {
// add tbody, if the first item in the object isn't a call for a new tbody
$tb.appendTo( $t );
}
$tr = $('<tr>').appendTo( $tb );
if (t) {
// row defined by object
for (j in d) {
if (d.hasOwnProperty(j) && j !== wo.build_objectCellKey){
$tr.attr(j, d[j]);
}
}
if (d.hasOwnProperty(wo.build_objectCellKey)) {
// cells contains each cell info
d = d.cells;
}
}
l = d.length;
for ( j = 0; j < l; j++ ) {
// cell(cellData, widgetOptions, 'td')
$c = bt.create.cell(d[j], wo, 'td', j);
if ($c[0] && $c[0].length) { $c[0].appendTo( $tr ); } // add cell
}
}
});
// add footer
if (data.hasOwnProperty(wo.build_objectFooterKey)) {
t = data[wo.build_objectFooterKey];
if (t === 'clone') {
$c = $t.find('thead').html();
$t.append('<tfoot>' + $c + '</tfoot>');
} else {
$c = $('<tfoot>').appendTo( $t );
$.each(t, function(i, d) {
$tr = $('<tr>').appendTo( $c );
l = d.length; // footer cells
for ( j = 0; j < l; j++ ) {
// cell(cellData, widgetOptions, 'th')
$tb = bt.create.cell(d[j], wo, 'th', j);
if ($tb[0] && $tb[0].length) { $tb[0].appendTo( $tr ); } // add cell
}
});
}
}
$(table).html( $t.html() );
bt.complete(table, wo);
};
bt.ajax = bt.json = function(table, data, wo) {
return bt.object(table, data, wo);
};
})(jQuery);

View File

@@ -0,0 +1,77 @@
/*! tablesorter Editable Content widget - updated 4/12/2013
* Requires tablesorter v2.8+ and jQuery 1.7+
* by Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
$.tablesorter.widget.add({
id: 'editable',
options : {
editable_columns : [],
editable_enterToAccept : true,
editable_autoResort : false,
editable_noEdit : 'no-edit',
editable_editComplete : 'editComplete'
},
init: function(table, thisWidget, c, wo){
if (!wo.editable_columns.length) { return; }
var $t, cols = [];
$.each(wo.editable_columns, function(i, col){
cols.push('td:nth-child(' + (col + 1) + ')');
});
// IE does not allow making TR/TH/TD cells directly editable (issue #404)
// so add a div or span inside ( it's faster than using wrapInner() )
c.$tbodies.find( cols.join(',') ).not('.' + wo.editable_noEdit).each(function(){
// test for children, if they exist, then make the children editable
$t = $(this);
( $t.children().length ? $t.children() : $t ).prop('contenteditable', true);
});
c.$tbodies
.on('mouseleave.tseditable', function(){
if (c.$table.data('contentFocused')) {
$(':focus').trigger('blur');
}
})
.on('focus.tseditable', '[contenteditable]', function(){
c.$table.data('contentFocused', true);
var $this = $(this), v = $this.html();
if (wo.editable_enterToAccept) {
// prevent enter from adding into the content
$this.on('keydown.tseditable', function(e){
if (e.which === 13) {
e.preventDefault();
}
});
}
$this.data({ before : v, original: v });
})
.on('blur focusout keyup '.split(' ').join('.tseditable '), '[contenteditable]', function(e){
if (!c.$table.data('contentFocused')) { return; }
var $this = $(e.target), t;
if (e.which === 27) {
// user cancelled
$this.html( $this.data('original') ).trigger('blur.tseditable');
c.$table.data('contentFocused', false);
return false;
}
t = e.type !== 'keyup' || (wo.editable_enterToAccept && e.which === 13);
// change if new or user hits enter (if option set)
if ($this.data('before') !== $this.html() || t) {
$this.data('before', $this.html()).trigger('change');
if (t) {
c.$table
.data('contentFocused', false)
.trigger('updateCell', [ $this.closest('td'), wo.editable_autoResort, function(table){
$this.trigger( wo.editable_editComplete );
} ]);
$this.trigger('blur.tseditable');
}
}
});
}
});
})(jQuery);

View File

@@ -0,0 +1,159 @@
/*! tablesorter Grouping widget - updated 10/18/2013
* Requires tablesorter v2.8+ and jQuery 1.7+
* by Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter;
ts.grouping = {
number : function(c, $col, txt, num, group){
var t, w;
if (num > 1 && txt !== '') {
if ($col.hasClass(ts.css.sortAsc)) {
t = Math.floor(parseFloat(txt)/num) * num;
return t > parseFloat(group || 0) ? t : parseFloat(group || 0);
} else {
t = Math.ceil(parseFloat(txt)/num) * num;
return t < parseFloat(group || num) - t ? parseFloat(group || num) - t : t;
}
} else {
w = (txt + '').match(/\d+/g);
return w && w.length >= num ? w[num - 1] : txt || '';
}
},
separator : function(c, $col, txt, num){
var wo = c.widgetOptions,
w = (txt + '').split(wo.group_separator);
return $.trim(w && num > 0 && w.length >= num ? w[(num || 1) - 1] : '');
},
word : function(c, $col, txt, num){
var w = (txt + ' ').match(/\w+/g);
return w && w.length >= num ? w[num - 1] : txt || '';
},
letter : function(c, $col, txt, num){
return txt ? (txt + ' ').substring(0, num) : '';
},
date : function(c, $col, txt, part){
var wo = c.widgetOptions,
t = new Date(txt || ''),
t2 = t.getHours();
return part === 'year' ? t.getFullYear() :
part === 'month' ? wo.group_months[t.getMonth()] :
part === 'day' ? wo.group_months[t.getMonth()] + ' ' + t.getDate() :
part === 'week' ? wo.group_week[t.getDay()] :
part === 'time' ? ('00' + (t2 > 12 ? t2 - 12 : t2 === 0 ? t2 + 12 : t2)).slice(-2) + ':' +
('00' + t.getMinutes()).slice(-2) + ' ' + ('00' + wo.group_time[t2 >= 12 ? 1 : 0]).slice(-2) :
t.toString();
}
};
ts.widget.add({
id: 'group',
// run AFTER the zebra widget, so the header rows do not get zebra striping
priority: 100,
options: {
group_collapsible : true, // make the group header clickable and collapse the rows below it.
group_collapsed : false, // start with all groups collapsed
group_count : ' ({num})', // if not false, the "{num}" string is replaced with the number of rows in the group
group_separator : '-', // group name separator; used when group-separator-# class is used.
group_formatter : null, // function(txt, col, table, c, wo) { return txt; }
group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels
group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work
// change these default date names based on your language preferences
group_months : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
group_week : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
group_time : [ 'AM', 'PM' ]
},
init: function(table, thisWidget, c, wo){
if (wo.group_collapsible) {
// .on() requires jQuery 1.7+
c.$table.on('click toggleGroup', 'tr.group-header', function(e){
// use shift-click to toggle ALL groups
if (e.type === 'click' && e.shiftKey) {
$(this).siblings('.group-header').trigger('toggleGroup');
}
$(this).toggleClass('collapsed');
// nextUntil requires jQuery 1.4+
$(this).nextUntil('tr.group-header').toggleClass('group-hidden', $(this).hasClass('collapsed') );
e.stopPropagation();
});
}
},
format: function(table, c, wo) {
var j, k, curr, $tr, t, t2, time, n,
group = '',
col = c.sortList[0] ? c.sortList[0][0] : -1;
c.$table
.find('tr.group-hidden').removeClass('group-hidden').end()
.find('tr.group-header').remove();
if (wo.group_collapsible) {
// clear pager saved spacer height (in case the rows are collapsed)
$.data(table, 'pagerSavedHeight', 0);
}
if (col >= 0 && !c.$headers.eq(col).hasClass('group-false')) {
if (c.debug){ time = new Date(); }
for (k = 0; k < c.$tbodies.length; k++) {
n = c.cache[k].normalized;
group = ''; // clear grouping across tbodies
$tr = c.$tbodies.eq(k).children('tr');
if (wo.group_collapsed && wo.group_collapsible) {
$tr.addClass('group-hidden');
}
for (j = 0; j < $tr.length; j++) {
if ( $tr.eq(j).is(':visible') ) {
t = (c.$headers.eq(col).attr('class') || '').match(/(group-\w+(-\w+)?)/g);
// group-{type}-{number/date}
t2 = t ? t[0].split('-') : ['','letter',1]; // default to letter 1
curr = n[j] ? ts.grouping[t2[1]]( c, c.$headers.eq(col), c.cache[k].normalized[j][col], /date/.test(t) ? t2[2] : parseInt(t2[2] || 1, 10) || 1, group ) : curr;
if (group !== curr) {
group = curr;
// show range if number > 1
if (t2[1] === 'number' && t2[2] > 1 && curr !== '') {
curr += ' - ' + (parseInt(curr, 10) + ((parseInt(t2[2],10) - 1) * (c.$headers.eq(col).hasClass(ts.css.sortAsc) ? 1 : -1)));
}
if ($.isFunction(wo.group_formatter)) {
curr = wo.group_formatter((curr || '').toString(), col, table, c, wo) || curr;
}
$tr.eq(j).before('<tr class="group-header ' + c.selectorRemove.slice(1) + (wo.group_collapsed && wo.group_collapsible ? ' collapsed' : '') +
'" unselectable="on"><td colspan="' + (c.columns+1) + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' +
curr + '</span><span class="group-count"></span></td></tr>');
}
}
}
}
$tr = c.$table.find('tr.group-header').on('selectstart', false);
if (wo.group_count || $.isFunction(wo.group_callback)) {
$tr.each(function(){
var $rows,
$row = $(this),
$label = $row.find('.group-count');
if ($label.length) {
$rows = $(this).nextUntil('tr.group-header').filter(':visible');
if (wo.group_count) {
$label.html( wo.group_count.replace(/\{num\}/g, $rows.length) );
}
if ($.isFunction(wo.group_callback)) {
wo.group_callback($row.find('td'), $rows, col, table);
}
}
});
}
c.$table.trigger(wo.group_complete);
if (c.debug) {
$.tablesorter.utility.benchmark("Applying groups widget: ", time);
}
}
},
remove : function(table, c, wo){
c.$table
.off('click', 'tr.group-header')
.find('.group-hidden').removeClass('group-hidden').end()
.find('tr.group-header').remove();
}
});
})(jQuery);

View File

@@ -0,0 +1,748 @@
/* Pager widget (beta) for TableSorter 10/18/2013 */
/*jshint browser:true, jquery:true, unused:false */
;(function($){
"use strict";
var tsp,
ts = $.tablesorter;
ts.widget.add({
id: "pager",
priority: 5,
options : {
// output default: '{page}/{totalPages}'
// possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}'
// apply disabled classname to the pager arrows when the rows at either extreme is visible
pager_updateArrows: true,
// starting page of the pager (zero based index)
pager_startPage: 0,
// Number of visible rows
pager_size: 10,
// Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js)
pager_savePages: true,
// if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty
// table row set to a height to compensate; default is false
pager_fixedHeight: false,
// count child rows towards the set page size? (set true if it is a visible table row within the pager)
// if true, child row(s) may not appear to be attached to its parent row, may be split across pages or
// may distort the table if rowspan or cellspans are included.
pager_countChildRows: false,
// remove rows from the table to speed up the sort of large tables.
// setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled.
pager_removeRows: false, // removing rows in larger tables speeds up the sort
// use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}"
// where {page} is replaced by the page number, {size} is replaced by the number of records to show,
// {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds
// the filterList to the url into an "fcol" array.
// So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url
// and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url
pager_ajaxUrl: null,
// modify the url after all processing has been applied
pager_customAjaxUrl: function(table, url) { return url; },
// modify the $.ajax object to allow complete control over your ajax requests
pager_ajaxObject: {
dataType: 'json'
},
// process ajax so that the following information is returned:
// [ total_rows (number), rows (array of arrays), headers (array; optional) ]
// example:
// [
// 100, // total rows
// [
// [ "row1cell1", "row1cell2", ... "row1cellN" ],
// [ "row2cell1", "row2cell2", ... "row2cellN" ],
// ...
// [ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
// ],
// [ "header1", "header2", ... "headerN" ] // optional
// ]
pager_ajaxProcessing: function(ajax){ return [ 0, [], null ]; },
// css class names of pager arrows
pager_css: {
container : 'tablesorter-pager',
errorRow : 'tablesorter-errorRow', // error information row (don't include period at beginning)
disabled : 'disabled' // class added to arrows @ extremes (i.e. prev/first arrows "disabled" on first page)
},
// jQuery selectors
pager_selectors: {
container : '.pager', // target the pager markup
first : '.first', // go to first page arrow
prev : '.prev', // previous page arrow
next : '.next', // next page arrow
last : '.last', // go to last page arrow
goto : '.gotoPage', // go to page selector - select dropdown that sets the current page
pageDisplay : '.pagedisplay', // location of where the "output" is displayed
pageSize : '.pagesize' // page size selector - select dropdown that sets the "size" option
}
},
init: function(table){
tsp.init(table);
},
// only update to complete sorter initialization
format: function(table, c){
if (!(c.pager && c.pager.initialized)){
return tsp.initComplete(table, c);
}
tsp.moveToPage(table, c.pager, false);
},
remove: function(table, c, wo){
tsp.destroyPager(table, c);
}
});
/* pager widget functions */
tsp = ts.pager = {
init: function(table) {
// check if tablesorter has initialized
if (table.hasInitialized && table.config.pager.initialized) { return; }
var t,
c = table.config,
wo = c.widgetOptions,
s = wo.pager_selectors,
// save pager variables
p = c.pager = $.extend({
totalPages: 0,
filteredRows: 0,
filteredPages: 0,
currentFilters: [],
page: wo.pager_startPage,
size: wo.pager_size,
startRow: 0,
endRow: 0,
$size: null,
last: {}
}, c.pager);
// added in case the pager is reinitialized after being destroyed.
p.$container = $(s.container).addClass(wo.pager_css.container).show();
// goto selector
p.$goto = p.$container.find(s.goto);
// page size selector
p.$size = p.$container.find(s.pageSize);
p.totalRows = c.$tbodies.eq(0).children().length;
p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success;
c.appender = tsp.appender;
if (wo.pager_savePages && ts.storage) {
t = ts.storage(table, 'tablesorter-pager') || {}; // fixes #387
p.page = isNaN(t.page) ? p.page : t.page;
p.size = ( isNaN(t.size) ? p.size : t.size ) || 10;
}
// clear initialized flag
p.initialized = false;
// before initialization event
c.$table.trigger('pagerBeforeInitialized', c);
tsp.enablePager(table, c, false);
if ( typeof(wo.pager_ajaxUrl) === 'string' ) {
// ajax pager; interact with database
p.ajax = true;
// When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side.
wo.filter_serversideFiltering = true;
c.serverSideSorting = true;
tsp.moveToPage(table, p);
} else {
p.ajax = false;
// Regular pager; all rows stored in memory
c.$table.trigger("appendCache", true);
tsp.hideRowsSetup(table, c);
}
},
initComplete: function(table, c){
var p = c.pager;
tsp.changeHeight(table, c);
tsp.bindEvents(table, c);
// pager initialized
if (!p.ajax) {
p.initialized = true;
tsp.setPageSize(table, 0, c); // page size 0 is ignored
c.$table.trigger('pagerInitialized', c);
}
},
bindEvents: function(table, c){
var ctrls, fxn,
p = c.pager,
wo = c.widgetOptions,
s = wo.pager_selectors;
c.$table
.off('filterStart filterEnd sortEnd disable enable destroy update pageSize '.split(' ').join('.pager '))
.on('filterStart.pager', function(e, filters) {
c.$table.data('pagerUpdateTriggered', false);
p.currentFilters = filters;
})
// update pager after filter widget completes
.on('filterEnd.pager sortEnd.pager', function(e) {
//Prevent infinite event loops from occuring by setting this in all moveToPage calls and catching it here.
if (c.$table.data('pagerUpdateTriggered')) {
c.$table.data('pagerUpdateTriggered', false);
return;
}
//only run the server side sorting if it has been enabled
if (e.type === "filterEnd" || (e.type === "sortEnd" && c.serverSideSorting)) {
tsp.moveToPage(table, p, false);
}
tsp.updatePageDisplay(table, c, false);
tsp.fixHeight(table, c);
})
.on('disable.pager', function(e){
e.stopPropagation();
tsp.showAllRows(table, c);
})
.on('enable.pager', function(e){
e.stopPropagation();
tsp.enablePager(table, c, true);
})
.on('destroy.pager', function(e){
e.stopPropagation();
tsp.destroyPager(table, c);
})
.on('update.pager', function(e){
e.stopPropagation();
tsp.hideRows(table, c);
})
.on('pageSize.pager', function(e,v){
e.stopPropagation();
tsp.setPageSize(table, parseInt(v, 10) || 10, c);
tsp.hideRows(table, c);
tsp.updatePageDisplay(table, c, false);
if (p.$size.length) { p.$size.val(p.size); } // twice?
})
.on('pageSet.pager', function(e,v){
e.stopPropagation();
p.page = (parseInt(v, 10) || 1) - 1;
if (p.$goto.length) { p.$goto.val(c.size); } // twice?
tsp.moveToPage(table, p);
tsp.updatePageDisplay(table, c, false);
});
// clicked controls
ctrls = [ s.first, s.prev, s.next, s.last ];
fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ];
p.$container.find(ctrls.join(','))
.off('click.pager')
.on('click.pager', function(){
var i,
$c = $(this),
l = ctrls.length;
if ( !$c.hasClass(wo.pager_css.disabled) ) {
for (i = 0; i < l; i++) {
if ($c.is(ctrls[i])) {
tsp[fxn[i]](table, p);
break;
}
}
}
return false;
});
if ( p.$goto.length ) {
p.$goto
.off('change')
.on('change', function(){
p.page = $(this).val() - 1;
tsp.moveToPage(table, p);
tsp.updatePageDisplay(table, c, false);
});
}
if ( p.$size.length ) {
p.$size
.off('change.pager')
.on('change.pager', function() {
p.$size.val( $(this).val() ); // in case there are more than one pagers
if ( !$(this).hasClass(wo.pager_css.disabled) ) {
tsp.setPageSize(table, parseInt( $(this).val(), 10 ), c);
tsp.changeHeight(table, c);
}
return false;
});
}
},
// hide arrows at extremes
pagerArrows: function(c, disable) {
var p = c.pager,
dis = !!disable,
wo = c.widgetOptions,
s = wo.pager_selectors,
tp = Math.min( p.totalPages, p.filteredPages );
if ( wo.pager_updateArrows ) {
p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, dis || p.page === 0);
p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, dis || p.page === tp - 1);
}
},
updatePageDisplay: function(table, c, flag) {
var i, pg, s, t, out,
wo = c.widgetOptions,
p = c.pager,
f = c.$table.hasClass('hasFilters') && !wo.pager_ajaxUrl,
t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove,
sz = p.size || 10; // don't allow dividing by zero
p.$size.removeClass(wo.pager_css.disabled).removeAttr('disabled');
p.$goto.removeClass(wo.pager_css.disabled).removeAttr('disabled');
p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method
p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t).length : p.totalRows;
p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages;
if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) {
t = (p.size * p.page > p.filteredRows);
p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1);
p.page = (t) ? 0 : p.page;
p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) );
out = p.$container.find(wo.pager_selectors.pageDisplay);
// form the output string (can now get a new output string from the server)
s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output )
// {page} = one-based index; {page+#} = zero based index +/- value
.replace(/\{page([\-+]\d+)?\}/gi, function(m,n){
return p.page + (n ? parseInt(n, 10) : 1);
})
// {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object)
.replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){
var t = m.replace(/[{}\s]/g,''), a = t.split(':'), d = p.ajaxData;
return a.length > 1 && d && d[a[0]] ? d[a[0]][a[1]] : p[t] || (d ? d[t] : '') || '';
});
if (out.length) {
out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s);
if ( p.$goto.length ) {
t = '';
pg = Math.min( p.totalPages, p.filteredPages );
for ( i = 1; i <= pg; i++ ) {
t += '<option>' + i + '</option>';
}
p.$goto.html(t).val( p.page + 1 );
}
}
}
tsp.pagerArrows(c);
if (p.initialized && flag !== false) {
c.$table.trigger('pagerComplete', c);
// save pager info to storage
if (wo.pager_savePages && ts.storage) {
ts.storage(table, 'tablesorter-pager', {
page : p.page,
size : p.size
});
}
}
},
fixHeight: function(table, c) {
var d, h,
p = c.pager,
wo = c.widgetOptions,
$b = c.$tbodies.eq(0);
if (wo.pager_fixedHeight) {
$b.find('tr.pagerSavedHeightSpacer').remove();
h = c.$table.data('pagerSavedHeight');
if (h) {
d = h - $b.height();
if ( d > 5 && c.$table.data('pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) {
$b.append('<tr class="pagerSavedHeightSpacer ' + wo.pager_selectors.remove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>');
}
}
}
},
changeHeight: function(table, c) {
var $b = c.$tbodies.eq(0);
$b.find('tr.pagerSavedHeightSpacer').remove();
c.$table.data('pagerSavedHeight', $b.height());
tsp.fixHeight(table, c);
c.$table.data('pagerLastSize', c.pager.size);
},
hideRows: function(table, c){
if (!c.widgetOptions.pager_ajaxUrl) {
var i,
p = c.pager,
wo = c.widgetOptions,
rows = c.$tbodies.eq(0).children(),
l = rows.length,
s = ( p.page * p.size ),
e = s + p.size,
f = wo && wo.filter_filteredRow || 'filtered',
j = 0; // size counter
for ( i = 0; i < l; i++ ){
if ( !rows[i].className.match(f) ) {
rows[i].style.display = ( j >= s && j < e ) ? '' : 'none';
// don't count child rows
j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1;
}
}
}
},
hideRowsSetup: function(table, c){
var p = c.pager;
p.size = parseInt( p.$size.val(), 10 ) || p.size;
c.$table.data('pagerLastSize', p.size);
tsp.pagerArrows(c);
if ( !c.widgetOptions.pager_removeRows ) {
tsp.hideRows(table, c);
c.$table.on('sortEnd.pager filterEnd.pager', function(){
tsp.hideRows(table, c);
});
}
},
renderAjax: function(data, table, c, xhr, exception){
var p = c.pager,
wo = c.widgetOptions;
// process data
if ( $.isFunction(wo.pager_ajaxProcessing) ) {
// ajaxProcessing result: [ total, rows, headers ]
var i, j, t, hsh, $f, $sh, th, d, l, $err, rr_count,
$t = c.$table,
tds = '',
result = wo.pager_ajaxProcessing(data, table) || [ 0, [] ],
hl = $t.find('thead th').length;
$t.find('thead tr.' + wo.pager_css.errorRow).remove(); // Clean up any previous error.
if ( exception ) {
$err = $('<tr class="' + wo.pager_css.errorRow + '"><td style="text-align:center;" colspan="' +
hl + '">' + exception.message + ' (' + xhr.status + ')</td></tr>')
.click(function(){
$(this).remove();
})
// add error row to thead instead of tbody, or clicking on the header will result in a parser error
.appendTo( $t.find('thead:first') );
c.$tbodies.eq(0).empty();
if (c.debug) { ts.log({ 'exception' : exception, 'jqxhr' : xhr }); }
} else {
// process ajax object
if (!$.isArray(result)) {
p.ajaxData = result;
p.totalRows = result.total;
th = result.headers;
d = result.rows;
} else {
// allow [ total, rows, headers ] or [ rows, total, headers ]
t = isNaN(result[0]) && !isNaN(result[1]);
//ensure a zero returned row count doesn't fail the logical ||
rr_count = result[t ? 1 : 0];
p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count;
d = result[t ? 0 : 1] || []; // row data
th = result[2]; // headers
}
l = d.length;
if (d instanceof jQuery) {
// append jQuery object
c.$tbodies.eq(0).empty().append(d);
} else if (d.length) {
// build table from array
if ( l > 0 ) {
for ( i = 0; i < l; i++ ) {
tds += '<tr>';
for ( j = 0; j < d[i].length; j++ ) {
// build tbody cells
tds += '<td>' + d[i][j] + '</td>';
}
tds += '</tr>';
}
}
// add rows to first tbody
c.$tbodies.eq(0).html( tds );
}
// only add new header text if the length matches
if ( th && th.length === hl ) {
hsh = $t.hasClass('hasStickyHeaders');
$sh = hsh ? c.$sticky.children('thead:first').children().children() : '';
$f = $t.find('tfoot tr:first').children();
// don't change td headers (may contain pager)
c.$headers.filter('th').each(function(j){
var $t = $(this), icn;
// add new test within the first span it finds, or just in the header
if ( $t.find('.' + ts.css.icon).length ) {
icn = $t.find('.' + ts.css.icon).clone(true);
$t.find('.tablesorter-header-inner').html( th[j] ).append(icn);
if ( hsh && $sh.length ) {
icn = $sh.eq(j).find('.' + ts.css.icon).clone(true);
$sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn);
}
} else {
$t.find('.tablesorter-header-inner').html( th[j] );
if (hsh && $sh.length) {
$sh.eq(j).find('.tablesorter-header-inner').html( th[j] );
}
}
$f.eq(j).html( th[j] );
});
}
}
if (c.showProcessing) {
ts.isProcessing(table); // remove loading icon
}
p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
tsp.updatePageDisplay(table, c);
tsp.fixHeight(table, c);
if (p.initialized) {
$t.trigger('pagerChange', c);
$t.trigger('updateComplete');
} else {
$t.trigger('update');
}
}
if (!p.initialized) {
p.initialized = true;
c.$table.trigger('pagerInitialized', c);
}
},
getAjax: function(table, c){
var url = tsp.getAjaxUrl(table, c),
$doc = $(document),
wo = c.widgetOptions,
p = c.pager;
if ( url !== '' ) {
if (c.showProcessing) {
ts.isProcessing(table, true); // show loading icon
}
$doc.on('ajaxError.pager', function(e, xhr, settings, exception) {
tsp.renderAjax(null, table, c, xhr, exception);
$doc.off('ajaxError.pager');
});
wo.pager_ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl
wo.pager_ajaxObject.success = function(data) {
tsp.renderAjax(data, table, c);
$doc.off('ajaxError.pager');
if (typeof p.oldAjaxSuccess === 'function') {
p.oldAjaxSuccess(data);
}
};
$.ajax(wo.pager_ajaxObject);
}
},
getAjaxUrl: function(table, c) {
var p = c.pager,
wo = c.widgetOptions,
url = (wo.pager_ajaxUrl) ? wo.pager_ajaxUrl
// allow using "{page+1}" in the url string to switch to a non-zero based index
.replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); })
.replace(/\{size\}/g, p.size) : '',
sl = c.sortList,
fl = p.currentFilters || [],
sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/),
filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/),
arry = [];
if (sortCol) {
sortCol = sortCol[1];
$.each(sl, function(i,v){
arry.push(sortCol + '[' + v[0] + ']=' + v[1]);
});
// if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col"
url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol );
arry = [];
}
if (filterCol) {
filterCol = filterCol[1];
$.each(fl, function(i,v){
if (v) {
arry.push(filterCol + '[' + i + ']=' + encodeURIComponent(v));
}
});
// if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol"
url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol );
}
if ( $.isFunction(wo.pager_customAjaxUrl) ) {
url = wo.pager_customAjaxUrl(table, url);
}
return url;
},
renderTable: function(table, rows) {
var i, $tb,
c = table.config,
p = c.pager,
wo = c.widgetOptions,
l = rows && rows.length || 0, // rows may be undefined
s = ( p.page * p.size ),
e = ( s + p.size );
if ( l < 1 ) { return; } // empty table, abort!
if ( p.page >= p.totalPages ) {
// lets not render the table more than once
return tsp.moveToLastPage(table, p);
}
p.isDisabled = false; // needed because sorting will change the page and re-enable the pager
if (p.initialized) { c.$table.trigger('pagerChange', c); }
if ( !wo.pager_removeRows ) {
tsp.hideRows(table, c);
} else {
if ( e > rows.length ) {
e = rows.length;
}
ts.clearTableBody(table);
$tb = ts.utility.processTbody(table, c.$tbodies.eq(0), true);
for ( i = s; i < e; i++ ) {
$tb.append(rows[i]);
}
ts.utility.processTbody(table, $tb, false);
}
tsp.updatePageDisplay(table, c);
if ( !p.isDisabled ) { tsp.fixHeight(table, c); }
wo.pager_startPage = p.page;
wo.pager_size = p.size;
},
showAllRows: function(table, c){
var p = c.pager,
wo = c.widgetOptions;
if ( p.ajax ) {
tsp.pagerArrows(c, true);
} else {
p.isDisabled = true;
c.$table.data('pagerLastPage', p.page);
c.$table.data('pagerLastSize', p.size);
p.page = 0;
p.size = p.totalRows;
p.totalPages = 1;
c.$table.addClass('pagerDisabled').find('tr.pagerSavedHeightSpacer').remove();
tsp.renderTable(table, c.rowsCopy);
}
// disable size selector
p.$size.add(p.$goto).each(function(){
$(this).addClass(wo.pager_css.disabled).attr('disabled', 'disabled');
});
c.$table.trigger('applyWidgets');
},
moveToPage: function(table, p, flag) {
if ( p.isDisabled ) { return; }
var c = table.config,
l = p.last,
pg = Math.min( p.totalPages, p.filteredPages );
if ( p.page < 0 ) { p.page = 0; }
if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; }
// don't allow rendering multiple times on the same page/size/totalpages/filters
if (l.page === p.page && l.size === p.size && l.total === p.totalPages && l.filters === p.currentFilters ) { return; }
p.last = {
page : p.page,
size : p.size,
totalPages : p.totalPages,
currentFilters : p.currentFilters
};
if (p.ajax) {
tsp.getAjax(table, c);
} else if (!p.ajax) {
tsp.renderTable(table, c.rowsCopy);
}
c.$table.data('pagerLastPage', p.page);
c.$table.data('pagerUpdateTriggered', true);
if (p.initialized && flag !== false) {
c.$table.trigger('pageMoved', c);
c.$table.trigger('applyWidgets');
}
},
setPageSize: function(table, size, c) {
var p = c.pager;
p.size = size;
p.$size.val(size);
c.$table.data('pagerLastPage', p.page);
c.$table.data('pagerLastSize', p.size);
p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
tsp.moveToPage(table, p);
},
moveToFirstPage: function(table, p) {
p.page = 0;
tsp.moveToPage(table, p);
},
moveToLastPage: function(table, p) {
p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
tsp.moveToPage(table, p);
},
moveToNextPage: function(table, p) {
p.page++;
if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) {
p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
}
tsp.moveToPage(table, p);
},
moveToPrevPage: function(table, p) {
p.page--;
if ( p.page <= 0 ) {
p.page = 0;
}
tsp.moveToPage(table, p);
},
destroyPager: function(table, c){
var p = c.pager;
tsp.showAllRows(table, c);
p.$container.hide(); // hide pager
c.appender = null; // remove pager appender function
p.initialized = false;
c.$table.off('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager');
if (ts.storage) {
ts.storage(table, 'tablesorter-pager', '');
}
},
enablePager: function(table, c, triggered){
var p = c.pager,
wo = c.widgetOptions;
p.isDisabled = false;
p.page = c.$table.data('pagerLastPage') || p.page || 0;
p.size = c.$table.data('pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size;
p.$size.val(p.size); // set page size
p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / ( p.size || 10 ) );
c.$table.removeClass('pagerDisabled');
if ( triggered ) {
c.$table.trigger('update');
tsp.setPageSize(table, p.size, c);
tsp.hideRowsSetup(table, c);
tsp.fixHeight(table, c);
}
},
appender: function(table, rows) {
var c = table.config,
p = c.pager;
if ( !p.ajax ) {
c.rowsCopy = rows;
p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children().length : rows.length;
p.size = c.$table.data('pagerLastSize') || p.size;
p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
tsp.moveToPage(table, p);
}
}
};
})(jQuery);

View File

@@ -0,0 +1,50 @@
/*! tablesorter repeatHeaders widget - updated 4/12/2013
* Requires tablesorter v2.8+ and jQuery 1.7+
* Original by Christian Bach from the example-widgets.html demo
*/
/*global jQuery: false */
;(function($){
"use strict";
$.tablesorter.widget.add({
id: "repeatHeaders",
priority: 10,
options: {
rowsToSkip : 4
},
// format is called on init and when a sorting has finished
format: function(table, c, wo) {
var h = '', i, $tr, l, skip;
// cache and collect all TH headers
if (!wo.repeatHeaders) {
h = '<tr class="repeated-header remove-me">';
$.each(c.headerContent, function(i,t) {
h += '<th>' + t + '</th>';
});
// "remove-me" class was added in case the table needs to be updated, the "remove-me" rows will be
// removed prior to the update to prevent including the rows in the update - see "selectorRemove" option
wo.repeatHeaders = h + '</tr>';
}
// number of rows to skip
skip = wo && wo.rowsToSkip || 4;
// remove appended headers by classname
c.$table.find("tr.repeated-header").remove();
$tr = c.$tbodies.find('tr');
l = $tr.length;
// loop all tr elements and insert a copy of the "headers"
for (i = skip; i < l; i += skip) {
// insert a copy of the table head every X rows
$tr.eq(i).before(wo.repeatHeaders);
}
},
// this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin
// this function only applies to tablesorter v2.4+
remove: function(table, c){
c.$table.find("tr.repeated-header").remove();
}
});
})(jQuery);

View File

@@ -0,0 +1,241 @@
/*!
Copyright (C) 2011 T. Connell & Associates, Inc.
Dual-licensed under the MIT and GPL licenses
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Resizable scroller widget for the jQuery tablesorter plugin
Version 2.0 - modified by Rob Garrison (4/12/2013)
Requires jQuery, v1.2.3 or higher
Requires the tablesorter plugin, v2.0 or higher, available at http://mottie.github.com/tablesorter/docs/
Usage:
$(function() {
$('table.tablesorter').tablesorter({
widgets: ['zebra', 'scroller'],
widgetOptions : {
scroller_height : 300, // height of scroll window
scroller_barWidth : 17, // scroll bar width
scroller_jumpToHeader : true, // header snap to browser top when scrolling the tbody
scroller_idPrefix : 's_' // cloned thead id prefix (random number added to end)
}
});
});
Website: www.tconnell.com
*/
/*jshint browser:true, jquery:true, unused:false */
;(function($){
"use strict";
$.fn.hasScrollBar = function(){
return this.get(0).scrollHeight > this.height();
};
var ts = $.tablesorter;
ts.window_resize = function(){
if (this.resize_timer) {
clearTimeout(this.resize_timer);
}
this.resize_timer = setTimeout(function(){
$(this).trigger('resizeEnd');
}, 250);
};
// Add extra scroller css
$(function(){
var s = '<style>' +
'.tablesorter-scroller-header table.tablesorter { margin-bottom: 0; }' +
'.tablesorter-scroller-table table.tablesorter { margin-top: 0; } ' +
'.tablesorter-scroller-table .tablesorter-filter-row, .tablesorter-scroller-table tfoot { display: none; }' +
'.tablesorter-scroller-table table.tablesorter thead tr.tablesorter-headerRow * {' +
'line-height:0;height:0;border:none;background-image:none;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;' +
'}</style>';
$(s).appendTo('body');
});
ts.widget.add({
id: 'scroller',
priority: 60, // run after the filter widget
options: {
scroller_height : 300,
scroller_barWidth : 17,
scroller_jumpToHeader: true,
scroller_idPrefix : 's_'
},
init: function(table, thisWidget, c, wo){
var $win = $(window);
//Setup window.resizeEnd event
$win
.on('resize', ts.window_resize)
.on('resizeEnd', function(e) {
// init is run before format, so scroller_resizeWidth
// won't be defined within the "c" or "wo" parameters
if (typeof table.config.widgetOptions.scroller_resizeWidth === 'function') {
//IE calls resize when you modify content, so we have to unbind the resize event
//so we don't end up with an infinite loop. we can rebind after we're done.
$win.off('resize', ts.window_resize);
table.config.widgetOptions.scroller_resizeWidth();
$win.on('resize', ts.window_resize);
}
});
},
format: function(table, c, wo) {
var h, $hdr, id, t, resize, $cells,
$win = $(window),
$tbl = c.$table,
flag = false,
filterInputs = 'input, select';
if (!c.isScrolling) {
h = wo.scroller_height || 300;
t = $tbl.find('tbody').height();
if (t !== 0 && h > t) { h = t + 10; } // Table is less than h px
id = wo.scroller_id = wo.scroller_idPrefix + Math.floor(Math.random() * 101);
$hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0><thead>' + $tbl.find('thead:first').html() + '</thead></table>');
$tbl
.wrap('<div id="' + id + '" class="tablesorter-scroller" style="text-align:left;" />')
.before($hdr)
.find('.tablesorter-filter-row').addClass('hideme');
$cells = $hdr
.wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />')
.find('.' + ts.css.header)
.on('mousedown', function(){
this.onselectstart = function(){ return false; };
return false;
});
$tbl
.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />')
.off('sortEnd.tsScroller')
.on('sortEnd.tsScroller', function(){
c.$headers.each(function(i){
var t = $cells.eq(i);
t
.attr('class', $(this).attr('class'))
// remove processing icon
.removeClass(ts.css.processing + ' ' + c.cssProcessing);
if (ts.css.icon){
t
.find('.' + ts.css.icon)
.attr('class', $(this).find('.' + ts.css.icon).attr('class'));
}
});
});
// make scroller header sortable
c.$headers.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(i){
var t = $(this);
$cells.eq(i)
// clicking on new header will trigger a sort
.on('mouseup', function(e){
t.trigger(e, true); // external mouseup flag (click timer is ignored)
})
// prevent header text selection
.on('mousedown', function(){
this.onselectstart = function(){ return false; };
return false;
});
});
// look for filter widget
$tbl.on('filterEnd', function(){
if (flag) { return; }
$cells.each(function(i){
$(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(i).val() );
});
});
$hdr.find(filterInputs).on('keyup search', function(e){
// ignore arrow and meta keys; allow backspace
if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; }
flag = true;
var $f = $(this), col = $f.attr('data-column');
c.$filters.find(filterInputs).eq(col)
.val( $f.val() )
.trigger('search');
setTimeout(function(){
flag = false;
}, wo.filter_searchDelay);
});
resize = function(){
var d,
//Hide other scrollers so we can resize
$div = $('div.scroller[id != "' + id + '"]').hide();
$tbl.find('thead').show();
//Reset sizes so parent can resize.
$hdr
.width(0)
.parent().width(0)
.find('th,td').width(0);
$tbl
.width(0)
.find('thead').find('th,td').width(0);
d = $tbl.parent();
d.width(0);
d.parent().trigger('resize');
// Shrink a bit to accommodate scrollbar
d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? wo.scroller_barWidth : 0 ) );
$tbl.width( d.innerWidth() - ( d.hasScrollBar() ? wo.scroller_barWidth : 0 ) );
$tbl.find('thead').find('th,td').filter(':visible').each(function(i, c){
var $th = $(c),
//Wrap in browser detect??
w = parseInt( $th.css('min-width').replace('auto', '0').replace(/(px|em)/, ''), 10 );
if ( $th.width() < w ) {
$th.width(w);
} else {
w = $th.width();
}
$hdr.find('th,td').eq(i).width(w);
});
$hdr.width($tbl.innerWidth());
$div.show();
};
//Expose to external calls
wo.scroller_resizeWidth = resize;
resize();
$tbl.find('thead').css('visibility', 'hidden');
c.isScrolling = true;
t = $tbl.parent().parent().height();
// The header will always jump into view if scrolling the table body
$tbl.parent().on('scroll', function(){
if (wo.scroller_jumpToHeader) {
var pos = $win.scrollTop() - $hdr.offset().top;
if ($(this).scrollTop() !== 0 && pos < t && pos > 0) {
$win.scrollTop( $hdr.offset().top );
}
}
});
}
//Sorting, so scroll to top
$tbl.parent().animate({ scrollTop: 0 }, 'fast');
},
remove : function(table, c, wo){
}
});
})(jQuery);