diff --git a/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/first.png b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/first.png new file mode 100644 index 000000000..6f11fcb08 Binary files /dev/null and b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/first.png differ diff --git a/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/last.png b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/last.png new file mode 100644 index 000000000..720793576 Binary files /dev/null and b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/last.png differ diff --git a/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/loading.gif b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/loading.gif new file mode 100644 index 000000000..72054717b Binary files /dev/null and b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/loading.gif differ diff --git a/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/next.png b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/next.png new file mode 100644 index 000000000..4a2f9d4e4 Binary files /dev/null and b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/next.png differ diff --git a/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/prev.png b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/prev.png new file mode 100644 index 000000000..15d1584bd Binary files /dev/null and b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/icons/prev.png differ diff --git a/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/jquery.tablesorter.pager.css b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/jquery.tablesorter.pager.css new file mode 100644 index 000000000..e77cd88ed --- /dev/null +++ b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/jquery.tablesorter.pager.css @@ -0,0 +1,42 @@ +/* pager wrapper, div */ +.tablesorter-pager { + padding: 5px; +} +/* pager wrapper, in thead/tfoot */ +td.tablesorter-pager { + background-color: #e6eeee; + margin: 0; /* needed for bootstrap .pager gets a 18px bottom margin */ +} +/* pager navigation arrows */ +.tablesorter-pager img { + vertical-align: middle; + margin-right: 2px; + cursor: pointer; +} + +/* pager output text */ +.tablesorter-pager .pagedisplay { + padding: 0 5px 0 5px; + width: auto; + white-space: nowrap; + text-align: center; +} + +/* pager element reset (needed for bootstrap) */ +.tablesorter-pager select { + margin: 0; + padding: 0; +} + +/*** css used when "updateArrows" option is true ***/ +/* the pager itself gets a disabled class when the number of rows is less than the size */ +.tablesorter-pager.disabled { + display: none; +} +/* hide or fade out pager arrows when the first or last row is visible */ +.tablesorter-pager .disabled { + /* visibility: hidden */ + opacity: 0.5; + filter: alpha(opacity=50); + cursor: default; +} \ No newline at end of file diff --git a/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/jquery.tablesorter.pager.js b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/jquery.tablesorter.pager.js new file mode 100644 index 000000000..a0239a417 --- /dev/null +++ b/ajax/libs/jquery.tablesorter/2.17.6/addons/pager/jquery.tablesorter.pager.js @@ -0,0 +1,921 @@ +/*! + * tablesorter pager plugin + * updated 8/1/2014 (v2.17.6) + */ +/*jshint browser:true, jquery:true, unused:false */ +;(function($) { + "use strict"; + /*jshint supernew:true */ + var ts = $.tablesorter; + + $.extend({ tablesorterPager: new function() { + + this.defaults = { + // target the pager markup + container: null, + + // 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 + ajaxUrl: null, + + // modify the url after all processing has been applied + customAjaxUrl: function(table, url) { return url; }, + + // modify the $.ajax object to allow complete control over your ajax requests + ajaxObject: { + dataType: 'json' + }, + + // set this to false if you want to block ajax loading on init + processAjaxOnInit: true, + + // 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 + // ] + ajaxProcessing: function(ajax){ return [ 0, [], null ]; }, + + // output default: '{page}/{totalPages}' + // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows} + output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}' + + // apply disabled classname to the pager arrows when the rows at either extreme is visible + updateArrows: true, + + // starting page of the pager (zero based index) + page: 0, + + // reset pager after filtering; set to desired page # + // set to false to not change page at filter start + pageReset: 0, + + // Number of visible rows + size: 10, + + // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js) + savePages: true, + + // defines custom storage key + storageKey: 'tablesorter-pager', + + // 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 + 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. + 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. + removeRows: false, // removing rows in larger tables speeds up the sort + + // css class names of pager arrows + cssFirst: '.first', // go to first page arrow + cssPrev: '.prev', // previous page arrow + cssNext: '.next', // next page arrow + cssLast: '.last', // go to last page arrow + cssGoto: '.gotoPage', // go to page selector - select dropdown that sets the current page + cssPageDisplay: '.pagedisplay', // location of where the "output" is displayed + cssPageSize: '.pagesize', // page size selector - select dropdown that sets the "size" option + cssErrorRow: 'tablesorter-errorRow', // error information row + + // class added to arrows when at the extremes (i.e. prev/first arrows are "disabled" when on the first page) + cssDisabled: 'disabled', // Note there is no period "." in front of this class name + + // stuff not set by the user + totalRows: 0, + totalPages: 0, + filteredRows: 0, + filteredPages: 0, + ajaxCounter: 0, + currentFilters: [], + startRow: 0, + endRow: 0, + $size: null, + last: {} + + }; + + var $this = this, + + // hide arrows at extremes + pagerArrows = function(p, disable) { + var a = 'addClass', + r = 'removeClass', + d = p.cssDisabled, + dis = !!disable, + first = ( dis || p.page === 0 ), + tp = Math.min( p.totalPages, p.filteredPages ), + last = ( dis || (p.page === tp - 1) || tp === 0 ); + if ( p.updateArrows ) { + p.$container.find(p.cssFirst + ',' + p.cssPrev)[ first ? a : r ](d).attr('aria-disabled', first); + p.$container.find(p.cssNext + ',' + p.cssLast)[ last ? a : r ](d).attr('aria-disabled', last); + } + }, + + updatePageDisplay = function(table, p, completed) { + var i, pg, s, $out, regex, + c = table.config, + f = c.$table.hasClass('hasFilters'), + t = [], + sz = p.size || 10; // don't allow dividing by zero + t = [ (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered'), c.selectorRemove.replace(/^(\w+\.)/g,'') ]; + if (p.countChildRows) { t.push(c.cssChildRow); } + regex = new RegExp( '(' + t.join('|') + ')' ); + if (f && !p.ajaxUrl) { + if ($.isEmptyObject(c.cache)) { + // delayInit: true so nothing is in the cache + p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( p.countChildRows ? '' : '.' + c.cssChildRow ).length; + } else { + p.filteredRows = 0; + $.each(c.cache[0].normalized, function(i, el) { + p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1; + }); + } + } else if (!f) { + p.filteredRows = p.totalRows; + } + p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method + c.totalRows = p.totalRows; + c.filteredRows = p.filteredRows; + p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0; + 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(p.cssPageDisplay); + // form the output string (can now get a new output string from the server) + s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output ) + // {page} = one-based index; {page+#} = zero based index +/- value + .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){ + return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0; + }) + // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object) + .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){ + var len, indx, + str = m.replace(/[{}\s]/g,''), + extra = str.split(':'), + data = p.ajaxData, + // return zero for default page/row numbers + deflt = /(rows?|pages?)$/i.test(str) ? 0 : ''; + if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') { + len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length; + indx = extra[0] === 'page' ? p.page + 1 : p.startRow; + return ''; + } + return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt; + }); + 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 += ''; + } + p.$goto.html(t).val( p.page + 1 ); + } + // rebind startRow/page inputs + $out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){ + var v = $(this).val(), + pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v; + c.$table.trigger('pageSet.pager', [ pg ]); + }); + } + } + pagerArrows(p); + if (p.initialized && completed !== false) { + c.$table.trigger('pagerComplete', p); + // save pager info to storage + if (p.savePages && ts.storage) { + ts.storage(table, p.storageKey, { + page : p.page, + size : p.size + }); + } + } + }, + + fixHeight = function(table, p) { + var d, h, + c = table.config, + $b = c.$tbodies.eq(0); + if (p.fixedHeight) { + $b.find('tr.pagerSavedHeightSpacer').remove(); + h = $.data(table, 'pagerSavedHeight'); + if (h) { + d = h - $b.height(); + if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) { + $b.append('
This is a p
+ * @before $.metadata.setType("class") + * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" + * @desc Reads metadata from the class attribute + * + * @exampleThis is a p
+ * @before $.metadata.setType("attr", "data") + * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" + * @desc Reads metadata from a "data" attribute + * + * @exampleThis is a 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); \ No newline at end of file diff --git a/ajax/libs/jquery.tablesorter/2.17.6/js/jquery.tablesorter.js b/ajax/libs/jquery.tablesorter/2.17.6/js/jquery.tablesorter.js new file mode 100644 index 000000000..ed482414e --- /dev/null +++ b/ajax/libs/jquery.tablesorter/2.17.6/js/jquery.tablesorter.js @@ -0,0 +1,1897 @@ +/**! +* TableSorter 2.17.6 - Client-side table sorting with ease! +* @requires jQuery v1.2.6+ +* +* Copyright (c) 2007 Christian Bach +* Examples and docs at: http://tablesorter.com +* Dual licensed under the MIT and GPL licenses: +* http://www.opensource.org/licenses/mit-license.php +* http://www.gnu.org/licenses/gpl.html +* +* @type jQuery +* @name tablesorter +* @cat Plugins/Tablesorter +* @author Christian Bach/christian.bach@polyester.se +* @contributor Rob Garrison/https://github.com/Mottie/tablesorter +*/ +/*jshint browser:true, jquery:true, unused:false, expr: true */ +/*global console:false, alert:false */ +!(function($) { + "use strict"; + $.extend({ + /*jshint supernew:true */ + tablesorter: new function() { + + var ts = this; + + ts.version = "2.17.6"; + + ts.parsers = []; + ts.widgets = []; + ts.defaults = { + + // *** appearance + theme : 'default', // adds tablesorter-{theme} to the table for styling + widthFixed : false, // adds colgroup to fix widths of columns + showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. + + headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = (class from cssIcon) + onRenderTemplate : null, // function(index, template){ return template; }, (template is a string) + onRenderHeader : null, // function(index){}, (nothing to return) + + // *** functionality + cancelSelection : true, // prevent text selection in the header + tabIndex : true, // add tabindex to header for keyboard accessibility + dateFormat : 'mmddyyyy', // other options: "ddmmyyy" or "yyyymmdd" + sortMultiSortKey : 'shiftKey', // key used to select additional columns + sortResetKey : 'ctrlKey', // key used to remove sorting on a column + usNumberFormat : true, // false for German "1.234.567,89" or French "1 234 567,89" + delayInit : false, // if false, the parsed table contents will not update until the first sort + serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. + + // *** sort options + headers : {}, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. + ignoreCase : true, // ignore case while sorting + sortForce : null, // column(s) first sorted; always applied + sortList : [], // Initial sort order; applied initially; updated when manually sorted + sortAppend : null, // column(s) sorted last; always applied + sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained + + sortInitialOrder : 'asc', // sort direction on first click + sortLocaleCompare: false, // replace equivalent character (accented characters) + sortReset : false, // third click on the header will reset column to default - unsorted + sortRestart : false, // restart sort to "sortInitialOrder" when clicking on previously unsorted columns + + emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero + stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero + textExtraction : 'basic', // text extraction method/function - function(node, table, cellIndex){} + textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in textExtraction function) + textSorter : null, // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText] + numberSorter : null, // choose overall numeric sorter function(a, b, direction, maxColumnValue) + + // *** widget options + widgets: [], // method to add widgets, e.g. widgets: ['zebra'] + widgetOptions : { + zebra : [ 'even', 'odd' ] // zebra widget alternating row class names + }, + initWidgets : true, // apply widgets on tablesorter initialization + + // *** callbacks + initialized : null, // function(table){}, + + // *** extra css class names + tableClass : '', + cssAsc : '', + cssDesc : '', + cssNone : '', + cssHeader : '', + cssHeaderRow : '', + cssProcessing : '', // processing icon applied to header during sort/filter + + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent + cssIcon : 'tablesorter-icon', // if this class exists, a will be added to the header automatically + cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) + + // *** selectors + selectorHeaders : '> thead th, > thead td', + selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort + selectorRemove : '.remove-me', + + // *** advanced + debug : false, + + // *** Internal variables + headerList: [], + empties: {}, + strings: {}, + parsers: [] + + // deprecated; but retained for backwards compatibility + // widgetZebra: { css: ["even", "odd"] } + + }; + + // internal css classes - these will ALWAYS be added to + // the table and MUST only contain one class name - fixes #381 + ts.css = { + table : 'tablesorter', + cssHasChild: 'tablesorter-hasChildRow', + childRow : 'tablesorter-childRow', + header : 'tablesorter-header', + headerRow : 'tablesorter-headerRow', + headerIn : 'tablesorter-header-inner', + icon : 'tablesorter-icon', + info : 'tablesorter-infoOnly', + processing : 'tablesorter-processing', + sortAsc : 'tablesorter-headerAsc', + sortDesc : 'tablesorter-headerDesc', + sortNone : 'tablesorter-headerUnSorted' + }; + + // labels applied to sortable headers for accessibility (aria) support + ts.language = { + sortAsc : 'Ascending sort applied, ', + sortDesc : 'Descending sort applied, ', + sortNone : 'No sort applied, ', + nextAsc : 'activate to apply an ascending sort', + nextDesc : 'activate to apply a descending sort', + nextNone : 'activate to remove the sort' + }; + + /* debuging utils */ + function log() { + var a = arguments[0], + s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : a; + if (typeof console !== "undefined" && typeof console.log !== "undefined") { + console[ /error/i.test(a) ? 'error' : /warn/i.test(a) ? 'warn' : 'log' ](s); + } else { + alert(s); + } + } + + function benchmark(s, d) { + log(s + " (" + (new Date().getTime() - d.getTime()) + "ms)"); + } + + ts.log = log; + ts.benchmark = benchmark; + + // $.isEmptyObject from jQuery v1.4 + function isEmptyObject(obj) { + /*jshint forin: false */ + for (var name in obj) { + return false; + } + return true; + } + + function getElementText(table, node, cellIndex) { + if (!node) { return ""; } + var te, c = table.config, + t = c.textExtraction || '', + text = ""; + if (t === "basic") { + // check data-attribute first + text = $(node).attr(c.textAttribute) || node.textContent || node.innerText || $(node).text() || ""; + } else { + if (typeof(t) === "function") { + text = t(node, table, cellIndex); + } else if (typeof (te = ts.getColumnData( table, t, cellIndex )) === 'function') { + text = te(node, table, cellIndex); + } else { + // previous "simple" method + text = node.textContent || node.innerText || $(node).text() || ""; + } + } + return $.trim(text); + } + + function detectParserForColumn(table, rows, rowIndex, cellIndex) { + var cur, + i = ts.parsers.length, + node = false, + nodeValue = '', + keepLooking = true; + while (nodeValue === '' && keepLooking) { + rowIndex++; + if (rows[rowIndex]) { + node = rows[rowIndex].cells[cellIndex]; + nodeValue = getElementText(table, node, cellIndex); + if (table.config.debug) { + log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"'); + } + } else { + keepLooking = false; + } + } + while (--i >= 0) { + cur = ts.parsers[i]; + // ignore the default text parser because it will always be true + if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node)) { + return cur; + } + } + // nothing found, return the generic parser (text) + return ts.getParserById('text'); + } + + function buildParserCache(table) { + var c = table.config, + // update table bodies in case we start with an empty table + tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'), + rows, list, l, i, h, ch, np, p, e, time, + j = 0, + parsersDebug = "", + len = tb.length; + if ( len === 0) { + return c.debug ? log('Warning: *Empty table!* Not building a parser cache') : ''; + } else if (c.debug) { + time = new Date(); + log('Detecting parsers for each column'); + } + list = { + extractors: [], + parsers: [] + }; + while (j < len) { + rows = tb[j].rows; + if (rows[j]) { + l = c.columns; // rows[j].cells.length; + for (i = 0; i < l; i++) { + h = c.$headers.filter('[data-column="' + i + '"]:last'); + // get column indexed table cell + ch = ts.getColumnData( table, c.headers, i ); + // get column parser/extractor + e = ts.getParserById( ts.getData(h, ch, 'extractor') ); + p = ts.getParserById( ts.getData(h, ch, 'sorter') ); + np = ts.getData(h, ch, 'parser') === 'false'; + // empty cells behaviour - keeping emptyToBottom for backwards compatibility + c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' ); + // text strings behaviour in numerical sorts + c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max'; + if (np) { + p = ts.getParserById('no-parser'); + } + if (!e) { + // For now, maybe detect someday + e = false; + } + if (!p) { + p = detectParserForColumn(table, rows, -1, i); + } + if (c.debug) { + parsersDebug += "column:" + i + "; extractor:" + e.id + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n"; + } + list.parsers[i] = p; + list.extractors[i] = e; + } + } + j += (list.parsers.length) ? len : 1; + } + if (c.debug) { + log(parsersDebug ? parsersDebug : "No parsers detected"); + benchmark("Completed detecting parsers", time); + } + c.parsers = list.parsers; + c.extractors = list.extractors; + } + + /* utils */ + function buildCache(table) { + var cc, t, tx, v, i, j, k, $row, rows, cols, cacheTime, + totalRows, rowData, colMax, + c = table.config, + $tb = c.$table.children('tbody'), + extractors = c.extractors, + parsers = c.parsers; + c.cache = {}; + c.totalRows = 0; + // if no parsers found, return - it's an empty table. + if (!parsers) { + return c.debug ? log('Warning: *Empty table!* Not building a cache') : ''; + } + if (c.debug) { + cacheTime = new Date(); + } + // processing icon + if (c.showProcessing) { + ts.isProcessing(table, true); + } + for (k = 0; k < $tb.length; k++) { + colMax = []; // column max value per tbody + cc = c.cache[k] = { + normalized: [] // array of normalized row data; last entry contains "rowData" above + // colMax: # // added at the end + }; + + // ignore tbodies with class name from c.cssInfoBlock + if (!$tb.eq(k).hasClass(c.cssInfoBlock)) { + totalRows = ($tb[k] && $tb[k].rows.length) || 0; + for (i = 0; i < totalRows; ++i) { + rowData = { + // order: original row order # + // $row : jQuery Object[] + child: [] // child row text (filter widget) + }; + /** Add the table data to main data array */ + $row = $($tb[k].rows[i]); + rows = [ new Array(c.columns) ]; + cols = []; + // if this is a child row, add it to the last row's children and continue to the next row + // ignore child row class, if it is the first row + if ($row.hasClass(c.cssChildRow) && i !== 0) { + t = cc.normalized.length - 1; + cc.normalized[t][c.columns].$row = cc.normalized[t][c.columns].$row.add($row); + // add "hasChild" class name to parent row + if (!$row.prev().hasClass(c.cssChildRow)) { + $row.prev().addClass(ts.css.cssHasChild); + } + // save child row content (un-parsed!) + rowData.child[t] = $.trim( $row[0].textContent || $row[0].innerText || $row.text() || "" ); + // go to the next for loop + continue; + } + rowData.$row = $row; + rowData.order = i; // add original row position to rowCache + for (j = 0; j < c.columns; ++j) { + if (typeof parsers[j] === 'undefined') { + if (c.debug) { + log('No parser found for cell:', $row[0].cells[j], 'does it have a header?'); + } + continue; + } + t = getElementText(table, $row[0].cells[j], j); + // do extract before parsing if there is one + if (typeof extractors[j].id === 'undefined') { + tx = t; + } else { + tx = extractors[j].format(t, table, $row[0].cells[j], j); + } + // allow parsing if the string is empty, previously parsing would change it to zero, + // in case the parser needs to extract data from the table cell attributes + v = parsers[j].id === 'no-parser' ? '' : parsers[j].format(tx, table, $row[0].cells[j], j); + cols.push( c.ignoreCase && typeof v === 'string' ? v.toLowerCase() : v ); + if ((parsers[j].type || '').toLowerCase() === "numeric") { + // determine column max value (ignore sign) + colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); + } + } + // ensure rowData is always in the same location (after the last column) + cols[c.columns] = rowData; + cc.normalized.push(cols); + } + cc.colMax = colMax; + // total up rows, not including child rows + c.totalRows += cc.normalized.length; + } + } + if (c.showProcessing) { + ts.isProcessing(table); // remove processing icon + } + if (c.debug) { + benchmark("Building cache for " + totalRows + " rows", cacheTime); + } + } + + // init flag (true) used by pager plugin to prevent widget application + function appendToTable(table, init) { + var c = table.config, + wo = c.widgetOptions, + b = table.tBodies, + rows = [], + cc = c.cache, + n, totalRows, $bk, $tb, + i, k, appendTime; + // empty table - fixes #206/#346 + if (isEmptyObject(cc)) { + // run pager appender in case the table was just emptied + return c.appender ? c.appender(table, rows) : + table.isUpdating ? c.$table.trigger("updateComplete", table) : ''; // Fixes #532 + } + if (c.debug) { + appendTime = new Date(); + } + for (k = 0; k < b.length; k++) { + $bk = $(b[k]); + if ($bk.length && !$bk.hasClass(c.cssInfoBlock)) { + // get tbody + $tb = ts.processTbody(table, $bk, true); + $tb.children().detach(); // remove rows + n = cc[k].normalized; + totalRows = n.length; + for (i = 0; i < totalRows; i++) { + rows.push(n[i][c.columns].$row); + // removeRows used by the pager plugin; don't render if using ajax - fixes #411 + if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) { + $tb.append(n[i][c.columns].$row); + } + } + // restore tbody + ts.processTbody(table, $tb, false); + } + } + if (c.appender) { + c.appender(table, rows); + } + if (c.debug) { + benchmark("Rebuilt table", appendTime); + } + // apply table widgets; but not before ajax completes + if (!init && !c.appender) { ts.applyWidget(table); } + if (table.isUpdating) { + c.$table.trigger("updateComplete", table); + } + } + + function formatSortingOrder(v) { + // look for "d" in "desc" order; return true + return (/^d/i.test(v) || v === 1); + } + + function buildHeaders(table) { + var ch, $t, + h, i, t, lock, time, + c = table.config; + c.headerList = []; + c.headerContent = []; + if (c.debug) { + time = new Date(); + } + // children tr in tfoot - see issue #196 & #547 + c.columns = ts.computeColumnIndex( c.$table.children('thead, tfoot').children('tr') ); + // add icon if cssIcon option exists + i = c.cssIcon ? '' : ''; + // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 + c.$headers = $(table).find(c.selectorHeaders).each(function(index) { + $t = $(this); + // make sure to get header cell & not column indexed cell + ch = ts.getColumnData( table, c.headers, index, true ); + // save original header content + c.headerContent[index] = $(this).html(); + // set up header template + t = c.headerTemplate.replace(/\{content\}/g, $(this).html()).replace(/\{icon\}/g, i); + if (c.onRenderTemplate) { + h = c.onRenderTemplate.apply($t, [index, t]); + if (h && typeof h === 'string') { t = h; } // only change t if something is returned + } + $(this).html('