From 891c72dc7c17e1480822d767f71e6960e9310b2b Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Thu, 4 Aug 2016 08:52:51 -0700 Subject: [PATCH 01/30] Sort order respect caseSensitive flag --- .gitignore | 3 +- Gruntfile.js | 6 +- demo/index.htm | 616 +++-- dist/jQuery.Bootgrid.1.3.1.nupkg | Bin 27538 -> 0 bytes dist/jquery.bootgrid-1.3.1.zip | Bin 29488 -> 0 bytes dist/jquery.bootgrid.css | 4 +- dist/jquery.bootgrid.fa.js | 20 +- dist/jquery.bootgrid.fa.min.js | 6 - dist/jquery.bootgrid.js | 3605 +++++++++++++++--------------- dist/jquery.bootgrid.min.css | 5 - dist/jquery.bootgrid.min.js | 6 - package.json | 127 +- src/internal.js | 1655 +++++++------- 13 files changed, 3075 insertions(+), 2978 deletions(-) delete mode 100644 dist/jQuery.Bootgrid.1.3.1.nupkg delete mode 100644 dist/jquery.bootgrid-1.3.1.zip delete mode 100644 dist/jquery.bootgrid.fa.min.js delete mode 100644 dist/jquery.bootgrid.min.css delete mode 100644 dist/jquery.bootgrid.min.js diff --git a/.gitignore b/.gitignore index 1cc5b6e..cd945ed 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/Gruntfile.js b/Gruntfile.js index e584c33..f9e9e4f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -55,7 +55,7 @@ module.exports = function (grunt) scripts: { options: { separator: '\r\n\r\n', - banner: '<%= banner %>;(function ($, window, undefined)\r\n{\r\n /*jshint validthis: true */\r\n "use strict";\r\n\r\n', + banner: '<%= banner %>;(function ($, window, undefined)\r\n{\r\n /*jshint validthis: true, latedef: false */\r\n "use strict";\r\n\r\n', footer: '\r\n})(jQuery, window);', process: function(src, filepath) { @@ -210,7 +210,7 @@ module.exports = function (grunt) files: [ { flatten: true, - expand: true, + expand: true, src: ['<%= folders.dist %>/*.js', '<%= folders.dist %>/*.css'], dest: '/' } ] @@ -259,4 +259,4 @@ module.exports = function (grunt) grunt.registerTask('build', ['clean:build', 'version', 'less', 'concat', 'csslint', 'jshint', 'test']); grunt.registerTask('release', ['build', 'api', 'cssmin', 'uglify', 'compress', 'nugetpack']); grunt.registerTask('publish', ['nugetpush', 'exec:publish']); -}; \ No newline at end of file +}; diff --git a/demo/index.htm b/demo/index.htm index ae13225..5572e37 100644 --- a/demo/index.htm +++ b/demo/index.htm @@ -1,23 +1,40 @@ - + - + jQuery Bootgrid Demo - - - + + + @@ -33,7 +50,9 @@ @@ -62,108 +81,381 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IDSenderReceivedLinkStatusHidden
1me@rafaelstaib.com11.12.2014Link999Hidden value 1
2me@rafaelstaib.com12.12.2014Link999Hidden value 1
3me@rafaelstaib.com10.12.2014Link2Hidden value 1
4mo@rafaelstaib.com12.08.2014Link999Hidden value 1
5ma@rafaelstaib.com12.06.2014Link3Hidden value 1
6me@rafaelstaib.com12.12.2014Link999Hidden value 1
7ma@rafaelstaib.com12.11.2014Link999Hidden value 1
8mo@rafaelstaib.com15.12.2014Link999Hidden value 1
9me@rafaelstaib.com24.12.2014Link0Hidden value 1
10ma@rafaelstaib.com14.12.2014Link1Hidden value 1
11mo@rafaelstaib.com12.12.2014Link999Hidden value 1
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDNameSenderReceivedLinkStatusHidden
1Aimee Delaneyerat.vitae@estvitaesodales.com03.06.16Maecenas Malesuada Foundation1C9F35176-EE9C-7DA5-DB47-5B62C57AA176
2BORIS BULEvitae.diam@Proinultrices.edu10.12.16Ac Corporation31BC83AA2-0979-C426-A3B4-4CC4F8B0CFF9
3ALEX Reesemauris.sit.amet@nonmassa.ca27.06.17Nulla At Incorporated5C3CF97AE-0E0E-EA4E-FBF5-A63444134B8E
4Juliet Mcfarlandegestas.Duis.ac@eu.net02.07.16Nisi Sem Semper Limited333AC322B-8AB4-B9F0-FAF0-BD69A7937627
5Lester WeaverCurabitur@fermentumconvallisligula.org07.06.17Magna Nec Quam Industries2235B6F73-2150-A423-DC0A-A75C74669824
6Aaron HendersonIn@volutpat.co.uk29.01.16Nec Tellus Incorporated1284D00C0-6A24-E9B3-E462-3A3892BA7EF1
7Sloane CraftFusce.feugiat.Lorem@tellusAeneanegestas.co.uk02.02.17Non Sollicitudin Institute1BC72D4C2-C600-44CF-D8CE-A3A0C8C98224
8Mufutau CookeSed@tempusnonlacinia.ca02.05.16Nascetur Ridiculus Mus Company11DF470B0-15B8-44C2-0991-A15F13EBE087
9Brooke MaloneAliquam.adipiscing@posuere.edu27.06.16Ut Eros Inc.47346FBE9-4AC2-ECD4-96D8-75BF2B20E021
10Rooney ClineProin.ultrices.Duis@eu.com29.08.16Mauris Integer Inc.287412B97-5862-AB2C-EB92-F7692EB0C17A
11Alexander VelasquezPraesent.interdum@infaucibusorci.org14.01.17Luctus Et Limited2E4DAE99C-6078-A89E-210A-C21487A90361
12Stone SosaUt@turpis.com18.05.16Orci Luctus Et Foundation444723C37-B0D3-FBE6-E1A2-6EA3E13388E3
13Graham Harveynulla.Integer@Phasellus.edu02.03.17Nibh Donec Est Consulting1AA3B3771-D300-8411-1167-AC7AEA0995B2
14Lance Oliverelit.sed.consequat@mollis.com19.03.16Dignissim Consulting4D8241C7E-4B13-7A24-DD90-CFAC74C44E40
15Cassady Brightlibero.dui.nec@dis.org24.12.16Nulla Consulting16A4BB223-3FC0-242F-CDBD-341E014BA7CD
16Brett Sloanligula.Donec.luctus@scelerisquescelerisque.co.uk25.05.17Velit Pellentesque LLP378A0AC6A-55CE-E7F9-9E8D-F9638A067FC1
17Cheyenne Herringmolestie.pharetra@egetmetus.ca17.09.16Egestas Foundation437D88706-931A-25EF-ABEB-D65F3D72BB8A
18Riley Vanceporttitor.interdum@pharetra.edu19.06.16Elit Nulla Facilisi Inc.5C919D2B9-63E6-BF6D-6639-F058F0141D27
19Neve Owennunc.sed.pede@lectusrutrum.edu15.11.15Leo Morbi Incorporated2AA9836DC-8FA0-DCE3-12F0-78ABD416279C
20Fredericka Turnernulla.magna.malesuada@musProin.edu30.01.17Ac Urna Corp.2F51F8323-42AB-5E84-091D-F02B5E1E04E3
21Martin SharpeCras@Suspendissenon.com06.10.15Etiam Imperdiet Dictum LLP47EDF8E88-70D2-4A80-AEF6-60661DF2ECA9
22Ray OconnorFusce.aliquam.enim@tristiquepharetraQuisque.edu03.11.16Consectetuer Cursus Company3334C5153-2E2C-EB0A-C42C-CF221CCD6674
23Yetta Beckerdictum.placerat@in.org24.12.16Sed Company489877E4F-8A15-5CAE-75DA-278F27D0B49F
24Ishmael Wileyarcu@ornare.edu14.08.16Velit Institute438FB6436-85EB-9DE4-4A77-17B20941C627
25Omar Mckinneya.dui.Cras@Integeraliquamadipiscing.edu03.02.17Quis Massa Ltd20619ED7E-4295-962D-A068-99AC32D0D088
26Lucy SmallMaecenas.iaculis.aliquet@diamDuismi.co.uk13.03.17Dolor Dapibus Gravida Associates45322357B-864C-9D07-6220-551095B9E85A
27Davis Hensonmus.Aenean@enim.net26.08.16Elementum Corp.5F8403A4B-1604-1798-7948-1B6C2E48D386
28Ralph Travistristique.pellentesque.tellus@vulputatedui.ca16.08.15Posuere Cubilia Curae; LLP48081C1C0-5EF3-C7FF-7307-2E59683D86E0
29Wayne HamptonNam.interdum@Proin.ca19.01.17Dictum Placerat Augue Foundation51A7276D5-A929-72E5-A397-F3AB7AE0EEBC
30Priscilla Robersonmagna.Nam.ligula@lectus.com26.08.16Tortor At Industries22AFA1C2B-CDD0-84E7-8535-DC8551AE9945
31Garth GreeneCum.sociis.natoque@lacusUt.org04.08.16Tincidunt Inc.54580886C-89E0-8FEE-CEFD-ED87FAC5FDA2
32Erasmus Hamptonnunc@non.net08.10.16Dui Nec Urna PC4C1B98F7D-0554-7719-697D-754B1711F76C
33Doris MaynardDonec.egestas.Aliquam@dolorsitamet.com11.07.17Est Arcu PC1C039DE61-4AFA-1B40-1074-5DD823625E00
34Margaret Gonzalezpellentesque.tellus.sem@mi.ca19.11.16A Associates294C687D6-0085-D04C-1D16-7945411E9E3A
35Kylan WattsCras@atnisiCum.com17.07.16Rutrum Consulting18CF021E9-AEE6-79DE-A368-BBE456153B5A
36Ursula Lewistempus@sitametconsectetuer.net16.09.16Parturient Montes Consulting4EC79724F-9632-7C26-ED36-60737A57E57A
37Vance AdkinsCurabitur.egestas.nunc@Sed.ca17.12.16Tristique Pharetra Company10720A4E2-19D4-5023-4E56-659C63B3D655
38Steven Frankviverra@lectus.co.uk15.10.15Aliquam Gravida Mauris Foundation2C73C063E-68AF-BA1B-F318-B5A1F8D98E27
39Rhoda Mccartyelit.dictum@sollicitudincommodo.com24.08.15Pellentesque Inc.461308679-75BF-06BA-AA56-A29DF911537D
40Dara Hudsonac.feugiat.non@neque.ca05.09.15Sit Consulting18B1634F8-05AA-CBBD-B7A7-EC9F3CE97AC3
@@ -178,101 +470,87 @@ - \ No newline at end of file + diff --git a/dist/jQuery.Bootgrid.1.3.1.nupkg b/dist/jQuery.Bootgrid.1.3.1.nupkg deleted file mode 100644 index c430a89692eed3a2e7d5fb7b5e5ecfbb6e8445ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27538 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 A4gdfE diff --git a/dist/jquery.bootgrid-1.3.1.zip b/dist/jquery.bootgrid-1.3.1.zip deleted file mode 100644 index 61cbe122174be1b1191aed3a4b537e73f4f229d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29488 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≻54{i51ivl7?F&(5>(`*m z9*X(orXj{Nv7Ry*6ZUCXD4RY$i32JxWWjE@crC2)>_GcVh)+%xjV|(q@ZlI2N3fF5 zV#{z$rD>tPSGjf57EfF&=)k60Q+cAEF9I2g>s)Z!ZH4nmbQcq5j;{Tl!M7o>i^k=p zUURGo-Ij0kkX)t?NQ0C#n^)*`U(P~&kRX6sctc8!lTA7?6de8Z-SpzN(Lp7r0H30? z3u@t5bcfEo@%NA+s4KrP9HjKOAj$DM_YIl5GnVU-EFihFV40c+bNxO4{4ler#aim3 zfJZ5;TVZ8N-RL}usCw|WE_53&&0)sg9+h&?`%7+iCcUzwSGdc|K~6Vr-b8|NZZE94 z@ufM>{-?{+g2QrY<&eQtQXbHC5kfO-XVBB&NnD?q12vXBNQLx%v3%X&DGUKT^0jA4 z=m4U(Jw+yDKn7dZJm+m&#bo<^=|eLx`NHiVz<+l9+Fj&x&EFkw4foHE2LwU^_*V|& zzb69zlf(Fb)_+Z9{LdEuo_Y6Abl|`0*WVPp|I*@r0MGwy^Y6guKLL6Fs{6lbh5y;c z%K1OX|ND{WAFT9W_4zk#@;}G__c{E#HvV%s`d4{c{@*?RYT tr").first(), - sorted = false; - - /*jshint -W018*/ - firstHeadRow.children().each(function () - { - 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; - } - - // Prevents multiple identifiers - if (column.identifier) - { - that.identifier = column.id; - that.converter = column.converter; - } - - // 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*/ - } - - /* - response = { - current: 1, - rowCount: 10, - rows: [{}, {}], - sort: [{ "columnId": "asc" }], - total: 101 - } - */ - - function loadData() - { - var that = this; - - this.element._bgBusyAria(true).trigger("load" + namespace); - showLoading.call(this); - - function containsPhrase(row) - { - 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 && - column.converter.to(row[column.id]).search(searchPattern) > -1) - { - return true; - } - } - - return false; - } - - function update(rows, total) - { - that.currentRows = rows; - setTotals.call(that, total); - - if (!that.options.keepSelection) - { - that.selectedRows = []; - } - - renderRows.call(that, rows); - renderInfos.call(that); - renderPagination.call(that); - - that.element._bgBusyAria(false).trigger("loaded" + namespace); - } - - if (this.options.ajax) - { - var request = getRequest.call(this), - url = getUrl.call(this); - - 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(); - } - - var settings = { - url: url, - data: request, - success: function(response) - { - that.xqr = null; - - if (typeof (response) === "string") - { - response = $.parseJSON(response); - } - - response = that.options.responseHandler(response); - - that.current = response.current; - update(response.rows, response.total); - }, - error: function (jqXHR, textStatus, errorThrown) - { - that.xqr = null; - - if (textStatus !== "abort") - { - 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); - } - - // 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() - { - if (!this.options.ajax) - { - var that = this, - rows = this.element.find("tbody > tr"); - - rows.each(function () - { - var $this = $(this), - cells = $this.children("td"), - row = {}; - - $.each(that.columns, function (i, column) - { - row[column.id] = column.converter.from(cells.eq(i).text()); - }); - - appendRow.call(that, row); - }); - - 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); - } - - if (this.options.navigation & 1) - { - this.header = $(tpl.header.resolve(getParams.call(this, { id: this.element._bgId() + "-header" }))); - wrapper.before(this.header); - } - - if (this.options.navigation & 2) - { - this.footer = $(tpl.footer.resolve(getParams.call(this, { id: this.element._bgId() + "-footer" }))); - wrapper.after(this.footer); - } - } - - function renderActions() - { - if (this.options.navigation !== 0) - { - var css = this.options.css, - selector = getCssSelector(css.actions), - actionItems = findFooterAndHeaderItems.call(this, selector); - - if (actionItems.length > 0) - { - 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) - { - // todo: prevent multiple fast clicks (fast click detection) - e.stopPropagation(); - that.current = 1; - loadData.call(that); - }); - actions.append(refresh); - } - - // Row count selection - renderRowCountSelection.call(this, actions); - - // Column selection - renderColumnSelection.call(this, actions); - - replacePlaceHolder.call(this, actionItems, actions); - } - } - } - - function renderColumnSelection(actions) - { - 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); - - $.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); - } - } - - function renderInfos() - { - if (this.options.navigation !== 0) - { - var selector = getCssSelector(this.options.css.infos), - infoItems = findFooterAndHeaderItems.call(this, selector); - - 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 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) - { - 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 (count === 0) - { - renderPaginationItem.call(this, pagination, 1, 1, "page-" + 1) - ._bgEnableAria(false)._bgSelectAria(); - } - - renderPaginationItem.call(this, pagination, "next", ">", "next") - ._bgEnableAria(totalPages > current); - renderPaginationItem.call(this, pagination, "last", "»", "last") - ._bgEnableAria(totalPages > current); - - replacePlaceHolder.call(this, paginationItems, pagination); - } - } - } - - 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) - { - 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; - } - - function renderRowCountSelection(actions) - { - var that = this, - rowCountList = this.options.rowCount; - - function getText(value) - { - return (value === -1) ? that.options.labels.all : value; - } - - if ($.isArray(rowCountList)) - { - 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); - - $.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) - { - 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 () - { - 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) - { - 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 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) - { - rowCss += status; - } - - $.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 + ";" })); - } - }); - - if (rowCss.length > 0) - { - rowAttr += " class=\"" + rowCss + "\""; - } - 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); - - tbody.html(html); - - registerRowEvents.call(this, tbody); - } - else - { - renderNoResultsRow.call(this); - } - } - - function registerRowEvents(tbody) - { - 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()); - - if ($this.prop("checked")) - { - that.select([id]); - } - else - { - that.deselect([id]); - } - }); - } - - tbody.off("click" + namespace, "> tr") - .on("click" + namespace, "> tr", 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; }); - - 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) - { - 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 !== "")) - { - currentValue = newValue; - if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) - { - window.clearTimeout(timer); - timer = window.setTimeout(function () - { - executeSearch.call(that, newValue); - }, that.options.searchSettings.delay); - } - } - }); - - replacePlaceHolder.call(this, searchItems, search); - } - } - } - - function executeSearch(phrase) - { - if (this.searchPhrase !== phrase) - { - this.current = 1; - this.searchPhrase = phrase; - loadData.call(this); - } +// GRID INTERNAL FIELDS +// ==================== + +var namespace = ".rs.jquery.bootgrid"; + +// 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 findFooterAndHeaderItems(selector) { + var footer = (this.footer) ? this.footer.find(selector) : $(), + header = (this.header) ? this.header.find(selector) : $(); + return $.merge(footer, header); +} + +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); + + this.element.trigger("initialized" + namespace); +} + +function highlightAppendedRows(rows) { + if (this.options.highlightRows) { + // todo: implement + } +} + +function isVisible(column) { + return column.visible; +} + +function loadColumns() { + var that = this, + firstHeadRow = this.element.find("thead > tr").first(), + sorted = false; + + /*jshint -W018*/ + firstHeadRow.children().each(function() { + 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; + } + + // Prevents multiple identifiers + if (column.identifier) { + that.identifier = column.id; + that.converter = column.converter; + } + + // 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*/ +} + +/* +response = { + current: 1, + rowCount: 10, + rows: [{}, {}], + sort: [{ "columnId": "asc" }], + total: 101 +} +*/ + +function loadData() { + var that = this; + + this.element._bgBusyAria(true).trigger("load" + namespace); + showLoading.call(this); + + function containsPhrase(row) { + 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 && + column.converter.to(row[column.id]).search(searchPattern) > -1) { + return true; + } + } + + return false; + } + + function update(rows, total) { + that.currentRows = rows; + setTotals.call(that, total); + + if (!that.options.keepSelection) { + that.selectedRows = []; + } + + renderRows.call(that, rows); + renderInfos.call(that); + renderPagination.call(that); + + that.element._bgBusyAria(false).trigger("loaded" + namespace); + } + + if (this.options.ajax) { + var request = getRequest.call(this), + url = getUrl.call(this); + + 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(); + } + + var settings = { + url: url, + data: request, + success: function(response) { + that.xqr = null; + + if (typeof(response) === "string") { + response = $.parseJSON(response); + } + + response = that.options.responseHandler(response); + + that.current = response.current; + update(response.rows, response.total); + }, + error: function(jqXHR, textStatus, errorThrown) { + that.xqr = null; + + if (textStatus !== "abort") { + 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); + } + + // 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() { + if (!this.options.ajax) { + var that = this, + rows = this.element.find("tbody > tr"); + + rows.each(function() { + var $this = $(this), + cells = $this.children("td"), + row = {}; + + $.each(that.columns, function(i, column) { + row[column.id] = column.converter.from(cells.eq(i).text()); + }); + + appendRow.call(that, row); + }); + + 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); + } + + if (this.options.navigation & 1) { + this.header = $(tpl.header.resolve(getParams.call(this, { + id: this.element._bgId() + "-header" + }))); + wrapper.before(this.header); + } + + if (this.options.navigation & 2) { + this.footer = $(tpl.footer.resolve(getParams.call(this, { + id: this.element._bgId() + "-footer" + }))); + wrapper.after(this.footer); + } +} + +function renderActions() { + if (this.options.navigation !== 0) { + var css = this.options.css, + selector = getCssSelector(css.actions), + actionItems = findFooterAndHeaderItems.call(this, selector); + + if (actionItems.length > 0) { + 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) { + // todo: prevent multiple fast clicks (fast click detection) + e.stopPropagation(); + that.current = 1; + loadData.call(that); + }); + actions.append(refresh); + } + + // Row count selection + renderRowCountSelection.call(this, actions); + + // Column selection + renderColumnSelection.call(this, actions); + + replacePlaceHolder.call(this, actionItems, actions); + } + } +} + +function renderColumnSelection(actions) { + 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); + + $.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); + } +} + +function renderInfos() { + if (this.options.navigation !== 0) { + var selector = getCssSelector(this.options.css.infos), + infoItems = findFooterAndHeaderItems.call(this, selector); + + 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 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) { + 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 (count === 0) { + renderPaginationItem.call(this, pagination, 1, 1, "page-" + 1) + ._bgEnableAria(false)._bgSelectAria(); + } + + renderPaginationItem.call(this, pagination, "next", ">", "next") + ._bgEnableAria(totalPages > current); + renderPaginationItem.call(this, pagination, "last", "»", "last") + ._bgEnableAria(totalPages > current); + + replacePlaceHolder.call(this, paginationItems, pagination); + } + } +} + +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) { + 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; +} + +function renderRowCountSelection(actions) { + var that = this, + rowCountList = this.options.rowCount; + + function getText(value) { + return (value === -1) ? that.options.labels.all : value; + } + + if ($.isArray(rowCountList)) { + 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); + + $.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) { + 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() { + 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) { + 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 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) { + rowCss += status; + } + + $.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 + ";" + })); + } + }); + + if (rowCss.length > 0) { + rowAttr += " class=\"" + rowCss + "\""; + } + 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); + + tbody.html(html); + + registerRowEvents.call(this, tbody); + } else { + renderNoResultsRow.call(this); + } +} + +function registerRowEvents(tbody) { + 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()); + + if ($this.prop("checked")) { + that.select([id]); + } else { + that.deselect([id]); + } + }); + } + + tbody.off("click" + namespace, "> tr") + .on("click" + namespace, "> tr", 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; + }); + + 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) { + 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 !== "")) { + currentValue = newValue; + if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) { + window.clearTimeout(timer); + timer = window.setTimeout(function() { + executeSearch.call(that, newValue); + }, that.options.searchSettings.delay); + } + } + }); + + replacePlaceHolder.call(this, searchItems, search); + } + } +} + +function executeSearch(phrase) { + if (this.searchPhrase !== phrase) { + this.current = 1; + this.searchPhrase = phrase; + loadData.call(this); + } +} + +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 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 + })); + } + + $.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); + + 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); + }); + } + + // 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(); + + if ($(this).prop("checked")) { + 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); + + 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 (this.options.multiSort) { + var newSort = {}; + for (var key in this.sortDictionary) { + if (key !== columnId) { + newSort[key] = this.sortDictionary[key]; + } + } + this.sortDictionary = newSort; + icon.removeClass(css.iconDown); + } else { + this.sortDictionary[columnId] = "asc"; + icon.removeClass(css.iconDown).addClass(css.iconUp); + } + } else { + this.sortDictionary[columnId] = "asc"; + icon.addClass(css.iconUp); + } +} + +function replacePlaceHolder(placeholder, element) { + placeholder.each(function(index, item) { + // todo: check how append is implemented. Perhaps cloning here is superfluous. + $(item).before(element.clone(true)).remove(); + }); +} + +function showLoading() { + var that = this; + + window.setTimeout(function() { + 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) { + 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"); + } + } + }, 250); +} + +function sortRows() { + var sortArray = []; + + function sort(x, y, current) { + current = current || 0; + var next = current + 1, + item = sortArray[current]; + + function sortOrder(value) { + return (item.order === "asc") ? value : value * -1; + } + + var a = that.options.caseSensitive ? x[item.id] : x[item.id].toLowerCase(); + var b = that.options.caseSensitive ? y[item.id] : y[item.id].toLowerCase(); + + return (a > b) ? sortOrder(1) : + (a < b) ? sortOrder(-1) : + (sortArray.length > next) ? sort(x, y, next) : 0; + } + + if (!this.options.ajax) { + var that = this; + + for (var key in this.sortDictionary) { + if (this.options.multiSort || sortArray.length === 0) { + sortArray.push({ + id: key, + order: this.sortDictionary[key] + }); + } + } + + if (sortArray.length > 0) { + this.rows.sort(sort); + } + } } - 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 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 })); - } - - $.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); - - 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); - }); - } - - // 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(); - - if ($(this).prop("checked")) - { - 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); - - 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 (this.options.multiSort) - { - var newSort = {}; - for (var key in this.sortDictionary) - { - if (key !== columnId) - { - newSort[key] = this.sortDictionary[key]; - } - } - this.sortDictionary = newSort; - icon.removeClass(css.iconDown); - } - else - { - this.sortDictionary[columnId] = "asc"; - icon.removeClass(css.iconDown).addClass(css.iconUp); - } - } - else - { - this.sortDictionary[columnId] = "asc"; - icon.addClass(css.iconUp); - } - } - - function replacePlaceHolder(placeholder, element) - { - placeholder.each(function (index, item) - { - // todo: check how append is implemented. Perhaps cloning here is superfluous. - $(item).before(element.clone(true)).remove(); - }); - } - - function showLoading() - { - var that = this; - - window.setTimeout(function() - { - 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) - { - 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"); - } - } - }, 250); - } - - function sortRows() - { - var sortArray = []; - - function sort(x, y, current) - { - current = current || 0; - var next = current + 1, - item = sortArray[current]; - - 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; - } - - if (!this.options.ajax) - { - var that = this; - - for (var key in this.sortDictionary) - { - if (this.options.multiSort || sortArray.length === 0) - { - sortArray.push({ - id: key, - order: this.sortDictionary[key] - }); - } - } +// 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") - if (sortArray.length > 0) - { - this.rows.sort(sort); - } - } - } + /** + * 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, - // GRID PUBLIC CLASS DEFINITION - // ==================== + /** + * 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, /** - * Represents the jQuery Bootgrid plugin. + * Enables entire row click selection (`selection` must be set to `true` as well). 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 rowSelect + * @type Boolean + * @default false + * @for defaults + * @since 1.1.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; - - // todo: implement cache - }; + rowSelect: false, /** - * An object that represents the default settings. + * Defines whether the row selection is saved internally on filtering, paging and sorting + * (even if the selected rows are not visible). * - * @static - * @class defaults - * @for Grid - * @example - * // Global approach - * $.bootgrid.defaults.selection = true; - * @example - * // Initialization approach - * $("#bootgrid").bootgrid({ selection = true }); + * @property keepSelection + * @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") + keepSelection: 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, + 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 multi selection (`selection` must be set to `true` as well). Default value is `false`. + * The time in milliseconds to wait before search gets executed. * - * @property multiSelect - * @type Boolean - * @default false - * @for defaults - * @since 1.0.0 + * @property delay + * @type Number + * @default 250 + * @for searchSettings **/ - multiSelect: false, - + delay: 250, + /** - * Enables entire row click selection (`selection` must be set to `true` as well). Default value is `false`. + * The characters to type before the search gets executed. * - * @property rowSelect - * @type Boolean - * @default false - * @for defaults - * @since 1.1.0 + * @property characters + * @type Number + * @default 1 + * @for searchSettings **/ - rowSelect: false, + characters: 1 + }, + /** + * 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: { /** - * Defines whether the row selection is saved internally on filtering, paging and sorting - * (even if the selected rows are not visible). + * 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 keepSelection - * @type Boolean - * @default false - * @for defaults - * @since 1.1.0 + * @property method + * @type String + * @default "POST" + * @for ajaxSettings **/ - keepSelection: false, + method: "POST" + }, - highlightRows: false, // highlights new rows (find the page of the first new row) - sorting: true, - multiSort: 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") - /** - * General search settings to configure the search field behaviour. - * - * @property searchSettings - * @type Object - * @for defaults - * @since 1.2.0 - **/ - 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 - }, + /** + * 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 whether the data shall be loaded via an asynchronous HTTP (Ajax) request. - * - * @property ajax - * @type Boolean - * @default false - * @for defaults - **/ - ajax: false, + /** + * Defines whether the search is case sensitive or insensitive. + * + * @property caseSensitive + * @type Boolean + * @default true + * @for defaults + * @since 1.1.0 + **/ + caseSensitive: true, - /** - * 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" - }, + // note: The following properties should not be used via data-api attributes - /** - * 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") + /** + * 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; }, - /** - * 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 ""; } + /** + * 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; }, - /** - * Defines whether the search is case sensitive or insensitive. - * - * @property caseSensitive - * @type Boolean - * @default true - * @for defaults - * @since 1.1.0 - **/ - caseSensitive: true, + /** + * 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: "", - actionDropDown: "
    ", - actionDropDownItem: "
  • {{ctx.text}}
  • ", - actionDropDownCheckboxItem: "
  • ", - actions: "
    ", - body: "", - cell: "{{ctx.content}}", - footer: "

    ", - header: "

    ", - headerCell: "{{ctx.column.text}}{{ctx.icon}}", - icon: "", - infos: "
    {{lbl.infos}}
    ", - loading: "{{lbl.loading}}", - noResults: "{{lbl.noResults}}", - pagination: "
      ", - paginationItem: "
    • {{ctx.text}}
    • ", - rawHeaderCell: "{{ctx.content}}", // Used for the multi select box - row: "{{ctx.cells}}", - search: "
      ", - select: "" - } - }; - - /** - * Appends rows. - * - * @method append - * @param rows {Array} An array of rows to append - * @chainable - **/ - Grid.prototype.append = function(rows) - { - if (this.options.ajax) - { - // todo: implement ajax PUT - } - else - { - var appendedRows = []; - for (var i = 0; i < rows.length; i++) - { - if (appendRow.call(this, rows[i])) - { - appendedRows.push(rows[i]); - } - } - sortRows.call(this); - highlightAppendedRows.call(this, appendedRows); - loadData.call(this); - this.element.trigger("appended" + namespace, [appendedRows]); - } - - return this; - }; + 3: "danger" + }, /** - * Removes all rows. + * Contains all templates. * - * @method clear - * @chainable + * @property templates + * @type Object + * @for defaults **/ - Grid.prototype.clear = function() + templates: { + actionButton: "", + actionDropDown: "
        ", + actionDropDownItem: "
      • {{ctx.text}}
      • ", + actionDropDownCheckboxItem: "
      • ", + actions: "
        ", + body: "", + cell: "{{ctx.content}}", + footer: "

        ", + header: "

        ", + headerCell: "{{ctx.column.text}}{{ctx.icon}}", + icon: "", + infos: "
        {{lbl.infos}}
        ", + loading: "{{lbl.loading}}", + noResults: "{{lbl.noResults}}", + pagination: "
          ", + paginationItem: "
        • {{ctx.text}}
        • ", + rawHeaderCell: "{{ctx.content}}", // Used for the multi select box + row: "{{ctx.cells}}", + search: "
          ", + select: "" + } +}; + +/** + * 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 POST - } - else - { - var removedRows = $.extend([], this.rows); - this.rows = []; - this.current = 1; - this.total = 0; - loadData.call(this); - this.element.trigger("cleared" + namespace, [removedRows]); - } - - return this; - }; - - /** - * Removes the control functionality completely and transforms the current state to the initial HTML structure. - * - * @method destroy - * @chainable - **/ - Grid.prototype.destroy = function() + // todo: implement ajax PUT + } + else { - // todo: this method has to be optimized (the complete initial state must be restored) - $(window).off(namespace); - if (this.options.navigation & 1) + var appendedRows = []; + for (var i = 0; i < rows.length; i++) { - this.header.remove(); - } - if (this.options.navigation & 2) - { - this.footer.remove(); + if (appendRow.call(this, rows[i])) + { + appendedRows.push(rows[i]); + } } - this.element.before(this.origin).remove(); + sortRows.call(this); + highlightAppendedRows.call(this, appendedRows); + loadData.call(this); + this.element.trigger("appended" + namespace, [appendedRows]); + } - return this; - }; + return this; +}; - /** - * Resets the state and reloads rows. - * - * @method reload - * @chainable - **/ - Grid.prototype.reload = function() +/** + * Removes all rows. + * + * @method clear + * @chainable + **/ +Grid.prototype.clear = function() +{ + if (this.options.ajax) + { + // 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) + { + 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) { - if (this.identifier != null) + var that = this; + + if (this.options.ajax) + { + // todo: implement ajax DELETE + } + else { - var that = this; + 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 - // ============ - - $.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"); - }, + return (select == null || select) ? + this.addClass("active")._bgAria("selected", "true") : + this.removeClass("active")._bgAria("selected", "false"); + }, - _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") - { - return; - } - if (!instance) + 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) { - $this.data(namespace, (instance = new Grid(this, options))); - init.call(instance); + returnValue = instance[option].apply(instance, args); } - if (typeof option === "string") + else if (option.indexOf("get") !== 0) { - 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 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 deleted file mode 100644 index 358c1ac..0000000 --- a/dist/jquery.bootgrid.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * jQuery Bootgrid v1.3.1 - 09/11/2015 - * Copyright (c) 2014-2015 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 deleted file mode 100644 index b84a714..0000000 --- a/dist/jquery.bootgrid.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * jQuery Bootgrid v1.3.1 - 09/11/2015 - * Copyright (c) 2014-2015 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-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]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:'',actionDropDown:'
          ',actionDropDownItem:'
        • {{ctx.text}}
        • ',actionDropDownCheckboxItem:'
        • ',actions:'
          ',body:"",cell:'{{ctx.content}}',footer:'

          ',header:'

          ',headerCell:'{{ctx.column.text}}{{ctx.icon}}',icon:'',infos:'
          {{lbl.infos}}
          ',loading:'{{lbl.loading}}',noResults:'{{lbl.noResults}}',pagination:'
            ',paginationItem:'
          • {{ctx.text}}
          • ',rawHeaderCell:'{{ctx.content}}',row:"{{ctx.cells}}",search:'
            ',select:''}},I.prototype.append=function(a){if(this.options.ajax);else{for(var b=[],c=0;c0&&(this.options.multiSelect||1!==e.length);)if(c=b.pop(),-1===a.inArray(c,this.selectedRows))for(d=0;d0){var f=h(this.options.css.selectBox),g=this.selectedRows.length>=this.currentRows.length;for(d=0;!this.options.keepSelection&&g&&d tr "+f+":checked").trigger("click"+H),d=0;d 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;d0){var g=h(this.options.css.selectBox);for(this.element.find("thead "+g).prop("checked",!1),d=0;d 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;bc?this.length>d?this.slice(c,d):this.slice(c):[]}),Array.prototype.where||(Array.prototype.where=function(a){for(var b=[],c=0;c=1.9.0", - "bootstrap": ">=3.1.1" - }, - "devDependencies": { - "grunt": "~0.4.5", - "grunt-cli": "~0.1.13", - "grunt-contrib-less": "~0.11.0", - "grunt-contrib-csslint": "~0.3.1", - "grunt-contrib-cssmin": "~0.10.0", - "grunt-contrib-qunit": "~0.4.0", - "grunt-contrib-jshint": "~0.10.0", - "grunt-contrib-uglify": "~0.9.2", - "grunt-contrib-concat": "~0.4.0", - "grunt-contrib-yuidoc": "~0.5.2", - "grunt-contrib-clean": "~0.5.0", - "grunt-contrib-compress": "~0.8.0", - "grunt-nuget": "~0.1.4", - "grunt-regex-replace": "~0.2.6", - "grunt-exec": "~0.4.5" - }, - "readmeFilename": "README.md" -} \ No newline at end of file + "name": "jquery-bootgrid", + "namespace": "jquery.bootgrid", + "title": "jQuery Bootgrid", + "version": "1.3.1", + "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", + "homepage": "http://www.jquery-bootgrid.com", + "authors": [{ + "name": "Rafael Staib", + "email": "me@rafaelstaib.com", + "url": "http://www.rafaelstaib.com" + }, { + "name": "Matt Groth", + "email": "mattgroth85@gmail.com", + "url": "https://github.com/Iodine-" + }], + "bugs": "https://github.com/MoveInc/jquery-bootgrid/issues", + "scripts": { + "test": "grunt" + }, + "repository": { + "type": "git", + "url": "https://github.com/MoveInc/jquery-bootgrid.git" + }, + "keywords": [ + "jquery-plugin", + "jQuery", + "Bootstrap", + "Plugin", + "Component", + "Control", + "UI", + "Grid", + "Table", + "Data", + "Sorting", + "Filtering", + "HTML5", + "Accessibility" + ], + "licenses": [{ + "type": "MIT", + "url": "http://www.opensource.org/licenses/MIT" + }], + "dependencies": { + "jquery": ">=1.9.0", + "bootstrap": ">=3.1.1" + }, + "devDependencies": { + "grunt": "^1.0.1", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-compress": "^1.3.0", + "grunt-contrib-concat": "^1.0.1", + "grunt-contrib-csslint": "^1.0.0", + "grunt-contrib-cssmin": "^1.0.1", + "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-less": "^1.3.0", + "grunt-contrib-qunit": "^1.2.0", + "grunt-contrib-uglify": "^2.0.0", + "grunt-contrib-yuidoc": "^1.0.0", + "grunt-exec": "^1.0.0", + "grunt-nuget": "~0.1.4", + "grunt-regex-replace": "^0.3.0" + }, + "readmeFilename": "README.md" +} diff --git a/src/internal.js b/src/internal.js index 9b9bba4..ca8b540 100644 --- a/src/internal.js +++ b/src/internal.js @@ -6,140 +6,125 @@ var namespace = ".rs.jquery.bootgrid"; // 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 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 findFooterAndHeaderItems(selector) -{ - var footer = (this.footer) ? this.footer.find(selector) : $(), - header = (this.header) ? this.header.find(selector) : $(); - return $.merge(footer, header); +function findFooterAndHeaderItems(selector) { + var footer = (this.footer) ? this.footer.find(selector) : $(), + header = (this.header) ? this.header.find(selector) : $(); + return $.merge(footer, header); } -function getParams(context) -{ - return (context) ? $.extend({}, this.cachedParams, { ctx: context }) : - this.cachedParams; +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 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 getCssSelector(css) { + return "." + $.trim(css).replace(/\s+/gm, "."); } -function getUrl() -{ - var url = this.options.url; - return ($.isFunction(url)) ? url() : url; +function getUrl() { + var url = this.options.url; + return ($.isFunction(url)) ? url() : url; } -function init() -{ - this.element.trigger("initialize" + namespace); +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); + 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); - this.element.trigger("initialized" + namespace); + this.element.trigger("initialized" + namespace); } -function highlightAppendedRows(rows) -{ - if (this.options.highlightRows) - { - // todo: implement - } +function highlightAppendedRows(rows) { + if (this.options.highlightRows) { + // todo: implement + } } -function isVisible(column) -{ - return column.visible; +function isVisible(column) { + return column.visible; } -function loadColumns() -{ - var that = this, - firstHeadRow = this.element.find("thead > tr").first(), - sorted = false; - - /*jshint -W018*/ - firstHeadRow.children().each(function () - { - 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; - } - - // Prevents multiple identifiers - if (column.identifier) - { - that.identifier = column.id; - that.converter = column.converter; - } - - // 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 loadColumns() { + var that = this, + firstHeadRow = this.element.find("thead > tr").first(), + sorted = false; + + /*jshint -W018*/ + firstHeadRow.children().each(function() { + 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; + } + + // Prevents multiple identifiers + if (column.identifier) { + that.identifier = column.id; + that.converter = column.converter; + } + + // 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*/ } /* @@ -152,797 +137,727 @@ response = { } */ -function loadData() -{ - var that = this; - - this.element._bgBusyAria(true).trigger("load" + namespace); - showLoading.call(this); - - function containsPhrase(row) - { - 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 && - column.converter.to(row[column.id]).search(searchPattern) > -1) - { - return true; - } - } - - return false; - } - - function update(rows, total) - { - that.currentRows = rows; - setTotals.call(that, total); - - if (!that.options.keepSelection) - { - that.selectedRows = []; - } - - renderRows.call(that, rows); - renderInfos.call(that); - renderPagination.call(that); - - that.element._bgBusyAria(false).trigger("loaded" + namespace); - } - - if (this.options.ajax) - { - var request = getRequest.call(this), - url = getUrl.call(this); - - 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(); - } - - var settings = { - url: url, - data: request, - success: function(response) - { - that.xqr = null; - - if (typeof (response) === "string") - { - response = $.parseJSON(response); - } - - response = that.options.responseHandler(response); - - that.current = response.current; - update(response.rows, response.total); - }, - error: function (jqXHR, textStatus, errorThrown) - { - that.xqr = null; - - if (textStatus !== "abort") - { - 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); - } - - // todo: improve the following comment - // setTimeout decouples the initialization so that adding event handlers happens before - window.setTimeout(function () { update(rows, total); }, 10); - } +function loadData() { + var that = this; + + this.element._bgBusyAria(true).trigger("load" + namespace); + showLoading.call(this); + + function containsPhrase(row) { + 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 && + column.converter.to(row[column.id]).search(searchPattern) > -1) { + return true; + } + } + + return false; + } + + function update(rows, total) { + that.currentRows = rows; + setTotals.call(that, total); + + if (!that.options.keepSelection) { + that.selectedRows = []; + } + + renderRows.call(that, rows); + renderInfos.call(that); + renderPagination.call(that); + + that.element._bgBusyAria(false).trigger("loaded" + namespace); + } + + if (this.options.ajax) { + var request = getRequest.call(this), + url = getUrl.call(this); + + 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(); + } + + var settings = { + url: url, + data: request, + success: function(response) { + that.xqr = null; + + if (typeof(response) === "string") { + response = $.parseJSON(response); + } + + response = that.options.responseHandler(response); + + that.current = response.current; + update(response.rows, response.total); + }, + error: function(jqXHR, textStatus, errorThrown) { + that.xqr = null; + + if (textStatus !== "abort") { + 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); + } + + // 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() -{ - if (!this.options.ajax) - { - var that = this, - rows = this.element.find("tbody > tr"); - - rows.each(function () - { - var $this = $(this), - cells = $this.children("td"), - row = {}; - - $.each(that.columns, function (i, column) - { - row[column.id] = column.converter.from(cells.eq(i).text()); - }); - - appendRow.call(that, row); - }); - - setTotals.call(this, this.rows.length); - sortRows.call(this); - } +function loadRows() { + if (!this.options.ajax) { + var that = this, + rows = this.element.find("tbody > tr"); + + rows.each(function() { + var $this = $(this), + cells = $this.children("td"), + row = {}; + + $.each(that.columns, function(i, column) { + row[column.id] = column.converter.from(cells.eq(i).text()); + }); + + appendRow.call(that, row); + }); + + 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 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); - } - - if (this.options.navigation & 1) - { - this.header = $(tpl.header.resolve(getParams.call(this, { id: this.element._bgId() + "-header" }))); - wrapper.before(this.header); - } - - if (this.options.navigation & 2) - { - this.footer = $(tpl.footer.resolve(getParams.call(this, { id: this.element._bgId() + "-footer" }))); - wrapper.after(this.footer); - } +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); + } + + if (this.options.navigation & 1) { + this.header = $(tpl.header.resolve(getParams.call(this, { + id: this.element._bgId() + "-header" + }))); + wrapper.before(this.header); + } + + if (this.options.navigation & 2) { + this.footer = $(tpl.footer.resolve(getParams.call(this, { + id: this.element._bgId() + "-footer" + }))); + wrapper.after(this.footer); + } } -function renderActions() -{ - if (this.options.navigation !== 0) - { - var css = this.options.css, - selector = getCssSelector(css.actions), - actionItems = findFooterAndHeaderItems.call(this, selector); - - if (actionItems.length > 0) - { - 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) - { - // todo: prevent multiple fast clicks (fast click detection) - e.stopPropagation(); - that.current = 1; - loadData.call(that); - }); - actions.append(refresh); - } - - // Row count selection - renderRowCountSelection.call(this, actions); - - // Column selection - renderColumnSelection.call(this, actions); - - replacePlaceHolder.call(this, actionItems, actions); - } - } +function renderActions() { + if (this.options.navigation !== 0) { + var css = this.options.css, + selector = getCssSelector(css.actions), + actionItems = findFooterAndHeaderItems.call(this, selector); + + if (actionItems.length > 0) { + 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) { + // todo: prevent multiple fast clicks (fast click detection) + e.stopPropagation(); + that.current = 1; + loadData.call(that); + }); + actions.append(refresh); + } + + // Row count selection + renderRowCountSelection.call(this, actions); + + // Column selection + renderColumnSelection.call(this, actions); + + replacePlaceHolder.call(this, actionItems, actions); + } + } } -function renderColumnSelection(actions) -{ - 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); - - $.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); - } +function renderColumnSelection(actions) { + 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); + + $.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); + } } -function renderInfos() -{ - if (this.options.navigation !== 0) - { - var selector = getCssSelector(this.options.css.infos), - infoItems = findFooterAndHeaderItems.call(this, selector); - - 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 renderInfos() { + if (this.options.navigation !== 0) { + var selector = getCssSelector(this.options.css.infos), + infoItems = findFooterAndHeaderItems.call(this, selector); + + 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 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 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) - { - 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 (count === 0) - { - renderPaginationItem.call(this, pagination, 1, 1, "page-" + 1) - ._bgEnableAria(false)._bgSelectAria(); - } - - renderPaginationItem.call(this, pagination, "next", ">", "next") - ._bgEnableAria(totalPages > current); - renderPaginationItem.call(this, pagination, "last", "»", "last") - ._bgEnableAria(totalPages > current); - - replacePlaceHolder.call(this, paginationItems, pagination); - } - } +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) { + 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 (count === 0) { + renderPaginationItem.call(this, pagination, 1, 1, "page-" + 1) + ._bgEnableAria(false)._bgSelectAria(); + } + + renderPaginationItem.call(this, pagination, "next", ">", "next") + ._bgEnableAria(totalPages > current); + renderPaginationItem.call(this, pagination, "last", "»", "last") + ._bgEnableAria(totalPages > current); + + replacePlaceHolder.call(this, paginationItems, pagination); + } + } } -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) - { - 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; +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) { + 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; } -function renderRowCountSelection(actions) -{ - var that = this, - rowCountList = this.options.rowCount; - - function getText(value) - { - return (value === -1) ? that.options.labels.all : value; - } - - if ($.isArray(rowCountList)) - { - 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); - - $.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) - { - 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 () - { - 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 renderRowCountSelection(actions) { + var that = this, + rowCountList = this.options.rowCount; + + function getText(value) { + return (value === -1) ? that.options.labels.all : value; + } + + if ($.isArray(rowCountList)) { + 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); + + $.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) { + 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() { + 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) -{ - 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 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) - { - rowCss += status; - } - - $.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 + ";" })); - } - }); - - if (rowCss.length > 0) - { - rowAttr += " class=\"" + rowCss + "\""; - } - 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); - - tbody.html(html); - - registerRowEvents.call(this, tbody); - } - else - { - renderNoResultsRow.call(this); - } +function renderRows(rows) { + 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 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) { + rowCss += status; + } + + $.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 + ";" + })); + } + }); + + if (rowCss.length > 0) { + rowAttr += " class=\"" + rowCss + "\""; + } + 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); + + tbody.html(html); + + registerRowEvents.call(this, tbody); + } else { + renderNoResultsRow.call(this); + } } -function registerRowEvents(tbody) -{ - 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()); - - if ($this.prop("checked")) - { - that.select([id]); - } - else - { - that.deselect([id]); - } - }); - } - - tbody.off("click" + namespace, "> tr") - .on("click" + namespace, "> tr", 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; }); - - 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 registerRowEvents(tbody) { + 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()); + + if ($this.prop("checked")) { + that.select([id]); + } else { + that.deselect([id]); + } + }); + } + + tbody.off("click" + namespace, "> tr") + .on("click" + namespace, "> tr", 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; + }); + + 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) - { - 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 !== "")) - { - currentValue = newValue; - if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) - { - window.clearTimeout(timer); - timer = window.setTimeout(function () - { - executeSearch.call(that, newValue); - }, that.options.searchSettings.delay); - } - } - }); - - replacePlaceHolder.call(this, searchItems, search); - } - } +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) { + 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 !== "")) { + currentValue = newValue; + if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) { + window.clearTimeout(timer); + timer = window.setTimeout(function() { + executeSearch.call(that, newValue); + }, that.options.searchSettings.delay); + } + } + }); + + replacePlaceHolder.call(this, searchItems, search); + } + } } -function executeSearch(phrase) -{ - if (this.searchPhrase !== phrase) - { - this.current = 1; - this.searchPhrase = phrase; - loadData.call(this); - } +function executeSearch(phrase) { + if (this.searchPhrase !== phrase) { + this.current = 1; + this.searchPhrase = phrase; + loadData.call(this); + } } -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 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 })); - } - - $.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); - - 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); - }); - } - - // 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(); - - if ($(this).prop("checked")) - { - that.select(); - } - else - { - that.deselect(); - } - }); - } +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 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 + })); + } + + $.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); + + 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); + }); + } + + // 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(); + + if ($(this).prop("checked")) { + 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); - - 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 (this.options.multiSort) - { - var newSort = {}; - for (var key in this.sortDictionary) - { - if (key !== columnId) - { - newSort[key] = this.sortDictionary[key]; - } - } - this.sortDictionary = newSort; - icon.removeClass(css.iconDown); - } - else - { - this.sortDictionary[columnId] = "asc"; - icon.removeClass(css.iconDown).addClass(css.iconUp); - } - } - else - { - this.sortDictionary[columnId] = "asc"; - icon.addClass(css.iconUp); - } +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 (sortOrder && sortOrder === "asc") { + this.sortDictionary[columnId] = "desc"; + icon.removeClass(css.iconUp).addClass(css.iconDown); + } else if (sortOrder && sortOrder === "desc") { + if (this.options.multiSort) { + var newSort = {}; + for (var key in this.sortDictionary) { + if (key !== columnId) { + newSort[key] = this.sortDictionary[key]; + } + } + this.sortDictionary = newSort; + icon.removeClass(css.iconDown); + } else { + this.sortDictionary[columnId] = "asc"; + icon.removeClass(css.iconDown).addClass(css.iconUp); + } + } else { + this.sortDictionary[columnId] = "asc"; + icon.addClass(css.iconUp); + } } -function replacePlaceHolder(placeholder, element) -{ - placeholder.each(function (index, item) - { - // todo: check how append is implemented. Perhaps cloning here is superfluous. - $(item).before(element.clone(true)).remove(); - }); +function replacePlaceHolder(placeholder, element) { + placeholder.each(function(index, item) { + // todo: check how append is implemented. Perhaps cloning here is superfluous. + $(item).before(element.clone(true)).remove(); + }); } -function showLoading() -{ - var that = this; - - window.setTimeout(function() - { - 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) - { - 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"); - } - } - }, 250); +function showLoading() { + var that = this; + + window.setTimeout(function() { + 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) { + 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"); + } + } + }, 250); } -function sortRows() -{ - var sortArray = []; - - function sort(x, y, current) - { - current = current || 0; - var next = current + 1, - item = sortArray[current]; - - 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; - } - - if (!this.options.ajax) - { - var that = this; - - for (var key in this.sortDictionary) - { - if (this.options.multiSort || sortArray.length === 0) - { - sortArray.push({ - id: key, - order: this.sortDictionary[key] - }); - } - } - - if (sortArray.length > 0) - { - this.rows.sort(sort); - } - } -} \ No newline at end of file +function sortRows() { + var sortArray = []; + + function sort(x, y, current) { + current = current || 0; + var next = current + 1, + item = sortArray[current]; + + function sortOrder(value) { + return (item.order === "asc") ? value : value * -1; + } + + var a = that.options.caseSensitive ? x[item.id] : x[item.id].toLowerCase(); + var b = that.options.caseSensitive ? y[item.id] : y[item.id].toLowerCase(); + + return (a > b) ? sortOrder(1) : + (a < b) ? sortOrder(-1) : + (sortArray.length > next) ? sort(x, y, next) : 0; + } + + if (!this.options.ajax) { + var that = this; + + for (var key in this.sortDictionary) { + if (this.options.multiSort || sortArray.length === 0) { + sortArray.push({ + id: key, + order: this.sortDictionary[key] + }); + } + } + + if (sortArray.length > 0) { + this.rows.sort(sort); + } + } +} From bd40492aed0df7f7d0181035cf6f2538a1e812e1 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Thu, 4 Aug 2016 15:48:31 -0700 Subject: [PATCH 02/30] Updated caseSensitive function description --- CHANGELOG.md | 2 +- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 12 ++++++------ package.json | 5 +++-- src/public.js | 12 ++++++------ 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3c5e48..094b6c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,4 +102,4 @@ There are no breaking changes but some HTML templates changed during development ## 0.9.7 ### Bug Fixes -- Fixed a column header visualization bug regarding sorting \ No newline at end of file +- Fixed a column header visualization bug regarding sorting diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 0c0687c..519e92e 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.1 - 08/03/2016 + * jQuery Bootgrid v1.3.1 - 08/04/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 372efb9..17ec716 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.1 - 08/03/2016 + * jQuery Bootgrid v1.3.1 - 08/04/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 4346cf1..06fb896 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.1 - 08/03/2016 + * jQuery Bootgrid v1.3.1 - 08/04/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -1002,7 +1002,7 @@ Grid.defaults = { * @for searchSettings **/ delay: 250, - + /** * The characters to type before the search gets executed. * @@ -1072,7 +1072,7 @@ Grid.defaults = { url: "", // or use function () { return ""; } /** - * Defines whether the search is case sensitive or insensitive. + * Defines whether the search and sorting is case sensitive. * * @property caseSensitive * @type Boolean @@ -1250,7 +1250,7 @@ Grid.defaults = { * @for statusMapping **/ 2: "warning", - + /** * Specifies a dangerous or potentially negative action. * @@ -1434,7 +1434,7 @@ Grid.prototype.remove = function(rowIds) }; /** - * Searches in all rows for a specific phrase (but only in visible cells). + * 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 @@ -1579,7 +1579,7 @@ Grid.prototype.deselect = function(rowIds) }; /** - * Sorts the rows by a given sort descriptor dictionary. + * Sorts the rows by a given sort descriptor dictionary. * The sort filter will be reseted, if no argument is provided. * * @method sort diff --git a/package.json b/package.json index 4596337..a34c5c8 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,12 @@ "version": "1.3.1", "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", "homepage": "http://www.jquery-bootgrid.com", - "authors": [{ + "author": { "name": "Rafael Staib", "email": "me@rafaelstaib.com", "url": "http://www.rafaelstaib.com" - }, { + }, + "contributors": [{ "name": "Matt Groth", "email": "mattgroth85@gmail.com", "url": "https://github.com/Iodine-" diff --git a/src/public.js b/src/public.js index 16c5c44..c387958 100644 --- a/src/public.js +++ b/src/public.js @@ -128,7 +128,7 @@ Grid.defaults = { * @for searchSettings **/ delay: 250, - + /** * The characters to type before the search gets executed. * @@ -198,7 +198,7 @@ Grid.defaults = { url: "", // or use function () { return ""; } /** - * Defines whether the search is case sensitive or insensitive. + * Defines whether the search and sorting is case sensitive. * * @property caseSensitive * @type Boolean @@ -376,7 +376,7 @@ Grid.defaults = { * @for statusMapping **/ 2: "warning", - + /** * Specifies a dangerous or potentially negative action. * @@ -560,7 +560,7 @@ Grid.prototype.remove = function(rowIds) }; /** - * Searches in all rows for a specific phrase (but only in visible cells). + * 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 @@ -705,7 +705,7 @@ Grid.prototype.deselect = function(rowIds) }; /** - * Sorts the rows by a given sort descriptor dictionary. + * Sorts the rows by a given sort descriptor dictionary. * The sort filter will be reseted, if no argument is provided. * * @method sort @@ -853,4 +853,4 @@ Grid.prototype.getTotalPageCount = function() Grid.prototype.getTotalRowCount = function() { return this.total; -}; \ No newline at end of file +}; From e13b1db7e1318e52cec0b8622e061c563913daed Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Thu, 4 Aug 2016 15:50:23 -0700 Subject: [PATCH 03/30] Increment patch --- CHANGELOG.md | 5 +++ bower.json | 104 +++++++++++++++++++++++++-------------------------- package.json | 2 +- 3 files changed, 57 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 094b6c0..45b8b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.3.2 + +### Enhancements & Features +- Sorting columns now respects the `caseSensitive` flag. + ## 1.3.1 ### Enhancements & Features diff --git a/bower.json b/bower.json index 856e916..589e58a 100644 --- a/bower.json +++ b/bower.json @@ -1,54 +1,52 @@ { - "name": "jquery.bootgrid", - "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", - "keywords": [ - "grid", - "table", - "data", - "sorting", - "filtering", - "UI", - "component", - "HTML5", - "accessibility", - "bootstrap" - ], - "version": "1.3.1", - "authors": [ - { - "name": "Rafael Staib", - "email": "me@rafaelstaib.com", - "url": "http://www.rafaelstaib.com" - } - ], - "homepage": "http://www.jquery-bootgrid.com", - "repository": { - "type": "git", - "url": "git://github.com/rstaib/jquery-bootgrid.git" - }, - "license": "MIT", - "main": [ - "dist/jquery.bootgrid.js", - "dist/jquery.bootgrid.css" - ], - "ignore": [ - "build", - "demo", - "dist/*.nupkg", - "dist/*.zip", - "docs", - "lib", - "test", - "*.nuspec", - ".gitattributes", - ".gitignore", - ".travis.yml", - "bootgrid.jquery.json", - "Gruntfile.js", - "package.json" - ], - "dependencies": { - "jquery": ">=1.9.0", - "bootstrap": ">=3.1.1" - } -} \ No newline at end of file + "name": "jquery.bootgrid", + "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", + "keywords": [ + "grid", + "table", + "data", + "sorting", + "filtering", + "UI", + "component", + "HTML5", + "accessibility", + "bootstrap" + ], + "version": "1.3.2", + "authors": [{ + "name": "Rafael Staib", + "email": "me@rafaelstaib.com", + "url": "http://www.rafaelstaib.com" + }], + "homepage": "http://www.jquery-bootgrid.com", + "repository": { + "type": "git", + "url": "git://github.com/rstaib/jquery-bootgrid.git" + }, + "license": "MIT", + "main": [ + "dist/jquery.bootgrid.js", + "dist/jquery.bootgrid.css" + ], + "ignore": [ + "build", + "demo", + "dist/*.nupkg", + "dist/*.zip", + "docs", + "lib", + "test", + "*.nuspec", + ".gitattributes", + ".gitignore", + ".travis.yml", + "bootgrid.jquery.json", + "Gruntfile.js", + "package.json" + ], + "dependencies": { + "jquery": ">=1.9.0", + "bootstrap": ">=3.1.1" + } +} diff --git a/package.json b/package.json index a34c5c8..3ab0d86 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "jquery-bootgrid", "namespace": "jquery.bootgrid", "title": "jQuery Bootgrid", - "version": "1.3.1", + "version": "1.3.2", "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", "homepage": "http://www.jquery-bootgrid.com", "author": { From 40186cb55623ddb33555a9094537638927ba6700 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Tue, 9 Aug 2016 13:21:30 -0700 Subject: [PATCH 04/30] Added new events and minor fixes/improvements --- CHANGELOG.md | 11 ++++++++++ bower.json | 2 +- demo/index.htm | 8 +++---- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 44 +++++++++++++++++++++++++++++++++----- package.json | 2 +- src/internal.js | 14 ++++++++++-- src/public.js | 28 ++++++++++++++++++++++-- 9 files changed, 96 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45b8b61..6976459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 1.4.0 + +### Enhancements & Features +- Added 3 new events + - **search** Fired when a search is executed + - **changePage** Fired when pagination is clicked + - **toggleColumn** Fired when the checkbox is clicked to show/hide a column + - **changeRowCount** Fired when changing the row count +- Added function that resolves the relative page the table show display after changing row count. +- Added option to disable the feature to resolve relative page after changing row count. `resolvePageFromRowCount: {boolean} [true]` + ## 1.3.2 ### Enhancements & Features diff --git a/bower.json b/bower.json index 589e58a..d280d28 100644 --- a/bower.json +++ b/bower.json @@ -13,7 +13,7 @@ "accessibility", "bootstrap" ], - "version": "1.3.2", + "version": "1.4.0", "authors": [{ "name": "Rafael Staib", "email": "me@rafaelstaib.com", diff --git a/demo/index.htm b/demo/index.htm index 5572e37..be96906 100644 --- a/demo/index.htm +++ b/demo/index.htm @@ -249,7 +249,7 @@ 18 - Riley Vance + 2 porttitor.interdum@pharetra.edu 19.06.16 Elit Nulla Facilisi Inc. @@ -258,7 +258,7 @@ 19 - Neve Owen + 11 nunc.sed.pede@lectusrutrum.edu 15.11.15 Leo Morbi Incorporated @@ -267,7 +267,7 @@ 20 - Fredericka Turner + 01 nulla.magna.malesuada@musProin.edu 30.01.17 Ac Urna Corp. @@ -479,7 +479,7 @@ } }, caseSensitive: false, - rowCount: [-1, 10, 50, 75] + rowCount: [-1, 5, 10, 15] }); } diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 519e92e..02512a6 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.1 - 08/04/2016 + * jQuery Bootgrid v1.3.2 - 08/09/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 17ec716..66e7583 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.1 - 08/04/2016 + * jQuery Bootgrid v1.3.2 - 08/09/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 06fb896..ee50132 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.1 - 08/04/2016 + * jQuery Bootgrid v1.3.2 - 08/09/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -362,6 +362,7 @@ function renderColumnSelection(actions) { checkbox = $this.find(checkboxSelector); if (!checkbox.prop("disabled")) { column.visible = checkbox.prop("checked"); + that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); var enable = that.columns.where(isVisible).length > 1; $this.parents(itemsSelector).find(selector + ":has(" + checkboxSelector + ":checked)") ._bgEnableAria(enable).find(checkboxSelector)._bgEnableField(enable); @@ -478,6 +479,7 @@ function renderPaginationItem(list, page, text, markerCss) { }; var command = $this.data("page"); that.current = commandList[command] || command; + that.element.trigger('changePage', [command.toString(), that.current]); loadData.call(that); } $this.trigger("blur"); @@ -518,8 +520,14 @@ function renderRowCountSelection(actions) { 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 + if(that.options.resolvePageFromRowCount){ + var page = that.current > 1 ? that.current : 1; + var skip = that.current > 1 ? that.rowCount * (that.current-1) + 1 : 0; + var newPage = skip > 1 ? Math.ceil(skip/newRowCount) : 1; + that.current = newRowCount > 0 ? newPage : 1; + }else{ + that.current = 1; + } that.rowCount = newRowCount; $this.parents(menuItemsSelector).children().each(function() { var $item = $(this), @@ -527,6 +535,7 @@ function renderRowCountSelection(actions) { $item._bgSelectAria(currentRowCount === newRowCount); }); $this.parents(menuSelector).find(menuTextSelector).text(getText(newRowCount)); + that.element.trigger('changeRowCount' + namespace, [newRowCount]); loadData.call(that); } }); @@ -692,6 +701,7 @@ function executeSearch(phrase) { if (this.searchPhrase !== phrase) { this.current = 1; this.searchPhrase = phrase; + this.element.trigger("search" + namespace, [this.searchPhrase]); loadData.call(this); } } @@ -935,6 +945,17 @@ Grid.defaults = { columnSelection: true, rowCount: [10, 25, 50, -1], // rows per page int or array of int (-1 represents "All") + /** + * Resolves the correct page number after changing the row count so that the top most row will remain in the table + * + * @property resolvePageFromRowCount + * @type Boolean + * @default true + * @for defaults + * @since 1.4.0 + **/ + resolvePageFromRowCount: true, + /** * Enables row selection (to enable multi selection see also `multiSelect`). Default value is `false`. * @@ -1609,12 +1630,25 @@ Grid.prototype.sort = function(dictionary) * Therefore be sure that only one grid instance is catched by your selector. * * @method getColumnSettings + * @param {Object} filter object to filter return array with * @return {Array} Returns a list of the column settings. * @since 1.2.0 + * @version 1.4.0 **/ -Grid.prototype.getColumnSettings = function() +Grid.prototype.getColumnSettings = function(filter) { - return $.merge([], this.columns); + var res = this.columns; + if(filter && typeof filter === 'object'){ + res = this.columns.filter(function(el){ + for (var key in filter) { + if(el[key] !== filter[key]){ + return false; + } + } + return true; + }); + } + return $.merge([], res); }; /** diff --git a/package.json b/package.json index 3ab0d86..da3f1c5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "jquery-bootgrid", "namespace": "jquery.bootgrid", "title": "jQuery Bootgrid", - "version": "1.3.2", + "version": "1.4.0", "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", "homepage": "http://www.jquery-bootgrid.com", "author": { diff --git a/src/internal.js b/src/internal.js index ca8b540..7236617 100644 --- a/src/internal.js +++ b/src/internal.js @@ -352,6 +352,7 @@ function renderColumnSelection(actions) { checkbox = $this.find(checkboxSelector); if (!checkbox.prop("disabled")) { column.visible = checkbox.prop("checked"); + that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); var enable = that.columns.where(isVisible).length > 1; $this.parents(itemsSelector).find(selector + ":has(" + checkboxSelector + ":checked)") ._bgEnableAria(enable).find(checkboxSelector)._bgEnableField(enable); @@ -468,6 +469,7 @@ function renderPaginationItem(list, page, text, markerCss) { }; var command = $this.data("page"); that.current = commandList[command] || command; + that.element.trigger('changePage', [command.toString(), that.current]); loadData.call(that); } $this.trigger("blur"); @@ -508,8 +510,14 @@ function renderRowCountSelection(actions) { 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 + if(that.options.resolvePageFromRowCount){ + var page = that.current > 1 ? that.current : 1; + var skip = that.current > 1 ? that.rowCount * (that.current-1) + 1 : 0; + var newPage = skip > 1 ? Math.ceil(skip/newRowCount) : 1; + that.current = newRowCount > 0 ? newPage : 1; + }else{ + that.current = 1; + } that.rowCount = newRowCount; $this.parents(menuItemsSelector).children().each(function() { var $item = $(this), @@ -517,6 +525,7 @@ function renderRowCountSelection(actions) { $item._bgSelectAria(currentRowCount === newRowCount); }); $this.parents(menuSelector).find(menuTextSelector).text(getText(newRowCount)); + that.element.trigger('changeRowCount' + namespace, [newRowCount]); loadData.call(that); } }); @@ -682,6 +691,7 @@ function executeSearch(phrase) { if (this.searchPhrase !== phrase) { this.current = 1; this.searchPhrase = phrase; + this.element.trigger("search" + namespace, [this.searchPhrase]); loadData.call(this); } } diff --git a/src/public.js b/src/public.js index c387958..a691dec 100644 --- a/src/public.js +++ b/src/public.js @@ -61,6 +61,17 @@ Grid.defaults = { columnSelection: true, rowCount: [10, 25, 50, -1], // rows per page int or array of int (-1 represents "All") + /** + * Resolves the correct page number after changing the row count so that the top most row will remain in the table + * + * @property resolvePageFromRowCount + * @type Boolean + * @default true + * @for defaults + * @since 1.4.0 + **/ + resolvePageFromRowCount: true, + /** * Enables row selection (to enable multi selection see also `multiSelect`). Default value is `false`. * @@ -735,12 +746,25 @@ Grid.prototype.sort = function(dictionary) * Therefore be sure that only one grid instance is catched by your selector. * * @method getColumnSettings + * @param {Object} filter object to filter return array with * @return {Array} Returns a list of the column settings. * @since 1.2.0 + * @version 1.4.0 **/ -Grid.prototype.getColumnSettings = function() +Grid.prototype.getColumnSettings = function(filter) { - return $.merge([], this.columns); + var res = this.columns; + if(filter && typeof filter === 'object'){ + res = this.columns.filter(function(el){ + for (var key in filter) { + if(el[key] !== filter[key]){ + return false; + } + } + return true; + }); + } + return $.merge([], res); }; /** From 6b5d5b56e307b55e162282c320d2e2a82832b040 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Tue, 9 Aug 2016 13:27:22 -0700 Subject: [PATCH 05/30] Fix changelog typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6976459..f4eebda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 1.4.0 ### Enhancements & Features -- Added 3 new events +- Added 4 new events - **search** Fired when a search is executed - **changePage** Fired when pagination is clicked - **toggleColumn** Fired when the checkbox is clicked to show/hide a column From 4f433cdfcc76c12c670c0e7f3e83637c04c819a2 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Wed, 10 Aug 2016 11:04:31 -0700 Subject: [PATCH 06/30] Fix issue with toggleColumn event --- CHANGELOG.md | 5 ++ bower.json | 104 +++++++++++++++++++------------------ dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 9 ++-- package.json | 2 +- src/internal.js | 7 ++- 7 files changed, 72 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4eebda..34dc82b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.4.1 + +### Bug Fixes +- Fixed toggleColumn event being fired twice in some cases + ## 1.4.0 ### Enhancements & Features diff --git a/bower.json b/bower.json index d280d28..600d5f1 100644 --- a/bower.json +++ b/bower.json @@ -1,52 +1,54 @@ { - "name": "jquery.bootgrid", - "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", - "keywords": [ - "grid", - "table", - "data", - "sorting", - "filtering", - "UI", - "component", - "HTML5", - "accessibility", - "bootstrap" - ], - "version": "1.4.0", - "authors": [{ - "name": "Rafael Staib", - "email": "me@rafaelstaib.com", - "url": "http://www.rafaelstaib.com" - }], - "homepage": "http://www.jquery-bootgrid.com", - "repository": { - "type": "git", - "url": "git://github.com/rstaib/jquery-bootgrid.git" - }, - "license": "MIT", - "main": [ - "dist/jquery.bootgrid.js", - "dist/jquery.bootgrid.css" - ], - "ignore": [ - "build", - "demo", - "dist/*.nupkg", - "dist/*.zip", - "docs", - "lib", - "test", - "*.nuspec", - ".gitattributes", - ".gitignore", - ".travis.yml", - "bootgrid.jquery.json", - "Gruntfile.js", - "package.json" - ], - "dependencies": { - "jquery": ">=1.9.0", - "bootstrap": ">=3.1.1" - } -} + "name": "jquery.bootgrid", + "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", + "keywords": [ + "grid", + "table", + "data", + "sorting", + "filtering", + "UI", + "component", + "HTML5", + "accessibility", + "bootstrap" + ], + "version": "1.4.1", + "authors": [ + { + "name": "Rafael Staib", + "email": "me@rafaelstaib.com", + "url": "http://www.rafaelstaib.com" + } + ], + "homepage": "http://www.jquery-bootgrid.com", + "repository": { + "type": "git", + "url": "git://github.com/rstaib/jquery-bootgrid.git" + }, + "license": "MIT", + "main": [ + "dist/jquery.bootgrid.js", + "dist/jquery.bootgrid.css" + ], + "ignore": [ + "build", + "demo", + "dist/*.nupkg", + "dist/*.zip", + "docs", + "lib", + "test", + "*.nuspec", + ".gitattributes", + ".gitignore", + ".travis.yml", + "bootgrid.jquery.json", + "Gruntfile.js", + "package.json" + ], + "dependencies": { + "jquery": ">=1.9.0", + "bootstrap": ">=3.1.1" + } +} \ No newline at end of file diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 02512a6..3beaa72 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.2 - 08/09/2016 + * jQuery Bootgrid v1.4.1 - 08/10/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 66e7583..418ba75 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.2 - 08/09/2016 + * jQuery Bootgrid v1.4.1 - 08/10/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index ee50132..04da038 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.3.2 - 08/09/2016 + * jQuery Bootgrid v1.4.1 - 08/10/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -361,8 +361,6 @@ function renderColumnSelection(actions) { var $this = $(this), checkbox = $this.find(checkboxSelector); if (!checkbox.prop("disabled")) { - column.visible = checkbox.prop("checked"); - that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); var enable = that.columns.where(isVisible).length > 1; $this.parents(itemsSelector).find(selector + ":has(" + checkboxSelector + ":checked)") ._bgEnableAria(enable).find(checkboxSelector)._bgEnableField(enable); @@ -371,6 +369,11 @@ function renderColumnSelection(actions) { renderTableHeader.call(that); loadData.call(that); } + }) + .on("change" + namespace, checkboxSelector, function(e){ + var $this = $(this); + column.visible = $this.prop("checked"); + that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); }); dropDown.find(getCssSelector(css.dropDownMenuItems)).append(item); } diff --git a/package.json b/package.json index da3f1c5..e871f4c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "jquery-bootgrid", "namespace": "jquery.bootgrid", "title": "jQuery Bootgrid", - "version": "1.4.0", + "version": "1.4.1", "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", "homepage": "http://www.jquery-bootgrid.com", "author": { diff --git a/src/internal.js b/src/internal.js index 7236617..6c1a5a0 100644 --- a/src/internal.js +++ b/src/internal.js @@ -351,8 +351,6 @@ function renderColumnSelection(actions) { var $this = $(this), checkbox = $this.find(checkboxSelector); if (!checkbox.prop("disabled")) { - column.visible = checkbox.prop("checked"); - that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); var enable = that.columns.where(isVisible).length > 1; $this.parents(itemsSelector).find(selector + ":has(" + checkboxSelector + ":checked)") ._bgEnableAria(enable).find(checkboxSelector)._bgEnableField(enable); @@ -361,6 +359,11 @@ function renderColumnSelection(actions) { renderTableHeader.call(that); loadData.call(that); } + }) + .on("change" + namespace, checkboxSelector, function(e){ + var $this = $(this); + column.visible = $this.prop("checked"); + that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); }); dropDown.find(getCssSelector(css.dropDownMenuItems)).append(item); } From f7a1375209240b8675c11b598aa83b61398e161c Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Wed, 10 Aug 2016 11:38:32 -0700 Subject: [PATCH 07/30] Fixed issue with showing/hiding columns --- CHANGELOG.md | 5 +++++ dist/jquery.bootgrid.js | 2 +- src/internal.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34dc82b..e5ac42b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.4.2 + +### Bug Fixes +- Fixed bug with showing/hiding columns + ## 1.4.1 ### Bug Fixes diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 04da038..0961795 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -361,6 +361,7 @@ function renderColumnSelection(actions) { 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); @@ -372,7 +373,6 @@ function renderColumnSelection(actions) { }) .on("change" + namespace, checkboxSelector, function(e){ var $this = $(this); - column.visible = $this.prop("checked"); that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); }); dropDown.find(getCssSelector(css.dropDownMenuItems)).append(item); diff --git a/src/internal.js b/src/internal.js index 6c1a5a0..870b473 100644 --- a/src/internal.js +++ b/src/internal.js @@ -351,6 +351,7 @@ function renderColumnSelection(actions) { 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); @@ -362,7 +363,6 @@ function renderColumnSelection(actions) { }) .on("change" + namespace, checkboxSelector, function(e){ var $this = $(this); - column.visible = $this.prop("checked"); that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); }); dropDown.find(getCssSelector(css.dropDownMenuItems)).append(item); From e194f8f0a68a60ced3234bbd5dd52f8d8b5a9180 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Wed, 31 Aug 2016 12:44:10 -0700 Subject: [PATCH 08/30] Added includeHidden search option --- CHANGELOG.md | 3 ++ demo/ajax.htm | 107 +++++++++++++++++++++++++++++++++++++ demo/data.json | 61 +++++++++++---------- demo/index.htm | 5 +- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 16 ++++-- npm-debug.log | 44 +++++++++++++++ src/internal.js | 2 +- src/public.js | 12 ++++- 10 files changed, 215 insertions(+), 39 deletions(-) create mode 100644 demo/ajax.htm create mode 100644 npm-debug.log diff --git a/CHANGELOG.md b/CHANGELOG.md index e5ac42b..2254ea2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Bug Fixes - Fixed bug with showing/hiding columns +### Enhancements & Features +- Added options for search to include hidden columns + ## 1.4.1 ### Bug Fixes diff --git a/demo/ajax.htm b/demo/ajax.htm new file mode 100644 index 0000000..c2ce5b0 --- /dev/null +++ b/demo/ajax.htm @@ -0,0 +1,107 @@ + + + + + + jQuery Bootgrid Demo + + + + + + + + +
            +
            +
            +
            + Sub Nav +
            +
            +
            + + + + + + + + +
            IDSenderReceived
            +
            +
            +
            + +
            + © Copyright 2014-2015, Rafael Staib +
            + + + + + + + diff --git a/demo/data.json b/demo/data.json index b177807..5c20b79 100644 --- a/demo/data.json +++ b/demo/data.json @@ -1,32 +1,31 @@ { - "current": 1, - "rowCount": 5, - "rows": [ - { - "id": "a0e3a286-4343-4240-8d6d-e79fa2e94b4c", - "sender": "test@test.de", - "received": "2014-04-17 15:08:03Z" - }, - { - "id": "dd9f2d42-9442-404c-8d2a-dd3bd2156c03", - "sender": "test@test.de", - "received": "2014-04-16 15:19:31Z" - }, - { - "id": "e9b8ede5-c1bf-4d90-b724-e7379b25f7b3", - "sender": "test@test.de", - "received": "2014-04-16 15:17:05Z" - }, - { - "id": "153d3acb-efe7-4b5f-a3a9-e8ac18bdec30", - "sender": "test@test.de", - "received": "2014-04-16 15:17:05Z" - }, - { - "id": "49bad60a-bbf7-42bf-b040-d901805ccbf1", - "sender": "test@test.de", - "received": "2014-04-15 11:23:06Z" - } - ], - "total": 5 -} \ No newline at end of file + "current": 1, + "rowCount": 5, + "rows": [{ + "id": "a0e3a286-4343-4240-8d6d-e79fa2e94b4c", + "sender": "test@test.de", + "received": "2014-04-17 15:08:03Z", + "checked": true + }, { + "id": "dd9f2d42-9442-404c-8d2a-dd3bd2156c03", + "sender": "test@test.de", + "received": "2014-04-16 15:19:31Z", + "checked": true + }, { + "id": "e9b8ede5-c1bf-4d90-b724-e7379b25f7b3", + "sender": "test@test.de", + "received": "2014-04-16 15:17:05Z", + "checked": false + }, { + "id": "153d3acb-efe7-4b5f-a3a9-e8ac18bdec30", + "sender": "test@test.de", + "received": "2014-04-16 15:17:05Z", + "checked": true + }, { + "id": "49bad60a-bbf7-42bf-b040-d901805ccbf1", + "sender": "test@test.de", + "received": "2014-04-15 11:23:06Z", + "checked": false + }], + "total": 5 +} diff --git a/demo/index.htm b/demo/index.htm index be96906..266fe24 100644 --- a/demo/index.htm +++ b/demo/index.htm @@ -479,7 +479,10 @@ } }, caseSensitive: false, - rowCount: [-1, 5, 10, 15] + rowCount: [-1, 5, 10, 15], + searchSettings:{ + includeHidden: false + } }); } diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 3beaa72..79efdcc 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/10/2016 + * jQuery Bootgrid v1.4.1 - 08/31/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 418ba75..70c0a7a 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/10/2016 + * jQuery Bootgrid v1.4.1 - 08/31/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 0961795..760f385 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/10/2016 + * jQuery Bootgrid v1.4.1 - 08/31/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -159,7 +159,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && column.visible && + if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && column.converter.to(row[column.id]).search(searchPattern) > -1) { return true; } @@ -1035,7 +1035,17 @@ Grid.defaults = { * @default 1 * @for searchSettings **/ - characters: 1 + characters: 1, + + /** + * Option if search should ignore hidden columns + * + * @property includeHidden + * @type Boolean + * @default false + * @for searchSettings + **/ + includeHidden: false }, /** diff --git a/npm-debug.log b/npm-debug.log new file mode 100644 index 0000000..7d6413a --- /dev/null +++ b/npm-debug.log @@ -0,0 +1,44 @@ +0 info it worked if it ends with ok +1 verbose cli [ '/usr/local/bin/node', +1 verbose cli '/usr/local/bin/npm', +1 verbose cli 'version', +1 verbose cli 'minor' ] +2 info using npm@3.10.3 +3 info using node@v6.4.0 +4 info git [ 'status', '--porcelain' ] +5 verbose stack Error: Git working directory not clean. +5 verbose stack M CHANGELOG.md +5 verbose stack M demo/data.json +5 verbose stack M demo/index.htm +5 verbose stack M dist/jquery.bootgrid.css +5 verbose stack M dist/jquery.bootgrid.fa.js +5 verbose stack M dist/jquery.bootgrid.js +5 verbose stack M src/internal.js +5 verbose stack M src/public.js +5 verbose stack at /usr/local/lib/node_modules/npm/lib/version.js:247:19 +5 verbose stack at /usr/local/lib/node_modules/npm/lib/utils/no-progress-while-running.js:21:8 +5 verbose stack at ChildProcess.exithandler (child_process.js:197:7) +5 verbose stack at emitTwo (events.js:106:13) +5 verbose stack at ChildProcess.emit (events.js:191:7) +5 verbose stack at maybeClose (internal/child_process.js:852:16) +5 verbose stack at Socket. (internal/child_process.js:323:11) +5 verbose stack at emitOne (events.js:96:13) +5 verbose stack at Socket.emit (events.js:188:7) +5 verbose stack at Pipe._handle.close [as _onclose] (net.js:492:12) +6 verbose cwd /Users/mgroth/Documents/GitHub/jquery-bootgrid +7 error Darwin 16.0.0 +8 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "version" "minor" +9 error node v6.4.0 +10 error npm v3.10.3 +11 error Git working directory not clean. +11 error M CHANGELOG.md +11 error M demo/data.json +11 error M demo/index.htm +11 error M dist/jquery.bootgrid.css +11 error M dist/jquery.bootgrid.fa.js +11 error M dist/jquery.bootgrid.js +11 error M src/internal.js +11 error M src/public.js +12 error If you need help, you may report this error at: +12 error +13 verbose exit [ 1, true ] diff --git a/src/internal.js b/src/internal.js index 870b473..4e7fbb5 100644 --- a/src/internal.js +++ b/src/internal.js @@ -149,7 +149,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && column.visible && + if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && column.converter.to(row[column.id]).search(searchPattern) > -1) { return true; } diff --git a/src/public.js b/src/public.js index a691dec..83a1bc0 100644 --- a/src/public.js +++ b/src/public.js @@ -148,7 +148,17 @@ Grid.defaults = { * @default 1 * @for searchSettings **/ - characters: 1 + characters: 1, + + /** + * Option if search should ignore hidden columns + * + * @property includeHidden + * @type Boolean + * @default false + * @for searchSettings + **/ + includeHidden: false }, /** From 8c62420717dc90faa7c759318ca031461a1bce8e Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Wed, 31 Aug 2016 12:45:29 -0700 Subject: [PATCH 09/30] 1.4.2 --- package.json | 136 ++++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index e871f4c..5437f95 100644 --- a/package.json +++ b/package.json @@ -1,68 +1,72 @@ { - "name": "jquery-bootgrid", - "namespace": "jquery.bootgrid", - "title": "jQuery Bootgrid", - "version": "1.4.1", - "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", - "homepage": "http://www.jquery-bootgrid.com", - "author": { - "name": "Rafael Staib", - "email": "me@rafaelstaib.com", - "url": "http://www.rafaelstaib.com" - }, - "contributors": [{ - "name": "Matt Groth", - "email": "mattgroth85@gmail.com", - "url": "https://github.com/Iodine-" - }], - "bugs": "https://github.com/MoveInc/jquery-bootgrid/issues", - "scripts": { - "test": "grunt" - }, - "repository": { - "type": "git", - "url": "https://github.com/MoveInc/jquery-bootgrid.git" - }, - "keywords": [ - "jquery-plugin", - "jQuery", - "Bootstrap", - "Plugin", - "Component", - "Control", - "UI", - "Grid", - "Table", - "Data", - "Sorting", - "Filtering", - "HTML5", - "Accessibility" - ], - "licenses": [{ - "type": "MIT", - "url": "http://www.opensource.org/licenses/MIT" - }], - "dependencies": { - "jquery": ">=1.9.0", - "bootstrap": ">=3.1.1" - }, - "devDependencies": { - "grunt": "^1.0.1", - "grunt-cli": "^1.2.0", - "grunt-contrib-clean": "^1.0.0", - "grunt-contrib-compress": "^1.3.0", - "grunt-contrib-concat": "^1.0.1", - "grunt-contrib-csslint": "^1.0.0", - "grunt-contrib-cssmin": "^1.0.1", - "grunt-contrib-jshint": "^1.0.0", - "grunt-contrib-less": "^1.3.0", - "grunt-contrib-qunit": "^1.2.0", - "grunt-contrib-uglify": "^2.0.0", - "grunt-contrib-yuidoc": "^1.0.0", - "grunt-exec": "^1.0.0", - "grunt-nuget": "~0.1.4", - "grunt-regex-replace": "^0.3.0" - }, - "readmeFilename": "README.md" + "name": "jquery-bootgrid", + "namespace": "jquery.bootgrid", + "title": "jQuery Bootgrid", + "version": "1.4.2", + "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", + "homepage": "http://www.jquery-bootgrid.com", + "author": { + "name": "Rafael Staib", + "email": "me@rafaelstaib.com", + "url": "http://www.rafaelstaib.com" + }, + "contributors": [ + { + "name": "Matt Groth", + "email": "mattgroth85@gmail.com", + "url": "https://github.com/Iodine-" + } + ], + "bugs": "https://github.com/MoveInc/jquery-bootgrid/issues", + "scripts": { + "test": "grunt" + }, + "repository": { + "type": "git", + "url": "https://github.com/MoveInc/jquery-bootgrid.git" + }, + "keywords": [ + "jquery-plugin", + "jQuery", + "Bootstrap", + "Plugin", + "Component", + "Control", + "UI", + "Grid", + "Table", + "Data", + "Sorting", + "Filtering", + "HTML5", + "Accessibility" + ], + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/MIT" + } + ], + "dependencies": { + "jquery": ">=1.9.0", + "bootstrap": ">=3.1.1" + }, + "devDependencies": { + "grunt": "^1.0.1", + "grunt-cli": "^1.2.0", + "grunt-contrib-clean": "^1.0.0", + "grunt-contrib-compress": "^1.3.0", + "grunt-contrib-concat": "^1.0.1", + "grunt-contrib-csslint": "^1.0.0", + "grunt-contrib-cssmin": "^1.0.1", + "grunt-contrib-jshint": "^1.0.0", + "grunt-contrib-less": "^1.3.0", + "grunt-contrib-qunit": "^1.2.0", + "grunt-contrib-uglify": "^2.0.0", + "grunt-contrib-yuidoc": "^1.0.0", + "grunt-exec": "^1.0.0", + "grunt-nuget": "~0.1.4", + "grunt-regex-replace": "^0.3.0" + }, + "readmeFilename": "README.md" } From cf5241c4982e182ff954747a3a45a2cbe38e9bf4 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Thu, 1 Sep 2016 12:15:50 -0700 Subject: [PATCH 10/30] updated sort function to use column converter --- CHANGELOG.md | 5 +++++ bower.json | 2 +- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 9 ++++++++- src/internal.js | 7 +++++++ 6 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2254ea2..b2953cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.4.3 + +### Enhancements & Features +- Updated sort function to use column converter if it exists + ## 1.4.2 ### Bug Fixes diff --git a/bower.json b/bower.json index 600d5f1..45fbf86 100644 --- a/bower.json +++ b/bower.json @@ -13,7 +13,7 @@ "accessibility", "bootstrap" ], - "version": "1.4.1", + "version": "1.4.2", "authors": [ { "name": "Rafael Staib", diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 79efdcc..a170671 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/31/2016 + * jQuery Bootgrid v1.4.2 - 09/01/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 70c0a7a..7d29397 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/31/2016 + * jQuery Bootgrid v1.4.2 - 09/01/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 760f385..d99b7c6 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/31/2016 + * jQuery Bootgrid v1.4.2 - 09/01/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -862,6 +862,13 @@ function sortRows() { var a = that.options.caseSensitive ? x[item.id] : x[item.id].toLowerCase(); var b = that.options.caseSensitive ? y[item.id] : y[item.id].toLowerCase(); + // if column has a converter, use it + var col = that.getColumnSettings({id: item.id}); + if(col.length > 0){ + a = col[0].converter ? col[0].converter.to(a, x) : a; + b = col[0].converter ? col[0].converter.to(b, y) : b; + } + return (a > b) ? sortOrder(1) : (a < b) ? sortOrder(-1) : (sortArray.length > next) ? sort(x, y, next) : 0; diff --git a/src/internal.js b/src/internal.js index 4e7fbb5..7363865 100644 --- a/src/internal.js +++ b/src/internal.js @@ -852,6 +852,13 @@ function sortRows() { var a = that.options.caseSensitive ? x[item.id] : x[item.id].toLowerCase(); var b = that.options.caseSensitive ? y[item.id] : y[item.id].toLowerCase(); + // if column has a converter, use it + var col = that.getColumnSettings({id: item.id}); + if(col.length > 0){ + a = col[0].converter ? col[0].converter.to(a, x) : a; + b = col[0].converter ? col[0].converter.to(b, y) : b; + } + return (a > b) ? sortOrder(1) : (a < b) ? sortOrder(-1) : (sortArray.length > next) ? sort(x, y, next) : 0; From 821343a6a788ec4137f765e07b343a46bae56c15 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Thu, 1 Sep 2016 12:26:18 -0700 Subject: [PATCH 11/30] added row param for search converter call --- CHANGELOG.md | 1 + src/internal.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2953cb..16d13ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Enhancements & Features - Updated sort function to use column converter if it exists +- Updated search to pass row to converter ## 1.4.2 diff --git a/src/internal.js b/src/internal.js index 7363865..2ac767b 100644 --- a/src/internal.js +++ b/src/internal.js @@ -150,7 +150,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && - column.converter.to(row[column.id]).search(searchPattern) > -1) { + column.converter.to(row[column.id], row).search(searchPattern) > -1) { return true; } } @@ -579,7 +579,7 @@ function renderRows(rows) { if (column.visible) { var value = ($.isFunction(column.formatter)) ? column.formatter.call(that, column, row) : - column.converter.to(row[column.id]), + column.converter.to(row[column.id], row), cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; cells += tpl.cell.resolve(getParams.call(that, { content: (value == null || value === "") ? " " : value, From bbebd19fd27d25e4c2c032ac58bb80ee22fad4b5 Mon Sep 17 00:00:00 2001 From: Dreae Date: Thu, 1 Sep 2016 17:07:53 -0400 Subject: [PATCH 12/30] Add ability to provide data through a function call --- bower.json | 2 +- demo/dataFunc.htm | 137 +++++++++++++++++++++++++++++++++++++ dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 22 ++++-- npm-debug.log | 44 ------------ src/internal.js | 20 +++++- 7 files changed, 175 insertions(+), 54 deletions(-) create mode 100644 demo/dataFunc.htm delete mode 100644 npm-debug.log diff --git a/bower.json b/bower.json index 600d5f1..45fbf86 100644 --- a/bower.json +++ b/bower.json @@ -13,7 +13,7 @@ "accessibility", "bootstrap" ], - "version": "1.4.1", + "version": "1.4.2", "authors": [ { "name": "Rafael Staib", diff --git a/demo/dataFunc.htm b/demo/dataFunc.htm new file mode 100644 index 0000000..4731695 --- /dev/null +++ b/demo/dataFunc.htm @@ -0,0 +1,137 @@ + + + + + + jQuery Bootgrid Demo + + + + + + + + +
            +
            +
            +
            + Sub Nav +
            +
            +
            + + + + + + + + +
            IDSenderReceived
            +
            +
            +
            + +
            + © Copyright 2014-2015, Rafael Staib +
            + + + + + + + diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 79efdcc..a170671 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/31/2016 + * jQuery Bootgrid v1.4.2 - 09/01/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 70c0a7a..7d29397 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/31/2016 + * jQuery Bootgrid v1.4.2 - 09/01/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 760f385..9bf5108 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.1 - 08/31/2016 + * jQuery Bootgrid v1.4.2 - 09/01/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -223,6 +223,20 @@ function loadData() { settings = $.extend(this.options.ajaxSettings, settings); this.xqr = $.ajax(settings); + } else if (this.options.dataFunc) { + var querySettings = { + data: getRequest.call(this), + success: function(response) { + that.current = response.current; + update(response.rows, response.total); + }, + error: function(err) { + renderNoResultsRow.call(that); // overrides loading mask + that.element._bgBusyAria(false).trigger("loaded" + namespace); + } + }; + + this.options.dataFunc(querySettings); } else { var rows = (this.searchPhrase.length > 0) ? this.rows.where(containsPhrase) : this.rows, total = rows.length; @@ -239,7 +253,7 @@ function loadData() { } function loadRows() { - if (!this.options.ajax) { + if (!this.options.ajax && !this.options.dataFunc) { var that = this, rows = this.element.find("tbody > tr"); @@ -305,7 +319,7 @@ function renderActions() { actions = $(tpl.actions.resolve(getParams.call(this))); // Refresh Button - if (this.options.ajax) { + if (this.options.ajax || this.options.dataFunc) { var refreshIcon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconRefresh })), @@ -867,7 +881,7 @@ function sortRows() { (sortArray.length > next) ? sort(x, y, next) : 0; } - if (!this.options.ajax) { + if (!this.options.ajax || !this.options.dataFunc) { var that = this; for (var key in this.sortDictionary) { diff --git a/npm-debug.log b/npm-debug.log deleted file mode 100644 index 7d6413a..0000000 --- a/npm-debug.log +++ /dev/null @@ -1,44 +0,0 @@ -0 info it worked if it ends with ok -1 verbose cli [ '/usr/local/bin/node', -1 verbose cli '/usr/local/bin/npm', -1 verbose cli 'version', -1 verbose cli 'minor' ] -2 info using npm@3.10.3 -3 info using node@v6.4.0 -4 info git [ 'status', '--porcelain' ] -5 verbose stack Error: Git working directory not clean. -5 verbose stack M CHANGELOG.md -5 verbose stack M demo/data.json -5 verbose stack M demo/index.htm -5 verbose stack M dist/jquery.bootgrid.css -5 verbose stack M dist/jquery.bootgrid.fa.js -5 verbose stack M dist/jquery.bootgrid.js -5 verbose stack M src/internal.js -5 verbose stack M src/public.js -5 verbose stack at /usr/local/lib/node_modules/npm/lib/version.js:247:19 -5 verbose stack at /usr/local/lib/node_modules/npm/lib/utils/no-progress-while-running.js:21:8 -5 verbose stack at ChildProcess.exithandler (child_process.js:197:7) -5 verbose stack at emitTwo (events.js:106:13) -5 verbose stack at ChildProcess.emit (events.js:191:7) -5 verbose stack at maybeClose (internal/child_process.js:852:16) -5 verbose stack at Socket. (internal/child_process.js:323:11) -5 verbose stack at emitOne (events.js:96:13) -5 verbose stack at Socket.emit (events.js:188:7) -5 verbose stack at Pipe._handle.close [as _onclose] (net.js:492:12) -6 verbose cwd /Users/mgroth/Documents/GitHub/jquery-bootgrid -7 error Darwin 16.0.0 -8 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "version" "minor" -9 error node v6.4.0 -10 error npm v3.10.3 -11 error Git working directory not clean. -11 error M CHANGELOG.md -11 error M demo/data.json -11 error M demo/index.htm -11 error M dist/jquery.bootgrid.css -11 error M dist/jquery.bootgrid.fa.js -11 error M dist/jquery.bootgrid.js -11 error M src/internal.js -11 error M src/public.js -12 error If you need help, you may report this error at: -12 error -13 verbose exit [ 1, true ] diff --git a/src/internal.js b/src/internal.js index 4e7fbb5..ffd0f24 100644 --- a/src/internal.js +++ b/src/internal.js @@ -213,6 +213,20 @@ function loadData() { settings = $.extend(this.options.ajaxSettings, settings); this.xqr = $.ajax(settings); + } else if (this.options.dataFunc) { + var querySettings = { + data: getRequest.call(this), + success: function(response) { + that.current = response.current; + update(response.rows, response.total); + }, + error: function(err) { + renderNoResultsRow.call(that); // overrides loading mask + that.element._bgBusyAria(false).trigger("loaded" + namespace); + } + }; + + this.options.dataFunc(querySettings); } else { var rows = (this.searchPhrase.length > 0) ? this.rows.where(containsPhrase) : this.rows, total = rows.length; @@ -229,7 +243,7 @@ function loadData() { } function loadRows() { - if (!this.options.ajax) { + if (!this.options.ajax && !this.options.dataFunc) { var that = this, rows = this.element.find("tbody > tr"); @@ -295,7 +309,7 @@ function renderActions() { actions = $(tpl.actions.resolve(getParams.call(this))); // Refresh Button - if (this.options.ajax) { + if (this.options.ajax || this.options.dataFunc) { var refreshIcon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconRefresh })), @@ -857,7 +871,7 @@ function sortRows() { (sortArray.length > next) ? sort(x, y, next) : 0; } - if (!this.options.ajax) { + if (!this.options.ajax || !this.options.dataFunc) { var that = this; for (var key in this.sortDictionary) { From fe87724d587d08ff5e03e4e094be88b48c438877 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Tue, 20 Sep 2016 15:44:24 -0700 Subject: [PATCH 13/30] Add derived columns --- CHANGELOG.md | 8 +- demo/derived.htm | 297 +++++++++++++++++++++++++++++++++++++ dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 109 ++++++++++++-- src/internal.js | 65 +++++--- src/public.js | 49 +++++- 7 files changed, 492 insertions(+), 40 deletions(-) create mode 100644 demo/derived.htm diff --git a/CHANGELOG.md b/CHANGELOG.md index 16d13ac..acf6b3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,12 @@ ## 1.4.3 ### Enhancements & Features -- Updated sort function to use column converter if it exists -- Updated search to pass row to converter +- Updated search to pass row, column, and grid to converter +- Add derived cell support with a derived formatter + - Adds additional `sortKey` parameter to determine which reference should be considered for sorting +- Add `sortRendered` parameter that uses the rendered cell value as the comparator + - Useful is situations where the formatter expects values other than what you want to sort on +- Add `derived` formatter and converter to defaults ## 1.4.2 diff --git a/demo/derived.htm b/demo/derived.htm new file mode 100644 index 0000000..28813e6 --- /dev/null +++ b/demo/derived.htm @@ -0,0 +1,297 @@ + + + + + + jQuery Bootgrid Demo + + + + + + + + +
            +
            +
            + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
            IDNameSenderReceivedCompanyCost + Derived + Hidden
            1{"text": "Aimee Delaney", "url":"http://www.aaa.ca"}erat.vitae@estvitaesodales.com03.06.16Maecenas Malesuada Foundation222222 + + C9F35176-EE9C-7DA5-DB47-5B62C57AA176
            2{"text": "BORIS BULE", "url": "http://www.zzz.ca"}vitae.diam@Proinultrices.edu10.12.16Ac Corporation11111111 + + 1BC83AA2-0979-C426-A3B4-4CC4F8B0CFF9
            3{"text": "ALEX Reese", "url": "http://www.ccc.com"}mauris.sit.amet@nonmassa.ca27.06.17Nulla At Incorporated333333333 + + C3CF97AE-0E0E-EA4E-FBF5-A63444134B8E
            4{"text": "Juliet Mcfarland", "url": "http://www.eee.com"}egestas.Duis.ac@eu.net02.07.16Nisi Sem Semper Limited555555 + + 33AC322B-8AB4-B9F0-FAF0-BD69A7937627
            5{"url": "http://www.ddd.com", "text": "Aaron Weaver"}Curabitur@fermentumconvallisligula.org07.06.17Magna Nec Quam Industries4444444 + + 235B6F73-2150-A423-DC0A-A75C74669824
            + +
            +
            +
            + +
            + © Copyright 2014-2015, Rafael Staib +
            + + + + + + + + diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index a170671..c6f618f 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/01/2016 + * jQuery Bootgrid v1.4.2 - 09/20/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 7d29397..423d5c1 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/01/2016 + * jQuery Bootgrid v1.4.2 - 09/20/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 948d604..113e584 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/01/2016 + * jQuery Bootgrid v1.4.2 - 09/20/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -113,6 +113,9 @@ function loadColumns() { order: (!sorted && (data.order === "asc" || data.order === "desc")) ? data.order : null, searchable: !(data.searchable === false), // default: true sortable: !(data.sortable === false), // default: true + sortKey: data.sortKey || '', + sortRendered: data.sortRendered || false, // default: false + rows: data.derivedRows || [], visible: !(data.visible === false), // default: true visibleInSelection: !(data.visibleInSelection === false), // default: true width: ($.isNumeric(data.width)) ? data.width + "px" : @@ -159,8 +162,8 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && - column.converter.to(row[column.id]).search(searchPattern) > -1) { + if (column.searchable && (column.visible || that.options.searchSettings.includeHidden) && + column.converter.to(row[column.id], row, column, that).search(searchPattern) > -1) { return true; } } @@ -385,7 +388,7 @@ function renderColumnSelection(actions) { loadData.call(that); } }) - .on("change" + namespace, checkboxSelector, function(e){ + .on("change" + namespace, checkboxSelector, function(e) { var $this = $(this); that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); }); @@ -537,12 +540,12 @@ function renderRowCountSelection(actions) { var $this = $(this), newRowCount = $this.data("action"); if (newRowCount !== that.rowCount) { - if(that.options.resolvePageFromRowCount){ + if (that.options.resolvePageFromRowCount) { var page = that.current > 1 ? that.current : 1; - var skip = that.current > 1 ? that.rowCount * (that.current-1) + 1 : 0; - var newPage = skip > 1 ? Math.ceil(skip/newRowCount) : 1; + var skip = that.current > 1 ? that.rowCount * (that.current - 1) + 1 : 0; + var newPage = skip > 1 ? Math.ceil(skip / newRowCount) : 1; that.current = newRowCount > 0 ? newPage : 1; - }else{ + } else { that.current = 1; } that.rowCount = newRowCount; @@ -862,19 +865,52 @@ function showLoading() { } function sortRows() { + var that = this; var sortArray = []; function sort(x, y, current) { current = current || 0; var next = current + 1, - item = sortArray[current]; + item = sortArray[current], + cell = item.id; function sortOrder(value) { return (item.order === "asc") ? value : value * -1; } - var a = that.options.caseSensitive ? x[item.id] : x[item.id].toLowerCase(); - var b = that.options.caseSensitive ? y[item.id] : y[item.id].toLowerCase(); + var column = that.getColumnSettings({ + id: cell + })[0]; + + if (column.sortKey) { + cell = column.sortKey; + column = that.getColumnSettings({ + id: cell + })[0]; + } + + var a = x[cell]; + var b = y[cell]; + + if (column.sortRendered) { + a = ($.isFunction(column.formatter)) ? + column.formatter.call(that, column, x) : + column.converter.to(x[column.id]); + try { + a = $(a).text(); + } catch (e) {} + b = ($.isFunction(column.formatter)) ? + column.formatter.call(that, column, y) : + column.converter.to(y[column.id]); + try { + b = $(b).text(); + } catch (e) {} + } + + if (that.options.caseSensitive) { + a = $.type(a) === 'string' ? a.toLowerCase() : a; + b = $.type(b) === 'string' ? b.toLowerCase() : b; + } // if column has a converter, use it var col = that.getColumnSettings({id: item.id}); @@ -889,8 +925,6 @@ function sortRows() { } if (!this.options.ajax || !this.options.dataFunc) { - var that = this; - for (var key in this.sortDictionary) { if (this.options.multiSort || sortArray.length === 0) { sortArray.push({ @@ -1178,7 +1212,28 @@ Grid.defaults = { // default converter from: function (value) { return value; }, to: function (value) { return value; } - } + }, + derived: { + // applies reference column converter to each row + to: function(val, row, column, grid) { + try { + var rows = column.rows.split(','); + var self = grid; + var compiledRows = []; + $.each(rows, function(index, element) { + var column = self.getColumnSettings({id: element})[0]; + var content = column.converter.to(row[column.id]); + compiledRows.push(content); + }); + return compiledRows.join(''); + } catch (e) { + return val; + } + }, + from: function(val) { + return val; + } + } }, /** @@ -1252,7 +1307,31 @@ Grid.defaults = { * @for defaults * @since 1.0.0 **/ - formatters: {}, + formatters: { + 'derived': function(column, row) { + try { + var rows = column.rows.split(','); + var self = this; + var compiledRows = []; + $.each(rows, function(index, element) { + var content = row[element]; + var template = element.template || '

            __data__

            '; + var column = self.getColumnSettings({id: element})[0]; + if (element.formatter || column.formatter) { + var formatter = column.formatter || element.formatter; + content = ($.isFunction(formatter)) ? + formatter.call(self, column, row) : + column.converter.to(row[column.id]); + } + var data = template.replace('__data__', content); + compiledRows.push(data); + }); + return compiledRows.join(''); + } catch (e) { + return row[column.id]; + } + } + }, /** * Contains all labels. diff --git a/src/internal.js b/src/internal.js index 83d1b93..b08bedc 100644 --- a/src/internal.js +++ b/src/internal.js @@ -103,6 +103,9 @@ function loadColumns() { order: (!sorted && (data.order === "asc" || data.order === "desc")) ? data.order : null, searchable: !(data.searchable === false), // default: true sortable: !(data.sortable === false), // default: true + sortKey: data.sortKey || '', + sortRendered: data.sortRendered || false, // default: false + rows: data.derivedRows || [], visible: !(data.visible === false), // default: true visibleInSelection: !(data.visibleInSelection === false), // default: true width: ($.isNumeric(data.width)) ? data.width + "px" : @@ -149,8 +152,8 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && - column.converter.to(row[column.id], row).search(searchPattern) > -1) { + if (column.searchable && (column.visible || that.options.searchSettings.includeHidden) && + column.converter.to(row[column.id], row, column, that).search(searchPattern) > -1) { return true; } } @@ -375,7 +378,7 @@ function renderColumnSelection(actions) { loadData.call(that); } }) - .on("change" + namespace, checkboxSelector, function(e){ + .on("change" + namespace, checkboxSelector, function(e) { var $this = $(this); that.element.trigger('toggleColumn', [column.id, column.text, column.visible]); }); @@ -527,12 +530,12 @@ function renderRowCountSelection(actions) { var $this = $(this), newRowCount = $this.data("action"); if (newRowCount !== that.rowCount) { - if(that.options.resolvePageFromRowCount){ + if (that.options.resolvePageFromRowCount) { var page = that.current > 1 ? that.current : 1; - var skip = that.current > 1 ? that.rowCount * (that.current-1) + 1 : 0; - var newPage = skip > 1 ? Math.ceil(skip/newRowCount) : 1; + var skip = that.current > 1 ? that.rowCount * (that.current - 1) + 1 : 0; + var newPage = skip > 1 ? Math.ceil(skip / newRowCount) : 1; that.current = newRowCount > 0 ? newPage : 1; - }else{ + } else { that.current = 1; } that.rowCount = newRowCount; @@ -593,7 +596,7 @@ function renderRows(rows) { if (column.visible) { var value = ($.isFunction(column.formatter)) ? column.formatter.call(that, column, row) : - column.converter.to(row[column.id], row), + column.converter.to(row[column.id], row, column, that), cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; cells += tpl.cell.resolve(getParams.call(that, { content: (value == null || value === "") ? " " : value, @@ -852,26 +855,52 @@ function showLoading() { } function sortRows() { + var that = this; var sortArray = []; function sort(x, y, current) { current = current || 0; var next = current + 1, - item = sortArray[current]; + item = sortArray[current], + cell = item.id; function sortOrder(value) { return (item.order === "asc") ? value : value * -1; } - var a = that.options.caseSensitive ? x[item.id] : x[item.id].toLowerCase(); - var b = that.options.caseSensitive ? y[item.id] : y[item.id].toLowerCase(); + var column = that.getColumnSettings({ + id: cell + })[0]; + + if (column.sortKey) { + cell = column.sortKey; + column = that.getColumnSettings({ + id: cell + })[0]; + } - // if column has a converter, use it - var col = that.getColumnSettings({id: item.id}); - if(col.length > 0){ - a = col[0].converter ? col[0].converter.to(a, x) : a; - b = col[0].converter ? col[0].converter.to(b, y) : b; - } + var a = x[cell]; + var b = y[cell]; + + if (column.sortRendered) { + a = ($.isFunction(column.formatter)) ? + column.formatter.call(that, column, x) : + column.converter.to(x[column.id]); + try { + a = $(a).text(); + } catch (e) {} + b = ($.isFunction(column.formatter)) ? + column.formatter.call(that, column, y) : + column.converter.to(y[column.id]); + try { + b = $(b).text(); + } catch (e) {} + } + + if (that.options.caseSensitive) { + a = $.type(a) === 'string' ? a.toLowerCase() : a; + b = $.type(b) === 'string' ? b.toLowerCase() : b; + } return (a > b) ? sortOrder(1) : (a < b) ? sortOrder(-1) : @@ -879,8 +908,6 @@ function sortRows() { } if (!this.options.ajax || !this.options.dataFunc) { - var that = this; - for (var key in this.sortDictionary) { if (this.options.multiSort || sortArray.length === 0) { sortArray.push({ diff --git a/src/public.js b/src/public.js index 83a1bc0..765f122 100644 --- a/src/public.js +++ b/src/public.js @@ -270,7 +270,28 @@ Grid.defaults = { // default converter from: function (value) { return value; }, to: function (value) { return value; } - } + }, + derived: { + // applies reference column converter to each row + to: function(val, row, column, grid) { + try { + var rows = column.rows.split(','); + var self = grid; + var compiledRows = []; + $.each(rows, function(index, element) { + var column = self.getColumnSettings({id: element})[0]; + var content = column.converter.to(row[column.id]); + compiledRows.push(content); + }); + return compiledRows.join(''); + } catch (e) { + return val; + } + }, + from: function(val) { + return val; + } + } }, /** @@ -344,7 +365,31 @@ Grid.defaults = { * @for defaults * @since 1.0.0 **/ - formatters: {}, + formatters: { + 'derived': function(column, row) { + try { + var rows = column.rows.split(','); + var self = this; + var compiledRows = []; + $.each(rows, function(index, element) { + var content = row[element]; + var template = element.template || '

            __data__

            '; + var column = self.getColumnSettings({id: element})[0]; + if (element.formatter || column.formatter) { + var formatter = column.formatter || element.formatter; + content = ($.isFunction(formatter)) ? + formatter.call(self, column, row) : + column.converter.to(row[column.id]); + } + var data = template.replace('__data__', content); + compiledRows.push(data); + }); + return compiledRows.join(''); + } catch (e) { + return row[column.id]; + } + } + }, /** * Contains all labels. From a2ba4c0e6fa9e59483d847c96e53fd0432121696 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Fri, 30 Sep 2016 11:53:09 -0700 Subject: [PATCH 14/30] Fix search to ensure value is string --- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 6 +++--- src/internal.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index a170671..8ac8510 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/01/2016 + * jQuery Bootgrid v1.4.2 - 09/30/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 7d29397..b18f2ed 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/01/2016 + * jQuery Bootgrid v1.4.2 - 09/30/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 948d604..a2588b3 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/01/2016 + * jQuery Bootgrid v1.4.2 - 09/30/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -160,7 +160,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && - column.converter.to(row[column.id]).search(searchPattern) > -1) { + column.converter.to(row[column.id], row).toString().search(searchPattern) > -1) { return true; } } @@ -603,7 +603,7 @@ function renderRows(rows) { if (column.visible) { var value = ($.isFunction(column.formatter)) ? column.formatter.call(that, column, row) : - column.converter.to(row[column.id]), + column.converter.to(row[column.id], row), cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; cells += tpl.cell.resolve(getParams.call(that, { content: (value == null || value === "") ? " " : value, diff --git a/src/internal.js b/src/internal.js index 83d1b93..412a6fa 100644 --- a/src/internal.js +++ b/src/internal.js @@ -150,7 +150,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && - column.converter.to(row[column.id], row).search(searchPattern) > -1) { + column.converter.to(row[column.id], row).toString().search(searchPattern) > -1) { return true; } } From f3e61ec9251fb5de0570a13c344f03c3e4dd058b Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Thu, 3 Nov 2016 08:52:33 -0700 Subject: [PATCH 15/30] Maintain data properties for rows --- demo/index.htm | 2 +- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 3 ++- src/internal.js | 1 + 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/demo/index.htm b/demo/index.htm index 266fe24..bb5d015 100644 --- a/demo/index.htm +++ b/demo/index.htm @@ -94,7 +94,7 @@ - + 1 Aimee Delaney erat.vitae@estvitaesodales.com diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 8ac8510..699f8e1 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/30/2016 + * jQuery Bootgrid v1.4.2 - 11/03/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index b18f2ed..e7e495a 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/30/2016 + * jQuery Bootgrid v1.4.2 - 11/03/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index a2588b3..cc019dc 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/30/2016 + * jQuery Bootgrid v1.4.2 - 11/03/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -262,6 +262,7 @@ function loadRows() { cells = $this.children("td"), row = {}; + row['data'] = $this.data(); $.each(that.columns, function(i, column) { row[column.id] = column.converter.from(cells.eq(i).text()); }); diff --git a/src/internal.js b/src/internal.js index 412a6fa..4b40c49 100644 --- a/src/internal.js +++ b/src/internal.js @@ -252,6 +252,7 @@ function loadRows() { cells = $this.children("td"), row = {}; + row['data'] = $this.data(); $.each(that.columns, function(i, column) { row[column.id] = column.converter.from(cells.eq(i).text()); }); From 875c3f23f166bc9cfdbbadd8074d754ab1144315 Mon Sep 17 00:00:00 2001 From: awestbro Date: Thu, 3 Nov 2016 15:30:43 -0400 Subject: [PATCH 16/30] Appended rows now get run through the column converter --- demo/append.html | 157 ++++++++++++++++++++++++++++++++++++++++ dist/jquery.bootgrid.js | 9 ++- src/public.js | 9 ++- 3 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 demo/append.html diff --git a/demo/append.html b/demo/append.html new file mode 100644 index 0000000..be57c0c --- /dev/null +++ b/demo/append.html @@ -0,0 +1,157 @@ + + + + + + jQuery Bootgrid Demo + + + + + + + + +
            +
            +
            +
            + Sub Nav +
            +
            +
            + + + + + + + + + + + + + + + +
            ID
            1111
            2222
            3333
            11111
            + +
            +
            +
            + +
            + © Copyright 2014-2015, Rafael Staib +
            + + + + + + + + diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index cc019dc..37f2f86 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1366,9 +1366,14 @@ Grid.prototype.append = function(rows) var appendedRows = []; for (var i = 0; i < rows.length; i++) { - if (appendRow.call(this, rows[i])) + var row = rows[i]; + for (var j = 0; j < this.columns.length; j++) { + var column = this.columns[j]; + row[column.id] = column.converter.from(row[column.id]); + } + if (appendRow.call(this, row)) { - appendedRows.push(rows[i]); + appendedRows.push(row); } } sortRows.call(this); diff --git a/src/public.js b/src/public.js index 83a1bc0..893824c 100644 --- a/src/public.js +++ b/src/public.js @@ -457,9 +457,14 @@ Grid.prototype.append = function(rows) var appendedRows = []; for (var i = 0; i < rows.length; i++) { - if (appendRow.call(this, rows[i])) + var row = rows[i]; + for (var j = 0; j < this.columns.length; j++) { + var column = this.columns[j]; + row[column.id] = column.converter.from(row[column.id]); + } + if (appendRow.call(this, row)) { - appendedRows.push(rows[i]); + appendedRows.push(row); } } sortRows.call(this); From 85fafcf5e2fb5d31da026ea468c50a8e9e8121bc Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Thu, 3 Nov 2016 13:59:18 -0700 Subject: [PATCH 17/30] Build dist --- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 15 ++++----------- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index c6f618f..699f8e1 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/20/2016 + * jQuery Bootgrid v1.4.2 - 11/03/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 423d5c1..e7e495a 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/20/2016 + * jQuery Bootgrid v1.4.2 - 11/03/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index acd733f..3e7d6d7 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 09/20/2016 + * jQuery Bootgrid v1.4.2 - 11/03/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -162,8 +162,8 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && (column.visible || that.options.searchSettings.includeHidden) && - column.converter.to(row[column.id], row, column, that).search(searchPattern) > -1) { + if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && + column.converter.to(row[column.id], row).toString().search(searchPattern) > -1) { return true; } } @@ -607,7 +607,7 @@ function renderRows(rows) { if (column.visible) { var value = ($.isFunction(column.formatter)) ? column.formatter.call(that, column, row) : - column.converter.to(row[column.id], row), + column.converter.to(row[column.id], row, column, that), cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; cells += tpl.cell.resolve(getParams.call(that, { content: (value == null || value === "") ? " " : value, @@ -913,13 +913,6 @@ function sortRows() { b = $.type(b) === 'string' ? b.toLowerCase() : b; } - // if column has a converter, use it - var col = that.getColumnSettings({id: item.id}); - if(col.length > 0){ - a = col[0].converter ? col[0].converter.to(a, x) : a; - b = col[0].converter ? col[0].converter.to(b, y) : b; - } - return (a > b) ? sortOrder(1) : (a < b) ? sortOrder(-1) : (sortArray.length > next) ? sort(x, y, next) : 0; From 5bde561d0d898ee4727710e4477423d0581d788a Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Thu, 3 Nov 2016 14:08:08 -0700 Subject: [PATCH 18/30] Ensure string value before tolowercase --- dist/jquery.bootgrid.js | 8 ++++++-- src/internal.js | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 37f2f86..f7dec48 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -874,8 +874,12 @@ function sortRows() { return (item.order === "asc") ? value : value * -1; } - var a = that.options.caseSensitive ? x[item.id] : x[item.id].toLowerCase(); - var b = that.options.caseSensitive ? y[item.id] : y[item.id].toLowerCase(); + var a = x[item.id] || ''; + var b = y[item.id] || ''; + if (that.options.caseSensitive) { + a = $.type(a) === 'string' ? a.toLowerCase() : a; + b = $.type(b) === 'string' ? b.toLowerCase() : b; + } // if column has a converter, use it var col = that.getColumnSettings({id: item.id}); diff --git a/src/internal.js b/src/internal.js index 4b40c49..dbb1033 100644 --- a/src/internal.js +++ b/src/internal.js @@ -864,8 +864,12 @@ function sortRows() { return (item.order === "asc") ? value : value * -1; } - var a = that.options.caseSensitive ? x[item.id] : x[item.id].toLowerCase(); - var b = that.options.caseSensitive ? y[item.id] : y[item.id].toLowerCase(); + var a = x[item.id] || ''; + var b = y[item.id] || ''; + if (that.options.caseSensitive) { + a = $.type(a) === 'string' ? a.toLowerCase() : a; + b = $.type(b) === 'string' ? b.toLowerCase() : b; + } // if column has a converter, use it var col = that.getColumnSettings({id: item.id}); From b31862713487ba7c937c17f5e6bdb8f65be6885f Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Mon, 7 Nov 2016 08:06:19 -0800 Subject: [PATCH 19/30] Optionally render refresh button when using ajax --- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 16 +++++++++++++--- src/internal.js | 2 +- src/public.js | 12 +++++++++++- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 699f8e1..00adeb0 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/03/2016 + * jQuery Bootgrid v1.4.2 - 11/07/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index e7e495a..537204c 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/03/2016 + * jQuery Bootgrid v1.4.2 - 11/07/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index f7dec48..3a58d8c 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/03/2016 + * jQuery Bootgrid v1.4.2 - 11/07/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -320,7 +320,7 @@ function renderActions() { actions = $(tpl.actions.resolve(getParams.call(this))); // Refresh Button - if (this.options.ajax || this.options.dataFunc) { + if ((this.options.ajax || this.options.dataFunc) && this.options.ajaxSettings.includeRefresh) { var refreshIcon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconRefresh })), @@ -1105,7 +1105,17 @@ Grid.defaults = { * @default "POST" * @for ajaxSettings **/ - method: "POST" + method: "POST", + + /** + * Option if refresh button should be shown + * + * @property includeRefresh + * @type Boolean + * @default true + * @for ajaxSettings + **/ + includeRefresh: true }, /** diff --git a/src/internal.js b/src/internal.js index dbb1033..139ab12 100644 --- a/src/internal.js +++ b/src/internal.js @@ -310,7 +310,7 @@ function renderActions() { actions = $(tpl.actions.resolve(getParams.call(this))); // Refresh Button - if (this.options.ajax || this.options.dataFunc) { + if ((this.options.ajax || this.options.dataFunc) && this.options.ajaxSettings.includeRefresh) { var refreshIcon = tpl.icon.resolve(getParams.call(this, { iconCss: css.iconRefresh })), diff --git a/src/public.js b/src/public.js index 893824c..7915c02 100644 --- a/src/public.js +++ b/src/public.js @@ -192,7 +192,17 @@ Grid.defaults = { * @default "POST" * @for ajaxSettings **/ - method: "POST" + method: "POST", + + /** + * Option if refresh button should be shown + * + * @property includeRefresh + * @type Boolean + * @default true + * @for ajaxSettings + **/ + includeRefresh: true }, /** From 1522d32d0e2c31bbed16d454b706159aa73618eb Mon Sep 17 00:00:00 2001 From: DavidMove Date: Wed, 14 Dec 2016 16:29:13 -0800 Subject: [PATCH 20/30] Fixed converter sort issue. --- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 12 ++++++------ src/internal.js | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 00adeb0..72c96c5 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/07/2016 + * jQuery Bootgrid v1.4.2 - 12/14/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 537204c..2a98bbc 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/07/2016 + * jQuery Bootgrid v1.4.2 - 12/14/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 3a58d8c..575bb02 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/07/2016 + * jQuery Bootgrid v1.4.2 - 12/14/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -882,11 +882,11 @@ function sortRows() { } // if column has a converter, use it - var col = that.getColumnSettings({id: item.id}); - if(col.length > 0){ - a = col[0].converter ? col[0].converter.to(a, x) : a; - b = col[0].converter ? col[0].converter.to(b, y) : b; - } + var col = that.getColumnSettings({id: item.id}); + if(col.length > 0){ + a = col[0].converter ? col[0].converter.from(a, x) : a; + b = col[0].converter ? col[0].converter.from(b, y) : b; + } return (a > b) ? sortOrder(1) : (a < b) ? sortOrder(-1) : diff --git a/src/internal.js b/src/internal.js index 139ab12..587ae2c 100644 --- a/src/internal.js +++ b/src/internal.js @@ -872,11 +872,11 @@ function sortRows() { } // if column has a converter, use it - var col = that.getColumnSettings({id: item.id}); - if(col.length > 0){ - a = col[0].converter ? col[0].converter.to(a, x) : a; - b = col[0].converter ? col[0].converter.to(b, y) : b; - } + var col = that.getColumnSettings({id: item.id}); + if(col.length > 0){ + a = col[0].converter ? col[0].converter.from(a, x) : a; + b = col[0].converter ? col[0].converter.from(b, y) : b; + } return (a > b) ? sortOrder(1) : (a < b) ? sortOrder(-1) : From 858f7466a055c297b14b28e959e6d7996fadbe99 Mon Sep 17 00:00:00 2001 From: DavidMove Date: Tue, 20 Dec 2016 14:38:51 -0800 Subject: [PATCH 21/30] Fixed derived column sort/search and added avatar example. --- demo/derived.htm | 98 ++++++++++++++++++++++++-------------- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 8 ++-- src/internal.js | 6 +-- 5 files changed, 72 insertions(+), 44 deletions(-) diff --git a/demo/derived.htm b/demo/derived.htm index 28813e6..92ed217 100644 --- a/demo/derived.htm +++ b/demo/derived.htm @@ -79,77 +79,71 @@ - - + + + - + - - + + + - + + - + + - + + - + + - + @@ -195,25 +189,59 @@ link = $.extend(true, {}, defaults, options); return '' + link.text + ''; } catch (e) { - console.log(e); return row[column.id]; } + }, + // Fixed column: Media object with media-object and text + 'avatar': function(column, row) { + try { + var rows = column.rows.split(','); + var self = this; + var compiledRows = []; + var $avatarMarkup = $('
            '); + var imgMarkup = '
            '; + if (row[column.id] == '') { + imgMarkup = '
            Add Photo
            '; + } + $avatarMarkup.find('.media').prepend(imgMarkup); + + $.each(rows, function(index, element) { + var content = row[element]; + var template = element.template || '

            __data__

            '; + var column = self.getColumnSettings({id: element})[0]; + + if (element.formatter || column.formatter) { + var formatter = column.formatter || element.formatter; + content = ($.isFunction(formatter)) ? + formatter.call(self, column, row) : + column.converter.to(row[column.id]); + } + var data = template.replace('__data__', content); + compiledRows.push(data); + }); + + $avatarMarkup.find('.media-left').append(compiledRows.join('')); + + return $avatarMarkup.html(); + } catch (e) { + return row[column.id]; + } } }, converters: { - link: { - to: function(val, row, column, that){ - try{ - val = JSON.parse(val); - return val.text; - }catch(e){ - return val; - } - }, - from: function(val){ - return val; - } + link: { + to: function(val, row, column, that){ + try{ + val = JSON.parse(val); + return val.text; + }catch(e){ + return val; } + }, + from: function(val){ + return val; + } + } }, caseSensitive: true, rowCount: [-1, 5, 10, 15], diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 699f8e1..d7011f7 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/03/2016 + * jQuery Bootgrid v1.4.2 - 12/20/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index e7e495a..13ea238 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/03/2016 + * jQuery Bootgrid v1.4.2 - 12/20/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 3e7d6d7..bc70a76 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 11/03/2016 + * jQuery Bootgrid v1.4.2 - 12/20/2016 * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -163,7 +163,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && - column.converter.to(row[column.id], row).toString().search(searchPattern) > -1) { + column.converter.to(row[column.id], row, column, that).toString().search(searchPattern) > -1) { return true; } } @@ -898,13 +898,13 @@ function sortRows() { column.formatter.call(that, column, x) : column.converter.to(x[column.id]); try { - a = $(a).text(); + a = $(a).text() || a; } catch (e) {} b = ($.isFunction(column.formatter)) ? column.formatter.call(that, column, y) : column.converter.to(y[column.id]); try { - b = $(b).text(); + b = $(b).text() || b; } catch (e) {} } diff --git a/src/internal.js b/src/internal.js index b94402f..45c33e2 100644 --- a/src/internal.js +++ b/src/internal.js @@ -153,7 +153,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && - column.converter.to(row[column.id], row).toString().search(searchPattern) > -1) { + column.converter.to(row[column.id], row, column, that).toString().search(searchPattern) > -1) { return true; } } @@ -888,13 +888,13 @@ function sortRows() { column.formatter.call(that, column, x) : column.converter.to(x[column.id]); try { - a = $(a).text(); + a = $(a).text() || a; } catch (e) {} b = ($.isFunction(column.formatter)) ? column.formatter.call(that, column, y) : column.converter.to(y[column.id]); try { - b = $(b).text(); + b = $(b).text() || b; } catch (e) {} } From 082db7bf39943b7f6a729e86df0ef7e7616dc892 Mon Sep 17 00:00:00 2001 From: DavidMove Date: Wed, 21 Dec 2016 09:16:53 -0800 Subject: [PATCH 22/30] Removed avatar from derived demo file. --- demo/derived.htm | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/demo/derived.htm b/demo/derived.htm index 92ed217..ad63f52 100644 --- a/demo/derived.htm +++ b/demo/derived.htm @@ -80,7 +80,6 @@ - @@ -93,7 +92,6 @@ - @@ -104,7 +102,6 @@ - @@ -115,7 +112,6 @@ - @@ -126,7 +122,6 @@ - @@ -137,7 +132,6 @@ - @@ -191,41 +185,6 @@ } catch (e) { return row[column.id]; } - }, - // Fixed column: Media object with media-object and text - 'avatar': function(column, row) { - try { - var rows = column.rows.split(','); - var self = this; - var compiledRows = []; - var $avatarMarkup = $('
            '); - var imgMarkup = '
            '; - if (row[column.id] == '') { - imgMarkup = '
            Add Photo
            '; - } - $avatarMarkup.find('.media').prepend(imgMarkup); - - $.each(rows, function(index, element) { - var content = row[element]; - var template = element.template || '

            __data__

            '; - var column = self.getColumnSettings({id: element})[0]; - - if (element.formatter || column.formatter) { - var formatter = column.formatter || element.formatter; - content = ($.isFunction(formatter)) ? - formatter.call(self, column, row) : - column.converter.to(row[column.id]); - } - var data = template.replace('__data__', content); - compiledRows.push(data); - }); - - $avatarMarkup.find('.media-left').append(compiledRows.join('')); - - return $avatarMarkup.html(); - } catch (e) { - return row[column.id]; - } } }, converters: { From 6e07259cc1977b1210b2ca40a4181d644a8165d6 Mon Sep 17 00:00:00 2001 From: DavidMove Date: Wed, 21 Dec 2016 09:18:24 -0800 Subject: [PATCH 23/30] Took out data-visible=false on name column in derived example. --- demo/derived.htm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/derived.htm b/demo/derived.htm index ad63f52..a1fc66c 100644 --- a/demo/derived.htm +++ b/demo/derived.htm @@ -80,7 +80,7 @@ - + From e1dcb62bee454d25b9e115d086d1e3bcf3fc4334 Mon Sep 17 00:00:00 2001 From: awestbro Date: Tue, 10 Jan 2017 15:45:26 -0500 Subject: [PATCH 24/30] Flipped case sensitivity logic for sortable string columns --- dist/jquery.bootgrid.css | 4 ++-- dist/jquery.bootgrid.fa.js | 4 ++-- dist/jquery.bootgrid.js | 6 +++--- src/internal.js | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index d7011f7..c845a01 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.4.2 - 12/20/2016 - * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.4.2 - 01/10/2017 + * Copyright (c) 2014-2017 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 13ea238..26f317b 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.4.2 - 12/20/2016 - * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.4.2 - 01/10/2017 + * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index ed7112f..e012c2c 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.4.2 - 12/20/2016 - * Copyright (c) 2014-2016 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.4.2 - 01/10/2017 + * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) @@ -908,7 +908,7 @@ function sortRows() { } catch (e) {} } - if (that.options.caseSensitive) { + if (!that.options.caseSensitive) { a = $.type(a) === 'string' ? a.toLowerCase() : a; b = $.type(b) === 'string' ? b.toLowerCase() : b; } diff --git a/src/internal.js b/src/internal.js index ad0bbb6..7d37bcd 100644 --- a/src/internal.js +++ b/src/internal.js @@ -898,7 +898,7 @@ function sortRows() { } catch (e) {} } - if (that.options.caseSensitive) { + if (!that.options.caseSensitive) { a = $.type(a) === 'string' ? a.toLowerCase() : a; b = $.type(b) === 'string' ? b.toLowerCase() : b; } From 641ae997bdf0c40dc3e29ed45a46fd91ba842de4 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Fri, 13 Jan 2017 09:43:09 -0800 Subject: [PATCH 25/30] add sort event trigger --- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 4 +++- src/internal.js | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index c845a01..3249fec 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/10/2017 + * jQuery Bootgrid v1.4.2 - 01/13/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index 26f317b..d659881 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/10/2017 + * jQuery Bootgrid v1.4.2 - 01/13/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index e012c2c..9d5c652 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/10/2017 + * jQuery Bootgrid v1.4.2 - 01/13/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -869,6 +869,8 @@ function sortRows() { var that = this; var sortArray = []; + that.element.trigger('sort', that.getSortDictionary()); + function sort(x, y, current) { current = current || 0; var next = current + 1, diff --git a/src/internal.js b/src/internal.js index 7d37bcd..8d8a3bc 100644 --- a/src/internal.js +++ b/src/internal.js @@ -859,6 +859,8 @@ function sortRows() { var that = this; var sortArray = []; + that.element.trigger('sort', that.getSortDictionary()); + function sort(x, y, current) { current = current || 0; var next = current + 1, From 7a56086079cf17b9d4febede10a751ed3c7a927c Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Fri, 20 Jan 2017 08:58:09 -0800 Subject: [PATCH 26/30] added defaultRowCount parameter --- CHANGELOG.md | 6 ++++++ src/public.js | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acf6b3d..278ce3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Enhancements & Features +- Add `defaultRowCount` property to set default row count from rowCount array +- Add `sort` event; triggered when column is sorted + ## 1.4.3 ### Enhancements & Features diff --git a/src/public.js b/src/public.js index f606cc6..3399ab6 100644 --- a/src/public.js +++ b/src/public.js @@ -23,7 +23,11 @@ var Grid = function(element, options) 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.rowCount = rowCount; + if($.isArray(rowCount)){ + var defaultRowCount = this.options.defaultRowCount = this.element.data().defaultRowCount || options.defaultRowCount || this.options.defaultRowCount; + this.rowCount = (rowCount.indexOf(defaultRowCount) > -1) ? defaultRowCount : rowCount[0]; + }; this.rows = []; this.searchPhrase = ""; this.selectedRows = []; From c12f1df58d76a5c0fe10c9e46e4eb7e34eb24196 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Fri, 20 Jan 2017 09:01:08 -0800 Subject: [PATCH 27/30] build dist --- dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 8 ++++++-- src/public.js | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 3249fec..c6593f4 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/13/2017 + * jQuery Bootgrid v1.4.2 - 01/20/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index d659881..ba6ac6d 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/13/2017 + * jQuery Bootgrid v1.4.2 - 01/20/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 9d5c652..5ac87e3 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/13/2017 + * jQuery Bootgrid v1.4.2 - 01/20/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -961,7 +961,11 @@ var Grid = function(element, options) 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.rowCount = rowCount; + if($.isArray(rowCount)){ + var defaultRowCount = this.options.defaultRowCount = this.element.data().defaultRowCount || options.defaultRowCount || this.options.defaultRowCount; + this.rowCount = (rowCount.indexOf(defaultRowCount) > -1) ? defaultRowCount : rowCount[0]; + } this.rows = []; this.searchPhrase = ""; this.selectedRows = []; diff --git a/src/public.js b/src/public.js index 3399ab6..43c33d1 100644 --- a/src/public.js +++ b/src/public.js @@ -27,7 +27,7 @@ var Grid = function(element, options) if($.isArray(rowCount)){ var defaultRowCount = this.options.defaultRowCount = this.element.data().defaultRowCount || options.defaultRowCount || this.options.defaultRowCount; this.rowCount = (rowCount.indexOf(defaultRowCount) > -1) ? defaultRowCount : rowCount[0]; - }; + } this.rows = []; this.searchPhrase = ""; this.selectedRows = []; From 260098c1796add22796de1395d56c75c40398c1f Mon Sep 17 00:00:00 2001 From: DavidMove Date: Thu, 9 Feb 2017 13:53:49 -0800 Subject: [PATCH 28/30] Added text highlighting to search results --- CHANGELOG.md | 3 + demo/index.htm | 1055 ++++++++++++++++++------------------ dist/jquery.bootgrid.css | 5 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 66 ++- src/internal.js | 47 +- src/jquery.bootgrid.less | 9 +- src/public.js | 17 +- 8 files changed, 666 insertions(+), 538 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 278ce3e..7bbaf98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Enhancements & Features - Add `defaultRowCount` property to set default row count from rowCount array - Add `sort` event; triggered when column is sorted +- Add `searchSettings.highlightResults` option to wrap matching substrings that match the query in result set + - Add `highlightResults` css class to options + - Add `highlightResults` template which wraps the matched substring ## 1.4.3 diff --git a/demo/index.htm b/demo/index.htm index bb5d015..ed4dd00 100644 --- a/demo/index.htm +++ b/demo/index.htm @@ -1,559 +1,564 @@ - - - - jQuery Bootgrid Demo - - - - - - - + + + -
            -
            -
            -
            - Sub Nav -
            -
            -
            - - - - - - - - - - - - - - - -
            IDNameIDAvatarName Sender Received Company Cost - Derived - Derived Hidden
            1{"text": "Aimee Delaney", "url":"http://www.aaa.ca"}erat.vitae@estvitaesodales.comAimee Delaney 03.06.16 Maecenas Malesuada Foundation 222222 - - C9F35176-EE9C-7DA5-DB47-5B62C57AA176
            2http://www.gefsgpegypt.org/Portals/0/empty_profile.gif {"text": "BORIS BULE", "url": "http://www.zzz.ca"} vitae.diam@Proinultrices.edu 10.12.16 Ac Corporation 11111111 - - 1BC83AA2-0979-C426-A3B4-4CC4F8B0CFF9
            3http://www.gefsgpegypt.org/Portals/0/empty_profile.gif {"text": "ALEX Reese", "url": "http://www.ccc.com"} mauris.sit.amet@nonmassa.ca 27.06.17 Nulla At Incorporated 333333333 - - C3CF97AE-0E0E-EA4E-FBF5-A63444134B8E
            4http://www.gefsgpegypt.org/Portals/0/empty_profile.gif {"text": "Juliet Mcfarland", "url": "http://www.eee.com"} egestas.Duis.ac@eu.net 02.07.16 Nisi Sem Semper Limited 555555 - - 33AC322B-8AB4-B9F0-FAF0-BD69A7937627
            5http://www.gefsgpegypt.org/Portals/0/empty_profile.gif {"url": "http://www.ddd.com", "text": "Aaron Weaver"} Curabitur@fermentumconvallisligula.org 07.06.17 Magna Nec Quam Industries 4444444 - - 235B6F73-2150-A423-DC0A-A75C74669824
            IDAvatar Name Sender Received
            1 Aimee Delaney 03.06.16
            2http://www.gefsgpegypt.org/Portals/0/empty_profile.gif {"text": "BORIS BULE", "url": "http://www.zzz.ca"} vitae.diam@Proinultrices.edu 10.12.16
            3http://www.gefsgpegypt.org/Portals/0/empty_profile.gif {"text": "ALEX Reese", "url": "http://www.ccc.com"} mauris.sit.amet@nonmassa.ca 27.06.17
            4http://www.gefsgpegypt.org/Portals/0/empty_profile.gif {"text": "Juliet Mcfarland", "url": "http://www.eee.com"} egestas.Duis.ac@eu.net 02.07.16
            5http://www.gefsgpegypt.org/Portals/0/empty_profile.gif {"url": "http://www.ddd.com", "text": "Aaron Weaver"} Curabitur@fermentumconvallisligula.org 07.06.17
            IDNameName Sender Received Company
            - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            IDNameSenderReceivedLinkStatusHidden
            1Aimee Delaneyerat.vitae@estvitaesodales.com03.06.16Maecenas Malesuada Foundation1C9F35176-EE9C-7DA5-DB47-5B62C57AA176
            2BORIS BULEvitae.diam@Proinultrices.edu10.12.16Ac Corporation31BC83AA2-0979-C426-A3B4-4CC4F8B0CFF9
            3ALEX Reesemauris.sit.amet@nonmassa.ca27.06.17Nulla At Incorporated5C3CF97AE-0E0E-EA4E-FBF5-A63444134B8E
            4Juliet Mcfarlandegestas.Duis.ac@eu.net02.07.16Nisi Sem Semper Limited333AC322B-8AB4-B9F0-FAF0-BD69A7937627
            5Lester WeaverCurabitur@fermentumconvallisligula.org07.06.17Magna Nec Quam Industries2235B6F73-2150-A423-DC0A-A75C74669824
            6Aaron HendersonIn@volutpat.co.uk29.01.16Nec Tellus Incorporated1284D00C0-6A24-E9B3-E462-3A3892BA7EF1
            7Sloane CraftFusce.feugiat.Lorem@tellusAeneanegestas.co.uk02.02.17Non Sollicitudin Institute1BC72D4C2-C600-44CF-D8CE-A3A0C8C98224
            8Mufutau CookeSed@tempusnonlacinia.ca02.05.16Nascetur Ridiculus Mus Company11DF470B0-15B8-44C2-0991-A15F13EBE087
            9Brooke MaloneAliquam.adipiscing@posuere.edu27.06.16Ut Eros Inc.47346FBE9-4AC2-ECD4-96D8-75BF2B20E021
            10Rooney ClineProin.ultrices.Duis@eu.com29.08.16Mauris Integer Inc.287412B97-5862-AB2C-EB92-F7692EB0C17A
            11Alexander VelasquezPraesent.interdum@infaucibusorci.org14.01.17Luctus Et Limited2E4DAE99C-6078-A89E-210A-C21487A90361
            12Stone SosaUt@turpis.com18.05.16Orci Luctus Et Foundation444723C37-B0D3-FBE6-E1A2-6EA3E13388E3
            13Graham Harveynulla.Integer@Phasellus.edu02.03.17Nibh Donec Est Consulting1AA3B3771-D300-8411-1167-AC7AEA0995B2
            14Lance Oliverelit.sed.consequat@mollis.com19.03.16Dignissim Consulting4D8241C7E-4B13-7A24-DD90-CFAC74C44E40
            15Cassady Brightlibero.dui.nec@dis.org24.12.16Nulla Consulting16A4BB223-3FC0-242F-CDBD-341E014BA7CD
            16Brett Sloanligula.Donec.luctus@scelerisquescelerisque.co.uk25.05.17Velit Pellentesque LLP378A0AC6A-55CE-E7F9-9E8D-F9638A067FC1
            17Cheyenne Herringmolestie.pharetra@egetmetus.ca17.09.16Egestas Foundation437D88706-931A-25EF-ABEB-D65F3D72BB8A
            182porttitor.interdum@pharetra.edu19.06.16Elit Nulla Facilisi Inc.5C919D2B9-63E6-BF6D-6639-F058F0141D27
            1911nunc.sed.pede@lectusrutrum.edu15.11.15Leo Morbi Incorporated2AA9836DC-8FA0-DCE3-12F0-78ABD416279C
            2001nulla.magna.malesuada@musProin.edu30.01.17Ac Urna Corp.2F51F8323-42AB-5E84-091D-F02B5E1E04E3
            21Martin SharpeCras@Suspendissenon.com06.10.15Etiam Imperdiet Dictum LLP47EDF8E88-70D2-4A80-AEF6-60661DF2ECA9
            22Ray OconnorFusce.aliquam.enim@tristiquepharetraQuisque.edu03.11.16Consectetuer Cursus Company3334C5153-2E2C-EB0A-C42C-CF221CCD6674
            23Yetta Beckerdictum.placerat@in.org24.12.16Sed Company489877E4F-8A15-5CAE-75DA-278F27D0B49F
            24Ishmael Wileyarcu@ornare.edu14.08.16Velit Institute438FB6436-85EB-9DE4-4A77-17B20941C627
            25Omar Mckinneya.dui.Cras@Integeraliquamadipiscing.edu03.02.17Quis Massa Ltd20619ED7E-4295-962D-A068-99AC32D0D088
            26Lucy SmallMaecenas.iaculis.aliquet@diamDuismi.co.uk13.03.17Dolor Dapibus Gravida Associates45322357B-864C-9D07-6220-551095B9E85A
            27Davis Hensonmus.Aenean@enim.net26.08.16Elementum Corp.5F8403A4B-1604-1798-7948-1B6C2E48D386
            28Ralph Travistristique.pellentesque.tellus@vulputatedui.ca16.08.15Posuere Cubilia Curae; LLP48081C1C0-5EF3-C7FF-7307-2E59683D86E0
            29Wayne HamptonNam.interdum@Proin.ca19.01.17Dictum Placerat Augue Foundation51A7276D5-A929-72E5-A397-F3AB7AE0EEBC
            30Priscilla Robersonmagna.Nam.ligula@lectus.com26.08.16Tortor At Industries22AFA1C2B-CDD0-84E7-8535-DC8551AE9945
            31Garth GreeneCum.sociis.natoque@lacusUt.org04.08.16Tincidunt Inc.54580886C-89E0-8FEE-CEFD-ED87FAC5FDA2
            32Erasmus Hamptonnunc@non.net08.10.16Dui Nec Urna PC4C1B98F7D-0554-7719-697D-754B1711F76C
            33Doris MaynardDonec.egestas.Aliquam@dolorsitamet.com11.07.17Est Arcu PC1C039DE61-4AFA-1B40-1074-5DD823625E00
            34Margaret Gonzalezpellentesque.tellus.sem@mi.ca19.11.16A Associates294C687D6-0085-D04C-1D16-7945411E9E3A
            35Kylan WattsCras@atnisiCum.com17.07.16Rutrum Consulting18CF021E9-AEE6-79DE-A368-BBE456153B5A
            36Ursula Lewistempus@sitametconsectetuer.net16.09.16Parturient Montes Consulting4EC79724F-9632-7C26-ED36-60737A57E57A
            37Vance AdkinsCurabitur.egestas.nunc@Sed.ca17.12.16Tristique Pharetra Company10720A4E2-19D4-5023-4E56-659C63B3D655
            38Steven Frankviverra@lectus.co.uk15.10.15Aliquam Gravida Mauris Foundation2C73C063E-68AF-BA1B-F318-B5A1F8D98E27
            39Rhoda Mccartyelit.dictum@sollicitudincommodo.com24.08.15Pellentesque Inc.461308679-75BF-06BA-AA56-A29DF911537D
            40Dara Hudsonac.feugiat.non@neque.ca05.09.15Sit Consulting18B1634F8-05AA-CBBD-B7A7-EC9F3CE97AC3
            - +
            +
            +
            +
            + Sub Nav
            +
            + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
            IDNameSenderReceivedLinkStatusHidden
            1Aimee Delaneyerat.vitae@estvitaesodales.com03.06.16Maecenas Malesuada Foundation1C9F35176-EE9C-7DA5-DB47-5B62C57AA176
            2BORIS BULEvitae.diam@Proinultrices.edu10.12.16Ac Corporation31BC83AA2-0979-C426-A3B4-4CC4F8B0CFF9
            3ALEX Reesemauris.sit.amet@nonmassa.ca27.06.17Nulla At Incorporated5C3CF97AE-0E0E-EA4E-FBF5-A63444134B8E
            4Juliet Mcfarlandegestas.Duis.ac@eu.net02.07.16Nisi Sem Semper Limited333AC322B-8AB4-B9F0-FAF0-BD69A7937627
            5Lester WeaverCurabitur@fermentumconvallisligula.org07.06.17Magna Nec Quam Industries2235B6F73-2150-A423-DC0A-A75C74669824
            6Aaron HendersonIn@volutpat.co.uk29.01.16Nec Tellus Incorporated1284D00C0-6A24-E9B3-E462-3A3892BA7EF1
            7Sloane CraftFusce.feugiat.Lorem@tellusAeneanegestas.co.uk02.02.17Non Sollicitudin Institute1BC72D4C2-C600-44CF-D8CE-A3A0C8C98224
            8Mufutau CookeSed@tempusnonlacinia.ca02.05.16Nascetur Ridiculus Mus Company11DF470B0-15B8-44C2-0991-A15F13EBE087
            9Brooke MaloneAliquam.adipiscing@posuere.edu27.06.16Ut Eros Inc.47346FBE9-4AC2-ECD4-96D8-75BF2B20E021
            10Rooney ClineProin.ultrices.Duis@eu.com29.08.16Mauris Integer Inc.287412B97-5862-AB2C-EB92-F7692EB0C17A
            11Alexander VelasquezPraesent.interdum@infaucibusorci.org14.01.17Luctus Et Limited2E4DAE99C-6078-A89E-210A-C21487A90361
            12Stone SosaUt@turpis.com18.05.16Orci Luctus Et Foundation444723C37-B0D3-FBE6-E1A2-6EA3E13388E3
            13Graham Harveynulla.Integer@Phasellus.edu02.03.17Nibh Donec Est Consulting1AA3B3771-D300-8411-1167-AC7AEA0995B2
            14Lance Oliverelit.sed.consequat@mollis.com19.03.16Dignissim Consulting4D8241C7E-4B13-7A24-DD90-CFAC74C44E40
            15Cassady Brightlibero.dui.nec@dis.org24.12.16Nulla Consulting16A4BB223-3FC0-242F-CDBD-341E014BA7CD
            16Brett Sloanligula.Donec.luctus@scelerisquescelerisque.co.uk25.05.17Velit Pellentesque LLP378A0AC6A-55CE-E7F9-9E8D-F9638A067FC1
            17Cheyenne Herringmolestie.pharetra@egetmetus.ca17.09.16Egestas Foundation437D88706-931A-25EF-ABEB-D65F3D72BB8A
            182porttitor.interdum@pharetra.edu19.06.16Elit Nulla Facilisi Inc.5C919D2B9-63E6-BF6D-6639-F058F0141D27
            1911nunc.sed.pede@lectusrutrum.edu15.11.15Leo Morbi Incorporated2AA9836DC-8FA0-DCE3-12F0-78ABD416279C
            2001nulla.magna.malesuada@musProin.edu30.01.17Ac Urna Corp.2F51F8323-42AB-5E84-091D-F02B5E1E04E3
            21Martin SharpeCras@Suspendissenon.com06.10.15Etiam Imperdiet Dictum LLP47EDF8E88-70D2-4A80-AEF6-60661DF2ECA9
            22Ray OconnorFusce.aliquam.enim@tristiquepharetraQuisque.edu03.11.16Consectetuer Cursus Company3334C5153-2E2C-EB0A-C42C-CF221CCD6674
            23Yetta Beckerdictum.placerat@in.org24.12.16Sed Company489877E4F-8A15-5CAE-75DA-278F27D0B49F
            24Ishmael Wileyarcu@ornare.edu14.08.16Velit Institute438FB6436-85EB-9DE4-4A77-17B20941C627
            25Omar Mckinneya.dui.Cras@Integeraliquamadipiscing.edu03.02.17Quis Massa Ltd20619ED7E-4295-962D-A068-99AC32D0D088
            26Lucy SmallMaecenas.iaculis.aliquet@diamDuismi.co.uk13.03.17Dolor Dapibus Gravida Associates45322357B-864C-9D07-6220-551095B9E85A
            27Davis Hensonmus.Aenean@enim.net26.08.16Elementum Corp.5F8403A4B-1604-1798-7948-1B6C2E48D386
            28Ralph Travistristique.pellentesque.tellus@vulputatedui.ca16.08.15Posuere Cubilia Curae; LLP48081C1C0-5EF3-C7FF-7307-2E59683D86E0
            29Wayne HamptonNam.interdum@Proin.ca19.01.17Dictum Placerat Augue Foundation51A7276D5-A929-72E5-A397-F3AB7AE0EEBC
            30Priscilla Robersonmagna.Nam.ligula@lectus.com26.08.16Tortor At Industries22AFA1C2B-CDD0-84E7-8535-DC8551AE9945
            31Garth GreeneCum.sociis.natoque@lacusUt.org04.08.16Tincidunt Inc.54580886C-89E0-8FEE-CEFD-ED87FAC5FDA2
            32Erasmus Hamptonnunc@non.net08.10.16Dui Nec Urna PC4C1B98F7D-0554-7719-697D-754B1711F76C
            33Doris MaynardDonec.egestas.Aliquam@dolorsitamet.com11.07.17Est Arcu PC1C039DE61-4AFA-1B40-1074-5DD823625E00
            34Margaret Gonzalezpellentesque.tellus.sem@mi.ca19.11.16A Associates294C687D6-0085-D04C-1D16-7945411E9E3A
            35Kylan WattsCras@atnisiCum.com17.07.16Rutrum Consulting18CF021E9-AEE6-79DE-A368-BBE456153B5A
            36Ursula Lewistempus@sitametconsectetuer.net16.09.16Parturient Montes Consulting4EC79724F-9632-7C26-ED36-60737A57E57A
            37Vance AdkinsCurabitur.egestas.nunc@Sed.ca17.12.16Tristique Pharetra Company10720A4E2-19D4-5023-4E56-659C63B3D655
            38Steven Frankviverra@lectus.co.uk15.10.15Aliquam Gravida Mauris Foundation2C73C063E-68AF-BA1B-F318-B5A1F8D98E27
            39Rhoda Mccartyelit.dictum@sollicitudincommodo.com24.08.15Pellentesque Inc.461308679-75BF-06BA-AA56-A29DF911537D
            40Dara Hudsonac.feugiat.non@neque.ca05.09.15Sit Consulting18B1634F8-05AA-CBBD-B7A7-EC9F3CE97AC3
            + +
            +
            -
            - © Copyright 2014-2015, Rafael Staib -
            +
            + © Copyright 2014-2015, Rafael Staib +
            - - - - - + + + + - + }); + + + diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index c6593f4..4a04a3d 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/20/2017 + * jQuery Bootgrid v1.4.2 - 02/10/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -145,3 +145,6 @@ text-overflow: inherit !important; white-space: inherit !important; } +.bootgrid-search-highlight { + font-weight: bold; +} diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index ba6ac6d..a917ccd 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/20/2017 + * jQuery Bootgrid v1.4.2 - 02/10/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index 5ac87e3..f4cb6bd 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 01/20/2017 + * jQuery Bootgrid v1.4.2 - 02/10/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -87,6 +87,45 @@ function highlightAppendedRows(rows) { } } +// Replaces all occurrences of the word in the given html +// Original source: http://stackoverflow.com/questions/8503121/replace-words-in-a-string-but-ignore-html +function highlightResults(html) { + var that = this, + word = that.searchPhrase, + tpl = this.options.templates, + css = this.options.css, + container = document.createElement("div"), + regexFlag = that.options.caseSensitive ? 'g' : 'gi', + regex = new RegExp('(' + word + ')', regexFlag); + + container.innerHTML = html; + + // Traverses the given element and apply the text replacement function with the given regex + function traverseElement(el, regex, textReplacerFunc) { + var child = el.lastChild; + while (child) { + if (child.nodeType === 1) { + traverseElement(child, regex, textReplacerFunc); + } else if (child.nodeType === 3) { + textReplacerFunc(child, regex); + } + child = child.previousSibling; + } + } + + traverseElement(container, regex, function(textNode, regex) { + // need this as the rendered cell will encode any html tags which will not render + textNode.data = textNode.data.replace(regex, "{{$1}}"); + }); + + var reg = new RegExp("(?:{{)(.*?)(?:}})", "g"); + return container.innerHTML.replace(reg, function(str, el){ + return tpl.highlightResults.resolve(getParams.call(that, { + content: el + })); + }); +} + function isVisible(column) { return column.visible; } @@ -162,7 +201,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && + if (column.searchable && (column.visible || that.options.searchSettings.includeHidden) && column.converter.to(row[column.id], row, column, that).toString().search(searchPattern) > -1) { return true; } @@ -609,6 +648,12 @@ function renderRows(rows) { column.formatter.call(that, column, row) : column.converter.to(row[column.id], row, column, that), cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; + + // Highlight search phrase if available + if (that.searchPhrase !== '' && that.options.searchSettings.highlightResults) { + value = highlightResults.call(that, value); + } + cells += tpl.cell.resolve(getParams.call(that, { content: (value == null || value === "") ? " " : value, css: ((column.align === "right") ? css.right : (column.align === "center") ? @@ -1093,14 +1138,24 @@ Grid.defaults = { characters: 1, /** - * Option if search should ignore hidden columns + * Option if search should include hidden columns * * @property includeHidden * @type Boolean * @default false * @for searchSettings **/ - includeHidden: false + includeHidden: false, + + /** + * Option if search term in results should be highlighted + * + * @property highlightResults + * @type Boolean + * @default false + * @for searchSettings + **/ + highlightResults: true }, /** @@ -1266,6 +1321,7 @@ Grid.defaults = { 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", + highlightResults: "bootgrid-search-highlight", icon: "icon glyphicon", iconColumns: "glyphicon-th-list", iconDown: "glyphicon-chevron-down", @@ -1276,7 +1332,6 @@ Grid.defaults = { 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. * @@ -1423,6 +1478,7 @@ Grid.defaults = { footer: "

            ", header: "

            ", headerCell: "{{ctx.column.text}}{{ctx.icon}}", + highlightResults: "{{ctx.content}}", icon: "", infos: "
            {{lbl.infos}}
            ", loading: "{{lbl.loading}}", diff --git a/src/internal.js b/src/internal.js index 8d8a3bc..b363ddd 100644 --- a/src/internal.js +++ b/src/internal.js @@ -77,6 +77,45 @@ function highlightAppendedRows(rows) { } } +// Replaces all occurrences of the word in the given html +// Original source: http://stackoverflow.com/questions/8503121/replace-words-in-a-string-but-ignore-html +function highlightResults(html) { + var that = this, + word = that.searchPhrase, + tpl = this.options.templates, + css = this.options.css, + container = document.createElement("div"), + regexFlag = that.options.caseSensitive ? 'g' : 'gi', + regex = new RegExp('(' + word + ')', regexFlag); + + container.innerHTML = html; + + // Traverses the given element and apply the text replacement function with the given regex + function traverseElement(el, regex, textReplacerFunc) { + var child = el.lastChild; + while (child) { + if (child.nodeType === 1) { + traverseElement(child, regex, textReplacerFunc); + } else if (child.nodeType === 3) { + textReplacerFunc(child, regex); + } + child = child.previousSibling; + } + } + + traverseElement(container, regex, function(textNode, regex) { + // need this as the rendered cell will encode any html tags which will not render + textNode.data = textNode.data.replace(regex, "{{$1}}"); + }); + + var reg = new RegExp("(?:{{)(.*?)(?:}})", "g"); + return container.innerHTML.replace(reg, function(str, el){ + return tpl.highlightResults.resolve(getParams.call(that, { + content: el + })); + }); +} + function isVisible(column) { return column.visible; } @@ -152,7 +191,7 @@ function loadData() { for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && (column.visible || that.options.searchSettings.includeHidden ) && + if (column.searchable && (column.visible || that.options.searchSettings.includeHidden) && column.converter.to(row[column.id], row, column, that).toString().search(searchPattern) > -1) { return true; } @@ -599,6 +638,12 @@ function renderRows(rows) { column.formatter.call(that, column, row) : column.converter.to(row[column.id], row, column, that), cssClass = (column.cssClass.length > 0) ? " " + column.cssClass : ""; + + // Highlight search phrase if available + if (that.searchPhrase !== '' && that.options.searchSettings.highlightResults) { + value = highlightResults.call(that, value); + } + cells += tpl.cell.resolve(getParams.call(that, { content: (value == null || value === "") ? " " : value, css: ((column.align === "right") ? css.right : (column.align === "center") ? diff --git a/src/jquery.bootgrid.less b/src/jquery.bootgrid.less index 36bfc2e..9a421e6 100644 --- a/src/jquery.bootgrid.less +++ b/src/jquery.bootgrid.less @@ -54,7 +54,7 @@ text-align: right; .btn-group > .btn-group - { + { .dropdown-menu { text-align: left; @@ -157,4 +157,9 @@ td { .noTurncate(); } } -} \ No newline at end of file +} + +.bootgrid-search-highlight +{ + font-weight: bold; +} diff --git a/src/public.js b/src/public.js index 43c33d1..5dbc434 100644 --- a/src/public.js +++ b/src/public.js @@ -155,14 +155,24 @@ Grid.defaults = { characters: 1, /** - * Option if search should ignore hidden columns + * Option if search should include hidden columns * * @property includeHidden * @type Boolean * @default false * @for searchSettings **/ - includeHidden: false + includeHidden: false, + + /** + * Option if search term in results should be highlighted + * + * @property highlightResults + * @type Boolean + * @default false + * @for searchSettings + **/ + highlightResults: true }, /** @@ -328,6 +338,7 @@ Grid.defaults = { 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", + highlightResults: "bootgrid-search-highlight", icon: "icon glyphicon", iconColumns: "glyphicon-th-list", iconDown: "glyphicon-chevron-down", @@ -338,7 +349,6 @@ Grid.defaults = { 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. * @@ -485,6 +495,7 @@ Grid.defaults = { footer: "

            ", header: "

            ", headerCell: "{{ctx.column.text}}{{ctx.icon}}", + highlightResults: "{{ctx.content}}", icon: "", infos: "
            {{lbl.infos}}
            ", loading: "{{lbl.loading}}", From 229cf8635304bf499d880f3b538b69803f6bf930 Mon Sep 17 00:00:00 2001 From: Matt Groth Date: Wed, 2 Aug 2017 06:56:56 -0700 Subject: [PATCH 29/30] Add deselectAll method added new deselectAll method to better support deselecting rows across pages, especially when using ajax data source --- demo/ajax.htm | 17 ++++++-- demo/{data.json => data1.json} | 17 +++----- demo/data2.json | 26 ++++++++++++ demo/server.js | 74 ++++++++++++++++++++++++++++++++++ dist/jquery.bootgrid.css | 2 +- dist/jquery.bootgrid.fa.js | 2 +- dist/jquery.bootgrid.js | 33 ++++++++++++++- package.json | 4 +- src/public.js | 31 ++++++++++++++ 9 files changed, 188 insertions(+), 18 deletions(-) rename demo/{data.json => data1.json} (60%) create mode 100644 demo/data2.json create mode 100644 demo/server.js diff --git a/demo/ajax.htm b/demo/ajax.htm index c2ce5b0..befe92a 100644 --- a/demo/ajax.htm +++ b/demo/ajax.htm @@ -56,14 +56,15 @@
            - Sub Nav + +
            - + @@ -89,8 +90,10 @@ ajaxSettings: { method: "GET" }, + rowCount: 5, ajax: true, - url: 'http://127.0.0.1:8080/demo/data.json', + keepSelection: true, + url: 'http://127.0.0.1:8080/data', selection: true, multiSelect: true }).on('loaded.rs.jquery.bootgrid', function(){ @@ -101,6 +104,14 @@ } }) }); + + $("#deselect").on("click", function() { + $("#grid").bootgrid("deselect"); + }); + + $("#deselectAll").on("click", function() { + $("#grid").bootgrid("deselectAll"); + }); }); diff --git a/demo/data.json b/demo/data1.json similarity index 60% rename from demo/data.json rename to demo/data1.json index 5c20b79..e33e8aa 100644 --- a/demo/data.json +++ b/demo/data1.json @@ -4,28 +4,23 @@ "rows": [{ "id": "a0e3a286-4343-4240-8d6d-e79fa2e94b4c", "sender": "test@test.de", - "received": "2014-04-17 15:08:03Z", - "checked": true + "received": "2014-04-17 15:08:03Z" }, { "id": "dd9f2d42-9442-404c-8d2a-dd3bd2156c03", "sender": "test@test.de", - "received": "2014-04-16 15:19:31Z", - "checked": true + "received": "2014-04-16 15:19:31Z" }, { "id": "e9b8ede5-c1bf-4d90-b724-e7379b25f7b3", "sender": "test@test.de", - "received": "2014-04-16 15:17:05Z", - "checked": false + "received": "2014-04-16 15:17:05Z" }, { "id": "153d3acb-efe7-4b5f-a3a9-e8ac18bdec30", "sender": "test@test.de", - "received": "2014-04-16 15:17:05Z", - "checked": true + "received": "2014-04-16 15:17:05Z" }, { "id": "49bad60a-bbf7-42bf-b040-d901805ccbf1", "sender": "test@test.de", - "received": "2014-04-15 11:23:06Z", - "checked": false + "received": "2014-04-15 11:23:06Z" }], - "total": 5 + "total": 10 } diff --git a/demo/data2.json b/demo/data2.json new file mode 100644 index 0000000..35cfbc2 --- /dev/null +++ b/demo/data2.json @@ -0,0 +1,26 @@ +{ + "current": 2, + "rowCount": 5, + "rows": [{ + "id": "V5e3a286-4343-4240-8d6d-e79fa2e94b4c", + "sender": "test@test.de", + "received": "2014-04-17 15:08:03Z" + }, { + "id": "pp9f2d42-9442-404c-8d2a-dd3bd2156c03", + "sender": "test@test.de", + "received": "2014-04-16 15:19:31Z" + }, { + "id": "p7b8ede5-c1bf-4d90-b724-e7379b25f7b3", + "sender": "test@test.de", + "received": "2014-04-16 15:17:05Z" + }, { + "id": "553d3acb-efe7-4b5f-a3a9-e8ac18bdec30", + "sender": "test@test.de", + "received": "2014-04-16 15:17:05Z" + }, { + "id": "34bad60a-bbf7-42bf-b040-d901805ccbf1", + "sender": "test@test.de", + "received": "2014-04-15 11:23:06Z" + }], + "total": 10 +} diff --git a/demo/server.js b/demo/server.js new file mode 100644 index 0000000..4fd220e --- /dev/null +++ b/demo/server.js @@ -0,0 +1,74 @@ +'use strict'; + +const Hapi = require('hapi'); + +const server = new Hapi.Server(); +server.connection({ port: 8080, host: 'localhost' }); + +server.register(require('inert'), (err) => { + + server.route({ + method: 'GET', + path: '/demo/{file}', + handler: function (request, reply) { + reply.file('./demo/' + encodeURIComponent(request.params.file)); + } + }); + + server.route({ + method: 'GET', + path: '/data', + handler: function (request, reply) { + reply.file('./demo/data' + request.query.current + '.json'); + } + }); + + server.route({ + method: 'GET', + path: '/demo/css/{name*}', + handler: { + directory: { + path: 'demo/css', + redirectToSlash: true + } + } + }); + server.route({ + method: 'GET', + path: '/dist/{name*}', + handler: { + directory: { + path: 'dist', + redirectToSlash: true + } + } + }); + server.route({ + method: 'GET', + path: '/demo/{name*}', + handler: { + directory: { + path: 'demo/', + redirectToSlash: true + } + } + }); + server.route({ + method: 'GET', + path: '/lib/{name*}', + handler: { + directory: { + path: 'lib/', + redirectToSlash: true + } + } + }); +}); + +server.start((err) => { + + if (err) { + throw err; + } + console.log(`Server running at: ${server.info.uri}`); +}); \ No newline at end of file diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 4a04a3d..3164924 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 02/10/2017 + * jQuery Bootgrid v1.4.2 - 08/02/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js index a917ccd..928c54e 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 02/10/2017 + * jQuery Bootgrid v1.4.2 - 08/02/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index f4cb6bd..a6dcb24 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,5 +1,5 @@ /*! - * jQuery Bootgrid v1.4.2 - 02/10/2017 + * jQuery Bootgrid v1.4.2 - 08/02/2017 * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ @@ -1783,6 +1783,37 @@ Grid.prototype.deselect = function(rowIds) return this; }; +/** + * Deselects all rows. + * + * @method deselectAll + * @chainable + **/ +Grid.prototype.deselectAll = function() +{ + if (this.selection) + { + if (this.selectedRows.length > 0) + { + var deselected = []; + var selectBoxSelector = getCssSelector(this.options.css.selectBox); + + this.element.find("thead " + selectBoxSelector).prop("checked", false); + for (var i = 0; i < this.selectedRows.length; i++) + { + this.element.find("tbody > tr[data-row-id=\"" + this.selectedRows[i] + "\"]") + .removeClass(this.options.css.selected)._bgAria("selected", "false") + .find(selectBoxSelector).prop("checked", false); + } + + this.selectedRows = []; + this.element.trigger("deselected" + namespace, [deselected]); + } + } + + return this; +}; + /** * Sorts the rows by a given sort descriptor dictionary. * The sort filter will be reseted, if no argument is provided. diff --git a/package.json b/package.json index 5437f95..64f18fb 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,9 @@ "grunt-contrib-yuidoc": "^1.0.0", "grunt-exec": "^1.0.0", "grunt-nuget": "~0.1.4", - "grunt-regex-replace": "^0.3.0" + "grunt-regex-replace": "^0.3.0", + "hapi": "^16.5.0", + "inert": "^4.2.1" }, "readmeFilename": "README.md" } diff --git a/src/public.js b/src/public.js index 5dbc434..44e8763 100644 --- a/src/public.js +++ b/src/public.js @@ -800,6 +800,37 @@ Grid.prototype.deselect = function(rowIds) return this; }; +/** + * Deselects all rows. + * + * @method deselectAll + * @chainable + **/ +Grid.prototype.deselectAll = function() +{ + if (this.selection) + { + if (this.selectedRows.length > 0) + { + var deselected = []; + var selectBoxSelector = getCssSelector(this.options.css.selectBox); + + this.element.find("thead " + selectBoxSelector).prop("checked", false); + for (var i = 0; i < this.selectedRows.length; i++) + { + this.element.find("tbody > tr[data-row-id=\"" + this.selectedRows[i] + "\"]") + .removeClass(this.options.css.selected)._bgAria("selected", "false") + .find(selectBoxSelector).prop("checked", false); + } + + this.selectedRows = []; + this.element.trigger("deselected" + namespace, [deselected]); + } + } + + return this; +}; + /** * Sorts the rows by a given sort descriptor dictionary. * The sort filter will be reseted, if no argument is provided. From a5148c9e6376c04f4a95b6a150c52041731f54b0 Mon Sep 17 00:00:00 2001 From: Chris Tanner Date: Thu, 24 May 2018 15:49:21 -0700 Subject: [PATCH 30/30] Escape regex characters before parsing them --- demo/index.htm | 2 +- dist/jquery.bootgrid.css | 4 ++-- dist/jquery.bootgrid.fa.js | 4 ++-- dist/jquery.bootgrid.js | 17 +++++++++++------ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/demo/index.htm b/demo/index.htm index ed4dd00..a35686e 100644 --- a/demo/index.htm +++ b/demo/index.htm @@ -274,7 +274,7 @@ - + diff --git a/dist/jquery.bootgrid.css b/dist/jquery.bootgrid.css index 3164924..3a95473 100644 --- a/dist/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.4.2 - 08/02/2017 - * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.4.2 - 05/24/2018 + * Copyright (c) 2014-2018 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 928c54e..45e152d 100644 --- a/dist/jquery.bootgrid.fa.js +++ b/dist/jquery.bootgrid.fa.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.4.2 - 08/02/2017 - * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.4.2 - 05/24/2018 + * Copyright (c) 2014-2018 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) diff --git a/dist/jquery.bootgrid.js b/dist/jquery.bootgrid.js index a6dcb24..0b815c5 100644 --- a/dist/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.4.2 - 08/02/2017 - * Copyright (c) 2014-2017 Rafael Staib (http://www.jquery-bootgrid.com) + * jQuery Bootgrid v1.4.2 - 05/24/2018 + * Copyright (c) 2014-2018 Rafael Staib (http://www.jquery-bootgrid.com) * Licensed under MIT http://www.opensource.org/licenses/MIT */ ;(function ($, window, undefined) @@ -87,11 +87,15 @@ function highlightAppendedRows(rows) { } } +function escapeRegExp(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); +} + // Replaces all occurrences of the word in the given html // Original source: http://stackoverflow.com/questions/8503121/replace-words-in-a-string-but-ignore-html function highlightResults(html) { var that = this, - word = that.searchPhrase, + word = escapeRegExp(that.searchPhrase), tpl = this.options.templates, css = this.options.css, container = document.createElement("div"), @@ -189,6 +193,8 @@ response = { } */ + + function loadData() { var that = this; @@ -196,9 +202,8 @@ function loadData() { showLoading.call(this); function containsPhrase(row) { - var column, - searchPattern = new RegExp(that.searchPhrase, (that.options.caseSensitive) ? "g" : "gi"); - + var column; + var searchPattern = new RegExp(escapeRegExp(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 || that.options.searchSettings.includeHidden) &&
            IDID Sender Received
            20 01nulla.magna.malesuada@musProin.edu[nulla.magna.malesuada@musProin.edu 30.01.17 Ac Urna Corp. 2