diff --git a/demo/demo.css b/demo/demo.css
new file mode 100644
index 0000000..a4e9252
--- /dev/null
+++ b/demo/demo.css
@@ -0,0 +1,102 @@
+:focus {
+ outline: none; }
+
+.pagination {
+ display: inline-block;
+ box-sizing: border-box; }
+
+.pagination > li {
+ display: inline-block;
+ vertical-align: middle;
+ width: auto;
+ height: 30px;
+ position: relative;
+ border-radius: 2px;
+ box-sizing: border-box; }
+
+.pagination a {
+ color: #444;
+ font-size: 16px;
+ padding: 0 10px;
+ line-height: 30px;
+ display: inline-block;
+ text-decoration: none;
+ box-sizing: border-box; }
+ .pagination a .md-ripple-container {
+ border-radius: 2px; }
+
+.pagination-prev a,
+.pagination-next a,
+.pagination-first a,
+.pagination-last a {
+ padding: 3px 5px; }
+
+.pagination-prev svg,
+.pagination-next svg,
+.pagination-first svg,
+.pagination-last svg {
+ width: 24px;
+ height: 24px; }
+
+.pagination-none-svg svg {
+ display: none !important; }
+
+.pagination-none-svg .pagination-prev a,
+.pagination-none-svg .pagination-next a,
+.pagination-none-svg .pagination-first a,
+.pagination-none-svg .pagination-last a {
+ width: 30px;
+ text-align: center;
+ line-height: 24px; }
+
+.pagination-page {
+ transition: background .1s ease-out, color .1s ease-out; }
+ .pagination-page.active {
+ background: #106cc8; }
+ .pagination-page.active a {
+ color: #fff; }
+
+.pagination {
+ transition: background .1s ease-out, color .1s ease-out; }
+ .pagination .active {
+ background: #106cc8; }
+ .pagination .active a {
+ color: #fff; }
+
+.grid-data-table td {
+ border-bottom: 1px rgba(0, 0, 0, 0.12) solid;
+ padding: 12px; }
+
+.grid-data-table th {
+ padding: 0 12px;
+ border-bottom: 1px rgba(0, 0, 0, 0.12) solid; }
+
+th.sortable {
+ line-height: 24px;
+ padding-right: 31px; }
+ th.sortable svg {
+ height: 18px;
+ width: 18px;
+ margin: 0 4px;
+ transition: fill .25s,-webkit-transform .25s;
+ transition: fill .25s,transform .25s;
+ vertical-align: middle;
+ display: none !important; }
+ th.sortable.sort-descent {
+ padding-right: 0; }
+ th.sortable.sort-descent svg {
+ display: inline-block !important; }
+ th.sortable.sort-ascent {
+ padding-right: 0; }
+ th.sortable.sort-ascent svg {
+ display: inline-block !important;
+ -webkit-transform: rotate(180deg);
+ transform: rotate(180deg); }
+
+.items-per-page .active {
+ background: #106cc8; }
+ .items-per-page .active a {
+ color: #fff; }
+
+.items-per-page a {
+ cursor: pointer; }
diff --git a/demo/app.js b/demo/demoApp.js
similarity index 100%
rename from demo/app.js
rename to demo/demoApp.js
diff --git a/dist/dataGrid.css b/dist/dataGrid.css
new file mode 100644
index 0000000..a4e9252
--- /dev/null
+++ b/dist/dataGrid.css
@@ -0,0 +1,102 @@
+:focus {
+ outline: none; }
+
+.pagination {
+ display: inline-block;
+ box-sizing: border-box; }
+
+.pagination > li {
+ display: inline-block;
+ vertical-align: middle;
+ width: auto;
+ height: 30px;
+ position: relative;
+ border-radius: 2px;
+ box-sizing: border-box; }
+
+.pagination a {
+ color: #444;
+ font-size: 16px;
+ padding: 0 10px;
+ line-height: 30px;
+ display: inline-block;
+ text-decoration: none;
+ box-sizing: border-box; }
+ .pagination a .md-ripple-container {
+ border-radius: 2px; }
+
+.pagination-prev a,
+.pagination-next a,
+.pagination-first a,
+.pagination-last a {
+ padding: 3px 5px; }
+
+.pagination-prev svg,
+.pagination-next svg,
+.pagination-first svg,
+.pagination-last svg {
+ width: 24px;
+ height: 24px; }
+
+.pagination-none-svg svg {
+ display: none !important; }
+
+.pagination-none-svg .pagination-prev a,
+.pagination-none-svg .pagination-next a,
+.pagination-none-svg .pagination-first a,
+.pagination-none-svg .pagination-last a {
+ width: 30px;
+ text-align: center;
+ line-height: 24px; }
+
+.pagination-page {
+ transition: background .1s ease-out, color .1s ease-out; }
+ .pagination-page.active {
+ background: #106cc8; }
+ .pagination-page.active a {
+ color: #fff; }
+
+.pagination {
+ transition: background .1s ease-out, color .1s ease-out; }
+ .pagination .active {
+ background: #106cc8; }
+ .pagination .active a {
+ color: #fff; }
+
+.grid-data-table td {
+ border-bottom: 1px rgba(0, 0, 0, 0.12) solid;
+ padding: 12px; }
+
+.grid-data-table th {
+ padding: 0 12px;
+ border-bottom: 1px rgba(0, 0, 0, 0.12) solid; }
+
+th.sortable {
+ line-height: 24px;
+ padding-right: 31px; }
+ th.sortable svg {
+ height: 18px;
+ width: 18px;
+ margin: 0 4px;
+ transition: fill .25s,-webkit-transform .25s;
+ transition: fill .25s,transform .25s;
+ vertical-align: middle;
+ display: none !important; }
+ th.sortable.sort-descent {
+ padding-right: 0; }
+ th.sortable.sort-descent svg {
+ display: inline-block !important; }
+ th.sortable.sort-ascent {
+ padding-right: 0; }
+ th.sortable.sort-ascent svg {
+ display: inline-block !important;
+ -webkit-transform: rotate(180deg);
+ transform: rotate(180deg); }
+
+.items-per-page .active {
+ background: #106cc8; }
+ .items-per-page .active a {
+ color: #fff; }
+
+.items-per-page a {
+ cursor: pointer; }
diff --git a/dist/dataGrid.js b/dist/dataGrid.js
new file mode 100644
index 0000000..5772568
--- /dev/null
+++ b/dist/dataGrid.js
@@ -0,0 +1,420 @@
+angular.module('dataGrid', [])
+ .filter('startFrom', function () {
+ return function (input, start) {
+ if (input) {
+ start = +start;
+ return input.slice(start);
+ }
+ return [];
+ }
+ })
+ .controller('gridController', ['$scope', '$element', '$filter', '$location', 'filtersFactory', function ($scope, $element, $filter, $location, filtersFactory) {
+ // values by default
+ $scope._gridOptions = deepFind($scope, $element.attr('grid-options'));
+ $scope._gridActions = deepFind($scope, $element.attr('grid-actions'));
+ $scope.serverPagination = $element.attr('server-pagination') === 'true';
+ $scope.getDataDelay = $element.attr('get-delay') || 350;
+
+ if (!$scope._gridActions) {
+ $scope.$parent[$element.attr('grid-actions')] = {};
+ $scope._gridActions = $scope.$parent[$element.attr('grid-actions')];
+ }
+
+ $scope.filtered = angular.copy($scope._gridOptions.data);
+ $scope.paginationOptions = $scope._gridOptions.pagination ? angular.copy($scope._gridOptions.pagination) : {};
+ $scope.defaultsPaginationOptions = {
+ itemsPerPage: $scope.paginationOptions.itemsPerPage || 10,
+ currentPage: $scope.paginationOptions.currentPage || 1
+ };
+ $scope.paginationOptions = angular.copy($scope.defaultsPaginationOptions);
+ $scope.sortOptions = $scope._gridOptions.sort ? angular.copy($scope._gridOptions.sort) : {};
+ $scope.customFilters = $scope._gridOptions.customFilters ? angular.copy($scope._gridOptions.customFilters) : {};
+ $scope.urlSync = $scope._gridOptions.urlSync;
+
+ $scope.$watch('_gridOptions.data', function (newValue) {
+ if (newValue && newValue.length) {
+ if ($scope.urlSync) {
+ parseUrl($location.path());
+ } else {
+ applyFilters();
+ }
+ }
+ });
+
+ $scope.sort = function (predicate) {
+ var direction = $scope.sortOptions.predicate === predicate && $scope.sortOptions.direction === 'desc' ? 'asc' : 'desc';
+ $scope.sortOptions.predicate = predicate;
+ $scope.sortOptions.direction = direction;
+ $scope.paginationOptions.currentPage = 1;
+ $scope.reloadGrid();
+ };
+
+ $scope.filter = function () {
+ $scope.paginationOptions.currentPage = 1;
+ $scope.reloadGrid();
+ };
+
+ $scope.$on('$locationChangeSuccess', function () {
+ if ($scope.urlSync || $scope.serverPagination) {
+ if ($scope.serverPagination) {
+ clearTimeout($scope.getDataTimeout);
+ $scope.getDataTimeout = setTimeout(getData, $scope.getDataDelay);
+ }
+ parseUrl($location.path());
+ }
+ });
+
+ $scope.reloadGrid = function () {
+ if ($scope.urlSync || $scope.serverPagination) {
+ changePath();
+ } else {
+ applyFilters();
+ }
+ };
+
+ $scope._gridActions.refresh = $scope.reloadGrid;
+ $scope._gridActions.filter = $scope.filter;
+ $scope._gridActions.sort = $scope.sort;
+
+ function changePath() {
+ var path, needApplyFilters = false;
+
+ path = 'page=' + $scope.paginationOptions.currentPage;
+ if ($scope.paginationOptions.itemsPerPage !== $scope.defaultsPaginationOptions.itemsPerPage) {
+ path += '&itemsPerPage=' + $scope.paginationOptions.itemsPerPage;
+ }
+
+ if ($scope.sortOptions.predicate) {
+ path += '&sort=' + encodeURIComponent($scope.sortOptions.predicate + "-" + $scope.sortOptions.direction);
+ }
+
+ //custom filters
+ $scope.filters.forEach(function (filter) {
+ var urlName = filter.model,
+ value = $scope[urlName];
+
+ if (filter.disableUrl) {
+ needApplyFilters = true;
+ return;
+ }
+
+ if (value) {
+ var strValue;
+ if (value instanceof Date) {
+ if (isNaN(value.getTime())) {
+ return;
+ }
+ strValue = value.getFullYear() + '-';
+ strValue += value.getMonth() < 9 ? '0' + (value.getMonth() + 1) + '-' : (value.getMonth() + 1) + '-';
+ strValue += value.getDate() < 10 ? '0' + value.getDate() : value.getDate();
+ value = strValue;
+ }
+ path += '&' + encodeURIComponent(urlName) + '=' + encodeURIComponent(value);
+ }
+ });
+
+ if (needApplyFilters) {
+ applyFilters();
+ }
+ $location.path(path);
+ }
+
+ function parseUrl() {
+ var url = $location.path().slice(1),
+ params = {},
+ customParams = {};
+
+ $scope.params = params;
+
+ url.split('&').forEach(function (urlParam) {
+ var param = urlParam.split('=');
+ params[param[0]] = param[1];
+ if (param[0] !== 'page' && param[0] !== 'sort' && param[0] !== 'itemsPerPage') {
+ customParams[decodeURIComponent(param[0])] = decodeURIComponent(param[1]);
+ }
+ }
+ );
+
+ //custom filters
+ $scope.filters.forEach(function (filter) {
+ var urlName = filter.model,
+ value = customParams[urlName],
+ scope = $scope;
+
+ if (filter.disableUrl) {
+ return;
+ }
+
+ if (~filter.filterType.toLowerCase().indexOf('date') && value) {
+ scope[urlName] = new Date(value);
+ return;
+ }
+
+ if (filter.filterType === 'select' && !value) {
+ value = "";
+ }
+
+ scope[urlName] = value;
+ });
+
+ if (!$scope.serverPagination) {
+ applyCustomFilters();
+ }
+
+ //pagination options
+ $scope.paginationOptions.itemsPerPage = $scope.defaultsPaginationOptions.itemsPerPage;
+ $scope.paginationOptions.currentPage = $scope.defaultsPaginationOptions.currentPage;
+
+ if (params.itemsPerPage) {
+ $scope.paginationOptions.itemsPerPage = params.itemsPerPage;
+ }
+
+ if (params.page) {
+ if (!$scope.serverPagination && ((params.page - 1) * $scope.paginationOptions.itemsPerPage > $scope.filtered.length)) {
+ $scope.paginationOptions.currentPage = 1;
+ } else {
+ $scope.paginationOptions.currentPage = params.page;
+ }
+ }
+
+ //sort options
+ if (params.sort) {
+ var sort = params.sort.split('-');
+ $scope.sortOptions.predicate = decodeURIComponent(sort[0]);
+ $scope.sortOptions.direction = decodeURIComponent(sort[1]);
+ }
+ if (!$scope.serverPagination) {
+ applyFilters();
+ }
+ }
+
+ function getData() {
+ var url = $location.path().slice(1);
+ $scope._gridOptions.getData('?' + url, function (data, totalItems) {
+ $scope.filtered = data;
+ $scope.paginationOptions.totalItems = totalItems;
+ });
+ }
+
+ function applyFilters() {
+ $scope.filtered = angular.copy($scope._gridOptions.data);
+
+ applyCustomFilters();
+
+ //apply orderBy filter
+ $scope.filtered = $filter('orderBy')($scope.filtered, $scope.sortOptions.predicate, $scope.sortOptions.direction === 'desc');
+ $scope.paginationOptions.totalItems = $scope.filtered.length;
+ }
+
+ function applyCustomFilters() {
+ $scope.filters.forEach(function (filter) {
+ var predicate = filter.filterBy,
+ urlName = filter.model,
+ value = $scope[urlName],
+ type = filter.filterType;
+ if ($scope.customFilters[urlName]) {
+ $scope.filtered = $scope.customFilters[urlName]($scope.filtered, value, predicate);
+ } else if (value && type) {
+ var filterFunc = filtersFactory.getFilterByType(type);
+ if (filterFunc) {
+ $scope.filtered = filterFunc($scope.filtered, value, predicate);
+ }
+ }
+ });
+ }
+ }])
+ .directive('gridData', ['$compile', '$animate', function ($compile, $animate) {
+ return {
+ restrict: 'EA',
+ transclude: true,
+ replace: true,
+ controller: 'gridController',
+ link: function ($scope, $element, attrs, controller, $transclude) {
+ var sorting = [],
+ filters = [],
+ rows = [],
+ childScope = $scope.$new(),
+ directiveElement = $element.parent(),
+ gridId = attrs.id,
+ serverPagination = attrs.serverPagination === 'true';
+
+ $transclude($scope, function (clone) {
+ $animate.enter(clone, $element);
+ });
+
+ angular.forEach(angular.element(directiveElement[0].querySelectorAll('[sortable]')), function (sortable) {
+ var element = angular.element(sortable),
+ predicate = element.attr('sortable');
+ sorting.push(element);
+ element.attr('ng-class', "{'sort-ascent' : sortOptions.predicate ==='" +
+ predicate + "' && sortOptions.direction === 'asc', 'sort-descent' : sortOptions.predicate === '" +
+ predicate + "' && sortOptions.direction === 'desc'}");
+ element.attr('ng-click', "sort('" + predicate + "')");
+ $compile(element)(childScope);
+ });
+
+ angular.forEach(angular.element(document.querySelectorAll('[filter-by]')), function (filter) {
+ var element = angular.element(filter),
+ isInScope = directiveElement.find(element).length > 0,
+ predicate = element.attr('filter-by'),
+ filterType = element.attr('filter-type') || '',
+ urlName = element.attr('ng-model'),
+ disableUrl = element.attr('disable-url');
+
+ if (gridId && element.attr('grid-id') && gridId != element.attr('grid-id')) {
+ return;
+ }
+
+ if (filterType === 'select') {
+ var options = generateOptions(deepFind($scope, $element.attr('grid-options') + '.data'), predicate);
+ $scope[urlName + 'Options'] = options;
+ }
+
+ if (~filterType.indexOf('date') && !element.attr('ng-focus')
+ && !element.attr('ng-blur')) {
+ element.attr('ng-focus', "filter('{" + urlName + " : " + "this." + urlName + "}')");
+ element.attr('ng-blur', "filter('{" + urlName + " : " + "this." + urlName + "}')");
+ $compile(element)($scope);
+ }
+ if (!urlName) {
+ urlName = predicate;
+ element.attr('ng-model', predicate);
+ element.attr('ng-change', 'filter()');
+ $compile(element)($scope);
+ }
+
+ filters.push({
+ model: urlName,
+ isInScope: isInScope,
+ filterBy: predicate,
+ filterType: filterType,
+ disableUrl: disableUrl
+ });
+ });
+
+ angular.forEach(angular.element(directiveElement[0].querySelectorAll('[grid-item]')), function (row) {
+ var element = angular.element(row);
+ rows.push(element);
+ if (serverPagination) {
+ element.attr('ng-repeat', "item in filtered");
+ } else {
+ element.attr('ng-repeat', "item in filtered | startFrom:(paginationOptions.currentPage-1)*paginationOptions.itemsPerPage | limitTo:paginationOptions.itemsPerPage");
+ }
+ $compile(element)(childScope);
+ });
+
+ $scope.sorting = sorting;
+ $scope.rows = rows;
+ $scope.filters = filters;
+
+ function generateOptions(values, predicate) {
+ var array = [];
+ values.forEach(function (item) {
+ if (!~array.indexOf(item[predicate])) {
+ array.push(item[predicate]);
+ }
+ });
+
+ return array.map(function (option) {
+ return {text: option, value: option};
+ });
+ }
+ }
+ }
+ }])
+ .directive('gridItemPerPage', ['$compile', function ($compile) {
+ return {
+ replace: true,
+ template: '
',
+ link: function (scope, element, attrs) {
+ if (attrs.gridItemPerPage) {
+ var values = attrs.gridItemPerPage.replace(/ /g, '').split(',');
+ values.forEach(function (value) {
+ if (Number(value)) {
+ value = Number(value);
+ } else {
+ return;
+ }
+ var li = angular.element('');
+ var button = angular.element('' + value + '');
+ button.attr('ng-click', 'paginationOptions.itemsPerPage = ' + value + '; reloadGrid()');
+ li.attr('ng-class', '{"active" : paginationOptions.itemsPerPage == ' + value + '}');
+ li.append(button);
+ element.append(li);
+ element.append(' ');
+ $compile(li)(scope);
+ });
+ }
+ }
+ }
+ }])
+ .factory('filtersFactory', function () {
+ function selectFilter(items, value, predicate) {
+ return items.filter(function (item) {
+ return value && item[predicate] ? item[predicate] === value : true;
+ });
+ }
+
+ function textFilter(items, value, predicate) {
+ return items.filter(function (item) {
+ return value && item[predicate] ? ~item[predicate].toLowerCase().indexOf(value.toLowerCase()) : true;
+ });
+ }
+
+ function dateToFilter(items, value, predicate) {
+ value = new Date(value).getTime();
+ return items.filter(function (item) {
+ return value && item[predicate] ? item[predicate] <= value + 86399999 : true;
+ });
+ }
+
+ function dateFromFilter(items, value, predicate) {
+ value = new Date(value).getTime();
+ return items.filter(function (item) {
+ return value && item[predicate] ? item[predicate] >= value : true;
+ });
+ }
+
+ return {
+ getFilterByType: function (type) {
+ switch (type) {
+ case 'select' :
+ {
+ return selectFilter;
+ }
+ case 'text' :
+ {
+ return textFilter;
+ }
+ case 'dateTo':
+ {
+ return dateToFilter;
+ }
+ case 'dateFrom':
+ {
+ return dateFromFilter;
+ }
+ default :
+ {
+ return null;
+ }
+ }
+ }
+ }
+ });
+
+
+function deepFind(obj, path) {
+ var paths = path.split('.'),
+ current = obj,
+ i;
+
+ for (i = 0; i < paths.length; ++i) {
+ if (current[paths[i]] == undefined) {
+ return undefined;
+ } else {
+ current = current[paths[i]];
+ }
+ }
+ return current;
+}
\ No newline at end of file
diff --git a/dist/dataGrid.min.js b/dist/dataGrid.min.js
new file mode 100644
index 0000000..2c3351e
--- /dev/null
+++ b/dist/dataGrid.min.js
@@ -0,0 +1 @@
+function deepFind(t,e){var i,n=e.split("."),r=t;for(i=0;it.filtered.length?t.paginationOptions.currentPage=1:t.paginationOptions.currentPage=i.page),i.sort){var a=i.sort.split("-");t.sortOptions.predicate=decodeURIComponent(a[0]),t.sortOptions.direction=decodeURIComponent(a[1])}t.serverPagination||l()}function s(){var e=n.path().slice(1);t._gridOptions.getData("?"+e,function(e,i){t.filtered=e,t.paginationOptions.totalItems=i})}function l(){t.filtered=angular.copy(t._gridOptions.data),c(),t.filtered=i("orderBy")(t.filtered,t.sortOptions.predicate,"desc"===t.sortOptions.direction),t.paginationOptions.totalItems=t.filtered.length}function c(){t.filters.forEach(function(e){var i=e.filterBy,n=e.model,a=t[n],o=e.filterType;if(t.customFilters[n])t.filtered=t.customFilters[n](t.filtered,a,i);else if(a&&o){var s=r.getFilterByType(o);s&&(t.filtered=s(t.filtered,a,i))}})}t._gridOptions=deepFind(t,e.attr("grid-options")),t._gridActions=deepFind(t,e.attr("grid-actions")),t.serverPagination="true"===e.attr("server-pagination"),t.getDataDelay=e.attr("get-delay")||350,t._gridActions||(t.$parent[e.attr("grid-actions")]={},t._gridActions=t.$parent[e.attr("grid-actions")]),t.filtered=angular.copy(t._gridOptions.data),t.paginationOptions=t._gridOptions.pagination?angular.copy(t._gridOptions.pagination):{},t.defaultsPaginationOptions={itemsPerPage:t.paginationOptions.itemsPerPage||10,currentPage:t.paginationOptions.currentPage||1},t.paginationOptions=angular.copy(t.defaultsPaginationOptions),t.sortOptions=t._gridOptions.sort?angular.copy(t._gridOptions.sort):{},t.customFilters=t._gridOptions.customFilters?angular.copy(t._gridOptions.customFilters):{},t.urlSync=t._gridOptions.urlSync,t.$watch("_gridOptions.data",function(e){e&&e.length&&(t.urlSync?o(n.path()):l())}),t.sort=function(e){var i=t.sortOptions.predicate===e&&"desc"===t.sortOptions.direction?"asc":"desc";t.sortOptions.predicate=e,t.sortOptions.direction=i,t.paginationOptions.currentPage=1,t.reloadGrid()},t.filter=function(){t.paginationOptions.currentPage=1,t.reloadGrid()},t.$on("$locationChangeSuccess",function(){(t.urlSync||t.serverPagination)&&(t.serverPagination&&(clearTimeout(t.getDataTimeout),t.getDataTimeout=setTimeout(s,t.getDataDelay)),o(n.path()))}),t.reloadGrid=function(){t.urlSync||t.serverPagination?a():l()},t._gridActions.refresh=t.reloadGrid,t._gridActions.filter=t.filter,t._gridActions.sort=t.sort}]).directive("gridData",["$compile","$animate",function(t,e){return{restrict:"EA",transclude:!0,replace:!0,controller:"gridController",link:function(i,n,r,a,o){function s(t,e){var i=[];return t.forEach(function(t){~i.indexOf(t[e])||i.push(t[e])}),i.map(function(t){return{text:t,value:t}})}var l=[],c=[],g=[],p=i.$new(),d=n.parent(),u=r.id,f="true"===r.serverPagination;o(i,function(t){e.enter(t,n)}),angular.forEach(angular.element(d[0].querySelectorAll("[sortable]")),function(e){var i=angular.element(e),n=i.attr("sortable");l.push(i),i.attr("ng-class","{'sort-ascent' : sortOptions.predicate ==='"+n+"' && sortOptions.direction === 'asc', 'sort-descent' : sortOptions.predicate === '"+n+"' && sortOptions.direction === 'desc'}"),i.attr("ng-click","sort('"+n+"')"),t(i)(p)}),angular.forEach(angular.element(document.querySelectorAll("[filter-by]")),function(e){var r=angular.element(e),a=d.find(r).length>0,o=r.attr("filter-by"),l=r.attr("filter-type")||"",g=r.attr("ng-model"),p=r.attr("disable-url");if(!u||!r.attr("grid-id")||u==r.attr("grid-id")){if("select"===l){var f=s(deepFind(i,n.attr("grid-options")+".data"),o);i[g+"Options"]=f}!~l.indexOf("date")||r.attr("ng-focus")||r.attr("ng-blur")||(r.attr("ng-focus","filter('{"+g+" : this."+g+"}')"),r.attr("ng-blur","filter('{"+g+" : this."+g+"}')"),t(r)(i)),g||(g=o,r.attr("ng-model",o),r.attr("ng-change","filter()"),t(r)(i)),c.push({model:g,isInScope:a,filterBy:o,filterType:l,disableUrl:p})}}),angular.forEach(angular.element(d[0].querySelectorAll("[grid-item]")),function(e){var i=angular.element(e);g.push(i),f?i.attr("ng-repeat","item in filtered"):i.attr("ng-repeat","item in filtered | startFrom:(paginationOptions.currentPage-1)*paginationOptions.itemsPerPage | limitTo:paginationOptions.itemsPerPage"),t(i)(p)}),i.sorting=l,i.rows=g,i.filters=c}}}]).directive("gridItemPerPage",["$compile",function(t){return{replace:!0,template:'',link:function(e,i,n){if(n.gridItemPerPage){var r=n.gridItemPerPage.replace(/ /g,"").split(",");r.forEach(function(n){if(Number(n)){n=Number(n);var r=angular.element(""),a=angular.element(""+n+"");a.attr("ng-click","paginationOptions.itemsPerPage = "+n+"; reloadGrid()"),r.attr("ng-class",'{"active" : paginationOptions.itemsPerPage == '+n+"}"),r.append(a),i.append(r),i.append(" "),t(r)(e)}})}}}}]).factory("filtersFactory",function(){function t(t,e,i){return t.filter(function(t){return e&&t[i]?t[i]===e:!0})}function e(t,e,i){return t.filter(function(t){return e&&t[i]?~t[i].toLowerCase().indexOf(e.toLowerCase()):!0})}function i(t,e,i){return e=new Date(e).getTime(),t.filter(function(t){return e&&t[i]?t[i]<=e+86399999:!0})}function n(t,e,i){return e=new Date(e).getTime(),t.filter(function(t){return e&&t[i]?t[i]>=e:!0})}return{getFilterByType:function(r){switch(r){case"select":return t;case"text":return e;case"dateTo":return i;case"dateFrom":return n;default:return null}}}});
\ No newline at end of file
diff --git a/dist/pagination.js b/dist/pagination.js
new file mode 100644
index 0000000..249c4b4
--- /dev/null
+++ b/dist/pagination.js
@@ -0,0 +1,244 @@
+angular.module('ui.bootstrap.paging', [])
+ /**
+ * Helper internal service for generating common controller code between the
+ * pager and pagination components
+ */
+ .factory('uibPaging', ['$parse', function ($parse) {
+ return {
+ create: function (ctrl, $scope, $attrs) {
+ ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
+ ctrl.ngModelCtrl = {$setViewValue: angular.noop}; // nullModelCtrl
+
+ ctrl.init = function (ngModelCtrl, config) {
+ ctrl.ngModelCtrl = ngModelCtrl;
+ ctrl.config = config;
+
+ ngModelCtrl.$render = function () {
+ ctrl.render();
+ };
+
+ if ($attrs.itemsPerPage) {
+ $scope.$parent.$watch($parse($attrs.itemsPerPage), function (value) {
+ ctrl.itemsPerPage = parseInt(value, 10);
+ $scope.totalPages = ctrl.calculateTotalPages();
+ ctrl.updatePage();
+ });
+ } else {
+ ctrl.itemsPerPage = config.itemsPerPage;
+ }
+
+ $scope.$watch('totalItems', function (newTotal, oldTotal) {
+ if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
+ $scope.totalPages = ctrl.calculateTotalPages();
+ ctrl.updatePage();
+ }
+ });
+ };
+
+ ctrl.calculateTotalPages = function () {
+ var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
+ return Math.max(totalPages || 0, 1);
+ };
+
+ ctrl.render = function () {
+ $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
+ };
+
+ $scope.selectPage = function (page, evt) {
+ if (evt) {
+ evt.preventDefault();
+ }
+
+ var clickAllowed = !$scope.ngDisabled || !evt;
+ if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
+ if (evt && evt.target) {
+ evt.target.blur();
+ }
+ ctrl.ngModelCtrl.$setViewValue(page);
+ ctrl.ngModelCtrl.$render();
+ }
+ };
+
+ $scope.getText = function (key) {
+ return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
+ };
+
+ $scope.noPrevious = function () {
+ return $scope.page === 1;
+ };
+
+ $scope.noNext = function () {
+ return $scope.page === $scope.totalPages;
+ };
+
+ ctrl.updatePage = function () {
+ ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
+
+ if ($scope.page > $scope.totalPages) {
+ $scope.selectPage($scope.totalPages);
+ } else {
+ ctrl.ngModelCtrl.$render();
+ }
+ };
+ }
+ };
+ }]);
+
+angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
+ .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function ($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
+ var ctrl = this;
+ // Setup configuration parameters
+ var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
+ rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
+ forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
+ boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers;
+ $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
+ $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
+
+ uibPaging.create(this, $scope, $attrs);
+
+ if ($attrs.maxSize) {
+ $scope.$parent.$watch($parse($attrs.maxSize), function (value) {
+ maxSize = parseInt(value, 10);
+ ctrl.render();
+ });
+ }
+
+ // Create page object used in template
+ function makePage(number, text, isActive) {
+ return {
+ number: number,
+ text: text,
+ active: isActive
+ };
+ }
+
+ function getPages(currentPage, totalPages) {
+ var pages = [];
+
+ // Default page limits
+ var startPage = 1, endPage = totalPages;
+ var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
+
+ // recompute if maxSize
+ if (isMaxSized) {
+ if (rotate) {
+ // Current page is displayed in the middle of the visible ones
+ startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
+ endPage = startPage + maxSize - 1;
+
+ // Adjust if limit is exceeded
+ if (endPage > totalPages) {
+ endPage = totalPages;
+ startPage = endPage - maxSize + 1;
+ }
+ } else {
+ // Visible pages are paginated with maxSize
+ startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
+
+ // Adjust last page if limit is exceeded
+ endPage = Math.min(startPage + maxSize - 1, totalPages);
+ }
+ }
+
+ // Add page number links
+ for (var number = startPage; number <= endPage; number++) {
+ var page = makePage(number, number, number === currentPage);
+ pages.push(page);
+ }
+
+ // Add links to move between page sets
+ if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
+ if (startPage > 1) {
+ if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
+ var previousPageSet = makePage(startPage - 1, '...', false);
+ pages.unshift(previousPageSet);
+ }
+ if (boundaryLinkNumbers) {
+ if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
+ var secondPageLink = makePage(2, '2', false);
+ pages.unshift(secondPageLink);
+ }
+ //add the first page
+ var firstPageLink = makePage(1, '1', false);
+ pages.unshift(firstPageLink);
+ }
+ }
+
+ if (endPage < totalPages) {
+ if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
+ var nextPageSet = makePage(endPage + 1, '...', false);
+ pages.push(nextPageSet);
+ }
+ if (boundaryLinkNumbers) {
+ if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
+ var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
+ pages.push(secondToLastPageLink);
+ }
+ //add the last page
+ var lastPageLink = makePage(totalPages, totalPages, false);
+ pages.push(lastPageLink);
+ }
+ }
+ }
+ return pages;
+ }
+
+ var originalRender = this.render;
+ this.render = function () {
+ originalRender();
+ if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
+ $scope.pages = getPages($scope.page, $scope.totalPages);
+ }
+ };
+ }])
+
+ .constant('uibPaginationConfig', {
+ itemsPerPage: 10,
+ boundaryLinks: false,
+ boundaryLinkNumbers: false,
+ directionLinks: true,
+ firstText: 'First',
+ previousText: 'Previous',
+ nextText: 'Next',
+ lastText: 'Last',
+ rotate: true,
+ forceEllipses: false
+ })
+
+ .directive('uibPagination', ['$parse', 'uibPaginationConfig', function ($parse, uibPaginationConfig) {
+ return {
+ scope: {
+ totalItems: '=',
+ firstText: '@',
+ previousText: '@',
+ nextText: '@',
+ lastText: '@',
+ ngDisabled: '='
+ },
+ require: ['uibPagination', '?ngModel'],
+ controller: 'UibPaginationController',
+ controllerAs: 'pagination',
+ templateUrl: function (element, attrs) {
+ return attrs.templateUrl || 'src/template/pagination/pagination.html';
+ },
+ replace: true,
+ link: function (scope, element, attrs, ctrls) {
+ var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
+
+ if (!ngModelCtrl) {
+ return; // do nothing if no ng-model
+ }
+
+ paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
+ }
+ };
+ }])
+
+ .run(['$templateCache', function ($templateCache) {
+
+ $templateCache.put('src/template/pagination/pagination.html',
+ ""
+ );
+
+ }]);
\ No newline at end of file
diff --git a/dist/pagination.min.js b/dist/pagination.min.js
new file mode 100644
index 0000000..a25c266
--- /dev/null
+++ b/dist/pagination.min.js
@@ -0,0 +1 @@
+angular.module("ui.bootstrap.paging",[]).factory("uibPaging",["$parse",function(e){return{create:function(a,t,n){a.setNumPages=n.numPages?e(n.numPages).assign:angular.noop,a.ngModelCtrl={$setViewValue:angular.noop},a.init=function(i,r){a.ngModelCtrl=i,a.config=r,i.$render=function(){a.render()},n.itemsPerPage?t.$parent.$watch(e(n.itemsPerPage),function(e){a.itemsPerPage=parseInt(e,10),t.totalPages=a.calculateTotalPages(),a.updatePage()}):a.itemsPerPage=r.itemsPerPage,t.$watch("totalItems",function(e,n){(angular.isDefined(e)||e!==n)&&(t.totalPages=a.calculateTotalPages(),a.updatePage())})},a.calculateTotalPages=function(){var e=a.itemsPerPage<1?1:Math.ceil(t.totalItems/a.itemsPerPage);return Math.max(e||0,1)},a.render=function(){t.page=parseInt(a.ngModelCtrl.$viewValue,10)||1},t.selectPage=function(e,n){n&&n.preventDefault();var i=!t.ngDisabled||!n;i&&t.page!==e&&e>0&&e<=t.totalPages&&(n&&n.target&&n.target.blur(),a.ngModelCtrl.$setViewValue(e),a.ngModelCtrl.$render())},t.getText=function(e){return t[e+"Text"]||a.config[e+"Text"]},t.noPrevious=function(){return 1===t.page},t.noNext=function(){return t.page===t.totalPages},a.updatePage=function(){a.setNumPages(t.$parent,t.totalPages),t.page>t.totalPages?t.selectPage(t.totalPages):a.ngModelCtrl.$render()}}}}]),angular.module("ui.bootstrap.pagination",["ui.bootstrap.paging"]).controller("UibPaginationController",["$scope","$attrs","$parse","uibPaging","uibPaginationConfig",function(e,a,t,n,i){function r(e,a,t){return{number:e,text:a,active:t}}function s(e,a){var t=[],n=1,i=a,s=angular.isDefined(o)&&a>o;s&&(g?(n=Math.max(e-Math.floor(o/2),1),i=n+o-1,i>a&&(i=a,n=i-o+1)):(n=(Math.ceil(e/o)-1)*o+1,i=Math.min(n+o-1,a)));for(var l=n;i>=l;l++){var p=r(l,l,l===e);t.push(p)}if(s&&o>0&&(!g||u||c)){if(n>1){if(!c||n>3){var f=r(n-1,"...",!1);t.unshift(f)}if(c){if(3===n){var d=r(2,"2",!1);t.unshift(d)}var P=r(1,"1",!1);t.unshift(P)}}if(a>i){if(!c||a-2>i){var b=r(i+1,"...",!1);t.push(b)}if(c){if(i===a-2){var v=r(a-1,a-1,!1);t.push(v)}var m=r(a,a,!1);t.push(m)}}}return t}var l=this,o=angular.isDefined(a.maxSize)?e.$parent.$eval(a.maxSize):i.maxSize,g=angular.isDefined(a.rotate)?e.$parent.$eval(a.rotate):i.rotate,u=angular.isDefined(a.forceEllipses)?e.$parent.$eval(a.forceEllipses):i.forceEllipses,c=angular.isDefined(a.boundaryLinkNumbers)?e.$parent.$eval(a.boundaryLinkNumbers):i.boundaryLinkNumbers;e.boundaryLinks=angular.isDefined(a.boundaryLinks)?e.$parent.$eval(a.boundaryLinks):i.boundaryLinks,e.directionLinks=angular.isDefined(a.directionLinks)?e.$parent.$eval(a.directionLinks):i.directionLinks,n.create(this,e,a),a.maxSize&&e.$parent.$watch(t(a.maxSize),function(e){o=parseInt(e,10),l.render()});var p=this.render;this.render=function(){p(),e.page>0&&e.page<=e.totalPages&&(e.pages=s(e.page,e.totalPages))}}]).constant("uibPaginationConfig",{itemsPerPage:10,boundaryLinks:!1,boundaryLinkNumbers:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0,forceEllipses:!1}).directive("uibPagination",["$parse","uibPaginationConfig",function(e,a){return{scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["uibPagination","?ngModel"],controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(e,a){return a.templateUrl||"src/template/pagination/pagination.html"},replace:!0,link:function(e,t,n,i){var r=i[0],s=i[1];s&&r.init(s,a)}}}]).run(["$templateCache",function(e){e.put("src/template/pagination/pagination.html","")}]);
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index 59440f8..a763b39 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,93 +1,74 @@
-'use strict';
-
-var gulp = require('gulp'),
- uglify = require('gulp-uglify'),
- sourcemaps = require('gulp-sourcemaps'),
- rename = require('gulp-rename'),
- concat = require('gulp-concat'),
- sass = require('gulp-sass'),
- browserSync = require('browser-sync'),
- rimraf = require('gulp-rimraf'),
- addStream = require('add-stream'),
- templateCache = require('gulp-angular-templatecache');
-
-function browserSyncInit(baseDir) {
- var server = {
- baseDir: baseDir
- };
-
- browserSync.instance = browserSync.init({
- startPath: '/',
- server: server,
- open: false
+(function () {
+ 'use strict';
+
+ var gulp = require('gulp'),
+ uglify = require('gulp-uglify'),
+ rename = require('gulp-rename'),
+ concat = require('gulp-concat'),
+ sass = require('gulp-sass'),
+ browserSync = require('browser-sync'),
+ rimraf = require('gulp-rimraf');
+
+ function browserSyncInit(baseDir) {
+ var server = {
+ baseDir: baseDir
+ };
+
+ browserSync.instance = browserSync.init({
+ startPath: '/',
+ server: server,
+ open: false
+ });
+ }
+
+ gulp.task('js', function () {
+ gulp.src(['./src/**/*.js', '!./src/js/demoApp.js'])
+ .pipe(rename({dirname: ''}))
+ .pipe(gulp.dest('./dist'))
+ .pipe(uglify())
+ .pipe(rename({
+ suffix: ".min"
+ }))
+ .pipe(gulp.dest('./dist'));
+ gulp.src('./src/js/demoApp.js')
+ .pipe(gulp.dest('./demo'));
});
-}
-
-gulp.task('js', function () {
- gulp.src('./src/**/*.js')
- .pipe(addStream.obj(prepareTemplates()))
- .pipe(concat('index.js'))
- .pipe(gulp.dest('./demo/js'));
-
- gulp.src(['./src/**/*.js', '!./src/js/app.js'])
- .pipe(addStream.obj(prepareTemplates()))
- .pipe(concat('datagrid.js'))
- .pipe(gulp.dest('dist'))
- .pipe(sourcemaps.init())
- .pipe(concat('app.js'))
- .pipe(uglify())
- .pipe(rename('datagrid.min.js'))
- .pipe(sourcemaps.write('./'))
- .pipe(gulp.dest('dist'));
-
-});
+ gulp.task('sass', function () {
+ gulp.src('./src/css/demo.scss')
+ .pipe(sass().on('error', sass.logError))
+ .pipe(gulp.dest('./demo'));
+ gulp.src('./src/css/dataGrid.scss')
+ .pipe(sass().on('error', sass.logError))
+ .pipe(gulp.dest('./dist'));
+ });
-gulp.task('sass', function () {
- gulp.src('./src/**/*.scss')
- .pipe(sass().on('error', sass.logError))
- .pipe(gulp.dest('./demo'));
-});
+ gulp.task('build', ['js', 'sass']);
-gulp.task('html', function () {
- gulp.src(['./src/**/*.html', '!./src/js/**/*.html'])
- .pipe(gulp.dest('./demo'));
-});
+ gulp.task('watch', ['build'], function () {
-gulp.task('build', ['js', 'sass', 'html']);
+ gulp.watch(['./src/**/*.js'], function (event) {
+ gulp.start('js');
+ browserSync.reload(event.path);
+ });
-gulp.task('watch', ['build'], function () {
- gulp.watch(['./src/**/*.html'], function (event) {
- gulp.start('html');
- browserSync.reload(event.path);
+ gulp.watch(['./src/**/*.scss'], function (event) {
+ gulp.start('sass');
+ browserSync.reload(event.path);
+ });
});
- gulp.watch(['./src/**/*.js'], function (event) {
- gulp.start('js');
- browserSync.reload(event.path);
+ gulp.task('clean', function () {
+ return gulp.src(['demo'], {read: false})
+ .pipe(rimraf());
});
- gulp.watch(['./src/**/*.scss'], function (event) {
- gulp.start('sass');
- browserSync.reload(event.path);
+ gulp.task('full-clean', ['clean'], function () {
+ return gulp.src(['bower_components', 'node_modules'], {read: false})
+ .pipe(rimraf());
});
-});
-
-gulp.task('clean', function () {
- return gulp.src(['demo'], {read: false})
- .pipe(rimraf());
-});
-
-gulp.task('full-clean', ['clean'], function () {
- return gulp.src(['bower_components', 'node_modules'], {read: false})
- .pipe(rimraf());
-});
-gulp.task('serve', ['watch'], function () {
- browserSyncInit(['demo']);
-});
-
-function prepareTemplates() {
- return gulp.src('./src/js/**/*.html')
- .pipe(templateCache('templates.js', {module: 'dataGrid'}));
-}
\ No newline at end of file
+ gulp.task('serve', ['watch'], function () {
+ browserSyncInit(['demo']);
+ });
+})();
\ No newline at end of file
diff --git a/index.html b/index.html
index d5668f9..027ff5c 100644
--- a/index.html
+++ b/index.html
@@ -4,6 +4,7 @@
Flat JSON format for data tables
+
@@ -86,7 +87,14 @@
Angular Data Grid
-
+
@@ -134,10 +142,17 @@ Angular Data Grid
-
-
+
+
-
@@ -148,12 +163,10 @@
Angular Data Grid
-
+
-
-
-
-
-
+
+
+