/***
* The only plugin's dependency - jQuery. It is required before
* creating the plugin.
*/
var jQuery = require("jquery");
/**
* There is an IIFE around the plugin.
* The IIFE contains several helper functions.
* The plugin gets assigned to `$.fn.tablesorter`.
* @namespace TablesorterIIFE
* @author Curveball <x.curveball.x@gmail.com>
* @license MIT
*
*/
(function($, window, document, undefined) {
"use strict";
$.fn._init = $.fn.init;
/**
* Makes possible to get selector string later by attaching it to the jQuery object.
* Wrapper around original init() function.
* @function TablesorterIIFE~init
* @param {string|*} selector selector string
* @param {object} context an object serving as context for selectors search. The match will be searched for in its children instead of the entire page.
* @param {object} root usually $(document).
* @returns {object} a jQuery object
*/
$.fn.init = function(selector, context, root) {
return (typeof selector === 'string') ? new $.fn._init(selector, context, root).data('selector', selector) : new $.fn._init(selector, context, root);
};
/**
* Gets selector string passed to the plugin.
* @function TablesorterIIFE~getSelector
* @returns {string} selector string passed to the plugin.
*/
$.fn.getSelector = function() {
return $(this).data('selector');
};
/**
* The plugin function assigned to `$.fn.tablesorter`.
* Below go inner helper functions inside the plugin.
* @namespace Tablesorter
* @author Curveball <x.curveball.x@gmail.com>
* @license MIT
* @param {object.<string, *>} [options] object passed to the plugin upon calling
* @returns {object} returns collection of matched elements.
*/
$.fn.tablesorter = function(options) {
var settings = $.extend({}, $.fn.tablesorter.settings, options);
var selector = $(this).getSelector(),
ths = " ." + settings.tablesorterTitlesClass,
trs = " ." + settings.tablesorterGroupsClass,
cells = $(selector + ths)[0].cells,
columns = settings.tablesorterColumns,
curIndex = null,
curType = null,
curOrder = null,
curElem = null,
busy = false;
/**
* Handles clicks on columns' headers. Does some checks and invokes row sorting.
* @function Tablesorter~tablesorter
* @param {MouseEvent} event event object corresponding to click on controls
* @returns {undefined}
*/
function tablesorter(event) {
if(busy || !$(event.target).hasClass('sortable')) return;
toggleBusyFlag();
curElem = $(event.target);
curOrder = curElem.data('sortOrder');
curIndex = getColIndex();
curType = getValuesType();
if(curIndex === null || (curType !== 'number' && curType !== 'string')) return;
curElem.removeClass(curOrder === 'asc' ? 'desc' : 'asc').addClass(curOrder);
sortRows();
toggleSortingOrderForCol();
toggleBusyFlag();
}
/**
* Toggles plugin's busy flag.
* @function Tablesorter~toggleBusyFlag
* @returns {undefined}
*/
function toggleBusyFlag() {
busy = busy === true ? false : true;
}
/**
* Toggles column's sorting order flag.
* @function Tablesorter~toggleSortingOrderForCol
* @returns {undefined}
*/
function toggleSortingOrderForCol() {
if(curElem.data('sortOrder') === 'desc') {
curElem.data('sortOrder', 'asc');
} else {
curElem.data('sortOrder', 'desc');
}
}
/**
* Sorts table rows.
* @function Tablesorter~sortRows
* @returns {undefined}
*/
function sortRows() {
var rowsBlocks = $(selector + trs);
$.each(rowsBlocks, function(index, rowsBlock) {
var rows = $(rowsBlock).find("tr");
if(curType === 'number') {
rows.sort(sortAsNumbers);
} else {
rows.sort(sortAsStrings);
}
$(rowsBlock).empty().append(rows);
});
}
/**
* Sorting function. Compares two rows' cells with numeric content.
* @function Tablesorter~sortAsNumbers
* @param {HTMLTableRowElement} rowA one row object
* @param {HTMLTableRowElement} rowB another row object
* @returns {number} number, depending on what value is greater given the sorting order.
*/
function sortAsNumbers(rowA, rowB) {
var valA = parseFloat(rowA.cells[curIndex].textContent),
valB = parseFloat(rowB.cells[curIndex].textContent);
if(curOrder === 'asc') return (valA > valB) ? 1 : (valA < valB) ? -1 : rowA.sectionRowIndex - rowB.sectionRowIndex;
if(curOrder === 'desc') return (valB > valA) ? 1 : (valB < valA) ? -1 : rowA.sectionRowIndex - rowB.sectionRowIndex;
}
/**
* Sorting function. Compares two rows' cells with textual content.
* @function Tablesorter~sortAsStrings
* @param {HTMLTableRowElement} rowA one row object
* @param {HTMLTableRowElement} rowB another row object
* @returns {number} number, depending on what value is greater given the sorting order.
*/
function sortAsStrings(rowA, rowB) {
var valA = rowA.cells[curIndex].textContent,
valB = rowB.cells[curIndex].textContent;
if(curOrder === 'asc') return (valA > valB) ? 1 : (valA < valB) ? -1 : rowA.sectionRowIndex - rowB.sectionRowIndex;
if(curOrder === 'desc') return (valB > valA) ? 1 : (valB < valA) ? -1 : rowA.sectionRowIndex - rowB.sectionRowIndex;
}
/**
* Gets column values' datatype.
* @function Tablesorter~getValuesType
* @returns {string} column values' datatype.
*/
function getValuesType() {
return $.isNumeric($(selector + trs + " tr")[0].cells[curIndex].textContent) ? 'number' : 'string';
}
/**
* Gets column's index.
* @function Tablesorter~getColIndex
* @returns {number} column's index.
*/
function getColIndex() {
var allths = curElem[0].parentElement.cells, colIndex = null;
$.each(allths, function(index, cell) {
if(curElem[0] === cell) colIndex = index;
$(cell).removeClass( "desc asc" );
});
return colIndex;
}
(function() {
if(columns.length === 0) return;
for(var i = 0; i < columns.length; i++) {
$(cells[columns[i].col]).addClass('sortable').data('sortOrder', columns[i].order);
}
$("body").on( "click", selector + ths, function(event) {
tablesorter(event);
});
})();
return this;
};
/*** Plugin's default settings*/
$.fn.tablesorter.settings = {
tablesorterTitlesClass: 'tsTitles',
tablesorterGroupsClass: 'tsGroup',
tablesorterColumns: []
};
})(jQuery, window, document);