diff --git a/.gitignore b/.gitignore index 77cc13e..1cc5b6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,10 @@ -dist -jSteps docs downloads +node_modules +src/*.css +src/*.map *~ *.diff *.patch .DS_Store -node_modules -src/jquery.bootgrid.css -src/jquery.bootgrid.css.map -src/jquery.bootgrid.min.css \ No newline at end of file +settings.json \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e02a6ed --- /dev/null +++ b/.npmignore @@ -0,0 +1,14 @@ +build +demo +dist/*.nupkg +dist/*.zip +docs +lib +*.nuspec +test +.gitattributes +.gitignore +.travis.yml +bootgrid.jquery.json +bower.json +Gruntfile.js \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 343aa3f..a3c5e48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,62 @@ # Changelog +## 1.3.1 + +### Enhancements & Features +- Replaced href attributes in order to prevent hash tags so *angular JS* works proper; see issue [#69](http://github.com/rstaib/jquery-bootgrid/issues/69) for more details + +### Breaking Changes +There are no breaking changes but some HTML templates changed during development. In case you want to use the full new feature set be sure you did not override any affected (actionDropDownItem and paginationItem) templates. + +## 1.3.0 + +### Enhancements & Features +- Option to hide column from the selection box; see issue [#62](http://github.com/rstaib/jquery-bootgrid/issues/62) for more details + +## 1.2.0 + +### Enhancements & Features +- Configurable column width; see issue [#22](http://github.com/rstaib/jquery-bootgrid/issues/22) for more details +- Contextual classes to color rows; see issue [#72](http://github.com/rstaib/jquery-bootgrid/issues/72) for more details +- *FontAwesome* iconset overload +- Clear search filter method [#89](http://github.com/rstaib/jquery-bootgrid/issues/89) +- Overridable ajax request settings; see issue [#27](http://github.com/rstaib/jquery-bootgrid/issues/27) for more details +- Adjusting search input field to perform search not on every key [#109](http://github.com/rstaib/jquery-bootgrid/issues/109) +- Public getters for internal variables; see issues [#121](http://github.com/rstaib/jquery-bootgrid/issues/121) and [#116](http://github.com/rstaib/jquery-bootgrid/issues/116) for more details +- Noticeable flicker on any table update [#118](http://github.com/rstaib/jquery-bootgrid/issues/118) (Partially improved by deferring loading mask) + +### Bug Fixes +- Fixed bug [#120](http://github.com/rstaib/jquery-bootgrid/issues/120) +- Fixed bug [#58](http://github.com/rstaib/jquery-bootgrid/issues/58) by using the pull request [#65](http://github.com/rstaib/jquery-bootgrid/issues/65) +- Fixing sort method for jQuery plugin [#84](http://github.com/rstaib/jquery-bootgrid/issues/84) + +### Breaking Changes +There are no breaking changes but some HTML templates changed during development. In case you want to use the full new feature set be sure you did not override any affected templates. + +## 1.1.4 + +### Enhancements & Features +- Improved Bower and NPM packages +- Added minified version for CSS file + +## 1.1.3 + +### Enhancements & Features +- Improved destroy method behaviour + +### Bug Fixes +- Fixed bug [#40](http://github.com/rstaib/jquery-bootgrid/issues/40) + +## 1.1.2 + +### Bug Fixes +- Fixed bug [#32](http://github.com/rstaib/jquery-bootgrid/issues/32) + +## 1.1.1 + +### Bug Fixes +- Fixed issue [#25](http://github.com/rstaib/jquery-bootgrid/issues/25) + ## 1.1.0 ### Enhancements & Features @@ -22,7 +79,7 @@ - Fixed multi select issue ### Breaking Changes -There are breaking changes but some HTML templates changed during development. In case you want to use the full new feature set be sure you did not override any affected templates. +There are no breaking changes but some HTML templates changed during development. In case you want to use the full new feature set be sure you did not override any affected templates. ## 1.0.0 diff --git a/Gruntfile.js b/Gruntfile.js index 6974474..e584c33 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,92 +7,111 @@ module.exports = function (grunt) grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), - concat: { - options: { - separator: '\r\n\r\n', - banner: '/*! <%= "\\r\\n * " + pkg.title %> v<%= pkg.version %> - <%= grunt.template.today("mm/dd/yyyy") + "\\r\\n" %>' + - ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> <%= (pkg.homepage ? "(" + pkg.homepage + ")" : "") + "\\r\\n" %>' + - ' * Licensed under <%= pkg.licenses[0].type + " " + pkg.licenses[0].url + "\\r\\n */\\r\\n" %>' + - ';(function ($, window, undefined)\r\n{\r\n /*jshint validthis: true */\r\n "use strict";\r\n\r\n', - footer: '\r\n})(jQuery, window);', - process: function(src, filepath) - { - var result = src.trim().replace(/(.+?\r\n)/gm, ' $1'), - end = [0, ""], - lastChar = result[result.length - 1]; + fontawesome: 'fa', + banner: '/*! <%= "\\r\\n * " + pkg.title %> v<%= pkg.version %> - <%= grunt.template.today("mm/dd/yyyy") + "\\r\\n" %>' + + ' * Copyright (c) 2014-<%= grunt.template.today("yyyy") %> <%= pkg.author.name %> <%= (pkg.homepage ? "(" + pkg.homepage + ")" : "") + "\\r\\n" %>' + + ' * Licensed under <%= pkg.licenses[0].type + " " + pkg.licenses[0].url + "\\r\\n */\\r\\n" %>', + folders: { + dist: "dist", + docs: "docs", + src: "src" + }, - if (lastChar === ";") - { - end = (result[result.length - 2] === ")") ? - (result[result.length - 2] === "}") ? - [3, " });"] : [2, ");"] : [2, " };"]; - } - else if (lastChar === "}") - { - end = [1, " }"]; - } + clean: { + api: ["<%= folders.docs %>"], + build: ["<%= folders.dist %>"] + }, - return result.substr(0, result.length - end[0]) + end[1]; - } - }, - dist: { - files: { - '<%= pkg.folders.dist %>/<%= pkg.namespace %>.js': [ - '<%= pkg.folders.src %>/internal.js', - '<%= pkg.folders.src %>/public.js', - '<%= pkg.folders.src %>/extensions.js', - '<%= pkg.folders.src %>/plugin.js' - ] + yuidoc: { + compile: { + name: '<%= pkg.name %>', + description: '<%= pkg.description %>', + version: '<%= pkg.version %>', + url: '<%= pkg.homepage %>', + options: { + paths: '<%= folders.dist %>', + outdir: '<%= folders.docs %>/' } } }, - //"regex-replace": { - // all: { - // src: ['<%= pkg.folders.nuget %>/<%= pkg.namespace %>.nuspec'], - // actions: [ - // { - // name: 'versionNumber', - // search: /.*?<\/version>/gi, - // replace: '<%= pkg.version %>' - // } - // ] - // } - //}, - exec: { - createPkg: { - cmd: "<%= pkg.folders.nuget %>\\Nuget pack <%= pkg.folders.nuget %>\\<%= pkg.namespace %>.nuspec -OutputDirectory <%= pkg.folders.dist %> -Version <%= pkg.version %>" - } - }, - compress: { - main: { + + version: { + default: { + src: 'bower.json', options: { - archive: '<%= pkg.folders.dist %>/<%= pkg.namespace %>-<%= pkg.version %>.zip' - }, - files: [ - { flatten: true, expand: true, src: ['<%= pkg.folders.dist %>/*.js', '<%= pkg.folders.dist %>/*.css'], dest: '/' } - ] + version: '<%= pkg.version %>' + } } }, - uglify: { - options: { - preserveComments: 'some', - report: 'gzip' - }, - all: { + + less: { + default: { files: { - '<%= pkg.folders.dist %>/<%= pkg.namespace %>.min.js': ['<%= pkg.folders.dist %>/<%= pkg.namespace %>.js'] + "<%= folders.dist %>/<%= pkg.namespace %>.css": "<%= folders.src %>/<%= pkg.namespace %>.less" } } }, - less: { - development: { + concat: { + 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', + footer: '\r\n})(jQuery, window);', + process: function(src, filepath) + { + var result = src.trim().replace(/(.+?\r\n)/gm, ' $1'), + end = [0, ""], + lastChar = result[result.length - 1]; + + if (lastChar === ";") + { + end = (result[result.length - 2] === ")") ? + (result[result.length - 2] === "}") ? + [3, " });"] : [2, ");"] : [2, " };"]; + } + else if (lastChar === "}") + { + end = [1, " }"]; + } + + return result.substr(0, result.length - end[0]) + end[1]; + } + }, + files: { + '<%= folders.dist %>/<%= pkg.namespace %>.js': [ + '<%= folders.src %>/internal.js', + '<%= folders.src %>/public.js', + '<%= folders.src %>/extensions.js', + '<%= folders.src %>/plugin.js' + ], + '<%= folders.dist %>/<%= pkg.namespace %>.<%= fontawesome %>.js': [ + '<%= folders.src %>/fontawesome.js' + ] + } + }, + styles: { + options: { + separator: '\r\n\r\n', + banner: '<%= banner %>' + }, files: { - "<%= pkg.folders.dist %>/<%= pkg.namespace %>.css": "<%= pkg.folders.src %>/<%= pkg.namespace %>.less" + '<%= folders.dist %>/<%= pkg.namespace %>.css': [ + '<%= folders.dist %>/<%= pkg.namespace %>.css' + ] } } }, - qunit: { - files: ['test/index.html'] + + csslint: { + default: { + options: { + 'adjoining-classes': false, + 'important': false, + 'outline-none': false, + 'overqualified-elements': false + }, + src: '<%= folders.dist %>/<%= pkg.namespace %>.css' + } }, jshint: { options: { @@ -112,7 +131,7 @@ module.exports = function (grunt) console: true } }, - files: ['<%= pkg.folders.dist %>/<%= pkg.namespace %>.js'], + files: ['<%= folders.dist %>/<%= pkg.namespace %>.js'], test: { options: { globals: { @@ -146,44 +165,98 @@ module.exports = function (grunt) } } }, - yuidoc: { - compile: { - name: '<%= pkg.name %>', - description: '<%= pkg.description %>', - version: '<%= pkg.version %>', - url: '<%= pkg.homepage %>', + + cssmin: { + default: { options: { - paths: '<%= pkg.folders.dist %>', - outdir: '<%= pkg.folders.docs %>/' + report: 'gzip' + }, + files: { + '<%= folders.dist %>/<%= pkg.namespace %>.min.css': ['<%= folders.dist %>/<%= pkg.namespace %>.css'] } } }, - clean: { - api: ["<%= pkg.folders.docs %>"], - build: ["<%= pkg.folders.dist %>"] + uglify: { + default: { + options: { + preserveComments: 'some', + report: 'gzip' + }, + files: { + '<%= folders.dist %>/<%= pkg.namespace %>.min.js': [ + '<%= folders.dist %>/<%= pkg.namespace %>.js' + ], + '<%= folders.dist %>/<%= pkg.namespace %>.<%= fontawesome %>.min.js': [ + '<%= folders.dist %>/<%= pkg.namespace %>.<%= fontawesome %>.js' + ] + } + } }, - coveralls: { - options: { - src: "coverage-results/lcov.info", - force: true - }, + + nugetpack: { + default: { + src: '<%= pkg.namespace %>.nuspec', + dest: '<%= folders.dist %>', + options: { + version: '<%= pkg.version %>' + } + } + }, + compress: { + default: { + options: { + archive: '<%= folders.dist %>/<%= pkg.namespace %>-<%= pkg.version %>.zip' + }, + files: [ + { + flatten: true, + expand: true, + src: ['<%= folders.dist %>/*.js', '<%= folders.dist %>/*.css'], dest: '/' + } + ] + } + }, + + qunit: { + files: ['test/index.html'] + }, + + exec: { + publish: { + cmd: 'npm publish .' + } + }, + nugetpush: { + default: { + src: '<%= folders.dist %>/*.nupkg' + } } }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-less'); + grunt.loadNpmTasks('grunt-contrib-csslint'); + grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-qunit'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-yuidoc'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-compress'); - grunt.loadNpmTasks('grunt-regex-replace'); grunt.loadNpmTasks('grunt-exec'); - grunt.loadNpmTasks('grunt-coveralls'); + grunt.loadNpmTasks('grunt-nuget'); + grunt.loadNpmTasks('grunt-regex-replace'); + grunt.registerMultiTask('version', 'sets version tag', function () + { + var pkg = grunt.file.readJSON(this.data.src); + pkg["version"] = this.data.options.version; + grunt.file.write(this.data.src, JSON.stringify(pkg, null, 4)); + }); grunt.registerTask('default', ['build']); grunt.registerTask('api', ['clean:api', 'yuidoc']); - grunt.registerTask('build', ['clean:build', 'concat', 'jshint', 'qunit', 'less']); - grunt.registerTask('release', ['build', 'api', 'uglify', 'compress', 'exec:createPkg']); + grunt.registerTask('test', ['qunit']); + 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/LICENSE.txt b/LICENSE.txt index 8f7ede6..6c71285 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Rafael J. Staib +Copyright (c) 2014-2015 Rafael J. Staib Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 082e03d..9ef1416 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -jQuery Bootgrid Plugin [![Build Status](https://travis-ci.org/rstaib/jquery-bootgrid.svg?branch=master)](https://travis-ci.org/rstaib/jquery-bootgrid) [![Bower version](https://badge.fury.io/bo/jquery.bootgrid.svg)](http://badge.fury.io/bo/jquery.bootgrid) [![NuGet version](https://badge.fury.io/nu/jquery.bootgrid.svg)](http://badge.fury.io/nu/jquery.bootgrid) +jQuery Bootgrid Plugin [![Build Status](http://img.shields.io/travis/rstaib/jquery-bootgrid/master.svg?style=flat-square)](https://travis-ci.org/rstaib/jquery-bootgrid) ![Bower version](http://img.shields.io/bower/v/jquery.bootgrid.svg?style=flat-square) ![NuGet version](http://img.shields.io/nuget/v/jquery.bootgrid.svg?style=flat-square) ![NPM version](http://img.shields.io/npm/v/jquery-bootgrid.svg?style=flat-square) ![Gratipay](http://img.shields.io/gratipay/RafaelStaib.svg?style=flat-square) ============ Nice, sleek and intuitive. A grid control especially designed for bootstrap. @@ -59,4 +59,4 @@ Instructions will follow soon! ## License -Copyright (c) 2013 Rafael J. Staib Licensed under the [MIT license](https://github.com/rstaib/jquery-bootgrid/blob/master/LICENSE.txt). +Copyright (c) 2014-2015 Rafael J. Staib Licensed under the [MIT license](https://github.com/rstaib/jquery-bootgrid/blob/master/LICENSE.txt). diff --git a/bootgrid.jquery.json b/bootgrid.jquery.json deleted file mode 100644 index c8f2dd8..0000000 --- a/bootgrid.jquery.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "bootgrid", - "title": "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.1.0", - "author": { - "name": "Rafael Staib", - "email": "me@rafaelstaib.com", - "url": "http://www.rafaelstaib.com" - }, - "licenses": [ - { "type": "MIT", "url": "http://www.opensource.org/licenses/MIT" } - ], - "bugs": "https://github.com/rstaib/jquery-bootgrid/issues", - "homepage": "http://www.jquery-bootgrid.com", - "docs": "https://github.com/rstaib/jquery-bootgrid/wiki", - "download": "http://www.jquery-bootgrid.com", - "demo": "http://www.jquery-bootgrid.com/Examples", - "dependencies": { - "jquery": ">=1.9.0" - } -} diff --git a/bower.json b/bower.json index d4cca30..856e916 100644 --- a/bower.json +++ b/bower.json @@ -13,9 +13,13 @@ "accessibility", "bootstrap" ], - "version": "1.1.0", + "version": "1.3.1", "authors": [ - { "name": "Rafael Staib", "email": "me@rafaelstaib.com", "url": "http://www.rafaelstaib.com" } + { + "name": "Rafael Staib", + "email": "me@rafaelstaib.com", + "url": "http://www.rafaelstaib.com" + } ], "homepage": "http://www.jquery-bootgrid.com", "repository": { @@ -24,25 +28,27 @@ }, "license": "MIT", "main": [ - "build/jquery.bootgrid.js", - "demo/css/jquery.bootgrid.css" + "dist/jquery.bootgrid.js", + "dist/jquery.bootgrid.css" ], "ignore": [ - "**/.*", - "build/*.nupkg", - "build/*.zip", + "build", + "demo", + "dist/*.nupkg", + "dist/*.zip", + "docs", "lib", - "nuget", - "src", "test", - "*.md", - "*.txt", + "*.nuspec", + ".gitattributes", + ".gitignore", + ".travis.yml", + "bootgrid.jquery.json", "Gruntfile.js", - "package.json", - "bootgrid.jquery.json" + "package.json" ], "dependencies": { "jquery": ">=1.9.0", "bootstrap": ">=3.1.1" } -} +} \ No newline at end of file diff --git a/build/jQuery.Bootgrid.1.1.0.nupkg b/build/jQuery.Bootgrid.1.1.0.nupkg deleted file mode 100644 index 8ef8c36..0000000 Binary files a/build/jQuery.Bootgrid.1.1.0.nupkg and /dev/null differ diff --git a/build/jquery.bootgrid-1.1.0.zip b/build/jquery.bootgrid-1.1.0.zip deleted file mode 100644 index db93ca6..0000000 Binary files a/build/jquery.bootgrid-1.1.0.zip and /dev/null differ diff --git a/build/jquery.bootgrid.min.js b/build/jquery.bootgrid.min.js deleted file mode 100644 index 3750183..0000000 --- a/build/jquery.bootgrid.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * jQuery Bootgrid v1.1.0 - 09/12/2014 - * Copyright (c) 2014 Rafael Staib (http://www.jquery-bootgrid.com) - * Licensed under MIT http://www.opensource.org/licenses/MIT - */ -!function(a,b){"use strict";function c(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 d(b){return b?a.extend({},this.cachedParams,{ctx:b}):this.cachedParams}function e(){var b={current:this.current,rowCount:this.rowCount,sort:this.sort,searchPhrase:this.searchPhrase},c=this.options.post;return c=a.isFunction(c)?c():c,this.options.requestHandler(a.extend(!0,b,c))}function f(b){return"."+a.trim(b).replace(/\s+/gm,".")}function g(){var b=this.options.url;return a.isFunction(b)?b():b}function h(){this.element.trigger("initialize"+C),k.call(this),m.call(this),n.call(this),y.call(this),x.call(this),o.call(this),l.call(this),this.element.trigger("initialized"+C)}function i(){this.options.highlightRows}function j(a){return a.visible}function k(){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)};b.columns.push(f),null!=f.order&&(b.sort[f.id]=f.order),f.identifier&&(b.identifier=f.id,b.converter=f.converter),b.options.multiSort||null===f.order||(d=!0)})}function l(){function c(a){for(var b,c=new RegExp(f.searchPhrase,f.options.caseSensitive?"g":"gi"),d=0;d-1)return!0;return!1}function d(a,b){f.currentRows=a,f.total=b,f.totalPages=Math.ceil(b/f.rowCount),f.options.keepSelection||(f.selectedRows=[]),v.call(f,a),q.call(f),s.call(f),f.element._bgBusyAria(!1).trigger("loaded"+C)}var f=this,h=e.call(this),i=g.call(this);if(this.options.ajax&&(null==i||"string"!=typeof i||0===i.length))throw new Error("Url setting must be a none empty string or a function that returns one.");if(this.element._bgBusyAria(!0).trigger("load"+C),A.call(this),this.options.ajax)f.xqr&&f.xqr.abort(),f.xqr=a.post(i,h,function(b){f.xqr=null,"string"==typeof b&&(b=a.parseJSON(b)),b=f.options.responseHandler(b),f.current=b.current,d(b.rows,b.total)}).fail(function(a,b){f.xqr=null,"abort"!==b&&(r.call(f),f.element._bgBusyAria(!1).trigger("loaded"+C))});else{var j=this.searchPhrase.length>0?this.rows.where(c):this.rows,k=j.length;-1!==this.rowCount&&(j=j.page(this.current,this.rowCount)),b.setTimeout(function(){d(j,k)},10)}}function m(){if(!this.options.ajax){var b=this,d=this.element.find("tbody > tr");d.each(function(){var d=a(this),e=d.children("td"),f={};a.each(b.columns,function(a,b){f[b.id]=b.converter.from(e.eq(a).text())}),c.call(b,f)}),this.total=this.rows.length,this.totalPages=-1===this.rowCount?1:Math.ceil(this.total/this.rowCount),B.call(this)}}function n(){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(d.call(this,{id:this.element._bgId()+"-header"}))),c.before(this.header)),2&this.options.navigation&&(this.footer=a(b.footer.resolve(d.call(this,{id:this.element._bgId()+"-footer"}))),c.after(this.footer))}function o(){if(0!==this.options.navigation){var b=this.options.css,c=f(b.actions),e=this.header.find(c),g=this.footer.find(c);if(e.length+g.length>0){var h=this,i=this.options.templates,j=a(i.actions.resolve(d.call(this)));if(this.options.ajax){var k=i.icon.resolve(d.call(this,{iconCss:b.iconRefresh})),m=a(i.actionButton.resolve(d.call(this,{content:k,text:this.options.labels.refresh}))).on("click"+C,function(a){a.stopPropagation(),h.current=1,l.call(h)});j.append(m)}u.call(this,j),p.call(this,j),z.call(this,e,j,1),z.call(this,g,j,2)}}}function p(b){if(this.options.columnSelection&&this.columns.length>1){var c=this,e=this.options.css,g=this.options.templates,h=g.icon.resolve(d.call(this,{iconCss:e.iconColumns})),i=a(g.actionDropDown.resolve(d.call(this,{content:h}))),k=f(e.dropDownItem),m=f(e.dropDownItemCheckbox),n=f(e.dropDownMenuItems);a.each(this.columns,function(b,h){var o=a(g.actionDropDownCheckboxItem.resolve(d.call(c,{name:h.id,label:h.text,checked:h.visible}))).on("click"+C,k,function(b){b.stopPropagation();var d=a(this),e=d.find(m);if(!e.prop("disabled")){h.visible=e.prop("checked");var f=c.columns.where(j).length>1;d.parents(n).find(k+":has("+m+":checked)")._bgEnableAria(f).find(m)._bgEnableField(f),c.element.find("tbody").empty(),y.call(c),l.call(c)}});i.find(f(e.dropDownMenuItems)).append(o)}),b.append(i)}}function q(){if(0!==this.options.navigation){var b=f(this.options.css.infos),c=this.header.find(b),e=this.footer.find(b);if(c.length+e.length>0){var g=this.current*this.rowCount,h=a(this.options.templates.infos.resolve(d.call(this,{end:0===this.total||-1===g||g>this.total?this.total:g,start:0===this.total?0:g-this.rowCount+1,total:this.total})));z.call(this,c,h,1),z.call(this,e,h,2)}}}function r(){var a=this.element.children("tbody").first(),b=this.options.templates,c=this.columns.where(j).length;this.options.selection&&null!=this.identifier&&(c+=1),a.html(b.noResults.resolve(d.call(this,{columns:c})))}function s(){if(0!==this.options.navigation){var b=f(this.options.css.pagination),c=this.header.find(b)._bgShowAria(-1!==this.rowCount),e=this.footer.find(b)._bgShowAria(-1!==this.rowCount);if(-1!==this.rowCount&&c.length+e.length>0){var g=this.options.templates,h=this.current,i=this.totalPages,j=a(g.pagination.resolve(d.call(this))),k=i-h,l=-1*(this.options.padding-h),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;t.call(this,j,"first","«","first")._bgEnableAria(h>1),t.call(this,j,"prev","<","prev")._bgEnableAria(h>1);for(var p=0;o>p;p++){var q=p+m;t.call(this,j,q,q,"page-"+q)._bgEnableAria()._bgSelectAria(q===h)}0===o&&t.call(this,j,1,1,"page-1")._bgEnableAria(!1)._bgSelectAria(),t.call(this,j,"next",">","next")._bgEnableAria(i>h),t.call(this,j,"last","»","last")._bgEnableAria(i>h),z.call(this,c,j,1),z.call(this,e,j,2)}}}function t(b,c,e,g){var h=this,i=this.options.templates,j=this.options.css,k=d.call(this,{css:g,text:e,uri:"#"+c}),m=a(i.paginationItem.resolve(k)).on("click"+C,f(j.paginationButton),function(b){b.stopPropagation();var c=a(this),d=c.parent();if(!d.hasClass("active")&&!d.hasClass("disabled")){var e={first:1,prev:h.current-1,next:h.current+1,last:h.totalPages},f=c.attr("href").substr(1);h.current=e[f]||+f,l.call(h)}c.trigger("blur")});return b.append(m),m}function u(b){function c(a){return-1===a?e.options.labels.all:a}var e=this,g=this.options.rowCount;if(a.isArray(g)){var h=this.options.css,i=this.options.templates,j=a(i.actionDropDown.resolve(d.call(this,{content:this.rowCount}))),k=f(h.dropDownMenu),m=f(h.dropDownMenuText),n=f(h.dropDownMenuItems),o=f(h.dropDownItemButton);a.each(g,function(b,f){var g=a(i.actionDropDownItem.resolve(d.call(e,{text:c(f),uri:"#"+f})))._bgSelectAria(f===e.rowCount).on("click"+C,o,function(b){b.preventDefault();var d=a(this),f=+d.attr("href").substr(1);f!==e.rowCount&&(e.current=1,e.rowCount=f,d.parents(n).children().each(function(){var b=a(this),c=+b.find(o).attr("href").substr(1);b._bgSelectAria(c===f)}),d.parents(k).find(m).text(c(f)),l.call(e))});j.find(n).append(g)}),b.append(j)}}function v(b){if(b.length>0){var c=this,e=this.options.css,g=this.options.templates,h=this.element.children("tbody").first(),i=this.options.selection&&null!=this.identifier,j=!0,k="",l="",m="",n="";a.each(b,function(b,f){if(l="",m=' data-row-id="'+(null==c.identifier?b:f[c.identifier])+'"',n="",i){var h=-1!==a.inArray(f[c.identifier],c.selectedRows),o=g.select.resolve(d.call(c,{type:"checkbox",value:f[c.identifier],checked:h}));l+=g.cell.resolve(d.call(c,{content:o,css:e.selectCell})),j=j&&h,h&&(n+=e.selected,m+=' aria-selected="true"')}a.each(c.columns,function(b,h){if(h.visible){var i=a.isFunction(h.formatter)?h.formatter.call(c,h,f):h.converter.to(f[h.id]),j=h.cssClass.length>0?" "+h.cssClass:"";l+=g.cell.resolve(d.call(c,{content:null==i||""===i?" ":i,css:("right"===h.align?e.right:"center"===h.align?e.center:e.left)+j}))}}),n.length>0&&(m+=' class="'+n+'"'),k+=g.row.resolve(d.call(c,{attr:m,cells:l}))}),c.element.find("thead "+f(c.options.css.selectBox)).prop("checked",j),h.html(k),w.call(this,h)}else r.call(this)}function w(b){var c=this,d=this.options.selection&&null!=this.identifier,e=f(this.options.css.selectBox);d&&b.off("click"+C,e).on("click"+C,e,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"+C,"> tr").on("click"+C,"> tr",function(b){b.stopPropagation();var e=a(this),f=c.converter.from(e.data("row-id")),g=null==this.identifier?c.currentRows[f]:c.currentRows.first(function(a){return a[c.identifier]===f});d&&c.options.rowSelect&&(e.hasClass(c.options.css.selected)?c.deselect([f]):c.select([f])),c.element.trigger("click"+C,[c.columns,g])})}function x(){if(0!==this.options.navigation){var c=this.options.css,e=f(c.search),g=this.header.find(e),h=this.footer.find(e);if(g.length+h.length>0){var i=this,j=this.options.templates,k=null,l="",m=f(c.searchField),n=a(j.search.resolve(d.call(this))),o=n.is(m)?n:n.find(m);o.on("keyup"+C,function(c){c.stopPropagation();var d=a(this).val();l!==d&&(l=d,b.clearTimeout(k),k=b.setTimeout(function(){i.search(d)},250))}),z.call(this,g,n,1),z.call(this,h,n,2)}}}function y(){var b=this,c=this.element.find("thead > tr"),e=this.options.css,g=this.options.templates,h="",i=this.options.sorting,j=this.options.selection&&null!=this.identifier;if(j){var k=this.options.multiSelect?g.select.resolve(d.call(b,{type:"checkbox",value:"all"})):"";h+=g.rawHeaderCell.resolve(d.call(b,{content:k,css:e.selectCell}))}if(a.each(this.columns,function(a,c){if(c.visible){var f=b.sort[c.id],j=i&&f&&"asc"===f?e.iconUp:i&&f&&"desc"===f?e.iconDown:"",k=g.icon.resolve(d.call(b,{iconCss:j})),l=c.headerAlign,m=c.headerCssClass.length>0?" "+c.headerCssClass:"";h+=g.headerCell.resolve(d.call(b,{column:c,icon:k,sortable:i&&c.sortable&&e.sortable||"",css:("right"===l?e.right:"center"===l?e.center:e.left)+m}))}}),c.html(h),i){var m=f(e.sortable),n=f(e.icon);c.off("click"+C,m).on("click"+C,m,function(c){c.preventDefault();var d=a(this),f=d.data("column-id")||d.parents("th").first().data("column-id"),g=b.sort[f],h=d.find(n);if(b.options.multiSort||(d.parents("tr").first().find(n).removeClass(e.iconDown+" "+e.iconUp),b.sort={}),g&&"asc"===g)b.sort[f]="desc",h.removeClass(e.iconUp).addClass(e.iconDown);else if(g&&"desc"===g)if(b.options.multiSort){var i={};for(var j in b.sort)j!==f&&(i[j]=b.sort[j]);b.sort=i,h.removeClass(e.iconDown)}else b.sort[f]="asc",h.removeClass(e.iconDown).addClass(e.iconUp);else b.sort[f]="asc",h.addClass(e.iconUp);B.call(b),l.call(b)})}if(j&&this.options.multiSelect){var o=f(e.selectBox);c.off("click"+C,o).on("click"+C,o,function(c){c.stopPropagation(),a(this).prop("checked")?b.select():b.deselect()})}}function z(b,c,d){this.options.navigation&d&&b.each(function(b,d){a(d).before(c.clone(!0)).remove()})}function A(){var a=this.options.templates,b=this.element.children("thead").first(),c=this.element.children("tbody").first(),e=c.find("tr > td").first(),f=this.element.height()-b.height()-(e.height()+20),g=this.columns.where(j).length;this.options.selection&&null!=this.identifier&&(g+=1),c.html(a.loading.resolve(d.call(this,{columns:g}))),-1!==this.rowCount&&f>0&&c.find("tr > td").css("padding","20px 0 "+f+"px")}function B(){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.sort)(this.options.multiSort||0===b.length)&&b.push({id:c,order:this.sort[c]});b.length>0&&this.rows.sort(a)}}var C=".rs.jquery.bootgrid",D=function(b,c){this.element=a(b),this.options=a.extend(!0,{},D.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.converter=null,this.rowCount=a.isArray(d)?d[0]:d,this.rows=[],this.searchPhrase="",this.selectedRows=[],this.sort={},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(D.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,ajax:!1,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",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"},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:''}},D.prototype.append=function(a){if(this.options.ajax);else{for(var b=[],d=0;d0&&(this.options.multiSelect||1!==e.length);)if(c=b.pop(),-1===a.inArray(c,this.selectedRows))for(d=0;d0){var g=f(this.options.css.selectBox),h=this.selectedRows.length>=this.currentRows.length;for(d=0;!this.options.keepSelection&&h&&d tr "+g+":checked").trigger("click"+C),d=0;d tr[data-row-id="'+this.selectedRows[d]+'"]').addClass(this.options.css.selected)._bgAria("selected","true").find(g).prop("checked",!0);this.element.trigger("selected"+C,[e])}}return this},D.prototype.deselect=function(b){if(this.options.selection&&null!=this.identifier){b=b||this.currentRows.propValues(this.identifier);for(var c,d,e,g=[];b.length>0;)if(c=b.pop(),e=a.inArray(c,this.selectedRows),-1!==e)for(d=0;d0){var h=f(this.options.css.selectBox);for(this.element.find("thead "+h).prop("checked",!1),d=0;d tr[data-row-id="'+g[d][this.identifier]+'"]').removeClass(this.options.css.selected)._bgAria("selected","false").find(h).prop("checked",!1);this.element.trigger("deselected"+C,[g])}}return this},D.prototype.sort=function(b){var c=b?a.extend({},b):{};return c===this.sort?this:(this.sort=c,y.call(this),B.call(this),l.call(this),this)},a.fn.extend({_bgAria:function(a,b){return this.attr("aria-"+a,b)},_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 E={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{E&&E[b]&&"function"==typeof E[b]&&(e=E[b](e)),b=c?c.join(".")+"."+b:b;var g=new RegExp("\\{\\{"+b+"\\}\\}","gm");d=d.replace(g,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 jQuery Bootgrid Demo - + @-webkit-viewport { width: device-width; } @@ -52,14 +52,25 @@ + + + + + + + + + - - + + - + + + @@ -68,66 +79,88 @@ + + + + + + + + + + + + + + + + + + + + + +
      IDSenderIDSender ReceivedLinkLinkStatusHidden
      me@rafaelstaib.com 11.12.2014 Link999Hidden value 1
      2 me@rafaelstaib.com 12.12.2014 Link999Hidden value 1
      3 me@rafaelstaib.com 10.12.2014 Link2Hidden value 1
      4 mo@rafaelstaib.com 12.08.2014 Link999Hidden value 1
      5 ma@rafaelstaib.com 12.06.2014 Link3Hidden value 1
      6 me@rafaelstaib.com 12.12.2014 Link999Hidden value 1
      7 ma@rafaelstaib.com 12.11.2014 Link999Hidden value 1
      8 mo@rafaelstaib.com 15.12.2014 Link999Hidden value 1
      9 me@rafaelstaib.com 24.12.2014 Link0Hidden value 1
      10 ma@rafaelstaib.com 14.12.2014 Link1Hidden value 1
      11 mo@rafaelstaib.com 12.12.2014 Link999Hidden value 1
      @@ -137,23 +170,30 @@
      - © Copyright 2014, Rafael Staib + © Copyright 2014-2015, Rafael Staib
      - + + diff --git a/demo/send.htm b/demo/send.htm index 7d24658..1299d18 100644 --- a/demo/send.htm +++ b/demo/send.htm @@ -5,7 +5,7 @@ jQuery Bootgrid Demo - + @-webkit-viewport { @@ -132,12 +132,12 @@
      - © Copyright 2014, Rafael Staib + © Copyright 2014-2015, Rafael Staib
      - + $(function () { diff --git a/dist/jQuery.Bootgrid.1.3.1.nupkg b/dist/jQuery.Bootgrid.1.3.1.nupkg new file mode 100644 index 0000000..c430a89 Binary files /dev/null and b/dist/jQuery.Bootgrid.1.3.1.nupkg differ diff --git a/dist/jquery.bootgrid-1.3.1.zip b/dist/jquery.bootgrid-1.3.1.zip new file mode 100644 index 0000000..61cbe12 Binary files /dev/null and b/dist/jquery.bootgrid-1.3.1.zip differ diff --git a/build/jquery.bootgrid.css b/dist/jquery.bootgrid.css similarity index 93% rename from build/jquery.bootgrid.css rename to dist/jquery.bootgrid.css index 652d291..13512d9 100644 --- a/build/jquery.bootgrid.css +++ b/dist/jquery.bootgrid.css @@ -1,3 +1,8 @@ +/*! + * 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-header, .bootgrid-footer { margin: 15px 0; @@ -17,6 +22,10 @@ .bootgrid-footer .search .glyphicon { top: 0; } +.bootgrid-header .search .fa, +.bootgrid-footer .search .fa { + display: table-cell; +} .bootgrid-header .search.search-field::-ms-clear, .bootgrid-footer .search.search-field::-ms-clear, .bootgrid-header .search .search-field::-ms-clear, diff --git a/dist/jquery.bootgrid.fa.js b/dist/jquery.bootgrid.fa.js new file mode 100644 index 0000000..c120aa0 --- /dev/null +++ b/dist/jquery.bootgrid.fa.js @@ -0,0 +1,19 @@ +/*! + * 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 ($, window, undefined) +{ + /*jshint validthis: true */ + "use strict"; + + $.extend($.fn.bootgrid.Constructor.defaults.css, { + icon: "icon fa", + iconColumns: "fa-th-list", + iconDown: "fa-sort-desc", + iconRefresh: "fa-refresh", + iconSearch: "fa-search", + iconUp: "fa-sort-asc" +}); +})(jQuery, window); \ No newline at end of file diff --git a/dist/jquery.bootgrid.fa.min.js b/dist/jquery.bootgrid.fa.min.js new file mode 100644 index 0000000..5e78cfb --- /dev/null +++ b/dist/jquery.bootgrid.fa.min.js @@ -0,0 +1,6 @@ +/*! + * 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";a.extend(a.fn.bootgrid.Constructor.defaults.css,{icon:"icon fa",iconColumns:"fa-th-list",iconDown:"fa-sort-desc",iconRefresh:"fa-refresh",iconSearch:"fa-search",iconUp:"fa-sort-asc"})}(jQuery,window); \ No newline at end of file diff --git a/build/jquery.bootgrid.js b/dist/jquery.bootgrid.js similarity index 71% rename from build/jquery.bootgrid.js rename to dist/jquery.bootgrid.js index 54b1058..4f27d44 100644 --- a/build/jquery.bootgrid.js +++ b/dist/jquery.bootgrid.js @@ -1,6 +1,6 @@ /*! - * jQuery Bootgrid v1.1.0 - 09/12/2014 - * Copyright (c) 2014 Rafael Staib (http://www.jquery-bootgrid.com) + * 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 ($, window, undefined) @@ -34,9 +34,16 @@ 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 }) : + return (context) ? $.extend({}, this.cachedParams, { ctx: context }) : this.cachedParams; } @@ -45,7 +52,7 @@ var request = { current: this.current, rowCount: this.rowCount, - sort: this.sort, + sort: this.sortDictionary, searchPhrase: this.searchPhrase }, post = this.options.post; @@ -70,6 +77,7 @@ 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); @@ -117,12 +125,15 @@ 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 + 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.sort[column.id] = column.order; + that.sortDictionary[column.id] = column.order; } // Prevents multiple identifiers @@ -153,14 +164,7 @@ function loadData() { - var that = this, - request = getRequest.call(this), - url = getUrl.call(this); - - if (this.options.ajax && (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."); - } + var that = this; this.element._bgBusyAria(true).trigger("load" + namespace); showLoading.call(this); @@ -173,7 +177,7 @@ for (var i = 0; i < that.columns.length; i++) { column = that.columns[i]; - if (column.searchable && column.visible && + if (column.searchable && column.visible && column.converter.to(row[column.id]).search(searchPattern) > -1) { return true; @@ -186,8 +190,7 @@ function update(rows, total) { that.currentRows = rows; - that.total = total; - that.totalPages = Math.ceil(total / that.rowCount); + setTotals.call(that, total); if (!that.options.keepSelection) { @@ -203,35 +206,51 @@ if (this.options.ajax) { - // aborts the previous ajax request if not already finished or failed - if (that.xqr) + var request = getRequest.call(this), + url = getUrl.call(this); + + if (url == null || typeof url !== "string" || url.length === 0) { - that.xqr.abort(); + throw new Error("Url setting must be a none empty string or a function that returns one."); } - that.xqr = $.post(url, request, function (response) + // aborts the previous ajax request if not already finished or failed + if (this.xqr) { - that.xqr = null; + this.xqr.abort(); + } - if (typeof (response) === "string") + var settings = { + url: url, + data: request, + success: function(response) { - response = $.parseJSON(response); - } + that.xqr = null; - response = that.options.responseHandler(response); + if (typeof (response) === "string") + { + response = $.parseJSON(response); + } - that.current = response.current; - update(response.rows, response.total); - }).fail(function (jqXHR, textStatus, errorThrown) - { - that.xqr = null; + response = that.options.responseHandler(response); - if (textStatus !== "abort") + that.current = response.current; + update(response.rows, response.total); + }, + error: function (jqXHR, textStatus, errorThrown) { - renderNoResultsRow.call(that); // overrides loading mask - that.element._bgBusyAria(false).trigger("loaded" + namespace); + 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 { @@ -269,18 +288,22 @@ appendRow.call(that, row); }); - this.total = this.rows.length; - this.totalPages = (this.rowCount === -1) ? 1 : - Math.ceil(this.total / this.rowCount); - + 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)) ? + wrapper = (this.element.parent().hasClass(this.options.css.responsiveTable)) ? this.element.parent() : this.element; this.element.addClass(this.options.css.table); @@ -310,10 +333,9 @@ { var css = this.options.css, selector = getCssSelector(css.actions), - headerActions = this.header.find(selector), - footerActions = this.footer.find(selector); + actionItems = findFooterAndHeaderItems.call(this, selector); - if ((headerActions.length + footerActions.length) > 0) + if (actionItems.length > 0) { var that = this, tpl = this.options.templates, @@ -341,8 +363,7 @@ // Column selection renderColumnSelection.call(this, actions); - replacePlaceHolder.call(this, headerActions, actions, 1); - replacePlaceHolder.call(this, footerActions, actions, 2); + replacePlaceHolder.call(this, actionItems, actions); } } } @@ -362,27 +383,30 @@ $.each(this.columns, function (i, column) { - 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")) + 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) { - 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); + 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); } @@ -393,10 +417,9 @@ if (this.options.navigation !== 0) { var selector = getCssSelector(this.options.css.infos), - headerInfos = this.header.find(selector), - footerInfos = this.footer.find(selector); + infoItems = findFooterAndHeaderItems.call(this, selector); - if ((headerInfos.length + footerInfos.length) > 0) + if (infoItems.length > 0) { var end = (this.current * this.rowCount), infos = $(this.options.templates.infos.resolve(getParams.call(this, { @@ -405,8 +428,7 @@ total: this.total }))); - replacePlaceHolder.call(this, headerInfos, infos, 1); - replacePlaceHolder.call(this, footerInfos, infos, 2); + replacePlaceHolder.call(this, infoItems, infos); } } } @@ -417,7 +439,7 @@ tpl = this.options.templates, count = this.columns.where(isVisible).length; - if (this.options.selection && this.identifier != null) + if (this.selection) { count = count + 1; } @@ -429,10 +451,9 @@ if (this.options.navigation !== 0) { var selector = getCssSelector(this.options.css.pagination), - headerPagination = this.header.find(selector)._bgShowAria(this.rowCount !== -1), - footerPagination = this.footer.find(selector)._bgShowAria(this.rowCount !== -1); + paginationItems = findFooterAndHeaderItems.call(this, selector)._bgShowAria(this.rowCount !== -1); - if (this.rowCount !== -1 && (headerPagination.length + footerPagination.length) > 0) + if (this.rowCount !== -1 && paginationItems.length > 0) { var tpl = this.options.templates, current = this.current, @@ -469,22 +490,22 @@ renderPaginationItem.call(this, pagination, "last", "»", "last") ._bgEnableAria(totalPages > current); - replacePlaceHolder.call(this, headerPagination, pagination, 1); - replacePlaceHolder.call(this, footerPagination, pagination, 2); + replacePlaceHolder.call(this, paginationItems, pagination); } } } - function renderPaginationItem(list, uri, text, markerCss) + 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, uri: "#" + uri }), + 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(); @@ -496,8 +517,8 @@ next: that.current + 1, last: that.totalPages }; - var command = $this.attr("href").substr(1); - that.current = commandList[command] || +command; // + converts string to int + var command = $this.data("page"); + that.current = commandList[command] || command; loadData.call(that); } $this.trigger("blur"); @@ -521,7 +542,7 @@ { var css = this.options.css, tpl = this.options.templates, - dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: this.rowCount }))), + dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: getText(this.rowCount) }))), menuSelector = getCssSelector(css.dropDownMenu), menuTextSelector = getCssSelector(css.dropDownMenuText), menuItemsSelector = getCssSelector(css.dropDownMenuItems), @@ -530,14 +551,14 @@ $.each(rowCountList, function (index, value) { var item = $(tpl.actionDropDownItem.resolve(getParams.call(that, - { text: getText(value), uri: "#" + value }))) + { text: getText(value), action: value }))) ._bgSelectAria(value === that.rowCount) .on("click" + namespace, menuItemSelector, function (e) { e.preventDefault(); var $this = $(this), - newRowCount = +$this.attr("href").substr(1); + newRowCount = $this.data("action"); if (newRowCount !== that.rowCount) { // todo: sophisticated solution needed for calculating which page is selected @@ -546,7 +567,7 @@ $this.parents(menuItemsSelector).children().each(function () { var $item = $(this), - currentRowCount = +$item.find(menuItemSelector).attr("href").substr(1); + currentRowCount = $item.find(menuItemSelector).data("action"); $item._bgSelectAria(currentRowCount === newRowCount); }); $this.parents(menuSelector).find(menuTextSelector).text(getText(newRowCount)); @@ -567,23 +588,19 @@ css = this.options.css, tpl = this.options.templates, tbody = this.element.children("tbody").first(), - selection = this.options.selection && this.identifier != null, allRowsSelected = true, - html = "", - cells = "", - rowAttr = "", - rowCss = ""; + html = ""; - $.each(rows, function (i, row) + $.each(rows, function (index, row) { - cells = ""; - rowAttr = " data-row-id=\"" + ((that.identifier == null) ? i : row[that.identifier]) + "\""; - rowCss = ""; + var cells = "", + rowAttr = " data-row-id=\"" + ((that.identifier == null) ? index : row[that.identifier]) + "\"", + rowCss = ""; - if (selection) + if (that.selection) { var selected = ($.inArray(row[that.identifier], that.selectedRows) !== -1), - selectBox = tpl.select.resolve(getParams.call(that, + 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); @@ -594,18 +611,25 @@ } } + 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) : + 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 })); + css: ((column.align === "right") ? css.right : (column.align === "center") ? + css.center : css.left) + cssClass, + style: (column.width == null) ? "" : "width:" + column.width + ";" })); } }); @@ -633,10 +657,9 @@ function registerRowEvents(tbody) { var that = this, - selection = this.options.selection && this.identifier != null, selectBoxSelector = getCssSelector(this.options.css.selectBox); - if (selection) + if (this.selection) { tbody.off("click" + namespace, selectBoxSelector) .on("click" + namespace, selectBoxSelector, function(e) @@ -663,11 +686,12 @@ e.stopPropagation(); var $this = $(this), - id = that.converter.from($this.data("row-id")), - row = (this.identifier == null) ? that.currentRows[id] : + 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 (selection && that.options.rowSelect) + if (that.selection && that.options.rowSelect) { if ($this.hasClass(that.options.css.selected)) { @@ -689,10 +713,9 @@ { var css = this.options.css, selector = getCssSelector(css.search), - headerSearch = this.header.find(selector), - footerSearch = this.footer.find(selector); + searchItems = findFooterAndHeaderItems.call(this, selector); - if ((headerSearch.length + footerSearch.length) > 0) + if (searchItems.length > 0) { var that = this, tpl = this.options.templates, @@ -707,23 +730,35 @@ { e.stopPropagation(); var newValue = $(this).val(); - if (currentValue !== newValue) + if (currentValue !== newValue || (e.which === 13 && newValue !== "")) { currentValue = newValue; - window.clearTimeout(timer); - timer = window.setTimeout(function () + if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) { - that.search(newValue); - }, 250); + window.clearTimeout(timer); + timer = window.setTimeout(function () + { + executeSearch.call(that, newValue); + }, that.options.searchSettings.delay); + } } }); - replacePlaceHolder.call(this, headerSearch, search, 1); - replacePlaceHolder.call(this, footerSearch, search, 2); + 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, @@ -731,14 +766,13 @@ css = this.options.css, tpl = this.options.templates, html = "", - sorting = this.options.sorting, - selection = this.options.selection && this.identifier != null; + sorting = this.options.sorting; - if (selection) + if (this.selection) { - var selectBox = (this.options.multiSelect) ? + var selectBox = (this.options.multiSelect) ? tpl.select.resolve(getParams.call(that, { type: "checkbox", value: "all" })) : ""; - html += tpl.rawHeaderCell.resolve(getParams.call(that, { content: selectBox, + html += tpl.rawHeaderCell.resolve(getParams.call(that, { content: selectBox, css: css.selectCell })); } @@ -746,7 +780,7 @@ { if (column.visible) { - var sortOrder = that.sort[column.id], + 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 })), @@ -754,72 +788,30 @@ 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 })); + css: ((align === "right") ? css.right : (align === "center") ? + css.center : css.left) + cssClass, + style: (column.width == null) ? "" : "width:" + column.width + ";" })); } }); headerRow.html(html); - // todo: create a own function for that piece of code if (sorting) { - var sortingSelector = getCssSelector(css.sortable), - iconSelector = getCssSelector(css.icon); + var sortingSelector = getCssSelector(css.sortable); headerRow.off("click" + namespace, sortingSelector) .on("click" + namespace, sortingSelector, function (e) { e.preventDefault(); - var $this = $(this), - columnId = $this.data("column-id") || $this.parents("th").first().data("column-id"), - sortOrder = that.sort[columnId], - icon = $this.find(iconSelector); - - if (!that.options.multiSort) - { - $this.parents("tr").first().find(iconSelector).removeClass(css.iconDown + " " + css.iconUp); - that.sort = {}; - } - - if (sortOrder && sortOrder === "asc") - { - that.sort[columnId] = "desc"; - icon.removeClass(css.iconUp).addClass(css.iconDown); - } - else if (sortOrder && sortOrder === "desc") - { - if (that.options.multiSort) - { - var newSort = {}; - for (var key in that.sort) - { - if (key !== columnId) - { - newSort[key] = that.sort[key]; - } - } - that.sort = newSort; - icon.removeClass(css.iconDown); - } - else - { - that.sort[columnId] = "asc"; - icon.removeClass(css.iconDown).addClass(css.iconUp); - } - } - else - { - that.sort[columnId] = "asc"; - icon.addClass(css.iconUp); - } + setTableHeaderSortDirection.call(that, $(this)); sortRows.call(that); loadData.call(that); }); } // todo: create a own function for that piece of code - if (selection && this.options.multiSelect) + if (this.selection && this.options.multiSelect) { var selectBoxSelector = getCssSelector(css.selectBox); headerRow.off("click" + namespace, selectBoxSelector) @@ -839,36 +831,88 @@ } } - function replacePlaceHolder(placeholder, element, flag) + function setTableHeaderSortDirection(element) { - if (this.options.navigation & flag) + 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") { - placeholder.each(function (index, item) + if (this.options.multiSort) { - // todo: check how append is implemented. Perhaps cloning here is superfluous. - $(item).before(element.clone(true)).remove(); - }); + 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 tpl = this.options.templates, - thead = this.element.children("thead").first(), - tbody = this.element.children("tbody").first(), - firstCell = tbody.find("tr > td").first(), - padding = (this.element.height() - thead.height()) - (firstCell.height() + 20), - count = this.columns.where(isVisible).length; + var that = this; - if (this.options.selection && this.identifier != null) - { - count = count + 1; - } - tbody.html(tpl.loading.resolve(getParams.call(this, { columns: count }))); - if (this.rowCount !== -1 && padding > 0) + window.setTimeout(function() { - tbody.find("tr > td").css("padding", "20px 0 " + padding + "px"); - } + 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() @@ -895,13 +939,13 @@ { var that = this; - for (var key in this.sort) + for (var key in this.sortDictionary) { if (this.options.multiSort || sortArray.length === 0) { sortArray.push({ id: key, - order: this.sort[key] + order: this.sortDictionary[key] }); } } @@ -928,6 +972,7 @@ 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; @@ -935,12 +980,13 @@ 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.sort = {}; + this.sortDictionary = {}; this.total = 0; this.totalPages = 0; this.cachedParams = { @@ -957,8 +1003,6 @@ /** * An object that represents the default settings. - * There are two ways to override the sub-properties. - * Either by doing it generally (global) or on initialization. * * @static * @class defaults @@ -1010,7 +1054,7 @@ rowSelect: false, /** - * Defines whether the row selection is saved internally on filtering, paging and sorting + * Defines whether the row selection is saved internally on filtering, paging and sorting * (even if the selected rows are not visible). * * @property keepSelection @@ -1024,10 +1068,73 @@ highlightRows: false, // highlights new rows (find the page of the first new row) sorting: true, multiSort: false, - ajax: false, // todo: find a better name for this property to differentiate between client-side and server-side data /** - * Enriches the request object with additional properties. Either a `PlainObject` or a `Function` + * 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 + }, + + /** + * 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: { + /** + * 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" + }, + + /** + * 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 @@ -1039,7 +1146,7 @@ post: {}, // or use function () { return {}; } (reserved properties are "current", "rowCount", "sort" and "searchPhrase") /** - * Sets the data URL to a data service (e.g. a REST service). Either a `String` or a `Function` + * 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 @@ -1128,6 +1235,7 @@ 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", @@ -1192,6 +1300,52 @@ search: "Search" }, + /** + * Specifies the mapping between status and contextual classes to color rows. + * + * @property statusMapping + * @type Object + * @for defaults + * @since 1.2.0 + **/ + 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" + }, + /** * Contains all templates. * @@ -1202,23 +1356,23 @@ templates: { actionButton: "", actionDropDown: "
        ", - actionDropDownItem: "
      • {{ctx.text}}
      • ", + actionDropDownItem: "
      • {{ctx.text}}
      • ", actionDropDownCheckboxItem: "
      • ", actions: "
        ", body: "", - cell: "{{ctx.content}}", + cell: "{{ctx.content}}", footer: "

        ", header: "

        ", - headerCell: "{{ctx.column.text}}{{ctx.icon}}", + headerCell: "{{ctx.column.text}}{{ctx.icon}}", icon: "", infos: "
        {{lbl.infos}}
        ", loading: "{{lbl.loading}}", noResults: "{{lbl.noResults}}", pagination: "
          ", - paginationItem: "
        • {{ctx.text}}
        • ", + paginationItem: "
        • {{ctx.text}}
        • ", rawHeaderCell: "{{ctx.content}}", // Used for the multi select box row: "{{ctx.cells}}", - search: "
          ", + search: "
          ", select: "" } }; @@ -1234,7 +1388,7 @@ { if (this.options.ajax) { - // todo: implement ajax DELETE + // todo: implement ajax PUT } else { @@ -1298,7 +1452,7 @@ { this.footer.remove(); } - this.element.remove("tbody").off(namespace).removeData(namespace); + this.element.before(this.origin).remove(); return this; }; @@ -1365,21 +1519,27 @@ }; /** - * 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 - * @param phrase {String} The phrase to search for + * @param [phrase] {String} The phrase to search for * @chainable **/ Grid.prototype.search = function(phrase) { + phrase = phrase || ""; + if (this.searchPhrase !== phrase) { - this.current = 1; - this.searchPhrase = phrase; - loadData.call(this); + var selector = getCssSelector(this.options.css.searchField), + searchFields = findFooterAndHeaderItems.call(this, selector); + searchFields.val(phrase); } + executeSearch.call(this, phrase); + + return this; }; @@ -1393,11 +1553,11 @@ **/ Grid.prototype.select = function(rowIds) { - if (this.options.selection && this.identifier != null) + if (this.selection) { rowIds = rowIds || this.currentRows.propValues(this.identifier); - var id, i, + var id, i, selectedRows = []; while (rowIds.length > 0 && !(!this.options.multiSelect && selectedRows.length === 1)) @@ -1459,7 +1619,7 @@ **/ Grid.prototype.deselect = function(rowIds) { - if (this.options.selection && this.identifier != null) + if (this.selection) { rowIds = rowIds || this.currentRows.propValues(this.identifier); @@ -1495,7 +1655,7 @@ .removeClass(this.options.css.selected)._bgAria("selected", "false") .find(selectBoxSelector).prop("checked", false); } - + this.element.trigger("deselected" + namespace, [deselectedRows]); } } @@ -1503,24 +1663,24 @@ return this; }; - /** - * Sorts rows. + * 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 dictionary which contains the sort information + * @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.sort) + + if (values === this.sortDictionary) { return this; } - this.sort = values; - + this.sortDictionary = values; renderTableHeader.call(this); sortRows.call(this); loadData.call(this); @@ -1528,13 +1688,139 @@ 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; + }; + // GRID COMMON TYPE EXTENSIONS // ============ $.fn.extend({ _bgAria: function (name, value) { - return this.attr("aria-" + name, value); + return (value) ? this.attr("aria-" + name, value) : this.attr("aria-" + name); }, _bgBusyAria: function(busy) @@ -1617,7 +1903,7 @@ } key = (prefixes) ? prefixes.join(".") + "." + key : key; var pattern = new RegExp("\\{\\{" + key + "\\}\\}", "gm"); - result = result.replace(pattern, value); + result = result.replace(pattern, (value.replace) ? value.replace(/\$/gi, "$") : value); } } }); @@ -1706,27 +1992,36 @@ $.fn.bootgrid = function (option) { - var args = Array.prototype.slice.call(arguments, 1); - return this.each(function () - { - var $this = $(this), - instance = $this.data(namespace), - options = typeof option === "object" && option; - - if (!instance && option === "destroy") - { - return; - } - if (!instance) - { - $this.data(namespace, (instance = new Grid(this, options))); - init.call(instance); - } - if (typeof option === "string") + var args = Array.prototype.slice.call(arguments, 1), + returnValue = null, + elements = this.each(function (index) { - return instance[option].apply(instance, args); - } - }); + var $this = $(this), + instance = $this.data(namespace), + options = typeof option === "object" && option; + + 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) + { + returnValue = instance[option].apply(instance, args); + } + else if (option.indexOf("get") !== 0) + { + return instance[option].apply(instance, args); + } + } + }); + return (typeof option === "string" && option.indexOf("get") === 0) ? returnValue : elements; }; $.fn.bootgrid.Constructor = Grid; diff --git a/dist/jquery.bootgrid.min.css b/dist/jquery.bootgrid.min.css new file mode 100644 index 0000000..358c1ac --- /dev/null +++ b/dist/jquery.bootgrid.min.css @@ -0,0 +1,5 @@ +/*! + * 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 new file mode 100644 index 0000000..b84a714 --- /dev/null +++ b/dist/jquery.bootgrid.min.js @@ -0,0 +1,6 @@ +/*! + * 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;cr_staib https://github.com/rstaib/jquery-bootgrid/blob/master/LICENSE.txt http://www.jquery-bootgrid.com + http://www.jquery-bootgrid.com/icon.png true Nice, sleek and intuitive. A grid control especially designed for bootstrap. Nice, sleek and intuitive. A grid control especially designed for bootstrap. - � Copyright 2014, Rafael Staib + � Copyright 2014-2015, Rafael Staib jQuery, Grid, Table, Bootstrap, Accessibility, HTML5, Sorting, Filtering, UI - + - - + + \ No newline at end of file diff --git a/nuget/NuGet.exe b/nuget/NuGet.exe deleted file mode 100644 index 63c19c9..0000000 Binary files a/nuget/NuGet.exe and /dev/null differ diff --git a/package.json b/package.json index 7ee9894..2528243 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "jquery-bootgrid", "namespace": "jquery.bootgrid", "title": "jQuery Bootgrid", - "version": "1.1.0", + "version": "1.3.1", "description": "Nice, sleek and intuitive. A grid control especially designed for bootstrap.", "homepage": "http://www.jquery-bootgrid.com", "author": { @@ -19,36 +19,44 @@ "url": "https://github.com/rstaib/jquery-bootgrid.git" }, "keywords": [ + "jquery-plugin", "jQuery", + "Bootstrap", + "Plugin", + "Component", + "Control", + "UI", "Grid", "Table", "Data", - "Bootstrap", - "Plugin" + "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": "~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.4.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", - "grunt-coveralls": "~1.0.0" + "grunt-exec": "~0.4.5" }, - "readmeFilename": "README.md", - "folders": { - "dist": "build", - "src": "src", - "docs": "docs", - "nuget": "nuget" - } + "readmeFilename": "README.md" } \ No newline at end of file diff --git a/src/extensions.js b/src/extensions.js index 9b82705..cb37139 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -4,7 +4,7 @@ $.fn.extend({ _bgAria: function (name, value) { - return this.attr("aria-" + name, value); + return (value) ? this.attr("aria-" + name, value) : this.attr("aria-" + name); }, _bgBusyAria: function(busy) @@ -87,7 +87,7 @@ if (!String.prototype.resolve) } key = (prefixes) ? prefixes.join(".") + "." + key : key; var pattern = new RegExp("\\{\\{" + key + "\\}\\}", "gm"); - result = result.replace(pattern, value); + result = result.replace(pattern, (value.replace) ? value.replace(/\$/gi, "$") : value); } } }); diff --git a/src/fontawesome.js b/src/fontawesome.js new file mode 100644 index 0000000..6be8c30 --- /dev/null +++ b/src/fontawesome.js @@ -0,0 +1,8 @@ +$.extend($.fn.bootgrid.Constructor.defaults.css, { + icon: "icon fa", + iconColumns: "fa-th-list", + iconDown: "fa-sort-desc", + iconRefresh: "fa-refresh", + iconSearch: "fa-search", + iconUp: "fa-sort-asc" +}); \ No newline at end of file diff --git a/src/internal.js b/src/internal.js index 26c8202..9b9bba4 100644 --- a/src/internal.js +++ b/src/internal.js @@ -24,9 +24,16 @@ function appendRow(row) 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 }) : + return (context) ? $.extend({}, this.cachedParams, { ctx: context }) : this.cachedParams; } @@ -35,7 +42,7 @@ function getRequest() var request = { current: this.current, rowCount: this.rowCount, - sort: this.sort, + sort: this.sortDictionary, searchPhrase: this.searchPhrase }, post = this.options.post; @@ -60,6 +67,7 @@ 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); @@ -107,12 +115,15 @@ 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 - visible: !(data.visible === 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.sort[column.id] = column.order; + that.sortDictionary[column.id] = column.order; } // Prevents multiple identifiers @@ -143,14 +154,7 @@ response = { function loadData() { - var that = this, - request = getRequest.call(this), - url = getUrl.call(this); - - if (this.options.ajax && (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."); - } + var that = this; this.element._bgBusyAria(true).trigger("load" + namespace); showLoading.call(this); @@ -163,7 +167,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 && column.converter.to(row[column.id]).search(searchPattern) > -1) { return true; @@ -176,8 +180,7 @@ function loadData() function update(rows, total) { that.currentRows = rows; - that.total = total; - that.totalPages = Math.ceil(total / that.rowCount); + setTotals.call(that, total); if (!that.options.keepSelection) { @@ -193,35 +196,51 @@ function loadData() if (this.options.ajax) { - // aborts the previous ajax request if not already finished or failed - if (that.xqr) + var request = getRequest.call(this), + url = getUrl.call(this); + + if (url == null || typeof url !== "string" || url.length === 0) { - that.xqr.abort(); + throw new Error("Url setting must be a none empty string or a function that returns one."); } - that.xqr = $.post(url, request, function (response) + // aborts the previous ajax request if not already finished or failed + if (this.xqr) { - that.xqr = null; + this.xqr.abort(); + } - if (typeof (response) === "string") + var settings = { + url: url, + data: request, + success: function(response) { - response = $.parseJSON(response); - } + that.xqr = null; - response = that.options.responseHandler(response); + if (typeof (response) === "string") + { + response = $.parseJSON(response); + } - that.current = response.current; - update(response.rows, response.total); - }).fail(function (jqXHR, textStatus, errorThrown) - { - that.xqr = null; + response = that.options.responseHandler(response); - if (textStatus !== "abort") + that.current = response.current; + update(response.rows, response.total); + }, + error: function (jqXHR, textStatus, errorThrown) { - renderNoResultsRow.call(that); // overrides loading mask - that.element._bgBusyAria(false).trigger("loaded" + namespace); + 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 { @@ -259,18 +278,22 @@ function loadRows() appendRow.call(that, row); }); - this.total = this.rows.length; - this.totalPages = (this.rowCount === -1) ? 1 : - Math.ceil(this.total / this.rowCount); - + 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)) ? + wrapper = (this.element.parent().hasClass(this.options.css.responsiveTable)) ? this.element.parent() : this.element; this.element.addClass(this.options.css.table); @@ -300,10 +323,9 @@ function renderActions() { var css = this.options.css, selector = getCssSelector(css.actions), - headerActions = this.header.find(selector), - footerActions = this.footer.find(selector); + actionItems = findFooterAndHeaderItems.call(this, selector); - if ((headerActions.length + footerActions.length) > 0) + if (actionItems.length > 0) { var that = this, tpl = this.options.templates, @@ -331,8 +353,7 @@ function renderActions() // Column selection renderColumnSelection.call(this, actions); - replacePlaceHolder.call(this, headerActions, actions, 1); - replacePlaceHolder.call(this, footerActions, actions, 2); + replacePlaceHolder.call(this, actionItems, actions); } } } @@ -352,27 +373,30 @@ function renderColumnSelection(actions) $.each(this.columns, function (i, column) { - 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")) + 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) { - 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); + 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); } @@ -383,10 +407,9 @@ function renderInfos() if (this.options.navigation !== 0) { var selector = getCssSelector(this.options.css.infos), - headerInfos = this.header.find(selector), - footerInfos = this.footer.find(selector); + infoItems = findFooterAndHeaderItems.call(this, selector); - if ((headerInfos.length + footerInfos.length) > 0) + if (infoItems.length > 0) { var end = (this.current * this.rowCount), infos = $(this.options.templates.infos.resolve(getParams.call(this, { @@ -395,8 +418,7 @@ function renderInfos() total: this.total }))); - replacePlaceHolder.call(this, headerInfos, infos, 1); - replacePlaceHolder.call(this, footerInfos, infos, 2); + replacePlaceHolder.call(this, infoItems, infos); } } } @@ -407,7 +429,7 @@ function renderNoResultsRow() tpl = this.options.templates, count = this.columns.where(isVisible).length; - if (this.options.selection && this.identifier != null) + if (this.selection) { count = count + 1; } @@ -419,10 +441,9 @@ function renderPagination() if (this.options.navigation !== 0) { var selector = getCssSelector(this.options.css.pagination), - headerPagination = this.header.find(selector)._bgShowAria(this.rowCount !== -1), - footerPagination = this.footer.find(selector)._bgShowAria(this.rowCount !== -1); + paginationItems = findFooterAndHeaderItems.call(this, selector)._bgShowAria(this.rowCount !== -1); - if (this.rowCount !== -1 && (headerPagination.length + footerPagination.length) > 0) + if (this.rowCount !== -1 && paginationItems.length > 0) { var tpl = this.options.templates, current = this.current, @@ -459,22 +480,22 @@ function renderPagination() renderPaginationItem.call(this, pagination, "last", "»", "last") ._bgEnableAria(totalPages > current); - replacePlaceHolder.call(this, headerPagination, pagination, 1); - replacePlaceHolder.call(this, footerPagination, pagination, 2); + replacePlaceHolder.call(this, paginationItems, pagination); } } } -function renderPaginationItem(list, uri, text, markerCss) +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, uri: "#" + uri }), + 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(); @@ -486,8 +507,8 @@ function renderPaginationItem(list, uri, text, markerCss) next: that.current + 1, last: that.totalPages }; - var command = $this.attr("href").substr(1); - that.current = commandList[command] || +command; // + converts string to int + var command = $this.data("page"); + that.current = commandList[command] || command; loadData.call(that); } $this.trigger("blur"); @@ -511,7 +532,7 @@ function renderRowCountSelection(actions) { var css = this.options.css, tpl = this.options.templates, - dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: this.rowCount }))), + dropDown = $(tpl.actionDropDown.resolve(getParams.call(this, { content: getText(this.rowCount) }))), menuSelector = getCssSelector(css.dropDownMenu), menuTextSelector = getCssSelector(css.dropDownMenuText), menuItemsSelector = getCssSelector(css.dropDownMenuItems), @@ -520,14 +541,14 @@ function renderRowCountSelection(actions) $.each(rowCountList, function (index, value) { var item = $(tpl.actionDropDownItem.resolve(getParams.call(that, - { text: getText(value), uri: "#" + value }))) + { text: getText(value), action: value }))) ._bgSelectAria(value === that.rowCount) .on("click" + namespace, menuItemSelector, function (e) { e.preventDefault(); var $this = $(this), - newRowCount = +$this.attr("href").substr(1); + newRowCount = $this.data("action"); if (newRowCount !== that.rowCount) { // todo: sophisticated solution needed for calculating which page is selected @@ -536,7 +557,7 @@ function renderRowCountSelection(actions) $this.parents(menuItemsSelector).children().each(function () { var $item = $(this), - currentRowCount = +$item.find(menuItemSelector).attr("href").substr(1); + currentRowCount = $item.find(menuItemSelector).data("action"); $item._bgSelectAria(currentRowCount === newRowCount); }); $this.parents(menuSelector).find(menuTextSelector).text(getText(newRowCount)); @@ -557,23 +578,19 @@ function renderRows(rows) css = this.options.css, tpl = this.options.templates, tbody = this.element.children("tbody").first(), - selection = this.options.selection && this.identifier != null, allRowsSelected = true, - html = "", - cells = "", - rowAttr = "", - rowCss = ""; + html = ""; - $.each(rows, function (i, row) + $.each(rows, function (index, row) { - cells = ""; - rowAttr = " data-row-id=\"" + ((that.identifier == null) ? i : row[that.identifier]) + "\""; - rowCss = ""; + var cells = "", + rowAttr = " data-row-id=\"" + ((that.identifier == null) ? index : row[that.identifier]) + "\"", + rowCss = ""; - if (selection) + if (that.selection) { var selected = ($.inArray(row[that.identifier], that.selectedRows) !== -1), - selectBox = tpl.select.resolve(getParams.call(that, + 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); @@ -584,18 +601,25 @@ function renderRows(rows) } } + 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) : + 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 })); + css: ((column.align === "right") ? css.right : (column.align === "center") ? + css.center : css.left) + cssClass, + style: (column.width == null) ? "" : "width:" + column.width + ";" })); } }); @@ -623,10 +647,9 @@ function renderRows(rows) function registerRowEvents(tbody) { var that = this, - selection = this.options.selection && this.identifier != null, selectBoxSelector = getCssSelector(this.options.css.selectBox); - if (selection) + if (this.selection) { tbody.off("click" + namespace, selectBoxSelector) .on("click" + namespace, selectBoxSelector, function(e) @@ -653,11 +676,12 @@ function registerRowEvents(tbody) e.stopPropagation(); var $this = $(this), - id = that.converter.from($this.data("row-id")), - row = (this.identifier == null) ? that.currentRows[id] : + 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 (selection && that.options.rowSelect) + if (that.selection && that.options.rowSelect) { if ($this.hasClass(that.options.css.selected)) { @@ -679,10 +703,9 @@ function renderSearchField() { var css = this.options.css, selector = getCssSelector(css.search), - headerSearch = this.header.find(selector), - footerSearch = this.footer.find(selector); + searchItems = findFooterAndHeaderItems.call(this, selector); - if ((headerSearch.length + footerSearch.length) > 0) + if (searchItems.length > 0) { var that = this, tpl = this.options.templates, @@ -697,23 +720,35 @@ function renderSearchField() { e.stopPropagation(); var newValue = $(this).val(); - if (currentValue !== newValue) + if (currentValue !== newValue || (e.which === 13 && newValue !== "")) { currentValue = newValue; - window.clearTimeout(timer); - timer = window.setTimeout(function () + if (e.which === 13 || newValue.length === 0 || newValue.length >= that.options.searchSettings.characters) { - that.search(newValue); - }, 250); + window.clearTimeout(timer); + timer = window.setTimeout(function () + { + executeSearch.call(that, newValue); + }, that.options.searchSettings.delay); + } } }); - replacePlaceHolder.call(this, headerSearch, search, 1); - replacePlaceHolder.call(this, footerSearch, search, 2); + 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, @@ -721,14 +756,13 @@ function renderTableHeader() css = this.options.css, tpl = this.options.templates, html = "", - sorting = this.options.sorting, - selection = this.options.selection && this.identifier != null; + sorting = this.options.sorting; - if (selection) + if (this.selection) { - var selectBox = (this.options.multiSelect) ? + var selectBox = (this.options.multiSelect) ? tpl.select.resolve(getParams.call(that, { type: "checkbox", value: "all" })) : ""; - html += tpl.rawHeaderCell.resolve(getParams.call(that, { content: selectBox, + html += tpl.rawHeaderCell.resolve(getParams.call(that, { content: selectBox, css: css.selectCell })); } @@ -736,7 +770,7 @@ function renderTableHeader() { if (column.visible) { - var sortOrder = that.sort[column.id], + 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 })), @@ -744,72 +778,30 @@ function renderTableHeader() 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 })); + css: ((align === "right") ? css.right : (align === "center") ? + css.center : css.left) + cssClass, + style: (column.width == null) ? "" : "width:" + column.width + ";" })); } }); headerRow.html(html); - // todo: create a own function for that piece of code if (sorting) { - var sortingSelector = getCssSelector(css.sortable), - iconSelector = getCssSelector(css.icon); + var sortingSelector = getCssSelector(css.sortable); headerRow.off("click" + namespace, sortingSelector) .on("click" + namespace, sortingSelector, function (e) { e.preventDefault(); - var $this = $(this), - columnId = $this.data("column-id") || $this.parents("th").first().data("column-id"), - sortOrder = that.sort[columnId], - icon = $this.find(iconSelector); - - if (!that.options.multiSort) - { - $this.parents("tr").first().find(iconSelector).removeClass(css.iconDown + " " + css.iconUp); - that.sort = {}; - } - - if (sortOrder && sortOrder === "asc") - { - that.sort[columnId] = "desc"; - icon.removeClass(css.iconUp).addClass(css.iconDown); - } - else if (sortOrder && sortOrder === "desc") - { - if (that.options.multiSort) - { - var newSort = {}; - for (var key in that.sort) - { - if (key !== columnId) - { - newSort[key] = that.sort[key]; - } - } - that.sort = newSort; - icon.removeClass(css.iconDown); - } - else - { - that.sort[columnId] = "asc"; - icon.removeClass(css.iconDown).addClass(css.iconUp); - } - } - else - { - that.sort[columnId] = "asc"; - icon.addClass(css.iconUp); - } + setTableHeaderSortDirection.call(that, $(this)); sortRows.call(that); loadData.call(that); }); } // todo: create a own function for that piece of code - if (selection && this.options.multiSelect) + if (this.selection && this.options.multiSelect) { var selectBoxSelector = getCssSelector(css.selectBox); headerRow.off("click" + namespace, selectBoxSelector) @@ -829,36 +821,88 @@ function renderTableHeader() } } -function replacePlaceHolder(placeholder, element, flag) +function setTableHeaderSortDirection(element) { - if (this.options.navigation & flag) + 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) { - placeholder.each(function (index, item) + 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) { - // todo: check how append is implemented. Perhaps cloning here is superfluous. - $(item).before(element.clone(true)).remove(); - }); + 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 tpl = this.options.templates, - thead = this.element.children("thead").first(), - tbody = this.element.children("tbody").first(), - firstCell = tbody.find("tr > td").first(), - padding = (this.element.height() - thead.height()) - (firstCell.height() + 20), - count = this.columns.where(isVisible).length; + var that = this; - if (this.options.selection && this.identifier != null) + window.setTimeout(function() { - count = count + 1; - } - tbody.html(tpl.loading.resolve(getParams.call(this, { columns: count }))); - if (this.rowCount !== -1 && padding > 0) - { - tbody.find("tr > td").css("padding", "20px 0 " + padding + "px"); - } + 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() @@ -885,13 +929,13 @@ function sortRows() { var that = this; - for (var key in this.sort) + for (var key in this.sortDictionary) { if (this.options.multiSort || sortArray.length === 0) { sortArray.push({ id: key, - order: this.sort[key] + order: this.sortDictionary[key] }); } } diff --git a/src/jquery.bootgrid.less b/src/jquery.bootgrid.less index c516f17..36bfc2e 100644 --- a/src/jquery.bootgrid.less +++ b/src/jquery.bootgrid.less @@ -37,6 +37,7 @@ width: 180px; .glyphicon { top: 0; } + .fa { display: table-cell; } &.search-field, .search-field { diff --git a/src/plugin.js b/src/plugin.js index c545abc..c51abe9 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -5,27 +5,36 @@ var old = $.fn.bootgrid; $.fn.bootgrid = function (option) { - var args = Array.prototype.slice.call(arguments, 1); - return this.each(function () - { - var $this = $(this), - instance = $this.data(namespace), - options = typeof option === "object" && option; - - if (!instance && option === "destroy") + var args = Array.prototype.slice.call(arguments, 1), + returnValue = null, + elements = this.each(function (index) { - return; - } - if (!instance) - { - $this.data(namespace, (instance = new Grid(this, options))); - init.call(instance); - } - if (typeof option === "string") - { - return instance[option].apply(instance, args); - } - }); + var $this = $(this), + instance = $this.data(namespace), + options = typeof option === "object" && option; + + 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) + { + returnValue = instance[option].apply(instance, args); + } + else if (option.indexOf("get") !== 0) + { + return instance[option].apply(instance, args); + } + } + }); + return (typeof option === "string" && option.indexOf("get") === 0) ? returnValue : elements; }; $.fn.bootgrid.Constructor = Grid; diff --git a/src/public.js b/src/public.js index 5bce9cb..16c5c44 100644 --- a/src/public.js +++ b/src/public.js @@ -13,6 +13,7 @@ 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; @@ -20,12 +21,13 @@ var Grid = function(element, options) 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.sort = {}; + this.sortDictionary = {}; this.total = 0; this.totalPages = 0; this.cachedParams = { @@ -42,8 +44,6 @@ var Grid = function(element, options) /** * An object that represents the default settings. - * There are two ways to override the sub-properties. - * Either by doing it generally (global) or on initialization. * * @static * @class defaults @@ -95,7 +95,7 @@ Grid.defaults = { rowSelect: false, /** - * Defines whether the row selection is saved internally on filtering, paging and sorting + * Defines whether the row selection is saved internally on filtering, paging and sorting * (even if the selected rows are not visible). * * @property keepSelection @@ -109,10 +109,73 @@ Grid.defaults = { highlightRows: false, // highlights new rows (find the page of the first new row) sorting: true, multiSort: false, - ajax: false, // todo: find a better name for this property to differentiate between client-side and server-side data /** - * Enriches the request object with additional properties. Either a `PlainObject` or a `Function` + * 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 + }, + + /** + * 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: { + /** + * 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" + }, + + /** + * 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 @@ -124,7 +187,7 @@ Grid.defaults = { post: {}, // or use function () { return {}; } (reserved properties are "current", "rowCount", "sort" and "searchPhrase") /** - * Sets the data URL to a data service (e.g. a REST service). Either a `String` or a `Function` + * 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 @@ -213,6 +276,7 @@ Grid.defaults = { 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", @@ -277,6 +341,52 @@ Grid.defaults = { search: "Search" }, + /** + * Specifies the mapping between status and contextual classes to color rows. + * + * @property statusMapping + * @type Object + * @for defaults + * @since 1.2.0 + **/ + 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" + }, + /** * Contains all templates. * @@ -287,23 +397,23 @@ Grid.defaults = { templates: { actionButton: "", actionDropDown: "
              ", - actionDropDownItem: "
            • {{ctx.text}}
            • ", + actionDropDownItem: "
            • {{ctx.text}}
            • ", actionDropDownCheckboxItem: "
            • ", actions: "
              ", body: "", - cell: "{{ctx.content}}", + cell: "{{ctx.content}}", footer: "

              ", header: "

              ", - headerCell: "{{ctx.column.text}}{{ctx.icon}}", + headerCell: "{{ctx.column.text}}{{ctx.icon}}", icon: "", infos: "
              {{lbl.infos}}
              ", loading: "{{lbl.loading}}", noResults: "{{lbl.noResults}}", pagination: "
                ", - paginationItem: "
              • {{ctx.text}}
              • ", + paginationItem: "
              • {{ctx.text}}
              • ", rawHeaderCell: "{{ctx.content}}", // Used for the multi select box row: "{{ctx.cells}}", - search: "
                ", + search: "
                ", select: "" } }; @@ -319,7 +429,7 @@ Grid.prototype.append = function(rows) { if (this.options.ajax) { - // todo: implement ajax DELETE + // todo: implement ajax PUT } else { @@ -383,7 +493,7 @@ Grid.prototype.destroy = function() { this.footer.remove(); } - this.element.remove("tbody").off(namespace).removeData(namespace); + this.element.before(this.origin).remove(); return this; }; @@ -450,21 +560,27 @@ 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 - * @param phrase {String} The phrase to search for + * @param [phrase] {String} The phrase to search for * @chainable **/ Grid.prototype.search = function(phrase) { + phrase = phrase || ""; + if (this.searchPhrase !== phrase) { - this.current = 1; - this.searchPhrase = phrase; - loadData.call(this); + var selector = getCssSelector(this.options.css.searchField), + searchFields = findFooterAndHeaderItems.call(this, selector); + searchFields.val(phrase); } + executeSearch.call(this, phrase); + + return this; }; @@ -478,11 +594,11 @@ Grid.prototype.search = function(phrase) **/ Grid.prototype.select = function(rowIds) { - if (this.options.selection && this.identifier != null) + if (this.selection) { rowIds = rowIds || this.currentRows.propValues(this.identifier); - var id, i, + var id, i, selectedRows = []; while (rowIds.length > 0 && !(!this.options.multiSelect && selectedRows.length === 1)) @@ -544,7 +660,7 @@ Grid.prototype.select = function(rowIds) **/ Grid.prototype.deselect = function(rowIds) { - if (this.options.selection && this.identifier != null) + if (this.selection) { rowIds = rowIds || this.currentRows.propValues(this.identifier); @@ -580,7 +696,7 @@ Grid.prototype.deselect = function(rowIds) .removeClass(this.options.css.selected)._bgAria("selected", "false") .find(selectBoxSelector).prop("checked", false); } - + this.element.trigger("deselected" + namespace, [deselectedRows]); } } @@ -588,27 +704,153 @@ Grid.prototype.deselect = function(rowIds) return this; }; - /** - * Sorts rows. + * 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 dictionary which contains the sort information + * @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.sort) + + if (values === this.sortDictionary) { return this; } - this.sort = values; - + 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; }; \ No newline at end of file diff --git a/test/tests-internal.js b/test/tests-internal.js index 2e292d3..dea9eb6 100644 --- a/test/tests-internal.js +++ b/test/tests-internal.js @@ -4,7 +4,7 @@ module("internal functions", { setup: function () { - $("#qunit-fixture").html("
                "); + $("#qunit-fixture").html("
                "); }, teardown: function () { @@ -12,6 +12,88 @@ module("internal functions", { } }); +test("findFooterAndHeaderItems test", 1, function () +{ + // given + var instance = { + footer: $("#test > tfoot"), + header: $("#test > thead") + }; + var selector = "tr"; + + // when + var result = findFooterAndHeaderItems.call(instance, selector); + + // then + equal(result.length, 2, "Found two elements as expected"); +}); + +test("findFooterAndHeaderItems test (footer is null)", 1, function () +{ + // given + var instance = { + footer: null, + header: $("#test > thead") + }; + var selector = "tr"; + + // when + var result = findFooterAndHeaderItems.call(instance, selector); + + // then + equal(result.length, 1, "Found one element as expected"); +}); + +test("findFooterAndHeaderItems test (header is null)", 1, function () +{ + // given + var instance = { + footer: $("#test > tfoot"), + header: null + }; + var selector = "tr"; + + // when + var result = findFooterAndHeaderItems.call(instance, selector); + + // then + equal(result.length, 1, "Found one element as expected"); +}); + +test("findFooterAndHeaderItems test (footer and header is string empty)", 2, function () +{ + // given + var instance = { + footer: "", + header: "" + }; + var selector = "tr"; + + // when + var result = findFooterAndHeaderItems.call(instance, selector); + + // then + equal(result.length, 0, "Foundd one element as expecte"); + ok(result.find, "Got an empty jQuery array as expected"); +}); + +test("findFooterAndHeaderItems test (footer and header is null)", 2, function () +{ + // given + var instance = { + footer: null, + header: null + }; + var selector = "tr"; + + // when + var result = findFooterAndHeaderItems.call(instance, selector); + + // then + equal(result.length, 0, "Found no elements as expected"); + ok(result.find, "Got an empty jQuery array as expected"); +}); + test("getRequest post function test", 1, function () { // given @@ -27,7 +109,7 @@ test("getRequest post function test", 1, function () }, current: 1, rowCount: 5, - sort: [], + sortDictionary: [], searchPhrase: "" }, expected = { @@ -56,7 +138,7 @@ test("getRequest post object test", 1, function() { }, current: 1, rowCount: 5, - sort: [], + sortDictionary: [], searchPhrase: "" }, expected = { @@ -119,4 +201,4 @@ test("getUrl string test", 1, function () // then equal(result, "url/test/1", "Valid URL"); -}); \ No newline at end of file +});