diff --git a/.gitignore b/.gitignore index 1cc5b6e..09f5cfb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ src/*.map *.diff *.patch .DS_Store -settings.json \ No newline at end of file +settings.json +bower_components/ diff --git a/demo/index.htm b/demo/index.htm index ae13225..84edd2e 100644 --- a/demo/index.htm +++ b/demo/index.htm @@ -189,6 +189,7 @@ return "<a href=\"#\">" + column.id + ": " + row.id + "</a>"; } }, + caseSensitive: false, rowCount: [-1, 10, 50, 75] }); } diff --git a/dist/jQuery.Bootgrid.1.3.1.nupkg b/dist/jQuery.Bootgrid.1.3.1.nupkg deleted file mode 100644 index c430a89..0000000 Binary files a/dist/jQuery.Bootgrid.1.3.1.nupkg and /dev/null differ diff --git a/dist/jquery.bootgrid-1.3.1.zip b/dist/jquery.bootgrid-1.3.1.zip index 61cbe12..b7933e9 100644 Binary files a/dist/jquery.bootgrid-1.3.1.zip and b/dist/jquery.bootgrid-1.3.1.zip differ diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 13512d9..c90b472 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.3.1 - 09/11/2015 - * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.3.1 - 02/17/2016 + * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ .bootgrid-header, diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index c120aa0..d858ce0 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.3.1 - 09/11/2015 - * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.3.1 - 02/17/2016 + * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) @@ -8,12 +8,12 @@ /*jshint validthis: true */ "use strict"; - $.extend($.fn.bootgrid.Constructor.defaults.css, { - icon: "icon fa", - iconColumns: "fa-th-list", - iconDown: "fa-sort-desc", - iconRefresh: "fa-refresh", - iconSearch: "fa-search", - iconUp: "fa-sort-asc" +$.extend($.fn.bootgrid.Constructor.defaults.css, { + icon: "icon fa", + iconColumns: "fa-th-list", + iconDown: "fa-sort-desc", + iconRefresh: "fa-refresh", + iconSearch: "fa-search", + iconUp: "fa-sort-asc" }); })(jQuery, window); \ No newline at end of file diff --git a/dist/jquery.bootgrid.fa.min.js b/dist/jquery.bootgrid.fa.min.js index 5e78cfb..7f60014 100644 --- a/dist/jquery.bootgrid.fa.min.js +++ b/dist/jquery.bootgrid.fa.min.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.3.1 - 09/11/2015 - * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.3.1 - 02/17/2016 + * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ !function(a,b,c){"use strict";a.extend(a.fn.bootgrid.Constructor.defaults.css,{icon:"icon fa",iconColumns:"fa-th-list",iconDown:"fa-sort-desc",iconRefresh:"fa-refresh",iconSearch:"fa-search",iconUp:"fa-sort-asc"})}(jQuery,window); \ No newline at end of file diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 4f27d44..521424a 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.3.1 - 09/11/2015 - * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.3.1 - 02/17/2016 + * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) @@ -8,2035 +8,2065 @@ /*jshint validthis: true */ "use strict"; - // GRID INTERNAL FIELDS - // ==================== +// GRID INTERNAL FIELDS +// ==================== - var namespace = ".rs.jquery.bootgrid"; +var namespace = ".rs.jquery.bootgrid"; - // GRID INTERNAL FUNCTIONS - // ===================== +// GRID INTERNAL FUNCTIONS +// ===================== - function appendRow(row) - { - var that = this; - - function exists(item) - { - return that.identifier && item[that.identifier] === row[that.identifier]; - } - - if (!this.rows.contains(exists)) - { - this.rows.push(row); - return true; - } - - return false; - } +function appendRow(row) +{ + var that = this; - function findFooterAndHeaderItems(selector) + function exists(item) { - var footer = (this.footer) ? this.footer.find(selector) : $(), - header = (this.header) ? this.header.find(selector) : $(); - return $.merge(footer, header); + return that.identifier && item[that.identifier] === row[that.identifier]; } - function getParams(context) + if (!this.rows.contains(exists)) { - return (context) ? $.extend({}, this.cachedParams, { ctx: context }) : - this.cachedParams; + this.rows.push(row); + return true; } - function getRequest() - { - var request = { - current: this.current, - rowCount: this.rowCount, - sort: this.sortDictionary, - searchPhrase: this.searchPhrase - }, - post = this.options.post; + return false; +} - post = ($.isFunction(post)) ? post() : post; - return this.options.requestHandler($.extend(true, request, post)); - } +function findFooterAndHeaderItems(selector) +{ + var footer = (this.footer) ? this.footer.find(selector) : $(), + header = (this.header) ? this.header.find(selector) : $(); + return $.merge(footer, header); +} - function getCssSelector(css) - { - return "." + $.trim(css).replace(/\s+/gm, "."); - } +function getParams(context) +{ + return (context) ? $.extend({}, this.cachedParams, { ctx: context }) : + this.cachedParams; +} + +function getRequest() +{ + var request = { + current: this.current, + rowCount: this.rowCount, + sort: this.sortDictionary, + searchPhrase: this.searchPhrase + }, + post = this.options.post; + + post = ($.isFunction(post)) ? post() : post; + return this.options.requestHandler($.extend(true, request, post)); +} + +function getCssSelector(css) +{ + return "." + $.trim(css).replace(/\s+/gm, "."); +} + +function getUrl() +{ + var url = this.options.url; + return ($.isFunction(url)) ? url() : url; +} + +function init() +{ + this.element.trigger("initialize" + namespace); + + loadColumns.call(this); // Loads columns from HTML thead tag + this.selection = this.options.selection && this.identifier != null; + loadRows.call(this); // Loads rows from HTML tbody tag if ajax is false + prepareTable.call(this); + renderTableHeader.call(this); + renderSearchField.call(this); + renderActions.call(this); + loadData.call(this); - function getUrl() + this.element.trigger("initialized" + namespace); +} + +function highlightAppendedRows(rows) +{ + if (this.options.highlightRows) { - var url = this.options.url; - return ($.isFunction(url)) ? url() : url; + // todo: implement } +} - function init() - { - this.element.trigger("initialize" + namespace); - - loadColumns.call(this); // Loads columns from HTML thead tag - this.selection = this.options.selection && this.identifier != null; - loadRows.call(this); // Loads rows from HTML tbody tag if ajax is false - prepareTable.call(this); - renderTableHeader.call(this); - renderSearchField.call(this); - renderActions.call(this); - loadData.call(this); +function isVisible(column) +{ + return column.visible; +} - this.element.trigger("initialized" + namespace); - } +function loadColumns() +{ + var that = this, + firstHeadRow = this.element.find("thead > tr").first(), + sorted = false; - function highlightAppendedRows(rows) + /*jshint -W018*/ + firstHeadRow.children().each(function () { - if (this.options.highlightRows) + var $this = $(this), + data = $this.data(), + column = { + id: data.columnId, + identifier: that.identifier == null && data.identifier || false, + converter: that.options.converters[data.converter || data.type] || that.options.converters["string"], + text: $this.text(), + align: data.align || "left", + headerAlign: data.headerAlign || "left", + cssClass: data.cssClass || "", + headerCssClass: data.headerCssClass || "", + formatter: that.options.formatters[data.formatter] || null, + order: (!sorted && (data.order === "asc" || data.order === "desc")) ? data.order : null, + searchable: !(data.searchable === false), // default: true + sortable: !(data.sortable === false), // default: true + visible: !(data.visible === false), // default: true + visibleInSelection: !(data.visibleInSelection === false), // default: true + width: ($.isNumeric(data.width)) ? data.width + "px" : + (typeof(data.width) === "string") ? data.width : null + }; + that.columns.push(column); + if (column.order != null) { - // todo: implement + that.sortDictionary[column.id] = column.order; } - } - - function isVisible(column) - { - return column.visible; - } - function loadColumns() - { - var that = this, - firstHeadRow = this.element.find("thead > tr").first(), - sorted = false; + // Prevents multiple identifiers + if (column.identifier) + { + that.identifier = column.id; + that.converter = column.converter; + } - /*jshint -W018*/ - firstHeadRow.children().each(function () + // ensures that only the first order will be applied in case of multi sorting is disabled + if (!that.options.multiSort && column.order !== null) { - var $this = $(this), - data = $this.data(), - column = { - id: data.columnId, - identifier: that.identifier == null && data.identifier || false, - converter: that.options.converters[data.converter || data.type] || that.options.converters["string"], - text: $this.text(), - align: data.align || "left", - headerAlign: data.headerAlign || "left", - cssClass: data.cssClass || "", - headerCssClass: data.headerCssClass || "", - formatter: that.options.formatters[data.formatter] || null, - order: (!sorted && (data.order === "asc" || data.order === "desc")) ? data.order : null, - searchable: !(data.searchable === false), // default: true - sortable: !(data.sortable === false), // default: true - visible: !(data.visible === false), // default: true - visibleInSelection: !(data.visibleInSelection === false), // default: true - width: ($.isNumeric(data.width)) ? data.width + "px" : - (typeof(data.width) === "string") ? data.width : null - }; - that.columns.push(column); - if (column.order != null) - { - that.sortDictionary[column.id] = column.order; - } + sorted = true; + } + }); + /*jshint +W018*/ +} + +/* +response = { + current: 1, + rowCount: 10, + rows: [{}, {}], + sort: [{ "columnId": "asc" }], + total: 101 +} +*/ + +function loadData() +{ + var that = this; - // Prevents multiple identifiers - if (column.identifier) - { - that.identifier = column.id; - that.converter = column.converter; - } + this.element._bgBusyAria(true).trigger("load" + namespace); + showLoading.call(this); - // ensures that only the first order will be applied in case of multi sorting is disabled - if (!that.options.multiSort && column.order !== null) - { - sorted = true; - } - }); - /*jshint +W018*/ + function stringSearch(needle, haystack) + { + var index; + if (that.options.regexSearch) { + index = haystack.search(needle); + } else { + index = haystack.toLowerCase().indexOf(needle); + } + return index > -1 ? true : false; } - /* - response = { - current: 1, - rowCount: 10, - rows: [{}, {}], - sort: [{ "columnId": "asc" }], - total: 101 + function getRegexSearchPhrase (searchPhrase) { + return new RegExp(searchPhrase, that.options.caseSensitive ? "g" : "gi"); } - */ - function loadData() - { - var that = this; + function getPlainSearchPhrase (searchPhrase) { + return that.options.caseSensitive ? searchPhrase : searchPhrase.toLowerCase(); + } - this.element._bgBusyAria(true).trigger("load" + namespace); - showLoading.call(this); + function containsPhrase(row) + { + var column; + var searchPattern = + that.options.regexSearch ? + getRegexSearchPhrase(that.searchPhrase) : + getPlainSearchPhrase(that.searchPhrase); - function containsPhrase(row) + for (var i = 0; i < that.columns.length; i++) { - var column, - searchPattern = new RegExp(that.searchPhrase, (that.options.caseSensitive) ? "g" : "gi"); - - for (var i = 0; i < that.columns.length; i++) + column = that.columns[i]; + if (column.searchable && column.visible && stringSearch(searchPattern, column.converter.to(row[column.id]))) { - column = that.columns[i]; - if (column.searchable && column.visible && - column.converter.to(row[column.id]).search(searchPattern) > -1) - { - return true; - } + return true; } - - return false; } - function update(rows, total) + return false; + } + + function update(rows, total) + { + that.currentRows = rows; + setTotals.call(that, total); + + if (!that.options.keepSelection) { - that.currentRows = rows; - setTotals.call(that, total); + that.selectedRows = []; + } - if (!that.options.keepSelection) - { - that.selectedRows = []; - } + renderRows.call(that, rows); + renderInfos.call(that); + renderPagination.call(that); - renderRows.call(that, rows); - renderInfos.call(that); - renderPagination.call(that); + that.element._bgBusyAria(false).trigger("loaded" + namespace); + } - that.element._bgBusyAria(false).trigger("loaded" + namespace); - } + if (this.options.ajax) + { + var request = getRequest.call(this), + url = getUrl.call(this); - if (this.options.ajax) + if (url == null || typeof url !== "string" || url.length === 0) { - var request = getRequest.call(this), - url = getUrl.call(this); + throw new Error("Url setting must be a none empty string or a function that returns one."); + } - if (url == null || typeof url !== "string" || url.length === 0) - { - throw new Error("Url setting must be a none empty string or a function that returns one."); - } + // aborts the previous ajax request if not already finished or failed + if (this.xqr) + { + this.xqr.abort(); + } - // aborts the previous ajax request if not already finished or failed - if (this.xqr) + var settings = { + url: url, + data: request, + success: function(response) { - this.xqr.abort(); - } + that.xqr = null; - var settings = { - url: url, - data: request, - success: function(response) + if (typeof (response) === "string") { - that.xqr = null; + response = $.parseJSON(response); + } - if (typeof (response) === "string") - { - response = $.parseJSON(response); - } + response = that.options.responseHandler(response); - response = that.options.responseHandler(response); + that.current = response.current; + update(response.rows, response.total); + }, + error: function (jqXHR, textStatus, errorThrown) + { + that.xqr = null; - that.current = response.current; - update(response.rows, response.total); - }, - error: function (jqXHR, textStatus, errorThrown) + if (textStatus !== "abort") { - that.xqr = null; - - if (textStatus !== "abort") - { - renderNoResultsRow.call(that); // overrides loading mask - that.element._bgBusyAria(false).trigger("loaded" + namespace); - } + renderNoResultsRow.call(that); // overrides loading mask + that.element._bgBusyAria(false).trigger("loaded" + namespace); } - }; - settings = $.extend(this.options.ajaxSettings, settings); - - this.xqr = $.ajax(settings); - } - else - { - var rows = (this.searchPhrase.length > 0) ? this.rows.where(containsPhrase) : this.rows, - total = rows.length; - if (this.rowCount !== -1) - { - rows = rows.page(this.current, this.rowCount); } + }; + settings = $.extend(this.options.ajaxSettings, settings); - // todo: improve the following comment - // setTimeout decouples the initialization so that adding event handlers happens before - window.setTimeout(function () { update(rows, total); }, 10); + this.xqr = $.ajax(settings); + } + else + { + var rows = (this.searchPhrase.length > 0) ? this.rows.where(containsPhrase) : this.rows, + total = rows.length; + if (this.rowCount !== -1) + { + rows = rows.page(this.current, this.rowCount); } + + // todo: improve the following comment + // setTimeout decouples the initialization so that adding event handlers happens before + window.setTimeout(function () { update(rows, total); }, 10); } +} - function loadRows() +function loadRows() +{ + if (!this.options.ajax) { - if (!this.options.ajax) + var that = this, + rows = this.element.find("tbody > tr"); + + rows.each(function () { - var that = this, - rows = this.element.find("tbody > tr"); + var $this = $(this), + cells = $this.children("td"), + row = {}; - rows.each(function () + $.each(that.columns, function (i, column) { - var $this = $(this), - cells = $this.children("td"), - row = {}; + row[column.id] = column.converter.from(cells.eq(i).text()); + }); - $.each(that.columns, function (i, column) - { - row[column.id] = column.converter.from(cells.eq(i).text()); - }); + appendRow.call(that, row); + }); - appendRow.call(that, row); - }); + setTotals.call(this, this.rows.length); + sortRows.call(this); + } +} - setTotals.call(this, this.rows.length); - sortRows.call(this); - } +function setTotals(total) +{ + this.total = total; + this.totalPages = (this.rowCount === -1) ? 1 : + Math.ceil(this.total / this.rowCount); +} + +function prepareTable() +{ + var tpl = this.options.templates, + wrapper = (this.element.parent().hasClass(this.options.css.responsiveTable)) ? + this.element.parent() : this.element; + + this.element.addClass(this.options.css.table); + + // checks whether there is an tbody element; otherwise creates one + if (this.element.children("tbody").length === 0) + { + this.element.append(tpl.body); } - function setTotals(total) + if (this.options.navigation & 1) { - this.total = total; - this.totalPages = (this.rowCount === -1) ? 1 : - Math.ceil(this.total / this.rowCount); + this.header = $(tpl.header.resolve(getParams.call(this, { id: this.element._bgId() + "-header" }))); + wrapper.before(this.header); } - function prepareTable() + if (this.options.navigation & 2) { - var tpl = this.options.templates, - wrapper = (this.element.parent().hasClass(this.options.css.responsiveTable)) ? - this.element.parent() : this.element; + this.footer = $(tpl.footer.resolve(getParams.call(this, { id: this.element._bgId() + "-footer" }))); + wrapper.after(this.footer); + } +} - this.element.addClass(this.options.css.table); +function renderActions() +{ + if (this.options.navigation !== 0) + { + var css = this.options.css, + selector = getCssSelector(css.actions), + actionItems = findFooterAndHeaderItems.call(this, selector); - // checks whether there is an tbody element; otherwise creates one - if (this.element.children("tbody").length === 0) + if (actionItems.length > 0) { - this.element.append(tpl.body); - } + var that = this, + tpl = this.options.templates, + actions = $(tpl.actions.resolve(getParams.call(this))); - if (this.options.navigation & 1) - { - this.header = $(tpl.header.resolve(getParams.call(this, { id: this.element._bgId() + "-header" }))); - wrapper.before(this.header); - } + // Refresh Button + if (this.options.ajax) + { + var refreshIcon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconRefresh })), + refresh = $(tpl.actionButton.resolve(getParams.call(this, + { content: refreshIcon, text: this.options.labels.refresh }))) + .on("click" + namespace, function (e) + { + // todo: prevent multiple fast clicks (fast click detection) + e.stopPropagation(); + that.current = 1; + loadData.call(that); + }); + actions.append(refresh); + } - if (this.options.navigation & 2) - { - this.footer = $(tpl.footer.resolve(getParams.call(this, { id: this.element._bgId() + "-footer" }))); - wrapper.after(this.footer); + // Row count selection + renderRowCountSelection.call(this, actions); + + // Column selection + renderColumnSelection.call(this, actions); + + replacePlaceHolder.call(this, actionItems, actions); } } +} - function renderActions() +function renderColumnSelection(actions) +{ + if (this.options.columnSelection && this.columns.length > 1) { - if (this.options.navigation !== 0) - { - var css = this.options.css, - selector = getCssSelector(css.actions), - actionItems = findFooterAndHeaderItems.call(this, selector); + var that = this, + css = this.options.css, + tpl = this.options.templates, + icon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconColumns })), + dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: icon }))), + selector = getCssSelector(css.dropDownItem), + checkboxSelector = getCssSelector(css.dropDownItemCheckbox), + itemsSelector = getCssSelector(css.dropDownMenuItems); - if (actionItems.length > 0) + $.each(this.columns, function (i, column) + { + if (column.visibleInSelection) { - var that = this, - tpl = this.options.templates, - actions = $(tpl.actions.resolve(getParams.call(this))); - - // Refresh Button - if (this.options.ajax) - { - var refreshIcon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconRefresh })), - refresh = $(tpl.actionButton.resolve(getParams.call(this, - { content: refreshIcon, text: this.options.labels.refresh }))) - .on("click" + namespace, function (e) + var item = $(tpl.actionDropDownCheckboxItem.resolve(getParams.call(that, + { name: column.id, label: column.text, checked: column.visible }))) + .on("click" + namespace, selector, function (e) + { + e.stopPropagation(); + + var $this = $(this), + checkbox = $this.find(checkboxSelector); + if (!checkbox.prop("disabled")) { - // todo: prevent multiple fast clicks (fast click detection) - e.stopPropagation(); - that.current = 1; + column.visible = checkbox.prop("checked"); + var enable = that.columns.where(isVisible).length > 1; + $this.parents(itemsSelector).find(selector + ":has(" + checkboxSelector + ":checked)") + ._bgEnableAria(enable).find(checkboxSelector)._bgEnableField(enable); + + that.element.find("tbody").empty(); // Fixes an column visualization bug + renderTableHeader.call(that); loadData.call(that); - }); - actions.append(refresh); - } - - // Row count selection - renderRowCountSelection.call(this, actions); - - // Column selection - renderColumnSelection.call(this, actions); - - replacePlaceHolder.call(this, actionItems, actions); + } + }); + dropDown.find(getCssSelector(css.dropDownMenuItems)).append(item); } - } + }); + actions.append(dropDown); } +} - function renderColumnSelection(actions) +function renderInfos() +{ + if (this.options.navigation !== 0) { - if (this.options.columnSelection && this.columns.length > 1) - { - var that = this, - css = this.options.css, - tpl = this.options.templates, - icon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconColumns })), - dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: icon }))), - selector = getCssSelector(css.dropDownItem), - checkboxSelector = getCssSelector(css.dropDownItemCheckbox), - itemsSelector = getCssSelector(css.dropDownMenuItems); + var selector = getCssSelector(this.options.css.infos), + infoItems = findFooterAndHeaderItems.call(this, selector); - $.each(this.columns, function (i, column) - { - if (column.visibleInSelection) - { - var item = $(tpl.actionDropDownCheckboxItem.resolve(getParams.call(that, - { name: column.id, label: column.text, checked: column.visible }))) - .on("click" + namespace, selector, function (e) - { - e.stopPropagation(); - - var $this = $(this), - checkbox = $this.find(checkboxSelector); - if (!checkbox.prop("disabled")) - { - column.visible = checkbox.prop("checked"); - var enable = that.columns.where(isVisible).length > 1; - $this.parents(itemsSelector).find(selector + ":has(" + checkboxSelector + ":checked)") - ._bgEnableAria(enable).find(checkboxSelector)._bgEnableField(enable); - - that.element.find("tbody").empty(); // Fixes an column visualization bug - renderTableHeader.call(that); - loadData.call(that); - } - }); - dropDown.find(getCssSelector(css.dropDownMenuItems)).append(item); - } - }); - actions.append(dropDown); + if (infoItems.length > 0) + { + var end = (this.current * this.rowCount), + infos = $(this.options.templates.infos.resolve(getParams.call(this, { + end: (this.total === 0 || end === -1 || end > this.total) ? this.total : end, + start: (this.total === 0) ? 0 : (end - this.rowCount + 1), + total: this.total + }))); + + replacePlaceHolder.call(this, infoItems, infos); } } +} + +function renderNoResultsRow() +{ + var tbody = this.element.children("tbody").first(), + tpl = this.options.templates, + count = this.columns.where(isVisible).length; + + if (this.selection) + { + count = count + 1; + } + tbody.html(tpl.noResults.resolve(getParams.call(this, { columns: count }))); +} - function renderInfos() +function renderPagination() +{ + if (this.options.navigation !== 0) { - if (this.options.navigation !== 0) + var selector = getCssSelector(this.options.css.pagination), + paginationItems = findFooterAndHeaderItems.call(this, selector)._bgShowAria(this.rowCount !== -1); + + if (this.rowCount !== -1 && paginationItems.length > 0) { - var selector = getCssSelector(this.options.css.infos), - infoItems = findFooterAndHeaderItems.call(this, selector); + var tpl = this.options.templates, + current = this.current, + totalPages = this.totalPages, + pagination = $(tpl.pagination.resolve(getParams.call(this))), + offsetRight = totalPages - current, + offsetLeft = (this.options.padding - current) * -1, + startWith = ((offsetRight >= this.options.padding) ? + Math.max(offsetLeft, 1) : + Math.max((offsetLeft - this.options.padding + offsetRight), 1)), + maxCount = this.options.padding * 2 + 1, + count = (totalPages >= maxCount) ? maxCount : totalPages; + + renderPaginationItem.call(this, pagination, "first", "«", "first") + ._bgEnableAria(current > 1); + renderPaginationItem.call(this, pagination, "prev", "<", "prev") + ._bgEnableAria(current > 1); + + for (var i = 0; i < count; i++) + { + var pos = i + startWith; + renderPaginationItem.call(this, pagination, pos, pos, "page-" + pos) + ._bgEnableAria()._bgSelectAria(pos === current); + } - if (infoItems.length > 0) + if (count === 0) { - var end = (this.current * this.rowCount), - infos = $(this.options.templates.infos.resolve(getParams.call(this, { - end: (this.total === 0 || end === -1 || end > this.total) ? this.total : end, - start: (this.total === 0) ? 0 : (end - this.rowCount + 1), - total: this.total - }))); - - replacePlaceHolder.call(this, infoItems, infos); + renderPaginationItem.call(this, pagination, 1, 1, "page-" + 1) + ._bgEnableAria(false)._bgSelectAria(); } - } - } - function renderNoResultsRow() - { - var tbody = this.element.children("tbody").first(), - tpl = this.options.templates, - count = this.columns.where(isVisible).length; + renderPaginationItem.call(this, pagination, "next", ">", "next") + ._bgEnableAria(totalPages > current); + renderPaginationItem.call(this, pagination, "last", "»", "last") + ._bgEnableAria(totalPages > current); - if (this.selection) - { - count = count + 1; + replacePlaceHolder.call(this, paginationItems, pagination); } - tbody.html(tpl.noResults.resolve(getParams.call(this, { columns: count }))); } +} - function renderPagination() - { - if (this.options.navigation !== 0) - { - var selector = getCssSelector(this.options.css.pagination), - paginationItems = findFooterAndHeaderItems.call(this, selector)._bgShowAria(this.rowCount !== -1); - - if (this.rowCount !== -1 && paginationItems.length > 0) +function renderPaginationItem(list, page, text, markerCss) +{ + var that = this, + tpl = this.options.templates, + css = this.options.css, + values = getParams.call(this, { css: markerCss, text: text, page: page }), + item = $(tpl.paginationItem.resolve(values)) + .on("click" + namespace, getCssSelector(css.paginationButton), function (e) { - var tpl = this.options.templates, - current = this.current, - totalPages = this.totalPages, - pagination = $(tpl.pagination.resolve(getParams.call(this))), - offsetRight = totalPages - current, - offsetLeft = (this.options.padding - current) * -1, - startWith = ((offsetRight >= this.options.padding) ? - Math.max(offsetLeft, 1) : - Math.max((offsetLeft - this.options.padding + offsetRight), 1)), - maxCount = this.options.padding * 2 + 1, - count = (totalPages >= maxCount) ? maxCount : totalPages; - - renderPaginationItem.call(this, pagination, "first", "«", "first") - ._bgEnableAria(current > 1); - renderPaginationItem.call(this, pagination, "prev", "<", "prev") - ._bgEnableAria(current > 1); - - for (var i = 0; i < count; i++) - { - var pos = i + startWith; - renderPaginationItem.call(this, pagination, pos, pos, "page-" + pos) - ._bgEnableAria()._bgSelectAria(pos === current); - } + e.stopPropagation(); + e.preventDefault(); - if (count === 0) + var $this = $(this), + parent = $this.parent(); + if (!parent.hasClass("active") && !parent.hasClass("disabled")) { - renderPaginationItem.call(this, pagination, 1, 1, "page-" + 1) - ._bgEnableAria(false)._bgSelectAria(); + var commandList = { + first: 1, + prev: that.current - 1, + next: that.current + 1, + last: that.totalPages + }; + var command = $this.data("page"); + that.current = commandList[command] || command; + loadData.call(that); } + $this.trigger("blur"); + }); - renderPaginationItem.call(this, pagination, "next", ">", "next") - ._bgEnableAria(totalPages > current); - renderPaginationItem.call(this, pagination, "last", "»", "last") - ._bgEnableAria(totalPages > current); + list.append(item); + return item; +} - replacePlaceHolder.call(this, paginationItems, pagination); - } - } - } +function renderRowCountSelection(actions) +{ + var that = this, + rowCountList = this.options.rowCount; - function renderPaginationItem(list, page, text, markerCss) + function getText(value) { - var that = this, - tpl = this.options.templates, - css = this.options.css, - values = getParams.call(this, { css: markerCss, text: text, page: page }), - item = $(tpl.paginationItem.resolve(values)) - .on("click" + namespace, getCssSelector(css.paginationButton), function (e) - { - e.stopPropagation(); - e.preventDefault(); - - var $this = $(this), - parent = $this.parent(); - if (!parent.hasClass("active") && !parent.hasClass("disabled")) - { - var commandList = { - first: 1, - prev: that.current - 1, - next: that.current + 1, - last: that.totalPages - }; - var command = $this.data("page"); - that.current = commandList[command] || command; - loadData.call(that); - } - $this.trigger("blur"); - }); - - list.append(item); - return item; + return (value === -1) ? that.options.labels.all : value; } - function renderRowCountSelection(actions) + if ($.isArray(rowCountList)) { - var that = this, - rowCountList = this.options.rowCount; - - function getText(value) - { - return (value === -1) ? that.options.labels.all : value; - } + var css = this.options.css, + tpl = this.options.templates, + dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: getText(this.rowCount) }))), + menuSelector = getCssSelector(css.dropDownMenu), + menuTextSelector = getCssSelector(css.dropDownMenuText), + menuItemsSelector = getCssSelector(css.dropDownMenuItems), + menuItemSelector = getCssSelector(css.dropDownItemButton); - if ($.isArray(rowCountList)) + $.each(rowCountList, function (index, value) { - var css = this.options.css, - tpl = this.options.templates, - dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: getText(this.rowCount) }))), - menuSelector = getCssSelector(css.dropDownMenu), - menuTextSelector = getCssSelector(css.dropDownMenuText), - menuItemsSelector = getCssSelector(css.dropDownMenuItems), - menuItemSelector = getCssSelector(css.dropDownItemButton); + var item = $(tpl.actionDropDownItem.resolve(getParams.call(that, + { text: getText(value), action: value }))) + ._bgSelectAria(value === that.rowCount) + .on("click" + namespace, menuItemSelector, function (e) + { + e.preventDefault(); - $.each(rowCountList, function (index, value) - { - var item = $(tpl.actionDropDownItem.resolve(getParams.call(that, - { text: getText(value), action: value }))) - ._bgSelectAria(value === that.rowCount) - .on("click" + namespace, menuItemSelector, function (e) + var $this = $(this), + newRowCount = $this.data("action"); + if (newRowCount !== that.rowCount) { - e.preventDefault(); - - var $this = $(this), - newRowCount = $this.data("action"); - if (newRowCount !== that.rowCount) + // todo: sophisticated solution needed for calculating which page is selected + that.current = 1; // that.rowCount === -1 ---> All + that.rowCount = newRowCount; + $this.parents(menuItemsSelector).children().each(function () { - // todo: sophisticated solution needed for calculating which page is selected - that.current = 1; // that.rowCount === -1 ---> All - that.rowCount = newRowCount; - $this.parents(menuItemsSelector).children().each(function () - { - var $item = $(this), - currentRowCount = $item.find(menuItemSelector).data("action"); - $item._bgSelectAria(currentRowCount === newRowCount); - }); - $this.parents(menuSelector).find(menuTextSelector).text(getText(newRowCount)); - loadData.call(that); - } - }); - dropDown.find(menuItemsSelector).append(item); - }); - actions.append(dropDown); - } + var $item = $(this), + currentRowCount = $item.find(menuItemSelector).data("action"); + $item._bgSelectAria(currentRowCount === newRowCount); + }); + $this.parents(menuSelector).find(menuTextSelector).text(getText(newRowCount)); + loadData.call(that); + } + }); + dropDown.find(menuItemsSelector).append(item); + }); + actions.append(dropDown); } +} - function renderRows(rows) +function renderRows(rows) +{ + if (rows.length > 0) { - if (rows.length > 0) + var that = this, + css = this.options.css, + tpl = this.options.templates, + tbody = this.element.children("tbody").first(), + allRowsSelected = true, + html = ""; + + $.each(rows, function (index, row) { - var that = this, - css = this.options.css, - tpl = this.options.templates, - tbody = this.element.children("tbody").first(), - allRowsSelected = true, - html = ""; + var cells = "", + rowAttr = " data-row-id=\"" + ((that.identifier == null) ? index : row[that.identifier]) + "\"", + rowCss = ""; - $.each(rows, function (index, row) + if (that.selection) { - var cells = "", - rowAttr = " data-row-id=\"" + ((that.identifier == null) ? index : row[that.identifier]) + "\"", - rowCss = ""; - - if (that.selection) - { - var selected = ($.inArray(row[that.identifier], that.selectedRows) !== -1), - selectBox = tpl.select.resolve(getParams.call(that, - { type: "checkbox", value: row[that.identifier], checked: selected })); - cells += tpl.cell.resolve(getParams.call(that, { content: selectBox, css: css.selectCell })); - allRowsSelected = (allRowsSelected && selected); - if (selected) - { - rowCss += css.selected; - rowAttr += " aria-selected=\"true\""; - } - } - - var status = row.status != null && that.options.statusMapping[row.status]; - if (status) + var selected = ($.inArray(row[that.identifier], that.selectedRows) !== -1), + selectBox = tpl.select.resolve(getParams.call(that, + { type: "checkbox", value: row[that.identifier], checked: selected })); + cells += tpl.cell.resolve(getParams.call(that, { content: selectBox, css: css.selectCell })); + allRowsSelected = (allRowsSelected && selected); + if (selected) { - rowCss += status; + rowCss += css.selected; + rowAttr += " aria-selected=\"true\""; } + } - $.each(that.columns, function (j, column) - { - if (column.visible) - { - var value = ($.isFunction(column.formatter)) ? - column.formatter.call(that, column, row) : - column.converter.to(row[column.id]), - cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; - cells += tpl.cell.resolve(getParams.call(that, { - content: (value == null || value === "") ? " " : value, - css: ((column.align === "right") ? css.right : (column.align === "center") ? - css.center : css.left) + cssClass, - style: (column.width == null) ? "" : "width:" + column.width + ";" })); - } - }); + var status = row.status != null && that.options.statusMapping[row.status]; + if (status) + { + rowCss += status; + } - if (rowCss.length > 0) + $.each(that.columns, function (j, column) + { + if (column.visible) { - rowAttr += " class=\"" + rowCss + "\""; + var value = ($.isFunction(column.formatter)) ? + column.formatter.call(that, column, row) : + column.converter.to(row[column.id]), + cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; + cells += tpl.cell.resolve(getParams.call(that, { + content: (value == null || value === "") ? " " : value, + css: ((column.align === "right") ? css.right : (column.align === "center") ? + css.center : css.left) + cssClass, + style: (column.width == null) ? "" : "width:" + column.width + ";" })); } - html += tpl.row.resolve(getParams.call(that, { attr: rowAttr, cells: cells })); }); - // sets or clears multi selectbox state - that.element.find("thead " + getCssSelector(that.options.css.selectBox)) - .prop("checked", allRowsSelected); + if (rowCss.length > 0) + { + rowAttr += " class=\"" + rowCss + "\""; + } + html += tpl.row.resolve(getParams.call(that, { attr: rowAttr, cells: cells })); + }); - tbody.html(html); + // sets or clears multi selectbox state + that.element.find("thead " + getCssSelector(that.options.css.selectBox)) + .prop("checked", allRowsSelected); - registerRowEvents.call(this, tbody); - } - else - { - renderNoResultsRow.call(this); - } - } + tbody.html(html); - function registerRowEvents(tbody) + registerRowEvents.call(this, tbody); + } + else { - var that = this, - selectBoxSelector = getCssSelector(this.options.css.selectBox); - - if (this.selection) - { - tbody.off("click" + namespace, selectBoxSelector) - .on("click" + namespace, selectBoxSelector, function(e) - { - e.stopPropagation(); - - var $this = $(this), - id = that.converter.from($this.val()); + renderNoResultsRow.call(this); + } +} - if ($this.prop("checked")) - { - that.select([id]); - } - else - { - that.deselect([id]); - } - }); - } +function registerRowEvents(tbody) +{ + var that = this, + selectBoxSelector = getCssSelector(this.options.css.selectBox); - tbody.off("click" + namespace, "> tr") - .on("click" + namespace, "> tr", function(e) + if (this.selection) + { + tbody.off("click" + namespace, selectBoxSelector) + .on("click" + namespace, selectBoxSelector, function(e) { e.stopPropagation(); var $this = $(this), - id = (that.identifier == null) ? $this.data("row-id") : - that.converter.from($this.data("row-id") + ""), - row = (that.identifier == null) ? that.currentRows[id] : - that.currentRows.first(function (item) { return item[that.identifier] === id; }); + id = that.converter.from($this.val()); - if (that.selection && that.options.rowSelect) + if ($this.prop("checked")) { - if ($this.hasClass(that.options.css.selected)) - { - that.deselect([id]); - } - else - { - that.select([id]); - } + that.select([id]); + } + else + { + that.deselect([id]); } - - that.element.trigger("click" + namespace, [that.columns, row]); }); } - function renderSearchField() - { - if (this.options.navigation !== 0) + tbody.off("click" + namespace, "> tr") + .on("click" + namespace, "> tr", function(e) { - var css = this.options.css, - selector = getCssSelector(css.search), - searchItems = findFooterAndHeaderItems.call(this, selector); + e.stopPropagation(); + + var $this = $(this), + id = (that.identifier == null) ? $this.data("row-id") : + that.converter.from($this.data("row-id") + ""), + row = (that.identifier == null) ? that.currentRows[id] : + that.currentRows.first(function (item) { return item[that.identifier] === id; }); + + if (that.selection && that.options.rowSelect) + { + if ($this.hasClass(that.options.css.selected)) + { + that.deselect([id]); + } + else + { + that.select([id]); + } + } + + that.element.trigger("click" + namespace, [that.columns, row]); + }); +} + +function renderSearchField() +{ + if (this.options.navigation !== 0) + { + var css = this.options.css, + selector = getCssSelector(css.search), + searchItems = findFooterAndHeaderItems.call(this, selector); - if (searchItems.length > 0) + if (searchItems.length > 0) + { + var that = this, + tpl = this.options.templates, + timer = null, // fast keyup detection + currentValue = "", + searchFieldSelector = getCssSelector(css.searchField), + search = $(tpl.search.resolve(getParams.call(this))), + searchField = (search.is(searchFieldSelector)) ? search : + search.find(searchFieldSelector); + + searchField.on("keyup" + namespace, function (e) { - var that = this, - tpl = this.options.templates, - timer = null, // fast keyup detection - currentValue = "", - searchFieldSelector = getCssSelector(css.searchField), - search = $(tpl.search.resolve(getParams.call(this))), - searchField = (search.is(searchFieldSelector)) ? search : - search.find(searchFieldSelector); - - searchField.on("keyup" + namespace, function (e) + e.stopPropagation(); + var newValue = $(this).val(); + if (currentValue !== newValue || (e.which === 13 && newValue !== "")) { - e.stopPropagation(); - var newValue = $(this).val(); - if (currentValue !== newValue || (e.which === 13 && newValue !== "")) + currentValue = newValue; + if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) { - currentValue = newValue; - if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) + window.clearTimeout(timer); + timer = window.setTimeout(function () { - window.clearTimeout(timer); - timer = window.setTimeout(function () - { - executeSearch.call(that, newValue); - }, that.options.searchSettings.delay); - } + executeSearch.call(that, newValue); + }, that.options.searchSettings.delay); } - }); + } + }); - replacePlaceHolder.call(this, searchItems, search); - } + replacePlaceHolder.call(this, searchItems, search); } } +} - function executeSearch(phrase) +function executeSearch(phrase) +{ + if (this.searchPhrase !== phrase) { - if (this.searchPhrase !== phrase) - { - this.current = 1; - this.searchPhrase = phrase; - loadData.call(this); - } + this.current = 1; + this.searchPhrase = phrase; + loadData.call(this); } +} - function renderTableHeader() +function renderTableHeader() +{ + var that = this, + headerRow = this.element.find("thead > tr"), + css = this.options.css, + tpl = this.options.templates, + html = "", + sorting = this.options.sorting; + + if (this.selection) { - var that = this, - headerRow = this.element.find("thead > tr"), - css = this.options.css, - tpl = this.options.templates, - html = "", - sorting = this.options.sorting; + var selectBox = (this.options.multiSelect) ? + tpl.select.resolve(getParams.call(that, { type: "checkbox", value: "all" })) : ""; + html += tpl.rawHeaderCell.resolve(getParams.call(that, { content: selectBox, + css: css.selectCell })); + } - if (this.selection) + $.each(this.columns, function (index, column) + { + if (column.visible) { - var selectBox = (this.options.multiSelect) ? - tpl.select.resolve(getParams.call(that, { type: "checkbox", value: "all" })) : ""; - html += tpl.rawHeaderCell.resolve(getParams.call(that, { content: selectBox, - css: css.selectCell })); + var sortOrder = that.sortDictionary[column.id], + iconCss = ((sorting && sortOrder && sortOrder === "asc") ? css.iconUp : + (sorting && sortOrder && sortOrder === "desc") ? css.iconDown : ""), + icon = tpl.icon.resolve(getParams.call(that, { iconCss: iconCss })), + align = column.headerAlign, + cssClass = (column.headerCssClass.length > 0) ? " " + column.headerCssClass : ""; + html += tpl.headerCell.resolve(getParams.call(that, { + column: column, icon: icon, sortable: sorting && column.sortable && css.sortable || "", + css: ((align === "right") ? css.right : (align === "center") ? + css.center : css.left) + cssClass, + style: (column.width == null) ? "" : "width:" + column.width + ";" })); } + }); - $.each(this.columns, function (index, column) - { - if (column.visible) - { - var sortOrder = that.sortDictionary[column.id], - iconCss = ((sorting && sortOrder && sortOrder === "asc") ? css.iconUp : - (sorting && sortOrder && sortOrder === "desc") ? css.iconDown : ""), - icon = tpl.icon.resolve(getParams.call(that, { iconCss: iconCss })), - align = column.headerAlign, - cssClass = (column.headerCssClass.length > 0) ? " " + column.headerCssClass : ""; - html += tpl.headerCell.resolve(getParams.call(that, { - column: column, icon: icon, sortable: sorting && column.sortable && css.sortable || "", - css: ((align === "right") ? css.right : (align === "center") ? - css.center : css.left) + cssClass, - style: (column.width == null) ? "" : "width:" + column.width + ";" })); - } - }); + headerRow.html(html); - headerRow.html(html); + if (sorting) + { + var sortingSelector = getCssSelector(css.sortable); + headerRow.off("click" + namespace, sortingSelector) + .on("click" + namespace, sortingSelector, function (e) + { + e.preventDefault(); - if (sorting) - { - var sortingSelector = getCssSelector(css.sortable); - headerRow.off("click" + namespace, sortingSelector) - .on("click" + namespace, sortingSelector, function (e) - { - e.preventDefault(); + setTableHeaderSortDirection.call(that, $(this)); + sortRows.call(that); + loadData.call(that); + }); + } - setTableHeaderSortDirection.call(that, $(this)); - sortRows.call(that); - loadData.call(that); - }); - } + // todo: create a own function for that piece of code + if (this.selection && this.options.multiSelect) + { + var selectBoxSelector = getCssSelector(css.selectBox); + headerRow.off("click" + namespace, selectBoxSelector) + .on("click" + namespace, selectBoxSelector, function(e) + { + e.stopPropagation(); - // todo: create a own function for that piece of code - if (this.selection && this.options.multiSelect) - { - var selectBoxSelector = getCssSelector(css.selectBox); - headerRow.off("click" + namespace, selectBoxSelector) - .on("click" + namespace, selectBoxSelector, function(e) + if ($(this).prop("checked")) { - e.stopPropagation(); - - if ($(this).prop("checked")) - { - that.select(); - } - else - { - that.deselect(); - } - }); - } + that.select(); + } + else + { + that.deselect(); + } + }); } +} - function setTableHeaderSortDirection(element) - { - var css = this.options.css, - iconSelector = getCssSelector(css.icon), - columnId = element.data("column-id") || element.parents("th").first().data("column-id"), - sortOrder = this.sortDictionary[columnId], - icon = element.find(iconSelector); +function setTableHeaderSortDirection(element) +{ + var css = this.options.css, + iconSelector = getCssSelector(css.icon), + columnId = element.data("column-id") || element.parents("th").first().data("column-id"), + sortOrder = this.sortDictionary[columnId], + icon = element.find(iconSelector); - if (!this.options.multiSort) - { - element.parents("tr").first().find(iconSelector).removeClass(css.iconDown + " " + css.iconUp); - this.sortDictionary = {}; - } + if (!this.options.multiSort) + { + element.parents("tr").first().find(iconSelector).removeClass(css.iconDown + " " + css.iconUp); + this.sortDictionary = {}; + } - if (sortOrder && sortOrder === "asc") - { - this.sortDictionary[columnId] = "desc"; - icon.removeClass(css.iconUp).addClass(css.iconDown); - } - else if (sortOrder && sortOrder === "desc") + if (sortOrder && sortOrder === "asc") + { + this.sortDictionary[columnId] = "desc"; + icon.removeClass(css.iconUp).addClass(css.iconDown); + } + else if (sortOrder && sortOrder === "desc") + { + if (this.options.multiSort) { - if (this.options.multiSort) + var newSort = {}; + for (var key in this.sortDictionary) { - var newSort = {}; - for (var key in this.sortDictionary) + if (key !== columnId) { - if (key !== columnId) - { - newSort[key] = this.sortDictionary[key]; - } + newSort[key] = this.sortDictionary[key]; } - this.sortDictionary = newSort; - icon.removeClass(css.iconDown); - } - else - { - this.sortDictionary[columnId] = "asc"; - icon.removeClass(css.iconDown).addClass(css.iconUp); } + this.sortDictionary = newSort; + icon.removeClass(css.iconDown); } else { this.sortDictionary[columnId] = "asc"; - icon.addClass(css.iconUp); + icon.removeClass(css.iconDown).addClass(css.iconUp); } } - - function replacePlaceHolder(placeholder, element) + else { - placeholder.each(function (index, item) - { - // todo: check how append is implemented. Perhaps cloning here is superfluous. - $(item).before(element.clone(true)).remove(); - }); + this.sortDictionary[columnId] = "asc"; + icon.addClass(css.iconUp); } +} - function showLoading() +function replacePlaceHolder(placeholder, element) +{ + placeholder.each(function (index, item) { - var that = this; + // todo: check how append is implemented. Perhaps cloning here is superfluous. + $(item).before(element.clone(true)).remove(); + }); +} - window.setTimeout(function() +function showLoading() +{ + var that = this; + + window.setTimeout(function() + { + if (that.element._bgAria("busy") === "true") { - if (that.element._bgAria("busy") === "true") + var tpl = that.options.templates, + thead = that.element.children("thead").first(), + tbody = that.element.children("tbody").first(), + firstCell = tbody.find("tr > td").first(), + padding = (that.element.height() - thead.height()) - (firstCell.height() + 20), + count = that.columns.where(isVisible).length; + + if (that.selection) { - var tpl = that.options.templates, - thead = that.element.children("thead").first(), - tbody = that.element.children("tbody").first(), - firstCell = tbody.find("tr > td").first(), - padding = (that.element.height() - thead.height()) - (firstCell.height() + 20), - count = that.columns.where(isVisible).length; - - if (that.selection) - { - count = count + 1; - } - tbody.html(tpl.loading.resolve(getParams.call(that, { columns: count }))); - if (that.rowCount !== -1 && padding > 0) - { - tbody.find("tr > td").css("padding", "20px 0 " + padding + "px"); - } + count = count + 1; } - }, 250); - } + tbody.html(tpl.loading.resolve(getParams.call(that, { columns: count }))); + if (that.rowCount !== -1 && padding > 0) + { + tbody.find("tr > td").css("padding", "20px 0 " + padding + "px"); + } + } + }, 250); +} + +function sortRows() +{ + var sortArray = []; - function sortRows() + function sort(x, y, current) { - var sortArray = []; + current = current || 0; + var next = current + 1, + item = sortArray[current]; - function sort(x, y, current) + function sortOrder(value) { - current = current || 0; - var next = current + 1, - item = sortArray[current]; + return (item.order === "asc") ? value : value * -1; + } - function sortOrder(value) - { - return (item.order === "asc") ? value : value * -1; - } + return (x[item.id] > y[item.id]) ? sortOrder(1) : + (x[item.id] < y[item.id]) ? sortOrder(-1) : + (sortArray.length > next) ? sort(x, y, next) : 0; + } - return (x[item.id] > y[item.id]) ? sortOrder(1) : - (x[item.id] < y[item.id]) ? sortOrder(-1) : - (sortArray.length > next) ? sort(x, y, next) : 0; - } + if (!this.options.ajax) + { + var that = this; - if (!this.options.ajax) + for (var key in this.sortDictionary) { - var that = this; - - for (var key in this.sortDictionary) + if (this.options.multiSort || sortArray.length === 0) { - if (this.options.multiSort || sortArray.length === 0) - { - sortArray.push({ - id: key, - order: this.sortDictionary[key] - }); - } + sortArray.push({ + id: key, + order: this.sortDictionary[key] + }); } + } - if (sortArray.length > 0) - { - this.rows.sort(sort); - } + if (sortArray.length > 0) + { + this.rows.sort(sort); } } + } - // GRID PUBLIC CLASS DEFINITION - // ==================== +// GRID PUBLIC CLASS DEFINITION +// ==================== + +/** + * Represents the jQuery Bootgrid plugin. + * + * @class Grid + * @constructor + * @param element {Object} The corresponding DOM element. + * @param options {Object} The options to override default settings. + * @chainable + **/ +var Grid = function(element, options) +{ + this.element = $(element); + this.origin = this.element.clone(); + this.options = $.extend(true, {}, Grid.defaults, this.element.data(), options); + // overrides rowCount explicitly because deep copy ($.extend) leads to strange behaviour + var rowCount = this.options.rowCount = this.element.data().rowCount || options.rowCount || this.options.rowCount; + this.columns = []; + this.current = 1; + this.currentRows = []; + this.identifier = null; // The first column ID that is marked as identifier + this.selection = false; + this.converter = null; // The converter for the column that is marked as identifier + this.rowCount = ($.isArray(rowCount)) ? rowCount[0] : rowCount; + this.rows = []; + this.searchPhrase = ""; + this.selectedRows = []; + this.sortDictionary = {}; + this.total = 0; + this.totalPages = 0; + this.cachedParams = { + lbl: this.options.labels, + css: this.options.css, + ctx: {} + }; + this.header = null; + this.footer = null; + this.xqr = null; + + // todo: implement cache +}; + +/** + * An object that represents the default settings. + * + * @static + * @class defaults + * @for Grid + * @example + * // Global approach + * $.bootgrid.defaults.selection = true; + * @example + * // Initialization approach + * $("#bootgrid").bootgrid({ selection = true }); + **/ +Grid.defaults = { + navigation: 3, // it's a flag: 0 = none, 1 = top, 2 = bottom, 3 = both (top and bottom) + padding: 2, // page padding (pagination) + columnSelection: true, + rowCount: [10, 25, 50, -1], // rows per page int or array of int (-1 represents "All") /** - * Represents the jQuery Bootgrid plugin. + * Enables row selection (to enable multi selection see also `multiSelect`). Default value is `false`. * - * @class Grid - * @constructor - * @param element {Object} The corresponding DOM element. - * @param options {Object} The options to override default settings. - * @chainable + * @property selection + * @type Boolean + * @default false + * @for defaults + * @since 1.0.0 **/ - var Grid = function(element, options) - { - this.element = $(element); - this.origin = this.element.clone(); - this.options = $.extend(true, {}, Grid.defaults, this.element.data(), options); - // overrides rowCount explicitly because deep copy ($.extend) leads to strange behaviour - var rowCount = this.options.rowCount = this.element.data().rowCount || options.rowCount || this.options.rowCount; - this.columns = []; - this.current = 1; - this.currentRows = []; - this.identifier = null; // The first column ID that is marked as identifier - this.selection = false; - this.converter = null; // The converter for the column that is marked as identifier - this.rowCount = ($.isArray(rowCount)) ? rowCount[0] : rowCount; - this.rows = []; - this.searchPhrase = ""; - this.selectedRows = []; - this.sortDictionary = {}; - this.total = 0; - this.totalPages = 0; - this.cachedParams = { - lbl: this.options.labels, - css: this.options.css, - ctx: {} - }; - this.header = null; - this.footer = null; - this.xqr = null; + selection: false, - // todo: implement cache - }; + /** + * Enables multi selection (`selection` must be set to `true` as well). Default value is `false`. + * + * @property multiSelect + * @type Boolean + * @default false + * @for defaults + * @since 1.0.0 + **/ + multiSelect: false, /** - * An object that represents the default settings. + * Enables entire row click selection (`selection` must be set to `true` as well). Default value is `false`. * - * @static - * @class defaults - * @for Grid - * @example - * // Global approach - * $.bootgrid.defaults.selection = true; - * @example - * // Initialization approach - * $("#bootgrid").bootgrid({ selection = true }); + * @property rowSelect + * @type Boolean + * @default false + * @for defaults + * @since 1.1.0 **/ - Grid.defaults = { - navigation: 3, // it's a flag: 0 = none, 1 = top, 2 = bottom, 3 = both (top and bottom) - padding: 2, // page padding (pagination) - columnSelection: true, - rowCount: [10, 25, 50, -1], // rows per page int or array of int (-1 represents "All") + rowSelect: false, - /** - * Enables row selection (to enable multi selection see also `multiSelect`). Default value is `false`. - * - * @property selection - * @type Boolean - * @default false - * @for defaults - * @since 1.0.0 - **/ - selection: false, + /** + * Defines whether the row selection is saved internally on filtering, paging and sorting + * (even if the selected rows are not visible). + * + * @property keepSelection + * @type Boolean + * @default false + * @for defaults + * @since 1.1.0 + **/ + keepSelection: false, - /** - * Enables multi selection (`selection` must be set to `true` as well). Default value is `false`. - * - * @property multiSelect - * @type Boolean - * @default false - * @for defaults - * @since 1.0.0 - **/ - multiSelect: false, + highlightRows: false, // highlights new rows (find the page of the first new row) + sorting: true, + multiSort: false, + /** + * General search settings to configure the search field behaviour. + * + * @property searchSettings + * @type Object + * @for defaults + * @since 1.2.0 + **/ + searchSettings: { /** - * Enables entire row click selection (`selection` must be set to `true` as well). Default value is `false`. + * The time in milliseconds to wait before search gets executed. * - * @property rowSelect - * @type Boolean - * @default false - * @for defaults - * @since 1.1.0 + * @property delay + * @type Number + * @default 250 + * @for searchSettings **/ - rowSelect: false, - + delay: 250, + /** - * Defines whether the row selection is saved internally on filtering, paging and sorting - * (even if the selected rows are not visible). + * The characters to type before the search gets executed. * - * @property keepSelection - * @type Boolean - * @default false - * @for defaults - * @since 1.1.0 + * @property characters + * @type Number + * @default 1 + * @for searchSettings **/ - keepSelection: false, + characters: 1 + }, - highlightRows: false, // highlights new rows (find the page of the first new row) - sorting: true, - multiSort: false, + /** + * Defines whether the data shall be loaded via an asynchronous HTTP (Ajax) request. + * + * @property ajax + * @type Boolean + * @default false + * @for defaults + **/ + ajax: false, + /** + * Ajax request settings that shall be used for server-side communication. + * All setting except data, error, success and url can be overridden. + * For the full list of settings go to http://api.jquery.com/jQuery.ajax/. + * + * @property ajaxSettings + * @type Object + * @for defaults + * @since 1.2.0 + **/ + ajaxSettings: { /** - * General search settings to configure the search field behaviour. + * Specifies the HTTP method which shall be used when sending data to the server. + * Go to http://api.jquery.com/jQuery.ajax/ for more details. + * This setting is overriden for backward compatibility. * - * @property searchSettings - * @type Object - * @for defaults - * @since 1.2.0 + * @property method + * @type String + * @default "POST" + * @for ajaxSettings **/ - searchSettings: { - /** - * The time in milliseconds to wait before search gets executed. - * - * @property delay - * @type Number - * @default 250 - * @for searchSettings - **/ - delay: 250, - - /** - * The characters to type before the search gets executed. - * - * @property characters - * @type Number - * @default 1 - * @for searchSettings - **/ - characters: 1 - }, + method: "POST" + }, - /** - * Defines whether the data shall be loaded via an asynchronous HTTP (Ajax) request. - * - * @property ajax - * @type Boolean - * @default false - * @for defaults - **/ - ajax: false, + /** + * Enriches the request object with additional properties. Either a `PlainObject` or a `Function` + * that returns a `PlainObject` can be passed. Default value is `{}`. + * + * @property post + * @type Object|Function + * @default function (request) { return request; } + * @for defaults + * @deprecated Use instead `requestHandler` + **/ + post: {}, // or use function () { return {}; } (reserved properties are "current", "rowCount", "sort" and "searchPhrase") - /** - * Ajax request settings that shall be used for server-side communication. - * All setting except data, error, success and url can be overridden. - * For the full list of settings go to http://api.jquery.com/jQuery.ajax/. - * - * @property ajaxSettings - * @type Object - * @for defaults - * @since 1.2.0 - **/ - ajaxSettings: { - /** - * Specifies the HTTP method which shall be used when sending data to the server. - * Go to http://api.jquery.com/jQuery.ajax/ for more details. - * This setting is overriden for backward compatibility. - * - * @property method - * @type String - * @default "POST" - * @for ajaxSettings - **/ - method: "POST" - }, + /** + * Sets the data URL to a data service (e.g. a REST service). Either a `String` or a `Function` + * that returns a `String` can be passed. Default value is `""`. + * + * @property url + * @type String|Function + * @default "" + * @for defaults + **/ + url: "", // or use function () { return ""; } - /** - * Enriches the request object with additional properties. Either a `PlainObject` or a `Function` - * that returns a `PlainObject` can be passed. Default value is `{}`. - * - * @property post - * @type Object|Function - * @default function (request) { return request; } - * @for defaults - * @deprecated Use instead `requestHandler` - **/ - post: {}, // or use function () { return {}; } (reserved properties are "current", "rowCount", "sort" and "searchPhrase") + /** + * Defines whether the search is case sensitive or insensitive. + * + * @property caseSensitive + * @type Boolean + * @default true + * @for defaults + * @since 1.1.0 + **/ + caseSensitive: true, - /** - * Sets the data URL to a data service (e.g. a REST service). Either a `String` or a `Function` - * that returns a `String` can be passed. Default value is `""`. - * - * @property url - * @type String|Function - * @default "" - * @for defaults - **/ - url: "", // or use function () { return ""; } + /** + * Defines if regex should be used when searching + * + * @property regexSearch + * @type Boolean + * @for defaults + **/ + regexSearch: true, - /** - * Defines whether the search is case sensitive or insensitive. - * - * @property caseSensitive - * @type Boolean - * @default true - * @for defaults - * @since 1.1.0 - **/ - caseSensitive: true, + // note: The following properties should not be used via data-api attributes + + /** + * Transforms the JSON request object in what ever is needed on the server-side implementation. + * + * @property requestHandler + * @type Function + * @default function (request) { return request; } + * @for defaults + * @since 1.1.0 + **/ + requestHandler: function (request) { return request; }, + + /** + * Transforms the response object into the expected JSON response object. + * + * @property responseHandler + * @type Function + * @default function (response) { return response; } + * @for defaults + * @since 1.1.0 + **/ + responseHandler: function (response) { return response; }, + + /** + * A list of converters. + * + * @property converters + * @type Object + * @for defaults + * @since 1.0.0 + **/ + converters: { + numeric: { + from: function (value) { return +value; }, // converts from string to numeric + to: function (value) { return value + ""; } // converts from numeric to string + }, + string: { + // default converter + from: function (value) { return value; }, + to: function (value) { return value; } + } + }, - // note: The following properties should not be used via data-api attributes + /** + * Contains all css classes. + * + * @property css + * @type Object + * @for defaults + **/ + css: { + actions: "actions btn-group", // must be a unique class name or constellation of class names within the header and footer + center: "text-center", + columnHeaderAnchor: "column-header-anchor", // must be a unique class name or constellation of class names within the column header cell + columnHeaderText: "text", + dropDownItem: "dropdown-item", // must be a unique class name or constellation of class names within the actionDropDown, + dropDownItemButton: "dropdown-item-button", // must be a unique class name or constellation of class names within the actionDropDown + dropDownItemCheckbox: "dropdown-item-checkbox", // must be a unique class name or constellation of class names within the actionDropDown + dropDownMenu: "dropdown btn-group", // must be a unique class name or constellation of class names within the actionDropDown + dropDownMenuItems: "dropdown-menu pull-right", // must be a unique class name or constellation of class names within the actionDropDown + dropDownMenuText: "dropdown-text", // must be a unique class name or constellation of class names within the actionDropDown + footer: "bootgrid-footer container-fluid", + header: "bootgrid-header container-fluid", + icon: "icon glyphicon", + iconColumns: "glyphicon-th-list", + iconDown: "glyphicon-chevron-down", + iconRefresh: "glyphicon-refresh", + iconSearch: "glyphicon-search", + iconUp: "glyphicon-chevron-up", + infos: "infos", // must be a unique class name or constellation of class names within the header and footer, + left: "text-left", + pagination: "pagination", // must be a unique class name or constellation of class names within the header and footer + paginationButton: "button", // must be a unique class name or constellation of class names within the pagination /** - * Transforms the JSON request object in what ever is needed on the server-side implementation. + * CSS class to select the parent div which activates responsive mode. * - * @property requestHandler - * @type Function - * @default function (request) { return request; } - * @for defaults + * @property responsiveTable + * @type String + * @default "table-responsive" + * @for css * @since 1.1.0 **/ - requestHandler: function (request) { return request; }, + responsiveTable: "table-responsive", + + right: "text-right", + search: "search form-group", // must be a unique class name or constellation of class names within the header and footer + searchField: "search-field form-control", + selectBox: "select-box", // must be a unique class name or constellation of class names within the entire table + selectCell: "select-cell", // must be a unique class name or constellation of class names within the entire table /** - * Transforms the response object into the expected JSON response object. + * CSS class to highlight selected rows. * - * @property responseHandler - * @type Function - * @default function (response) { return response; } - * @for defaults + * @property selected + * @type String + * @default "active" + * @for css * @since 1.1.0 **/ - responseHandler: function (response) { return response; }, + selected: "active", - /** - * A list of converters. - * - * @property converters - * @type Object - * @for defaults - * @since 1.0.0 - **/ - converters: { - numeric: { - from: function (value) { return +value; }, // converts from string to numeric - to: function (value) { return value + ""; } // converts from numeric to string - }, - string: { - // default converter - from: function (value) { return value; }, - to: function (value) { return value; } - } - }, + sortable: "sortable", + table: "bootgrid-table table" + }, - /** - * Contains all css classes. - * - * @property css - * @type Object - * @for defaults - **/ - css: { - actions: "actions btn-group", // must be a unique class name or constellation of class names within the header and footer - center: "text-center", - columnHeaderAnchor: "column-header-anchor", // must be a unique class name or constellation of class names within the column header cell - columnHeaderText: "text", - dropDownItem: "dropdown-item", // must be a unique class name or constellation of class names within the actionDropDown, - dropDownItemButton: "dropdown-item-button", // must be a unique class name or constellation of class names within the actionDropDown - dropDownItemCheckbox: "dropdown-item-checkbox", // must be a unique class name or constellation of class names within the actionDropDown - dropDownMenu: "dropdown btn-group", // must be a unique class name or constellation of class names within the actionDropDown - dropDownMenuItems: "dropdown-menu pull-right", // must be a unique class name or constellation of class names within the actionDropDown - dropDownMenuText: "dropdown-text", // must be a unique class name or constellation of class names within the actionDropDown - footer: "bootgrid-footer container-fluid", - header: "bootgrid-header container-fluid", - icon: "icon glyphicon", - iconColumns: "glyphicon-th-list", - iconDown: "glyphicon-chevron-down", - iconRefresh: "glyphicon-refresh", - iconSearch: "glyphicon-search", - iconUp: "glyphicon-chevron-up", - infos: "infos", // must be a unique class name or constellation of class names within the header and footer, - left: "text-left", - pagination: "pagination", // must be a unique class name or constellation of class names within the header and footer - paginationButton: "button", // must be a unique class name or constellation of class names within the pagination - - /** - * CSS class to select the parent div which activates responsive mode. - * - * @property responsiveTable - * @type String - * @default "table-responsive" - * @for css - * @since 1.1.0 - **/ - responsiveTable: "table-responsive", - - right: "text-right", - search: "search form-group", // must be a unique class name or constellation of class names within the header and footer - searchField: "search-field form-control", - selectBox: "select-box", // must be a unique class name or constellation of class names within the entire table - selectCell: "select-cell", // must be a unique class name or constellation of class names within the entire table - - /** - * CSS class to highlight selected rows. - * - * @property selected - * @type String - * @default "active" - * @for css - * @since 1.1.0 - **/ - selected: "active", - - sortable: "sortable", - table: "bootgrid-table table" - }, + /** + * A dictionary of formatters. + * + * @property formatters + * @type Object + * @for defaults + * @since 1.0.0 + **/ + formatters: {}, + + /** + * Contains all labels. + * + * @property labels + * @type Object + * @for defaults + **/ + labels: { + all: "All", + infos: "Showing {{ctx.start}} to {{ctx.end}} of {{ctx.total}} entries", + loading: "Loading...", + noResults: "No results found!", + refresh: "Refresh", + search: "Search" + }, + /** + * Specifies the mapping between status and contextual classes to color rows. + * + * @property statusMapping + * @type Object + * @for defaults + * @since 1.2.0 + **/ + statusMapping: { /** - * A dictionary of formatters. + * Specifies a successful or positive action. * - * @property formatters - * @type Object - * @for defaults - * @since 1.0.0 + * @property 0 + * @type String + * @for statusMapping **/ - formatters: {}, + 0: "success", /** - * Contains all labels. + * Specifies a neutral informative change or action. * - * @property labels - * @type Object - * @for defaults + * @property 1 + * @type String + * @for statusMapping **/ - labels: { - all: "All", - infos: "Showing {{ctx.start}} to {{ctx.end}} of {{ctx.total}} entries", - loading: "Loading...", - noResults: "No results found!", - refresh: "Refresh", - search: "Search" - }, + 1: "info", /** - * Specifies the mapping between status and contextual classes to color rows. + * Specifies a warning that might need attention. * - * @property statusMapping - * @type Object - * @for defaults - * @since 1.2.0 + * @property 2 + * @type String + * @for statusMapping **/ - statusMapping: { - /** - * Specifies a successful or positive action. - * - * @property 0 - * @type String - * @for statusMapping - **/ - 0: "success", - - /** - * Specifies a neutral informative change or action. - * - * @property 1 - * @type String - * @for statusMapping - **/ - 1: "info", - - /** - * Specifies a warning that might need attention. - * - * @property 2 - * @type String - * @for statusMapping - **/ - 2: "warning", - - /** - * Specifies a dangerous or potentially negative action. - * - * @property 3 - * @type String - * @for statusMapping - **/ - 3: "danger" - }, - + 2: "warning", + /** - * Contains all templates. + * Specifies a dangerous or potentially negative action. * - * @property templates - * @type Object - * @for defaults + * @property 3 + * @type String + * @for statusMapping **/ - templates: { - actionButton: "<button class=\"btn btn-default\" type=\"button\" title=\"{{ctx.text}}\">{{ctx.content}}</button>", - actionDropDown: "<div class=\"{{css.dropDownMenu}}\"><button class=\"btn btn-default dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\"><span class=\"{{css.dropDownMenuText}}\">{{ctx.content}}</span> <span class=\"caret\"></span></button><ul class=\"{{css.dropDownMenuItems}}\" role=\"menu\"></ul></div>", - actionDropDownItem: "<li><a data-action=\"{{ctx.action}}\" class=\"{{css.dropDownItem}} {{css.dropDownItemButton}}\">{{ctx.text}}</a></li>", - actionDropDownCheckboxItem: "<li><label class=\"{{css.dropDownItem}}\"><input name=\"{{ctx.name}}\" type=\"checkbox\" value=\"1\" class=\"{{css.dropDownItemCheckbox}}\" {{ctx.checked}} /> {{ctx.label}}</label></li>", - actions: "<div class=\"{{css.actions}}\"></div>", - body: "<tbody></tbody>", - cell: "<td class=\"{{ctx.css}}\" style=\"{{ctx.style}}\">{{ctx.content}}</td>", - footer: "<div id=\"{{ctx.id}}\" class=\"{{css.footer}}\"><div class=\"row\"><div class=\"col-sm-6\"><p class=\"{{css.pagination}}\"></p></div><div class=\"col-sm-6 infoBar\"><p class=\"{{css.infos}}\"></p></div></div></div>", - header: "<div id=\"{{ctx.id}}\" class=\"{{css.header}}\"><div class=\"row\"><div class=\"col-sm-12 actionBar\"><p class=\"{{css.search}}\"></p><p class=\"{{css.actions}}\"></p></div></div></div>", - headerCell: "<th data-column-id=\"{{ctx.column.id}}\" class=\"{{ctx.css}}\" style=\"{{ctx.style}}\"><a href=\"javascript:void(0);\" class=\"{{css.columnHeaderAnchor}} {{ctx.sortable}}\"><span class=\"{{css.columnHeaderText}}\">{{ctx.column.text}}</span>{{ctx.icon}}</a></th>", - icon: "<span class=\"{{css.icon}} {{ctx.iconCss}}\"></span>", - infos: "<div class=\"{{css.infos}}\">{{lbl.infos}}</div>", - loading: "<tr><td colspan=\"{{ctx.columns}}\" class=\"loading\">{{lbl.loading}}</td></tr>", - noResults: "<tr><td colspan=\"{{ctx.columns}}\" class=\"no-results\">{{lbl.noResults}}</td></tr>", - pagination: "<ul class=\"{{css.pagination}}\"></ul>", - paginationItem: "<li class=\"{{ctx.css}}\"><a data-page=\"{{ctx.page}}\" class=\"{{css.paginationButton}}\">{{ctx.text}}</a></li>", - rawHeaderCell: "<th class=\"{{ctx.css}}\">{{ctx.content}}</th>", // Used for the multi select box - row: "<tr{{ctx.attr}}>{{ctx.cells}}</tr>", - search: "<div class=\"{{css.search}}\"><div class=\"input-group\"><span class=\"{{css.icon}} input-group-addon {{css.iconSearch}}\"></span> <input type=\"text\" class=\"{{css.searchField}}\" placeholder=\"{{lbl.search}}\" /></div></div>", - select: "<input name=\"select\" type=\"{{ctx.type}}\" class=\"{{css.selectBox}}\" value=\"{{ctx.value}}\" {{ctx.checked}} />" - } - }; + 3: "danger" + }, /** - * Appends rows. + * Contains all templates. * - * @method append - * @param rows {Array} An array of rows to append - * @chainable + * @property templates + * @type Object + * @for defaults **/ - Grid.prototype.append = function(rows) + templates: { + actionButton: "<button class=\"btn btn-default\" type=\"button\" title=\"{{ctx.text}}\">{{ctx.content}}</button>", + actionDropDown: "<div class=\"{{css.dropDownMenu}}\"><button class=\"btn btn-default dropdown-toggle\" type=\"button\" data-toggle=\"dropdown\"><span class=\"{{css.dropDownMenuText}}\">{{ctx.content}}</span> <span class=\"caret\"></span></button><ul class=\"{{css.dropDownMenuItems}}\" role=\"menu\"></ul></div>", + actionDropDownItem: "<li><a data-action=\"{{ctx.action}}\" class=\"{{css.dropDownItem}} {{css.dropDownItemButton}}\">{{ctx.text}}</a></li>", + actionDropDownCheckboxItem: "<li><label class=\"{{css.dropDownItem}}\"><input name=\"{{ctx.name}}\" type=\"checkbox\" value=\"1\" class=\"{{css.dropDownItemCheckbox}}\" {{ctx.checked}} /> {{ctx.label}}</label></li>", + actions: "<div class=\"{{css.actions}}\"></div>", + body: "<tbody></tbody>", + cell: "<td class=\"{{ctx.css}}\" style=\"{{ctx.style}}\">{{ctx.content}}</td>", + footer: "<div id=\"{{ctx.id}}\" class=\"{{css.footer}}\"><div class=\"row\"><div class=\"col-sm-6\"><p class=\"{{css.pagination}}\"></p></div><div class=\"col-sm-6 infoBar\"><p class=\"{{css.infos}}\"></p></div></div></div>", + header: "<div id=\"{{ctx.id}}\" class=\"{{css.header}}\"><div class=\"row\"><div class=\"col-sm-12 actionBar\"><p class=\"{{css.search}}\"></p><p class=\"{{css.actions}}\"></p></div></div></div>", + headerCell: "<th data-column-id=\"{{ctx.column.id}}\" class=\"{{ctx.css}}\" style=\"{{ctx.style}}\"><a href=\"javascript:void(0);\" class=\"{{css.columnHeaderAnchor}} {{ctx.sortable}}\"><span class=\"{{css.columnHeaderText}}\">{{ctx.column.text}}</span>{{ctx.icon}}</a></th>", + icon: "<span class=\"{{css.icon}} {{ctx.iconCss}}\"></span>", + infos: "<div class=\"{{css.infos}}\">{{lbl.infos}}</div>", + loading: "<tr><td colspan=\"{{ctx.columns}}\" class=\"loading\">{{lbl.loading}}</td></tr>", + noResults: "<tr><td colspan=\"{{ctx.columns}}\" class=\"no-results\">{{lbl.noResults}}</td></tr>", + pagination: "<ul class=\"{{css.pagination}}\"></ul>", + paginationItem: "<li class=\"{{ctx.css}}\"><a data-page=\"{{ctx.page}}\" class=\"{{css.paginationButton}}\">{{ctx.text}}</a></li>", + rawHeaderCell: "<th class=\"{{ctx.css}}\">{{ctx.content}}</th>", // Used for the multi select box + row: "<tr{{ctx.attr}}>{{ctx.cells}}</tr>", + search: "<div class=\"{{css.search}}\"><div class=\"input-group\"><span class=\"{{css.icon}} input-group-addon {{css.iconSearch}}\"></span> <input type=\"text\" class=\"{{css.searchField}}\" placeholder=\"{{lbl.search}}\" /></div></div>", + select: "<input name=\"select\" type=\"{{ctx.type}}\" class=\"{{css.selectBox}}\" value=\"{{ctx.value}}\" {{ctx.checked}} />" + } +}; + +/** + * Appends rows. + * + * @method append + * @param rows {Array} An array of rows to append + * @chainable + **/ +Grid.prototype.append = function(rows) +{ + if (this.options.ajax) { - if (this.options.ajax) - { - // todo: implement ajax PUT - } - else + // todo: implement ajax PUT + } + else + { + var appendedRows = []; + for (var i = 0; i < rows.length; i++) { - var appendedRows = []; - for (var i = 0; i < rows.length; i++) + if (appendRow.call(this, rows[i])) { - if (appendRow.call(this, rows[i])) - { - appendedRows.push(rows[i]); - } + appendedRows.push(rows[i]); } - sortRows.call(this); - highlightAppendedRows.call(this, appendedRows); - loadData.call(this); - this.element.trigger("appended" + namespace, [appendedRows]); - } - - return this; - }; - - /** - * Removes all rows. - * - * @method clear - * @chainable - **/ - Grid.prototype.clear = function() - { - if (this.options.ajax) - { - // todo: implement ajax POST - } - else - { - var removedRows = $.extend([], this.rows); - this.rows = []; - this.current = 1; - this.total = 0; - loadData.call(this); - this.element.trigger("cleared" + namespace, [removedRows]); } + sortRows.call(this); + highlightAppendedRows.call(this, appendedRows); + loadData.call(this); + this.element.trigger("appended" + namespace, [appendedRows]); + } - return this; - }; + return this; +}; - /** - * Removes the control functionality completely and transforms the current state to the initial HTML structure. - * - * @method destroy - * @chainable - **/ - Grid.prototype.destroy = function() +/** + * Removes all rows. + * + * @method clear + * @chainable + **/ +Grid.prototype.clear = function() +{ + if (this.options.ajax) { - // todo: this method has to be optimized (the complete initial state must be restored) - $(window).off(namespace); - if (this.options.navigation & 1) - { - this.header.remove(); - } - if (this.options.navigation & 2) - { - this.footer.remove(); - } - this.element.before(this.origin).remove(); - - return this; - }; - - /** - * Resets the state and reloads rows. - * - * @method reload - * @chainable - **/ - Grid.prototype.reload = function() + // todo: implement ajax POST + } + else { - this.current = 1; // reset + var removedRows = $.extend([], this.rows); + this.rows = []; + this.current = 1; + this.total = 0; loadData.call(this); + this.element.trigger("cleared" + namespace, [removedRows]); + } - return this; - }; + return this; +}; - /** - * Removes rows by ids. Removes selected rows if no ids are provided. - * - * @method remove - * @param [rowsIds] {Array} An array of rows ids to remove - * @chainable - **/ - Grid.prototype.remove = function(rowIds) +/** + * Removes the control functionality completely and transforms the current state to the initial HTML structure. + * + * @method destroy + * @chainable + **/ +Grid.prototype.destroy = function() +{ + // todo: this method has to be optimized (the complete initial state must be restored) + $(window).off(namespace); + if (this.options.navigation & 1) { - if (this.identifier != null) + this.header.remove(); + } + if (this.options.navigation & 2) + { + this.footer.remove(); + } + this.element.before(this.origin).remove(); + + return this; +}; + +/** + * Resets the state and reloads rows. + * + * @method reload + * @chainable + **/ +Grid.prototype.reload = function() +{ + this.current = 1; // reset + loadData.call(this); + + return this; +}; + +/** + * Removes rows by ids. Removes selected rows if no ids are provided. + * + * @method remove + * @param [rowsIds] {Array} An array of rows ids to remove + * @chainable + **/ +Grid.prototype.remove = function(rowIds) +{ + if (this.identifier != null) + { + var that = this; + + if (this.options.ajax) { - var that = this; + // todo: implement ajax DELETE + } + else + { + rowIds = rowIds || this.selectedRows; + var id, + removedRows = []; - if (this.options.ajax) - { - // todo: implement ajax DELETE - } - else + for (var i = 0; i < rowIds.length; i++) { - rowIds = rowIds || this.selectedRows; - var id, - removedRows = []; + id = rowIds[i]; - for (var i = 0; i < rowIds.length; i++) + for (var j = 0; j < this.rows.length; j++) { - id = rowIds[i]; - - for (var j = 0; j < this.rows.length; j++) + if (this.rows[j][this.identifier] === id) { - if (this.rows[j][this.identifier] === id) - { - removedRows.push(this.rows[j]); - this.rows.splice(j, 1); - break; - } + removedRows.push(this.rows[j]); + this.rows.splice(j, 1); + break; } } - - this.current = 1; // reset - loadData.call(this); - this.element.trigger("removed" + namespace, [removedRows]); } + + this.current = 1; // reset + loadData.call(this); + this.element.trigger("removed" + namespace, [removedRows]); } + } - return this; - }; + return this; +}; + +/** + * Searches in all rows for a specific phrase (but only in visible cells). + * The search filter will be reseted, if no argument is provided. + * + * @method search + * @param [phrase] {String} The phrase to search for + * @chainable + **/ +Grid.prototype.search = function(phrase) +{ + phrase = phrase || ""; - /** - * Searches in all rows for a specific phrase (but only in visible cells). - * The search filter will be reseted, if no argument is provided. - * - * @method search - * @param [phrase] {String} The phrase to search for - * @chainable - **/ - Grid.prototype.search = function(phrase) + if (this.searchPhrase !== phrase) { - phrase = phrase || ""; - - if (this.searchPhrase !== phrase) - { - var selector = getCssSelector(this.options.css.searchField), - searchFields = findFooterAndHeaderItems.call(this, selector); - searchFields.val(phrase); - } + var selector = getCssSelector(this.options.css.searchField), + searchFields = findFooterAndHeaderItems.call(this, selector); + searchFields.val(phrase); + } - executeSearch.call(this, phrase); + executeSearch.call(this, phrase); - return this; - }; + return this; +}; - /** - * Selects rows by ids. Selects all visible rows if no ids are provided. - * In server-side scenarios only visible rows are selectable. - * - * @method select - * @param [rowsIds] {Array} An array of rows ids to select - * @chainable - **/ - Grid.prototype.select = function(rowIds) +/** + * Selects rows by ids. Selects all visible rows if no ids are provided. + * In server-side scenarios only visible rows are selectable. + * + * @method select + * @param [rowsIds] {Array} An array of rows ids to select + * @chainable + **/ +Grid.prototype.select = function(rowIds) +{ + if (this.selection) { - if (this.selection) - { - rowIds = rowIds || this.currentRows.propValues(this.identifier); + rowIds = rowIds || this.currentRows.propValues(this.identifier); - var id, i, - selectedRows = []; + var id, i, + selectedRows = []; - while (rowIds.length > 0 && !(!this.options.multiSelect && selectedRows.length === 1)) + while (rowIds.length > 0 && !(!this.options.multiSelect && selectedRows.length === 1)) + { + id = rowIds.pop(); + if ($.inArray(id, this.selectedRows) === -1) { - id = rowIds.pop(); - if ($.inArray(id, this.selectedRows) === -1) + for (i = 0; i < this.currentRows.length; i++) { - for (i = 0; i < this.currentRows.length; i++) + if (this.currentRows[i][this.identifier] === id) { - if (this.currentRows[i][this.identifier] === id) - { - selectedRows.push(this.currentRows[i]); - this.selectedRows.push(id); - break; - } + selectedRows.push(this.currentRows[i]); + this.selectedRows.push(id); + break; } } } + } - if (selectedRows.length > 0) - { - var selectBoxSelector = getCssSelector(this.options.css.selectBox), - selectMultiSelectBox = this.selectedRows.length >= this.currentRows.length; - - i = 0; - while (!this.options.keepSelection && selectMultiSelectBox && i < this.currentRows.length) - { - selectMultiSelectBox = ($.inArray(this.currentRows[i++][this.identifier], this.selectedRows) !== -1); - } - this.element.find("thead " + selectBoxSelector).prop("checked", selectMultiSelectBox); + if (selectedRows.length > 0) + { + var selectBoxSelector = getCssSelector(this.options.css.selectBox), + selectMultiSelectBox = this.selectedRows.length >= this.currentRows.length; - if (!this.options.multiSelect) - { - this.element.find("tbody > tr " + selectBoxSelector + ":checked") - .trigger("click" + namespace); - } + i = 0; + while (!this.options.keepSelection && selectMultiSelectBox && i < this.currentRows.length) + { + selectMultiSelectBox = ($.inArray(this.currentRows[i++][this.identifier], this.selectedRows) !== -1); + } + this.element.find("thead " + selectBoxSelector).prop("checked", selectMultiSelectBox); - for (i = 0; i < this.selectedRows.length; i++) - { - this.element.find("tbody > tr[data-row-id=\"" + this.selectedRows[i] + "\"]") - .addClass(this.options.css.selected)._bgAria("selected", "true") - .find(selectBoxSelector).prop("checked", true); - } + if (!this.options.multiSelect) + { + this.element.find("tbody > tr " + selectBoxSelector + ":checked") + .trigger("click" + namespace); + } - this.element.trigger("selected" + namespace, [selectedRows]); + for (i = 0; i < this.selectedRows.length; i++) + { + this.element.find("tbody > tr[data-row-id=\"" + this.selectedRows[i] + "\"]") + .addClass(this.options.css.selected)._bgAria("selected", "true") + .find(selectBoxSelector).prop("checked", true); } - } - return this; - }; + this.element.trigger("selected" + namespace, [selectedRows]); + } + } - /** - * Deselects rows by ids. Deselects all visible rows if no ids are provided. - * In server-side scenarios only visible rows are deselectable. - * - * @method deselect - * @param [rowsIds] {Array} An array of rows ids to deselect - * @chainable - **/ - Grid.prototype.deselect = function(rowIds) + return this; +}; + +/** + * Deselects rows by ids. Deselects all visible rows if no ids are provided. + * In server-side scenarios only visible rows are deselectable. + * + * @method deselect + * @param [rowsIds] {Array} An array of rows ids to deselect + * @chainable + **/ +Grid.prototype.deselect = function(rowIds) +{ + if (this.selection) { - if (this.selection) - { - rowIds = rowIds || this.currentRows.propValues(this.identifier); + rowIds = rowIds || this.currentRows.propValues(this.identifier); - var id, i, pos, - deselectedRows = []; + var id, i, pos, + deselectedRows = []; - while (rowIds.length > 0) + while (rowIds.length > 0) + { + id = rowIds.pop(); + pos = $.inArray(id, this.selectedRows); + if (pos !== -1) { - id = rowIds.pop(); - pos = $.inArray(id, this.selectedRows); - if (pos !== -1) + for (i = 0; i < this.currentRows.length; i++) { - for (i = 0; i < this.currentRows.length; i++) + if (this.currentRows[i][this.identifier] === id) { - if (this.currentRows[i][this.identifier] === id) - { - deselectedRows.push(this.currentRows[i]); - this.selectedRows.splice(pos, 1); - break; - } + deselectedRows.push(this.currentRows[i]); + this.selectedRows.splice(pos, 1); + break; } } } - - if (deselectedRows.length > 0) - { - var selectBoxSelector = getCssSelector(this.options.css.selectBox); - - this.element.find("thead " + selectBoxSelector).prop("checked", false); - for (i = 0; i < deselectedRows.length; i++) - { - this.element.find("tbody > tr[data-row-id=\"" + deselectedRows[i][this.identifier] + "\"]") - .removeClass(this.options.css.selected)._bgAria("selected", "false") - .find(selectBoxSelector).prop("checked", false); - } - - this.element.trigger("deselected" + namespace, [deselectedRows]); - } } - return this; - }; + if (deselectedRows.length > 0) + { + var selectBoxSelector = getCssSelector(this.options.css.selectBox); - /** - * Sorts the rows by a given sort descriptor dictionary. - * The sort filter will be reseted, if no argument is provided. - * - * @method sort - * @param [dictionary] {Object} A sort descriptor dictionary that contains the sort information - * @chainable - **/ - Grid.prototype.sort = function(dictionary) - { - var values = (dictionary) ? $.extend({}, dictionary) : {}; + this.element.find("thead " + selectBoxSelector).prop("checked", false); + for (i = 0; i < deselectedRows.length; i++) + { + this.element.find("tbody > tr[data-row-id=\"" + deselectedRows[i][this.identifier] + "\"]") + .removeClass(this.options.css.selected)._bgAria("selected", "false") + .find(selectBoxSelector).prop("checked", false); + } - if (values === this.sortDictionary) - { - return this; + this.element.trigger("deselected" + namespace, [deselectedRows]); } + } - this.sortDictionary = values; - renderTableHeader.call(this); - sortRows.call(this); - loadData.call(this); + return this; +}; + +/** + * Sorts the rows by a given sort descriptor dictionary. + * The sort filter will be reseted, if no argument is provided. + * + * @method sort + * @param [dictionary] {Object} A sort descriptor dictionary that contains the sort information + * @chainable + **/ +Grid.prototype.sort = function(dictionary) +{ + var values = (dictionary) ? $.extend({}, dictionary) : {}; + if (values === this.sortDictionary) + { return this; - }; + } - /** - * Gets a list of the column settings. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getColumnSettings - * @return {Array} Returns a list of the column settings. - * @since 1.2.0 - **/ - Grid.prototype.getColumnSettings = function() - { - return $.merge([], this.columns); + this.sortDictionary = values; + renderTableHeader.call(this); + sortRows.call(this); + loadData.call(this); + + return this; +}; + +/** + * Gets a list of the column settings. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getColumnSettings + * @return {Array} Returns a list of the column settings. + * @since 1.2.0 + **/ +Grid.prototype.getColumnSettings = function() +{ + return $.merge([], this.columns); +}; + +/** + * Gets the current page index. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getCurrentPage + * @return {Number} Returns the current page index. + * @since 1.2.0 + **/ +Grid.prototype.getCurrentPage = function() +{ + return this.current; +}; + +/** + * Gets the current rows. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getCurrentPage + * @return {Array} Returns the current rows. + * @since 1.2.0 + **/ +Grid.prototype.getCurrentRows = function() +{ + return $.merge([], this.currentRows); +}; + +/** + * Gets a number represents the row count per page. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getRowCount + * @return {Number} Returns the row count per page. + * @since 1.2.0 + **/ +Grid.prototype.getRowCount = function() +{ + return this.rowCount; +}; + +/** + * Gets the actual search phrase. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getSearchPhrase + * @return {String} Returns the actual search phrase. + * @since 1.2.0 + **/ +Grid.prototype.getSearchPhrase = function() +{ + return this.searchPhrase; +}; + +/** + * Gets the complete list of currently selected rows. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getSelectedRows + * @return {Array} Returns all selected rows. + * @since 1.2.0 + **/ +Grid.prototype.getSelectedRows = function() +{ + return $.merge([], this.selectedRows); +}; + +/** + * Gets the sort dictionary which represents the state of column sorting. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getSortDictionary + * @return {Object} Returns the sort dictionary. + * @since 1.2.0 + **/ +Grid.prototype.getSortDictionary = function() +{ + return $.extend({}, this.sortDictionary); +}; + +/** + * Gets a number represents the total page count. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getTotalPageCount + * @return {Number} Returns the total page count. + * @since 1.2.0 + **/ +Grid.prototype.getTotalPageCount = function() +{ + return this.totalPages; +}; + +/** + * Gets a number represents the total row count. + * This method returns only for the first grid instance a value. + * Therefore be sure that only one grid instance is catched by your selector. + * + * @method getTotalRowCount + * @return {Number} Returns the total row count. + * @since 1.2.0 + **/ +Grid.prototype.getTotalRowCount = function() +{ + return this.total; }; - /** - * Gets the current page index. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getCurrentPage - * @return {Number} Returns the current page index. - * @since 1.2.0 - **/ - Grid.prototype.getCurrentPage = function() - { - return this.current; - }; +// GRID COMMON TYPE EXTENSIONS +// ============ - /** - * Gets the current rows. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getCurrentPage - * @return {Array} Returns the current rows. - * @since 1.2.0 - **/ - Grid.prototype.getCurrentRows = function() +$.fn.extend({ + _bgAria: function (name, value) { - return $.merge([], this.currentRows); - }; + return (value) ? this.attr("aria-" + name, value) : this.attr("aria-" + name); + }, - /** - * Gets a number represents the row count per page. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getRowCount - * @return {Number} Returns the row count per page. - * @since 1.2.0 - **/ - Grid.prototype.getRowCount = function() + _bgBusyAria: function(busy) { - return this.rowCount; - }; + return (busy == null || busy) ? + this._bgAria("busy", "true") : + this._bgAria("busy", "false"); + }, - /** - * Gets the actual search phrase. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getSearchPhrase - * @return {String} Returns the actual search phrase. - * @since 1.2.0 - **/ - Grid.prototype.getSearchPhrase = function() + _bgRemoveAria: function (name) { - return this.searchPhrase; - }; + return this.removeAttr("aria-" + name); + }, - /** - * Gets the complete list of currently selected rows. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getSelectedRows - * @return {Array} Returns all selected rows. - * @since 1.2.0 - **/ - Grid.prototype.getSelectedRows = function() + _bgEnableAria: function (enable) { - return $.merge([], this.selectedRows); - }; + return (enable == null || enable) ? + this.removeClass("disabled")._bgAria("disabled", "false") : + this.addClass("disabled")._bgAria("disabled", "true"); + }, - /** - * Gets the sort dictionary which represents the state of column sorting. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getSortDictionary - * @return {Object} Returns the sort dictionary. - * @since 1.2.0 - **/ - Grid.prototype.getSortDictionary = function() + _bgEnableField: function (enable) { - return $.extend({}, this.sortDictionary); - }; + return (enable == null || enable) ? + this.removeAttr("disabled") : + this.attr("disabled", "disable"); + }, - /** - * Gets a number represents the total page count. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getTotalPageCount - * @return {Number} Returns the total page count. - * @since 1.2.0 - **/ - Grid.prototype.getTotalPageCount = function() + _bgShowAria: function (show) { - return this.totalPages; - }; + return (show == null || show) ? + this.show()._bgAria("hidden", "false") : + this.hide()._bgAria("hidden", "true"); + }, - /** - * Gets a number represents the total row count. - * This method returns only for the first grid instance a value. - * Therefore be sure that only one grid instance is catched by your selector. - * - * @method getTotalRowCount - * @return {Number} Returns the total row count. - * @since 1.2.0 - **/ - Grid.prototype.getTotalRowCount = function() + _bgSelectAria: function (select) { - return this.total; - }; - - // GRID COMMON TYPE EXTENSIONS - // ============ + return (select == null || select) ? + this.addClass("active")._bgAria("selected", "true") : + this.removeClass("active")._bgAria("selected", "false"); + }, - $.fn.extend({ - _bgAria: function (name, value) - { - return (value) ? this.attr("aria-" + name, value) : this.attr("aria-" + name); - }, - - _bgBusyAria: function(busy) - { - return (busy == null || busy) ? - this._bgAria("busy", "true") : - this._bgAria("busy", "false"); - }, - - _bgRemoveAria: function (name) - { - return this.removeAttr("aria-" + name); - }, - - _bgEnableAria: function (enable) - { - return (enable == null || enable) ? - this.removeClass("disabled")._bgAria("disabled", "false") : - this.addClass("disabled")._bgAria("disabled", "true"); - }, - - _bgEnableField: function (enable) - { - return (enable == null || enable) ? - this.removeAttr("disabled") : - this.attr("disabled", "disable"); - }, - - _bgShowAria: function (show) - { - return (show == null || show) ? - this.show()._bgAria("hidden", "false") : - this.hide()._bgAria("hidden", "true"); - }, - - _bgSelectAria: function (select) - { - return (select == null || select) ? - this.addClass("active")._bgAria("selected", "true") : - this.removeClass("active")._bgAria("selected", "false"); - }, + _bgId: function (id) + { + return (id) ? this.attr("id", id) : this.attr("id"); + } +}); - _bgId: function (id) +if (!String.prototype.resolve) +{ + var formatter = { + "checked": function(value) { - return (id) ? this.attr("id", id) : this.attr("id"); + if (typeof value === "boolean") + { + return (value) ? "checked=\"checked\"" : ""; + } + return value; } - }); + }; - if (!String.prototype.resolve) + String.prototype.resolve = function (substitutes, prefixes) { - var formatter = { - "checked": function(value) + var result = this; + $.each(substitutes, function (key, value) + { + if (value != null && typeof value !== "function") { - if (typeof value === "boolean") + if (typeof value === "object") { - return (value) ? "checked=\"checked\"" : ""; + var keys = (prefixes) ? $.extend([], prefixes) : []; + keys.push(key); + result = result.resolve(value, keys) + ""; } - return value; - } - }; - - String.prototype.resolve = function (substitutes, prefixes) - { - var result = this; - $.each(substitutes, function (key, value) - { - if (value != null && typeof value !== "function") + else { - if (typeof value === "object") - { - var keys = (prefixes) ? $.extend([], prefixes) : []; - keys.push(key); - result = result.resolve(value, keys) + ""; - } - else + if (formatter && formatter[key] && typeof formatter[key] === "function") { - if (formatter && formatter[key] && typeof formatter[key] === "function") - { - value = formatter[key](value); - } - key = (prefixes) ? prefixes.join(".") + "." + key : key; - var pattern = new RegExp("\\{\\{" + key + "\\}\\}", "gm"); - result = result.replace(pattern, (value.replace) ? value.replace(/\$/gi, "$") : value); + value = formatter[key](value); } + key = (prefixes) ? prefixes.join(".") + "." + key : key; + var pattern = new RegExp("\\{\\{" + key + "\\}\\}", "gm"); + result = result.replace(pattern, (value.replace) ? value.replace(/\$/gi, "$") : value); } - }); - return result; - }; - } + } + }); + return result; + }; +} - if (!Array.prototype.first) +if (!Array.prototype.first) +{ + Array.prototype.first = function (condition) { - Array.prototype.first = function (condition) + for (var i = 0; i < this.length; i++) { - for (var i = 0; i < this.length; i++) + var item = this[i]; + if (condition(item)) { - var item = this[i]; - if (condition(item)) - { - return item; - } + return item; } - return null; - }; - } + } + return null; + }; +} - if (!Array.prototype.contains) +if (!Array.prototype.contains) +{ + Array.prototype.contains = function (condition) { - Array.prototype.contains = function (condition) + for (var i = 0; i < this.length; i++) { - for (var i = 0; i < this.length; i++) + var item = this[i]; + if (condition(item)) { - var item = this[i]; - if (condition(item)) - { - return true; - } + return true; } - return false; - }; - } + } + return false; + }; +} - if (!Array.prototype.page) +if (!Array.prototype.page) +{ + Array.prototype.page = function (page, size) { - Array.prototype.page = function (page, size) - { - var skip = (page - 1) * size, - end = skip + size; - return (this.length > skip) ? - (this.length > end) ? this.slice(skip, end) : - this.slice(skip) : []; - }; - } + var skip = (page - 1) * size, + end = skip + size; + return (this.length > skip) ? + (this.length > end) ? this.slice(skip, end) : + this.slice(skip) : []; + }; +} - if (!Array.prototype.where) +if (!Array.prototype.where) +{ + Array.prototype.where = function (condition) { - Array.prototype.where = function (condition) + var result = []; + for (var i = 0; i < this.length; i++) { - var result = []; - for (var i = 0; i < this.length; i++) + var item = this[i]; + if (condition(item)) { - var item = this[i]; - if (condition(item)) - { - result.push(item); - } + result.push(item); } - return result; - }; - } + } + return result; + }; +} - if (!Array.prototype.propValues) +if (!Array.prototype.propValues) +{ + Array.prototype.propValues = function (propName) { - Array.prototype.propValues = function (propName) + var result = []; + for (var i = 0; i < this.length; i++) { - var result = []; - for (var i = 0; i < this.length; i++) - { - result.push(this[i][propName]); - } - return result; - }; + result.push(this[i][propName]); + } + return result; + }; } - // GRID PLUGIN DEFINITION - // ===================== +// GRID PLUGIN DEFINITION +// ===================== - var old = $.fn.bootgrid; +var old = $.fn.bootgrid; - $.fn.bootgrid = function (option) - { - var args = Array.prototype.slice.call(arguments, 1), - returnValue = null, - elements = this.each(function (index) - { - var $this = $(this), - instance = $this.data(namespace), - options = typeof option === "object" && option; +$.fn.bootgrid = function (option) +{ + var args = Array.prototype.slice.call(arguments, 1), + returnValue = null, + elements = this.each(function (index) + { + var $this = $(this), + instance = $this.data(namespace), + options = typeof option === "object" && option; - if (!instance && option === "destroy") + if (!instance && option === "destroy") + { + return; + } + if (!instance) + { + $this.data(namespace, (instance = new Grid(this, options))); + init.call(instance); + } + if (typeof option === "string") + { + if (option.indexOf("get") === 0 && index === 0) { - return; + returnValue = instance[option].apply(instance, args); } - if (!instance) + else if (option.indexOf("get") !== 0) { - $this.data(namespace, (instance = new Grid(this, options))); - init.call(instance); + return instance[option].apply(instance, args); } - if (typeof option === "string") - { - if (option.indexOf("get") === 0 && index === 0) - { - returnValue = instance[option].apply(instance, args); - } - else if (option.indexOf("get") !== 0) - { - return instance[option].apply(instance, args); - } - } - }); - return (typeof option === "string" && option.indexOf("get") === 0) ? returnValue : elements; - }; + } + }); + return (typeof option === "string" && option.indexOf("get") === 0) ? returnValue : elements; +}; - $.fn.bootgrid.Constructor = Grid; +$.fn.bootgrid.Constructor = Grid; - // GRID NO CONFLICT - // =============== +// GRID NO CONFLICT +// =============== - $.fn.bootgrid.noConflict = function () - { - $.fn.bootgrid = old; - return this; - }; +$.fn.bootgrid.noConflict = function () +{ + $.fn.bootgrid = old; + return this; +}; - // GRID DATA-API - // ============ +// GRID DATA-API +// ============ $("[data-toggle=\"bootgrid\"]").bootgrid(); })(jQuery, window); \ No newline at end of file diff --git a/dist/jquery.bootgrid.min.css b/dist/jquery.bootgrid.min.css index 358c1ac..ab9fda1 100644 --- a/dist/jquery.bootgrid.min.css +++ b/dist/jquery.bootgrid.min.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.1 - 09/11/2015 - * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.3.1 - 02/17/2016 + * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */.bootgrid-footer,.bootgrid-header{margin:15px 0}.bootgrid-footer a,.bootgrid-header a{outline:0}.bootgrid-footer .search,.bootgrid-header .search{display:inline-block;margin:0 20px 0 0;vertical-align:middle;width:180px}.bootgrid-footer .search .glyphicon,.bootgrid-header .search .glyphicon{top:0}.bootgrid-footer .search .fa,.bootgrid-header .search .fa{display:table-cell}.bootgrid-footer .search .search-field::-ms-clear,.bootgrid-footer .search.search-field::-ms-clear,.bootgrid-header .search .search-field::-ms-clear,.bootgrid-header .search.search-field::-ms-clear{display:none}.bootgrid-footer .pagination,.bootgrid-header .pagination{margin:0!important}.bootgrid-footer .infoBar,.bootgrid-header .actionBar{text-align:right}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu{text-align:left}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item{cursor:pointer;display:block;margin:0;padding:3px 20px;white-space:nowrap}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item:focus,.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item:hover,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item:focus,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item .dropdown-item-checkbox,.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item.dropdown-item-checkbox,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item .dropdown-item-checkbox,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item.dropdown-item-checkbox{margin:0 2px 4px 0;vertical-align:middle}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item.disabled,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item.disabled{cursor:not-allowed}.bootgrid-table{table-layout:fixed}.bootgrid-table a{outline:0}.bootgrid-table th>.column-header-anchor{color:#333;cursor:not-allowed;display:block;position:relative;text-decoration:none}.bootgrid-table th>.column-header-anchor.sortable{cursor:pointer}.bootgrid-table th>.column-header-anchor>.text{display:block;margin:0 16px 0 0;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap}.bootgrid-table th>.column-header-anchor>.icon{display:block;position:absolute;right:0;top:2px}.bootgrid-table th:active,.bootgrid-table th:hover{background:#fafafa}.bootgrid-table td{overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap}.bootgrid-table td.loading,.bootgrid-table td.no-results{background:#fff;text-align:center}.bootgrid-table td.select-cell,.bootgrid-table th.select-cell{text-align:center;width:30px}.bootgrid-table td.select-cell .select-box,.bootgrid-table th.select-cell .select-box{margin:0;outline:0}.table-responsive .bootgrid-table{table-layout:inherit!important}.table-responsive .bootgrid-table td,.table-responsive .bootgrid-table th>.column-header-anchor>.text{overflow:inherit!important;-ms-text-overflow:inherit!important;-o-text-overflow:inherit!important;text-overflow:inherit!important;white-space:inherit!important} \ No newline at end of file diff --git a/dist/jquery.bootgrid.min.js b/dist/jquery.bootgrid.min.js index b84a714..f5aedda 100644 --- a/dist/jquery.bootgrid.min.js +++ b/dist/jquery.bootgrid.min.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.3.1 - 09/11/2015 - * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.3.1 - 02/17/2016 + * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ -!function(a,b,c){"use strict";function d(a){function b(b){return c.identifier&&b[c.identifier]===a[c.identifier]}var c=this;return this.rows.contains(b)?!1:(this.rows.push(a),!0)}function e(b){var c=this.footer?this.footer.find(b):a(),d=this.header?this.header.find(b):a();return a.merge(c,d)}function f(b){return b?a.extend({},this.cachedParams,{ctx:b}):this.cachedParams}function g(){var b={current:this.current,rowCount:this.rowCount,sort:this.sortDictionary,searchPhrase:this.searchPhrase},c=this.options.post;return c=a.isFunction(c)?c():c,this.options.requestHandler(a.extend(!0,b,c))}function h(b){return"."+a.trim(b).replace(/\s+/gm,".")}function i(){var b=this.options.url;return a.isFunction(b)?b():b}function j(){this.element.trigger("initialize"+H),m.call(this),this.selection=this.options.selection&&null!=this.identifier,o.call(this),q.call(this),C.call(this),A.call(this),r.call(this),n.call(this),this.element.trigger("initialized"+H)}function k(a){this.options.highlightRows}function l(a){return a.visible}function m(){var b=this,c=this.element.find("thead > tr").first(),d=!1;c.children().each(function(){var c=a(this),e=c.data(),f={id:e.columnId,identifier:null==b.identifier&&e.identifier||!1,converter:b.options.converters[e.converter||e.type]||b.options.converters.string,text:c.text(),align:e.align||"left",headerAlign:e.headerAlign||"left",cssClass:e.cssClass||"",headerCssClass:e.headerCssClass||"",formatter:b.options.formatters[e.formatter]||null,order:d||"asc"!==e.order&&"desc"!==e.order?null:e.order,searchable:!(e.searchable===!1),sortable:!(e.sortable===!1),visible:!(e.visible===!1),visibleInSelection:!(e.visibleInSelection===!1),width:a.isNumeric(e.width)?e.width+"px":"string"==typeof e.width?e.width:null};b.columns.push(f),null!=f.order&&(b.sortDictionary[f.id]=f.order),f.identifier&&(b.identifier=f.id,b.converter=f.converter),b.options.multiSort||null===f.order||(d=!0)})}function n(){function c(a){for(var b,c=new RegExp(e.searchPhrase,e.options.caseSensitive?"g":"gi"),d=0;d<e.columns.length;d++)if(b=e.columns[d],b.searchable&&b.visible&&b.converter.to(a[b.id]).search(c)>-1)return!0;return!1}function d(a,b){e.currentRows=a,p.call(e,b),e.options.keepSelection||(e.selectedRows=[]),y.call(e,a),t.call(e),v.call(e),e.element._bgBusyAria(!1).trigger("loaded"+H)}var e=this;if(this.element._bgBusyAria(!0).trigger("load"+H),F.call(this),this.options.ajax){var f=g.call(this),h=i.call(this);if(null==h||"string"!=typeof h||0===h.length)throw new Error("Url setting must be a none empty string or a function that returns one.");this.xqr&&this.xqr.abort();var j={url:h,data:f,success:function(b){e.xqr=null,"string"==typeof b&&(b=a.parseJSON(b)),b=e.options.responseHandler(b),e.current=b.current,d(b.rows,b.total)},error:function(a,b,c){e.xqr=null,"abort"!==b&&(u.call(e),e.element._bgBusyAria(!1).trigger("loaded"+H))}};j=a.extend(this.options.ajaxSettings,j),this.xqr=a.ajax(j)}else{var k=this.searchPhrase.length>0?this.rows.where(c):this.rows,l=k.length;-1!==this.rowCount&&(k=k.page(this.current,this.rowCount)),b.setTimeout(function(){d(k,l)},10)}}function o(){if(!this.options.ajax){var b=this,c=this.element.find("tbody > tr");c.each(function(){var c=a(this),e=c.children("td"),f={};a.each(b.columns,function(a,b){f[b.id]=b.converter.from(e.eq(a).text())}),d.call(b,f)}),p.call(this,this.rows.length),G.call(this)}}function p(a){this.total=a,this.totalPages=-1===this.rowCount?1:Math.ceil(this.total/this.rowCount)}function q(){var b=this.options.templates,c=this.element.parent().hasClass(this.options.css.responsiveTable)?this.element.parent():this.element;this.element.addClass(this.options.css.table),0===this.element.children("tbody").length&&this.element.append(b.body),1&this.options.navigation&&(this.header=a(b.header.resolve(f.call(this,{id:this.element._bgId()+"-header"}))),c.before(this.header)),2&this.options.navigation&&(this.footer=a(b.footer.resolve(f.call(this,{id:this.element._bgId()+"-footer"}))),c.after(this.footer))}function r(){if(0!==this.options.navigation){var b=this.options.css,c=h(b.actions),d=e.call(this,c);if(d.length>0){var g=this,i=this.options.templates,j=a(i.actions.resolve(f.call(this)));if(this.options.ajax){var k=i.icon.resolve(f.call(this,{iconCss:b.iconRefresh})),l=a(i.actionButton.resolve(f.call(this,{content:k,text:this.options.labels.refresh}))).on("click"+H,function(a){a.stopPropagation(),g.current=1,n.call(g)});j.append(l)}x.call(this,j),s.call(this,j),E.call(this,d,j)}}}function s(b){if(this.options.columnSelection&&this.columns.length>1){var c=this,d=this.options.css,e=this.options.templates,g=e.icon.resolve(f.call(this,{iconCss:d.iconColumns})),i=a(e.actionDropDown.resolve(f.call(this,{content:g}))),j=h(d.dropDownItem),k=h(d.dropDownItemCheckbox),m=h(d.dropDownMenuItems);a.each(this.columns,function(b,g){if(g.visibleInSelection){var o=a(e.actionDropDownCheckboxItem.resolve(f.call(c,{name:g.id,label:g.text,checked:g.visible}))).on("click"+H,j,function(b){b.stopPropagation();var d=a(this),e=d.find(k);if(!e.prop("disabled")){g.visible=e.prop("checked");var f=c.columns.where(l).length>1;d.parents(m).find(j+":has("+k+":checked)")._bgEnableAria(f).find(k)._bgEnableField(f),c.element.find("tbody").empty(),C.call(c),n.call(c)}});i.find(h(d.dropDownMenuItems)).append(o)}}),b.append(i)}}function t(){if(0!==this.options.navigation){var b=h(this.options.css.infos),c=e.call(this,b);if(c.length>0){var d=this.current*this.rowCount,g=a(this.options.templates.infos.resolve(f.call(this,{end:0===this.total||-1===d||d>this.total?this.total:d,start:0===this.total?0:d-this.rowCount+1,total:this.total})));E.call(this,c,g)}}}function u(){var a=this.element.children("tbody").first(),b=this.options.templates,c=this.columns.where(l).length;this.selection&&(c+=1),a.html(b.noResults.resolve(f.call(this,{columns:c})))}function v(){if(0!==this.options.navigation){var b=h(this.options.css.pagination),c=e.call(this,b)._bgShowAria(-1!==this.rowCount);if(-1!==this.rowCount&&c.length>0){var d=this.options.templates,g=this.current,i=this.totalPages,j=a(d.pagination.resolve(f.call(this))),k=i-g,l=-1*(this.options.padding-g),m=k>=this.options.padding?Math.max(l,1):Math.max(l-this.options.padding+k,1),n=2*this.options.padding+1,o=i>=n?n:i;w.call(this,j,"first","«","first")._bgEnableAria(g>1),w.call(this,j,"prev","<","prev")._bgEnableAria(g>1);for(var p=0;o>p;p++){var q=p+m;w.call(this,j,q,q,"page-"+q)._bgEnableAria()._bgSelectAria(q===g)}0===o&&w.call(this,j,1,1,"page-1")._bgEnableAria(!1)._bgSelectAria(),w.call(this,j,"next",">","next")._bgEnableAria(i>g),w.call(this,j,"last","»","last")._bgEnableAria(i>g),E.call(this,c,j)}}}function w(b,c,d,e){var g=this,i=this.options.templates,j=this.options.css,k=f.call(this,{css:e,text:d,page:c}),l=a(i.paginationItem.resolve(k)).on("click"+H,h(j.paginationButton),function(b){b.stopPropagation(),b.preventDefault();var c=a(this),d=c.parent();if(!d.hasClass("active")&&!d.hasClass("disabled")){var e={first:1,prev:g.current-1,next:g.current+1,last:g.totalPages},f=c.data("page");g.current=e[f]||f,n.call(g)}c.trigger("blur")});return b.append(l),l}function x(b){function c(a){return-1===a?d.options.labels.all:a}var d=this,e=this.options.rowCount;if(a.isArray(e)){var g=this.options.css,i=this.options.templates,j=a(i.actionDropDown.resolve(f.call(this,{content:c(this.rowCount)}))),k=h(g.dropDownMenu),l=h(g.dropDownMenuText),m=h(g.dropDownMenuItems),o=h(g.dropDownItemButton);a.each(e,function(b,e){var g=a(i.actionDropDownItem.resolve(f.call(d,{text:c(e),action:e})))._bgSelectAria(e===d.rowCount).on("click"+H,o,function(b){b.preventDefault();var e=a(this),f=e.data("action");f!==d.rowCount&&(d.current=1,d.rowCount=f,e.parents(m).children().each(function(){var b=a(this),c=b.find(o).data("action");b._bgSelectAria(c===f)}),e.parents(k).find(l).text(c(f)),n.call(d))});j.find(m).append(g)}),b.append(j)}}function y(b){if(b.length>0){var c=this,d=this.options.css,e=this.options.templates,g=this.element.children("tbody").first(),i=!0,j="";a.each(b,function(b,g){var h="",k=' data-row-id="'+(null==c.identifier?b:g[c.identifier])+'"',l="";if(c.selection){var m=-1!==a.inArray(g[c.identifier],c.selectedRows),n=e.select.resolve(f.call(c,{type:"checkbox",value:g[c.identifier],checked:m}));h+=e.cell.resolve(f.call(c,{content:n,css:d.selectCell})),i=i&&m,m&&(l+=d.selected,k+=' aria-selected="true"')}var o=null!=g.status&&c.options.statusMapping[g.status];o&&(l+=o),a.each(c.columns,function(b,i){if(i.visible){var j=a.isFunction(i.formatter)?i.formatter.call(c,i,g):i.converter.to(g[i.id]),k=i.cssClass.length>0?" "+i.cssClass:"";h+=e.cell.resolve(f.call(c,{content:null==j||""===j?" ":j,css:("right"===i.align?d.right:"center"===i.align?d.center:d.left)+k,style:null==i.width?"":"width:"+i.width+";"}))}}),l.length>0&&(k+=' class="'+l+'"'),j+=e.row.resolve(f.call(c,{attr:k,cells:h}))}),c.element.find("thead "+h(c.options.css.selectBox)).prop("checked",i),g.html(j),z.call(this,g)}else u.call(this)}function z(b){var c=this,d=h(this.options.css.selectBox);this.selection&&b.off("click"+H,d).on("click"+H,d,function(b){b.stopPropagation();var d=a(this),e=c.converter.from(d.val());d.prop("checked")?c.select([e]):c.deselect([e])}),b.off("click"+H,"> tr").on("click"+H,"> tr",function(b){b.stopPropagation();var d=a(this),e=null==c.identifier?d.data("row-id"):c.converter.from(d.data("row-id")+""),f=null==c.identifier?c.currentRows[e]:c.currentRows.first(function(a){return a[c.identifier]===e});c.selection&&c.options.rowSelect&&(d.hasClass(c.options.css.selected)?c.deselect([e]):c.select([e])),c.element.trigger("click"+H,[c.columns,f])})}function A(){if(0!==this.options.navigation){var c=this.options.css,d=h(c.search),g=e.call(this,d);if(g.length>0){var i=this,j=this.options.templates,k=null,l="",m=h(c.searchField),n=a(j.search.resolve(f.call(this))),o=n.is(m)?n:n.find(m);o.on("keyup"+H,function(c){c.stopPropagation();var d=a(this).val();(l!==d||13===c.which&&""!==d)&&(l=d,(13===c.which||0===d.length||d.length>=i.options.searchSettings.characters)&&(b.clearTimeout(k),k=b.setTimeout(function(){B.call(i,d)},i.options.searchSettings.delay)))}),E.call(this,g,n)}}}function B(a){this.searchPhrase!==a&&(this.current=1,this.searchPhrase=a,n.call(this))}function C(){var b=this,c=this.element.find("thead > tr"),d=this.options.css,e=this.options.templates,g="",i=this.options.sorting;if(this.selection){var j=this.options.multiSelect?e.select.resolve(f.call(b,{type:"checkbox",value:"all"})):"";g+=e.rawHeaderCell.resolve(f.call(b,{content:j,css:d.selectCell}))}if(a.each(this.columns,function(a,c){if(c.visible){var h=b.sortDictionary[c.id],j=i&&h&&"asc"===h?d.iconUp:i&&h&&"desc"===h?d.iconDown:"",k=e.icon.resolve(f.call(b,{iconCss:j})),l=c.headerAlign,m=c.headerCssClass.length>0?" "+c.headerCssClass:"";g+=e.headerCell.resolve(f.call(b,{column:c,icon:k,sortable:i&&c.sortable&&d.sortable||"",css:("right"===l?d.right:"center"===l?d.center:d.left)+m,style:null==c.width?"":"width:"+c.width+";"}))}}),c.html(g),i){var k=h(d.sortable);c.off("click"+H,k).on("click"+H,k,function(c){c.preventDefault(),D.call(b,a(this)),G.call(b),n.call(b)})}if(this.selection&&this.options.multiSelect){var l=h(d.selectBox);c.off("click"+H,l).on("click"+H,l,function(c){c.stopPropagation(),a(this).prop("checked")?b.select():b.deselect()})}}function D(a){var b=this.options.css,c=h(b.icon),d=a.data("column-id")||a.parents("th").first().data("column-id"),e=this.sortDictionary[d],f=a.find(c);if(this.options.multiSort||(a.parents("tr").first().find(c).removeClass(b.iconDown+" "+b.iconUp),this.sortDictionary={}),e&&"asc"===e)this.sortDictionary[d]="desc",f.removeClass(b.iconUp).addClass(b.iconDown);else if(e&&"desc"===e)if(this.options.multiSort){var g={};for(var i in this.sortDictionary)i!==d&&(g[i]=this.sortDictionary[i]);this.sortDictionary=g,f.removeClass(b.iconDown)}else this.sortDictionary[d]="asc",f.removeClass(b.iconDown).addClass(b.iconUp);else this.sortDictionary[d]="asc",f.addClass(b.iconUp)}function E(b,c){b.each(function(b,d){a(d).before(c.clone(!0)).remove()})}function F(){var a=this;b.setTimeout(function(){if("true"===a.element._bgAria("busy")){var b=a.options.templates,c=a.element.children("thead").first(),d=a.element.children("tbody").first(),e=d.find("tr > td").first(),g=a.element.height()-c.height()-(e.height()+20),h=a.columns.where(l).length;a.selection&&(h+=1),d.html(b.loading.resolve(f.call(a,{columns:h}))),-1!==a.rowCount&&g>0&&d.find("tr > td").css("padding","20px 0 "+g+"px")}},250)}function G(){function a(c,d,e){function f(a){return"asc"===h.order?a:-1*a}e=e||0;var g=e+1,h=b[e];return c[h.id]>d[h.id]?f(1):c[h.id]<d[h.id]?f(-1):b.length>g?a(c,d,g):0}var b=[];if(!this.options.ajax){for(var c in this.sortDictionary)(this.options.multiSort||0===b.length)&&b.push({id:c,order:this.sortDictionary[c]});b.length>0&&this.rows.sort(a)}}var H=".rs.jquery.bootgrid",I=function(b,c){this.element=a(b),this.origin=this.element.clone(),this.options=a.extend(!0,{},I.defaults,this.element.data(),c);var d=this.options.rowCount=this.element.data().rowCount||c.rowCount||this.options.rowCount;this.columns=[],this.current=1,this.currentRows=[],this.identifier=null,this.selection=!1,this.converter=null,this.rowCount=a.isArray(d)?d[0]:d,this.rows=[],this.searchPhrase="",this.selectedRows=[],this.sortDictionary={},this.total=0,this.totalPages=0,this.cachedParams={lbl:this.options.labels,css:this.options.css,ctx:{}},this.header=null,this.footer=null,this.xqr=null};if(I.defaults={navigation:3,padding:2,columnSelection:!0,rowCount:[10,25,50,-1],selection:!1,multiSelect:!1,rowSelect:!1,keepSelection:!1,highlightRows:!1,sorting:!0,multiSort:!1,searchSettings:{delay:250,characters:1},ajax:!1,ajaxSettings:{method:"POST"},post:{},url:"",caseSensitive:!0,requestHandler:function(a){return a},responseHandler:function(a){return a},converters:{numeric:{from:function(a){return+a},to:function(a){return a+""}},string:{from:function(a){return a},to:function(a){return a}}},css:{actions:"actions btn-group",center:"text-center",columnHeaderAnchor:"column-header-anchor",columnHeaderText:"text",dropDownItem:"dropdown-item",dropDownItemButton:"dropdown-item-button",dropDownItemCheckbox:"dropdown-item-checkbox",dropDownMenu:"dropdown btn-group",dropDownMenuItems:"dropdown-menu pull-right",dropDownMenuText:"dropdown-text",footer:"bootgrid-footer container-fluid",header:"bootgrid-header container-fluid",icon:"icon glyphicon",iconColumns:"glyphicon-th-list",iconDown:"glyphicon-chevron-down",iconRefresh:"glyphicon-refresh",iconSearch:"glyphicon-search",iconUp:"glyphicon-chevron-up",infos:"infos",left:"text-left",pagination:"pagination",paginationButton:"button",responsiveTable:"table-responsive",right:"text-right",search:"search form-group",searchField:"search-field form-control",selectBox:"select-box",selectCell:"select-cell",selected:"active",sortable:"sortable",table:"bootgrid-table table"},formatters:{},labels:{all:"All",infos:"Showing {{ctx.start}} to {{ctx.end}} of {{ctx.total}} entries",loading:"Loading...",noResults:"No results found!",refresh:"Refresh",search:"Search"},statusMapping:{0:"success",1:"info",2:"warning",3:"danger"},templates:{actionButton:'<button class="btn btn-default" type="button" title="{{ctx.text}}">{{ctx.content}}</button>',actionDropDown:'<div class="{{css.dropDownMenu}}"><button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown"><span class="{{css.dropDownMenuText}}">{{ctx.content}}</span> <span class="caret"></span></button><ul class="{{css.dropDownMenuItems}}" role="menu"></ul></div>',actionDropDownItem:'<li><a data-action="{{ctx.action}}" class="{{css.dropDownItem}} {{css.dropDownItemButton}}">{{ctx.text}}</a></li>',actionDropDownCheckboxItem:'<li><label class="{{css.dropDownItem}}"><input name="{{ctx.name}}" type="checkbox" value="1" class="{{css.dropDownItemCheckbox}}" {{ctx.checked}} /> {{ctx.label}}</label></li>',actions:'<div class="{{css.actions}}"></div>',body:"<tbody></tbody>",cell:'<td class="{{ctx.css}}" style="{{ctx.style}}">{{ctx.content}}</td>',footer:'<div id="{{ctx.id}}" class="{{css.footer}}"><div class="row"><div class="col-sm-6"><p class="{{css.pagination}}"></p></div><div class="col-sm-6 infoBar"><p class="{{css.infos}}"></p></div></div></div>',header:'<div id="{{ctx.id}}" class="{{css.header}}"><div class="row"><div class="col-sm-12 actionBar"><p class="{{css.search}}"></p><p class="{{css.actions}}"></p></div></div></div>',headerCell:'<th data-column-id="{{ctx.column.id}}" class="{{ctx.css}}" style="{{ctx.style}}"><a href="javascript:void(0);" class="{{css.columnHeaderAnchor}} {{ctx.sortable}}"><span class="{{css.columnHeaderText}}">{{ctx.column.text}}</span>{{ctx.icon}}</a></th>',icon:'<span class="{{css.icon}} {{ctx.iconCss}}"></span>',infos:'<div class="{{css.infos}}">{{lbl.infos}}</div>',loading:'<tr><td colspan="{{ctx.columns}}" class="loading">{{lbl.loading}}</td></tr>',noResults:'<tr><td colspan="{{ctx.columns}}" class="no-results">{{lbl.noResults}}</td></tr>',pagination:'<ul class="{{css.pagination}}"></ul>',paginationItem:'<li class="{{ctx.css}}"><a data-page="{{ctx.page}}" class="{{css.paginationButton}}">{{ctx.text}}</a></li>',rawHeaderCell:'<th class="{{ctx.css}}">{{ctx.content}}</th>',row:"<tr{{ctx.attr}}>{{ctx.cells}}</tr>",search:'<div class="{{css.search}}"><div class="input-group"><span class="{{css.icon}} input-group-addon {{css.iconSearch}}"></span> <input type="text" class="{{css.searchField}}" placeholder="{{lbl.search}}" /></div></div>',select:'<input name="select" type="{{ctx.type}}" class="{{css.selectBox}}" value="{{ctx.value}}" {{ctx.checked}} />'}},I.prototype.append=function(a){if(this.options.ajax);else{for(var b=[],c=0;c<a.length;c++)d.call(this,a[c])&&b.push(a[c]);G.call(this),k.call(this,b),n.call(this),this.element.trigger("appended"+H,[b])}return this},I.prototype.clear=function(){if(this.options.ajax);else{var b=a.extend([],this.rows);this.rows=[],this.current=1,this.total=0,n.call(this),this.element.trigger("cleared"+H,[b])}return this},I.prototype.destroy=function(){return a(b).off(H),1&this.options.navigation&&this.header.remove(),2&this.options.navigation&&this.footer.remove(),this.element.before(this.origin).remove(),this},I.prototype.reload=function(){return this.current=1,n.call(this),this},I.prototype.remove=function(a){if(null!=this.identifier){if(this.options.ajax);else{a=a||this.selectedRows;for(var b,c=[],d=0;d<a.length;d++){b=a[d];for(var e=0;e<this.rows.length;e++)if(this.rows[e][this.identifier]===b){c.push(this.rows[e]),this.rows.splice(e,1);break}}this.current=1,n.call(this),this.element.trigger("removed"+H,[c])}}return this},I.prototype.search=function(a){if(a=a||"",this.searchPhrase!==a){var b=h(this.options.css.searchField),c=e.call(this,b);c.val(a)}return B.call(this,a),this},I.prototype.select=function(b){if(this.selection){b=b||this.currentRows.propValues(this.identifier);for(var c,d,e=[];b.length>0&&(this.options.multiSelect||1!==e.length);)if(c=b.pop(),-1===a.inArray(c,this.selectedRows))for(d=0;d<this.currentRows.length;d++)if(this.currentRows[d][this.identifier]===c){e.push(this.currentRows[d]),this.selectedRows.push(c);break}if(e.length>0){var f=h(this.options.css.selectBox),g=this.selectedRows.length>=this.currentRows.length;for(d=0;!this.options.keepSelection&&g&&d<this.currentRows.length;)g=-1!==a.inArray(this.currentRows[d++][this.identifier],this.selectedRows);for(this.element.find("thead "+f).prop("checked",g),this.options.multiSelect||this.element.find("tbody > tr "+f+":checked").trigger("click"+H),d=0;d<this.selectedRows.length;d++)this.element.find('tbody > tr[data-row-id="'+this.selectedRows[d]+'"]').addClass(this.options.css.selected)._bgAria("selected","true").find(f).prop("checked",!0);this.element.trigger("selected"+H,[e])}}return this},I.prototype.deselect=function(b){if(this.selection){b=b||this.currentRows.propValues(this.identifier);for(var c,d,e,f=[];b.length>0;)if(c=b.pop(),e=a.inArray(c,this.selectedRows),-1!==e)for(d=0;d<this.currentRows.length;d++)if(this.currentRows[d][this.identifier]===c){f.push(this.currentRows[d]),this.selectedRows.splice(e,1);break}if(f.length>0){var g=h(this.options.css.selectBox);for(this.element.find("thead "+g).prop("checked",!1),d=0;d<f.length;d++)this.element.find('tbody > tr[data-row-id="'+f[d][this.identifier]+'"]').removeClass(this.options.css.selected)._bgAria("selected","false").find(g).prop("checked",!1);this.element.trigger("deselected"+H,[f])}}return this},I.prototype.sort=function(b){var c=b?a.extend({},b):{};return c===this.sortDictionary?this:(this.sortDictionary=c,C.call(this),G.call(this),n.call(this),this)},I.prototype.getColumnSettings=function(){return a.merge([],this.columns)},I.prototype.getCurrentPage=function(){return this.current},I.prototype.getCurrentRows=function(){return a.merge([],this.currentRows)},I.prototype.getRowCount=function(){return this.rowCount},I.prototype.getSearchPhrase=function(){return this.searchPhrase},I.prototype.getSelectedRows=function(){return a.merge([],this.selectedRows)},I.prototype.getSortDictionary=function(){return a.extend({},this.sortDictionary)},I.prototype.getTotalPageCount=function(){return this.totalPages},I.prototype.getTotalRowCount=function(){return this.total},a.fn.extend({_bgAria:function(a,b){return b?this.attr("aria-"+a,b):this.attr("aria-"+a)},_bgBusyAria:function(a){return null==a||a?this._bgAria("busy","true"):this._bgAria("busy","false")},_bgRemoveAria:function(a){return this.removeAttr("aria-"+a)},_bgEnableAria:function(a){return null==a||a?this.removeClass("disabled")._bgAria("disabled","false"):this.addClass("disabled")._bgAria("disabled","true")},_bgEnableField:function(a){return null==a||a?this.removeAttr("disabled"):this.attr("disabled","disable")},_bgShowAria:function(a){return null==a||a?this.show()._bgAria("hidden","false"):this.hide()._bgAria("hidden","true")},_bgSelectAria:function(a){return null==a||a?this.addClass("active")._bgAria("selected","true"):this.removeClass("active")._bgAria("selected","false")},_bgId:function(a){return a?this.attr("id",a):this.attr("id")}}),!String.prototype.resolve){var J={checked:function(a){return"boolean"==typeof a?a?'checked="checked"':"":a}};String.prototype.resolve=function(b,c){var d=this;return a.each(b,function(b,e){if(null!=e&&"function"!=typeof e)if("object"==typeof e){var f=c?a.extend([],c):[];f.push(b),d=d.resolve(e,f)+""}else{J&&J[b]&&"function"==typeof J[b]&&(e=J[b](e)),b=c?c.join(".")+"."+b:b;var g=new RegExp("\\{\\{"+b+"\\}\\}","gm");d=d.replace(g,e.replace?e.replace(/\$/gi,"$"):e)}}),d}}Array.prototype.first||(Array.prototype.first=function(a){for(var b=0;b<this.length;b++){var c=this[b];if(a(c))return c}return null}),Array.prototype.contains||(Array.prototype.contains=function(a){for(var b=0;b<this.length;b++){var c=this[b];if(a(c))return!0}return!1}),Array.prototype.page||(Array.prototype.page=function(a,b){var c=(a-1)*b,d=c+b;return this.length>c?this.length>d?this.slice(c,d):this.slice(c):[]}),Array.prototype.where||(Array.prototype.where=function(a){for(var b=[],c=0;c<this.length;c++){var d=this[c];a(d)&&b.push(d)}return b}),Array.prototype.propValues||(Array.prototype.propValues=function(a){for(var b=[],c=0;c<this.length;c++)b.push(this[c][a]);return b});var K=a.fn.bootgrid;a.fn.bootgrid=function(b){var c=Array.prototype.slice.call(arguments,1),d=null,e=this.each(function(e){var f=a(this),g=f.data(H),h="object"==typeof b&&b;if((g||"destroy"!==b)&&(g||(f.data(H,g=new I(this,h)),j.call(g)),"string"==typeof b))if(0===b.indexOf("get")&&0===e)d=g[b].apply(g,c);else if(0!==b.indexOf("get"))return g[b].apply(g,c)});return"string"==typeof b&&0===b.indexOf("get")?d:e},a.fn.bootgrid.Constructor=I,a.fn.bootgrid.noConflict=function(){return a.fn.bootgrid=K,this},a('[data-toggle="bootgrid"]').bootgrid()}(jQuery,window); \ No newline at end of file +!function(a,b,c){"use strict";function d(a){function b(b){return c.identifier&&b[c.identifier]===a[c.identifier]}var c=this;return this.rows.contains(b)?!1:(this.rows.push(a),!0)}function e(b){var c=this.footer?this.footer.find(b):a(),d=this.header?this.header.find(b):a();return a.merge(c,d)}function f(b){return b?a.extend({},this.cachedParams,{ctx:b}):this.cachedParams}function g(){var b={current:this.current,rowCount:this.rowCount,sort:this.sortDictionary,searchPhrase:this.searchPhrase},c=this.options.post;return c=a.isFunction(c)?c():c,this.options.requestHandler(a.extend(!0,b,c))}function h(b){return"."+a.trim(b).replace(/\s+/gm,".")}function i(){var b=this.options.url;return a.isFunction(b)?b():b}function j(){this.element.trigger("initialize"+H),m.call(this),this.selection=this.options.selection&&null!=this.identifier,o.call(this),q.call(this),C.call(this),A.call(this),r.call(this),n.call(this),this.element.trigger("initialized"+H)}function k(a){this.options.highlightRows}function l(a){return a.visible}function m(){var b=this,c=this.element.find("thead > tr").first(),d=!1;c.children().each(function(){var c=a(this),e=c.data(),f={id:e.columnId,identifier:null==b.identifier&&e.identifier||!1,converter:b.options.converters[e.converter||e.type]||b.options.converters.string,text:c.text(),align:e.align||"left",headerAlign:e.headerAlign||"left",cssClass:e.cssClass||"",headerCssClass:e.headerCssClass||"",formatter:b.options.formatters[e.formatter]||null,order:d||"asc"!==e.order&&"desc"!==e.order?null:e.order,searchable:!(e.searchable===!1),sortable:!(e.sortable===!1),visible:!(e.visible===!1),visibleInSelection:!(e.visibleInSelection===!1),width:a.isNumeric(e.width)?e.width+"px":"string"==typeof e.width?e.width:null};b.columns.push(f),null!=f.order&&(b.sortDictionary[f.id]=f.order),f.identifier&&(b.identifier=f.id,b.converter=f.converter),b.options.multiSort||null===f.order||(d=!0)})}function n(){function c(a,b){var c;return c=j.options.regexSearch?b.search(a):b.toLowerCase().indexOf(a),c>-1?!0:!1}function d(a){return new RegExp(a,j.options.caseSensitive?"g":"gi")}function e(a){return j.options.caseSensitive?a:a.toLowerCase()}function f(a){for(var b,f=j.options.regexSearch?d(j.searchPhrase):e(j.searchPhrase),g=0;g<j.columns.length;g++)if(b=j.columns[g],b.searchable&&b.visible&&c(f,b.converter.to(a[b.id])))return!0;return!1}function h(a,b){j.currentRows=a,p.call(j,b),j.options.keepSelection||(j.selectedRows=[]),y.call(j,a),t.call(j),v.call(j),j.element._bgBusyAria(!1).trigger("loaded"+H)}var j=this;if(this.element._bgBusyAria(!0).trigger("load"+H),F.call(this),this.options.ajax){var k=g.call(this),l=i.call(this);if(null==l||"string"!=typeof l||0===l.length)throw new Error("Url setting must be a none empty string or a function that returns one.");this.xqr&&this.xqr.abort();var m={url:l,data:k,success:function(b){j.xqr=null,"string"==typeof b&&(b=a.parseJSON(b)),b=j.options.responseHandler(b),j.current=b.current,h(b.rows,b.total)},error:function(a,b,c){j.xqr=null,"abort"!==b&&(u.call(j),j.element._bgBusyAria(!1).trigger("loaded"+H))}};m=a.extend(this.options.ajaxSettings,m),this.xqr=a.ajax(m)}else{var n=this.searchPhrase.length>0?this.rows.where(f):this.rows,o=n.length;-1!==this.rowCount&&(n=n.page(this.current,this.rowCount)),b.setTimeout(function(){h(n,o)},10)}}function o(){if(!this.options.ajax){var b=this,c=this.element.find("tbody > tr");c.each(function(){var c=a(this),e=c.children("td"),f={};a.each(b.columns,function(a,b){f[b.id]=b.converter.from(e.eq(a).text())}),d.call(b,f)}),p.call(this,this.rows.length),G.call(this)}}function p(a){this.total=a,this.totalPages=-1===this.rowCount?1:Math.ceil(this.total/this.rowCount)}function q(){var b=this.options.templates,c=this.element.parent().hasClass(this.options.css.responsiveTable)?this.element.parent():this.element;this.element.addClass(this.options.css.table),0===this.element.children("tbody").length&&this.element.append(b.body),1&this.options.navigation&&(this.header=a(b.header.resolve(f.call(this,{id:this.element._bgId()+"-header"}))),c.before(this.header)),2&this.options.navigation&&(this.footer=a(b.footer.resolve(f.call(this,{id:this.element._bgId()+"-footer"}))),c.after(this.footer))}function r(){if(0!==this.options.navigation){var b=this.options.css,c=h(b.actions),d=e.call(this,c);if(d.length>0){var g=this,i=this.options.templates,j=a(i.actions.resolve(f.call(this)));if(this.options.ajax){var k=i.icon.resolve(f.call(this,{iconCss:b.iconRefresh})),l=a(i.actionButton.resolve(f.call(this,{content:k,text:this.options.labels.refresh}))).on("click"+H,function(a){a.stopPropagation(),g.current=1,n.call(g)});j.append(l)}x.call(this,j),s.call(this,j),E.call(this,d,j)}}}function s(b){if(this.options.columnSelection&&this.columns.length>1){var c=this,d=this.options.css,e=this.options.templates,g=e.icon.resolve(f.call(this,{iconCss:d.iconColumns})),i=a(e.actionDropDown.resolve(f.call(this,{content:g}))),j=h(d.dropDownItem),k=h(d.dropDownItemCheckbox),m=h(d.dropDownMenuItems);a.each(this.columns,function(b,g){if(g.visibleInSelection){var o=a(e.actionDropDownCheckboxItem.resolve(f.call(c,{name:g.id,label:g.text,checked:g.visible}))).on("click"+H,j,function(b){b.stopPropagation();var d=a(this),e=d.find(k);if(!e.prop("disabled")){g.visible=e.prop("checked");var f=c.columns.where(l).length>1;d.parents(m).find(j+":has("+k+":checked)")._bgEnableAria(f).find(k)._bgEnableField(f),c.element.find("tbody").empty(),C.call(c),n.call(c)}});i.find(h(d.dropDownMenuItems)).append(o)}}),b.append(i)}}function t(){if(0!==this.options.navigation){var b=h(this.options.css.infos),c=e.call(this,b);if(c.length>0){var d=this.current*this.rowCount,g=a(this.options.templates.infos.resolve(f.call(this,{end:0===this.total||-1===d||d>this.total?this.total:d,start:0===this.total?0:d-this.rowCount+1,total:this.total})));E.call(this,c,g)}}}function u(){var a=this.element.children("tbody").first(),b=this.options.templates,c=this.columns.where(l).length;this.selection&&(c+=1),a.html(b.noResults.resolve(f.call(this,{columns:c})))}function v(){if(0!==this.options.navigation){var b=h(this.options.css.pagination),c=e.call(this,b)._bgShowAria(-1!==this.rowCount);if(-1!==this.rowCount&&c.length>0){var d=this.options.templates,g=this.current,i=this.totalPages,j=a(d.pagination.resolve(f.call(this))),k=i-g,l=-1*(this.options.padding-g),m=k>=this.options.padding?Math.max(l,1):Math.max(l-this.options.padding+k,1),n=2*this.options.padding+1,o=i>=n?n:i;w.call(this,j,"first","«","first")._bgEnableAria(g>1),w.call(this,j,"prev","<","prev")._bgEnableAria(g>1);for(var p=0;o>p;p++){var q=p+m;w.call(this,j,q,q,"page-"+q)._bgEnableAria()._bgSelectAria(q===g)}0===o&&w.call(this,j,1,1,"page-1")._bgEnableAria(!1)._bgSelectAria(),w.call(this,j,"next",">","next")._bgEnableAria(i>g),w.call(this,j,"last","»","last")._bgEnableAria(i>g),E.call(this,c,j)}}}function w(b,c,d,e){var g=this,i=this.options.templates,j=this.options.css,k=f.call(this,{css:e,text:d,page:c}),l=a(i.paginationItem.resolve(k)).on("click"+H,h(j.paginationButton),function(b){b.stopPropagation(),b.preventDefault();var c=a(this),d=c.parent();if(!d.hasClass("active")&&!d.hasClass("disabled")){var e={first:1,prev:g.current-1,next:g.current+1,last:g.totalPages},f=c.data("page");g.current=e[f]||f,n.call(g)}c.trigger("blur")});return b.append(l),l}function x(b){function c(a){return-1===a?d.options.labels.all:a}var d=this,e=this.options.rowCount;if(a.isArray(e)){var g=this.options.css,i=this.options.templates,j=a(i.actionDropDown.resolve(f.call(this,{content:c(this.rowCount)}))),k=h(g.dropDownMenu),l=h(g.dropDownMenuText),m=h(g.dropDownMenuItems),o=h(g.dropDownItemButton);a.each(e,function(b,e){var g=a(i.actionDropDownItem.resolve(f.call(d,{text:c(e),action:e})))._bgSelectAria(e===d.rowCount).on("click"+H,o,function(b){b.preventDefault();var e=a(this),f=e.data("action");f!==d.rowCount&&(d.current=1,d.rowCount=f,e.parents(m).children().each(function(){var b=a(this),c=b.find(o).data("action");b._bgSelectAria(c===f)}),e.parents(k).find(l).text(c(f)),n.call(d))});j.find(m).append(g)}),b.append(j)}}function y(b){if(b.length>0){var c=this,d=this.options.css,e=this.options.templates,g=this.element.children("tbody").first(),i=!0,j="";a.each(b,function(b,g){var h="",k=' data-row-id="'+(null==c.identifier?b:g[c.identifier])+'"',l="";if(c.selection){var m=-1!==a.inArray(g[c.identifier],c.selectedRows),n=e.select.resolve(f.call(c,{type:"checkbox",value:g[c.identifier],checked:m}));h+=e.cell.resolve(f.call(c,{content:n,css:d.selectCell})),i=i&&m,m&&(l+=d.selected,k+=' aria-selected="true"')}var o=null!=g.status&&c.options.statusMapping[g.status];o&&(l+=o),a.each(c.columns,function(b,i){if(i.visible){var j=a.isFunction(i.formatter)?i.formatter.call(c,i,g):i.converter.to(g[i.id]),k=i.cssClass.length>0?" "+i.cssClass:"";h+=e.cell.resolve(f.call(c,{content:null==j||""===j?" ":j,css:("right"===i.align?d.right:"center"===i.align?d.center:d.left)+k,style:null==i.width?"":"width:"+i.width+";"}))}}),l.length>0&&(k+=' class="'+l+'"'),j+=e.row.resolve(f.call(c,{attr:k,cells:h}))}),c.element.find("thead "+h(c.options.css.selectBox)).prop("checked",i),g.html(j),z.call(this,g)}else u.call(this)}function z(b){var c=this,d=h(this.options.css.selectBox);this.selection&&b.off("click"+H,d).on("click"+H,d,function(b){b.stopPropagation();var d=a(this),e=c.converter.from(d.val());d.prop("checked")?c.select([e]):c.deselect([e])}),b.off("click"+H,"> tr").on("click"+H,"> tr",function(b){b.stopPropagation();var d=a(this),e=null==c.identifier?d.data("row-id"):c.converter.from(d.data("row-id")+""),f=null==c.identifier?c.currentRows[e]:c.currentRows.first(function(a){return a[c.identifier]===e});c.selection&&c.options.rowSelect&&(d.hasClass(c.options.css.selected)?c.deselect([e]):c.select([e])),c.element.trigger("click"+H,[c.columns,f])})}function A(){if(0!==this.options.navigation){var c=this.options.css,d=h(c.search),g=e.call(this,d);if(g.length>0){var i=this,j=this.options.templates,k=null,l="",m=h(c.searchField),n=a(j.search.resolve(f.call(this))),o=n.is(m)?n:n.find(m);o.on("keyup"+H,function(c){c.stopPropagation();var d=a(this).val();(l!==d||13===c.which&&""!==d)&&(l=d,(13===c.which||0===d.length||d.length>=i.options.searchSettings.characters)&&(b.clearTimeout(k),k=b.setTimeout(function(){B.call(i,d)},i.options.searchSettings.delay)))}),E.call(this,g,n)}}}function B(a){this.searchPhrase!==a&&(this.current=1,this.searchPhrase=a,n.call(this))}function C(){var b=this,c=this.element.find("thead > tr"),d=this.options.css,e=this.options.templates,g="",i=this.options.sorting;if(this.selection){var j=this.options.multiSelect?e.select.resolve(f.call(b,{type:"checkbox",value:"all"})):"";g+=e.rawHeaderCell.resolve(f.call(b,{content:j,css:d.selectCell}))}if(a.each(this.columns,function(a,c){if(c.visible){var h=b.sortDictionary[c.id],j=i&&h&&"asc"===h?d.iconUp:i&&h&&"desc"===h?d.iconDown:"",k=e.icon.resolve(f.call(b,{iconCss:j})),l=c.headerAlign,m=c.headerCssClass.length>0?" "+c.headerCssClass:"";g+=e.headerCell.resolve(f.call(b,{column:c,icon:k,sortable:i&&c.sortable&&d.sortable||"",css:("right"===l?d.right:"center"===l?d.center:d.left)+m,style:null==c.width?"":"width:"+c.width+";"}))}}),c.html(g),i){var k=h(d.sortable);c.off("click"+H,k).on("click"+H,k,function(c){c.preventDefault(),D.call(b,a(this)),G.call(b),n.call(b)})}if(this.selection&&this.options.multiSelect){var l=h(d.selectBox);c.off("click"+H,l).on("click"+H,l,function(c){c.stopPropagation(),a(this).prop("checked")?b.select():b.deselect()})}}function D(a){var b=this.options.css,c=h(b.icon),d=a.data("column-id")||a.parents("th").first().data("column-id"),e=this.sortDictionary[d],f=a.find(c);if(this.options.multiSort||(a.parents("tr").first().find(c).removeClass(b.iconDown+" "+b.iconUp),this.sortDictionary={}),e&&"asc"===e)this.sortDictionary[d]="desc",f.removeClass(b.iconUp).addClass(b.iconDown);else if(e&&"desc"===e)if(this.options.multiSort){var g={};for(var i in this.sortDictionary)i!==d&&(g[i]=this.sortDictionary[i]);this.sortDictionary=g,f.removeClass(b.iconDown)}else this.sortDictionary[d]="asc",f.removeClass(b.iconDown).addClass(b.iconUp);else this.sortDictionary[d]="asc",f.addClass(b.iconUp)}function E(b,c){b.each(function(b,d){a(d).before(c.clone(!0)).remove()})}function F(){var a=this;b.setTimeout(function(){if("true"===a.element._bgAria("busy")){var b=a.options.templates,c=a.element.children("thead").first(),d=a.element.children("tbody").first(),e=d.find("tr > td").first(),g=a.element.height()-c.height()-(e.height()+20),h=a.columns.where(l).length;a.selection&&(h+=1),d.html(b.loading.resolve(f.call(a,{columns:h}))),-1!==a.rowCount&&g>0&&d.find("tr > td").css("padding","20px 0 "+g+"px")}},250)}function G(){function a(c,d,e){function f(a){return"asc"===h.order?a:-1*a}e=e||0;var g=e+1,h=b[e];return c[h.id]>d[h.id]?f(1):c[h.id]<d[h.id]?f(-1):b.length>g?a(c,d,g):0}var b=[];if(!this.options.ajax){for(var c in this.sortDictionary)(this.options.multiSort||0===b.length)&&b.push({id:c,order:this.sortDictionary[c]});b.length>0&&this.rows.sort(a)}}var H=".rs.jquery.bootgrid",I=function(b,c){this.element=a(b),this.origin=this.element.clone(),this.options=a.extend(!0,{},I.defaults,this.element.data(),c);var d=this.options.rowCount=this.element.data().rowCount||c.rowCount||this.options.rowCount;this.columns=[],this.current=1,this.currentRows=[],this.identifier=null,this.selection=!1,this.converter=null,this.rowCount=a.isArray(d)?d[0]:d,this.rows=[],this.searchPhrase="",this.selectedRows=[],this.sortDictionary={},this.total=0,this.totalPages=0,this.cachedParams={lbl:this.options.labels,css:this.options.css,ctx:{}},this.header=null,this.footer=null,this.xqr=null};if(I.defaults={navigation:3,padding:2,columnSelection:!0,rowCount:[10,25,50,-1],selection:!1,multiSelect:!1,rowSelect:!1,keepSelection:!1,highlightRows:!1,sorting:!0,multiSort:!1,searchSettings:{delay:250,characters:1},ajax:!1,ajaxSettings:{method:"POST"},post:{},url:"",caseSensitive:!0,regexSearch:!0,requestHandler:function(a){return a},responseHandler:function(a){return a},converters:{numeric:{from:function(a){return+a},to:function(a){return a+""}},string:{from:function(a){return a},to:function(a){return a}}},css:{actions:"actions btn-group",center:"text-center",columnHeaderAnchor:"column-header-anchor",columnHeaderText:"text",dropDownItem:"dropdown-item",dropDownItemButton:"dropdown-item-button",dropDownItemCheckbox:"dropdown-item-checkbox",dropDownMenu:"dropdown btn-group",dropDownMenuItems:"dropdown-menu pull-right",dropDownMenuText:"dropdown-text",footer:"bootgrid-footer container-fluid",header:"bootgrid-header container-fluid",icon:"icon glyphicon",iconColumns:"glyphicon-th-list",iconDown:"glyphicon-chevron-down",iconRefresh:"glyphicon-refresh",iconSearch:"glyphicon-search",iconUp:"glyphicon-chevron-up",infos:"infos",left:"text-left",pagination:"pagination",paginationButton:"button",responsiveTable:"table-responsive",right:"text-right",search:"search form-group",searchField:"search-field form-control",selectBox:"select-box",selectCell:"select-cell",selected:"active",sortable:"sortable",table:"bootgrid-table table"},formatters:{},labels:{all:"All",infos:"Showing {{ctx.start}} to {{ctx.end}} of {{ctx.total}} entries",loading:"Loading...",noResults:"No results found!",refresh:"Refresh",search:"Search"},statusMapping:{0:"success",1:"info",2:"warning",3:"danger"},templates:{actionButton:'<button class="btn btn-default" type="button" title="{{ctx.text}}">{{ctx.content}}</button>',actionDropDown:'<div class="{{css.dropDownMenu}}"><button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown"><span class="{{css.dropDownMenuText}}">{{ctx.content}}</span> <span class="caret"></span></button><ul class="{{css.dropDownMenuItems}}" role="menu"></ul></div>',actionDropDownItem:'<li><a data-action="{{ctx.action}}" class="{{css.dropDownItem}} {{css.dropDownItemButton}}">{{ctx.text}}</a></li>',actionDropDownCheckboxItem:'<li><label class="{{css.dropDownItem}}"><input name="{{ctx.name}}" type="checkbox" value="1" class="{{css.dropDownItemCheckbox}}" {{ctx.checked}} /> {{ctx.label}}</label></li>',actions:'<div class="{{css.actions}}"></div>',body:"<tbody></tbody>",cell:'<td class="{{ctx.css}}" style="{{ctx.style}}">{{ctx.content}}</td>',footer:'<div id="{{ctx.id}}" class="{{css.footer}}"><div class="row"><div class="col-sm-6"><p class="{{css.pagination}}"></p></div><div class="col-sm-6 infoBar"><p class="{{css.infos}}"></p></div></div></div>',header:'<div id="{{ctx.id}}" class="{{css.header}}"><div class="row"><div class="col-sm-12 actionBar"><p class="{{css.search}}"></p><p class="{{css.actions}}"></p></div></div></div>',headerCell:'<th data-column-id="{{ctx.column.id}}" class="{{ctx.css}}" style="{{ctx.style}}"><a href="javascript:void(0);" class="{{css.columnHeaderAnchor}} {{ctx.sortable}}"><span class="{{css.columnHeaderText}}">{{ctx.column.text}}</span>{{ctx.icon}}</a></th>',icon:'<span class="{{css.icon}} {{ctx.iconCss}}"></span>',infos:'<div class="{{css.infos}}">{{lbl.infos}}</div>',loading:'<tr><td colspan="{{ctx.columns}}" class="loading">{{lbl.loading}}</td></tr>',noResults:'<tr><td colspan="{{ctx.columns}}" class="no-results">{{lbl.noResults}}</td></tr>',pagination:'<ul class="{{css.pagination}}"></ul>',paginationItem:'<li class="{{ctx.css}}"><a data-page="{{ctx.page}}" class="{{css.paginationButton}}">{{ctx.text}}</a></li>',rawHeaderCell:'<th class="{{ctx.css}}">{{ctx.content}}</th>',row:"<tr{{ctx.attr}}>{{ctx.cells}}</tr>",search:'<div class="{{css.search}}"><div class="input-group"><span class="{{css.icon}} input-group-addon {{css.iconSearch}}"></span> <input type="text" class="{{css.searchField}}" placeholder="{{lbl.search}}" /></div></div>',select:'<input name="select" type="{{ctx.type}}" class="{{css.selectBox}}" value="{{ctx.value}}" {{ctx.checked}} />'}},I.prototype.append=function(a){if(this.options.ajax);else{for(var b=[],c=0;c<a.length;c++)d.call(this,a[c])&&b.push(a[c]);G.call(this),k.call(this,b),n.call(this),this.element.trigger("appended"+H,[b])}return this},I.prototype.clear=function(){if(this.options.ajax);else{var b=a.extend([],this.rows);this.rows=[],this.current=1,this.total=0,n.call(this),this.element.trigger("cleared"+H,[b])}return this},I.prototype.destroy=function(){return a(b).off(H),1&this.options.navigation&&this.header.remove(),2&this.options.navigation&&this.footer.remove(),this.element.before(this.origin).remove(),this},I.prototype.reload=function(){return this.current=1,n.call(this),this},I.prototype.remove=function(a){if(null!=this.identifier){if(this.options.ajax);else{a=a||this.selectedRows;for(var b,c=[],d=0;d<a.length;d++){b=a[d];for(var e=0;e<this.rows.length;e++)if(this.rows[e][this.identifier]===b){c.push(this.rows[e]),this.rows.splice(e,1);break}}this.current=1,n.call(this),this.element.trigger("removed"+H,[c])}}return this},I.prototype.search=function(a){if(a=a||"",this.searchPhrase!==a){var b=h(this.options.css.searchField),c=e.call(this,b);c.val(a)}return B.call(this,a),this},I.prototype.select=function(b){if(this.selection){b=b||this.currentRows.propValues(this.identifier);for(var c,d,e=[];b.length>0&&(this.options.multiSelect||1!==e.length);)if(c=b.pop(),-1===a.inArray(c,this.selectedRows))for(d=0;d<this.currentRows.length;d++)if(this.currentRows[d][this.identifier]===c){e.push(this.currentRows[d]),this.selectedRows.push(c);break}if(e.length>0){var f=h(this.options.css.selectBox),g=this.selectedRows.length>=this.currentRows.length;for(d=0;!this.options.keepSelection&&g&&d<this.currentRows.length;)g=-1!==a.inArray(this.currentRows[d++][this.identifier],this.selectedRows);for(this.element.find("thead "+f).prop("checked",g),this.options.multiSelect||this.element.find("tbody > tr "+f+":checked").trigger("click"+H),d=0;d<this.selectedRows.length;d++)this.element.find('tbody > tr[data-row-id="'+this.selectedRows[d]+'"]').addClass(this.options.css.selected)._bgAria("selected","true").find(f).prop("checked",!0);this.element.trigger("selected"+H,[e])}}return this},I.prototype.deselect=function(b){if(this.selection){b=b||this.currentRows.propValues(this.identifier);for(var c,d,e,f=[];b.length>0;)if(c=b.pop(),e=a.inArray(c,this.selectedRows),-1!==e)for(d=0;d<this.currentRows.length;d++)if(this.currentRows[d][this.identifier]===c){f.push(this.currentRows[d]),this.selectedRows.splice(e,1);break}if(f.length>0){var g=h(this.options.css.selectBox);for(this.element.find("thead "+g).prop("checked",!1),d=0;d<f.length;d++)this.element.find('tbody > tr[data-row-id="'+f[d][this.identifier]+'"]').removeClass(this.options.css.selected)._bgAria("selected","false").find(g).prop("checked",!1);this.element.trigger("deselected"+H,[f])}}return this},I.prototype.sort=function(b){var c=b?a.extend({},b):{};return c===this.sortDictionary?this:(this.sortDictionary=c,C.call(this),G.call(this),n.call(this),this)},I.prototype.getColumnSettings=function(){return a.merge([],this.columns)},I.prototype.getCurrentPage=function(){return this.current},I.prototype.getCurrentRows=function(){return a.merge([],this.currentRows)},I.prototype.getRowCount=function(){return this.rowCount},I.prototype.getSearchPhrase=function(){return this.searchPhrase},I.prototype.getSelectedRows=function(){return a.merge([],this.selectedRows)},I.prototype.getSortDictionary=function(){return a.extend({},this.sortDictionary)},I.prototype.getTotalPageCount=function(){return this.totalPages},I.prototype.getTotalRowCount=function(){return this.total},a.fn.extend({_bgAria:function(a,b){return b?this.attr("aria-"+a,b):this.attr("aria-"+a)},_bgBusyAria:function(a){return null==a||a?this._bgAria("busy","true"):this._bgAria("busy","false")},_bgRemoveAria:function(a){return this.removeAttr("aria-"+a)},_bgEnableAria:function(a){return null==a||a?this.removeClass("disabled")._bgAria("disabled","false"):this.addClass("disabled")._bgAria("disabled","true")},_bgEnableField:function(a){return null==a||a?this.removeAttr("disabled"):this.attr("disabled","disable")},_bgShowAria:function(a){return null==a||a?this.show()._bgAria("hidden","false"):this.hide()._bgAria("hidden","true")},_bgSelectAria:function(a){return null==a||a?this.addClass("active")._bgAria("selected","true"):this.removeClass("active")._bgAria("selected","false")},_bgId:function(a){return a?this.attr("id",a):this.attr("id")}}),!String.prototype.resolve){var J={checked:function(a){return"boolean"==typeof a?a?'checked="checked"':"":a}};String.prototype.resolve=function(b,c){var d=this;return a.each(b,function(b,e){if(null!=e&&"function"!=typeof e)if("object"==typeof e){var f=c?a.extend([],c):[];f.push(b),d=d.resolve(e,f)+""}else{J&&J[b]&&"function"==typeof J[b]&&(e=J[b](e)),b=c?c.join(".")+"."+b:b;var g=new RegExp("\\{\\{"+b+"\\}\\}","gm");d=d.replace(g,e.replace?e.replace(/\$/gi,"$"):e)}}),d}}Array.prototype.first||(Array.prototype.first=function(a){for(var b=0;b<this.length;b++){var c=this[b];if(a(c))return c}return null}),Array.prototype.contains||(Array.prototype.contains=function(a){for(var b=0;b<this.length;b++){var c=this[b];if(a(c))return!0}return!1}),Array.prototype.page||(Array.prototype.page=function(a,b){var c=(a-1)*b,d=c+b;return this.length>c?this.length>d?this.slice(c,d):this.slice(c):[]}),Array.prototype.where||(Array.prototype.where=function(a){for(var b=[],c=0;c<this.length;c++){var d=this[c];a(d)&&b.push(d)}return b}),Array.prototype.propValues||(Array.prototype.propValues=function(a){for(var b=[],c=0;c<this.length;c++)b.push(this[c][a]);return b});var K=a.fn.bootgrid;a.fn.bootgrid=function(b){var c=Array.prototype.slice.call(arguments,1),d=null,e=this.each(function(e){var f=a(this),g=f.data(H),h="object"==typeof b&&b;if((g||"destroy"!==b)&&(g||(f.data(H,g=new I(this,h)),j.call(g)),"string"==typeof b))if(0===b.indexOf("get")&&0===e)d=g[b].apply(g,c);else if(0!==b.indexOf("get"))return g[b].apply(g,c)});return"string"==typeof b&&0===b.indexOf("get")?d:e},a.fn.bootgrid.Constructor=I,a.fn.bootgrid.noConflict=function(){return a.fn.bootgrid=K,this},a('[data-toggle="bootgrid"]').bootgrid()}(jQuery,window); \ No newline at end of file diff --git a/src/internal.js b/src/internal.js index 9b9bba4..add3650 100644 --- a/src/internal.js +++ b/src/internal.js @@ -159,16 +159,37 @@ function loadData() this.element._bgBusyAria(true).trigger("load" + namespace); showLoading.call(this); + function stringSearch(needle, haystack) + { + var index; + if (that.options.regexSearch) { + index = haystack.search(needle); + } else { + index = haystack.toLowerCase().indexOf(needle); + } + return index > -1 ? true : false; + } + + function getRegexSearchPhrase (searchPhrase) { + return new RegExp(searchPhrase, that.options.caseSensitive ? "g" : "gi"); + } + + function getPlainSearchPhrase (searchPhrase) { + return that.options.caseSensitive ? searchPhrase : searchPhrase.toLowerCase(); + } + function containsPhrase(row) { - var column, - searchPattern = new RegExp(that.searchPhrase, (that.options.caseSensitive) ? "g" : "gi"); + var column; + var searchPattern = + that.options.regexSearch ? + getRegexSearchPhrase(that.searchPhrase) : + getPlainSearchPhrase(that.searchPhrase); for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && column.visible && - column.converter.to(row[column.id]).search(searchPattern) > -1) + if (column.searchable && column.visible && stringSearch(searchPattern, column.converter.to(row[column.id]))) { return true; } diff --git a/src/public.js b/src/public.js index 16c5c44..cf38ec9 100644 --- a/src/public.js +++ b/src/public.js @@ -208,6 +208,15 @@ Grid.defaults = { **/ caseSensitive: true, + /** + * Defines if regex should be used when searching + * + * @property regexSearch + * @type Boolean + * @for defaults + **/ + regexSearch: true, + // note: The following properties should not be used via data-api attributes /**