From 82ae3da094082a443ae97b45c56ba07eeacf1ce4 Mon Sep 17 00:00:00 2001 From: Jon Shaffer Date: Wed, 26 Oct 2016 15:35:30 -0400 Subject: [PATCH 1/4] +popover,menu. popover rename module to 'dt.popover'. --- src/components/popover/popover.js | 2 +- src/dataTable.js | 4 ++++ src/dataTable.less | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js index 6e72d76..f576fc4 100644 --- a/src/components/popover/popover.js +++ b/src/components/popover/popover.js @@ -4,7 +4,7 @@ import { PopoverRegistry } from './PopoverRegistry'; import { PositionHelper } from './PositionHelper'; export default angular - .module('popover', []) + .module('dt.popover', []) .service('PopoverRegistry', PopoverRegistry) .factory('PositionHelper', PositionHelper) .directive('popover', PopoverDirective); diff --git a/src/dataTable.js b/src/dataTable.js index bc559d0..43c4acc 100644 --- a/src/dataTable.js +++ b/src/dataTable.js @@ -15,6 +15,10 @@ import { GroupRowDirective } from './components/body/GroupRowDirective'; import { CellDirective } from './components/body/CellDirective'; import { FooterDirective } from './components/footer/FooterDirective'; import { PagerDirective } from './components/footer/PagerDirective'; +import dtPopover from './components/popover/popover'; +import dtMenu from './components/menu/menu'; + +export { dtPopover, dtMenu }; export default angular .module('data-table', []) diff --git a/src/dataTable.less b/src/dataTable.less index 399cc8f..65deb17 100644 --- a/src/dataTable.less +++ b/src/dataTable.less @@ -1,4 +1,6 @@ .dt{ + @import "./components/popover/popover.less"; + @import "./components/menu/menu.less"; visibility: hidden; overflow:hidden; @@ -195,4 +197,3 @@ } } } - From f4ffc1ef911edd77eee592570dcacf31171c1227 Mon Sep 17 00:00:00 2001 From: Jon Shaffer Date: Wed, 26 Oct 2016 15:36:29 -0400 Subject: [PATCH 2/4] removing deprecated bundle format es6 => es --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index c0e4373..d12d6da 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -123,7 +123,7 @@ gulp.task('release-build', function () { }).then(function (bundle) { return bundle.write({ dest: 'release/dataTable.es6.js', - format: 'es6', + format: 'es', moduleName: 'DataTable' }); }); From fc39fc41583b066051ad23e6e16f9e2df352ffff Mon Sep 17 00:00:00 2001 From: Jon Shaffer Date: Wed, 26 Oct 2016 15:36:44 -0400 Subject: [PATCH 3/4] release build for +popover,menu --- release/dataTable.cjs.js | 4171 ++++++++++++++++-------------- release/dataTable.css | 145 ++ release/dataTable.es6.js | 5220 +++++++++++++++++++++----------------- release/dataTable.js | 4185 ++++++++++++++++-------------- release/dataTable.min.js | 4 +- 5 files changed, 7578 insertions(+), 6147 deletions(-) diff --git a/release/dataTable.cjs.js b/release/dataTable.cjs.js index cb34ff8..f5acd01 100644 --- a/release/dataTable.cjs.js +++ b/release/dataTable.cjs.js @@ -6,20 +6,19 @@ */ "use strict"; -DataTableDirective.$inject = ["$window", "$timeout", "$parse"]; -ResizableDirective.$inject = ["$document", "$timeout"]; -SortableDirective.$inject = ["$timeout"]; -HeaderDirective.$inject = ["$timeout"]; -HeaderCellDirective.$inject = ["$compile"]; -BodyDirective.$inject = ["$timeout"]; -ScrollerDirective.$inject = ["$timeout", "$rootScope"]; -CellDirective.$inject = ["$rootScope", "$compile", "$log", "$timeout"]; Object.defineProperty(exports, "__esModule", { value: true }); +exports.dtMenu = exports.dtPopover = undefined; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); +var _angular = require("angular"); + +var _angular2 = _interopRequireDefault(_angular); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } @@ -62,308 +61,289 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons } })(); -var PagerController = function () { - PagerController.$inject = ["$scope"]; - function PagerController($scope) { - var _this = this; +function ResizableDirective($document, $timeout) { + return { + restrict: 'A', + scope: { + isResizable: '=resizable', + minWidth: '=', + maxWidth: '=', + onResize: '&' + }, + link: function link($scope, $element, $attrs) { + if ($scope.isResizable) { + $element.addClass('resizable'); + } - _classCallCheck(this, PagerController); + var handle = angular.element(""), + parent = $element.parent(), + prevScreenX; - $scope.$watch('pager.count', function (newVal) { - _this.calcTotalPages(_this.size, _this.count); - _this.getPages(_this.page || 1); - }); + handle.on('mousedown', function (event) { + if (!$element[0].classList.contains('resizable')) { + return false; + } - $scope.$watch('pager.size', function (newVal) { - _this.calcTotalPages(_this.size, _this.count); - _this.getPages(_this.page || 1); - }); + event.stopPropagation(); + event.preventDefault(); - $scope.$watch('pager.page', function (newVal) { - if (newVal !== 0 && newVal <= _this.totalPages) { - _this.getPages(newVal); + $document.on('mousemove', mousemove); + $document.on('mouseup', mouseup); + }); + + function mousemove(event) { + event = event.originalEvent || event; + + var width = parent[0].clientWidth, + movementX = event.movementX || event.mozMovementX || event.screenX - prevScreenX, + newWidth = width + (movementX || 0); + + prevScreenX = event.screenX; + + if ((!$scope.minWidth || newWidth >= $scope.minWidth) && (!$scope.maxWidth || newWidth <= $scope.maxWidth)) { + parent.css({ + width: newWidth + 'px' + }); + } } - }); - this.getPages(this.page || 1); - } + function mouseup() { + if ($scope.onResize) { + $timeout(function () { + var width = parent[0].clientWidth; + if (width < $scope.minWidth) { + width = $scope.minWidth; + } + $scope.onResize({ width: width }); + }); + } - _createClass(PagerController, [{ - key: "calcTotalPages", - value: function calcTotalPages(size, count) { - var count = size < 1 ? 1 : Math.ceil(count / size); - this.totalPages = Math.max(count || 0, 1); - } - }, { - key: "selectPage", - value: function selectPage(num) { - if (num > 0 && num <= this.totalPages) { - this.page = num; - this.onPage({ - page: num - }); + $document.unbind('mousemove', mousemove); + $document.unbind('mouseup', mouseup); } + + $element.append(handle); } - }, { - key: "prevPage", - value: function prevPage() { - if (this.page > 1) { - this.selectPage(--this.page); + }; +} + +function SortableDirective($timeout) { + return { + restrict: 'A', + scope: { + isSortable: '=sortable', + onSortableSort: '&' + }, + link: function link($scope, $element, $attrs) { + var rootEl = $element[0], + dragEl, + nextEl, + dropEl; + + function isbefore(a, b) { + if (a.parentNode == b.parentNode) { + for (var cur = a; cur; cur = cur.previousSibling) { + if (cur === b) { + return true; + } + } + } + return false; } - } - }, { - key: "nextPage", - value: function nextPage() { - this.selectPage(++this.page); - } - }, { - key: "canPrevious", - value: function canPrevious() { - return this.page > 1; - } - }, { - key: "canNext", - value: function canNext() { - return this.page < this.totalPages; - } - }, { - key: "getPages", - value: function getPages(page) { - var pages = [], - startPage = 1, - endPage = this.totalPages, - maxSize = 5, - isMaxSized = maxSize < this.totalPages; - if (isMaxSized) { - startPage = (Math.ceil(page / maxSize) - 1) * maxSize + 1; - endPage = Math.min(startPage + maxSize - 1, this.totalPages); + function onDragEnter(e) { + var target = e.target; + if (isbefore(dragEl, target)) { + target.parentNode.insertBefore(dragEl, target); + } else if (target.nextSibling && target.hasAttribute('draggable')) { + target.parentNode.insertBefore(dragEl, target.nextSibling.nextSibling); + } } - for (var number = startPage; number <= endPage; number++) { - pages.push({ - number: number, - text: number, - active: number === page - }); + function onDragEnd(evt) { + evt.preventDefault(); + + dragEl.classList.remove('dt-clone'); + + $element.off('dragend', onDragEnd); + $element.off('dragenter', onDragEnter); + + if (nextEl !== dragEl.nextSibling) { + $scope.onSortableSort({ + event: evt, + columnId: _angular2.default.element(dragEl).attr('data-id') + }); + } } - this.pages = pages; - } - }]); + function onDragStart(evt) { + if (!$scope.isSortable) return false; + evt = evt.originalEvent || evt; - return PagerController; -}(); + dragEl = evt.target; + nextEl = dragEl.nextSibling; + dragEl.classList.add('dt-clone'); -function PagerDirective() { - return { - restrict: 'E', - controller: PagerController, - controllerAs: 'pager', - scope: true, - bindToController: { - page: '=', - size: '=', - count: '=', - onPage: '&' - }, - template: "
\n
    \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n {{pg.text}}\n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
\n
", - replace: true + evt.dataTransfer.effectAllowed = 'move'; + evt.dataTransfer.setData('Text', dragEl.textContent); + + $element.on('dragenter', onDragEnter); + $element.on('dragend', onDragEnd); + } + + $element.on('dragstart', onDragStart); + + $scope.$on('$destroy', function () { + $element.off('dragstart', onDragStart); + }); + } }; } -var FooterController = function () { - FooterController.$inject = ["$scope"]; - function FooterController($scope) { - var _this2 = this; +var TableDefaults = { + scrollbarV: true, - _classCallCheck(this, FooterController); + rowHeight: 30, - this.page = this.paging.offset + 1; - $scope.$watch('footer.paging.offset', function (newVal) { - _this2.offsetChanged(newVal); - }); - } + columnMode: 'standard', - _createClass(FooterController, [{ - key: "offsetChanged", - value: function offsetChanged(newVal) { - this.page = newVal + 1; - } - }, { - key: "onPaged", - value: function onPaged(page) { - this.paging.offset = page - 1; - this.onPage({ - offset: this.paging.offset, - size: this.paging.size - }); - } - }]); + loadingMessage: 'Loading...', - return FooterController; -}(); + emptyMessage: 'No data to display', -function FooterDirective() { - return { - restrict: 'E', - controller: FooterController, - controllerAs: 'footer', - scope: true, - bindToController: { - paging: '=', - onPage: '&' - }, - template: "
\n
{{footer.paging.count}} total
\n 1\">\n \n
", - replace: true - }; -} + headerHeight: 30, -var CellController = function () { - function CellController() { - _classCallCheck(this, CellController); - } + footerHeight: 0, - _createClass(CellController, [{ - key: "styles", - value: function styles() { - return { - width: this.column.width + 'px', - 'min-width': this.column.width + 'px' - }; - } - }, { - key: "cellClass", - value: function cellClass() { - var style = { - 'dt-tree-col': this.column.isTreeColumn - }; + paging: { + externalPaging: false, - if (this.column.className) { - style[this.column.className] = true; - } + size: undefined, - return style; - } - }, { - key: "treeClass", - value: function treeClass() { - return { - 'dt-tree-toggle': true, - 'icon-right': !this.expanded, - 'icon-down': this.expanded - }; - } - }, { - key: "onTreeToggled", - value: function onTreeToggled(evt) { - evt.stopPropagation(); - this.expanded = !this.expanded; - this.onTreeToggle({ - cell: { - value: this.value, - column: this.column, - expanded: this.expanded - } - }); - } - }, { - key: "onCheckboxChanged", - value: function onCheckboxChanged(event) { - event.stopPropagation(); - this.onCheckboxChange({ $event: event }); - } - }, { - key: "getValue", - value: function getValue() { - var val = this.column.cellDataGetter ? this.column.cellDataGetter(this.value) : this.value; + count: 0, - if (val === undefined || val === null) val = ''; - return val; - } - }]); + offset: 0, - return CellController; -}(); + loadingIndicator: false + }, -function CellDirective($rootScope, $compile, $log, $timeout) { - return { - restrict: 'E', - controller: CellController, - scope: true, - controllerAs: 'cell', - bindToController: { - options: '=', - value: '=', - selected: '=', - column: '=', - row: '=', - expanded: '=', - hasChildren: '=', - onTreeToggle: '&', - onCheckboxChange: '&' - }, - template: "
\n \n \n \n
", - replace: true, - compile: function compile() { - return { - pre: function pre($scope, $elm, $attrs, ctrl) { - var content = angular.element($elm[0].querySelector('.dt-cell-content')), - cellScope; + selectable: false, - if (ctrl.column.template || ctrl.column.cellRenderer) { - createCellScope(); - } + multiSelect: false, - $scope.$watch('cell.row', function () { - if (cellScope) { - cellScope.$destroy(); + checkboxSelection: false, - createCellScope(); + reorderable: true, - cellScope.$cell = ctrl.value; - cellScope.$row = ctrl.row; - cellScope.$column = ctrl.column; - cellScope.$$watchers = null; - } + internal: { + offsetX: 0, + offsetY: 0, + innerWidth: 0, + bodyHeight: 300 + } - if (ctrl.column.template) { - content.empty(); - var elm = angular.element("" + ctrl.column.template.trim() + ""); - content.append($compile(elm)(cellScope)); - } else if (ctrl.column.cellRenderer) { - content.empty(); - var elm = angular.element(ctrl.column.cellRenderer(cellScope, content)); - content.append($compile(elm)(cellScope)); - } else { - content[0].innerHTML = ctrl.getValue(); - } - }, true); +}; - function createCellScope() { - cellScope = ctrl.options.$outer.$new(false); - cellScope.getValue = ctrl.getValue; - } - } - }; - } +var ColumnDefaults = { + frozenLeft: false, + + frozenRight: false, + + className: undefined, + + headerClassName: undefined, + + flexGrow: 0, + + minWidth: 100, + + maxWidth: undefined, + + width: 150, + + resizable: true, + + comparator: undefined, + + sortable: true, + + sort: undefined, + + sortBy: undefined, + + headerRenderer: undefined, + + cellRenderer: undefined, + + cellDataGetter: undefined, + + isTreeColumn: false, + + isCheckboxColumn: false, + + headerCheckbox: false, + + canAutoResize: true + +}; + +var requestAnimFrame = function () { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { + window.setTimeout(callback, 1000 / 60); }; +}(); + +function ObjectId() { + var timestamp = (new Date().getTime() / 1000 | 0).toString(16); + return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { + return (Math.random() * 16 | 0).toString(16); + }).toLowerCase(); } -var cache = {}, - testStyle = document.createElement('div').style; +function ColumnsByPin(cols) { + var ret = { + left: [], + center: [], + right: [] + }; -var prefix = function () { - var styles = window.getComputedStyle(document.documentElement, ''), - pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || styles.OLink === '' && ['', 'o'])[1], - dom = 'WebKit|Moz|MS|O'.match(new RegExp('(' + pre + ')', 'i'))[1]; + for (var i = 0, len = cols.length; i < len; i++) { + var c = cols[i]; + if (c.frozenLeft) { + ret.left.push(c); + } else if (c.frozenRight) { + ret.right.push(c); + } else { + ret.center.push(c); + } + } + + return ret; +} + +function ColumnGroupWidths(groups, all) { return { - dom: dom, - lowercase: pre, - css: '-' + pre + '-', - js: pre[0].toUpperCase() + pre.substr(1) + left: ColumnTotalWidth(groups.left), + center: ColumnTotalWidth(groups.center), + right: ColumnTotalWidth(groups.right), + total: ColumnTotalWidth(all) }; -}(); +} + +function DeepValueGetter(obj, path) { + if (!obj || !path) return obj; + + var current = obj, + split = path.split('.'); + + if (split.length) { + for (var i = 0, len = split.length; i < len; i++) { + current = current[split[i]]; + } + } + + return current; +} function CamelCase(str) { str = str.replace(/[^a-zA-Z0-9 ]/g, " "); @@ -378,2020 +358,2441 @@ function CamelCase(str) { return str; } -function GetVendorPrefixedName(property) { - var name = CamelCase(property); - if (!cache[name]) { - if (testStyle[prefix.css + property] !== undefined) { - cache[name] = prefix.css + property; - } else if (testStyle[property] !== undefined) { - cache[name] = property; - } - } - return cache[name]; -} +function ScrollbarWidth() { + var outer = document.createElement("div"); + outer.style.visibility = "hidden"; + outer.style.width = "100px"; + outer.style.msOverflowStyle = "scrollbar"; + document.body.appendChild(outer); -var transform = GetVendorPrefixedName('transform'), - backfaceVisibility = GetVendorPrefixedName('backfaceVisibility'), - hasCSSTransforms = !!GetVendorPrefixedName('transform'), - hasCSS3DTransforms = !!GetVendorPrefixedName('perspective'), - ua = window.navigator.userAgent, - isSafari = /Safari\//.test(ua) && !/Chrome\//.test(ua); + var widthNoScroll = outer.offsetWidth; + outer.style.overflow = "scroll"; -function TranslateXY(styles, x, y) { - if (hasCSSTransforms) { - if (!isSafari && hasCSS3DTransforms) { - styles[transform] = "translate3d(" + x + "px, " + y + "px, 0)"; - styles[backfaceVisibility] = 'hidden'; - } else { - styles[CamelCase(transform)] = "translate(" + x + "px, " + y + "px)"; - } - } else { - styles.top = y + 'px'; - styles.left = x + 'px'; - } -} + var inner = document.createElement("div"); + inner.style.width = "100%"; + outer.appendChild(inner); -var GroupRowController = function () { - function GroupRowController() { - _classCallCheck(this, GroupRowController); - } + var widthWithScroll = inner.offsetWidth; + outer.parentNode.removeChild(outer); - _createClass(GroupRowController, [{ - key: "onGroupToggled", - value: function onGroupToggled(evt) { - evt.stopPropagation(); - this.onGroupToggle({ - group: this.row - }); + return widthNoScroll - widthWithScroll; +} + +function NextSortDirection(sortType, currentSort) { + if (sortType === 'single') { + if (currentSort === 'asc') { + return 'desc'; + } else { + return 'asc'; } - }, { - key: "treeClass", - value: function treeClass() { - return { - 'dt-tree-toggle': true, - 'icon-right': !this.expanded, - 'icon-down': this.expanded - }; + } else { + if (!currentSort) { + return 'asc'; + } else if (currentSort === 'asc') { + return 'desc'; + } else if (currentSort === 'desc') { + return undefined; } - }]); + } +} - return GroupRowController; -}(); +function ColumnTotalWidth(columns, prop) { + var totalWidth = 0; -function GroupRowDirective() { - return { - restrict: 'E', - controller: GroupRowController, - controllerAs: 'group', - bindToController: { - row: '=', - onGroupToggle: '&', - expanded: '=', - options: '=' - }, - scope: true, - replace: true, - template: "\n
\n \n \n \n \n
", - link: function link($scope, $elm, $attrs, ctrl) { - TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); + columns.forEach(function (c) { + var has = prop && c[prop]; + totalWidth = totalWidth + (has ? c[prop] : c.width); + }); - ctrl.options.internal.styleTranslator.register($scope.$index, $elm); - } - }; + return totalWidth; } -function DeepValueGetter(obj, path) { - if (!obj || !path) return obj; +function GetTotalFlexGrow(columns) { + var totalFlexGrow = 0; - var current = obj, - split = path.split('.'); + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; - if (split.length) { - for (var i = 0, len = split.length; i < len; i++) { - current = current[split[i]]; + try { + for (var _iterator = columns[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var c = _step.value; + + totalFlexGrow += c.flexGrow || 0; + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } } } - return current; + return totalFlexGrow; } -var RowController = function () { - function RowController() { - _classCallCheck(this, RowController); - } +function AdjustColumnWidths(allColumns, expectedWidth) { + var columnsWidth = ColumnTotalWidth(allColumns), + totalFlexGrow = GetTotalFlexGrow(allColumns), + colsByGroup = ColumnsByPin(allColumns); - _createClass(RowController, [{ - key: "getValue", - value: function getValue(col) { - if (!col.prop) return ''; - return DeepValueGetter(this.row, col.prop); - } - }, { - key: "onTreeToggled", - value: function onTreeToggled(cell) { - this.onTreeToggle({ - cell: cell, - row: this.row - }); - } - }, { - key: "stylesByGroup", - value: function stylesByGroup(group) { - var styles = { - width: this.columnWidths[group] + 'px' - }; + if (columnsWidth !== expectedWidth) { + ScaleColumns(colsByGroup, expectedWidth, totalFlexGrow); + } +} - if (group === 'left') { - TranslateXY(styles, this.options.internal.offsetX, 0); - } else if (group === 'right') { - var offset = (this.columnWidths.total - this.options.internal.innerWidth - this.options.internal.offsetX + this.options.internal.scrollBarWidth) * -1; - TranslateXY(styles, offset, 0); +function ScaleColumns(colsByGroup, maxWidth, totalFlexGrow) { + _angular2.default.forEach(colsByGroup, function (cols) { + cols.forEach(function (column) { + if (!column.canAutoResize) { + maxWidth -= column.width; + totalFlexGrow -= column.flexGrow; + } else { + column.width = 0; } + }); + }); - return styles; - } - }, { - key: "onCheckboxChanged", - value: function onCheckboxChanged(ev) { - this.onCheckboxChange({ - $event: ev, - row: this.row + var hasMinWidth = {}; + var remainingWidth = maxWidth; + + var _loop = function _loop() { + var widthPerFlexPoint = remainingWidth / totalFlexGrow; + remainingWidth = 0; + _angular2.default.forEach(colsByGroup, function (cols) { + cols.forEach(function (column, i) { + if (column.canAutoResize && !hasMinWidth[i]) { + var newWidth = column.width + column.flexGrow * widthPerFlexPoint; + if (column.minWidth !== undefined && newWidth < column.minWidth) { + remainingWidth += newWidth - column.minWidth; + column.width = column.minWidth; + hasMinWidth[i] = true; + } else { + column.width = newWidth; + } + } }); + }); + }; + + do { + _loop(); + } while (remainingWidth !== 0); +} + +function ForceFillColumnWidths(allColumns, expectedWidth, startIdx) { + var contentWidth = 0, + columnsToResize = startIdx > -1 ? allColumns.slice(startIdx, allColumns.length).filter(function (c) { + return c.canAutoResize; + }) : allColumns.filter(function (c) { + return c.canAutoResize; + }); + + allColumns.forEach(function (c) { + if (!c.canAutoResize) { + contentWidth += c.width; + } else { + contentWidth += c.$$oldWidth || c.width; } - }]); + }); - return RowController; -}(); + var remainingWidth = expectedWidth - contentWidth, + additionWidthPerColumn = remainingWidth / columnsToResize.length, + exceedsWindow = contentWidth > expectedWidth; -function RowDirective() { - return { - restrict: 'E', - controller: RowController, - controllerAs: 'rowCtrl', - scope: true, - bindToController: { - row: '=', - columns: '=', - columnWidths: '=', - expanded: '=', - selected: '=', - hasChildren: '=', - options: '=', - onCheckboxChange: '&', - onTreeToggle: '&' - }, - link: function link($scope, $elm, $attrs, ctrl) { - if (ctrl.row) { - TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); + columnsToResize.forEach(function (column) { + if (exceedsWindow) { + column.width = column.$$oldWidth || column.width; + } else { + if (!column.$$oldWidth) { + column.$$oldWidth = column.width; } - ctrl.options.internal.styleTranslator.register($scope.$index, $elm); - }, - template: "\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", - replace: true - }; + var newSize = column.$$oldWidth + additionWidthPerColumn; + if (column.minWith && newSize < column.minWidth) { + column.width = column.minWidth; + } else if (column.maxWidth && newSize > column.maxWidth) { + column.width = column.maxWidth; + } else { + column.width = newSize; + } + } + }); } -var KEYS = { - BACKSPACE: 8, - TAB: 9, - RETURN: 13, - ALT: 18, - ESC: 27, - SPACE: 32, - PAGE_UP: 33, - PAGE_DOWN: 34, - END: 35, - HOME: 36, - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40, - DELETE: 46, - COMMA: 188, - PERIOD: 190, - A: 65, - Z: 90, - ZERO: 48, - NUMPAD_0: 96, - NUMPAD_9: 105 -}; +var DataTableController = function () { + function DataTableController($scope, $filter, $log, $transclude) { + var _this = this; -var SelectionController = function () { - SelectionController.$inject = ["$scope"]; - function SelectionController($scope) { - _classCallCheck(this, SelectionController); + _classCallCheck(this, DataTableController); - this.body = $scope.body; - this.options = $scope.body.options; - this.selected = $scope.body.selected; - } + Object.assign(this, { + $scope: $scope, + $filter: $filter, + $log: $log + }); - _createClass(SelectionController, [{ - key: "keyDown", - value: function keyDown(ev, index, row) { - if (KEYS[ev.keyCode]) { - ev.preventDefault(); + this.defaults(); + + this.options.$outer = $scope.$parent; + + $scope.$watch('dt.options.columns', function (newVal, oldVal) { + _this.transposeColumnDefaults(); + + if (newVal.length !== oldVal.length) { + _this.adjustColumns(); } - if (ev.keyCode === KEYS.DOWN) { - var next = ev.target.nextElementSibling; - if (next) { - next.focus(); - } - } else if (ev.keyCode === KEYS.UP) { - var prev = ev.target.previousElementSibling; - if (prev) { - prev.focus(); + _this.calculateColumns(); + }, true); + + var watch = $scope.$watch('dt.rows', function (newVal) { + if (newVal) { + watch(); + _this.onSorted(); + } + }); + } + + _createClass(DataTableController, [{ + key: "defaults", + value: function defaults() { + var _this2 = this; + + this.expanded = this.expanded || {}; + + this.options = _angular2.default.extend(_angular2.default.copy(TableDefaults), this.options); + + _angular2.default.forEach(TableDefaults.paging, function (v, k) { + if (!_this2.options.paging[k]) { + _this2.options.paging[k] = v; } - } else if (ev.keyCode === KEYS.RETURN) { - this.selectRow(index, row); + }); + + if (this.options.selectable && this.options.multiSelect) { + this.selected = this.selected || []; } } }, { - key: "rowClicked", - value: function rowClicked(event, index, row) { - if (!this.options.checkboxSelection) { - this.selectRow(event, index, row); - } + key: "transposeColumnDefaults", + value: function transposeColumnDefaults() { + for (var i = 0, len = this.options.columns.length; i < len; i++) { + var column = this.options.columns[i]; + column.$id = ObjectId(); - this.body.onRowClick({ row: row }); + _angular2.default.forEach(ColumnDefaults, function (v, k) { + if (!column.hasOwnProperty(k)) { + column[k] = v; + } + }); + + if (column.name && !column.prop) { + column.prop = CamelCase(column.name); + } + + this.options.columns[i] = column; + } } }, { - key: "rowDblClicked", - value: function rowDblClicked(event, index, row) { - if (!this.options.checkboxSelection) { - event.preventDefault(); - this.selectRow(event, index, row); - } - - this.body.onRowDblClick({ row: row }); + key: "calculateColumns", + value: function calculateColumns() { + var columns = this.options.columns; + this.columnsByPin = ColumnsByPin(columns); + this.columnWidths = ColumnGroupWidths(this.columnsByPin, columns); } }, { - key: "onCheckboxChange", - value: function onCheckboxChange(event, index, row) { - this.selectRow(event, index, row); + key: "tableCss", + value: function tableCss() { + return { + 'fixed': this.options.scrollbarV, + 'selectable': this.options.selectable, + 'checkboxable': this.options.checkboxSelection + }; } }, { - key: "selectRow", - value: function selectRow(event, index, row) { - if (this.options.selectable) { - if (this.options.multiSelect) { - var isCtrlKeyDown = event.ctrlKey || event.metaKey, - isShiftKeyDown = event.shiftKey; + key: "adjustColumns", + value: function adjustColumns(forceIdx) { + var width = this.options.internal.innerWidth - this.options.internal.scrollBarWidth; - if (isShiftKeyDown) { - this.selectRowsBetween(index, row); - } else { - var idx = this.selected.indexOf(row); - if (idx > -1) { - this.selected.splice(idx, 1); - } else { - if (this.options.multiSelectOnShift && this.selected.length === 1) { - this.selected.splice(0, 1); - } - this.selected.push(row); - this.body.onSelect({ rows: [row] }); - } - } - this.prevIndex = index; - } else { - this.selected = row; - this.body.onSelect({ rows: [row] }); - } + if (this.options.columnMode === 'force') { + ForceFillColumnWidths(this.options.columns, width, forceIdx); + } else if (this.options.columnMode === 'flex') { + AdjustColumnWidths(this.options.columns, width); } } }, { - key: "selectRowsBetween", - value: function selectRowsBetween(index) { - var reverse = index < this.prevIndex, - selecteds = []; - - for (var i = 0, len = this.body.rows.length; i < len; i++) { - var row = this.body.rows[i], - greater = i >= this.prevIndex && i <= index, - lesser = i <= this.prevIndex && i >= index; + key: "calculatePageSize", + value: function calculatePageSize() { + this.options.paging.size = Math.ceil(this.options.internal.bodyHeight / this.options.rowHeight) + 1; + } + }, { + key: "onSorted", + value: function onSorted() { + if (!this.rows) return; - var range = {}; - if (reverse) { - range = { - start: index, - end: this.prevIndex - index - }; - } else { - range = { - start: this.prevIndex, - end: index + 1 - }; + var sorts = this.options.columns.filter(function (c) { + return c.sort; + }).sort(function (a, b) { + if (a.sortPriority && b.sortPriority) { + if (a.sortPriority > b.sortPriority) return 1; + if (a.sortPriority < b.sortPriority) return -1; + } else if (a.sortPriority) { + return -1; + } else if (b.sortPriority) { + return 1; } - if (reverse && lesser || !reverse && greater) { - var idx = this.selected.indexOf(row); + return 0; + }).map(function (c, i) { + c.sortPriority = i + 1; + return c; + }); - if (reverse && idx > -1) { - this.selected.splice(idx, 1); - continue; - } + if (sorts.length) { + this.onSort({ sorts: sorts }); - if (i >= range.start && i < range.end) { - if (idx === -1) { - this.selected.push(row); - selecteds.push(row); + if (this.options.onSort) { + this.options.onSort(sorts); + } + + var clientSorts = []; + for (var i = 0, len = sorts.length; i < len; i++) { + var c = sorts[i]; + if (c.comparator !== false) { + var dir = c.sort === 'asc' ? '' : '-'; + if (c.sortBy !== undefined) { + clientSorts.push(dir + c.sortBy); + } else { + clientSorts.push(dir + c.prop); } } } + + if (clientSorts.length) { + var _rows; + + var sortedValues = this.$filter('orderBy')(this.rows, clientSorts); + this.rows.splice(0, this.rows.length); + (_rows = this.rows).push.apply(_rows, _toConsumableArray(sortedValues)); + } } - this.body.onSelect({ rows: selecteds }); + this.options.internal.setYOffset(0); } - }]); - - return SelectionController; -}(); + }, { + key: "onTreeToggled", + value: function onTreeToggled(row, cell) { + this.onTreeToggle({ + row: row, + cell: cell + }); + } + }, { + key: "onBodyPage", + value: function onBodyPage(offset, size) { + this.onPage({ + offset: offset, + size: size + }); + } + }, { + key: "onFooterPage", + value: function onFooterPage(offset, size) { + var pageBlockSize = this.options.rowHeight * size, + offsetY = pageBlockSize * offset; -function SelectionDirective() { - return { - controller: SelectionController, - restrict: 'A', - require: '^dtBody', - controllerAs: 'selCtrl' - }; -} - -var requestAnimFrame = function () { - return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { - window.setTimeout(callback, 1000 / 60); - }; -}(); - -var StyleTranslator = function () { - function StyleTranslator(height) { - _classCallCheck(this, StyleTranslator); + this.options.internal.setYOffset(offsetY); + } + }, { + key: "onHeaderCheckboxChange", + value: function onHeaderCheckboxChange() { + if (this.rows) { + var matches = this.selected.length === this.rows.length; + this.selected.splice(0, this.selected.length); - this.height = height; - this.map = new Map(); - } + if (!matches) { + var _selected; - _createClass(StyleTranslator, [{ - key: "update", - value: function update(rows) { - var n = 0; - while (n <= this.map.size) { - var dom = this.map.get(n); - var model = rows[n]; - if (dom && model) { - TranslateXY(dom[0].style, 0, model.$$index * this.height); + (_selected = this.selected).push.apply(_selected, _toConsumableArray(this.rows)); } - n++; } } }, { - key: "register", - value: function register(idx, dom) { - this.map.set(idx, dom); + key: "isAllRowsSelected", + value: function isAllRowsSelected() { + if (this.rows) return false; + return this.selected.length === this.rows.length; } - }]); - - return StyleTranslator; -}(); - -function ScrollerDirective($timeout, $rootScope) { - return { - restrict: 'E', - require: '^dtBody', - transclude: true, - replace: true, - template: "
", - link: function link($scope, $elm, $attrs, ctrl) { - var ticking = false, - lastScrollY = 0, - lastScrollX = 0, - parent = $elm.parent(); - - ctrl.options.internal.styleTranslator = new StyleTranslator(ctrl.options.rowHeight); - - ctrl.options.internal.setYOffset = function (offsetY) { - parent[0].scrollTop = offsetY; - }; - - function update() { - ctrl.options.internal.offsetY = lastScrollY; - ctrl.options.internal.offsetX = lastScrollX; - ctrl.updatePage(); - - if (ctrl.options.scrollbarV) { - ctrl.getRows(); - } - - ctrl.options.$outer.$digest(); - - ticking = false; - }; + }, { + key: "onResized", + value: function onResized(column, width) { + var idx = this.options.columns.indexOf(column); + if (idx > -1) { + var column = this.options.columns[idx]; + column.width = width; + column.canAutoResize = false; - function requestTick() { - if (!ticking) { - requestAnimFrame(update); - ticking = true; - } - }; + this.adjustColumns(idx); + this.calculateColumns(); + } - parent.on('scroll', function (ev) { - lastScrollY = this.scrollTop; - lastScrollX = this.scrollLeft; - requestTick(); + if (this.onColumnResize) { + this.onColumnResize({ + column: column, + width: width + }); + } + } + }, { + key: "onSelected", + value: function onSelected(rows) { + this.onSelect({ + rows: rows }); - - $scope.$on('$destroy', function () { - parent.off('scroll'); + } + }, { + key: "onRowClicked", + value: function onRowClicked(row) { + this.onRowClick({ + row: row + }); + } + }, { + key: "onRowDblClicked", + value: function onRowDblClicked(row) { + this.onRowDblClick({ + row: row }); + } + }]); - $scope.scrollerStyles = function () { - if (ctrl.options.scrollbarV) { - return { - height: ctrl.count * ctrl.options.rowHeight + 'px' - }; - } - }; + return DataTableController; +}(); + +function throttle(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + options || (options = {}); + var later = function later() { + previous = options.leading === false ? 0 : new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function () { + var now = new Date(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); } + return result; }; } -var BodyController = function () { - BodyController.$inject = ["$scope", "$timeout"]; - function BodyController($scope, $timeout) { - var _this3 = this; - - _classCallCheck(this, BodyController); +var DataTableService = { + columns: {}, + dTables: {}, - this.$scope = $scope; - this.tempRows = []; + saveColumns: function saveColumns(id, columnElms) { + if (columnElms && columnElms.length) { + var columnsArray = [].slice.call(columnElms); + this.dTables[id] = columnsArray; + } + }, + buildColumns: function buildColumns(scope, parse) { + var _this3 = this; - this.treeColumn = this.options.columns.find(function (c) { - return c.isTreeColumn; - }); + _angular2.default.forEach(this.dTables, function (columnElms, id) { + _this3.columns[id] = []; - this.groupColumn = this.options.columns.find(function (c) { - return c.group; - }); + _angular2.default.forEach(columnElms, function (c) { + var column = {}; - $scope.$watchCollection('body.rows', this.rowsUpdated.bind(this)); + var visible = true; - if (this.options.scrollbarV || !this.options.scrollbarV && this.options.paging.externalPaging) { - var sized = false; - $scope.$watch('body.options.paging.size', function (newVal, oldVal) { - if (!sized || newVal > oldVal) { - _this3.getRows(); - sized = true; - } - }); + _angular2.default.forEach(c.attributes, function (attr) { + var attrName = CamelCase(attr.name); - $scope.$watch('body.options.paging.count', function (count) { - _this3.count = count; - _this3.updatePage(); - }); + switch (attrName) { + case 'class': + column.className = attr.value; + break; + case 'name': + case 'prop': + column[attrName] = attr.value; + break; + case 'headerRenderer': + case 'cellRenderer': + case 'cellDataGetter': + column[attrName] = parse(attr.value); + break; + case 'visible': + visible = parse(attr.value)(scope); + break; + default: + column[attrName] = parse(attr.value)(scope); + break; + } + }); - $scope.$watch('body.options.paging.offset', function (newVal) { - if (_this3.options.paging.size) { - _this3.onPage({ - offset: newVal, - size: _this3.options.paging.size - }); + var header = c.getElementsByTagName('column-header'); + if (header.length) { + column.headerTemplate = header[0].innerHTML; + c.removeChild(header[0]); } - }); - } - } - _createClass(BodyController, [{ - key: "rowsUpdated", - value: function rowsUpdated(newVal, oldVal) { - if (newVal) { - if (!this.options.paging.externalPaging) { - this.options.paging.count = newVal.length; + if (c.innerHTML !== '') { + column.template = c.innerHTML; } - this.count = this.options.paging.count; + if (visible) _this3.columns[id].push(column); + }); + }); - if (this.treeColumn || this.groupColumn) { - this.buildRowsByGroup(); - } + this.dTables = {}; + } +}; - if (this.options.scrollbarV) { - var refresh = newVal && oldVal && (newVal.length === oldVal.length || newVal.length < oldVal.length); +function DataTableDirective($window, $timeout, $parse) { + return { + restrict: 'E', + replace: true, + controller: DataTableController, + scope: true, + bindToController: { + options: '=', + rows: '=', + selected: '=?', + expanded: '=?', + onSelect: '&', + onSort: '&', + onTreeToggle: '&', + onPage: '&', + onRowClick: '&', + onRowDblClick: '&', + onColumnResize: '&' + }, + controllerAs: 'dt', + template: function template(element) { + var columns = element[0].getElementsByTagName('column'), + id = ObjectId(); + DataTableService.saveColumns(id, columns); - this.getRows(refresh); - } else { - var rows = this.rows; + return "
\n \n \n \n \n \n \n
"; + }, + compile: function compile(tElem, tAttrs) { + return { + pre: function pre($scope, $elm, $attrs, ctrl) { + DataTableService.buildColumns($scope, $parse); - if (this.treeColumn) { - rows = this.buildTree(); - } else if (this.groupColumn) { - rows = this.buildGroups(); + var id = $elm.attr('data-column-id'), + columns = DataTableService.columns[id]; + if (columns) { + ctrl.options.columns = columns; } - if (this.options.paging.externalPaging) { - var idxs = this.getFirstLastIndexes(), - idx = idxs.first; + ctrl.transposeColumnDefaults(); + ctrl.options.internal.scrollBarWidth = ScrollbarWidth(); - this.tempRows.splice(0, this.tempRows.length); - while (idx < idxs.last) { - this.tempRows.push(rows[idx++]); + function resize() { + var rect = $elm[0].getBoundingClientRect(); + + ctrl.options.internal.innerWidth = Math.floor(rect.width); + + if (ctrl.options.scrollbarV) { + var height = rect.height; + + if (ctrl.options.headerHeight) { + height = height - ctrl.options.headerHeight; + } + + if (ctrl.options.footerHeight) { + height = height - ctrl.options.footerHeight; + } + + ctrl.options.internal.bodyHeight = height; + ctrl.calculatePageSize(); } - } else { - var _tempRows; - this.tempRows.splice(0, this.tempRows.length); - (_tempRows = this.tempRows).push.apply(_tempRows, _toConsumableArray(rows)); + ctrl.adjustColumns(); } + + $window.addEventListener('resize', throttle(function () { + $timeout(resize); + })); + + var checkVisibility = function checkVisibility() { + var bounds = $elm[0].getBoundingClientRect(), + visible = bounds.width && bounds.height; + if (visible) resize();else $timeout(checkVisibility, 100); + }; + checkVisibility(); + + $elm.addClass('dt-loaded'); + + $scope.$on('$destroy', function () { + _angular2.default.element($window).off('resize'); + }); } - } + }; } - }, { - key: "getFirstLastIndexes", - value: function getFirstLastIndexes() { - var firstRowIndex, endIndex; + }; +} - if (this.options.scrollbarV) { - firstRowIndex = Math.max(Math.floor((this.options.internal.offsetY || 0) / this.options.rowHeight, 0), 0); - endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); - } else { - if (this.options.paging.externalPaging) { - firstRowIndex = Math.max(this.options.paging.offset * this.options.paging.size, 0); - endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); - } else { - endIndex = this.count; - } - } +var cache = {}; +var testStyle = document.createElement('div').style; - return { - first: firstRowIndex, - last: endIndex - }; +var prefix = function () { + var styles = window.getComputedStyle(document.documentElement, ''), + pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || styles.OLink === '' && ['', 'o'])[1], + dom = 'WebKit|Moz|MS|O'.match(new RegExp('(' + pre + ')', 'i'))[1]; + return { + dom: dom, + lowercase: pre, + css: '-' + pre + '-', + js: pre[0].toUpperCase() + pre.substr(1) + }; +}(); + +function GetVendorPrefixedName(property) { + var name = CamelCase(property); + if (!cache[name]) { + if (testStyle[prefix.css + property] !== undefined) { + cache[name] = prefix.css + property; + } else if (testStyle[property] !== undefined) { + cache[name] = property; } - }, { - key: "updatePage", - value: function updatePage() { - var curPage = this.options.paging.offset, - idxs = this.getFirstLastIndexes(); + } + return cache[name]; +} - if (this.options.internal.oldScrollPosition === undefined) { - this.options.internal.oldScrollPosition = 0; - } +var transform = GetVendorPrefixedName('transform'); +var backfaceVisibility = GetVendorPrefixedName('backfaceVisibility'); +var hasCSSTransforms = !!GetVendorPrefixedName('transform'); +var hasCSS3DTransforms = !!GetVendorPrefixedName('perspective'); +var ua = window.navigator.userAgent; +var isSafari = /Safari\//.test(ua) && !/Chrome\//.test(ua); - var oldScrollPosition = this.options.internal.oldScrollPosition, - newPage = idxs.first / this.options.paging.size; +function TranslateXY(styles, x, y) { + if (hasCSSTransforms) { + if (!isSafari && hasCSS3DTransforms) { + styles[transform] = "translate3d(" + x + "px, " + y + "px, 0)"; + styles[backfaceVisibility] = 'hidden'; + } else { + styles[CamelCase(transform)] = "translate(" + x + "px, " + y + "px)"; + } + } else { + styles.top = y + 'px'; + styles.left = x + 'px'; + } +} - this.options.internal.oldScrollPosition = newPage; +var HeaderController = function () { + function HeaderController() { + _classCallCheck(this, HeaderController); + } - if (newPage < oldScrollPosition) { - newPage = Math.floor(newPage); - } else if (newPage > oldScrollPosition) { - newPage = Math.ceil(newPage); - } else { - newPage = curPage; - } + _createClass(HeaderController, [{ + key: "styles", + value: function styles() { + return { + width: this.options.internal.innerWidth + 'px', + height: this.options.headerHeight + 'px' + }; + } + }, { + key: "innerStyles", + value: function innerStyles() { + return { + width: this.columnWidths.total + 'px' + }; + } + }, { + key: "onSorted", + value: function onSorted(sortedColumn) { + if (this.options.sortType === 'single') { + var unsortColumn = function unsortColumn(column) { + if (column !== sortedColumn) { + column.sort = undefined; + } + }; - if (!isNaN(newPage)) { - this.options.paging.offset = newPage; + this.columns.left.forEach(unsortColumn); + this.columns.center.forEach(unsortColumn); + this.columns.right.forEach(unsortColumn); } + + this.onSort({ + column: sortedColumn + }); } }, { - key: "calculateDepth", - value: function calculateDepth(row) { - var depth = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; + key: "stylesByGroup", + value: function stylesByGroup(group) { + var styles = { + width: this.columnWidths[group] + 'px' + }; - var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; - var prop = this.treeColumn.prop; - if (!row[parentProp]) { - return depth; - } - if (row.$$depth) { - return row.$$depth + depth; + if (group === 'center') { + TranslateXY(styles, this.options.internal.offsetX * -1, 0); + } else if (group === 'right') { + var offset = (this.columnWidths.total - this.options.internal.innerWidth) * -1; + TranslateXY(styles, offset, 0); } - var cachedParent = this.index[row[parentProp]]; - if (cachedParent) { - depth += 1; - return this.calculateDepth(cachedParent, depth); - } - for (var i = 0, len = this.rows.length; i < len; i++) { - var parent = this.rows[i]; - if (parent[prop] == row[parentProp]) { - depth += 1; - return this.calculateDepth(parent, depth); - } - } - return depth; + return styles; } }, { - key: "buildRowsByGroup", - value: function buildRowsByGroup() { - this.index = {}; - this.rowsByGroup = {}; + key: "onCheckboxChanged", + value: function onCheckboxChanged() { + this.onCheckboxChange(); + } + }, { + key: "onResized", + value: function onResized(column, width) { + this.onResize({ + column: column, + width: width + }); + } + }]); - var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; + return HeaderController; +}(); - for (var i = 0, len = this.rows.length; i < len; i++) { - var row = this.rows[i]; +function HeaderDirective($timeout) { + return { + restrict: 'E', + controller: HeaderController, + controllerAs: 'header', + scope: true, + bindToController: { + options: '=', + columns: '=', + columnWidths: '=', + onSort: '&', + onResize: '&', + onCheckboxChange: '&' + }, + template: "\n
\n\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
", + replace: true, + link: function link($scope, $elm, $attrs, ctrl) { - var relVal = row[parentProp]; - if (relVal) { - if (this.rowsByGroup[relVal]) { - this.rowsByGroup[relVal].push(row); - } else { - this.rowsByGroup[relVal] = [row]; + $scope.columnsResorted = function (event, columnId) { + var col = findColumnById(columnId), + parent = _angular2.default.element(event.currentTarget), + newIdx = -1; + + _angular2.default.forEach(parent.children(), function (c, i) { + if (columnId === _angular2.default.element(c).attr('data-id')) { + newIdx = i; } - } + }); - if (this.treeColumn) { - var prop = this.treeColumn.prop; - this.index[row[prop]] = row; + $timeout(function () { + _angular2.default.forEach(ctrl.columns, function (group) { + var idx = group.indexOf(col); + if (idx > -1) { + var curColAtIdx = group[newIdx], + siblingIdx = ctrl.options.columns.indexOf(curColAtIdx), + curIdx = ctrl.options.columns.indexOf(col); - if (row[parentProp] === undefined) { - row.$$depth = 0; - } else { - var parent = this.index[row[parentProp]]; - if (parent === undefined) { - for (var j = 0; j < len; j++) { - if (this.rows[j][prop] == relVal) { - parent = this.rows[j]; - break; - } - } - } - if (parent.$$depth === undefined) { - parent.$$depth = this.calculateDepth(parent); - } - row.$$depth = parent.$$depth + 1; - if (parent.$$children) { - parent.$$children.push(row[prop]); - } else { - parent.$$children = [row[prop]]; - } - } - } - } - } - }, { - key: "buildGroups", - value: function buildGroups() { - var _this4 = this; + ctrl.options.columns.splice(curIdx, 1); + ctrl.options.columns.splice(siblingIdx, 0, col); - var temp = []; + return false; + } + }); + }); + }; - angular.forEach(this.rowsByGroup, function (v, k) { - temp.push({ - name: k, - group: true + var findColumnById = function findColumnById(columnId) { + var columns = ctrl.columns.left.concat(ctrl.columns.center).concat(ctrl.columns.right); + return columns.find(function (c) { + return c.$id === columnId; }); + }; + } + }; +} - if (_this4.expanded[k]) { - temp.push.apply(temp, _toConsumableArray(v)); - } - }); +var HeaderCellController = function () { + function HeaderCellController() { + _classCallCheck(this, HeaderCellController); + } - return temp; + _createClass(HeaderCellController, [{ + key: "styles", + value: function styles() { + return { + width: this.column.width + 'px', + minWidth: this.column.minWidth + 'px', + maxWidth: this.column.maxWidth + 'px', + height: this.column.height + 'px' + }; } }, { - key: "isSelected", - value: function isSelected(row) { - var selected = false; + key: "cellClass", + value: function cellClass() { + var cls = { + 'sortable': this.column.sortable, + 'resizable': this.column.resizable + }; - if (this.options.selectable) { - if (this.options.multiSelect) { - selected = this.selected.indexOf(row) > -1; - } else { - selected = this.selected === row; - } + if (this.column.headerClassName) { + cls[this.column.headerClassName] = true; } - return selected; + return cls; } }, { - key: "buildTree", - value: function buildTree() { - var temp = [], - self = this; + key: "onSorted", + value: function onSorted() { + if (this.column.sortable) { + this.column.sort = NextSortDirection(this.sortType, this.column.sort); - function addChildren(fromArray, toArray, level) { - fromArray.forEach(function (row) { - var relVal = row[self.treeColumn.relationProp], - key = row[self.treeColumn.prop], - groupRows = self.rowsByGroup[key], - expanded = self.expanded[key]; + if (this.column.sort === undefined) { + this.column.sortPriority = undefined; + } - if (level > 0 || !relVal) { - toArray.push(row); - if (groupRows && groupRows.length > 0 && expanded) { - addChildren(groupRows, toArray, level + 1); - } - } + this.onSort({ + column: this.column }); } - - addChildren(this.rows, temp, 0); - - return temp; } }, { - key: "getRows", - value: function getRows(refresh) { - if ((this.treeColumn || this.groupColumn) && !this.rowsByGroup) { - return false; - } + key: "sortClass", + value: function sortClass() { + return { + 'sort-btn': true, + 'sort-asc icon-down': this.column.sort === 'asc', + 'sort-desc icon-up': this.column.sort === 'desc' + }; + } + }, { + key: "onResized", + value: function onResized(width, column) { + this.onResize({ + column: column, + width: width + }); + } + }, { + key: "onCheckboxChange", + value: function onCheckboxChange() { + this.onCheckboxChanged(); + } + }]); - var temp; + return HeaderCellController; +}(); - if (this.treeColumn) { - temp = this.treeTemp || []; +function HeaderCellDirective($compile) { + return { + restrict: 'E', + controller: HeaderCellController, + controllerAs: 'hcell', + scope: true, + bindToController: { + options: '=', + column: '=', + onCheckboxChange: '&', + onSort: '&', + sortType: '=', + onResize: '&', + selected: '=' + }, + replace: true, + template: "
\n
\n \n \n \n \n
\n
", + compile: function compile() { + return { + pre: function pre($scope, $elm, $attrs, ctrl) { + var label = $elm[0].querySelector('.dt-header-cell-label'), + cellScope = void 0; - if (refresh || !this.treeTemp) { - this.treeTemp = temp = this.buildTree(); - this.count = temp.length; + if (ctrl.column.headerTemplate || ctrl.column.headerRenderer) { + cellScope = ctrl.options.$outer.$new(false); - this.tempRows.splice(0, this.tempRows.length); - } - } else if (this.groupColumn) { - temp = this.groupsTemp || []; + cellScope.$header = ctrl.column.name; + cellScope.$index = $scope.$index; + } - if (refresh || !this.groupsTemp) { - this.groupsTemp = temp = this.buildGroups(); - this.count = temp.length; - } - } else { - temp = this.rows; - if (refresh === true) { - this.tempRows.splice(0, this.tempRows.length); + if (ctrl.column.headerTemplate) { + var elm = _angular2.default.element("" + ctrl.column.headerTemplate.trim() + ""); + _angular2.default.element(label).append($compile(elm)(cellScope)); + } else if (ctrl.column.headerRenderer) { + var _elm = _angular2.default.element(ctrl.column.headerRenderer($elm)); + _angular2.default.element(label).append($compile(_elm)(cellScope)[0]); + } else { + var val = ctrl.column.name; + if (val === undefined || val === null) val = ''; + label.textContent = val; + } } - } + }; + } + }; +} - var idx = 0, - indexes = this.getFirstLastIndexes(), - rowIndex = indexes.first; +var BodyController = function () { + function BodyController($scope, $timeout) { + var _this4 = this; - this.tempRows.splice(0, indexes.last - indexes.first); + _classCallCheck(this, BodyController); - while (rowIndex < indexes.last && rowIndex < this.count) { - var row = temp[rowIndex]; - if (row) { - row.$$index = rowIndex; - this.tempRows[idx] = row; - } - idx++; - rowIndex++; - } + this.$scope = $scope; + this.tempRows = []; - this.options.internal.styleTranslator.update(this.tempRows); + this.treeColumn = this.options.columns.find(function (c) { + return c.isTreeColumn; + }); - return this.tempRows; - } - }, { - key: "styles", - value: function styles() { - var styles = { - width: this.options.internal.innerWidth + 'px' - }; + this.groupColumn = this.options.columns.find(function (c) { + return c.group; + }); - if (!this.options.scrollbarV) { - styles.overflowY = 'hidden'; - } else if (this.options.scrollbarH === false) { - styles.overflowX = 'hidden'; - } + $scope.$watchCollection('body.rows', this.rowsUpdated.bind(this)); - if (this.options.scrollbarV) { - styles.height = this.options.internal.bodyHeight + 'px'; - } + if (this.options.scrollbarV || !this.options.scrollbarV && this.options.paging.externalPaging) { + var sized = false; + $scope.$watch('body.options.paging.size', function (newVal, oldVal) { + if (!sized || newVal > oldVal) { + _this4.getRows(); + sized = true; + } + }); - return styles; + $scope.$watch('body.options.paging.count', function (count) { + _this4.count = count; + _this4.updatePage(); + }); + + $scope.$watch('body.options.paging.offset', function (newVal) { + if (_this4.options.paging.size) { + _this4.onPage({ + offset: newVal, + size: _this4.options.paging.size + }); + } + }); } - }, { - key: "rowStyles", - value: function rowStyles(row) { - var styles = {}; + } - if (this.options.rowHeight === 'auto') { - styles.height = this.options.rowHeight + 'px'; - } + _createClass(BodyController, [{ + key: "rowsUpdated", + value: function rowsUpdated(newVal, oldVal) { + if (newVal) { + if (!this.options.paging.externalPaging) { + this.options.paging.count = newVal.length; + } - return styles; - } - }, { - key: "groupRowStyles", - value: function groupRowStyles(row) { - var styles = this.rowStyles(row); - styles.width = this.columnWidths.total + 'px'; - return styles; - } - }, { - key: "rowClasses", - value: function rowClasses(row) { - var styles = { - 'selected': this.isSelected(row), - 'dt-row-even': row && row.$$index % 2 === 0, - 'dt-row-odd': row && row.$$index % 2 !== 0 - }; + this.count = this.options.paging.count; - if (this.treeColumn) { - styles['dt-leaf'] = this.rowsByGroup[row[this.treeColumn.relationProp]]; + if (this.treeColumn || this.groupColumn) { + this.buildRowsByGroup(); + } - styles['dt-has-leafs'] = this.rowsByGroup[row[this.treeColumn.prop]]; + if (this.options.scrollbarV) { + var refresh = newVal && oldVal && (newVal.length === oldVal.length || newVal.length < oldVal.length); - styles['dt-depth-' + row.$$depth] = true; - } + this.getRows(refresh); + } else { + var rows = this.rows; - return styles; - } - }, { - key: "getRowValue", - value: function getRowValue(idx) { - return this.tempRows[idx]; - } - }, { - key: "getRowExpanded", - value: function getRowExpanded(row) { - if (this.treeColumn) { - return this.expanded[row[this.treeColumn.prop]]; - } else if (this.groupColumn) { - return this.expanded[row.name]; - } - } - }, { - key: "getRowHasChildren", - value: function getRowHasChildren(row) { - if (!this.treeColumn) return; - var children = this.rowsByGroup[row[this.treeColumn.prop]]; - return children !== undefined || children && !children.length; - } - }, { - key: "onTreeToggled", - value: function onTreeToggled(row, cell) { - var val = row[this.treeColumn.prop]; - this.expanded[val] = !this.expanded[val]; + if (this.treeColumn) { + rows = this.buildTree(); + } else if (this.groupColumn) { + rows = this.buildGroups(); + } - if (this.options.scrollbarV) { - this.getRows(true); - } else { - var _tempRows2; + if (this.options.paging.externalPaging) { + var idxs = this.getFirstLastIndexes(), + idx = idxs.first; - var values = this.buildTree(); - this.tempRows.splice(0, this.tempRows.length); - (_tempRows2 = this.tempRows).push.apply(_tempRows2, _toConsumableArray(values)); - } + this.tempRows.splice(0, this.tempRows.length); + while (idx < idxs.last) { + this.tempRows.push(rows[idx++]); + } + } else { + var _tempRows; - this.onTreeToggle({ - row: row, - cell: cell - }); + this.tempRows.splice(0, this.tempRows.length); + (_tempRows = this.tempRows).push.apply(_tempRows, _toConsumableArray(rows)); + } + } + } } }, { - key: "onGroupToggle", - value: function onGroupToggle(row) { - this.expanded[row.name] = !this.expanded[row.name]; + key: "getFirstLastIndexes", + value: function getFirstLastIndexes() { + var firstRowIndex, endIndex; if (this.options.scrollbarV) { - this.getRows(true); + firstRowIndex = Math.max(Math.floor((this.options.internal.offsetY || 0) / this.options.rowHeight, 0), 0); + endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); } else { - var _tempRows3; - - var values = this.buildGroups(); - this.tempRows.splice(0, this.tempRows.length); - (_tempRows3 = this.tempRows).push.apply(_tempRows3, _toConsumableArray(values)); + if (this.options.paging.externalPaging) { + firstRowIndex = Math.max(this.options.paging.offset * this.options.paging.size, 0); + endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); + } else { + endIndex = this.count; + } } - } - }]); - - return BodyController; -}(); - -function BodyDirective($timeout) { - return { - restrict: 'E', - controller: BodyController, - controllerAs: 'body', - bindToController: { - columns: '=', - columnWidths: '=', - rows: '=', - options: '=', - selected: '=?', - expanded: '=?', - onPage: '&', - onTreeToggle: '&', - onSelect: '&', - onRowClick: '&', - onRowDblClick: '&' - }, - scope: true, - template: "\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n
" - }; -} -function NextSortDirection(sortType, currentSort) { - if (sortType === 'single') { - if (currentSort === 'asc') { - return 'desc'; - } else { - return 'asc'; - } - } else { - if (!currentSort) { - return 'asc'; - } else if (currentSort === 'asc') { - return 'desc'; - } else if (currentSort === 'desc') { - return undefined; - } - } -} - -var HeaderCellController = function () { - function HeaderCellController() { - _classCallCheck(this, HeaderCellController); - } - - _createClass(HeaderCellController, [{ - key: "styles", - value: function styles() { return { - width: this.column.width + 'px', - minWidth: this.column.minWidth + 'px', - maxWidth: this.column.maxWidth + 'px', - height: this.column.height + 'px' + first: firstRowIndex, + last: endIndex }; } }, { - key: "cellClass", - value: function cellClass() { - var cls = { - 'sortable': this.column.sortable, - 'resizable': this.column.resizable - }; + key: "updatePage", + value: function updatePage() { + var curPage = this.options.paging.offset, + idxs = this.getFirstLastIndexes(); - if (this.column.headerClassName) { - cls[this.column.headerClassName] = true; + if (this.options.internal.oldScrollPosition === undefined) { + this.options.internal.oldScrollPosition = 0; } - return cls; - } - }, { - key: "onSorted", - value: function onSorted() { - if (this.column.sortable) { - this.column.sort = NextSortDirection(this.sortType, this.column.sort); + var oldScrollPosition = this.options.internal.oldScrollPosition, + newPage = idxs.first / this.options.paging.size; - if (this.column.sort === undefined) { - this.column.sortPriority = undefined; - } + this.options.internal.oldScrollPosition = newPage; - this.onSort({ - column: this.column - }); - } - } - }, { - key: "sortClass", - value: function sortClass() { - return { - 'sort-btn': true, - 'sort-asc icon-down': this.column.sort === 'asc', - 'sort-desc icon-up': this.column.sort === 'desc' - }; - } - }, { - key: "onResized", - value: function onResized(width, column) { - this.onResize({ - column: column, - width: width - }); + if (newPage < oldScrollPosition) { + newPage = Math.floor(newPage); + } else if (newPage > oldScrollPosition) { + newPage = Math.ceil(newPage); + } else { + newPage = curPage; + } + + if (!isNaN(newPage)) { + this.options.paging.offset = newPage; + } } }, { - key: "onCheckboxChange", - value: function onCheckboxChange() { - this.onCheckboxChanged(); - } - }]); + key: "calculateDepth", + value: function calculateDepth(row) { + var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - return HeaderCellController; -}(); + var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; + var prop = this.treeColumn.prop; + if (!row[parentProp]) { + return depth; + } + if (row.$$depth) { + return row.$$depth + depth; + } -function HeaderCellDirective($compile) { - return { - restrict: 'E', - controller: HeaderCellController, - controllerAs: 'hcell', - scope: true, - bindToController: { - options: '=', - column: '=', - onCheckboxChange: '&', - onSort: '&', - sortType: '=', - onResize: '&', - selected: '=' - }, - replace: true, - template: "
\n
\n \n \n \n \n
\n
", - compile: function compile() { - return { - pre: function pre($scope, $elm, $attrs, ctrl) { - var label = $elm[0].querySelector('.dt-header-cell-label'), - cellScope = void 0; + var cachedParent = this.index[row[parentProp]]; + if (cachedParent) { + depth += 1; + return this.calculateDepth(cachedParent, depth); + } + for (var i = 0, len = this.rows.length; i < len; i++) { + var parent = this.rows[i]; + if (parent[prop] == row[parentProp]) { + depth += 1; + return this.calculateDepth(parent, depth); + } + } + return depth; + } + }, { + key: "buildRowsByGroup", + value: function buildRowsByGroup() { + this.index = {}; + this.rowsByGroup = {}; - if (ctrl.column.headerTemplate || ctrl.column.headerRenderer) { - cellScope = ctrl.options.$outer.$new(false); + var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; - cellScope.$header = ctrl.column.name; - cellScope.$index = $scope.$index; - } + for (var i = 0, len = this.rows.length; i < len; i++) { + var row = this.rows[i]; - if (ctrl.column.headerTemplate) { - var elm = angular.element("" + ctrl.column.headerTemplate.trim() + ""); - angular.element(label).append($compile(elm)(cellScope)); - } else if (ctrl.column.headerRenderer) { - var _elm = angular.element(ctrl.column.headerRenderer($elm)); - angular.element(label).append($compile(_elm)(cellScope)[0]); + var relVal = row[parentProp]; + if (relVal) { + if (this.rowsByGroup[relVal]) { + this.rowsByGroup[relVal].push(row); } else { - var val = ctrl.column.name; - if (val === undefined || val === null) val = ''; - label.textContent = val; + this.rowsByGroup[relVal] = [row]; } } - }; - } - }; -} -var HeaderController = function () { - function HeaderController() { - _classCallCheck(this, HeaderController); - } + if (this.treeColumn) { + var prop = this.treeColumn.prop; + this.index[row[prop]] = row; - _createClass(HeaderController, [{ - key: "styles", - value: function styles() { - return { - width: this.options.internal.innerWidth + 'px', - height: this.options.headerHeight + 'px' - }; - } - }, { - key: "innerStyles", - value: function innerStyles() { - return { - width: this.columnWidths.total + 'px' - }; + if (row[parentProp] === undefined) { + row.$$depth = 0; + } else { + var parent = this.index[row[parentProp]]; + if (parent === undefined) { + for (var j = 0; j < len; j++) { + if (this.rows[j][prop] == relVal) { + parent = this.rows[j]; + break; + } + } + } + if (parent.$$depth === undefined) { + parent.$$depth = this.calculateDepth(parent); + } + row.$$depth = parent.$$depth + 1; + if (parent.$$children) { + parent.$$children.push(row[prop]); + } else { + parent.$$children = [row[prop]]; + } + } + } + } } }, { - key: "onSorted", - value: function onSorted(sortedColumn) { - if (this.options.sortType === 'single') { - var unsortColumn = function unsortColumn(column) { - if (column !== sortedColumn) { - column.sort = undefined; - } - }; + key: "buildGroups", + value: function buildGroups() { + var _this5 = this; - this.columns.left.forEach(unsortColumn); - this.columns.center.forEach(unsortColumn); - this.columns.right.forEach(unsortColumn); - } + var temp = []; - this.onSort({ - column: sortedColumn + _angular2.default.forEach(this.rowsByGroup, function (v, k) { + temp.push({ + name: k, + group: true + }); + + if (_this5.expanded[k]) { + temp.push.apply(temp, _toConsumableArray(v)); + } }); + + return temp; } }, { - key: "stylesByGroup", - value: function stylesByGroup(group) { - var styles = { - width: this.columnWidths[group] + 'px' - }; + key: "isSelected", + value: function isSelected(row) { + var selected = false; - if (group === 'center') { - TranslateXY(styles, this.options.internal.offsetX * -1, 0); - } else if (group === 'right') { - var offset = (this.columnWidths.total - this.options.internal.innerWidth) * -1; - TranslateXY(styles, offset, 0); + if (this.options.selectable) { + if (this.options.multiSelect) { + selected = this.selected.indexOf(row) > -1; + } else { + selected = this.selected === row; + } } - return styles; - } - }, { - key: "onCheckboxChanged", - value: function onCheckboxChanged() { - this.onCheckboxChange(); + return selected; } }, { - key: "onResized", - value: function onResized(column, width) { - this.onResize({ - column: column, - width: width - }); - } - }]); - - return HeaderController; -}(); - -function HeaderDirective($timeout) { - return { - restrict: 'E', - controller: HeaderController, - controllerAs: 'header', - scope: true, - bindToController: { - options: '=', - columns: '=', - columnWidths: '=', - onSort: '&', - onResize: '&', - onCheckboxChange: '&' - }, - template: "\n
\n\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
", - replace: true, - link: function link($scope, $elm, $attrs, ctrl) { + key: "buildTree", + value: function buildTree() { + var temp = [], + self = this; - $scope.columnsResorted = function (event, columnId) { - var col = findColumnById(columnId), - parent = angular.element(event.currentTarget), - newIdx = -1; + function addChildren(fromArray, toArray, level) { + fromArray.forEach(function (row) { + var relVal = row[self.treeColumn.relationProp], + key = row[self.treeColumn.prop], + groupRows = self.rowsByGroup[key], + expanded = self.expanded[key]; - angular.forEach(parent.children(), function (c, i) { - if (columnId === angular.element(c).attr('data-id')) { - newIdx = i; + if (level > 0 || !relVal) { + toArray.push(row); + if (groupRows && groupRows.length > 0 && expanded) { + addChildren(groupRows, toArray, level + 1); + } } }); + } - $timeout(function () { - angular.forEach(ctrl.columns, function (group) { - var idx = group.indexOf(col); - if (idx > -1) { - var curColAtIdx = group[newIdx], - siblingIdx = ctrl.options.columns.indexOf(curColAtIdx), - curIdx = ctrl.options.columns.indexOf(col); - - ctrl.options.columns.splice(curIdx, 1); - ctrl.options.columns.splice(siblingIdx, 0, col); - - return false; - } - }); - }); - }; + addChildren(this.rows, temp, 0); - var findColumnById = function findColumnById(columnId) { - var columns = ctrl.columns.left.concat(ctrl.columns.center).concat(ctrl.columns.right); - return columns.find(function (c) { - return c.$id === columnId; - }); - }; + return temp; } - }; -} - -function SortableDirective($timeout) { - return { - restrict: 'A', - scope: { - isSortable: '=sortable', - onSortableSort: '&' - }, - link: function link($scope, $element, $attrs) { - var rootEl = $element[0], - dragEl, - nextEl, - dropEl; - - function isbefore(a, b) { - if (a.parentNode == b.parentNode) { - for (var cur = a; cur; cur = cur.previousSibling) { - if (cur === b) { - return true; - } - } - } + }, { + key: "getRows", + value: function getRows(refresh) { + if ((this.treeColumn || this.groupColumn) && !this.rowsByGroup) { return false; - }; - - function onDragEnter(e) { - var target = e.target; - if (isbefore(dragEl, target)) { - target.parentNode.insertBefore(dragEl, target); - } else if (target.nextSibling && target.hasAttribute('draggable')) { - target.parentNode.insertBefore(dragEl, target.nextSibling.nextSibling); - } - }; + } - function onDragEnd(evt) { - evt.preventDefault(); + var temp; - dragEl.classList.remove('dt-clone'); + if (this.treeColumn) { + temp = this.treeTemp || []; - $element.off('dragend', onDragEnd); - $element.off('dragenter', onDragEnter); + if (refresh || !this.treeTemp) { + this.treeTemp = temp = this.buildTree(); + this.count = temp.length; - if (nextEl !== dragEl.nextSibling) { - $scope.onSortableSort({ - event: evt, - columnId: angular.element(dragEl).attr('data-id') - }); + this.tempRows.splice(0, this.tempRows.length); } - }; + } else if (this.groupColumn) { + temp = this.groupsTemp || []; - function onDragStart(evt) { - if (!$scope.isSortable) return false; - evt = evt.originalEvent || evt; + if (refresh || !this.groupsTemp) { + this.groupsTemp = temp = this.buildGroups(); + this.count = temp.length; + } + } else { + temp = this.rows; + if (refresh === true) { + this.tempRows.splice(0, this.tempRows.length); + } + } - dragEl = evt.target; - nextEl = dragEl.nextSibling; - dragEl.classList.add('dt-clone'); + var idx = 0, + indexes = this.getFirstLastIndexes(), + rowIndex = indexes.first; - evt.dataTransfer.effectAllowed = 'move'; - evt.dataTransfer.setData('Text', dragEl.textContent); + this.tempRows.splice(0, indexes.last - indexes.first); - $element.on('dragenter', onDragEnter); - $element.on('dragend', onDragEnd); - }; + while (rowIndex < indexes.last && rowIndex < this.count) { + var row = temp[rowIndex]; + if (row) { + row.$$index = rowIndex; + this.tempRows[idx] = row; + } + idx++; + rowIndex++; + } - $element.on('dragstart', onDragStart); + this.options.internal.styleTranslator.update(this.tempRows); - $scope.$on('$destroy', function () { - $element.off('dragstart', onDragStart); - }); + return this.tempRows; } - }; -} + }, { + key: "styles", + value: function styles() { + var styles = { + width: this.options.internal.innerWidth + 'px' + }; -function ResizableDirective($document, $timeout) { - return { - restrict: 'A', - scope: { - isResizable: '=resizable', - minWidth: '=', - maxWidth: '=', - onResize: '&' - }, - link: function link($scope, $element, $attrs) { - if ($scope.isResizable) { - $element.addClass('resizable'); + if (!this.options.scrollbarV) { + styles.overflowY = 'hidden'; + } else if (this.options.scrollbarH === false) { + styles.overflowX = 'hidden'; } - var handle = angular.element(""), - parent = $element.parent(), - prevScreenX; + if (this.options.scrollbarV) { + styles.height = this.options.internal.bodyHeight + 'px'; + } - handle.on('mousedown', function (event) { - if (!$element[0].classList.contains('resizable')) { - return false; - } + return styles; + } + }, { + key: "rowStyles", + value: function rowStyles(row) { + var styles = {}; - event.stopPropagation(); - event.preventDefault(); + if (this.options.rowHeight === 'auto') { + styles.height = this.options.rowHeight + 'px'; + } - $document.on('mousemove', mousemove); - $document.on('mouseup', mouseup); - }); + return styles; + } + }, { + key: "groupRowStyles", + value: function groupRowStyles(row) { + var styles = this.rowStyles(row); + styles.width = this.columnWidths.total + 'px'; + return styles; + } + }, { + key: "rowClasses", + value: function rowClasses(row) { + var styles = { + 'selected': this.isSelected(row), + 'dt-row-even': row && row.$$index % 2 === 0, + 'dt-row-odd': row && row.$$index % 2 !== 0 + }; - function mousemove(event) { - event = event.originalEvent || event; + if (this.treeColumn) { + styles['dt-leaf'] = this.rowsByGroup[row[this.treeColumn.relationProp]]; - var width = parent[0].clientWidth, - movementX = event.movementX || event.mozMovementX || event.screenX - prevScreenX, - newWidth = width + (movementX || 0); + styles['dt-has-leafs'] = this.rowsByGroup[row[this.treeColumn.prop]]; - prevScreenX = event.screenX; + styles['dt-depth-' + row.$$depth] = true; + } - if ((!$scope.minWidth || newWidth >= $scope.minWidth) && (!$scope.maxWidth || newWidth <= $scope.maxWidth)) { - parent.css({ - width: newWidth + 'px' - }); - } + return styles; + } + }, { + key: "getRowValue", + value: function getRowValue(idx) { + return this.tempRows[idx]; + } + }, { + key: "getRowExpanded", + value: function getRowExpanded(row) { + if (this.treeColumn) { + return this.expanded[row[this.treeColumn.prop]]; + } else if (this.groupColumn) { + return this.expanded[row.name]; } + } + }, { + key: "getRowHasChildren", + value: function getRowHasChildren(row) { + if (!this.treeColumn) return; + var children = this.rowsByGroup[row[this.treeColumn.prop]]; + return children !== undefined || children && !children.length; + } + }, { + key: "onTreeToggled", + value: function onTreeToggled(row, cell) { + var val = row[this.treeColumn.prop]; + this.expanded[val] = !this.expanded[val]; - function mouseup() { - if ($scope.onResize) { - $timeout(function () { - var width = parent[0].clientWidth; - if (width < $scope.minWidth) { - width = $scope.minWidth; - } - $scope.onResize({ width: width }); - }); - } + if (this.options.scrollbarV) { + this.getRows(true); + } else { + var _tempRows2; - $document.unbind('mousemove', mousemove); - $document.unbind('mouseup', mouseup); + var values = this.buildTree(); + this.tempRows.splice(0, this.tempRows.length); + (_tempRows2 = this.tempRows).push.apply(_tempRows2, _toConsumableArray(values)); } - $element.append(handle); + this.onTreeToggle({ + row: row, + cell: cell + }); } - }; -} + }, { + key: "onGroupToggle", + value: function onGroupToggle(row) { + this.expanded[row.name] = !this.expanded[row.name]; -function throttle(func, wait, options) { - var context, args, result; - var timeout = null; - var previous = 0; - options || (options = {}); - var later = function later() { - previous = options.leading === false ? 0 : new Date(); - timeout = null; - result = func.apply(context, args); - }; - return function () { - var now = new Date(); - if (!previous && options.leading === false) previous = now; - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); - } - return result; - }; -} - -function ScrollbarWidth() { - var outer = document.createElement("div"); - outer.style.visibility = "hidden"; - outer.style.width = "100px"; - outer.style.msOverflowStyle = "scrollbar"; - document.body.appendChild(outer); - - var widthNoScroll = outer.offsetWidth; - outer.style.overflow = "scroll"; + if (this.options.scrollbarV) { + this.getRows(true); + } else { + var _tempRows3; - var inner = document.createElement("div"); - inner.style.width = "100%"; - outer.appendChild(inner); + var values = this.buildGroups(); + this.tempRows.splice(0, this.tempRows.length); + (_tempRows3 = this.tempRows).push.apply(_tempRows3, _toConsumableArray(values)); + } + } + }]); - var widthWithScroll = inner.offsetWidth; - outer.parentNode.removeChild(outer); + return BodyController; +}(); - return widthNoScroll - widthWithScroll; +function BodyDirective($timeout) { + return { + restrict: 'E', + controller: BodyController, + controllerAs: 'body', + bindToController: { + columns: '=', + columnWidths: '=', + rows: '=', + options: '=', + selected: '=?', + expanded: '=?', + onPage: '&', + onTreeToggle: '&', + onSelect: '&', + onRowClick: '&', + onRowDblClick: '&' + }, + scope: true, + template: "\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n
" + }; } -var DataTableService = { - columns: {}, - dTables: {}, +var StyleTranslator = function () { + function StyleTranslator(height) { + _classCallCheck(this, StyleTranslator); - saveColumns: function saveColumns(id, columnElms) { - if (columnElms && columnElms.length) { - var columnsArray = [].slice.call(columnElms); - this.dTables[id] = columnsArray; - } - }, - buildColumns: function buildColumns(scope, parse) { - var _this5 = this; + this.height = height; + this.map = new Map(); + } - angular.forEach(this.dTables, function (columnElms, id) { - _this5.columns[id] = []; + _createClass(StyleTranslator, [{ + key: "update", + value: function update(rows) { + var n = 0; + while (n <= this.map.size) { + var dom = this.map.get(n); + var model = rows[n]; + if (dom && model) { + TranslateXY(dom[0].style, 0, model.$$index * this.height); + } + n++; + } + } + }, { + key: "register", + value: function register(idx, dom) { + this.map.set(idx, dom); + } + }]); - angular.forEach(columnElms, function (c) { - var column = {}; + return StyleTranslator; +}(); - var visible = true; +function ScrollerDirective($timeout, $rootScope) { + return { + restrict: 'E', + require: '^dtBody', + transclude: true, + replace: true, + template: "
", + link: function link($scope, $elm, $attrs, ctrl) { + var ticking = false, + lastScrollY = 0, + lastScrollX = 0, + parent = $elm.parent(); - angular.forEach(c.attributes, function (attr) { - var attrName = CamelCase(attr.name); + ctrl.options.internal.styleTranslator = new StyleTranslator(ctrl.options.rowHeight); - switch (attrName) { - case 'class': - column.className = attr.value; - break; - case 'name': - case 'prop': - column[attrName] = attr.value; - break; - case 'headerRenderer': - case 'cellRenderer': - case 'cellDataGetter': - column[attrName] = parse(attr.value); - break; - case 'visible': - visible = parse(attr.value)(scope); - break; - default: - column[attrName] = parse(attr.value)(scope); - break; - } - }); + ctrl.options.internal.setYOffset = function (offsetY) { + parent[0].scrollTop = offsetY; + }; - var header = c.getElementsByTagName('column-header'); - if (header.length) { - column.headerTemplate = header[0].innerHTML; - c.removeChild(header[0]); - } + function update() { + ctrl.options.internal.offsetY = lastScrollY; + ctrl.options.internal.offsetX = lastScrollX; + ctrl.updatePage(); - if (c.innerHTML !== '') { - column.template = c.innerHTML; + if (ctrl.options.scrollbarV) { + ctrl.getRows(); } - if (visible) _this5.columns[id].push(column); - }); - }); - - this.dTables = {}; - } -}; - -function ObjectId() { - var timestamp = (new Date().getTime() / 1000 | 0).toString(16); - return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { - return (Math.random() * 16 | 0).toString(16); - }).toLowerCase(); -} + ctrl.options.$outer.$digest(); -function ScaleColumns(colsByGroup, maxWidth, totalFlexGrow) { - angular.forEach(colsByGroup, function (cols) { - cols.forEach(function (column) { - if (!column.canAutoResize) { - maxWidth -= column.width; - totalFlexGrow -= column.flexGrow; - } else { - column.width = 0; + ticking = false; } - }); - }); - var hasMinWidth = {}; - var remainingWidth = maxWidth; - - var _loop = function _loop() { - var widthPerFlexPoint = remainingWidth / totalFlexGrow; - remainingWidth = 0; - angular.forEach(colsByGroup, function (cols) { - cols.forEach(function (column, i) { - if (column.canAutoResize && !hasMinWidth[i]) { - var newWidth = column.width + column.flexGrow * widthPerFlexPoint; - if (column.minWidth !== undefined && newWidth < column.minWidth) { - remainingWidth += newWidth - column.minWidth; - column.width = column.minWidth; - hasMinWidth[i] = true; - } else { - column.width = newWidth; - } + function requestTick() { + if (!ticking) { + requestAnimFrame(update); + ticking = true; } - }); - }); - }; + } - do { - _loop(); - } while (remainingWidth !== 0); -} + parent.on('scroll', function (ev) { + lastScrollY = this.scrollTop; + lastScrollX = this.scrollLeft; + requestTick(); + }); -function ColumnsByPin(cols) { - var ret = { - left: [], - center: [], - right: [] - }; + $scope.$on('$destroy', function () { + parent.off('scroll'); + }); - for (var i = 0, len = cols.length; i < len; i++) { - var c = cols[i]; - if (c.frozenLeft) { - ret.left.push(c); - } else if (c.frozenRight) { - ret.right.push(c); - } else { - ret.center.push(c); + $scope.scrollerStyles = function () { + if (ctrl.options.scrollbarV) { + return { + height: ctrl.count * ctrl.options.rowHeight + 'px' + }; + } + }; } - } - - return ret; + }; } -function GetTotalFlexGrow(columns) { - var totalFlexGrow = 0; +var KEYS = { + BACKSPACE: 8, + TAB: 9, + RETURN: 13, + ALT: 18, + ESC: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46, + COMMA: 188, + PERIOD: 190, + A: 65, + Z: 90, + ZERO: 48, + NUMPAD_0: 96, + NUMPAD_9: 105 +}; - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; +var SelectionController = function () { + function SelectionController($scope) { + _classCallCheck(this, SelectionController); - try { - for (var _iterator = columns[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var c = _step.value; + this.body = $scope.body; + this.options = $scope.body.options; + this.selected = $scope.body.selected; + } - totalFlexGrow += c.flexGrow || 0; - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); + _createClass(SelectionController, [{ + key: "keyDown", + value: function keyDown(ev, index, row) { + if (KEYS[ev.keyCode]) { + ev.preventDefault(); } - } finally { - if (_didIteratorError) { - throw _iteratorError; + + if (ev.keyCode === KEYS.DOWN) { + var next = ev.target.nextElementSibling; + if (next) { + next.focus(); + } + } else if (ev.keyCode === KEYS.UP) { + var prev = ev.target.previousElementSibling; + if (prev) { + prev.focus(); + } + } else if (ev.keyCode === KEYS.RETURN) { + this.selectRow(index, row); } } - } - - return totalFlexGrow; -} - -function ColumnTotalWidth(columns, prop) { - var totalWidth = 0; - - columns.forEach(function (c) { - var has = prop && c[prop]; - totalWidth = totalWidth + (has ? c[prop] : c.width); - }); - - return totalWidth; -} - -function AdjustColumnWidths(allColumns, expectedWidth) { - var columnsWidth = ColumnTotalWidth(allColumns), - totalFlexGrow = GetTotalFlexGrow(allColumns), - colsByGroup = ColumnsByPin(allColumns); - - if (columnsWidth !== expectedWidth) { - ScaleColumns(colsByGroup, expectedWidth, totalFlexGrow); - } -} - -function ForceFillColumnWidths(allColumns, expectedWidth, startIdx) { - var contentWidth = 0, - columnsToResize = startIdx > -1 ? allColumns.slice(startIdx, allColumns.length).filter(function (c) { - return c.canAutoResize; - }) : allColumns.filter(function (c) { - return c.canAutoResize; - }); + }, { + key: "rowClicked", + value: function rowClicked(event, index, row) { + if (!this.options.checkboxSelection) { + this.selectRow(event, index, row); + } - allColumns.forEach(function (c) { - if (!c.canAutoResize) { - contentWidth += c.width; - } else { - contentWidth += c.$$oldWidth || c.width; + this.body.onRowClick({ row: row }); } - }); - - var remainingWidth = expectedWidth - contentWidth, - additionWidthPerColumn = remainingWidth / columnsToResize.length, - exceedsWindow = contentWidth > expectedWidth; - - columnsToResize.forEach(function (column) { - if (exceedsWindow) { - column.width = column.$$oldWidth || column.width; - } else { - if (!column.$$oldWidth) { - column.$$oldWidth = column.width; + }, { + key: "rowDblClicked", + value: function rowDblClicked(event, index, row) { + if (!this.options.checkboxSelection) { + event.preventDefault(); + this.selectRow(event, index, row); } - var newSize = column.$$oldWidth + additionWidthPerColumn; - if (column.minWith && newSize < column.minWidth) { - column.width = column.minWidth; - } else if (column.maxWidth && newSize > column.maxWidth) { - column.width = column.maxWidth; - } else { - column.width = newSize; - } + this.body.onRowDblClick({ row: row }); } - }); -} - -function ColumnGroupWidths(groups, all) { - return { - left: ColumnTotalWidth(groups.left), - center: ColumnTotalWidth(groups.center), - right: ColumnTotalWidth(groups.right), - total: ColumnTotalWidth(all) - }; -} - -var ColumnDefaults = { - frozenLeft: false, - - frozenRight: false, - - className: undefined, - - headerClassName: undefined, - - flexGrow: 0, + }, { + key: "onCheckboxChange", + value: function onCheckboxChange(event, index, row) { + this.selectRow(event, index, row); + } + }, { + key: "selectRow", + value: function selectRow(event, index, row) { + if (this.options.selectable) { + if (this.options.multiSelect) { + var isCtrlKeyDown = event.ctrlKey || event.metaKey, + isShiftKeyDown = event.shiftKey; - minWidth: 100, + if (isShiftKeyDown) { + this.selectRowsBetween(index, row); + } else { + var idx = this.selected.indexOf(row); + if (idx > -1) { + this.selected.splice(idx, 1); + } else { + if (this.options.multiSelectOnShift && this.selected.length === 1) { + this.selected.splice(0, 1); + } + this.selected.push(row); + this.body.onSelect({ rows: [row] }); + } + } + this.prevIndex = index; + } else { + this.selected = row; + this.body.onSelect({ rows: [row] }); + } + } + } + }, { + key: "selectRowsBetween", + value: function selectRowsBetween(index) { + var reverse = index < this.prevIndex, + selecteds = []; - maxWidth: undefined, + for (var i = 0, len = this.body.rows.length; i < len; i++) { + var row = this.body.rows[i], + greater = i >= this.prevIndex && i <= index, + lesser = i <= this.prevIndex && i >= index; - width: 150, + var range = {}; + if (reverse) { + range = { + start: index, + end: this.prevIndex - index + }; + } else { + range = { + start: this.prevIndex, + end: index + 1 + }; + } - resizable: true, + if (reverse && lesser || !reverse && greater) { + var idx = this.selected.indexOf(row); - comparator: undefined, + if (reverse && idx > -1) { + this.selected.splice(idx, 1); + continue; + } - sortable: true, + if (i >= range.start && i < range.end) { + if (idx === -1) { + this.selected.push(row); + selecteds.push(row); + } + } + } + } - sort: undefined, + this.body.onSelect({ rows: selecteds }); + } + }]); - sortBy: undefined, + return SelectionController; +}(); - headerRenderer: undefined, +function SelectionDirective() { + return { + controller: SelectionController, + restrict: 'A', + require: '^dtBody', + controllerAs: 'selCtrl' + }; +} - cellRenderer: undefined, +var RowController = function () { + function RowController() { + _classCallCheck(this, RowController); + } - cellDataGetter: undefined, + _createClass(RowController, [{ + key: "getValue", + value: function getValue(col) { + if (!col.prop) return ''; + return DeepValueGetter(this.row, col.prop); + } + }, { + key: "onTreeToggled", + value: function onTreeToggled(cell) { + this.onTreeToggle({ + cell: cell, + row: this.row + }); + } + }, { + key: "stylesByGroup", + value: function stylesByGroup(group) { + var styles = { + width: this.columnWidths[group] + 'px' + }; - isTreeColumn: false, + if (group === 'left') { + TranslateXY(styles, this.options.internal.offsetX, 0); + } else if (group === 'right') { + var offset = (this.columnWidths.total - this.options.internal.innerWidth - this.options.internal.offsetX + this.options.internal.scrollBarWidth) * -1; + TranslateXY(styles, offset, 0); + } - isCheckboxColumn: false, + return styles; + } + }, { + key: "onCheckboxChanged", + value: function onCheckboxChanged(ev) { + this.onCheckboxChange({ + $event: ev, + row: this.row + }); + } + }]); - headerCheckbox: false, + return RowController; +}(); - canAutoResize: true +function RowDirective() { + return { + restrict: 'E', + controller: RowController, + controllerAs: 'rowCtrl', + scope: true, + bindToController: { + row: '=', + columns: '=', + columnWidths: '=', + expanded: '=', + selected: '=', + hasChildren: '=', + options: '=', + onCheckboxChange: '&', + onTreeToggle: '&' + }, + link: function link($scope, $elm, $attrs, ctrl) { + if (ctrl.row) { + TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); + } -}; + ctrl.options.internal.styleTranslator.register($scope.$index, $elm); + }, + template: "\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + replace: true + }; +} -var TableDefaults = { - scrollbarV: true, +var GroupRowController = function () { + function GroupRowController() { + _classCallCheck(this, GroupRowController); + } - rowHeight: 30, + _createClass(GroupRowController, [{ + key: "onGroupToggled", + value: function onGroupToggled(evt) { + evt.stopPropagation(); + this.onGroupToggle({ + group: this.row + }); + } + }, { + key: "treeClass", + value: function treeClass() { + return { + 'dt-tree-toggle': true, + 'icon-right': !this.expanded, + 'icon-down': this.expanded + }; + } + }]); - columnMode: 'standard', + return GroupRowController; +}(); - loadingMessage: 'Loading...', +function GroupRowDirective() { + return { + restrict: 'E', + controller: GroupRowController, + controllerAs: 'group', + bindToController: { + row: '=', + onGroupToggle: '&', + expanded: '=', + options: '=' + }, + scope: true, + replace: true, + template: "\n
\n \n \n \n \n
", + link: function link($scope, $elm, $attrs, ctrl) { + TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); - emptyMessage: 'No data to display', + ctrl.options.internal.styleTranslator.register($scope.$index, $elm); + } + }; +} - headerHeight: 30, +var CellController = function () { + function CellController() { + _classCallCheck(this, CellController); + } - footerHeight: 0, + _createClass(CellController, [{ + key: "styles", + value: function styles() { + return { + width: this.column.width + 'px', + 'min-width': this.column.width + 'px' + }; + } + }, { + key: "cellClass", + value: function cellClass() { + var style = { + 'dt-tree-col': this.column.isTreeColumn + }; - paging: { - externalPaging: false, + if (this.column.className) { + style[this.column.className] = true; + } - size: undefined, + return style; + } + }, { + key: "treeClass", + value: function treeClass() { + return { + 'dt-tree-toggle': true, + 'icon-right': !this.expanded, + 'icon-down': this.expanded + }; + } + }, { + key: "onTreeToggled", + value: function onTreeToggled(evt) { + evt.stopPropagation(); + this.expanded = !this.expanded; + this.onTreeToggle({ + cell: { + value: this.value, + column: this.column, + expanded: this.expanded + } + }); + } + }, { + key: "onCheckboxChanged", + value: function onCheckboxChanged(event) { + event.stopPropagation(); + this.onCheckboxChange({ $event: event }); + } + }, { + key: "getValue", + value: function getValue() { + var val = this.column.cellDataGetter ? this.column.cellDataGetter(this.value) : this.value; - count: 0, + if (val === undefined || val === null) val = ''; + return val; + } + }]); - offset: 0, + return CellController; +}(); - loadingIndicator: false - }, +function CellDirective($rootScope, $compile, $log, $timeout) { + return { + restrict: 'E', + controller: CellController, + scope: true, + controllerAs: 'cell', + bindToController: { + options: '=', + value: '=', + selected: '=', + column: '=', + row: '=', + expanded: '=', + hasChildren: '=', + onTreeToggle: '&', + onCheckboxChange: '&' + }, + template: "
\n \n \n \n
", + replace: true, + compile: function compile() { + return { + pre: function pre($scope, $elm, $attrs, ctrl) { + var content = _angular2.default.element($elm[0].querySelector('.dt-cell-content')), + cellScope; - selectable: false, + if (ctrl.column.template || ctrl.column.cellRenderer) { + createCellScope(); + } - multiSelect: false, + $scope.$watch('cell.row', function () { + if (cellScope) { + cellScope.$destroy(); - checkboxSelection: false, + createCellScope(); - reorderable: true, + cellScope.$cell = ctrl.value; + cellScope.$row = ctrl.row; + cellScope.$column = ctrl.column; + cellScope.$$watchers = null; + } - internal: { - offsetX: 0, - offsetY: 0, - innerWidth: 0, - bodyHeight: 300 - } + if (ctrl.column.template) { + content.empty(); + var elm = _angular2.default.element("" + ctrl.column.template.trim() + ""); + content.append($compile(elm)(cellScope)); + } else if (ctrl.column.cellRenderer) { + content.empty(); + var elm = _angular2.default.element(ctrl.column.cellRenderer(cellScope, content)); + content.append($compile(elm)(cellScope)); + } else { + content[0].innerHTML = ctrl.getValue(); + } + }, true); -}; + function createCellScope() { + cellScope = ctrl.options.$outer.$new(false); + cellScope.getValue = ctrl.getValue; + } + } + }; + } + }; +} -var DataTableController = function () { - DataTableController.$inject = ["$scope", "$filter", "$log", "$transclude"]; - function DataTableController($scope, $filter, $log, $transclude) { +var FooterController = function () { + function FooterController($scope) { var _this6 = this; - _classCallCheck(this, DataTableController); + _classCallCheck(this, FooterController); - Object.assign(this, { - $scope: $scope, - $filter: $filter, - $log: $log + this.page = this.paging.offset + 1; + $scope.$watch('footer.paging.offset', function (newVal) { + _this6.offsetChanged(newVal); }); + } - this.defaults(); + _createClass(FooterController, [{ + key: "offsetChanged", + value: function offsetChanged(newVal) { + this.page = newVal + 1; + } + }, { + key: "onPaged", + value: function onPaged(page) { + this.paging.offset = page - 1; + this.onPage({ + offset: this.paging.offset, + size: this.paging.size + }); + } + }]); - this.options.$outer = $scope.$parent; + return FooterController; +}(); - $scope.$watch('dt.options.columns', function (newVal, oldVal) { - _this6.transposeColumnDefaults(); +function FooterDirective() { + return { + restrict: 'E', + controller: FooterController, + controllerAs: 'footer', + scope: true, + bindToController: { + paging: '=', + onPage: '&' + }, + template: "
\n
{{footer.paging.count}} total
\n 1\">\n \n
", + replace: true + }; +} - if (newVal.length !== oldVal.length) { - _this6.adjustColumns(); - } +var PagerController = function () { + function PagerController($scope) { + var _this7 = this; - _this6.calculateColumns(); - }, true); + _classCallCheck(this, PagerController); - var watch = $scope.$watch('dt.rows', function (newVal) { - if (newVal) { - watch(); - _this6.onSorted(); - } + $scope.$watch('pager.count', function (newVal) { + _this7.calcTotalPages(_this7.size, _this7.count); + _this7.getPages(_this7.page || 1); }); - } - - _createClass(DataTableController, [{ - key: "defaults", - value: function defaults() { - var _this7 = this; - this.expanded = this.expanded || {}; + $scope.$watch('pager.size', function (newVal) { + _this7.calcTotalPages(_this7.size, _this7.count); + _this7.getPages(_this7.page || 1); + }); - this.options = angular.extend(angular.copy(TableDefaults), this.options); + $scope.$watch('pager.page', function (newVal) { + if (newVal !== 0 && newVal <= _this7.totalPages) { + _this7.getPages(newVal); + } + }); - angular.forEach(TableDefaults.paging, function (v, k) { - if (!_this7.options.paging[k]) { - _this7.options.paging[k] = v; - } - }); + this.getPages(this.page || 1); + } - if (this.options.selectable && this.options.multiSelect) { - this.selected = this.selected || []; - } + _createClass(PagerController, [{ + key: "calcTotalPages", + value: function calcTotalPages(size, count) { + var count = size < 1 ? 1 : Math.ceil(count / size); + this.totalPages = Math.max(count || 0, 1); } }, { - key: "transposeColumnDefaults", - value: function transposeColumnDefaults() { - for (var i = 0, len = this.options.columns.length; i < len; i++) { - var column = this.options.columns[i]; - column.$id = ObjectId(); - - angular.forEach(ColumnDefaults, function (v, k) { - if (!column.hasOwnProperty(k)) { - column[k] = v; - } + key: "selectPage", + value: function selectPage(num) { + if (num > 0 && num <= this.totalPages) { + this.page = num; + this.onPage({ + page: num }); - - if (column.name && !column.prop) { - column.prop = CamelCase(column.name); - } - - this.options.columns[i] = column; } } }, { - key: "calculateColumns", - value: function calculateColumns() { - var columns = this.options.columns; - this.columnsByPin = ColumnsByPin(columns); - this.columnWidths = ColumnGroupWidths(this.columnsByPin, columns); + key: "prevPage", + value: function prevPage() { + if (this.page > 1) { + this.selectPage(--this.page); + } } }, { - key: "tableCss", - value: function tableCss() { - return { - 'fixed': this.options.scrollbarV, - 'selectable': this.options.selectable, - 'checkboxable': this.options.checkboxSelection - }; + key: "nextPage", + value: function nextPage() { + this.selectPage(++this.page); } }, { - key: "adjustColumns", - value: function adjustColumns(forceIdx) { - var width = this.options.internal.innerWidth - this.options.internal.scrollBarWidth; - - if (this.options.columnMode === 'force') { - ForceFillColumnWidths(this.options.columns, width, forceIdx); - } else if (this.options.columnMode === 'flex') { - AdjustColumnWidths(this.options.columns, width); - } + key: "canPrevious", + value: function canPrevious() { + return this.page > 1; } }, { - key: "calculatePageSize", - value: function calculatePageSize() { - this.options.paging.size = Math.ceil(this.options.internal.bodyHeight / this.options.rowHeight) + 1; + key: "canNext", + value: function canNext() { + return this.page < this.totalPages; } }, { - key: "onSorted", - value: function onSorted() { - if (!this.rows) return; + key: "getPages", + value: function getPages(page) { + var pages = [], + startPage = 1, + endPage = this.totalPages, + maxSize = 5, + isMaxSized = maxSize < this.totalPages; - var sorts = this.options.columns.filter(function (c) { - return c.sort; - }).sort(function (a, b) { - if (a.sortPriority && b.sortPriority) { - if (a.sortPriority > b.sortPriority) return 1; - if (a.sortPriority < b.sortPriority) return -1; - } else if (a.sortPriority) { - return -1; - } else if (b.sortPriority) { - return 1; - } + if (isMaxSized) { + startPage = (Math.ceil(page / maxSize) - 1) * maxSize + 1; + endPage = Math.min(startPage + maxSize - 1, this.totalPages); + } - return 0; - }).map(function (c, i) { - c.sortPriority = i + 1; - return c; - }); + for (var number = startPage; number <= endPage; number++) { + pages.push({ + number: number, + text: number, + active: number === page + }); + } - if (sorts.length) { - this.onSort({ sorts: sorts }); + this.pages = pages; + } + }]); + + return PagerController; +}(); + +function PagerDirective() { + return { + restrict: 'E', + controller: PagerController, + controllerAs: 'pager', + scope: true, + bindToController: { + page: '=', + size: '=', + count: '=', + onPage: '&' + }, + template: "
\n
    \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n {{pg.text}}\n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
\n
", + replace: true + }; +} + +function PopoverDirective($q, $timeout, $templateCache, $compile, PopoverRegistry, PositionHelper, $animate) { + function loadTemplate(template, plain) { + if (!template) { + return ''; + } + + if (_angular2.default.isString(template) && plain) { + return template; + } + + return $templateCache.get(template) || $http.get(template, { cache: true }); + } + + function toBoolean(value) { + if (value && value.length !== 0) { + var v = ("" + value).toLowerCase(); + value = v == 'true'; + } else { + value = false; + } + return value; + } + + return { + restrict: 'A', + scope: true, + replace: false, + link: function link($scope, $element, $attributes) { + $scope.popover = null; + $scope.popoverId = Date.now(); + + $scope.options = { + text: $attributes.popoverText, + template: $attributes.popoverTemplate, + plain: toBoolean($attributes.popoverPlain || false), + placement: $attributes.popoverPlacement || 'right', + alignment: $attributes.popoverAlignment || 'center', + group: $attributes.popoverGroup, + spacing: parseInt($attributes.popoverSpacing) || 0, + showCaret: toBoolean($attributes.popoverPlain || false) + }; + + $element.off('mouseenter', display); + $element.on('mouseenter', display); + $element.off('mouseleave', mouseOut); + $element.on('mouseleave', mouseOut); + + function mouseOut() { + $scope.exitTimeout = $timeout(remove, 500); + } + + function display() { + $timeout.cancel($scope.exitTimeout); + + var elm = document.getElementById("#" + $scope.popoverId); + if ($scope.popover && elm) return; - if (this.options.onSort) { - this.options.onSort(sorts); + if ($scope.options.group) { + PopoverRegistry.removeGroup($scope.options.group, $scope.popoverId); } - var clientSorts = []; - for (var i = 0, len = sorts.length; i < len; i++) { - var c = sorts[i]; - if (c.comparator !== false) { - var dir = c.sort === 'asc' ? '' : '-'; - if (c.sortBy !== undefined) { - clientSorts.push(dir + c.sortBy); - } else { - clientSorts.push(dir + c.prop); + if ($scope.options.text && !$scope.options.template) { + $scope.popover = _angular2.default.element("
"); + + $scope.popover.html($scope.options.text); + _angular2.default.element(document.body).append($scope.popover); + positionPopover($element, $scope.popover, $scope.options); + PopoverRegistry.add($scope.popoverId, { element: $element, popover: $scope.popover, group: $scope.options.group }); + } else { + $q.when(loadTemplate($scope.options.template, $scope.options.plain)).then(function (template) { + if (!_angular2.default.isString(template)) { + if (template.data && _angular2.default.isString(template.data)) { + template = template.data; + } else { + template = ''; + } } - } - } - if (clientSorts.length) { - var _rows; + $scope.popover = _angular2.default.element("
"); - var sortedValues = this.$filter('orderBy')(this.rows, clientSorts); - this.rows.splice(0, this.rows.length); - (_rows = this.rows).push.apply(_rows, _toConsumableArray(sortedValues)); + $scope.popover.html(template); + $compile($scope.popover)($scope); + _angular2.default.element(document.body).append($scope.popover); + positionPopover($element, $scope.popover, $scope.options); + + $scope.popover.off('mouseleave', mouseOut); + $scope.popover.on('mouseleave', mouseOut); + $scope.popover.on('mouseenter', function () { + $timeout.cancel($scope.exitTimeout); + }); + + PopoverRegistry.add($scope.popoverId, { + element: $element, + popover: $scope.popover, + group: $scope.options.group + }); + }); } } - this.options.internal.setYOffset(0); - } - }, { - key: "onTreeToggled", - value: function onTreeToggled(row, cell) { - this.onTreeToggle({ - row: row, - cell: cell - }); - } - }, { - key: "onBodyPage", - value: function onBodyPage(offset, size) { - this.onPage({ - offset: offset, - size: size - }); - } - }, { - key: "onFooterPage", - value: function onFooterPage(offset, size) { - var pageBlockSize = this.options.rowHeight * size, - offsetY = pageBlockSize * offset; + function remove() { + if ($scope.popover) { + $scope.popover.remove(); + } - this.options.internal.setYOffset(offsetY); - } - }, { - key: "onHeaderCheckboxChange", - value: function onHeaderCheckboxChange() { - if (this.rows) { - var matches = this.selected.length === this.rows.length; - this.selected.splice(0, this.selected.length); + $scope.popover = undefined; + PopoverRegistry.remove($scope.popoverId); + } - if (!matches) { - var _selected; + function positionPopover(triggerElement, popover, options) { + $timeout(function () { + var elDimensions = triggerElement[0].getBoundingClientRect(), + popoverDimensions = popover[0].getBoundingClientRect(), + top, + left; + + if (options.placement === 'right') { + left = elDimensions.left + elDimensions.width + options.spacing; + top = PositionHelper.calculateVerticalAlignment(elDimensions, popoverDimensions, options.alignment); + } + if (options.placement === 'left') { + left = elDimensions.left - popoverDimensions.width - options.spacing; + top = PositionHelper.calculateVerticalAlignment(elDimensions, popoverDimensions, options.alignment); + } + if (options.placement === 'top') { + top = elDimensions.top - popoverDimensions.height - options.spacing; + left = PositionHelper.calculateHorizontalAlignment(elDimensions, popoverDimensions, options.alignment); + } + if (options.placement === 'bottom') { + top = elDimensions.top + elDimensions.height + options.spacing; + left = PositionHelper.calculateHorizontalAlignment(elDimensions, popoverDimensions, options.alignment); + } - (_selected = this.selected).push.apply(_selected, _toConsumableArray(this.rows)); + popover.css({ + top: top + 'px', + left: left + 'px' + }); + + if ($scope.options.showCaret) { + addCaret($scope.popover, elDimensions, popoverDimensions); + } + + $animate.addClass($scope.popover, 'popover-animation'); + }, 50); + } + + function addCaret(popoverEl, elDimensions, popoverDimensions) { + var caret = _angular2.default.element(""); + popoverEl.append(caret); + var caretDimensions = caret[0].getBoundingClientRect(); + + var left, top; + if ($scope.options.placement === 'right') { + left = -6; + top = PositionHelper.calculateVerticalCaret(elDimensions, popoverDimensions, caretDimensions, $scope.options.alignment); } + if ($scope.options.placement === 'left') { + left = popoverDimensions.width - 2; + top = PositionHelper.calculateVerticalCaret(elDimensions, popoverDimensions, caretDimensions, $scope.options.alignment); + } + if ($scope.options.placement === 'top') { + top = popoverDimensions.height - 5; + left = PositionHelper.calculateHorizontalCaret(elDimensions, popoverDimensions, caretDimensions, $scope.options.alignment); + } + + if ($scope.options.placement === 'bottom') { + top = -8; + left = PositionHelper.calculateHorizontalCaret(elDimensions, popoverDimensions, caretDimensions, $scope.options.alignment); + } + + caret.css({ + top: top + 'px', + left: left + 'px' + }); } } - }, { - key: "isAllRowsSelected", - value: function isAllRowsSelected() { - if (this.rows) return false; - return this.selected.length === this.rows.length; - } - }, { - key: "onResized", - value: function onResized(column, width) { - var idx = this.options.columns.indexOf(column); - if (idx > -1) { - var column = this.options.columns[idx]; - column.width = width; - column.canAutoResize = false; + }; +} - this.adjustColumns(idx); - this.calculateColumns(); +function PopoverRegistry($animate) { + var popovers = {}; + this.add = function (id, object) { + popovers[id] = object; + }; + this.find = function (id) { + popovers[id]; + }; + this.remove = function (id) { + delete popovers[id]; + }; + this.removeGroup = function (group, currentId) { + angular.forEach(popovers, function (popoverOb, id) { + if (id === currentId) return; + + if (popoverOb.group && popoverOb.group === group) { + $animate.removeClass(popoverOb.popover, 'sw-popover-animate').then(function () { + popoverOb.popover.remove(); + delete popovers[id]; + }); } + }); + }; +} - if (this.onColumnResize) { - this.onColumnResize({ - column: column, - width: width - }); +function PositionHelper() { + return { + + calculateVerticalAlignment: function calculateVerticalAlignment(elDimensions, popoverDimensions, alignment) { + if (alignment === 'top') { + return elDimensions.top; + } + if (alignment === 'bottom') { + return elDimensions.top + elDimensions.height - popoverDimensions.height; + } + if (alignment === 'center') { + return elDimensions.top + elDimensions.height / 2 - popoverDimensions.height / 2; + } + }, + + calculateVerticalCaret: function calculateVerticalCaret(elDimensions, popoverDimensions, caretDimensions, alignment) { + if (alignment === 'top') { + return elDimensions.height / 2 - caretDimensions.height / 2 - 1; + } + if (alignment === 'bottom') { + return popoverDimensions.height - elDimensions.height / 2 - caretDimensions.height / 2 - 1; + } + if (alignment === 'center') { + return popoverDimensions.height / 2 - caretDimensions.height / 2 - 1; + } + }, + + calculateHorizontalCaret: function calculateHorizontalCaret(elDimensions, popoverDimensions, caretDimensions, alignment) { + if (alignment === 'left') { + return elDimensions.width / 2 - caretDimensions.height / 2 - 1; + } + if (alignment === 'right') { + return popoverDimensions.width - elDimensions.width / 2 - caretDimensions.height / 2 - 1; + } + if (alignment === 'center') { + return popoverDimensions.width / 2 - caretDimensions.height / 2 - 1; + } + }, + + calculateHorizontalAlignment: function calculateHorizontalAlignment(elDimensions, popoverDimensions, alignment) { + if (alignment === 'left') { + return elDimensions.left; + } + if (alignment === 'right') { + return elDimensions.left + elDimensions.width - popoverDimensions.width; + } + if (alignment === 'center') { + return elDimensions.left + elDimensions.width / 2 - popoverDimensions.width / 2; } } - }, { - key: "onSelected", - value: function onSelected(rows) { - this.onSelect({ - rows: rows + + }; +} + +var popover = _angular2.default.module('dt.popover', []).service('PopoverRegistry', PopoverRegistry).factory('PositionHelper', PositionHelper).directive('popover', PopoverDirective); + +var MenuController = function () { + function MenuController($scope, $timeout) { + _classCallCheck(this, MenuController); + + this.$scope = $scope; + } + + _createClass(MenuController, [{ + key: "getColumnIndex", + value: function getColumnIndex(model) { + return this.$scope.current.findIndex(function (col) { + return model.name == col.name; }); } }, { - key: "onRowClicked", - value: function onRowClicked(row) { - this.onRowClick({ - row: row - }); + key: "isChecked", + value: function isChecked(model) { + return this.getColumnIndex(model) > -1; } }, { - key: "onRowDblClicked", - value: function onRowDblClicked(row) { - this.onRowDblClick({ - row: row - }); + key: "onCheck", + value: function onCheck(model) { + var idx = this.getColumnIndex(model); + if (idx === -1) { + this.$scope.current.push(model); + } else { + this.$scope.current.splice(idx, 1); + } } }]); - return DataTableController; + return MenuController; }(); -function DataTableDirective($window, $timeout, $parse) { +function MenuDirective() { return { restrict: 'E', - replace: true, - controller: DataTableController, - scope: true, - bindToController: { - options: '=', - rows: '=', - selected: '=?', - expanded: '=?', - onSelect: '&', - onSort: '&', - onTreeToggle: '&', - onPage: '&', - onRowClick: '&', - onRowDblClick: '&', - onColumnResize: '&' + controller: 'MenuController', + controllerAs: 'dtm', + scope: { + current: '=', + available: '=' }, - controllerAs: 'dt', - template: function template(element) { - var columns = element[0].getElementsByTagName('column'), - id = ObjectId(); - DataTableService.saveColumns(id, columns); + template: "
\n \n Configure Columns\n \n
\n
\n \n
\n
    \n
  • \n \n
  • \n
\n
\n
" + }; +} - return "
\n \n \n \n \n \n \n
"; - }, - compile: function compile(tElem, tAttrs) { - return { - pre: function pre($scope, $elm, $attrs, ctrl) { - DataTableService.buildColumns($scope, $parse); +var DropdownController = function () { + function DropdownController($scope) { + _classCallCheck(this, DropdownController); - var id = $elm.attr('data-column-id'), - columns = DataTableService.columns[id]; - if (columns) { - ctrl.options.columns = columns; - } + $scope.open = false; + } - ctrl.transposeColumnDefaults(); - ctrl.options.internal.scrollBarWidth = ScrollbarWidth(); + _createClass(DropdownController, [{ + key: "toggle", + value: function toggle(scope) { + scope.open = !scope.open; + } + }]); - function resize() { - var rect = $elm[0].getBoundingClientRect(); + return DropdownController; +}(); - ctrl.options.internal.innerWidth = Math.floor(rect.width); +function DropdownDirective($document, $timeout) { + return { + restrict: 'C', + controller: 'DropdownController', + link: function link($scope, $elm, $attrs) { - if (ctrl.options.scrollbarV) { - var height = rect.height; + function closeDropdown(ev) { + if ($elm[0].contains(ev.target)) { + return; + } - if (ctrl.options.headerHeight) { - height = height - ctrl.options.headerHeight; - } + $timeout(function () { + $scope.open = false; + off(); + }); + } - if (ctrl.options.footerHeight) { - height = height - ctrl.options.footerHeight; - } + function keydown(ev) { + if (ev.which === 27) { + $timeout(function () { + $scope.open = false; + off(); + }); + } + } - ctrl.options.internal.bodyHeight = height; - ctrl.calculatePageSize(); - } + function off() { + $document.unbind('click', closeDropdown); + $document.unbind('keydown', keydown); + } - ctrl.adjustColumns(); - }; + $scope.$watch('open', function (newVal) { + if (newVal) { + $document.bind('click', closeDropdown); + $document.bind('keydown', keydown); + } + }); + } + }; +} - $window.addEventListener('resize', throttle(function () { - $timeout(resize); - })); +function DropdownToggleDirective($timeout) { + return { + restrict: 'C', + controller: 'DropdownController', + require: '?^dropdown', + link: function link($scope, $elm, $attrs, ctrl) { - var checkVisibility = function checkVisibility() { - var bounds = $elm[0].getBoundingClientRect(), - visible = bounds.width && bounds.height; - if (visible) resize();else $timeout(checkVisibility, 100); - }; - checkVisibility(); + function toggleClick(event) { + event.preventDefault(); + $timeout(function () { + ctrl.toggle($scope); + }); + } - $elm.addClass('dt-loaded'); + function toggleDestroy() { + $elm.unbind('click', toggleClick); + } - $scope.$on('$destroy', function () { - angular.element($window).off('resize'); - }); - } - }; + $elm.bind('click', toggleClick); + $scope.$on('$destroy', toggleDestroy); + } + }; +} + +function DropdownMenuDirective($animate) { + return { + restrict: 'C', + require: '?^dropdown', + link: function link($scope, $elm, $attrs, ctrl) { + $scope.$watch('open', function () { + $animate[$scope.open ? 'addClass' : 'removeClass']($elm, 'ddm-open'); + }); } }; } -var dataTable = angular.module('data-table', []).directive('dtable', DataTableDirective).directive('resizable', ResizableDirective).directive('sortable', SortableDirective).directive('dtHeader', HeaderDirective).directive('dtHeaderCell', HeaderCellDirective).directive('dtBody', BodyDirective).directive('dtScroller', ScrollerDirective).directive('dtSeletion', SelectionDirective).directive('dtRow', RowDirective).directive('dtGroupRow', GroupRowDirective).directive('dtCell', CellDirective).directive('dtFooter', FooterDirective).directive('dtPager', PagerDirective); +var dropdown = _angular2.default.module('dt.dropdown', []).controller('DropdownController', DropdownController).directive('dropdown', DropdownDirective).directive('dropdownToggle', DropdownToggleDirective).directive('dropdownMenu', DropdownMenuDirective); + +var menu = _angular2.default.module('dt.menu', [dropdown.name]).controller('MenuController', MenuController).directive('dtm', MenuDirective); + +var dataTable = _angular2.default.module('data-table', []).directive('dtable', DataTableDirective).directive('resizable', ResizableDirective).directive('sortable', SortableDirective).directive('dtHeader', HeaderDirective).directive('dtHeaderCell', HeaderCellDirective).directive('dtBody', BodyDirective).directive('dtScroller', ScrollerDirective).directive('dtSeletion', SelectionDirective).directive('dtRow', RowDirective).directive('dtGroupRow', GroupRowDirective).directive('dtCell', CellDirective).directive('dtFooter', FooterDirective).directive('dtPager', PagerDirective); +exports.dtPopover = popover; +exports.dtMenu = menu; exports.default = dataTable; \ No newline at end of file diff --git a/release/dataTable.css b/release/dataTable.css index a8c67a8..7e7a00b 100644 --- a/release/dataTable.css +++ b/release/dataTable.css @@ -4,6 +4,151 @@ justify-content: center; position: relative; } +.dt .popover { + position: absolute; + background: #fff; + color: #999; + z-index: 5000; + opacity: 0; + display: block; + max-width: 250px; + max-height: 300px; + overflow-y: auto; + -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24); + -moz-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.24); +} +.dt .popover h1, +.dt .popover h2, +.dt .popover h3, +.dt .popover h4, +.dt .popover h5, +.dt .popover h6 { + color: #272e33 !important; +} +.dt .popover .popover-caret { + position: absolute; + z-index: 5001; + min-width: 6px; + height: 11px; +} +.dt .popover .popover-caret.caret-left { + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + -o-transform: rotate(180deg); + transform: rotate(180deg); +} +.dt .popover .popover-caret.caret-top { + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + transform: rotate(-90deg); +} +.dt .popover .popover-caret.caret-bottom { + -webkit-transform: rotate(90deg); + -moz-transform: rotate(90deg); + -ms-transform: rotate(90deg); + -o-transform: rotate(90deg); + transform: rotate(90deg); +} +.dt .popover header { + padding: 15px 20px; +} +.dt .popover header h1 { + font-size: 16px; + font-weight: 600; + margin: 0; +} +.dt .popover header a, +.dt .popover header a:hover, +.dt .popover header a:focus { + color: #5ea1df; +} +.dt .popover p { + color: #999 !important; +} +.dt .popover .list { + list-style-type: none; + margin: 0px; + padding: 0px; +} +.dt .popover .list .main-text { + display: block; + color: #73777b; +} +.dt .popover .list .sub-text { + color: #afafaf; + font-size: 12px; +} +.dt .popover .list li { + border-top: 1px solid #f1f1f1; + margin: 0; + background: #fff; +} +.dt .popover .list li .list-link { + padding: 10px 20px; + display: block; + cursor: pointer; +} +.dt .popover .list li:hover { + background: #eff5fc; +} +.dt .popover .list .inactive, +.dt .popover .list .inactive:hover { + padding: 10px 20px; + background: none; +} +.dt .popover-text { + padding: 10px; + color: #000; + font-weight: 600; + font-size: 12px; +} +.dt .popover-right { + -webkit-transform: translate3d(10px, 0, 0); + transform: translate3d(10, 0, 0); +} +.dt .popover-left { + -webkit-transform: translate3d(-10px, 0, 0); + transform: translate3d(-10px, 0, 0); +} +.dt .popover-top { + -webkit-transform: translate3d(0, -10px, 0); + transform: translate3d(0, -10px, 0); +} +.dt .popover-bottom { + -webkit-transform: translate3d(0, 10px, 0); + transform: translate3d(0, 10px, 0); +} +.dt .popover-animation { + opacity: 1; + -webkit-transition: opacity 0.3s, -webkit-transform 0.3s; + transition: opacity 0.3s, transform 0.3s; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} +.dt .dt-menu { + text-align: left; +} +.dt .dt-menu ul, +.dt .dt-menu li { + padding: 0; + margin: 0; + list-style: none; +} +.dt .dt-menu .dropdown-menu { + width: 200px; + display: none; +} +.dt .dt-menu .dropdown-menu.ddm-open { + display: inline-block; +} +.dt .dt-menu ul { + max-height: 300px; + overflow-y: auto; +} .dt.dt-loaded { visibility: visible !important; } diff --git a/release/dataTable.es6.js b/release/dataTable.es6.js index 3a3f3b9..18a83a5 100644 --- a/release/dataTable.es6.js +++ b/release/dataTable.es6.js @@ -1,3 +1,5 @@ +import angular$1 from 'angular'; + /** * Array.prototype.find() * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find @@ -37,526 +39,368 @@ } }()); -class PagerController { - - /** - * Creates an instance of the Pager Controller - * @param {object} $scope - */ - /*@ngInject*/ - constructor($scope){ - $scope.$watch('pager.count', (newVal) => { - this.calcTotalPages(this.size, this.count); - this.getPages(this.page || 1); - }); - - $scope.$watch('pager.size', (newVal) => { - this.calcTotalPages(this.size, this.count); - this.getPages(this.page || 1); - }); - - $scope.$watch('pager.page', (newVal) => { - if (newVal !== 0 && newVal <= this.totalPages) { - this.getPages(newVal); +/** + * Resizable directive + * http://stackoverflow.com/questions/18368485/angular-js-resizable-div-directive + * @param {object} + * @param {function} + * @param {function} + */ +function ResizableDirective($document, $timeout){ + return { + restrict: 'A', + scope:{ + isResizable: '=resizable', + minWidth: '=', + maxWidth: '=', + onResize: '&' + }, + link: function($scope, $element, $attrs){ + if($scope.isResizable){ + $element.addClass('resizable'); } - }); - - this.getPages(this.page || 1); - } - /** - * Calculates the total number of pages given the count. - * @return {int} page count - */ - calcTotalPages(size, count) { - var count = size < 1 ? 1 : Math.ceil(count / size); - this.totalPages = Math.max(count || 0, 1); - } + var handle = angular.element(``), + parent = $element.parent(), + prevScreenX; - /** - * Select a page - * @param {int} num - */ - selectPage(num){ - if (num > 0 && num <= this.totalPages) { - this.page = num; - this.onPage({ - page: num - }); - } - } + handle.on('mousedown', function(event) { + if(!$element[0].classList.contains('resizable')) { + return false; + } - /** - * Selects the previous pager - */ - prevPage(){ - if (this.page > 1) { - this.selectPage(--this.page); - } - } + event.stopPropagation(); + event.preventDefault(); - /** - * Selects the next page - */ - nextPage(){ - this.selectPage(++this.page); - } + $document.on('mousemove', mousemove); + $document.on('mouseup', mouseup); + }); - /** - * Determines if the pager can go previous - * @return {boolean} - */ - canPrevious(){ - return this.page > 1; - } + function mousemove(event) { + event = event.originalEvent || event; - /** - * Determines if the pager can go forward - * @return {boolean} - */ - canNext(){ - return this.page < this.totalPages; - } + var width = parent[0].clientWidth, + movementX = event.movementX || event.mozMovementX || (event.screenX - prevScreenX), + newWidth = width + (movementX || 0); - /** - * Gets the page set given the current page - * @param {int} page - */ - getPages(page) { - var pages = [], - startPage = 1, - endPage = this.totalPages, - maxSize = 5, - isMaxSized = maxSize < this.totalPages; + prevScreenX = event.screenX; - if (isMaxSized) { - startPage = ((Math.ceil(page / maxSize) - 1) * maxSize) + 1; - endPage = Math.min(startPage + maxSize - 1, this.totalPages); - } + if((!$scope.minWidth || newWidth >= $scope.minWidth) && (!$scope.maxWidth || newWidth <= $scope.maxWidth)){ + parent.css({ + width: newWidth + 'px' + }); + } + } - for (var number = startPage; number <= endPage; number++) { - pages.push({ - number: number, - text: number, - active: number === page - }); - } + function mouseup() { + if ($scope.onResize) { + $timeout(function () { + let width = parent[0].clientWidth; + if (width < $scope.minWidth){ + width = $scope.minWidth; + } + $scope.onResize({ width: width }); + }); + } - /* - if (isMaxSized) { - if (startPage > 1) { - pages.unshift({ - number: startPage - 1, - text: '...' - }); + $document.unbind('mousemove', mousemove); + $document.unbind('mouseup', mouseup); } - if (endPage < this.totalPages) { - pages.push({ - number: endPage + 1, - text: '...' - }); - } + $element.append(handle); } - */ - - this.pages = pages; - } - + }; } -function PagerDirective(){ +/** + * Sortable Directive + * http://jsfiddle.net/RubaXa/zLq5J/3/ + * https://jsfiddle.net/hrohxze0/6/ + * @param {function} + */ +function SortableDirective($timeout) { return { - restrict: 'E', - controller: PagerController, - controllerAs: 'pager', - scope: true, - bindToController: { - page: '=', - size: '=', - count: '=', - onPage: '&' + restrict: 'A', + scope: { + isSortable: '=sortable', + onSortableSort: '&' }, - template: - `
- -
`, - replace: true - }; -} + link: function($scope, $element, $attrs){ + var rootEl = $element[0], dragEl, nextEl, dropEl; -class FooterController { + function isbefore(a, b) { + if (a.parentNode == b.parentNode) { + for (var cur = a; cur; cur = cur.previousSibling) { + if (cur === b) { + return true; + } + } + } + return false; + } - /** - * Creates an instance of the Footer Controller - * @param {scope} - * @return {[type]} - */ - /*@ngInject*/ - constructor($scope){ - this.page = this.paging.offset + 1; - $scope.$watch('footer.paging.offset', (newVal) => { - this.offsetChanged(newVal) - }); - } + function onDragEnter(e) { + var target = e.target; + if (isbefore(dragEl, target)) { + target.parentNode.insertBefore(dragEl, target); + } else if(target.nextSibling && target.hasAttribute('draggable')) { + target.parentNode.insertBefore(dragEl, target.nextSibling.nextSibling); + } + } - /** - * The offset ( page ) changed externally, update the page - * @param {new offset} - */ - offsetChanged(newVal){ - this.page = newVal + 1; - } + function onDragEnd(evt) { + evt.preventDefault(); - /** - * The pager was invoked - * @param {scope} - */ - onPaged(page){ - this.paging.offset = page - 1; - this.onPage({ - offset: this.paging.offset, - size: this.paging.size - }); - } + dragEl.classList.remove('dt-clone'); -} + $element.off('dragend', onDragEnd); + $element.off('dragenter', onDragEnter); -function FooterDirective(){ - return { - restrict: 'E', - controller: FooterController, - controllerAs: 'footer', - scope: true, - bindToController: { - paging: '=', - onPage: '&' - }, - template: - ``, - replace: true - }; -} + if (nextEl !== dragEl.nextSibling) { + $scope.onSortableSort({ + event: evt, + columnId: angular$1.element(dragEl).attr('data-id') + }); + } + } -class CellController { + function onDragStart(evt){ + if(!$scope.isSortable) return false; + evt = evt.originalEvent || evt; - /** - * Calculates the styles for the Cell Directive - * @return {styles object} - */ - styles(){ - return { - width: this.column.width + 'px', - 'min-width': this.column.width + 'px' - }; - } + dragEl = evt.target; + nextEl = dragEl.nextSibling; + dragEl.classList.add('dt-clone'); - /** - * Calculates the css classes for the cell directive - * @param {column} - * @return {class object} - */ - cellClass(){ - var style = { - 'dt-tree-col': this.column.isTreeColumn - }; + evt.dataTransfer.effectAllowed = 'move'; + evt.dataTransfer.setData('Text', dragEl.textContent); - if(this.column.className){ - style[this.column.className] = true; - } + $element.on('dragenter', onDragEnter); + $element.on('dragend', onDragEnd); + } - return style; - } + $element.on('dragstart', onDragStart); - /** - * Calculates the tree class styles. - * @return {css classes object} - */ - treeClass(){ - return { - 'dt-tree-toggle': true, - 'icon-right': !this.expanded, - 'icon-down': this.expanded + $scope.$on('$destroy', () => { + $element.off('dragstart', onDragStart); + }); } } +} - /** - * Invoked when the tree toggle button was clicked. - * @param {event} - */ - onTreeToggled(evt){ - evt.stopPropagation(); - this.expanded = !this.expanded; - this.onTreeToggle({ - cell: { - value: this.value, - column: this.column, - expanded: this.expanded - } - }); - } +/** + * Default Table Options + * @type {object} + */ +const TableDefaults = { - /** - * Invoked when the checkbox was changed - * @param {object} event - */ - onCheckboxChanged(event){ - event.stopPropagation(); - this.onCheckboxChange({ $event: event }); - } + // Enable vertical scrollbars + scrollbarV: true, - /** - * Returns the value in its fomatted form - * @return {string} value - */ - getValue(){ - var val = this.column.cellDataGetter ? - this.column.cellDataGetter(this.value) : this.value; + // Enable horz scrollbars + // scrollbarH: true, - if(val === undefined || val === null) val = ''; - return val; - } + // The row height, which is necessary + // to calculate the height for the lazy rendering. + rowHeight: 30, -} + // flex + // force + // standard + columnMode: 'standard', -function CellDirective($rootScope, $compile, $log, $timeout){ - return { - restrict: 'E', - controller: CellController, - scope: true, - controllerAs: 'cell', - bindToController: { - options: '=', - value: '=', - selected: '=', - column: '=', - row: '=', - expanded: '=', - hasChildren: '=', - onTreeToggle: '&', - onCheckboxChange: '&' - }, - template: - `
- - - -
`, - replace: true, - compile: function() { - return { - pre: function($scope, $elm, $attrs, ctrl) { - var content = angular.element($elm[0].querySelector('.dt-cell-content')), cellScope; + // Loading message presented when the array is undefined + loadingMessage: 'Loading...', - // extend the outer scope onto our new cell scope - if(ctrl.column.template || ctrl.column.cellRenderer){ - createCellScope(); - } + // Message to show when array is presented + // but contains no values + emptyMessage: 'No data to display', - $scope.$watch('cell.row', () => { - if(cellScope){ - cellScope.$destroy(); + // The minimum header height in pixels. + // pass falsey for no header + headerHeight: 30, - createCellScope(); + // The minimum footer height in pixels. + // pass falsey for no footer + footerHeight: 0, - cellScope.$cell = ctrl.value; - cellScope.$row = ctrl.row; - cellScope.$column = ctrl.column; - cellScope.$$watchers = null; - } + paging: { + // if external paging is turned on + externalPaging: false, - if(ctrl.column.template){ - content.empty(); - var elm = angular.element(`${ctrl.column.template.trim()}`); - content.append($compile(elm)(cellScope)); - } else if(ctrl.column.cellRenderer){ - content.empty(); - var elm = angular.element(ctrl.column.cellRenderer(cellScope, content)); - content.append($compile(elm)(cellScope)); - } else { - content[0].innerHTML = ctrl.getValue(); - } - - }, true); + // Page size + size: undefined, - function createCellScope(){ - cellScope = ctrl.options.$outer.$new(false); - cellScope.getValue = ctrl.getValue; - } - } - } - } - }; -} + // Total count + count: 0, -var cache = {}, - testStyle = document.createElement('div').style; + // Page offset + offset: 0, + // Loading indicator + loadingIndicator: false + }, -// Get Prefix -// http://davidwalsh.name/vendor-prefix -var prefix = (function () { - var styles = window.getComputedStyle(document.documentElement, ''), - pre = (Array.prototype.slice - .call(styles) - .join('') - .match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']) - )[1], - dom = ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1]; - return { - dom: dom, - lowercase: pre, - css: '-' + pre + '-', - js: pre[0].toUpperCase() + pre.substr(1) - }; -})(); + // if users can select itmes + selectable: false, + // if users can select mutliple items + multiSelect: false, + + // checkbox selection vs row click + checkboxSelection: false, + + // if you can reorder columns + reorderable: true, + + internal: { + offsetX: 0, + offsetY: 0, + innerWidth: 0, + bodyHeight: 300 + } + +}; /** - * Converts strings from something to camel case - * http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase - * @param {string} str - * @return {string} camel case string + * Default Column Options + * @type {object} */ -function CamelCase(str) { - // Replace special characters with a space - str = str.replace(/[^a-zA-Z0-9 ]/g, " "); - // put a space before an uppercase letter - str = str.replace(/([a-z](?=[A-Z]))/g, '$1 '); - // Lower case first character and some other stuff - str = str.replace(/([^a-zA-Z0-9 ])|^[0-9]+/g, '').trim().toLowerCase(); - // uppercase characters preceded by a space or number - str = str.replace(/([ 0-9]+)([a-zA-Z])/g, function(a,b,c) { - return b.trim()+c.toUpperCase(); - }); - return str; -} +const ColumnDefaults = { + + // pinned to the left + frozenLeft: false, + + // pinned to the right + frozenRight: false, + + // body cell css class name + className: undefined, + + // header cell css class name + headerClassName: undefined, + + // The grow factor relative to other columns. Same as the flex-grow + // API from http://www.w3.org/TR/css3-flexbox/. Basically, + // take any available extra width and distribute it proportionally + // according to all columns' flexGrow values. + flexGrow: 0, + + // Minimum width of the column. + minWidth: 100, + + //Maximum width of the column. + maxWidth: undefined, + + // The width of the column, by default (in pixels). + width: 150, + + // If yes then the column can be resized, otherwise it cannot. + resizable: true, + + // Custom sort comparator + // pass false if you want to server sort + comparator: undefined, + + // If yes then the column can be sorted. + sortable: true, + + // Default sort asecending/descending for the column + sort: undefined, + + // If you want to sort a column by a special property + // See an example in demos/sort.html + sortBy: undefined, + + // The cell renderer that returns content for table column header + headerRenderer: undefined, + + // The cell renderer function(scope, elm) that returns React-renderable content for table cell. + cellRenderer: undefined, + + // The getter function(value) that returns the cell data for the cellRenderer. + // If not provided, the cell data will be collected from row data instead. + cellDataGetter: undefined, + + // Adds +/- button and makes a secondary call to load nested data + isTreeColumn: false, + + // Adds the checkbox selection to the column + isCheckboxColumn: false, + + // Toggles the checkbox column in the header + // for selecting all values given to the grid + headerCheckbox: false, + + // Whether the column can automatically resize to fill space in the table. + canAutoResize: true +}; /** - * @param {string} property Name of a css property to check for. - * @return {?string} property name supported in the browser, or null if not - * supported. + * Shim layer with setTimeout fallback + * http://www.html5rocks.com/en/tutorials/speed/animations/ */ -function GetVendorPrefixedName(property) { - var name = CamelCase(property) - if(!cache[name]){ - if(testStyle[prefix.css + property] !== undefined) { - cache[name] = prefix.css + property; - } else if(testStyle[property] !== undefined){ - cache[name] = property; - } - } - return cache[name]; -} +var requestAnimFrame = (function(){ + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function( callback ){ + window.setTimeout(callback, 1000 / 60); + }; +})(); +/** + * Creates a unique object id. + */ +function ObjectId() { + var timestamp = (new Date().getTime() / 1000 | 0).toString(16); + return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { + return (Math.random() * 16 | 0).toString(16); + }).toLowerCase(); +} -// browser detection and prefixing tools -var transform = GetVendorPrefixedName('transform'), - backfaceVisibility = GetVendorPrefixedName('backfaceVisibility'), - hasCSSTransforms = !!GetVendorPrefixedName('transform'), - hasCSS3DTransforms = !!GetVendorPrefixedName('perspective'), - ua = window.navigator.userAgent, - isSafari = (/Safari\//).test(ua) && !(/Chrome\//).test(ua); +/** + * Returns the columns by pin. + * @param {array} colsumns + */ +function ColumnsByPin(cols){ + var ret = { + left: [], + center: [], + right: [] + }; -function TranslateXY(styles, x,y){ - if (hasCSSTransforms) { - if (!isSafari && hasCSS3DTransforms) { - styles[transform] = `translate3d(${x}px, ${y}px, 0)`; - styles[backfaceVisibility] = 'hidden'; + for(var i=0, len=cols.length; i < len; i++) { + var c = cols[i]; + if(c.frozenLeft){ + ret.left.push(c); + } else if(c.frozenRight){ + ret.right.push(c); } else { - styles[CamelCase(transform)] = `translate(${x}px, ${y}px)`; + ret.center.push(c); } - } else { - styles.top = y + 'px'; - styles.left = x + 'px'; - } -} - -class GroupRowController { - - onGroupToggled(evt){ - evt.stopPropagation(); - this.onGroupToggle({ - group: this.row - }); - } - - treeClass(){ - return { - 'dt-tree-toggle': true, - 'icon-right': !this.expanded, - 'icon-down': this.expanded - }; } + return ret; } -function GroupRowDirective(){ +/** + * Returns the widths of all group sets of a column + * @param {object} groups + * @param {array} all + */ +function ColumnGroupWidths(groups, all){ return { - restrict: 'E', - controller: GroupRowController, - controllerAs: 'group', - bindToController: { - row: '=', - onGroupToggle: '&', - expanded: '=', - options: '=' - }, - scope: true, - replace:true, - template: ` -
- - - - -
`, - link: function($scope, $elm, $attrs, ctrl){ - // inital render position - TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); - - // register w/ the style translator - ctrl.options.internal.styleTranslator.register($scope.$index, $elm); - } + left: ColumnTotalWidth(groups.left), + center: ColumnTotalWidth(groups.center), + right: ColumnTotalWidth(groups.right), + total: ColumnTotalWidth(all) }; } - /** * Returns a deep object given a string. zoo['animal.type'] * @param {object} obj @@ -577,1058 +421,1036 @@ function DeepValueGetter(obj, path) { return current; } -class RowController { +/** + * Converts strings from something to camel case + * http://stackoverflow.com/questions/10425287/convert-dash-separated-string-to-camelcase + * @param {string} str + * @return {string} camel case string + */ +function CamelCase(str) { + // Replace special characters with a space + str = str.replace(/[^a-zA-Z0-9 ]/g, " "); + // put a space before an uppercase letter + str = str.replace(/([a-z](?=[A-Z]))/g, '$1 '); + // Lower case first character and some other stuff + str = str.replace(/([^a-zA-Z0-9 ])|^[0-9]+/g, '').trim().toLowerCase(); + // uppercase characters preceded by a space or number + str = str.replace(/([ 0-9]+)([a-zA-Z])/g, function(a,b,c) { + return b.trim()+c.toUpperCase(); + }); + return str; +} - /** - * Returns the value for a given column - * @param {col} - * @return {value} - */ - getValue(col){ - if(!col.prop) return ''; - return DeepValueGetter(this.row, col.prop); - } - /** - * Invoked when a cell triggers the tree toggle - * @param {cell} - */ - onTreeToggled(cell){ - this.onTreeToggle({ - cell: cell, - row: this.row - }); - } - - /** - * Calculates the styles for a pin group - * @param {group} - * @return {styles object} - */ - stylesByGroup( group){ - var styles = { - width: this.columnWidths[group] + 'px' - }; +/** + * Gets the width of the scrollbar. Nesc for windows + * http://stackoverflow.com/a/13382873/888165 + * @return {int} width + */ +function ScrollbarWidth() { + var outer = document.createElement("div"); + outer.style.visibility = "hidden"; + outer.style.width = "100px"; + outer.style.msOverflowStyle = "scrollbar"; + document.body.appendChild(outer); - if(group === 'left'){ - TranslateXY(styles, this.options.internal.offsetX, 0); - } else if(group === 'right'){ - var offset = (((this.columnWidths.total - this.options.internal.innerWidth) - - this.options.internal.offsetX) + this.options.internal.scrollBarWidth) * -1; - TranslateXY(styles, offset, 0); - } + var widthNoScroll = outer.offsetWidth; + outer.style.overflow = "scroll"; - return styles; - } + var inner = document.createElement("div"); + inner.style.width = "100%"; + outer.appendChild(inner); - /** - * Invoked when the cell directive's checkbox changed state - */ - onCheckboxChanged(ev){ - this.onCheckboxChange({ - $event: ev, - row: this.row - }); - } + var widthWithScroll = inner.offsetWidth; + outer.parentNode.removeChild(outer); + return widthNoScroll - widthWithScroll; } -function RowDirective(){ - return { - restrict: 'E', - controller: RowController, - controllerAs: 'rowCtrl', - scope: true, - bindToController: { - row: '=', - columns: '=', - columnWidths: '=', - expanded: '=', - selected: '=', - hasChildren: '=', - options: '=', - onCheckboxChange: '&', - onTreeToggle: '&' - }, - link: function($scope, $elm, $attrs, ctrl){ - if(ctrl.row){ - // inital render position - TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); - } - - // register w/ the style translator - ctrl.options.internal.styleTranslator.register($scope.$index, $elm); - }, - template: ` -
-
- - -
-
- - -
-
- - -
-
`, - replace:true - }; +function NextSortDirection(sortType, currentSort) { + if (sortType === 'single') { + if(currentSort === 'asc'){ + return 'desc'; + } else { + return 'asc'; + } + } else { + if(!currentSort){ + return 'asc'; + } else if(currentSort === 'asc'){ + return 'desc'; + } else if(currentSort === 'desc'){ + return undefined; + } + } } - /** - * Shortcut for key handlers - * @type {Object} + * Calculates the total width of all columns and their groups + * @param {array} columns + * @param {string} property width to get */ -var KEYS = { - BACKSPACE: 8, - TAB: 9, - RETURN: 13, - ALT: 18, - ESC: 27, - SPACE: 32, - PAGE_UP: 33, - PAGE_DOWN: 34, - END: 35, - HOME: 36, - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40, - DELETE: 46, - COMMA: 188, - PERIOD: 190, - A: 65, - Z: 90, - ZERO: 48, - NUMPAD_0: 96, - NUMPAD_9: 105 -}; +function ColumnTotalWidth(columns, prop) { + var totalWidth = 0; -class SelectionController { + columns.forEach((c) => { + var has = prop && c[prop]; + totalWidth = totalWidth + (has ? c[prop] : c.width); + }); - /*@ngInject*/ - constructor($scope){ - this.body = $scope.body; - this.options = $scope.body.options; - this.selected = $scope.body.selected; - } + return totalWidth; +} - /** - * Handler for the keydown on a row - * @param {event} - * @param {index} - * @param {row} - */ - keyDown(ev, index, row){ - if(KEYS[ev.keyCode]){ - ev.preventDefault(); - } +/** + * Calculates the Total Flex Grow + * @param {array} + */ +function GetTotalFlexGrow(columns){ + var totalFlexGrow = 0; - if (ev.keyCode === KEYS.DOWN) { - var next = ev.target.nextElementSibling; - if(next){ - next.focus(); - } - } else if (ev.keyCode === KEYS.UP) { - var prev = ev.target.previousElementSibling; - if(prev){ - prev.focus(); - } - } else if(ev.keyCode === KEYS.RETURN){ - this.selectRow(index, row); - } + for (let c of columns) { + totalFlexGrow += c.flexGrow || 0; } - /** - * Handler for the row click event - * @param {object} event - * @param {int} index - * @param {object} row - */ - rowClicked(event, index, row){ - if(!this.options.checkboxSelection){ - // event.preventDefault(); - this.selectRow(event, index, row); - } + return totalFlexGrow; +} - this.body.onRowClick({ row: row }); - } - - /** - * Handler for the row double click event - * @param {object} event - * @param {int} index - * @param {object} row - */ - rowDblClicked(event, index, row){ - if(!this.options.checkboxSelection){ - event.preventDefault(); - this.selectRow(event, index, row); - } +/** + * Adjusts the column widths. + * Inspired by: https://github.com/facebook/fixed-data-table/blob/master/src/FixedDataTableWidthHelper.js + * @param {array} all columns + * @param {int} width + */ +function AdjustColumnWidths(allColumns, expectedWidth){ + var columnsWidth = ColumnTotalWidth(allColumns), + totalFlexGrow = GetTotalFlexGrow(allColumns), + colsByGroup = ColumnsByPin(allColumns); - this.body.onRowDblClick({ row: row }); + if (columnsWidth !== expectedWidth){ + ScaleColumns(colsByGroup, expectedWidth, totalFlexGrow); } +} - /** - * Invoked when a row directive's checkbox was changed. - * @param {index} - * @param {row} - */ - onCheckboxChange(event, index, row){ - this.selectRow(event, index, row); - } +/** + * Resizes columns based on the flexGrow property, while respecting manually set widths + * @param {array} colsByGroup + * @param {int} maxWidth + * @param {int} totalFlexGrow + */ +function ScaleColumns(colsByGroup, maxWidth, totalFlexGrow) { + // calculate total width and flexgrow points for coulumns that can be resized + angular$1.forEach(colsByGroup, (cols) => { + cols.forEach((column) => { + if (!column.canAutoResize){ + maxWidth -= column.width; + totalFlexGrow -= column.flexGrow; + } else { + column.width = 0; + } + }); + }); - /** - * Selects a row and places in the selection collection - * @param {index} - * @param {row} - */ - selectRow(event, index, row){ - if(this.options.selectable){ - if(this.options.multiSelect){ - var isCtrlKeyDown = event.ctrlKey || event.metaKey, - isShiftKeyDown = event.shiftKey; + var hasMinWidth = {}; + var remainingWidth = maxWidth; - if(isShiftKeyDown){ - this.selectRowsBetween(index, row); - } else { - var idx = this.selected.indexOf(row); - if(idx > -1){ - this.selected.splice(idx, 1); + // resize columns until no width is left to be distributed + do { + let widthPerFlexPoint = remainingWidth / totalFlexGrow; + remainingWidth = 0; + angular$1.forEach(colsByGroup, (cols) => { + cols.forEach((column, i) => { + // if the column can be resize and it hasn't reached its minimum width yet + if (column.canAutoResize && !hasMinWidth[i]){ + let newWidth = column.width + column.flexGrow * widthPerFlexPoint; + if (column.minWidth !== undefined && newWidth < column.minWidth){ + remainingWidth += newWidth - column.minWidth; + column.width = column.minWidth; + hasMinWidth[i] = true; } else { - if(this.options.multiSelectOnShift && this.selected.length === 1) { - this.selected.splice(0, 1); - } - this.selected.push(row); - this.body.onSelect({ rows: [ row ] }); + column.width = newWidth; } } - this.prevIndex = index; + }); + }); + } while (remainingWidth !== 0); + +} + +/** + * Forces the width of the columns to + * distribute equally but overflowing when nesc. + * + * Rules: + * + * - If combined withs are less than the total width of the grid, + * proporation the widths given the min / max / noraml widths to fill the width. + * + * - If the combined widths, exceed the total width of the grid, + * use the standard widths. + * + * - If a column is resized, it should always use that width + * + * - The proporational widths should never fall below min size if specified. + * + * - If the grid starts off small but then becomes greater than the size ( + / - ) + * the width should use the orginial width; not the newly proporatied widths. + * + * @param {array} allColumns + * @param {int} expectedWidth + */ +function ForceFillColumnWidths(allColumns, expectedWidth, startIdx){ + var contentWidth = 0, + columnsToResize = startIdx > -1 ? + allColumns.slice(startIdx, allColumns.length).filter((c) => { return c.canAutoResize }) : + allColumns.filter((c) => { return c.canAutoResize }); + + allColumns.forEach((c) => { + if(!c.canAutoResize){ + contentWidth += c.width; + } else { + contentWidth += (c.$$oldWidth || c.width); + } + }); + + var remainingWidth = expectedWidth - contentWidth, + additionWidthPerColumn = remainingWidth / columnsToResize.length, + exceedsWindow = contentWidth > expectedWidth; + + columnsToResize.forEach((column) => { + if(exceedsWindow){ + column.width = column.$$oldWidth || column.width; + } else { + if(!column.$$oldWidth){ + column.$$oldWidth = column.width; + } + + var newSize = column.$$oldWidth + additionWidthPerColumn; + if(column.minWith && newSize < column.minWidth){ + column.width = column.minWidth; + } else if(column.maxWidth && newSize > column.maxWidth){ + column.width = column.maxWidth; } else { - this.selected = row; - this.body.onSelect({ rows: [ row ] }); + column.width = newSize; } } - } + }); +} + +class DataTableController { /** - * Selects the rows between a index. Used for shift click selection. - * @param {index} + * Creates an instance of the DataTable Controller + * @param {scope} + * @param {filter} */ - selectRowsBetween(index){ - var reverse = index < this.prevIndex, - selecteds = []; + /*@ngInject*/ + constructor($scope, $filter, $log, $transclude){ + Object.assign(this, { + $scope: $scope, + $filter: $filter, + $log: $log + }); - for(var i=0, len=this.body.rows.length; i < len; i++) { - var row = this.body.rows[i], - greater = i >= this.prevIndex && i <= index, - lesser = i <= this.prevIndex && i >= index; + this.defaults(); - var range = {}; - if ( reverse ) { - range = { - start: index, - end: ( this.prevIndex - index ) - } - } else { - range = { - start: this.prevIndex, - end: index + 1 - } - } + // set scope to the parent + this.options.$outer = $scope.$parent; - if((reverse && lesser) || (!reverse && greater)){ - var idx = this.selected.indexOf(row); - // if reverse shift selection (unselect) and the - // row is already selected, remove it from selected - if ( reverse && idx > -1 ) { - this.selected.splice(idx, 1); - continue; - } - // if in the positive range to be added to `selected`, and - // not already in the selected array, add it - if( i >= range.start && i < range.end ){ - if ( idx === -1 ) { - this.selected.push(row); - selecteds.push(row); - } - } - } - } + $scope.$watch('dt.options.columns', (newVal, oldVal) => { + this.transposeColumnDefaults(); - this.body.onSelect({ rows: selecteds }); - } -} + if(newVal.length !== oldVal.length){ + this.adjustColumns(); + } -function SelectionDirective(){ - return { - controller: SelectionController, - restrict: 'A', - require:'^dtBody', - controllerAs: 'selCtrl' - }; -} + this.calculateColumns(); + }, true); + // default sort + var watch = $scope.$watch('dt.rows', (newVal) => { + if(newVal){ + watch(); + this.onSorted(); + } + }); + } -/** - * Shim layer with setTimeout fallback - * http://www.html5rocks.com/en/tutorials/speed/animations/ - */ -var requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function( callback ){ - window.setTimeout(callback, 1000 / 60); - }; -})(); + /** + * Creates and extends default options for the grid control + */ + defaults(){ + this.expanded = this.expanded || {}; + this.options = angular$1.extend(angular$1. + copy(TableDefaults), this.options); -/** - * This translates the dom position based on the model row index. - * This only exists because Angular's binding process is too slow. - */ -class StyleTranslator{ + angular$1.forEach(TableDefaults.paging, (v,k) => { + if(!this.options.paging[k]){ + this.options.paging[k] = v; + } + }); - constructor(height){ - this.height = height; - this.map = new Map(); + if(this.options.selectable && this.options.multiSelect){ + this.selected = this.selected || []; + } } /** - * Update the rows - * @param {Array} rows + * On init or when a column is added, we need to + * make sure all the columns added have the correct + * defaults applied. */ - update(rows){ - let n = 0; - while (n <= this.map.size) { - let dom = this.map.get(n); - let model = rows[n]; - if(dom && model){ - TranslateXY(dom[0].style, 0, model.$$index * this.height); + transposeColumnDefaults(){ + for(var i=0, len = this.options.columns.length; i < len; i++) { + var column = this.options.columns[i]; + column.$id = ObjectId(); + + angular$1.forEach(ColumnDefaults, (v,k) => { + if(!column.hasOwnProperty(k)){ + column[k] = v; + } + }); + + if(column.name && !column.prop){ + column.prop = CamelCase(column.name); } - n++; + + this.options.columns[i] = column; } } /** - * Register the row - * @param {int} idx - * @param {dom} dom + * Calculate column groups and widths */ - register(idx, dom){ - this.map.set(idx, dom); + calculateColumns(){ + var columns = this.options.columns; + this.columnsByPin = ColumnsByPin(columns); + this.columnWidths = ColumnGroupWidths(this.columnsByPin, columns); } -} - -function ScrollerDirective($timeout, $rootScope){ - return { - restrict: 'E', - require:'^dtBody', - transclude: true, - replace: true, - template: `
`, - link: function($scope, $elm, $attrs, ctrl){ - var ticking = false, - lastScrollY = 0, - lastScrollX = 0, - parent = $elm.parent(); - - ctrl.options.internal.styleTranslator = - new StyleTranslator(ctrl.options.rowHeight); - - ctrl.options.internal.setYOffset = function(offsetY){ - parent[0].scrollTop = offsetY; - }; + /** + * Returns the css classes for the data table. + * @return {style object} + */ + tableCss(){ + return { + 'fixed': this.options.scrollbarV, + 'selectable': this.options.selectable, + 'checkboxable': this.options.checkboxSelection + }; + } - function update(){ - ctrl.options.internal.offsetY = lastScrollY; - ctrl.options.internal.offsetX = lastScrollX; - ctrl.updatePage(); + /** + * Adjusts the column widths to handle greed/etc. + * @param {int} forceIdx + */ + adjustColumns(forceIdx){ + var width = this.options.internal.innerWidth - this.options.internal.scrollBarWidth; - if(ctrl.options.scrollbarV){ - ctrl.getRows(); - } + if(this.options.columnMode === 'force'){ + ForceFillColumnWidths(this.options.columns, width, forceIdx); + } else if(this.options.columnMode === 'flex') { + AdjustColumnWidths(this.options.columns, width); + } + } - // https://github.com/Swimlane/angular-data-table/pull/74 - ctrl.options.$outer.$digest(); + /** + * Calculates the page size given the height * row height. + * @return {[type]} + */ + calculatePageSize(){ + this.options.paging.size = Math.ceil( + this.options.internal.bodyHeight / this.options.rowHeight) + 1; + } - ticking = false; - }; + /** + * Sorts the values of the grid for client side sorting. + */ + onSorted(){ + if(!this.rows) return; - function requestTick() { - if(!ticking) { - requestAnimFrame(update); - ticking = true; + // return all sorted column, in the same order in which they were sorted + var sorts = this.options.columns + .filter((c) => { + return c.sort; + }) + .sort((a, b) => { + // sort the columns with lower sortPriority order first + if (a.sortPriority && b.sortPriority){ + if (a.sortPriority > b.sortPriority) return 1; + if (a.sortPriority < b.sortPriority) return -1; + } else if (a.sortPriority){ + return -1; + } else if (b.sortPriority){ + return 1; } - }; - parent.on('scroll', function(ev) { - lastScrollY = this.scrollTop; - lastScrollX = this.scrollLeft; - requestTick(); + return 0; + }) + .map((c, i) => { + // update sortPriority + c.sortPriority = i + 1; + return c; }); - $scope.$on('$destroy', () => { - parent.off('scroll'); - }); + if(sorts.length){ + this.onSort({sorts: sorts}); - $scope.scrollerStyles = function(){ - if(ctrl.options.scrollbarV){ - return { - height: ctrl.count * ctrl.options.rowHeight + 'px' + if (this.options.onSort){ + this.options.onSort(sorts); + } + + var clientSorts = []; + for(var i=0, len=sorts.length; i < len; i++) { + var c = sorts[i]; + if(c.comparator !== false){ + var dir = c.sort === 'asc' ? '' : '-'; + if (c.sortBy !== undefined) { + clientSorts.push(dir + c.sortBy); + } else { + clientSorts.push(dir + c.prop); } } - }; + } + if(clientSorts.length){ + // todo: more ideal to just resort vs splice and repush + // but wasn't responding to this change ... + var sortedValues = this.$filter('orderBy')(this.rows, clientSorts); + this.rows.splice(0, this.rows.length); + this.rows.push(...sortedValues); + } } - }; -} -class BodyController{ + this.options.internal.setYOffset(0); + } /** - * A tale body controller - * @param {$scope} - * @param {$timeout} - * @return {BodyController} + * Invoked when a tree is collasped/expanded + * @param {row model} + * @param {cell model} */ - /*@ngInject*/ - constructor($scope, $timeout){ - this.$scope = $scope; - this.tempRows = []; - - this.treeColumn = this.options.columns.find((c) => { - return c.isTreeColumn; + onTreeToggled(row, cell){ + this.onTreeToggle({ + row: row, + cell: cell }); + } - this.groupColumn = this.options.columns.find((c) => { - return c.group; + /** + * Invoked when the body triggers a page change. + * @param {offset} + * @param {size} + */ + onBodyPage(offset, size){ + this.onPage({ + offset: offset, + size: size }); - - $scope.$watchCollection('body.rows', this.rowsUpdated.bind(this)); - - if(this.options.scrollbarV || (!this.options.scrollbarV && this.options.paging.externalPaging)){ - var sized = false; - $scope.$watch('body.options.paging.size', (newVal, oldVal) => { - if(!sized || newVal > oldVal){ - this.getRows(); - sized = true; - } - }); - - $scope.$watch('body.options.paging.count', (count) => { - this.count = count; - this.updatePage(); - }); - - $scope.$watch('body.options.paging.offset', (newVal) => { - if(this.options.paging.size){ - this.onPage({ - offset: newVal, - size: this.options.paging.size - }); - } - }); - } } - rowsUpdated(newVal, oldVal){ - if(newVal) { - if(!this.options.paging.externalPaging){ - this.options.paging.count = newVal.length; - } + /** + * Invoked when the footer triggers a page change. + * @param {offset} + * @param {size} + */ + onFooterPage(offset, size){ + var pageBlockSize = this.options.rowHeight * size, + offsetY = pageBlockSize * offset; + + this.options.internal.setYOffset(offsetY); + } - this.count = this.options.paging.count; + /** + * Invoked when the header checkbox directive has changed. + */ + onHeaderCheckboxChange(){ + if(this.rows){ + var matches = this.selected.length === this.rows.length; + this.selected.splice(0, this.selected.length); - if(this.treeColumn || this.groupColumn){ - this.buildRowsByGroup(); + if(!matches){ + this.selected.push(...this.rows); } + } + } - if(this.options.scrollbarV){ - let refresh = newVal && oldVal && (newVal.length === oldVal.length - || newVal.length < oldVal.length); - - this.getRows(refresh); - } else { - let rows = this.rows; + /** + * Returns if all the rows are selected + * @return {Boolean} if all selected + */ + isAllRowsSelected(){ + if(this.rows) return false; + return this.selected.length === this.rows.length; + } - if(this.treeColumn){ - rows = this.buildTree(); - } else if(this.groupColumn){ - rows = this.buildGroups(); - } + /** + * Occurs when a header directive triggered a resize event + * @param {object} column + * @param {int} width + */ + onResized(column, width){ + var idx =this.options.columns.indexOf(column); + if(idx > -1){ + var column = this.options.columns[idx]; + column.width = width; + column.canAutoResize = false; - if(this.options.paging.externalPaging){ - let idxs = this.getFirstLastIndexes(), - idx = idxs.first; + this.adjustColumns(idx); + this.calculateColumns(); + } - this.tempRows.splice(0, this.tempRows.length); - while(idx < idxs.last){ - this.tempRows.push(rows[idx++]) - } - } else { - this.tempRows.splice(0, this.tempRows.length); - this.tempRows.push(...rows); - } - } + if (this.onColumnResize){ + this.onColumnResize({ + column: column, + width: width + }); } } /** - * Gets the first and last indexes based on the offset, row height, page size, and overall count. + * Occurs when a row was selected + * @param {object} rows */ - getFirstLastIndexes(){ - var firstRowIndex, endIndex; - - if(this.options.scrollbarV){ - firstRowIndex = Math.max(Math.floor(( - this.options.internal.offsetY || 0) / this.options.rowHeight, 0), 0); - endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); - } else { - if(this.options.paging.externalPaging){ - firstRowIndex = Math.max(this.options.paging.offset * this.options.paging.size, 0); - endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); - } else { - endIndex = this.count; - } - } + onSelected(rows){ + this.onSelect({ + rows: rows + }); + } - return { - first: firstRowIndex, - last: endIndex - }; + /** + * Occurs when a row was click but may not be selected. + * @param {object} row + */ + onRowClicked(row){ + this.onRowClick({ + row: row + }); } /** - * Updates the page's offset given the scroll position. + * Occurs when a row was double click but may not be selected. + * @param {object} row */ - updatePage(){ - let curPage = this.options.paging.offset, - idxs = this.getFirstLastIndexes(); + onRowDblClicked(row){ + this.onRowDblClick({ + row: row + }); + } - if (this.options.internal.oldScrollPosition === undefined){ - this.options.internal.oldScrollPosition = 0; - } +} - let oldScrollPosition = this.options.internal.oldScrollPosition, - newPage = idxs.first / this.options.paging.size; +/** + * Debounce helper + * @param {function} + * @param {int} + * @param {boolean} + */ - this.options.internal.oldScrollPosition = newPage; - if (newPage < oldScrollPosition) { - // scrolling up - newPage = Math.floor(newPage); - } else if (newPage > oldScrollPosition){ - // scrolling down - newPage = Math.ceil(newPage); - } else { - // equal, just stay on the current page - newPage = curPage; +/** + * Throttle helper + * @param {function} + * @param {boolean} + * @param {object} + */ +function throttle(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + options || (options = {}); + var later = function() { + previous = options.leading === false ? 0 : new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(); + if (!previous && options.leading === false) + previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); } + return result; + }; +} - if(!isNaN(newPage)){ - this.options.paging.offset = newPage; - } - } +let DataTableService = { - /** - * Recursively calculate row depth for unsorted backend data - * @param row - * @param depth - * @return {Integer} - */ - calculateDepth(row, depth=0){ - var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; - var prop = this.treeColumn.prop; - if (!row[parentProp]){ - return depth; - } - if (row.$$depth) { - return row.$$depth + depth; - } - /* Get data from cache, if exists*/ - var cachedParent = this.index[row[parentProp]]; - if (cachedParent) { - depth += 1; - return this.calculateDepth(cachedParent, depth); - } - for (var i=0, len = this.rows.length; i < len; i++){ - var parent = this.rows[i]; - if (parent[prop] == row[parentProp]){ - depth+=1; - return this.calculateDepth(parent, depth); - } + // id: [ column defs ] + columns: {}, + dTables: {}, + + saveColumns(id, columnElms) { + if (columnElms && columnElms.length) { + let columnsArray = [].slice.call(columnElms); + this.dTables[id] = columnsArray; } - return depth; - } + }, /** - * Matches groups to their respective parents by index. - * - * Example: - * - * { - * "Acme" : [ - * { name: "Acme Holdings", parent: "Acme" } - * ], - * "Acme Holdings": [ - * { name: "Acme Ltd", parent: "Acme Holdings" } - * ] - * } - * + * Create columns from elements + * @param {array} columnElms */ - buildRowsByGroup(){ - this.index = {}; - this.rowsByGroup = {}; + buildColumns(scope, parse) { + //FIXME: Too many nested for loops. O(n3) + + // Iterate through each dTable + angular$1.forEach(this.dTables, (columnElms, id) => { + this.columns[id] = []; + + // Iterate through each column + angular$1.forEach(columnElms, (c) => { + let column = {}; + + var visible = true; + // Iterate through each attribute + angular$1.forEach(c.attributes, (attr) => { + let attrName = CamelCase(attr.name); + + // cuz putting className vs class on + // a element feels weird + switch (attrName) { + case 'class': + column.className = attr.value; + break; + case 'name': + case 'prop': + column[attrName] = attr.value; + break; + case 'headerRenderer': + case 'cellRenderer': + case 'cellDataGetter': + column[attrName] = parse(attr.value); + break; + case 'visible': + visible = parse(attr.value)(scope); + break; + default: + column[attrName] = parse(attr.value)(scope); + break; + } + }); - var parentProp = this.treeColumn ? - this.treeColumn.relationProp : - this.groupColumn.prop; + let header = c.getElementsByTagName('column-header'); + if(header.length){ + column.headerTemplate = header[0].innerHTML; + c.removeChild(header[0]); + } - for(var i = 0, len = this.rows.length; i < len; i++) { - var row = this.rows[i]; - // build groups - var relVal = row[parentProp]; - if(relVal){ - if(this.rowsByGroup[relVal]){ - this.rowsByGroup[relVal].push(row); - } else { - this.rowsByGroup[relVal] = [ row ]; + if (c.innerHTML !== '') { + column.template = c.innerHTML; } - } - // build indexes - if(this.treeColumn){ - var prop = this.treeColumn.prop; - this.index[row[prop]] = row; + if (visible) + this.columns[id].push(column); + }); + }); - if (row[parentProp] === undefined){ - row.$$depth = 0; - } else { - var parent = this.index[row[parentProp]]; - if (parent === undefined){ - for (var j=0; j < len; j++){ - if (this.rows[j][prop] == relVal){ - parent = this.rows[j]; - break; - } - } - } - if (parent.$$depth === undefined) { - parent.$$depth = this.calculateDepth(parent); - } - row.$$depth = parent.$$depth + 1; - if (parent.$$children){ - parent.$$children.push(row[prop]); - } else { - parent.$$children = [row[prop]]; - } - } - } - } + this.dTables = {}; } +}; - /** - * Rebuilds the groups based on what is expanded. - * This function needs some optimization, todo for future release. - * @return {Array} the temp array containing expanded rows - */ - buildGroups(){ - var temp = []; +function DataTableDirective($window, $timeout, $parse){ + return { + restrict: 'E', + replace: true, + controller: DataTableController, + scope: true, + bindToController: { + options: '=', + rows: '=', + selected: '=?', + expanded: '=?', + onSelect: '&', + onSort: '&', + onTreeToggle: '&', + onPage: '&', + onRowClick: '&', + onRowDblClick: '&', + onColumnResize: '&' + }, + controllerAs: 'dt', + template: function(element){ + // Gets the column nodes to transposes to column objects + // http://stackoverflow.com/questions/30845397/angular-expressive-directive-design/30847609#30847609 + var columns = element[0].getElementsByTagName('column'), + id = ObjectId(); + DataTableService.saveColumns(id, columns); - angular.forEach(this.rowsByGroup, (v, k) => { - temp.push({ - name: k, - group: true - }); + return `
+ + + + + + +
` + }, + compile: function(tElem, tAttrs){ + return { + pre: function($scope, $elm, $attrs, ctrl){ + DataTableService.buildColumns($scope, $parse); - if(this.expanded[k]){ - temp.push(...v); - } - }); + // Check and see if we had expressive columns + // and if so, lets use those + var id = $elm.attr('data-column-id'), + columns = DataTableService.columns[id]; + if (columns) { + ctrl.options.columns = columns; + } - return temp; - } + ctrl.transposeColumnDefaults(); + ctrl.options.internal.scrollBarWidth = ScrollbarWidth(); - /** - * Returns if the row is selected - * @param {row} - * @return {Boolean} - */ - isSelected(row){ - var selected = false; + /** + * Invoked on init of control or when the window is resized; + */ + function resize() { + var rect = $elm[0].getBoundingClientRect(); - if(this.options.selectable){ - if(this.options.multiSelect){ - selected = this.selected.indexOf(row) > -1; - } else { - selected = this.selected === row; - } - } + ctrl.options.internal.innerWidth = Math.floor(rect.width); - return selected; - } + if (ctrl.options.scrollbarV) { + var height = rect.height; - /** - * Creates a tree of the existing expanded values - * @return {array} the built tree - */ - buildTree(){ - var temp = [], - self = this; + if (ctrl.options.headerHeight) { + height = height - ctrl.options.headerHeight; + } - function addChildren(fromArray, toArray, level) { - fromArray.forEach(function (row) { - var relVal = row[self.treeColumn.relationProp], - key = row[self.treeColumn.prop], - groupRows = self.rowsByGroup[key], - expanded = self.expanded[key]; + if (ctrl.options.footerHeight) { + height = height - ctrl.options.footerHeight; + } - if (level > 0 || !relVal) { - toArray.push(row); - if (groupRows && groupRows.length > 0 && expanded) { - addChildren(groupRows, toArray, level + 1); + ctrl.options.internal.bodyHeight = height; + ctrl.calculatePageSize(); + } + + ctrl.adjustColumns(); } + + $window.addEventListener('resize', + throttle(() => { + $timeout(resize); + })); + + // When an item is hidden for example + // in a tab with display none, the height + // is not calculated correrctly. We need to watch + // the visible attribute and resize if this occurs + var checkVisibility = function() { + var bounds = $elm[0].getBoundingClientRect(), + visible = bounds.width && bounds.height; + if (visible) resize(); + else $timeout(checkVisibility, 100); + }; + checkVisibility(); + + // add a loaded class to avoid flickering + $elm.addClass('dt-loaded'); + + // prevent memory leaks + $scope.$on('$destroy', () => { + angular$1.element($window).off('resize'); + }); } + }; + } + }; +} - }); +var cache = {}; +var testStyle = document.createElement('div').style; + +// Get Prefix +// http://davidwalsh.name/vendor-prefix +var prefix = (function () { + var styles = window.getComputedStyle(document.documentElement, ''), + pre = (Array.prototype.slice + .call(styles) + .join('') + .match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']) + )[1], + dom = ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1]; + return { + dom: dom, + lowercase: pre, + css: '-' + pre + '-', + js: pre[0].toUpperCase() + pre.substr(1) + }; +})(); + +/** + * @param {string} property Name of a css property to check for. + * @return {?string} property name supported in the browser, or null if not + * supported. + */ +function GetVendorPrefixedName(property) { + var name = CamelCase(property); + if(!cache[name]){ + if(testStyle[prefix.css + property] !== undefined) { + cache[name] = prefix.css + property; + } else if(testStyle[property] !== undefined){ + cache[name] = property; } - - addChildren(this.rows, temp, 0); - - return temp; } + return cache[name]; +} - /** - * Creates the intermediate collection that is shown in the view. - * @param {boolean} refresh - bust the tree/group cache - */ - getRows(refresh){ - // only proceed when we have pre-aggregated the values - if((this.treeColumn || this.groupColumn) && !this.rowsByGroup){ - return false; - } - - var temp; - - if(this.treeColumn) { - temp = this.treeTemp || []; - // cache the tree build - if((refresh || !this.treeTemp)){ - this.treeTemp = temp = this.buildTree(); - this.count = temp.length; +// browser detection and prefixing tools +var transform = GetVendorPrefixedName('transform'); +var backfaceVisibility = GetVendorPrefixedName('backfaceVisibility'); +var hasCSSTransforms = !!GetVendorPrefixedName('transform'); +var hasCSS3DTransforms = !!GetVendorPrefixedName('perspective'); +var ua = window.navigator.userAgent; +var isSafari = (/Safari\//).test(ua) && !(/Chrome\//).test(ua); - // have to force reset, optimize this later - this.tempRows.splice(0, this.tempRows.length); - } - } else if(this.groupColumn) { - temp = this.groupsTemp || []; - // cache the group build - if((refresh || !this.groupsTemp)){ - this.groupsTemp = temp = this.buildGroups(); - this.count = temp.length; - } +function TranslateXY(styles, x,y){ + if (hasCSSTransforms) { + if (!isSafari && hasCSS3DTransforms) { + styles[transform] = `translate3d(${x}px, ${y}px, 0)`; + styles[backfaceVisibility] = 'hidden'; } else { - temp = this.rows; - if(refresh === true){ - this.tempRows.splice(0, this.tempRows.length); - } + styles[CamelCase(transform)] = `translate(${x}px, ${y}px)`; } + } else { + styles.top = y + 'px'; + styles.left = x + 'px'; + } +} - var idx = 0, - indexes = this.getFirstLastIndexes(), - rowIndex = indexes.first; - - // slice out the old rows so we don't have duplicates - this.tempRows.splice(0, indexes.last - indexes.first); +class HeaderController { - while (rowIndex < indexes.last && rowIndex < this.count) { - var row = temp[rowIndex]; - if(row){ - row.$$index = rowIndex; - this.tempRows[idx] = row; - } - idx++; - rowIndex++; + /** + * Returns the styles for the header directive. + * @param {object} scope + * @return {object} styles + */ + styles() { + return { + width: this.options.internal.innerWidth + 'px', + height: this.options.headerHeight + 'px' } - - this.options.internal.styleTranslator.update(this.tempRows); - - return this.tempRows; } /** - * Returns the styles for the table body directive. - * @return {object} + * Returns the inner styles for the header directive + * @param {object} scope + * @return {object} styles */ - styles(){ - var styles = { - width: this.options.internal.innerWidth + 'px' + innerStyles(){ + return { + width: this.columnWidths.total + 'px' }; - - if(!this.options.scrollbarV){ - styles.overflowY = 'hidden'; - } else if(this.options.scrollbarH === false){ - styles.overflowX = 'hidden'; - } - - if(this.options.scrollbarV){ - styles.height = this.options.internal.bodyHeight + 'px'; - } - - return styles; } /** - * Returns the styles for the row diretive. - * @param {row} - * @return {styles object} + * Invoked when a column sort direction has changed + * @param {object} scope + * @param {object} column */ - rowStyles(row){ - let styles = {}; + onSorted(sortedColumn){ + if (this.options.sortType === 'single') { + // if sort type is single, then only one column can be sorted at once, + // so we set the sort to undefined for the other columns + function unsortColumn(column) { + if (column !== sortedColumn) { + column.sort = undefined; + } + } - if(this.options.rowHeight === 'auto'){ - styles.height = this.options.rowHeight + 'px'; + this.columns.left.forEach(unsortColumn); + this.columns.center.forEach(unsortColumn); + this.columns.right.forEach(unsortColumn); } - return styles; - } - - /** - * Builds the styles for the row group directive - * @param {object} row - * @return {object} styles - */ - groupRowStyles(row){ - var styles = this.rowStyles(row); - styles.width = this.columnWidths.total + 'px'; - return styles; + this.onSort({ + column: sortedColumn + }); } /** - * Returns the css classes for the row directive. - * @param {row} - * @return {css class object} + * Returns the styles by group for the headers. + * @param {scope} + * @param {group} + * @return {styles object} */ - rowClasses(row){ + stylesByGroup(group){ var styles = { - 'selected': this.isSelected(row), - 'dt-row-even': row && row.$$index%2 === 0, - 'dt-row-odd': row && row.$$index%2 !== 0 + width: this.columnWidths[group] + 'px' }; - if(this.treeColumn){ - // if i am a child - styles['dt-leaf'] = this.rowsByGroup[row[this.treeColumn.relationProp]]; - // if i have children - styles['dt-has-leafs'] = this.rowsByGroup[row[this.treeColumn.prop]]; - // the depth - styles['dt-depth-' + row.$$depth] = true; + if(group === 'center'){ + TranslateXY(styles, this.options.internal.offsetX * -1, 0); + } else if(group === 'right'){ + var offset = (this.columnWidths.total - this.options.internal.innerWidth) *-1; + TranslateXY(styles, offset, 0); } return styles; } /** - * Returns the row model for the index in the view. - * @param {index} - * @return {row model} + * Invoked when the header cell directive's checkbox has changed. + * @param {scope} */ - getRowValue(idx){ - return this.tempRows[idx]; + onCheckboxChanged(){ + this.onCheckboxChange(); } /** - * Calculates if a row is expanded or collasped for tree grids. - * @param {row} - * @return {boolean} + * Occurs when a header cell directive triggered a resize + * @param {object} scope + * @param {object} column + * @param {int} width */ - getRowExpanded(row){ - if(this.treeColumn) { - return this.expanded[row[this.treeColumn.prop]]; - } else if(this.groupColumn){ - return this.expanded[row.name]; - } + onResized(column, width){ + this.onResize({ + column: column, + width: width + }); } - /** - * Calculates if the row has children - * @param {row} - * @return {boolean} - */ - getRowHasChildren(row){ - if(!this.treeColumn) return; - var children = this.rowsByGroup[row[this.treeColumn.prop]]; - return children !== undefined || (children && !children.length); - } +} + +function HeaderDirective($timeout){ + return { + restrict: 'E', + controller: HeaderController, + controllerAs: 'header', + scope: true, + bindToController: { + options: '=', + columns: '=', + columnWidths: '=', + onSort: '&', + onResize: '&', + onCheckboxChange: '&' + }, + template: ` +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
`, + replace:true, + link: function($scope, $elm, $attrs, ctrl){ + + $scope.columnsResorted = function(event, columnId){ + var col = findColumnById(columnId), + parent = angular$1.element(event.currentTarget), + newIdx = -1; - /** - * Tree toggle event from a cell - * @param {row model} - * @param {cell model} - */ - onTreeToggled(row, cell){ - var val = row[this.treeColumn.prop]; - this.expanded[val] = !this.expanded[val]; + angular$1.forEach(parent.children(), (c, i) => { + if (columnId === angular$1.element(c).attr('data-id')) { + newIdx = i; + } + }); - if(this.options.scrollbarV){ - this.getRows(true); - } else { - var values = this.buildTree(); - this.tempRows.splice(0, this.tempRows.length); - this.tempRows.push(...values); - } + $timeout(() => { + angular$1.forEach(ctrl.columns, (group) => { + var idx = group.indexOf(col); + if(idx > -1){ - this.onTreeToggle({ - row: row, - cell: cell - }); - } + // this is tricky because we want to update the index + // in the orig columns array instead of the grouped one + var curColAtIdx = group[newIdx], + siblingIdx = ctrl.options.columns.indexOf(curColAtIdx), + curIdx = ctrl.options.columns.indexOf(col); - /** - * Invoked when the row group directive was expanded - * @param {object} row - */ - onGroupToggle(row){ - this.expanded[row.name] = !this.expanded[row.name]; + ctrl.options.columns.splice(curIdx, 1); + ctrl.options.columns.splice(siblingIdx, 0, col); - if(this.options.scrollbarV){ - this.getRows(true); - } else { - var values = this.buildGroups(); - this.tempRows.splice(0, this.tempRows.length); - this.tempRows.push(...values); - } - } -} + return false; + } + }); -function BodyDirective($timeout){ - return { - restrict: 'E', - controller: BodyController, - controllerAs: 'body', - bindToController: { - columns: '=', - columnWidths: '=', - rows: '=', - options: '=', - selected: '=?', - expanded: '=?', - onPage: '&', - onTreeToggle: '&', - onSelect: '&', - onRowClick: '&', - onRowDblClick: '&' - }, - scope: true, - template: ` -
-
-
-
-
-
- - - - - - -
-
-
-
-
` - }; -} + }); + }; -function NextSortDirection(sortType, currentSort) { - if (sortType === 'single') { - if(currentSort === 'asc'){ - return 'desc'; - } else { - return 'asc'; - } - } else { - if(!currentSort){ - return 'asc'; - } else if(currentSort === 'asc'){ - return 'desc'; - } else if(currentSort === 'desc'){ - return undefined; + var findColumnById = function(columnId){ + var columns = ctrl.columns.left.concat(ctrl.columns.center).concat(ctrl.columns.right); + return columns.find(function(c){ + return c.$id === columnId; + }) + }; } - } + }; } class HeaderCellController{ @@ -1756,1315 +1578,1973 @@ function HeaderCellDirective($compile){ if(ctrl.column.headerTemplate || ctrl.column.headerRenderer){ cellScope = ctrl.options.$outer.$new(false); - // copy some props - cellScope.$header = ctrl.column.name; - cellScope.$index = $scope.$index; - } + // copy some props + cellScope.$header = ctrl.column.name; + cellScope.$index = $scope.$index; + } + + if(ctrl.column.headerTemplate){ + let elm = angular$1.element(`${ctrl.column.headerTemplate.trim()}`); + angular$1.element(label).append($compile(elm)(cellScope)); + } else if(ctrl.column.headerRenderer){ + let elm = angular$1.element(ctrl.column.headerRenderer($elm)); + angular$1.element(label).append($compile(elm)(cellScope)[0]); + } else { + let val = ctrl.column.name; + if(val === undefined || val === null) val = ''; + label.textContent = val; + } + } + } + } + }; +} + +class BodyController{ + + /** + * A tale body controller + * @param {$scope} + * @param {$timeout} + * @return {BodyController} + */ + /*@ngInject*/ + constructor($scope, $timeout){ + this.$scope = $scope; + this.tempRows = []; + + this.treeColumn = this.options.columns.find((c) => { + return c.isTreeColumn; + }); + + this.groupColumn = this.options.columns.find((c) => { + return c.group; + }); + + $scope.$watchCollection('body.rows', this.rowsUpdated.bind(this)); + + if(this.options.scrollbarV || (!this.options.scrollbarV && this.options.paging.externalPaging)){ + var sized = false; + $scope.$watch('body.options.paging.size', (newVal, oldVal) => { + if(!sized || newVal > oldVal){ + this.getRows(); + sized = true; + } + }); + + $scope.$watch('body.options.paging.count', (count) => { + this.count = count; + this.updatePage(); + }); + + $scope.$watch('body.options.paging.offset', (newVal) => { + if(this.options.paging.size){ + this.onPage({ + offset: newVal, + size: this.options.paging.size + }); + } + }); + } + } + + rowsUpdated(newVal, oldVal){ + if(newVal) { + if(!this.options.paging.externalPaging){ + this.options.paging.count = newVal.length; + } + + this.count = this.options.paging.count; + + if(this.treeColumn || this.groupColumn){ + this.buildRowsByGroup(); + } + + if(this.options.scrollbarV){ + let refresh = newVal && oldVal && (newVal.length === oldVal.length + || newVal.length < oldVal.length); + + this.getRows(refresh); + } else { + let rows = this.rows; + + if(this.treeColumn){ + rows = this.buildTree(); + } else if(this.groupColumn){ + rows = this.buildGroups(); + } + + if(this.options.paging.externalPaging){ + let idxs = this.getFirstLastIndexes(), + idx = idxs.first; - if(ctrl.column.headerTemplate){ - let elm = angular.element(`${ctrl.column.headerTemplate.trim()}`); - angular.element(label).append($compile(elm)(cellScope)); - } else if(ctrl.column.headerRenderer){ - let elm = angular.element(ctrl.column.headerRenderer($elm)); - angular.element(label).append($compile(elm)(cellScope)[0]); - } else { - let val = ctrl.column.name; - if(val === undefined || val === null) val = ''; - label.textContent = val; + this.tempRows.splice(0, this.tempRows.length); + while(idx < idxs.last){ + this.tempRows.push(rows[idx++]); } + } else { + this.tempRows.splice(0, this.tempRows.length); + this.tempRows.push(...rows); } } } - }; -} - -class HeaderController { + } /** - * Returns the styles for the header directive. - * @param {object} scope - * @return {object} styles + * Gets the first and last indexes based on the offset, row height, page size, and overall count. */ - styles() { - return { - width: this.options.internal.innerWidth + 'px', - height: this.options.headerHeight + 'px' + getFirstLastIndexes(){ + var firstRowIndex, endIndex; + + if(this.options.scrollbarV){ + firstRowIndex = Math.max(Math.floor(( + this.options.internal.offsetY || 0) / this.options.rowHeight, 0), 0); + endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); + } else { + if(this.options.paging.externalPaging){ + firstRowIndex = Math.max(this.options.paging.offset * this.options.paging.size, 0); + endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); + } else { + endIndex = this.count; + } } - } - /** - * Returns the inner styles for the header directive - * @param {object} scope - * @return {object} styles - */ - innerStyles(){ return { - width: this.columnWidths.total + 'px' + first: firstRowIndex, + last: endIndex }; } /** - * Invoked when a column sort direction has changed - * @param {object} scope - * @param {object} column + * Updates the page's offset given the scroll position. */ - onSorted(sortedColumn){ - if (this.options.sortType === 'single') { - // if sort type is single, then only one column can be sorted at once, - // so we set the sort to undefined for the other columns - function unsortColumn(column) { - if (column !== sortedColumn) { - column.sort = undefined; - } - } + updatePage(){ + let curPage = this.options.paging.offset, + idxs = this.getFirstLastIndexes(); - this.columns.left.forEach(unsortColumn); - this.columns.center.forEach(unsortColumn); - this.columns.right.forEach(unsortColumn); + if (this.options.internal.oldScrollPosition === undefined){ + this.options.internal.oldScrollPosition = 0; } - this.onSort({ - column: sortedColumn - }); - } + let oldScrollPosition = this.options.internal.oldScrollPosition, + newPage = idxs.first / this.options.paging.size; - /** - * Returns the styles by group for the headers. - * @param {scope} - * @param {group} - * @return {styles object} - */ - stylesByGroup(group){ - var styles = { - width: this.columnWidths[group] + 'px' - }; + this.options.internal.oldScrollPosition = newPage; - if(group === 'center'){ - TranslateXY(styles, this.options.internal.offsetX * -1, 0); - } else if(group === 'right'){ - var offset = (this.columnWidths.total - this.options.internal.innerWidth) *-1; - TranslateXY(styles, offset, 0); + if (newPage < oldScrollPosition) { + // scrolling up + newPage = Math.floor(newPage); + } else if (newPage > oldScrollPosition){ + // scrolling down + newPage = Math.ceil(newPage); + } else { + // equal, just stay on the current page + newPage = curPage; } - return styles; + if(!isNaN(newPage)){ + this.options.paging.offset = newPage; + } } /** - * Invoked when the header cell directive's checkbox has changed. - * @param {scope} - */ - onCheckboxChanged(){ - this.onCheckboxChange(); + * Recursively calculate row depth for unsorted backend data + * @param row + * @param depth + * @return {Integer} + */ + calculateDepth(row, depth=0){ + var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; + var prop = this.treeColumn.prop; + if (!row[parentProp]){ + return depth; + } + if (row.$$depth) { + return row.$$depth + depth; + } + /* Get data from cache, if exists*/ + var cachedParent = this.index[row[parentProp]]; + if (cachedParent) { + depth += 1; + return this.calculateDepth(cachedParent, depth); + } + for (var i=0, len = this.rows.length; i < len; i++){ + var parent = this.rows[i]; + if (parent[prop] == row[parentProp]){ + depth+=1; + return this.calculateDepth(parent, depth); + } + } + return depth; } /** - * Occurs when a header cell directive triggered a resize - * @param {object} scope - * @param {object} column - * @param {int} width + * Matches groups to their respective parents by index. + * + * Example: + * + * { + * "Acme" : [ + * { name: "Acme Holdings", parent: "Acme" } + * ], + * "Acme Holdings": [ + * { name: "Acme Ltd", parent: "Acme Holdings" } + * ] + * } + * */ - onResized(column, width){ - this.onResize({ - column: column, - width: width - }); - } - -} + buildRowsByGroup(){ + this.index = {}; + this.rowsByGroup = {}; -function HeaderDirective($timeout){ - return { - restrict: 'E', - controller: HeaderController, - controllerAs: 'header', - scope: true, - bindToController: { - options: '=', - columns: '=', - columnWidths: '=', - onSort: '&', - onResize: '&', - onCheckboxChange: '&' - }, - template: ` -
+ var parentProp = this.treeColumn ? + this.treeColumn.relationProp : + this.groupColumn.prop; -
-
- - -
-
- - -
-
- - -
-
-
`, - replace:true, - link: function($scope, $elm, $attrs, ctrl){ + for(var i = 0, len = this.rows.length; i < len; i++) { + var row = this.rows[i]; + // build groups + var relVal = row[parentProp]; + if(relVal){ + if(this.rowsByGroup[relVal]){ + this.rowsByGroup[relVal].push(row); + } else { + this.rowsByGroup[relVal] = [ row ]; + } + } - $scope.columnsResorted = function(event, columnId){ - var col = findColumnById(columnId), - parent = angular.element(event.currentTarget), - newIdx = -1; + // build indexes + if(this.treeColumn){ + var prop = this.treeColumn.prop; + this.index[row[prop]] = row; - angular.forEach(parent.children(), (c, i) => { - if (columnId === angular.element(c).attr('data-id')) { - newIdx = i; + if (row[parentProp] === undefined){ + row.$$depth = 0; + } else { + var parent = this.index[row[parentProp]]; + if (parent === undefined){ + for (var j=0; j < len; j++){ + if (this.rows[j][prop] == relVal){ + parent = this.rows[j]; + break; + } + } } - }); + if (parent.$$depth === undefined) { + parent.$$depth = this.calculateDepth(parent); + } + row.$$depth = parent.$$depth + 1; + if (parent.$$children){ + parent.$$children.push(row[prop]); + } else { + parent.$$children = [row[prop]]; + } + } + } + } + } - $timeout(() => { - angular.forEach(ctrl.columns, (group) => { - var idx = group.indexOf(col); - if(idx > -1){ + /** + * Rebuilds the groups based on what is expanded. + * This function needs some optimization, todo for future release. + * @return {Array} the temp array containing expanded rows + */ + buildGroups(){ + var temp = []; - // this is tricky because we want to update the index - // in the orig columns array instead of the grouped one - var curColAtIdx = group[newIdx], - siblingIdx = ctrl.options.columns.indexOf(curColAtIdx), - curIdx = ctrl.options.columns.indexOf(col); + angular$1.forEach(this.rowsByGroup, (v, k) => { + temp.push({ + name: k, + group: true + }); - ctrl.options.columns.splice(curIdx, 1); - ctrl.options.columns.splice(siblingIdx, 0, col); + if(this.expanded[k]){ + temp.push(...v); + } + }); - return false; - } - }); + return temp; + } - }); - } + /** + * Returns if the row is selected + * @param {row} + * @return {Boolean} + */ + isSelected(row){ + var selected = false; - var findColumnById = function(columnId){ - var columns = ctrl.columns.left.concat(ctrl.columns.center).concat(ctrl.columns.right) - return columns.find(function(c){ - return c.$id === columnId; - }) + if(this.options.selectable){ + if(this.options.multiSelect){ + selected = this.selected.indexOf(row) > -1; + } else { + selected = this.selected === row; } } - }; -} + return selected; + } -/** - * Sortable Directive - * http://jsfiddle.net/RubaXa/zLq5J/3/ - * https://jsfiddle.net/hrohxze0/6/ - * @param {function} - */ -function SortableDirective($timeout) { - return { - restrict: 'A', - scope: { - isSortable: '=sortable', - onSortableSort: '&' - }, - link: function($scope, $element, $attrs){ - var rootEl = $element[0], dragEl, nextEl, dropEl; + /** + * Creates a tree of the existing expanded values + * @return {array} the built tree + */ + buildTree(){ + var temp = [], + self = this; - function isbefore(a, b) { - if (a.parentNode == b.parentNode) { - for (var cur = a; cur; cur = cur.previousSibling) { - if (cur === b) { - return true; - } - } - } - return false; - }; + function addChildren(fromArray, toArray, level) { + fromArray.forEach(function (row) { + var relVal = row[self.treeColumn.relationProp], + key = row[self.treeColumn.prop], + groupRows = self.rowsByGroup[key], + expanded = self.expanded[key]; - function onDragEnter(e) { - var target = e.target; - if (isbefore(dragEl, target)) { - target.parentNode.insertBefore(dragEl, target); - } else if(target.nextSibling && target.hasAttribute('draggable')) { - target.parentNode.insertBefore(dragEl, target.nextSibling.nextSibling); + if (level > 0 || !relVal) { + toArray.push(row); + if (groupRows && groupRows.length > 0 && expanded) { + addChildren(groupRows, toArray, level + 1); + } } - }; - - function onDragEnd(evt) { - evt.preventDefault(); - - dragEl.classList.remove('dt-clone'); - - $element.off('dragend', onDragEnd); - $element.off('dragenter', onDragEnter); - if (nextEl !== dragEl.nextSibling) { - $scope.onSortableSort({ - event: evt, - columnId: angular.element(dragEl).attr('data-id') - }); - } - }; + }); + } - function onDragStart(evt){ - if(!$scope.isSortable) return false; - evt = evt.originalEvent || evt; + addChildren(this.rows, temp, 0); - dragEl = evt.target; - nextEl = dragEl.nextSibling; - dragEl.classList.add('dt-clone'); + return temp; + } - evt.dataTransfer.effectAllowed = 'move'; - evt.dataTransfer.setData('Text', dragEl.textContent); + /** + * Creates the intermediate collection that is shown in the view. + * @param {boolean} refresh - bust the tree/group cache + */ + getRows(refresh){ + // only proceed when we have pre-aggregated the values + if((this.treeColumn || this.groupColumn) && !this.rowsByGroup){ + return false; + } - $element.on('dragenter', onDragEnter); - $element.on('dragend', onDragEnd); - }; + var temp; - $element.on('dragstart', onDragStart); + if(this.treeColumn) { + temp = this.treeTemp || []; + // cache the tree build + if((refresh || !this.treeTemp)){ + this.treeTemp = temp = this.buildTree(); + this.count = temp.length; - $scope.$on('$destroy', () => { - $element.off('dragstart', onDragStart); - }); + // have to force reset, optimize this later + this.tempRows.splice(0, this.tempRows.length); + } + } else if(this.groupColumn) { + temp = this.groupsTemp || []; + // cache the group build + if((refresh || !this.groupsTemp)){ + this.groupsTemp = temp = this.buildGroups(); + this.count = temp.length; + } + } else { + temp = this.rows; + if(refresh === true){ + this.tempRows.splice(0, this.tempRows.length); + } } - } -} + var idx = 0, + indexes = this.getFirstLastIndexes(), + rowIndex = indexes.first; -/** - * Resizable directive - * http://stackoverflow.com/questions/18368485/angular-js-resizable-div-directive - * @param {object} - * @param {function} - * @param {function} - */ -function ResizableDirective($document, $timeout){ - return { - restrict: 'A', - scope:{ - isResizable: '=resizable', - minWidth: '=', - maxWidth: '=', - onResize: '&' - }, - link: function($scope, $element, $attrs){ - if($scope.isResizable){ - $element.addClass('resizable'); + // slice out the old rows so we don't have duplicates + this.tempRows.splice(0, indexes.last - indexes.first); + + while (rowIndex < indexes.last && rowIndex < this.count) { + var row = temp[rowIndex]; + if(row){ + row.$$index = rowIndex; + this.tempRows[idx] = row; } + idx++; + rowIndex++; + } - var handle = angular.element(``), - parent = $element.parent(), - prevScreenX; + this.options.internal.styleTranslator.update(this.tempRows); - handle.on('mousedown', function(event) { - if(!$element[0].classList.contains('resizable')) { - return false; - } + return this.tempRows; + } - event.stopPropagation(); - event.preventDefault(); + /** + * Returns the styles for the table body directive. + * @return {object} + */ + styles(){ + var styles = { + width: this.options.internal.innerWidth + 'px' + }; - $document.on('mousemove', mousemove); - $document.on('mouseup', mouseup); - }); + if(!this.options.scrollbarV){ + styles.overflowY = 'hidden'; + } else if(this.options.scrollbarH === false){ + styles.overflowX = 'hidden'; + } - function mousemove(event) { - event = event.originalEvent || event; + if(this.options.scrollbarV){ + styles.height = this.options.internal.bodyHeight + 'px'; + } - var width = parent[0].clientWidth, - movementX = event.movementX || event.mozMovementX || (event.screenX - prevScreenX), - newWidth = width + (movementX || 0); + return styles; + } - prevScreenX = event.screenX; + /** + * Returns the styles for the row diretive. + * @param {row} + * @return {styles object} + */ + rowStyles(row){ + let styles = {}; - if((!$scope.minWidth || newWidth >= $scope.minWidth) && (!$scope.maxWidth || newWidth <= $scope.maxWidth)){ - parent.css({ - width: newWidth + 'px' - }); - } - } + if(this.options.rowHeight === 'auto'){ + styles.height = this.options.rowHeight + 'px'; + } - function mouseup() { - if ($scope.onResize) { - $timeout(function () { - let width = parent[0].clientWidth; - if (width < $scope.minWidth){ - width = $scope.minWidth; - } - $scope.onResize({ width: width }); - }); - } + return styles; + } - $document.unbind('mousemove', mousemove); - $document.unbind('mouseup', mouseup); - } + /** + * Builds the styles for the row group directive + * @param {object} row + * @return {object} styles + */ + groupRowStyles(row){ + var styles = this.rowStyles(row); + styles.width = this.columnWidths.total + 'px'; + return styles; + } - $element.append(handle); + /** + * Returns the css classes for the row directive. + * @param {row} + * @return {css class object} + */ + rowClasses(row){ + var styles = { + 'selected': this.isSelected(row), + 'dt-row-even': row && row.$$index%2 === 0, + 'dt-row-odd': row && row.$$index%2 !== 0 + }; + + if(this.treeColumn){ + // if i am a child + styles['dt-leaf'] = this.rowsByGroup[row[this.treeColumn.relationProp]]; + // if i have children + styles['dt-has-leafs'] = this.rowsByGroup[row[this.treeColumn.prop]]; + // the depth + styles['dt-depth-' + row.$$depth] = true; } - }; -} + return styles; + } -/** - * Throttle helper - * @param {function} - * @param {boolean} - * @param {object} - */ -function throttle(func, wait, options) { - var context, args, result; - var timeout = null; - var previous = 0; - options || (options = {}); - var later = function() { - previous = options.leading === false ? 0 : new Date(); - timeout = null; - result = func.apply(context, args); - }; - return function() { - var now = new Date(); - if (!previous && options.leading === false) - previous = now; - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); + /** + * Returns the row model for the index in the view. + * @param {index} + * @return {row model} + */ + getRowValue(idx){ + return this.tempRows[idx]; + } + + /** + * Calculates if a row is expanded or collasped for tree grids. + * @param {row} + * @return {boolean} + */ + getRowExpanded(row){ + if(this.treeColumn) { + return this.expanded[row[this.treeColumn.prop]]; + } else if(this.groupColumn){ + return this.expanded[row.name]; } - return result; - }; -} + } + /** + * Calculates if the row has children + * @param {row} + * @return {boolean} + */ + getRowHasChildren(row){ + if(!this.treeColumn) return; + var children = this.rowsByGroup[row[this.treeColumn.prop]]; + return children !== undefined || (children && !children.length); + } -/** - * Gets the width of the scrollbar. Nesc for windows - * http://stackoverflow.com/a/13382873/888165 - * @return {int} width - */ -function ScrollbarWidth() { - var outer = document.createElement("div"); - outer.style.visibility = "hidden"; - outer.style.width = "100px"; - outer.style.msOverflowStyle = "scrollbar"; - document.body.appendChild(outer); + /** + * Tree toggle event from a cell + * @param {row model} + * @param {cell model} + */ + onTreeToggled(row, cell){ + var val = row[this.treeColumn.prop]; + this.expanded[val] = !this.expanded[val]; - var widthNoScroll = outer.offsetWidth; - outer.style.overflow = "scroll"; + if(this.options.scrollbarV){ + this.getRows(true); + } else { + var values = this.buildTree(); + this.tempRows.splice(0, this.tempRows.length); + this.tempRows.push(...values); + } - var inner = document.createElement("div"); - inner.style.width = "100%"; - outer.appendChild(inner); + this.onTreeToggle({ + row: row, + cell: cell + }); + } - var widthWithScroll = inner.offsetWidth; - outer.parentNode.removeChild(outer); + /** + * Invoked when the row group directive was expanded + * @param {object} row + */ + onGroupToggle(row){ + this.expanded[row.name] = !this.expanded[row.name]; - return widthNoScroll - widthWithScroll; + if(this.options.scrollbarV){ + this.getRows(true); + } else { + var values = this.buildGroups(); + this.tempRows.splice(0, this.tempRows.length); + this.tempRows.push(...values); + } + } +} + +function BodyDirective($timeout){ + return { + restrict: 'E', + controller: BodyController, + controllerAs: 'body', + bindToController: { + columns: '=', + columnWidths: '=', + rows: '=', + options: '=', + selected: '=?', + expanded: '=?', + onPage: '&', + onTreeToggle: '&', + onSelect: '&', + onRowClick: '&', + onRowDblClick: '&' + }, + scope: true, + template: ` +
+
+
+
+
+
+ + + + + + +
+
+
+
+
` + }; } -let DataTableService = { +/** + * This translates the dom position based on the model row index. + * This only exists because Angular's binding process is too slow. + */ +class StyleTranslator{ - // id: [ column defs ] - columns: {}, - dTables: {}, + constructor(height){ + this.height = height; + this.map = new Map(); + } - saveColumns(id, columnElms) { - if (columnElms && columnElms.length) { - let columnsArray = [].slice.call(columnElms); - this.dTables[id] = columnsArray; + /** + * Update the rows + * @param {Array} rows + */ + update(rows){ + let n = 0; + while (n <= this.map.size) { + let dom = this.map.get(n); + let model = rows[n]; + if(dom && model){ + TranslateXY(dom[0].style, 0, model.$$index * this.height); + } + n++; } - }, + } /** - * Create columns from elements - * @param {array} columnElms + * Register the row + * @param {int} idx + * @param {dom} dom */ - buildColumns(scope, parse) { - //FIXME: Too many nested for loops. O(n3) + register(idx, dom){ + this.map.set(idx, dom); + } - // Iterate through each dTable - angular.forEach(this.dTables, (columnElms, id) => { - this.columns[id] = []; +} - // Iterate through each column - angular.forEach(columnElms, (c) => { - let column = {}; +function ScrollerDirective($timeout, $rootScope){ + return { + restrict: 'E', + require:'^dtBody', + transclude: true, + replace: true, + template: `
`, + link: function($scope, $elm, $attrs, ctrl){ + var ticking = false, + lastScrollY = 0, + lastScrollX = 0, + parent = $elm.parent(); - var visible = true; - // Iterate through each attribute - angular.forEach(c.attributes, (attr) => { - let attrName = CamelCase(attr.name); + ctrl.options.internal.styleTranslator = + new StyleTranslator(ctrl.options.rowHeight); - // cuz putting className vs class on - // a element feels weird - switch (attrName) { - case 'class': - column.className = attr.value; - break; - case 'name': - case 'prop': - column[attrName] = attr.value; - break; - case 'headerRenderer': - case 'cellRenderer': - case 'cellDataGetter': - column[attrName] = parse(attr.value); - break; - case 'visible': - visible = parse(attr.value)(scope); - break; - default: - column[attrName] = parse(attr.value)(scope); - break; - } - }); + ctrl.options.internal.setYOffset = function(offsetY){ + parent[0].scrollTop = offsetY; + }; - let header = c.getElementsByTagName('column-header'); - if(header.length){ - column.headerTemplate = header[0].innerHTML; - c.removeChild(header[0]) - } + function update(){ + ctrl.options.internal.offsetY = lastScrollY; + ctrl.options.internal.offsetX = lastScrollX; + ctrl.updatePage(); - if (c.innerHTML !== '') { - column.template = c.innerHTML; + if(ctrl.options.scrollbarV){ + ctrl.getRows(); } - if (visible) - this.columns[id].push(column); - }); - }); - - this.dTables = {}; - } -}; - - -/** - * Creates a unique object id. - */ -function ObjectId() { - var timestamp = (new Date().getTime() / 1000 | 0).toString(16); - return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { - return (Math.random() * 16 | 0).toString(16); - }).toLowerCase(); -} - + // https://github.com/Swimlane/angular-data-table/pull/74 + ctrl.options.$outer.$digest(); -/** - * Resizes columns based on the flexGrow property, while respecting manually set widths - * @param {array} colsByGroup - * @param {int} maxWidth - * @param {int} totalFlexGrow - */ -function ScaleColumns(colsByGroup, maxWidth, totalFlexGrow) { - // calculate total width and flexgrow points for coulumns that can be resized - angular.forEach(colsByGroup, (cols) => { - cols.forEach((column) => { - if (!column.canAutoResize){ - maxWidth -= column.width; - totalFlexGrow -= column.flexGrow; - } else { - column.width = 0; + ticking = false; } - }); - }); - var hasMinWidth = {} - var remainingWidth = maxWidth; - - // resize columns until no width is left to be distributed - do { - let widthPerFlexPoint = remainingWidth / totalFlexGrow; - remainingWidth = 0; - angular.forEach(colsByGroup, (cols) => { - cols.forEach((column, i) => { - // if the column can be resize and it hasn't reached its minimum width yet - if (column.canAutoResize && !hasMinWidth[i]){ - let newWidth = column.width + column.flexGrow * widthPerFlexPoint; - if (column.minWidth !== undefined && newWidth < column.minWidth){ - remainingWidth += newWidth - column.minWidth; - column.width = column.minWidth; - hasMinWidth[i] = true; - } else { - column.width = newWidth; - } + function requestTick() { + if(!ticking) { + requestAnimFrame(update); + ticking = true; } - }); - }); - } while (remainingWidth !== 0); + } -} + parent.on('scroll', function(ev) { + lastScrollY = this.scrollTop; + lastScrollX = this.scrollLeft; + requestTick(); + }); + $scope.$on('$destroy', () => { + parent.off('scroll'); + }); -/** - * Returns the columns by pin. - * @param {array} colsumns - */ -function ColumnsByPin(cols){ - var ret = { - left: [], - center: [], - right: [] - }; + $scope.scrollerStyles = function(){ + if(ctrl.options.scrollbarV){ + return { + height: ctrl.count * ctrl.options.rowHeight + 'px' + } + } + }; - for(var i=0, len=cols.length; i < len; i++) { - var c = cols[i]; - if(c.frozenLeft){ - ret.left.push(c) - } else if(c.frozenRight){ - ret.right.push(c); - } else { - ret.center.push(c); } - } - - return ret; + }; } - /** - * Calculates the Total Flex Grow - * @param {array} + * Shortcut for key handlers + * @type {Object} */ -function GetTotalFlexGrow(columns){ - var totalFlexGrow = 0; - - for (let c of columns) { - totalFlexGrow += c.flexGrow || 0; - } - - return totalFlexGrow; -} +var KEYS = { + BACKSPACE: 8, + TAB: 9, + RETURN: 13, + ALT: 18, + ESC: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46, + COMMA: 188, + PERIOD: 190, + A: 65, + Z: 90, + ZERO: 48, + NUMPAD_0: 96, + NUMPAD_9: 105 +}; +class SelectionController { -/** - * Calculates the total width of all columns and their groups - * @param {array} columns - * @param {string} property width to get - */ -function ColumnTotalWidth(columns, prop) { - var totalWidth = 0; + /*@ngInject*/ + constructor($scope){ + this.body = $scope.body; + this.options = $scope.body.options; + this.selected = $scope.body.selected; + } - columns.forEach((c) => { - var has = prop && c[prop]; - totalWidth = totalWidth + (has ? c[prop] : c.width); - }); + /** + * Handler for the keydown on a row + * @param {event} + * @param {index} + * @param {row} + */ + keyDown(ev, index, row){ + if(KEYS[ev.keyCode]){ + ev.preventDefault(); + } - return totalWidth; -} + if (ev.keyCode === KEYS.DOWN) { + var next = ev.target.nextElementSibling; + if(next){ + next.focus(); + } + } else if (ev.keyCode === KEYS.UP) { + var prev = ev.target.previousElementSibling; + if(prev){ + prev.focus(); + } + } else if(ev.keyCode === KEYS.RETURN){ + this.selectRow(index, row); + } + } + /** + * Handler for the row click event + * @param {object} event + * @param {int} index + * @param {object} row + */ + rowClicked(event, index, row){ + if(!this.options.checkboxSelection){ + // event.preventDefault(); + this.selectRow(event, index, row); + } -/** - * Adjusts the column widths. - * Inspired by: https://github.com/facebook/fixed-data-table/blob/master/src/FixedDataTableWidthHelper.js - * @param {array} all columns - * @param {int} width - */ -function AdjustColumnWidths(allColumns, expectedWidth){ - var columnsWidth = ColumnTotalWidth(allColumns), - totalFlexGrow = GetTotalFlexGrow(allColumns), - colsByGroup = ColumnsByPin(allColumns); + this.body.onRowClick({ row: row }); + } + + /** + * Handler for the row double click event + * @param {object} event + * @param {int} index + * @param {object} row + */ + rowDblClicked(event, index, row){ + if(!this.options.checkboxSelection){ + event.preventDefault(); + this.selectRow(event, index, row); + } - if (columnsWidth !== expectedWidth){ - ScaleColumns(colsByGroup, expectedWidth, totalFlexGrow); + this.body.onRowDblClick({ row: row }); } -} + /** + * Invoked when a row directive's checkbox was changed. + * @param {index} + * @param {row} + */ + onCheckboxChange(event, index, row){ + this.selectRow(event, index, row); + } -/** - * Forces the width of the columns to - * distribute equally but overflowing when nesc. - * - * Rules: - * - * - If combined withs are less than the total width of the grid, - * proporation the widths given the min / max / noraml widths to fill the width. - * - * - If the combined widths, exceed the total width of the grid, - * use the standard widths. - * - * - If a column is resized, it should always use that width - * - * - The proporational widths should never fall below min size if specified. - * - * - If the grid starts off small but then becomes greater than the size ( + / - ) - * the width should use the orginial width; not the newly proporatied widths. - * - * @param {array} allColumns - * @param {int} expectedWidth - */ -function ForceFillColumnWidths(allColumns, expectedWidth, startIdx){ - var contentWidth = 0, - columnsToResize = startIdx > -1 ? - allColumns.slice(startIdx, allColumns.length).filter((c) => { return c.canAutoResize }) : - allColumns.filter((c) => { return c.canAutoResize }); + /** + * Selects a row and places in the selection collection + * @param {index} + * @param {row} + */ + selectRow(event, index, row){ + if(this.options.selectable){ + if(this.options.multiSelect){ + var isCtrlKeyDown = event.ctrlKey || event.metaKey, + isShiftKeyDown = event.shiftKey; - allColumns.forEach((c) => { - if(!c.canAutoResize){ - contentWidth += c.width; - } else { - contentWidth += (c.$$oldWidth || c.width); + if(isShiftKeyDown){ + this.selectRowsBetween(index, row); + } else { + var idx = this.selected.indexOf(row); + if(idx > -1){ + this.selected.splice(idx, 1); + } else { + if(this.options.multiSelectOnShift && this.selected.length === 1) { + this.selected.splice(0, 1); + } + this.selected.push(row); + this.body.onSelect({ rows: [ row ] }); + } + } + this.prevIndex = index; + } else { + this.selected = row; + this.body.onSelect({ rows: [ row ] }); + } } - }); + } - var remainingWidth = expectedWidth - contentWidth, - additionWidthPerColumn = remainingWidth / columnsToResize.length, - exceedsWindow = contentWidth > expectedWidth; + /** + * Selects the rows between a index. Used for shift click selection. + * @param {index} + */ + selectRowsBetween(index){ + var reverse = index < this.prevIndex, + selecteds = []; - columnsToResize.forEach((column) => { - if(exceedsWindow){ - column.width = column.$$oldWidth || column.width; - } else { - if(!column.$$oldWidth){ - column.$$oldWidth = column.width; - } + for(var i=0, len=this.body.rows.length; i < len; i++) { + var row = this.body.rows[i], + greater = i >= this.prevIndex && i <= index, + lesser = i <= this.prevIndex && i >= index; - var newSize = column.$$oldWidth + additionWidthPerColumn; - if(column.minWith && newSize < column.minWidth){ - column.width = column.minWidth; - } else if(column.maxWidth && newSize > column.maxWidth){ - column.width = column.maxWidth; + var range = {}; + if ( reverse ) { + range = { + start: index, + end: ( this.prevIndex - index ) + }; } else { - column.width = newSize; + range = { + start: this.prevIndex, + end: index + 1 + }; + } + + if((reverse && lesser) || (!reverse && greater)){ + var idx = this.selected.indexOf(row); + // if reverse shift selection (unselect) and the + // row is already selected, remove it from selected + if ( reverse && idx > -1 ) { + this.selected.splice(idx, 1); + continue; + } + // if in the positive range to be added to `selected`, and + // not already in the selected array, add it + if( i >= range.start && i < range.end ){ + if ( idx === -1 ) { + this.selected.push(row); + selecteds.push(row); + } + } } } - }); -} + this.body.onSelect({ rows: selecteds }); + } +} -/** - * Returns the widths of all group sets of a column - * @param {object} groups - * @param {array} all - */ -function ColumnGroupWidths(groups, all){ +function SelectionDirective(){ return { - left: ColumnTotalWidth(groups.left), - center: ColumnTotalWidth(groups.center), - right: ColumnTotalWidth(groups.right), - total: ColumnTotalWidth(all) + controller: SelectionController, + restrict: 'A', + require:'^dtBody', + controllerAs: 'selCtrl' }; } +class RowController { -/** - * Default Column Options - * @type {object} - */ -const ColumnDefaults = { - - // pinned to the left - frozenLeft: false, - - // pinned to the right - frozenRight: false, - - // body cell css class name - className: undefined, - - // header cell css class name - headerClassName: undefined, - - // The grow factor relative to other columns. Same as the flex-grow - // API from http://www.w3.org/TR/css3-flexbox/. Basically, - // take any available extra width and distribute it proportionally - // according to all columns' flexGrow values. - flexGrow: 0, - - // Minimum width of the column. - minWidth: 100, - - //Maximum width of the column. - maxWidth: undefined, + /** + * Returns the value for a given column + * @param {col} + * @return {value} + */ + getValue(col){ + if(!col.prop) return ''; + return DeepValueGetter(this.row, col.prop); + } - // The width of the column, by default (in pixels). - width: 150, + /** + * Invoked when a cell triggers the tree toggle + * @param {cell} + */ + onTreeToggled(cell){ + this.onTreeToggle({ + cell: cell, + row: this.row + }); + } - // If yes then the column can be resized, otherwise it cannot. - resizable: true, + /** + * Calculates the styles for a pin group + * @param {group} + * @return {styles object} + */ + stylesByGroup( group){ + var styles = { + width: this.columnWidths[group] + 'px' + }; - // Custom sort comparator - // pass false if you want to server sort - comparator: undefined, + if(group === 'left'){ + TranslateXY(styles, this.options.internal.offsetX, 0); + } else if(group === 'right'){ + var offset = (((this.columnWidths.total - this.options.internal.innerWidth) - + this.options.internal.offsetX) + this.options.internal.scrollBarWidth) * -1; + TranslateXY(styles, offset, 0); + } - // If yes then the column can be sorted. - sortable: true, + return styles; + } - // Default sort asecending/descending for the column - sort: undefined, + /** + * Invoked when the cell directive's checkbox changed state + */ + onCheckboxChanged(ev){ + this.onCheckboxChange({ + $event: ev, + row: this.row + }); + } - // If you want to sort a column by a special property - // See an example in demos/sort.html - sortBy: undefined, +} - // The cell renderer that returns content for table column header - headerRenderer: undefined, +function RowDirective(){ + return { + restrict: 'E', + controller: RowController, + controllerAs: 'rowCtrl', + scope: true, + bindToController: { + row: '=', + columns: '=', + columnWidths: '=', + expanded: '=', + selected: '=', + hasChildren: '=', + options: '=', + onCheckboxChange: '&', + onTreeToggle: '&' + }, + link: function($scope, $elm, $attrs, ctrl){ + if(ctrl.row){ + // inital render position + TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); + } - // The cell renderer function(scope, elm) that returns React-renderable content for table cell. - cellRenderer: undefined, + // register w/ the style translator + ctrl.options.internal.styleTranslator.register($scope.$index, $elm); + }, + template: ` +
+
+ + +
+
+ + +
+
+ + +
+
`, + replace:true + }; +} - // The getter function(value) that returns the cell data for the cellRenderer. - // If not provided, the cell data will be collected from row data instead. - cellDataGetter: undefined, +class GroupRowController { - // Adds +/- button and makes a secondary call to load nested data - isTreeColumn: false, + onGroupToggled(evt){ + evt.stopPropagation(); + this.onGroupToggle({ + group: this.row + }); + } - // Adds the checkbox selection to the column - isCheckboxColumn: false, + treeClass(){ + return { + 'dt-tree-toggle': true, + 'icon-right': !this.expanded, + 'icon-down': this.expanded + }; + } - // Toggles the checkbox column in the header - // for selecting all values given to the grid - headerCheckbox: false, +} - // Whether the column can automatically resize to fill space in the table. - canAutoResize: true +function GroupRowDirective(){ + return { + restrict: 'E', + controller: GroupRowController, + controllerAs: 'group', + bindToController: { + row: '=', + onGroupToggle: '&', + expanded: '=', + options: '=' + }, + scope: true, + replace:true, + template: ` +
+ + + + +
`, + link: function($scope, $elm, $attrs, ctrl){ + // inital render position + TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); -}; + // register w/ the style translator + ctrl.options.internal.styleTranslator.register($scope.$index, $elm); + } + }; +} +class CellController { -/** - * Default Table Options - * @type {object} - */ -const TableDefaults = { + /** + * Calculates the styles for the Cell Directive + * @return {styles object} + */ + styles(){ + return { + width: this.column.width + 'px', + 'min-width': this.column.width + 'px' + }; + } - // Enable vertical scrollbars - scrollbarV: true, + /** + * Calculates the css classes for the cell directive + * @param {column} + * @return {class object} + */ + cellClass(){ + var style = { + 'dt-tree-col': this.column.isTreeColumn + }; - // Enable horz scrollbars - // scrollbarH: true, + if(this.column.className){ + style[this.column.className] = true; + } - // The row height, which is necessary - // to calculate the height for the lazy rendering. - rowHeight: 30, + return style; + } - // flex - // force - // standard - columnMode: 'standard', + /** + * Calculates the tree class styles. + * @return {css classes object} + */ + treeClass(){ + return { + 'dt-tree-toggle': true, + 'icon-right': !this.expanded, + 'icon-down': this.expanded + } + } - // Loading message presented when the array is undefined - loadingMessage: 'Loading...', + /** + * Invoked when the tree toggle button was clicked. + * @param {event} + */ + onTreeToggled(evt){ + evt.stopPropagation(); + this.expanded = !this.expanded; + this.onTreeToggle({ + cell: { + value: this.value, + column: this.column, + expanded: this.expanded + } + }); + } - // Message to show when array is presented - // but contains no values - emptyMessage: 'No data to display', + /** + * Invoked when the checkbox was changed + * @param {object} event + */ + onCheckboxChanged(event){ + event.stopPropagation(); + this.onCheckboxChange({ $event: event }); + } - // The minimum header height in pixels. - // pass falsey for no header - headerHeight: 30, + /** + * Returns the value in its fomatted form + * @return {string} value + */ + getValue(){ + var val = this.column.cellDataGetter ? + this.column.cellDataGetter(this.value) : this.value; - // The minimum footer height in pixels. - // pass falsey for no footer - footerHeight: 0, + if(val === undefined || val === null) val = ''; + return val; + } - paging: { - // if external paging is turned on - externalPaging: false, +} - // Page size - size: undefined, +function CellDirective($rootScope, $compile, $log, $timeout){ + return { + restrict: 'E', + controller: CellController, + scope: true, + controllerAs: 'cell', + bindToController: { + options: '=', + value: '=', + selected: '=', + column: '=', + row: '=', + expanded: '=', + hasChildren: '=', + onTreeToggle: '&', + onCheckboxChange: '&' + }, + template: + `
+ + + +
`, + replace: true, + compile: function() { + return { + pre: function($scope, $elm, $attrs, ctrl) { + var content = angular$1.element($elm[0].querySelector('.dt-cell-content')), cellScope; - // Total count - count: 0, + // extend the outer scope onto our new cell scope + if(ctrl.column.template || ctrl.column.cellRenderer){ + createCellScope(); + } - // Page offset - offset: 0, + $scope.$watch('cell.row', () => { + if(cellScope){ + cellScope.$destroy(); - // Loading indicator - loadingIndicator: false - }, + createCellScope(); - // if users can select itmes - selectable: false, + cellScope.$cell = ctrl.value; + cellScope.$row = ctrl.row; + cellScope.$column = ctrl.column; + cellScope.$$watchers = null; + } - // if users can select mutliple items - multiSelect: false, + if(ctrl.column.template){ + content.empty(); + var elm = angular$1.element(`${ctrl.column.template.trim()}`); + content.append($compile(elm)(cellScope)); + } else if(ctrl.column.cellRenderer){ + content.empty(); + var elm = angular$1.element(ctrl.column.cellRenderer(cellScope, content)); + content.append($compile(elm)(cellScope)); + } else { + content[0].innerHTML = ctrl.getValue(); + } + + }, true); - // checkbox selection vs row click - checkboxSelection: false, + function createCellScope(){ + cellScope = ctrl.options.$outer.$new(false); + cellScope.getValue = ctrl.getValue; + } + } + } + } + }; +} - // if you can reorder columns - reorderable: true, +class FooterController { - internal: { - offsetX: 0, - offsetY: 0, - innerWidth: 0, - bodyHeight: 300 + /** + * Creates an instance of the Footer Controller + * @param {scope} + * @return {[type]} + */ + /*@ngInject*/ + constructor($scope){ + this.page = this.paging.offset + 1; + $scope.$watch('footer.paging.offset', (newVal) => { + this.offsetChanged(newVal); + }); } -}; - -class DataTableController { + /** + * The offset ( page ) changed externally, update the page + * @param {new offset} + */ + offsetChanged(newVal){ + this.page = newVal + 1; + } /** - * Creates an instance of the DataTable Controller + * The pager was invoked * @param {scope} - * @param {filter} */ - /*@ngInject*/ - constructor($scope, $filter, $log, $transclude){ - Object.assign(this, { - $scope: $scope, - $filter: $filter, - $log: $log + onPaged(page){ + this.paging.offset = page - 1; + this.onPage({ + offset: this.paging.offset, + size: this.paging.size }); + } - this.defaults(); +} - // set scope to the parent - this.options.$outer = $scope.$parent; +function FooterDirective(){ + return { + restrict: 'E', + controller: FooterController, + controllerAs: 'footer', + scope: true, + bindToController: { + paging: '=', + onPage: '&' + }, + template: + ``, + replace: true + }; +} - $scope.$watch('dt.options.columns', (newVal, oldVal) => { - this.transposeColumnDefaults(); +class PagerController { - if(newVal.length !== oldVal.length){ - this.adjustColumns(); - } + /** + * Creates an instance of the Pager Controller + * @param {object} $scope + */ + /*@ngInject*/ + constructor($scope){ + $scope.$watch('pager.count', (newVal) => { + this.calcTotalPages(this.size, this.count); + this.getPages(this.page || 1); + }); - this.calculateColumns(); - }, true); + $scope.$watch('pager.size', (newVal) => { + this.calcTotalPages(this.size, this.count); + this.getPages(this.page || 1); + }); - // default sort - var watch = $scope.$watch('dt.rows', (newVal) => { - if(newVal){ - watch(); - this.onSorted(); + $scope.$watch('pager.page', (newVal) => { + if (newVal !== 0 && newVal <= this.totalPages) { + this.getPages(newVal); } }); + + this.getPages(this.page || 1); + } + + /** + * Calculates the total number of pages given the count. + * @return {int} page count + */ + calcTotalPages(size, count) { + var count = size < 1 ? 1 : Math.ceil(count / size); + this.totalPages = Math.max(count || 0, 1); + } + + /** + * Select a page + * @param {int} num + */ + selectPage(num){ + if (num > 0 && num <= this.totalPages) { + this.page = num; + this.onPage({ + page: num + }); + } + } + + /** + * Selects the previous pager + */ + prevPage(){ + if (this.page > 1) { + this.selectPage(--this.page); + } } /** - * Creates and extends default options for the grid control + * Selects the next page */ - defaults(){ - this.expanded = this.expanded || {}; - - this.options = angular.extend(angular. - copy(TableDefaults), this.options); + nextPage(){ + this.selectPage(++this.page); + } - angular.forEach(TableDefaults.paging, (v,k) => { - if(!this.options.paging[k]){ - this.options.paging[k] = v; - } - }); + /** + * Determines if the pager can go previous + * @return {boolean} + */ + canPrevious(){ + return this.page > 1; + } - if(this.options.selectable && this.options.multiSelect){ - this.selected = this.selected || []; - } + /** + * Determines if the pager can go forward + * @return {boolean} + */ + canNext(){ + return this.page < this.totalPages; } /** - * On init or when a column is added, we need to - * make sure all the columns added have the correct - * defaults applied. + * Gets the page set given the current page + * @param {int} page */ - transposeColumnDefaults(){ - for(var i=0, len = this.options.columns.length; i < len; i++) { - var column = this.options.columns[i]; - column.$id = ObjectId(); + getPages(page) { + var pages = [], + startPage = 1, + endPage = this.totalPages, + maxSize = 5, + isMaxSized = maxSize < this.totalPages; - angular.forEach(ColumnDefaults, (v,k) => { - if(!column.hasOwnProperty(k)){ - column[k] = v; - } + if (isMaxSized) { + startPage = ((Math.ceil(page / maxSize) - 1) * maxSize) + 1; + endPage = Math.min(startPage + maxSize - 1, this.totalPages); + } + + for (var number = startPage; number <= endPage; number++) { + pages.push({ + number: number, + text: number, + active: number === page }); + } - if(column.name && !column.prop){ - column.prop = CamelCase(column.name); + /* + if (isMaxSized) { + if (startPage > 1) { + pages.unshift({ + number: startPage - 1, + text: '...' + }); } - this.options.columns[i] = column; + if (endPage < this.totalPages) { + pages.push({ + number: endPage + 1, + text: '...' + }); + } } - } + */ - /** - * Calculate column groups and widths - */ - calculateColumns(){ - var columns = this.options.columns; - this.columnsByPin = ColumnsByPin(columns); - this.columnWidths = ColumnGroupWidths(this.columnsByPin, columns); + this.pages = pages; } - /** - * Returns the css classes for the data table. - * @return {style object} - */ - tableCss(){ - return { - 'fixed': this.options.scrollbarV, - 'selectable': this.options.selectable, - 'checkboxable': this.options.checkboxSelection - }; - } +} + +function PagerDirective(){ + return { + restrict: 'E', + controller: PagerController, + controllerAs: 'pager', + scope: true, + bindToController: { + page: '=', + size: '=', + count: '=', + onPage: '&' + }, + template: + `
+ +
`, + replace: true + }; +} + +/** + * Popover Directive + * @param {object} $q + * @param {function} $timeout + * @param {function} $templateCache + * @param {function} $compile + * @param {function} PopoverRegistry + * @param {function} $animate + */ +function PopoverDirective($q, $timeout, $templateCache, $compile, PopoverRegistry, PositionHelper, $animate){ /** - * Adjusts the column widths to handle greed/etc. - * @param {int} forceIdx + * Loads a template from the template cache + * @param {string} template + * @param {boolean} plain + * @return {object} html template */ - adjustColumns(forceIdx){ - var width = this.options.internal.innerWidth - this.options.internal.scrollBarWidth; + function loadTemplate(template, plain) { + if (!template) { + return ''; + } - if(this.options.columnMode === 'force'){ - ForceFillColumnWidths(this.options.columns, width, forceIdx); - } else if(this.options.columnMode === 'flex') { - AdjustColumnWidths(this.options.columns, width); + if (angular$1.isString(template) && plain) { + return template; } + + return $templateCache.get(template) || $http.get(template, { cache : true }); } /** - * Calculates the page size given the height * row height. - * @return {[type]} + * Determines a boolean given a value + * @param {object} value + * @return {boolean} */ - calculatePageSize(){ - this.options.paging.size = Math.ceil( - this.options.internal.bodyHeight / this.options.rowHeight) + 1; + function toBoolean(value) { + if (value && value.length !== 0) { + var v = ("" + value).toLowerCase(); + value = (v == 'true'); + } else { + value = false; + } + return value; } - /** - * Sorts the values of the grid for client side sorting. - */ - onSorted(){ - if(!this.rows) return; + return { + restrict: 'A', + scope: true, + replace: false, + link: function ($scope, $element, $attributes) { + $scope.popover = null; + $scope.popoverId = Date.now(); + + $scope.options = { + text: $attributes.popoverText, + template: $attributes.popoverTemplate, + plain: toBoolean($attributes.popoverPlain || false), + placement: $attributes.popoverPlacement || 'right', + alignment: $attributes.popoverAlignment || 'center', + group: $attributes.popoverGroup, + spacing: parseInt($attributes.popoverSpacing) || 0, + showCaret: toBoolean($attributes.popoverPlain || false) + }; - // return all sorted column, in the same order in which they were sorted - var sorts = this.options.columns - .filter((c) => { - return c.sort; - }) - .sort((a, b) => { - // sort the columns with lower sortPriority order first - if (a.sortPriority && b.sortPriority){ - if (a.sortPriority > b.sortPriority) return 1; - if (a.sortPriority < b.sortPriority) return -1; - } else if (a.sortPriority){ - return -1; - } else if (b.sortPriority){ - return 1; + // attach exit and enter events to element + $element.off('mouseenter', display); + $element.on('mouseenter', display); + $element.off('mouseleave', mouseOut); + $element.on('mouseleave', mouseOut); + + function mouseOut(){ + $scope.exitTimeout = $timeout(remove, 500); + } + + /** + * Displays the popover on the page + */ + function display(){ + // Cancel exit timeout + $timeout.cancel($scope.exitTimeout); + + var elm = document.getElementById(`#${$scope.popoverId}`); + if ($scope.popover && elm) return; + + // remove other popovers from the same group + if ($scope.options.group){ + PopoverRegistry.removeGroup($scope.options.group, $scope.popoverId); } - return 0; - }) - .map((c, i) => { - // update sortPriority - c.sortPriority = i + 1; - return c; - }); + if ($scope.options.text && !$scope.options.template){ + $scope.popover = angular$1.element(`
`); - if(sorts.length){ - this.onSort({sorts: sorts}); + $scope.popover.html($scope.options.text); + angular$1.element(document.body).append($scope.popover); + positionPopover($element, $scope.popover, $scope.options); + PopoverRegistry.add($scope.popoverId, {element: $element, popover: $scope.popover, group: $scope.options.group}); - if (this.options.onSort){ - this.options.onSort(sorts); + } else { + $q.when(loadTemplate($scope.options.template, $scope.options.plain)).then(function(template) { + if (!angular$1.isString(template)) { + if (template.data && angular$1.isString(template.data)){ + template = template.data; + } else { + template = ''; + } + } + + $scope.popover = angular$1.element(`
`); + + $scope.popover.html(template); + $compile($scope.popover)($scope); + angular$1.element(document.body).append($scope.popover); + positionPopover($element, $scope.popover, $scope.options); + + // attach exit and enter events to popover + $scope.popover.off('mouseleave', mouseOut); + $scope.popover.on('mouseleave', mouseOut); + $scope.popover.on('mouseenter', function(){ + $timeout.cancel($scope.exitTimeout); + }); + + PopoverRegistry.add($scope.popoverId, { + element: $element, + popover: $scope.popover, + group: $scope.options.group + }); + }); + } } - var clientSorts = []; - for(var i=0, len=sorts.length; i < len; i++) { - var c = sorts[i]; - if(c.comparator !== false){ - var dir = c.sort === 'asc' ? '' : '-'; - if (c.sortBy !== undefined) { - clientSorts.push(dir + c.sortBy); - } else { - clientSorts.push(dir + c.prop); - } + /** + * Removes the template from the registry and page + */ + function remove(){ + if ($scope.popover){ + $scope.popover.remove(); } + + $scope.popover = undefined; + PopoverRegistry.remove($scope.popoverId); } - if(clientSorts.length){ - // todo: more ideal to just resort vs splice and repush - // but wasn't responding to this change ... - var sortedValues = this.$filter('orderBy')(this.rows, clientSorts); - this.rows.splice(0, this.rows.length); - this.rows.push(...sortedValues); + /** + * Positions the popover + * @param {object} triggerElement + * @param {object} popover + * @param {object} options + */ + function positionPopover(triggerElement, popover, options){ + $timeout(function(){ + var elDimensions = triggerElement[0].getBoundingClientRect(), + popoverDimensions = popover[0].getBoundingClientRect(), + top, left; + + if (options.placement === 'right'){ + left = elDimensions.left + elDimensions.width + options.spacing; + top = PositionHelper.calculateVerticalAlignment(elDimensions, + popoverDimensions, options.alignment); + } + if (options.placement === 'left'){ + left = elDimensions.left - popoverDimensions.width - options.spacing; + top = PositionHelper.calculateVerticalAlignment(elDimensions, + popoverDimensions, options.alignment); + } + if (options.placement === 'top'){ + top = elDimensions.top - popoverDimensions.height - options.spacing; + left = PositionHelper.calculateHorizontalAlignment(elDimensions, + popoverDimensions, options.alignment); + } + if (options.placement === 'bottom'){ + top = elDimensions.top + elDimensions.height + options.spacing; + left = PositionHelper.calculateHorizontalAlignment(elDimensions, + popoverDimensions, options.alignment); + } + + popover.css({ + top: top + 'px', + left: left + 'px' + }); + + if($scope.options.showCaret){ + addCaret($scope.popover, elDimensions, popoverDimensions); + } + + $animate.addClass($scope.popover, 'popover-animation'); + }, 50); } - } - this.options.internal.setYOffset(0); + /** + * Adds a caret and positions it relatively to the popover + * @param {object} popoverEl + * @param {object} elDimensions + * @param {object} popoverDimensions + */ + function addCaret(popoverEl, elDimensions, popoverDimensions){ + var caret = angular$1.element(``); + popoverEl.append(caret); + var caretDimensions = caret[0].getBoundingClientRect(); + + var left, top; + if ($scope.options.placement === 'right'){ + left = -6; + top = PositionHelper.calculateVerticalCaret(elDimensions, + popoverDimensions, caretDimensions, $scope.options.alignment); + } + if ($scope.options.placement === 'left'){ + left = popoverDimensions.width - 2; + top = PositionHelper.calculateVerticalCaret(elDimensions, + popoverDimensions, caretDimensions, $scope.options.alignment); + } + if ($scope.options.placement === 'top'){ + top = popoverDimensions.height - 5; + left = PositionHelper.calculateHorizontalCaret(elDimensions, + popoverDimensions, caretDimensions, $scope.options.alignment); + } + + if ($scope.options.placement === 'bottom'){ + top = -8; + left = PositionHelper.calculateHorizontalCaret(elDimensions, + popoverDimensions, caretDimensions, $scope.options.alignment); + } + + caret.css({ + top: top + 'px', + left: left + 'px' + }); + } + + } } +} - /** - * Invoked when a tree is collasped/expanded - * @param {row model} - * @param {cell model} - */ - onTreeToggled(row, cell){ - this.onTreeToggle({ - row: row, - cell: cell +/** + * Registering to deal with popovers + * @param {function} $animate + */ +function PopoverRegistry($animate){ + var popovers = {}; + this.add = function(id, object){ + popovers[id] = object; + }; + this.find = function(id){ + popovers[id]; + }; + this.remove = function(id){ + delete popovers[id]; + }; + this.removeGroup = function(group, currentId){ + angular.forEach(popovers, function(popoverOb, id){ + if (id === currentId) return; + + if (popoverOb.group && popoverOb.group === group){ + $animate.removeClass(popoverOb.popover, 'sw-popover-animate').then(() => { + popoverOb.popover.remove(); + delete popovers[id]; + }); + } }); - } + }; +} - /** - * Invoked when the body triggers a page change. - * @param {offset} - * @param {size} - */ - onBodyPage(offset, size){ - this.onPage({ - offset: offset, - size: size - }); - } +/** + * Position helper for the popover directive. + */ +function PositionHelper(){ + return { - /** - * Invoked when the footer triggers a page change. - * @param {offset} - * @param {size} - */ - onFooterPage(offset, size){ - var pageBlockSize = this.options.rowHeight * size, - offsetY = pageBlockSize * offset; + calculateVerticalAlignment: function(elDimensions, popoverDimensions, alignment){ + if (alignment === 'top'){ + return elDimensions.top; + } + if (alignment === 'bottom'){ + return elDimensions.top + elDimensions.height - popoverDimensions.height; + } + if (alignment === 'center'){ + return elDimensions.top + elDimensions.height/2 - popoverDimensions.height/2; + } + }, - this.options.internal.setYOffset(offsetY); - } + calculateVerticalCaret: function(elDimensions, popoverDimensions, caretDimensions, alignment){ + if (alignment === 'top'){ + return elDimensions.height/2 - caretDimensions.height/2 - 1; + } + if (alignment === 'bottom'){ + return popoverDimensions.height - elDimensions.height/2 - caretDimensions.height/2 - 1; + } + if (alignment === 'center'){ + return popoverDimensions.height/2 - caretDimensions.height/2 - 1; + } + }, - /** - * Invoked when the header checkbox directive has changed. - */ - onHeaderCheckboxChange(){ - if(this.rows){ - var matches = this.selected.length === this.rows.length; - this.selected.splice(0, this.selected.length); + calculateHorizontalCaret: function(elDimensions, popoverDimensions, caretDimensions, alignment){ + if (alignment === 'left'){ + return elDimensions.width/2 - caretDimensions.height/2 - 1; + } + if (alignment === 'right'){ + return popoverDimensions.width - elDimensions.width/2 - caretDimensions.height/2 - 1; + } + if (alignment === 'center'){ + return popoverDimensions.width/2 - caretDimensions.height/2 - 1; + } + }, - if(!matches){ - this.selected.push(...this.rows); + calculateHorizontalAlignment: function(elDimensions, popoverDimensions, alignment){ + if (alignment === 'left'){ + return elDimensions.left; + } + if (alignment === 'right'){ + return elDimensions.left + elDimensions.width - popoverDimensions.width; + } + if (alignment === 'center'){ + return elDimensions.left + elDimensions.width/2 - popoverDimensions.width/2; } } - } - /** - * Returns if all the rows are selected - * @return {Boolean} if all selected - */ - isAllRowsSelected(){ - if(this.rows) return false; - return this.selected.length === this.rows.length; } +} - /** - * Occurs when a header directive triggered a resize event - * @param {object} column - * @param {int} width - */ - onResized(column, width){ - var idx =this.options.columns.indexOf(column); - if(idx > -1){ - var column = this.options.columns[idx]; - column.width = width; - column.canAutoResize = false; +var popover = angular$1 + .module('dt.popover', []) + .service('PopoverRegistry', PopoverRegistry) + .factory('PositionHelper', PositionHelper) + .directive('popover', PopoverDirective); - this.adjustColumns(idx); - this.calculateColumns(); - } +class MenuController{ - if (this.onColumnResize){ - this.onColumnResize({ - column: column, - width: width - }); - } + /*@ngInject*/ + constructor($scope, $timeout){ + this.$scope = $scope; } - /** - * Occurs when a row was selected - * @param {object} rows - */ - onSelected(rows){ - this.onSelect({ - rows: rows + getColumnIndex(model){ + return this.$scope.current.findIndex((col) => { + return model.name == col.name; }); } - /** - * Occurs when a row was click but may not be selected. - * @param {object} row - */ - onRowClicked(row){ - this.onRowClick({ - row: row - }); + isChecked(model){ + return this.getColumnIndex(model) > -1; } - /** - * Occurs when a row was double click but may not be selected. - * @param {object} row - */ - onRowDblClicked(row){ - this.onRowDblClick({ - row: row - }); + onCheck(model){ + var idx = this.getColumnIndex(model); + if(idx === -1){ + this.$scope.current.push(model); + } else { + this.$scope.current.splice(idx, 1); + } } - + } -function DataTableDirective($window, $timeout, $parse){ +function MenuDirective(){ return { restrict: 'E', - replace: true, - controller: DataTableController, - scope: true, - bindToController: { - options: '=', - rows: '=', - selected: '=?', - expanded: '=?', - onSelect: '&', - onSort: '&', - onTreeToggle: '&', - onPage: '&', - onRowClick: '&', - onRowDblClick: '&', - onColumnResize: '&' - }, - controllerAs: 'dt', - template: function(element){ - // Gets the column nodes to transposes to column objects - // http://stackoverflow.com/questions/30845397/angular-expressive-directive-design/30847609#30847609 - var columns = element[0].getElementsByTagName('column'), - id = ObjectId(); - DataTableService.saveColumns(id, columns); - - return `
- - - - - - -
` + controller: 'MenuController', + controllerAs: 'dtm', + scope: { + current: '=', + available: '=' }, - compile: function(tElem, tAttrs){ - return { - pre: function($scope, $elm, $attrs, ctrl){ - DataTableService.buildColumns($scope, $parse); + template: + `` + }; +} - // Check and see if we had expressive columns - // and if so, lets use those - var id = $elm.attr('data-column-id'), - columns = DataTableService.columns[id]; - if (columns) { - ctrl.options.columns = columns; - } +class DropdownController{ + /*@ngInject*/ + constructor($scope){ + $scope.open = false; + } - ctrl.transposeColumnDefaults(); - ctrl.options.internal.scrollBarWidth = ScrollbarWidth(); + toggle(scope){ + scope.open = !scope.open; + } +} - /** - * Invoked on init of control or when the window is resized; - */ - function resize() { - var rect = $elm[0].getBoundingClientRect(); +function DropdownDirective($document, $timeout){ + return { + restrict: 'C', + controller: 'DropdownController', + link: function($scope, $elm, $attrs) { - ctrl.options.internal.innerWidth = Math.floor(rect.width); + function closeDropdown(ev){ + if($elm[0].contains(ev.target) ) { + return; + } - if (ctrl.options.scrollbarV) { - var height = rect.height; + $timeout(() => { + $scope.open = false; + off(); + }); + } - if (ctrl.options.headerHeight) { - height = height - ctrl.options.headerHeight; - } + function keydown(ev){ + if (ev.which === 27) { + $timeout(() => { + $scope.open = false; + off(); + }); + } + } - if (ctrl.options.footerHeight) { - height = height - ctrl.options.footerHeight; - } + function off(){ + $document.unbind('click', closeDropdown); + $document.unbind('keydown', keydown); + } - ctrl.options.internal.bodyHeight = height; - ctrl.calculatePageSize(); - } + $scope.$watch('open', (newVal) => { + if(newVal){ + $document.bind('click', closeDropdown); + $document.bind('keydown', keydown); + } + }); + + } + }; +} - ctrl.adjustColumns(); - }; +function DropdownToggleDirective($timeout){ + return { + restrict: 'C', + controller: 'DropdownController', + require: '?^dropdown', + link: function($scope, $elm, $attrs, ctrl) { - $window.addEventListener('resize', - throttle(() => { - $timeout(resize); - })); + function toggleClick(event) { + event.preventDefault(); + $timeout(() => { + ctrl.toggle($scope); + }); + } - // When an item is hidden for example - // in a tab with display none, the height - // is not calculated correrctly. We need to watch - // the visible attribute and resize if this occurs - var checkVisibility = function() { - var bounds = $elm[0].getBoundingClientRect(), - visible = bounds.width && bounds.height; - if (visible) resize(); - else $timeout(checkVisibility, 100); - }; - checkVisibility(); + function toggleDestroy(){ + $elm.unbind('click', toggleClick); + } - // add a loaded class to avoid flickering - $elm.addClass('dt-loaded'); + $elm.bind('click', toggleClick); + $scope.$on('$destroy', toggleDestroy); + } + }; +} - // prevent memory leaks - $scope.$on('$destroy', () => { - angular.element($window).off('resize'); - }); - } - }; +function DropdownMenuDirective($animate){ + return { + restrict: 'C', + require: '?^dropdown', + link: function($scope, $elm, $attrs, ctrl) { + $scope.$watch('open', () => { + $animate[$scope.open ? 'addClass' : 'removeClass']($elm, 'ddm-open'); + }); } }; } -var dataTable = angular +var dropdown = angular$1 + .module('dt.dropdown', []) + .controller('DropdownController', DropdownController) + .directive('dropdown', DropdownDirective) + .directive('dropdownToggle', DropdownToggleDirective) + .directive('dropdownMenu', DropdownMenuDirective); + +var menu = angular$1 + .module('dt.menu', [ dropdown.name ]) + .controller('MenuController', MenuController) + .directive('dtm', MenuDirective); + +var dataTable = angular$1 .module('data-table', []) .directive('dtable', DataTableDirective) .directive('resizable', ResizableDirective) @@ -3080,4 +3560,4 @@ var dataTable = angular .directive('dtFooter', FooterDirective) .directive('dtPager', PagerDirective); -export default dataTable; \ No newline at end of file +export { popover as dtPopover, menu as dtMenu };export default dataTable; diff --git a/release/dataTable.js b/release/dataTable.js index 10dc89c..9b978fb 100644 --- a/release/dataTable.js +++ b/release/dataTable.js @@ -6,34 +6,37 @@ */ (function (global, factory) { if (typeof define === "function" && define.amd) { - define("DataTable", ["exports"], factory); + define("DataTable", ["exports", "angular"], factory); } else if (typeof exports !== "undefined") { - factory(exports); + factory(exports, require("angular")); } else { var mod = { exports: {} }; - factory(mod.exports); + factory(mod.exports, global.angular); global.DataTable = mod.exports; } -})(this, function (exports) { +})(this, function (exports, _angular) { "use strict"; - DataTableDirective.$inject = ["$window", "$timeout", "$parse"]; - ResizableDirective.$inject = ["$document", "$timeout"]; - SortableDirective.$inject = ["$timeout"]; - HeaderDirective.$inject = ["$timeout"]; - HeaderCellDirective.$inject = ["$compile"]; - BodyDirective.$inject = ["$timeout"]; - ScrollerDirective.$inject = ["$timeout", "$rootScope"]; - CellDirective.$inject = ["$rootScope", "$compile", "$log", "$timeout"]; Object.defineProperty(exports, "__esModule", { value: true }); + exports.dtMenu = exports.dtPopover = undefined; + + var _angular2 = _interopRequireDefault(_angular); + + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } function _toConsumableArray(arr) { if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { + arr2[i] = arr[i]; + } return arr2; } else { @@ -103,308 +106,289 @@ } })(); - var PagerController = function () { - PagerController.$inject = ["$scope"]; - function PagerController($scope) { - var _this = this; + function ResizableDirective($document, $timeout) { + return { + restrict: 'A', + scope: { + isResizable: '=resizable', + minWidth: '=', + maxWidth: '=', + onResize: '&' + }, + link: function link($scope, $element, $attrs) { + if ($scope.isResizable) { + $element.addClass('resizable'); + } - _classCallCheck(this, PagerController); + var handle = angular.element(""), + parent = $element.parent(), + prevScreenX; - $scope.$watch('pager.count', function (newVal) { - _this.calcTotalPages(_this.size, _this.count); - _this.getPages(_this.page || 1); - }); + handle.on('mousedown', function (event) { + if (!$element[0].classList.contains('resizable')) { + return false; + } - $scope.$watch('pager.size', function (newVal) { - _this.calcTotalPages(_this.size, _this.count); - _this.getPages(_this.page || 1); - }); + event.stopPropagation(); + event.preventDefault(); - $scope.$watch('pager.page', function (newVal) { - if (newVal !== 0 && newVal <= _this.totalPages) { - _this.getPages(newVal); + $document.on('mousemove', mousemove); + $document.on('mouseup', mouseup); + }); + + function mousemove(event) { + event = event.originalEvent || event; + + var width = parent[0].clientWidth, + movementX = event.movementX || event.mozMovementX || event.screenX - prevScreenX, + newWidth = width + (movementX || 0); + + prevScreenX = event.screenX; + + if ((!$scope.minWidth || newWidth >= $scope.minWidth) && (!$scope.maxWidth || newWidth <= $scope.maxWidth)) { + parent.css({ + width: newWidth + 'px' + }); + } } - }); - this.getPages(this.page || 1); - } + function mouseup() { + if ($scope.onResize) { + $timeout(function () { + var width = parent[0].clientWidth; + if (width < $scope.minWidth) { + width = $scope.minWidth; + } + $scope.onResize({ width: width }); + }); + } - _createClass(PagerController, [{ - key: "calcTotalPages", - value: function calcTotalPages(size, count) { - var count = size < 1 ? 1 : Math.ceil(count / size); - this.totalPages = Math.max(count || 0, 1); - } - }, { - key: "selectPage", - value: function selectPage(num) { - if (num > 0 && num <= this.totalPages) { - this.page = num; - this.onPage({ - page: num - }); + $document.unbind('mousemove', mousemove); + $document.unbind('mouseup', mouseup); } + + $element.append(handle); } - }, { - key: "prevPage", - value: function prevPage() { - if (this.page > 1) { - this.selectPage(--this.page); + }; + } + + function SortableDirective($timeout) { + return { + restrict: 'A', + scope: { + isSortable: '=sortable', + onSortableSort: '&' + }, + link: function link($scope, $element, $attrs) { + var rootEl = $element[0], + dragEl, + nextEl, + dropEl; + + function isbefore(a, b) { + if (a.parentNode == b.parentNode) { + for (var cur = a; cur; cur = cur.previousSibling) { + if (cur === b) { + return true; + } + } + } + return false; } - } - }, { - key: "nextPage", - value: function nextPage() { - this.selectPage(++this.page); - } - }, { - key: "canPrevious", - value: function canPrevious() { - return this.page > 1; - } - }, { - key: "canNext", - value: function canNext() { - return this.page < this.totalPages; - } - }, { - key: "getPages", - value: function getPages(page) { - var pages = [], - startPage = 1, - endPage = this.totalPages, - maxSize = 5, - isMaxSized = maxSize < this.totalPages; - if (isMaxSized) { - startPage = (Math.ceil(page / maxSize) - 1) * maxSize + 1; - endPage = Math.min(startPage + maxSize - 1, this.totalPages); + function onDragEnter(e) { + var target = e.target; + if (isbefore(dragEl, target)) { + target.parentNode.insertBefore(dragEl, target); + } else if (target.nextSibling && target.hasAttribute('draggable')) { + target.parentNode.insertBefore(dragEl, target.nextSibling.nextSibling); + } } - for (var number = startPage; number <= endPage; number++) { - pages.push({ - number: number, - text: number, - active: number === page - }); + function onDragEnd(evt) { + evt.preventDefault(); + + dragEl.classList.remove('dt-clone'); + + $element.off('dragend', onDragEnd); + $element.off('dragenter', onDragEnter); + + if (nextEl !== dragEl.nextSibling) { + $scope.onSortableSort({ + event: evt, + columnId: _angular2.default.element(dragEl).attr('data-id') + }); + } } - this.pages = pages; - } - }]); + function onDragStart(evt) { + if (!$scope.isSortable) return false; + evt = evt.originalEvent || evt; - return PagerController; - }(); + dragEl = evt.target; + nextEl = dragEl.nextSibling; + dragEl.classList.add('dt-clone'); - function PagerDirective() { - return { - restrict: 'E', - controller: PagerController, - controllerAs: 'pager', - scope: true, - bindToController: { - page: '=', - size: '=', - count: '=', - onPage: '&' - }, - template: "
\n
    \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n {{pg.text}}\n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
\n
", - replace: true + evt.dataTransfer.effectAllowed = 'move'; + evt.dataTransfer.setData('Text', dragEl.textContent); + + $element.on('dragenter', onDragEnter); + $element.on('dragend', onDragEnd); + } + + $element.on('dragstart', onDragStart); + + $scope.$on('$destroy', function () { + $element.off('dragstart', onDragStart); + }); + } }; } - var FooterController = function () { - FooterController.$inject = ["$scope"]; - function FooterController($scope) { - var _this2 = this; + var TableDefaults = { + scrollbarV: true, - _classCallCheck(this, FooterController); + rowHeight: 30, - this.page = this.paging.offset + 1; - $scope.$watch('footer.paging.offset', function (newVal) { - _this2.offsetChanged(newVal); - }); - } + columnMode: 'standard', - _createClass(FooterController, [{ - key: "offsetChanged", - value: function offsetChanged(newVal) { - this.page = newVal + 1; - } - }, { - key: "onPaged", - value: function onPaged(page) { - this.paging.offset = page - 1; - this.onPage({ - offset: this.paging.offset, - size: this.paging.size - }); - } - }]); + loadingMessage: 'Loading...', - return FooterController; - }(); + emptyMessage: 'No data to display', - function FooterDirective() { - return { - restrict: 'E', - controller: FooterController, - controllerAs: 'footer', - scope: true, - bindToController: { - paging: '=', - onPage: '&' - }, - template: "
\n
{{footer.paging.count}} total
\n 1\">\n \n
", - replace: true - }; - } + headerHeight: 30, - var CellController = function () { - function CellController() { - _classCallCheck(this, CellController); - } + footerHeight: 0, - _createClass(CellController, [{ - key: "styles", - value: function styles() { - return { - width: this.column.width + 'px', - 'min-width': this.column.width + 'px' - }; - } - }, { - key: "cellClass", - value: function cellClass() { - var style = { - 'dt-tree-col': this.column.isTreeColumn - }; + paging: { + externalPaging: false, - if (this.column.className) { - style[this.column.className] = true; - } + size: undefined, - return style; - } - }, { - key: "treeClass", - value: function treeClass() { - return { - 'dt-tree-toggle': true, - 'icon-right': !this.expanded, - 'icon-down': this.expanded - }; - } - }, { - key: "onTreeToggled", - value: function onTreeToggled(evt) { - evt.stopPropagation(); - this.expanded = !this.expanded; - this.onTreeToggle({ - cell: { - value: this.value, - column: this.column, - expanded: this.expanded - } - }); - } - }, { - key: "onCheckboxChanged", - value: function onCheckboxChanged(event) { - event.stopPropagation(); - this.onCheckboxChange({ $event: event }); - } - }, { - key: "getValue", - value: function getValue() { - var val = this.column.cellDataGetter ? this.column.cellDataGetter(this.value) : this.value; + count: 0, - if (val === undefined || val === null) val = ''; - return val; - } - }]); + offset: 0, - return CellController; - }(); + loadingIndicator: false + }, - function CellDirective($rootScope, $compile, $log, $timeout) { - return { - restrict: 'E', - controller: CellController, - scope: true, - controllerAs: 'cell', - bindToController: { - options: '=', - value: '=', - selected: '=', - column: '=', - row: '=', - expanded: '=', - hasChildren: '=', - onTreeToggle: '&', - onCheckboxChange: '&' - }, - template: "
\n \n \n \n
", - replace: true, - compile: function compile() { - return { - pre: function pre($scope, $elm, $attrs, ctrl) { - var content = angular.element($elm[0].querySelector('.dt-cell-content')), - cellScope; + selectable: false, - if (ctrl.column.template || ctrl.column.cellRenderer) { - createCellScope(); - } + multiSelect: false, - $scope.$watch('cell.row', function () { - if (cellScope) { - cellScope.$destroy(); + checkboxSelection: false, - createCellScope(); + reorderable: true, - cellScope.$cell = ctrl.value; - cellScope.$row = ctrl.row; - cellScope.$column = ctrl.column; - cellScope.$$watchers = null; - } + internal: { + offsetX: 0, + offsetY: 0, + innerWidth: 0, + bodyHeight: 300 + } - if (ctrl.column.template) { - content.empty(); - var elm = angular.element("" + ctrl.column.template.trim() + ""); - content.append($compile(elm)(cellScope)); - } else if (ctrl.column.cellRenderer) { - content.empty(); - var elm = angular.element(ctrl.column.cellRenderer(cellScope, content)); - content.append($compile(elm)(cellScope)); - } else { - content[0].innerHTML = ctrl.getValue(); - } - }, true); + }; - function createCellScope() { - cellScope = ctrl.options.$outer.$new(false); - cellScope.getValue = ctrl.getValue; - } - } - }; - } + var ColumnDefaults = { + frozenLeft: false, + + frozenRight: false, + + className: undefined, + + headerClassName: undefined, + + flexGrow: 0, + + minWidth: 100, + + maxWidth: undefined, + + width: 150, + + resizable: true, + + comparator: undefined, + + sortable: true, + + sort: undefined, + + sortBy: undefined, + + headerRenderer: undefined, + + cellRenderer: undefined, + + cellDataGetter: undefined, + + isTreeColumn: false, + + isCheckboxColumn: false, + + headerCheckbox: false, + + canAutoResize: true + + }; + + var requestAnimFrame = function () { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { + window.setTimeout(callback, 1000 / 60); }; + }(); + + function ObjectId() { + var timestamp = (new Date().getTime() / 1000 | 0).toString(16); + return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { + return (Math.random() * 16 | 0).toString(16); + }).toLowerCase(); } - var cache = {}, - testStyle = document.createElement('div').style; + function ColumnsByPin(cols) { + var ret = { + left: [], + center: [], + right: [] + }; - var prefix = function () { - var styles = window.getComputedStyle(document.documentElement, ''), - pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || styles.OLink === '' && ['', 'o'])[1], - dom = 'WebKit|Moz|MS|O'.match(new RegExp('(' + pre + ')', 'i'))[1]; + for (var i = 0, len = cols.length; i < len; i++) { + var c = cols[i]; + if (c.frozenLeft) { + ret.left.push(c); + } else if (c.frozenRight) { + ret.right.push(c); + } else { + ret.center.push(c); + } + } + + return ret; + } + + function ColumnGroupWidths(groups, all) { return { - dom: dom, - lowercase: pre, - css: '-' + pre + '-', - js: pre[0].toUpperCase() + pre.substr(1) + left: ColumnTotalWidth(groups.left), + center: ColumnTotalWidth(groups.center), + right: ColumnTotalWidth(groups.right), + total: ColumnTotalWidth(all) }; - }(); + } + + function DeepValueGetter(obj, path) { + if (!obj || !path) return obj; + + var current = obj, + split = path.split('.'); + + if (split.length) { + for (var i = 0, len = split.length; i < len; i++) { + current = current[split[i]]; + } + } + + return current; + } function CamelCase(str) { str = str.replace(/[^a-zA-Z0-9 ]/g, " "); @@ -419,2021 +403,2442 @@ return str; } - function GetVendorPrefixedName(property) { - var name = CamelCase(property); - if (!cache[name]) { - if (testStyle[prefix.css + property] !== undefined) { - cache[name] = prefix.css + property; - } else if (testStyle[property] !== undefined) { - cache[name] = property; - } - } - return cache[name]; - } + function ScrollbarWidth() { + var outer = document.createElement("div"); + outer.style.visibility = "hidden"; + outer.style.width = "100px"; + outer.style.msOverflowStyle = "scrollbar"; + document.body.appendChild(outer); - var transform = GetVendorPrefixedName('transform'), - backfaceVisibility = GetVendorPrefixedName('backfaceVisibility'), - hasCSSTransforms = !!GetVendorPrefixedName('transform'), - hasCSS3DTransforms = !!GetVendorPrefixedName('perspective'), - ua = window.navigator.userAgent, - isSafari = /Safari\//.test(ua) && !/Chrome\//.test(ua); + var widthNoScroll = outer.offsetWidth; + outer.style.overflow = "scroll"; - function TranslateXY(styles, x, y) { - if (hasCSSTransforms) { - if (!isSafari && hasCSS3DTransforms) { - styles[transform] = "translate3d(" + x + "px, " + y + "px, 0)"; - styles[backfaceVisibility] = 'hidden'; - } else { - styles[CamelCase(transform)] = "translate(" + x + "px, " + y + "px)"; - } - } else { - styles.top = y + 'px'; - styles.left = x + 'px'; - } - } + var inner = document.createElement("div"); + inner.style.width = "100%"; + outer.appendChild(inner); - var GroupRowController = function () { - function GroupRowController() { - _classCallCheck(this, GroupRowController); - } + var widthWithScroll = inner.offsetWidth; + outer.parentNode.removeChild(outer); - _createClass(GroupRowController, [{ - key: "onGroupToggled", - value: function onGroupToggled(evt) { - evt.stopPropagation(); - this.onGroupToggle({ - group: this.row - }); + return widthNoScroll - widthWithScroll; + } + + function NextSortDirection(sortType, currentSort) { + if (sortType === 'single') { + if (currentSort === 'asc') { + return 'desc'; + } else { + return 'asc'; } - }, { - key: "treeClass", - value: function treeClass() { - return { - 'dt-tree-toggle': true, - 'icon-right': !this.expanded, - 'icon-down': this.expanded - }; + } else { + if (!currentSort) { + return 'asc'; + } else if (currentSort === 'asc') { + return 'desc'; + } else if (currentSort === 'desc') { + return undefined; } - }]); + } + } - return GroupRowController; - }(); + function ColumnTotalWidth(columns, prop) { + var totalWidth = 0; - function GroupRowDirective() { - return { - restrict: 'E', - controller: GroupRowController, - controllerAs: 'group', - bindToController: { - row: '=', - onGroupToggle: '&', - expanded: '=', - options: '=' - }, - scope: true, - replace: true, - template: "\n
\n \n \n \n \n
", - link: function link($scope, $elm, $attrs, ctrl) { - TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); + columns.forEach(function (c) { + var has = prop && c[prop]; + totalWidth = totalWidth + (has ? c[prop] : c.width); + }); - ctrl.options.internal.styleTranslator.register($scope.$index, $elm); - } - }; + return totalWidth; } - function DeepValueGetter(obj, path) { - if (!obj || !path) return obj; + function GetTotalFlexGrow(columns) { + var totalFlexGrow = 0; - var current = obj, - split = path.split('.'); + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; - if (split.length) { - for (var i = 0, len = split.length; i < len; i++) { - current = current[split[i]]; + try { + for (var _iterator = columns[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var c = _step.value; + + totalFlexGrow += c.flexGrow || 0; + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } } } - return current; + return totalFlexGrow; } - var RowController = function () { - function RowController() { - _classCallCheck(this, RowController); - } + function AdjustColumnWidths(allColumns, expectedWidth) { + var columnsWidth = ColumnTotalWidth(allColumns), + totalFlexGrow = GetTotalFlexGrow(allColumns), + colsByGroup = ColumnsByPin(allColumns); - _createClass(RowController, [{ - key: "getValue", - value: function getValue(col) { - if (!col.prop) return ''; - return DeepValueGetter(this.row, col.prop); - } - }, { - key: "onTreeToggled", - value: function onTreeToggled(cell) { - this.onTreeToggle({ - cell: cell, - row: this.row - }); - } - }, { - key: "stylesByGroup", - value: function stylesByGroup(group) { - var styles = { - width: this.columnWidths[group] + 'px' - }; + if (columnsWidth !== expectedWidth) { + ScaleColumns(colsByGroup, expectedWidth, totalFlexGrow); + } + } - if (group === 'left') { - TranslateXY(styles, this.options.internal.offsetX, 0); - } else if (group === 'right') { - var offset = (this.columnWidths.total - this.options.internal.innerWidth - this.options.internal.offsetX + this.options.internal.scrollBarWidth) * -1; - TranslateXY(styles, offset, 0); + function ScaleColumns(colsByGroup, maxWidth, totalFlexGrow) { + _angular2.default.forEach(colsByGroup, function (cols) { + cols.forEach(function (column) { + if (!column.canAutoResize) { + maxWidth -= column.width; + totalFlexGrow -= column.flexGrow; + } else { + column.width = 0; } + }); + }); - return styles; - } - }, { - key: "onCheckboxChanged", - value: function onCheckboxChanged(ev) { - this.onCheckboxChange({ - $event: ev, - row: this.row + var hasMinWidth = {}; + var remainingWidth = maxWidth; + + var _loop = function _loop() { + var widthPerFlexPoint = remainingWidth / totalFlexGrow; + remainingWidth = 0; + _angular2.default.forEach(colsByGroup, function (cols) { + cols.forEach(function (column, i) { + if (column.canAutoResize && !hasMinWidth[i]) { + var newWidth = column.width + column.flexGrow * widthPerFlexPoint; + if (column.minWidth !== undefined && newWidth < column.minWidth) { + remainingWidth += newWidth - column.minWidth; + column.width = column.minWidth; + hasMinWidth[i] = true; + } else { + column.width = newWidth; + } + } }); + }); + }; + + do { + _loop(); + } while (remainingWidth !== 0); + } + + function ForceFillColumnWidths(allColumns, expectedWidth, startIdx) { + var contentWidth = 0, + columnsToResize = startIdx > -1 ? allColumns.slice(startIdx, allColumns.length).filter(function (c) { + return c.canAutoResize; + }) : allColumns.filter(function (c) { + return c.canAutoResize; + }); + + allColumns.forEach(function (c) { + if (!c.canAutoResize) { + contentWidth += c.width; + } else { + contentWidth += c.$$oldWidth || c.width; } - }]); + }); - return RowController; - }(); + var remainingWidth = expectedWidth - contentWidth, + additionWidthPerColumn = remainingWidth / columnsToResize.length, + exceedsWindow = contentWidth > expectedWidth; - function RowDirective() { - return { - restrict: 'E', - controller: RowController, - controllerAs: 'rowCtrl', - scope: true, - bindToController: { - row: '=', - columns: '=', - columnWidths: '=', - expanded: '=', - selected: '=', - hasChildren: '=', - options: '=', - onCheckboxChange: '&', - onTreeToggle: '&' - }, - link: function link($scope, $elm, $attrs, ctrl) { - if (ctrl.row) { - TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); + columnsToResize.forEach(function (column) { + if (exceedsWindow) { + column.width = column.$$oldWidth || column.width; + } else { + if (!column.$$oldWidth) { + column.$$oldWidth = column.width; } - ctrl.options.internal.styleTranslator.register($scope.$index, $elm); - }, - template: "\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", - replace: true - }; + var newSize = column.$$oldWidth + additionWidthPerColumn; + if (column.minWith && newSize < column.minWidth) { + column.width = column.minWidth; + } else if (column.maxWidth && newSize > column.maxWidth) { + column.width = column.maxWidth; + } else { + column.width = newSize; + } + } + }); } - var KEYS = { - BACKSPACE: 8, - TAB: 9, - RETURN: 13, - ALT: 18, - ESC: 27, - SPACE: 32, - PAGE_UP: 33, - PAGE_DOWN: 34, - END: 35, - HOME: 36, - LEFT: 37, - UP: 38, - RIGHT: 39, - DOWN: 40, - DELETE: 46, - COMMA: 188, - PERIOD: 190, - A: 65, - Z: 90, - ZERO: 48, - NUMPAD_0: 96, - NUMPAD_9: 105 - }; + var DataTableController = function () { + function DataTableController($scope, $filter, $log, $transclude) { + var _this = this; - var SelectionController = function () { - SelectionController.$inject = ["$scope"]; - function SelectionController($scope) { - _classCallCheck(this, SelectionController); + _classCallCheck(this, DataTableController); - this.body = $scope.body; - this.options = $scope.body.options; - this.selected = $scope.body.selected; - } + Object.assign(this, { + $scope: $scope, + $filter: $filter, + $log: $log + }); - _createClass(SelectionController, [{ - key: "keyDown", - value: function keyDown(ev, index, row) { - if (KEYS[ev.keyCode]) { - ev.preventDefault(); + this.defaults(); + + this.options.$outer = $scope.$parent; + + $scope.$watch('dt.options.columns', function (newVal, oldVal) { + _this.transposeColumnDefaults(); + + if (newVal.length !== oldVal.length) { + _this.adjustColumns(); } - if (ev.keyCode === KEYS.DOWN) { - var next = ev.target.nextElementSibling; - if (next) { - next.focus(); - } - } else if (ev.keyCode === KEYS.UP) { - var prev = ev.target.previousElementSibling; - if (prev) { - prev.focus(); + _this.calculateColumns(); + }, true); + + var watch = $scope.$watch('dt.rows', function (newVal) { + if (newVal) { + watch(); + _this.onSorted(); + } + }); + } + + _createClass(DataTableController, [{ + key: "defaults", + value: function defaults() { + var _this2 = this; + + this.expanded = this.expanded || {}; + + this.options = _angular2.default.extend(_angular2.default.copy(TableDefaults), this.options); + + _angular2.default.forEach(TableDefaults.paging, function (v, k) { + if (!_this2.options.paging[k]) { + _this2.options.paging[k] = v; } - } else if (ev.keyCode === KEYS.RETURN) { - this.selectRow(index, row); + }); + + if (this.options.selectable && this.options.multiSelect) { + this.selected = this.selected || []; } } }, { - key: "rowClicked", - value: function rowClicked(event, index, row) { - if (!this.options.checkboxSelection) { - this.selectRow(event, index, row); - } + key: "transposeColumnDefaults", + value: function transposeColumnDefaults() { + for (var i = 0, len = this.options.columns.length; i < len; i++) { + var column = this.options.columns[i]; + column.$id = ObjectId(); - this.body.onRowClick({ row: row }); + _angular2.default.forEach(ColumnDefaults, function (v, k) { + if (!column.hasOwnProperty(k)) { + column[k] = v; + } + }); + + if (column.name && !column.prop) { + column.prop = CamelCase(column.name); + } + + this.options.columns[i] = column; + } } }, { - key: "rowDblClicked", - value: function rowDblClicked(event, index, row) { - if (!this.options.checkboxSelection) { - event.preventDefault(); - this.selectRow(event, index, row); - } - - this.body.onRowDblClick({ row: row }); + key: "calculateColumns", + value: function calculateColumns() { + var columns = this.options.columns; + this.columnsByPin = ColumnsByPin(columns); + this.columnWidths = ColumnGroupWidths(this.columnsByPin, columns); } }, { - key: "onCheckboxChange", - value: function onCheckboxChange(event, index, row) { - this.selectRow(event, index, row); + key: "tableCss", + value: function tableCss() { + return { + 'fixed': this.options.scrollbarV, + 'selectable': this.options.selectable, + 'checkboxable': this.options.checkboxSelection + }; } }, { - key: "selectRow", - value: function selectRow(event, index, row) { - if (this.options.selectable) { - if (this.options.multiSelect) { - var isCtrlKeyDown = event.ctrlKey || event.metaKey, - isShiftKeyDown = event.shiftKey; + key: "adjustColumns", + value: function adjustColumns(forceIdx) { + var width = this.options.internal.innerWidth - this.options.internal.scrollBarWidth; - if (isShiftKeyDown) { - this.selectRowsBetween(index, row); - } else { - var idx = this.selected.indexOf(row); - if (idx > -1) { - this.selected.splice(idx, 1); - } else { - if (this.options.multiSelectOnShift && this.selected.length === 1) { - this.selected.splice(0, 1); - } - this.selected.push(row); - this.body.onSelect({ rows: [row] }); - } - } - this.prevIndex = index; - } else { - this.selected = row; - this.body.onSelect({ rows: [row] }); - } + if (this.options.columnMode === 'force') { + ForceFillColumnWidths(this.options.columns, width, forceIdx); + } else if (this.options.columnMode === 'flex') { + AdjustColumnWidths(this.options.columns, width); } } }, { - key: "selectRowsBetween", - value: function selectRowsBetween(index) { - var reverse = index < this.prevIndex, - selecteds = []; - - for (var i = 0, len = this.body.rows.length; i < len; i++) { - var row = this.body.rows[i], - greater = i >= this.prevIndex && i <= index, - lesser = i <= this.prevIndex && i >= index; + key: "calculatePageSize", + value: function calculatePageSize() { + this.options.paging.size = Math.ceil(this.options.internal.bodyHeight / this.options.rowHeight) + 1; + } + }, { + key: "onSorted", + value: function onSorted() { + if (!this.rows) return; - var range = {}; - if (reverse) { - range = { - start: index, - end: this.prevIndex - index - }; - } else { - range = { - start: this.prevIndex, - end: index + 1 - }; + var sorts = this.options.columns.filter(function (c) { + return c.sort; + }).sort(function (a, b) { + if (a.sortPriority && b.sortPriority) { + if (a.sortPriority > b.sortPriority) return 1; + if (a.sortPriority < b.sortPriority) return -1; + } else if (a.sortPriority) { + return -1; + } else if (b.sortPriority) { + return 1; } - if (reverse && lesser || !reverse && greater) { - var idx = this.selected.indexOf(row); + return 0; + }).map(function (c, i) { + c.sortPriority = i + 1; + return c; + }); - if (reverse && idx > -1) { - this.selected.splice(idx, 1); - continue; - } + if (sorts.length) { + this.onSort({ sorts: sorts }); - if (i >= range.start && i < range.end) { - if (idx === -1) { - this.selected.push(row); - selecteds.push(row); + if (this.options.onSort) { + this.options.onSort(sorts); + } + + var clientSorts = []; + for (var i = 0, len = sorts.length; i < len; i++) { + var c = sorts[i]; + if (c.comparator !== false) { + var dir = c.sort === 'asc' ? '' : '-'; + if (c.sortBy !== undefined) { + clientSorts.push(dir + c.sortBy); + } else { + clientSorts.push(dir + c.prop); } } } + + if (clientSorts.length) { + var _rows; + + var sortedValues = this.$filter('orderBy')(this.rows, clientSorts); + this.rows.splice(0, this.rows.length); + (_rows = this.rows).push.apply(_rows, _toConsumableArray(sortedValues)); + } } - this.body.onSelect({ rows: selecteds }); + this.options.internal.setYOffset(0); } - }]); - - return SelectionController; - }(); + }, { + key: "onTreeToggled", + value: function onTreeToggled(row, cell) { + this.onTreeToggle({ + row: row, + cell: cell + }); + } + }, { + key: "onBodyPage", + value: function onBodyPage(offset, size) { + this.onPage({ + offset: offset, + size: size + }); + } + }, { + key: "onFooterPage", + value: function onFooterPage(offset, size) { + var pageBlockSize = this.options.rowHeight * size, + offsetY = pageBlockSize * offset; - function SelectionDirective() { - return { - controller: SelectionController, - restrict: 'A', - require: '^dtBody', - controllerAs: 'selCtrl' - }; - } - - var requestAnimFrame = function () { - return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { - window.setTimeout(callback, 1000 / 60); - }; - }(); - - var StyleTranslator = function () { - function StyleTranslator(height) { - _classCallCheck(this, StyleTranslator); + this.options.internal.setYOffset(offsetY); + } + }, { + key: "onHeaderCheckboxChange", + value: function onHeaderCheckboxChange() { + if (this.rows) { + var matches = this.selected.length === this.rows.length; + this.selected.splice(0, this.selected.length); - this.height = height; - this.map = new Map(); - } + if (!matches) { + var _selected; - _createClass(StyleTranslator, [{ - key: "update", - value: function update(rows) { - var n = 0; - while (n <= this.map.size) { - var dom = this.map.get(n); - var model = rows[n]; - if (dom && model) { - TranslateXY(dom[0].style, 0, model.$$index * this.height); + (_selected = this.selected).push.apply(_selected, _toConsumableArray(this.rows)); } - n++; } } }, { - key: "register", - value: function register(idx, dom) { - this.map.set(idx, dom); + key: "isAllRowsSelected", + value: function isAllRowsSelected() { + if (this.rows) return false; + return this.selected.length === this.rows.length; } - }]); - - return StyleTranslator; - }(); - - function ScrollerDirective($timeout, $rootScope) { - return { - restrict: 'E', - require: '^dtBody', - transclude: true, - replace: true, - template: "
", - link: function link($scope, $elm, $attrs, ctrl) { - var ticking = false, - lastScrollY = 0, - lastScrollX = 0, - parent = $elm.parent(); - - ctrl.options.internal.styleTranslator = new StyleTranslator(ctrl.options.rowHeight); - - ctrl.options.internal.setYOffset = function (offsetY) { - parent[0].scrollTop = offsetY; - }; - - function update() { - ctrl.options.internal.offsetY = lastScrollY; - ctrl.options.internal.offsetX = lastScrollX; - ctrl.updatePage(); - - if (ctrl.options.scrollbarV) { - ctrl.getRows(); - } - - ctrl.options.$outer.$digest(); - - ticking = false; - }; + }, { + key: "onResized", + value: function onResized(column, width) { + var idx = this.options.columns.indexOf(column); + if (idx > -1) { + var column = this.options.columns[idx]; + column.width = width; + column.canAutoResize = false; - function requestTick() { - if (!ticking) { - requestAnimFrame(update); - ticking = true; - } - }; + this.adjustColumns(idx); + this.calculateColumns(); + } - parent.on('scroll', function (ev) { - lastScrollY = this.scrollTop; - lastScrollX = this.scrollLeft; - requestTick(); + if (this.onColumnResize) { + this.onColumnResize({ + column: column, + width: width + }); + } + } + }, { + key: "onSelected", + value: function onSelected(rows) { + this.onSelect({ + rows: rows }); - - $scope.$on('$destroy', function () { - parent.off('scroll'); + } + }, { + key: "onRowClicked", + value: function onRowClicked(row) { + this.onRowClick({ + row: row }); + } + }, { + key: "onRowDblClicked", + value: function onRowDblClicked(row) { + this.onRowDblClick({ + row: row + }); + } + }]); - $scope.scrollerStyles = function () { - if (ctrl.options.scrollbarV) { - return { - height: ctrl.count * ctrl.options.rowHeight + 'px' - }; - } - }; + return DataTableController; + }(); + + function throttle(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + options || (options = {}); + var later = function later() { + previous = options.leading === false ? 0 : new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function () { + var now = new Date(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); } + return result; }; } - var BodyController = function () { - BodyController.$inject = ["$scope", "$timeout"]; - function BodyController($scope, $timeout) { - var _this3 = this; - - _classCallCheck(this, BodyController); + var DataTableService = { + columns: {}, + dTables: {}, - this.$scope = $scope; - this.tempRows = []; + saveColumns: function saveColumns(id, columnElms) { + if (columnElms && columnElms.length) { + var columnsArray = [].slice.call(columnElms); + this.dTables[id] = columnsArray; + } + }, + buildColumns: function buildColumns(scope, parse) { + var _this3 = this; - this.treeColumn = this.options.columns.find(function (c) { - return c.isTreeColumn; - }); + _angular2.default.forEach(this.dTables, function (columnElms, id) { + _this3.columns[id] = []; - this.groupColumn = this.options.columns.find(function (c) { - return c.group; - }); + _angular2.default.forEach(columnElms, function (c) { + var column = {}; - $scope.$watchCollection('body.rows', this.rowsUpdated.bind(this)); + var visible = true; - if (this.options.scrollbarV || !this.options.scrollbarV && this.options.paging.externalPaging) { - var sized = false; - $scope.$watch('body.options.paging.size', function (newVal, oldVal) { - if (!sized || newVal > oldVal) { - _this3.getRows(); - sized = true; - } - }); + _angular2.default.forEach(c.attributes, function (attr) { + var attrName = CamelCase(attr.name); - $scope.$watch('body.options.paging.count', function (count) { - _this3.count = count; - _this3.updatePage(); - }); + switch (attrName) { + case 'class': + column.className = attr.value; + break; + case 'name': + case 'prop': + column[attrName] = attr.value; + break; + case 'headerRenderer': + case 'cellRenderer': + case 'cellDataGetter': + column[attrName] = parse(attr.value); + break; + case 'visible': + visible = parse(attr.value)(scope); + break; + default: + column[attrName] = parse(attr.value)(scope); + break; + } + }); - $scope.$watch('body.options.paging.offset', function (newVal) { - if (_this3.options.paging.size) { - _this3.onPage({ - offset: newVal, - size: _this3.options.paging.size - }); + var header = c.getElementsByTagName('column-header'); + if (header.length) { + column.headerTemplate = header[0].innerHTML; + c.removeChild(header[0]); } - }); - } - } - _createClass(BodyController, [{ - key: "rowsUpdated", - value: function rowsUpdated(newVal, oldVal) { - if (newVal) { - if (!this.options.paging.externalPaging) { - this.options.paging.count = newVal.length; + if (c.innerHTML !== '') { + column.template = c.innerHTML; } - this.count = this.options.paging.count; + if (visible) _this3.columns[id].push(column); + }); + }); - if (this.treeColumn || this.groupColumn) { - this.buildRowsByGroup(); - } + this.dTables = {}; + } + }; - if (this.options.scrollbarV) { - var refresh = newVal && oldVal && (newVal.length === oldVal.length || newVal.length < oldVal.length); + function DataTableDirective($window, $timeout, $parse) { + return { + restrict: 'E', + replace: true, + controller: DataTableController, + scope: true, + bindToController: { + options: '=', + rows: '=', + selected: '=?', + expanded: '=?', + onSelect: '&', + onSort: '&', + onTreeToggle: '&', + onPage: '&', + onRowClick: '&', + onRowDblClick: '&', + onColumnResize: '&' + }, + controllerAs: 'dt', + template: function template(element) { + var columns = element[0].getElementsByTagName('column'), + id = ObjectId(); + DataTableService.saveColumns(id, columns); - this.getRows(refresh); - } else { - var rows = this.rows; + return "
\n \n \n \n \n \n \n
"; + }, + compile: function compile(tElem, tAttrs) { + return { + pre: function pre($scope, $elm, $attrs, ctrl) { + DataTableService.buildColumns($scope, $parse); - if (this.treeColumn) { - rows = this.buildTree(); - } else if (this.groupColumn) { - rows = this.buildGroups(); + var id = $elm.attr('data-column-id'), + columns = DataTableService.columns[id]; + if (columns) { + ctrl.options.columns = columns; } - if (this.options.paging.externalPaging) { - var idxs = this.getFirstLastIndexes(), - idx = idxs.first; + ctrl.transposeColumnDefaults(); + ctrl.options.internal.scrollBarWidth = ScrollbarWidth(); - this.tempRows.splice(0, this.tempRows.length); - while (idx < idxs.last) { - this.tempRows.push(rows[idx++]); + function resize() { + var rect = $elm[0].getBoundingClientRect(); + + ctrl.options.internal.innerWidth = Math.floor(rect.width); + + if (ctrl.options.scrollbarV) { + var height = rect.height; + + if (ctrl.options.headerHeight) { + height = height - ctrl.options.headerHeight; + } + + if (ctrl.options.footerHeight) { + height = height - ctrl.options.footerHeight; + } + + ctrl.options.internal.bodyHeight = height; + ctrl.calculatePageSize(); } - } else { - var _tempRows; - this.tempRows.splice(0, this.tempRows.length); - (_tempRows = this.tempRows).push.apply(_tempRows, _toConsumableArray(rows)); + ctrl.adjustColumns(); } + + $window.addEventListener('resize', throttle(function () { + $timeout(resize); + })); + + var checkVisibility = function checkVisibility() { + var bounds = $elm[0].getBoundingClientRect(), + visible = bounds.width && bounds.height; + if (visible) resize();else $timeout(checkVisibility, 100); + }; + checkVisibility(); + + $elm.addClass('dt-loaded'); + + $scope.$on('$destroy', function () { + _angular2.default.element($window).off('resize'); + }); } - } + }; } - }, { - key: "getFirstLastIndexes", - value: function getFirstLastIndexes() { - var firstRowIndex, endIndex; + }; + } - if (this.options.scrollbarV) { - firstRowIndex = Math.max(Math.floor((this.options.internal.offsetY || 0) / this.options.rowHeight, 0), 0); - endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); - } else { - if (this.options.paging.externalPaging) { - firstRowIndex = Math.max(this.options.paging.offset * this.options.paging.size, 0); - endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); - } else { - endIndex = this.count; - } - } + var cache = {}; + var testStyle = document.createElement('div').style; - return { - first: firstRowIndex, - last: endIndex - }; + var prefix = function () { + var styles = window.getComputedStyle(document.documentElement, ''), + pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || styles.OLink === '' && ['', 'o'])[1], + dom = 'WebKit|Moz|MS|O'.match(new RegExp('(' + pre + ')', 'i'))[1]; + return { + dom: dom, + lowercase: pre, + css: '-' + pre + '-', + js: pre[0].toUpperCase() + pre.substr(1) + }; + }(); + + function GetVendorPrefixedName(property) { + var name = CamelCase(property); + if (!cache[name]) { + if (testStyle[prefix.css + property] !== undefined) { + cache[name] = prefix.css + property; + } else if (testStyle[property] !== undefined) { + cache[name] = property; } - }, { - key: "updatePage", - value: function updatePage() { - var curPage = this.options.paging.offset, - idxs = this.getFirstLastIndexes(); + } + return cache[name]; + } - if (this.options.internal.oldScrollPosition === undefined) { - this.options.internal.oldScrollPosition = 0; - } + var transform = GetVendorPrefixedName('transform'); + var backfaceVisibility = GetVendorPrefixedName('backfaceVisibility'); + var hasCSSTransforms = !!GetVendorPrefixedName('transform'); + var hasCSS3DTransforms = !!GetVendorPrefixedName('perspective'); + var ua = window.navigator.userAgent; + var isSafari = /Safari\//.test(ua) && !/Chrome\//.test(ua); - var oldScrollPosition = this.options.internal.oldScrollPosition, - newPage = idxs.first / this.options.paging.size; + function TranslateXY(styles, x, y) { + if (hasCSSTransforms) { + if (!isSafari && hasCSS3DTransforms) { + styles[transform] = "translate3d(" + x + "px, " + y + "px, 0)"; + styles[backfaceVisibility] = 'hidden'; + } else { + styles[CamelCase(transform)] = "translate(" + x + "px, " + y + "px)"; + } + } else { + styles.top = y + 'px'; + styles.left = x + 'px'; + } + } - this.options.internal.oldScrollPosition = newPage; + var HeaderController = function () { + function HeaderController() { + _classCallCheck(this, HeaderController); + } - if (newPage < oldScrollPosition) { - newPage = Math.floor(newPage); - } else if (newPage > oldScrollPosition) { - newPage = Math.ceil(newPage); - } else { - newPage = curPage; - } + _createClass(HeaderController, [{ + key: "styles", + value: function styles() { + return { + width: this.options.internal.innerWidth + 'px', + height: this.options.headerHeight + 'px' + }; + } + }, { + key: "innerStyles", + value: function innerStyles() { + return { + width: this.columnWidths.total + 'px' + }; + } + }, { + key: "onSorted", + value: function onSorted(sortedColumn) { + if (this.options.sortType === 'single') { + var unsortColumn = function unsortColumn(column) { + if (column !== sortedColumn) { + column.sort = undefined; + } + }; - if (!isNaN(newPage)) { - this.options.paging.offset = newPage; + this.columns.left.forEach(unsortColumn); + this.columns.center.forEach(unsortColumn); + this.columns.right.forEach(unsortColumn); } + + this.onSort({ + column: sortedColumn + }); } }, { - key: "calculateDepth", - value: function calculateDepth(row) { - var depth = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; + key: "stylesByGroup", + value: function stylesByGroup(group) { + var styles = { + width: this.columnWidths[group] + 'px' + }; - var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; - var prop = this.treeColumn.prop; - if (!row[parentProp]) { - return depth; - } - if (row.$$depth) { - return row.$$depth + depth; + if (group === 'center') { + TranslateXY(styles, this.options.internal.offsetX * -1, 0); + } else if (group === 'right') { + var offset = (this.columnWidths.total - this.options.internal.innerWidth) * -1; + TranslateXY(styles, offset, 0); } - var cachedParent = this.index[row[parentProp]]; - if (cachedParent) { - depth += 1; - return this.calculateDepth(cachedParent, depth); - } - for (var i = 0, len = this.rows.length; i < len; i++) { - var parent = this.rows[i]; - if (parent[prop] == row[parentProp]) { - depth += 1; - return this.calculateDepth(parent, depth); - } - } - return depth; + return styles; } }, { - key: "buildRowsByGroup", - value: function buildRowsByGroup() { - this.index = {}; - this.rowsByGroup = {}; + key: "onCheckboxChanged", + value: function onCheckboxChanged() { + this.onCheckboxChange(); + } + }, { + key: "onResized", + value: function onResized(column, width) { + this.onResize({ + column: column, + width: width + }); + } + }]); - var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; + return HeaderController; + }(); - for (var i = 0, len = this.rows.length; i < len; i++) { - var row = this.rows[i]; + function HeaderDirective($timeout) { + return { + restrict: 'E', + controller: HeaderController, + controllerAs: 'header', + scope: true, + bindToController: { + options: '=', + columns: '=', + columnWidths: '=', + onSort: '&', + onResize: '&', + onCheckboxChange: '&' + }, + template: "\n
\n\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
", + replace: true, + link: function link($scope, $elm, $attrs, ctrl) { - var relVal = row[parentProp]; - if (relVal) { - if (this.rowsByGroup[relVal]) { - this.rowsByGroup[relVal].push(row); - } else { - this.rowsByGroup[relVal] = [row]; + $scope.columnsResorted = function (event, columnId) { + var col = findColumnById(columnId), + parent = _angular2.default.element(event.currentTarget), + newIdx = -1; + + _angular2.default.forEach(parent.children(), function (c, i) { + if (columnId === _angular2.default.element(c).attr('data-id')) { + newIdx = i; } - } + }); - if (this.treeColumn) { - var prop = this.treeColumn.prop; - this.index[row[prop]] = row; + $timeout(function () { + _angular2.default.forEach(ctrl.columns, function (group) { + var idx = group.indexOf(col); + if (idx > -1) { + var curColAtIdx = group[newIdx], + siblingIdx = ctrl.options.columns.indexOf(curColAtIdx), + curIdx = ctrl.options.columns.indexOf(col); - if (row[parentProp] === undefined) { - row.$$depth = 0; - } else { - var parent = this.index[row[parentProp]]; - if (parent === undefined) { - for (var j = 0; j < len; j++) { - if (this.rows[j][prop] == relVal) { - parent = this.rows[j]; - break; - } - } - } - if (parent.$$depth === undefined) { - parent.$$depth = this.calculateDepth(parent); - } - row.$$depth = parent.$$depth + 1; - if (parent.$$children) { - parent.$$children.push(row[prop]); - } else { - parent.$$children = [row[prop]]; - } - } - } - } - } - }, { - key: "buildGroups", - value: function buildGroups() { - var _this4 = this; + ctrl.options.columns.splice(curIdx, 1); + ctrl.options.columns.splice(siblingIdx, 0, col); - var temp = []; + return false; + } + }); + }); + }; - angular.forEach(this.rowsByGroup, function (v, k) { - temp.push({ - name: k, - group: true + var findColumnById = function findColumnById(columnId) { + var columns = ctrl.columns.left.concat(ctrl.columns.center).concat(ctrl.columns.right); + return columns.find(function (c) { + return c.$id === columnId; }); + }; + } + }; + } - if (_this4.expanded[k]) { - temp.push.apply(temp, _toConsumableArray(v)); - } - }); + var HeaderCellController = function () { + function HeaderCellController() { + _classCallCheck(this, HeaderCellController); + } - return temp; + _createClass(HeaderCellController, [{ + key: "styles", + value: function styles() { + return { + width: this.column.width + 'px', + minWidth: this.column.minWidth + 'px', + maxWidth: this.column.maxWidth + 'px', + height: this.column.height + 'px' + }; } }, { - key: "isSelected", - value: function isSelected(row) { - var selected = false; + key: "cellClass", + value: function cellClass() { + var cls = { + 'sortable': this.column.sortable, + 'resizable': this.column.resizable + }; - if (this.options.selectable) { - if (this.options.multiSelect) { - selected = this.selected.indexOf(row) > -1; - } else { - selected = this.selected === row; - } + if (this.column.headerClassName) { + cls[this.column.headerClassName] = true; } - return selected; + return cls; } }, { - key: "buildTree", - value: function buildTree() { - var temp = [], - self = this; + key: "onSorted", + value: function onSorted() { + if (this.column.sortable) { + this.column.sort = NextSortDirection(this.sortType, this.column.sort); - function addChildren(fromArray, toArray, level) { - fromArray.forEach(function (row) { - var relVal = row[self.treeColumn.relationProp], - key = row[self.treeColumn.prop], - groupRows = self.rowsByGroup[key], - expanded = self.expanded[key]; + if (this.column.sort === undefined) { + this.column.sortPriority = undefined; + } - if (level > 0 || !relVal) { - toArray.push(row); - if (groupRows && groupRows.length > 0 && expanded) { - addChildren(groupRows, toArray, level + 1); - } - } + this.onSort({ + column: this.column }); } - - addChildren(this.rows, temp, 0); - - return temp; } }, { - key: "getRows", - value: function getRows(refresh) { - if ((this.treeColumn || this.groupColumn) && !this.rowsByGroup) { - return false; - } + key: "sortClass", + value: function sortClass() { + return { + 'sort-btn': true, + 'sort-asc icon-down': this.column.sort === 'asc', + 'sort-desc icon-up': this.column.sort === 'desc' + }; + } + }, { + key: "onResized", + value: function onResized(width, column) { + this.onResize({ + column: column, + width: width + }); + } + }, { + key: "onCheckboxChange", + value: function onCheckboxChange() { + this.onCheckboxChanged(); + } + }]); - var temp; + return HeaderCellController; + }(); - if (this.treeColumn) { - temp = this.treeTemp || []; + function HeaderCellDirective($compile) { + return { + restrict: 'E', + controller: HeaderCellController, + controllerAs: 'hcell', + scope: true, + bindToController: { + options: '=', + column: '=', + onCheckboxChange: '&', + onSort: '&', + sortType: '=', + onResize: '&', + selected: '=' + }, + replace: true, + template: "
\n
\n \n \n \n \n
\n
", + compile: function compile() { + return { + pre: function pre($scope, $elm, $attrs, ctrl) { + var label = $elm[0].querySelector('.dt-header-cell-label'), + cellScope = void 0; - if (refresh || !this.treeTemp) { - this.treeTemp = temp = this.buildTree(); - this.count = temp.length; + if (ctrl.column.headerTemplate || ctrl.column.headerRenderer) { + cellScope = ctrl.options.$outer.$new(false); - this.tempRows.splice(0, this.tempRows.length); - } - } else if (this.groupColumn) { - temp = this.groupsTemp || []; + cellScope.$header = ctrl.column.name; + cellScope.$index = $scope.$index; + } - if (refresh || !this.groupsTemp) { - this.groupsTemp = temp = this.buildGroups(); - this.count = temp.length; - } - } else { - temp = this.rows; - if (refresh === true) { - this.tempRows.splice(0, this.tempRows.length); + if (ctrl.column.headerTemplate) { + var elm = _angular2.default.element("" + ctrl.column.headerTemplate.trim() + ""); + _angular2.default.element(label).append($compile(elm)(cellScope)); + } else if (ctrl.column.headerRenderer) { + var _elm = _angular2.default.element(ctrl.column.headerRenderer($elm)); + _angular2.default.element(label).append($compile(_elm)(cellScope)[0]); + } else { + var val = ctrl.column.name; + if (val === undefined || val === null) val = ''; + label.textContent = val; + } } - } - - var idx = 0, - indexes = this.getFirstLastIndexes(), - rowIndex = indexes.first; + }; + } + }; + } - this.tempRows.splice(0, indexes.last - indexes.first); + var BodyController = function () { + function BodyController($scope, $timeout) { + var _this4 = this; - while (rowIndex < indexes.last && rowIndex < this.count) { - var row = temp[rowIndex]; - if (row) { - row.$$index = rowIndex; - this.tempRows[idx] = row; - } - idx++; - rowIndex++; - } + _classCallCheck(this, BodyController); - this.options.internal.styleTranslator.update(this.tempRows); + this.$scope = $scope; + this.tempRows = []; - return this.tempRows; - } - }, { - key: "styles", - value: function styles() { - var styles = { - width: this.options.internal.innerWidth + 'px' - }; + this.treeColumn = this.options.columns.find(function (c) { + return c.isTreeColumn; + }); - if (!this.options.scrollbarV) { - styles.overflowY = 'hidden'; - } else if (this.options.scrollbarH === false) { - styles.overflowX = 'hidden'; - } + this.groupColumn = this.options.columns.find(function (c) { + return c.group; + }); - if (this.options.scrollbarV) { - styles.height = this.options.internal.bodyHeight + 'px'; - } + $scope.$watchCollection('body.rows', this.rowsUpdated.bind(this)); - return styles; - } - }, { - key: "rowStyles", - value: function rowStyles(row) { - var styles = {}; + if (this.options.scrollbarV || !this.options.scrollbarV && this.options.paging.externalPaging) { + var sized = false; + $scope.$watch('body.options.paging.size', function (newVal, oldVal) { + if (!sized || newVal > oldVal) { + _this4.getRows(); + sized = true; + } + }); - if (this.options.rowHeight === 'auto') { - styles.height = this.options.rowHeight + 'px'; - } + $scope.$watch('body.options.paging.count', function (count) { + _this4.count = count; + _this4.updatePage(); + }); - return styles; - } - }, { - key: "groupRowStyles", - value: function groupRowStyles(row) { - var styles = this.rowStyles(row); - styles.width = this.columnWidths.total + 'px'; - return styles; + $scope.$watch('body.options.paging.offset', function (newVal) { + if (_this4.options.paging.size) { + _this4.onPage({ + offset: newVal, + size: _this4.options.paging.size + }); + } + }); } - }, { - key: "rowClasses", - value: function rowClasses(row) { - var styles = { - 'selected': this.isSelected(row), - 'dt-row-even': row && row.$$index % 2 === 0, - 'dt-row-odd': row && row.$$index % 2 !== 0 - }; + } - if (this.treeColumn) { - styles['dt-leaf'] = this.rowsByGroup[row[this.treeColumn.relationProp]]; + _createClass(BodyController, [{ + key: "rowsUpdated", + value: function rowsUpdated(newVal, oldVal) { + if (newVal) { + if (!this.options.paging.externalPaging) { + this.options.paging.count = newVal.length; + } - styles['dt-has-leafs'] = this.rowsByGroup[row[this.treeColumn.prop]]; + this.count = this.options.paging.count; - styles['dt-depth-' + row.$$depth] = true; - } + if (this.treeColumn || this.groupColumn) { + this.buildRowsByGroup(); + } - return styles; - } - }, { - key: "getRowValue", - value: function getRowValue(idx) { - return this.tempRows[idx]; - } - }, { - key: "getRowExpanded", - value: function getRowExpanded(row) { - if (this.treeColumn) { - return this.expanded[row[this.treeColumn.prop]]; - } else if (this.groupColumn) { - return this.expanded[row.name]; - } - } - }, { - key: "getRowHasChildren", - value: function getRowHasChildren(row) { - if (!this.treeColumn) return; - var children = this.rowsByGroup[row[this.treeColumn.prop]]; - return children !== undefined || children && !children.length; - } - }, { - key: "onTreeToggled", - value: function onTreeToggled(row, cell) { - var val = row[this.treeColumn.prop]; - this.expanded[val] = !this.expanded[val]; + if (this.options.scrollbarV) { + var refresh = newVal && oldVal && (newVal.length === oldVal.length || newVal.length < oldVal.length); - if (this.options.scrollbarV) { - this.getRows(true); - } else { - var _tempRows2; + this.getRows(refresh); + } else { + var rows = this.rows; - var values = this.buildTree(); - this.tempRows.splice(0, this.tempRows.length); - (_tempRows2 = this.tempRows).push.apply(_tempRows2, _toConsumableArray(values)); - } + if (this.treeColumn) { + rows = this.buildTree(); + } else if (this.groupColumn) { + rows = this.buildGroups(); + } - this.onTreeToggle({ - row: row, - cell: cell - }); + if (this.options.paging.externalPaging) { + var idxs = this.getFirstLastIndexes(), + idx = idxs.first; + + this.tempRows.splice(0, this.tempRows.length); + while (idx < idxs.last) { + this.tempRows.push(rows[idx++]); + } + } else { + var _tempRows; + + this.tempRows.splice(0, this.tempRows.length); + (_tempRows = this.tempRows).push.apply(_tempRows, _toConsumableArray(rows)); + } + } + } } }, { - key: "onGroupToggle", - value: function onGroupToggle(row) { - this.expanded[row.name] = !this.expanded[row.name]; + key: "getFirstLastIndexes", + value: function getFirstLastIndexes() { + var firstRowIndex, endIndex; if (this.options.scrollbarV) { - this.getRows(true); + firstRowIndex = Math.max(Math.floor((this.options.internal.offsetY || 0) / this.options.rowHeight, 0), 0); + endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); } else { - var _tempRows3; - - var values = this.buildGroups(); - this.tempRows.splice(0, this.tempRows.length); - (_tempRows3 = this.tempRows).push.apply(_tempRows3, _toConsumableArray(values)); + if (this.options.paging.externalPaging) { + firstRowIndex = Math.max(this.options.paging.offset * this.options.paging.size, 0); + endIndex = Math.min(firstRowIndex + this.options.paging.size, this.count); + } else { + endIndex = this.count; + } } - } - }]); - - return BodyController; - }(); - - function BodyDirective($timeout) { - return { - restrict: 'E', - controller: BodyController, - controllerAs: 'body', - bindToController: { - columns: '=', - columnWidths: '=', - rows: '=', - options: '=', - selected: '=?', - expanded: '=?', - onPage: '&', - onTreeToggle: '&', - onSelect: '&', - onRowClick: '&', - onRowDblClick: '&' - }, - scope: true, - template: "\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n
" - }; - } - - function NextSortDirection(sortType, currentSort) { - if (sortType === 'single') { - if (currentSort === 'asc') { - return 'desc'; - } else { - return 'asc'; - } - } else { - if (!currentSort) { - return 'asc'; - } else if (currentSort === 'asc') { - return 'desc'; - } else if (currentSort === 'desc') { - return undefined; - } - } - } - - var HeaderCellController = function () { - function HeaderCellController() { - _classCallCheck(this, HeaderCellController); - } - _createClass(HeaderCellController, [{ - key: "styles", - value: function styles() { return { - width: this.column.width + 'px', - minWidth: this.column.minWidth + 'px', - maxWidth: this.column.maxWidth + 'px', - height: this.column.height + 'px' + first: firstRowIndex, + last: endIndex }; } }, { - key: "cellClass", - value: function cellClass() { - var cls = { - 'sortable': this.column.sortable, - 'resizable': this.column.resizable - }; + key: "updatePage", + value: function updatePage() { + var curPage = this.options.paging.offset, + idxs = this.getFirstLastIndexes(); - if (this.column.headerClassName) { - cls[this.column.headerClassName] = true; + if (this.options.internal.oldScrollPosition === undefined) { + this.options.internal.oldScrollPosition = 0; } - return cls; - } - }, { - key: "onSorted", - value: function onSorted() { - if (this.column.sortable) { - this.column.sort = NextSortDirection(this.sortType, this.column.sort); + var oldScrollPosition = this.options.internal.oldScrollPosition, + newPage = idxs.first / this.options.paging.size; - if (this.column.sort === undefined) { - this.column.sortPriority = undefined; - } + this.options.internal.oldScrollPosition = newPage; - this.onSort({ - column: this.column - }); + if (newPage < oldScrollPosition) { + newPage = Math.floor(newPage); + } else if (newPage > oldScrollPosition) { + newPage = Math.ceil(newPage); + } else { + newPage = curPage; + } + + if (!isNaN(newPage)) { + this.options.paging.offset = newPage; } } }, { - key: "sortClass", - value: function sortClass() { - return { - 'sort-btn': true, - 'sort-asc icon-down': this.column.sort === 'asc', - 'sort-desc icon-up': this.column.sort === 'desc' - }; - } - }, { - key: "onResized", - value: function onResized(width, column) { - this.onResize({ - column: column, - width: width - }); - } - }, { - key: "onCheckboxChange", - value: function onCheckboxChange() { - this.onCheckboxChanged(); - } - }]); + key: "calculateDepth", + value: function calculateDepth(row) { + var depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - return HeaderCellController; - }(); + var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; + var prop = this.treeColumn.prop; + if (!row[parentProp]) { + return depth; + } + if (row.$$depth) { + return row.$$depth + depth; + } - function HeaderCellDirective($compile) { - return { - restrict: 'E', - controller: HeaderCellController, - controllerAs: 'hcell', - scope: true, - bindToController: { - options: '=', - column: '=', - onCheckboxChange: '&', - onSort: '&', - sortType: '=', - onResize: '&', - selected: '=' - }, - replace: true, - template: "
\n
\n \n \n \n \n
\n
", - compile: function compile() { - return { - pre: function pre($scope, $elm, $attrs, ctrl) { - var label = $elm[0].querySelector('.dt-header-cell-label'), - cellScope = void 0; + var cachedParent = this.index[row[parentProp]]; + if (cachedParent) { + depth += 1; + return this.calculateDepth(cachedParent, depth); + } + for (var i = 0, len = this.rows.length; i < len; i++) { + var parent = this.rows[i]; + if (parent[prop] == row[parentProp]) { + depth += 1; + return this.calculateDepth(parent, depth); + } + } + return depth; + } + }, { + key: "buildRowsByGroup", + value: function buildRowsByGroup() { + this.index = {}; + this.rowsByGroup = {}; - if (ctrl.column.headerTemplate || ctrl.column.headerRenderer) { - cellScope = ctrl.options.$outer.$new(false); + var parentProp = this.treeColumn ? this.treeColumn.relationProp : this.groupColumn.prop; - cellScope.$header = ctrl.column.name; - cellScope.$index = $scope.$index; - } + for (var i = 0, len = this.rows.length; i < len; i++) { + var row = this.rows[i]; - if (ctrl.column.headerTemplate) { - var elm = angular.element("" + ctrl.column.headerTemplate.trim() + ""); - angular.element(label).append($compile(elm)(cellScope)); - } else if (ctrl.column.headerRenderer) { - var _elm = angular.element(ctrl.column.headerRenderer($elm)); - angular.element(label).append($compile(_elm)(cellScope)[0]); + var relVal = row[parentProp]; + if (relVal) { + if (this.rowsByGroup[relVal]) { + this.rowsByGroup[relVal].push(row); } else { - var val = ctrl.column.name; - if (val === undefined || val === null) val = ''; - label.textContent = val; + this.rowsByGroup[relVal] = [row]; } } - }; - } - }; - } - var HeaderController = function () { - function HeaderController() { - _classCallCheck(this, HeaderController); - } + if (this.treeColumn) { + var prop = this.treeColumn.prop; + this.index[row[prop]] = row; - _createClass(HeaderController, [{ - key: "styles", - value: function styles() { - return { - width: this.options.internal.innerWidth + 'px', - height: this.options.headerHeight + 'px' - }; - } - }, { - key: "innerStyles", - value: function innerStyles() { - return { - width: this.columnWidths.total + 'px' - }; + if (row[parentProp] === undefined) { + row.$$depth = 0; + } else { + var parent = this.index[row[parentProp]]; + if (parent === undefined) { + for (var j = 0; j < len; j++) { + if (this.rows[j][prop] == relVal) { + parent = this.rows[j]; + break; + } + } + } + if (parent.$$depth === undefined) { + parent.$$depth = this.calculateDepth(parent); + } + row.$$depth = parent.$$depth + 1; + if (parent.$$children) { + parent.$$children.push(row[prop]); + } else { + parent.$$children = [row[prop]]; + } + } + } + } } }, { - key: "onSorted", - value: function onSorted(sortedColumn) { - if (this.options.sortType === 'single') { - var unsortColumn = function unsortColumn(column) { - if (column !== sortedColumn) { - column.sort = undefined; - } - }; + key: "buildGroups", + value: function buildGroups() { + var _this5 = this; - this.columns.left.forEach(unsortColumn); - this.columns.center.forEach(unsortColumn); - this.columns.right.forEach(unsortColumn); - } + var temp = []; - this.onSort({ - column: sortedColumn + _angular2.default.forEach(this.rowsByGroup, function (v, k) { + temp.push({ + name: k, + group: true + }); + + if (_this5.expanded[k]) { + temp.push.apply(temp, _toConsumableArray(v)); + } }); + + return temp; } }, { - key: "stylesByGroup", - value: function stylesByGroup(group) { - var styles = { - width: this.columnWidths[group] + 'px' - }; + key: "isSelected", + value: function isSelected(row) { + var selected = false; - if (group === 'center') { - TranslateXY(styles, this.options.internal.offsetX * -1, 0); - } else if (group === 'right') { - var offset = (this.columnWidths.total - this.options.internal.innerWidth) * -1; - TranslateXY(styles, offset, 0); + if (this.options.selectable) { + if (this.options.multiSelect) { + selected = this.selected.indexOf(row) > -1; + } else { + selected = this.selected === row; + } } - return styles; - } - }, { - key: "onCheckboxChanged", - value: function onCheckboxChanged() { - this.onCheckboxChange(); + return selected; } }, { - key: "onResized", - value: function onResized(column, width) { - this.onResize({ - column: column, - width: width - }); - } - }]); - - return HeaderController; - }(); - - function HeaderDirective($timeout) { - return { - restrict: 'E', - controller: HeaderController, - controllerAs: 'header', - scope: true, - bindToController: { - options: '=', - columns: '=', - columnWidths: '=', - onSort: '&', - onResize: '&', - onCheckboxChange: '&' - }, - template: "\n
\n\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
", - replace: true, - link: function link($scope, $elm, $attrs, ctrl) { + key: "buildTree", + value: function buildTree() { + var temp = [], + self = this; - $scope.columnsResorted = function (event, columnId) { - var col = findColumnById(columnId), - parent = angular.element(event.currentTarget), - newIdx = -1; + function addChildren(fromArray, toArray, level) { + fromArray.forEach(function (row) { + var relVal = row[self.treeColumn.relationProp], + key = row[self.treeColumn.prop], + groupRows = self.rowsByGroup[key], + expanded = self.expanded[key]; - angular.forEach(parent.children(), function (c, i) { - if (columnId === angular.element(c).attr('data-id')) { - newIdx = i; + if (level > 0 || !relVal) { + toArray.push(row); + if (groupRows && groupRows.length > 0 && expanded) { + addChildren(groupRows, toArray, level + 1); + } } }); + } - $timeout(function () { - angular.forEach(ctrl.columns, function (group) { - var idx = group.indexOf(col); - if (idx > -1) { - var curColAtIdx = group[newIdx], - siblingIdx = ctrl.options.columns.indexOf(curColAtIdx), - curIdx = ctrl.options.columns.indexOf(col); - - ctrl.options.columns.splice(curIdx, 1); - ctrl.options.columns.splice(siblingIdx, 0, col); - - return false; - } - }); - }); - }; + addChildren(this.rows, temp, 0); - var findColumnById = function findColumnById(columnId) { - var columns = ctrl.columns.left.concat(ctrl.columns.center).concat(ctrl.columns.right); - return columns.find(function (c) { - return c.$id === columnId; - }); - }; + return temp; } - }; - } - - function SortableDirective($timeout) { - return { - restrict: 'A', - scope: { - isSortable: '=sortable', - onSortableSort: '&' - }, - link: function link($scope, $element, $attrs) { - var rootEl = $element[0], - dragEl, - nextEl, - dropEl; - - function isbefore(a, b) { - if (a.parentNode == b.parentNode) { - for (var cur = a; cur; cur = cur.previousSibling) { - if (cur === b) { - return true; - } - } - } + }, { + key: "getRows", + value: function getRows(refresh) { + if ((this.treeColumn || this.groupColumn) && !this.rowsByGroup) { return false; - }; - - function onDragEnter(e) { - var target = e.target; - if (isbefore(dragEl, target)) { - target.parentNode.insertBefore(dragEl, target); - } else if (target.nextSibling && target.hasAttribute('draggable')) { - target.parentNode.insertBefore(dragEl, target.nextSibling.nextSibling); - } - }; + } - function onDragEnd(evt) { - evt.preventDefault(); + var temp; - dragEl.classList.remove('dt-clone'); + if (this.treeColumn) { + temp = this.treeTemp || []; - $element.off('dragend', onDragEnd); - $element.off('dragenter', onDragEnter); + if (refresh || !this.treeTemp) { + this.treeTemp = temp = this.buildTree(); + this.count = temp.length; - if (nextEl !== dragEl.nextSibling) { - $scope.onSortableSort({ - event: evt, - columnId: angular.element(dragEl).attr('data-id') - }); + this.tempRows.splice(0, this.tempRows.length); } - }; + } else if (this.groupColumn) { + temp = this.groupsTemp || []; - function onDragStart(evt) { - if (!$scope.isSortable) return false; - evt = evt.originalEvent || evt; + if (refresh || !this.groupsTemp) { + this.groupsTemp = temp = this.buildGroups(); + this.count = temp.length; + } + } else { + temp = this.rows; + if (refresh === true) { + this.tempRows.splice(0, this.tempRows.length); + } + } - dragEl = evt.target; - nextEl = dragEl.nextSibling; - dragEl.classList.add('dt-clone'); + var idx = 0, + indexes = this.getFirstLastIndexes(), + rowIndex = indexes.first; - evt.dataTransfer.effectAllowed = 'move'; - evt.dataTransfer.setData('Text', dragEl.textContent); + this.tempRows.splice(0, indexes.last - indexes.first); - $element.on('dragenter', onDragEnter); - $element.on('dragend', onDragEnd); - }; + while (rowIndex < indexes.last && rowIndex < this.count) { + var row = temp[rowIndex]; + if (row) { + row.$$index = rowIndex; + this.tempRows[idx] = row; + } + idx++; + rowIndex++; + } - $element.on('dragstart', onDragStart); + this.options.internal.styleTranslator.update(this.tempRows); - $scope.$on('$destroy', function () { - $element.off('dragstart', onDragStart); - }); + return this.tempRows; } - }; - } + }, { + key: "styles", + value: function styles() { + var styles = { + width: this.options.internal.innerWidth + 'px' + }; - function ResizableDirective($document, $timeout) { - return { - restrict: 'A', - scope: { - isResizable: '=resizable', - minWidth: '=', - maxWidth: '=', - onResize: '&' - }, - link: function link($scope, $element, $attrs) { - if ($scope.isResizable) { - $element.addClass('resizable'); + if (!this.options.scrollbarV) { + styles.overflowY = 'hidden'; + } else if (this.options.scrollbarH === false) { + styles.overflowX = 'hidden'; } - var handle = angular.element(""), - parent = $element.parent(), - prevScreenX; + if (this.options.scrollbarV) { + styles.height = this.options.internal.bodyHeight + 'px'; + } - handle.on('mousedown', function (event) { - if (!$element[0].classList.contains('resizable')) { - return false; - } + return styles; + } + }, { + key: "rowStyles", + value: function rowStyles(row) { + var styles = {}; - event.stopPropagation(); - event.preventDefault(); + if (this.options.rowHeight === 'auto') { + styles.height = this.options.rowHeight + 'px'; + } - $document.on('mousemove', mousemove); - $document.on('mouseup', mouseup); - }); + return styles; + } + }, { + key: "groupRowStyles", + value: function groupRowStyles(row) { + var styles = this.rowStyles(row); + styles.width = this.columnWidths.total + 'px'; + return styles; + } + }, { + key: "rowClasses", + value: function rowClasses(row) { + var styles = { + 'selected': this.isSelected(row), + 'dt-row-even': row && row.$$index % 2 === 0, + 'dt-row-odd': row && row.$$index % 2 !== 0 + }; - function mousemove(event) { - event = event.originalEvent || event; + if (this.treeColumn) { + styles['dt-leaf'] = this.rowsByGroup[row[this.treeColumn.relationProp]]; - var width = parent[0].clientWidth, - movementX = event.movementX || event.mozMovementX || event.screenX - prevScreenX, - newWidth = width + (movementX || 0); + styles['dt-has-leafs'] = this.rowsByGroup[row[this.treeColumn.prop]]; - prevScreenX = event.screenX; + styles['dt-depth-' + row.$$depth] = true; + } - if ((!$scope.minWidth || newWidth >= $scope.minWidth) && (!$scope.maxWidth || newWidth <= $scope.maxWidth)) { - parent.css({ - width: newWidth + 'px' - }); - } + return styles; + } + }, { + key: "getRowValue", + value: function getRowValue(idx) { + return this.tempRows[idx]; + } + }, { + key: "getRowExpanded", + value: function getRowExpanded(row) { + if (this.treeColumn) { + return this.expanded[row[this.treeColumn.prop]]; + } else if (this.groupColumn) { + return this.expanded[row.name]; } + } + }, { + key: "getRowHasChildren", + value: function getRowHasChildren(row) { + if (!this.treeColumn) return; + var children = this.rowsByGroup[row[this.treeColumn.prop]]; + return children !== undefined || children && !children.length; + } + }, { + key: "onTreeToggled", + value: function onTreeToggled(row, cell) { + var val = row[this.treeColumn.prop]; + this.expanded[val] = !this.expanded[val]; - function mouseup() { - if ($scope.onResize) { - $timeout(function () { - var width = parent[0].clientWidth; - if (width < $scope.minWidth) { - width = $scope.minWidth; - } - $scope.onResize({ width: width }); - }); - } + if (this.options.scrollbarV) { + this.getRows(true); + } else { + var _tempRows2; - $document.unbind('mousemove', mousemove); - $document.unbind('mouseup', mouseup); + var values = this.buildTree(); + this.tempRows.splice(0, this.tempRows.length); + (_tempRows2 = this.tempRows).push.apply(_tempRows2, _toConsumableArray(values)); } - $element.append(handle); + this.onTreeToggle({ + row: row, + cell: cell + }); } - }; - } - - function throttle(func, wait, options) { - var context, args, result; - var timeout = null; - var previous = 0; - options || (options = {}); - var later = function later() { - previous = options.leading === false ? 0 : new Date(); - timeout = null; - result = func.apply(context, args); - }; - return function () { - var now = new Date(); - if (!previous && options.leading === false) previous = now; - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); - } - return result; - }; - } - - function ScrollbarWidth() { - var outer = document.createElement("div"); - outer.style.visibility = "hidden"; - outer.style.width = "100px"; - outer.style.msOverflowStyle = "scrollbar"; - document.body.appendChild(outer); + }, { + key: "onGroupToggle", + value: function onGroupToggle(row) { + this.expanded[row.name] = !this.expanded[row.name]; - var widthNoScroll = outer.offsetWidth; - outer.style.overflow = "scroll"; + if (this.options.scrollbarV) { + this.getRows(true); + } else { + var _tempRows3; - var inner = document.createElement("div"); - inner.style.width = "100%"; - outer.appendChild(inner); + var values = this.buildGroups(); + this.tempRows.splice(0, this.tempRows.length); + (_tempRows3 = this.tempRows).push.apply(_tempRows3, _toConsumableArray(values)); + } + } + }]); - var widthWithScroll = inner.offsetWidth; - outer.parentNode.removeChild(outer); + return BodyController; + }(); - return widthNoScroll - widthWithScroll; + function BodyDirective($timeout) { + return { + restrict: 'E', + controller: BodyController, + controllerAs: 'body', + bindToController: { + columns: '=', + columnWidths: '=', + rows: '=', + options: '=', + selected: '=?', + expanded: '=?', + onPage: '&', + onTreeToggle: '&', + onSelect: '&', + onRowClick: '&', + onRowDblClick: '&' + }, + scope: true, + template: "\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n
" + }; } - var DataTableService = { - columns: {}, - dTables: {}, + var StyleTranslator = function () { + function StyleTranslator(height) { + _classCallCheck(this, StyleTranslator); - saveColumns: function saveColumns(id, columnElms) { - if (columnElms && columnElms.length) { - var columnsArray = [].slice.call(columnElms); - this.dTables[id] = columnsArray; - } - }, - buildColumns: function buildColumns(scope, parse) { - var _this5 = this; + this.height = height; + this.map = new Map(); + } - angular.forEach(this.dTables, function (columnElms, id) { - _this5.columns[id] = []; + _createClass(StyleTranslator, [{ + key: "update", + value: function update(rows) { + var n = 0; + while (n <= this.map.size) { + var dom = this.map.get(n); + var model = rows[n]; + if (dom && model) { + TranslateXY(dom[0].style, 0, model.$$index * this.height); + } + n++; + } + } + }, { + key: "register", + value: function register(idx, dom) { + this.map.set(idx, dom); + } + }]); - angular.forEach(columnElms, function (c) { - var column = {}; + return StyleTranslator; + }(); - var visible = true; + function ScrollerDirective($timeout, $rootScope) { + return { + restrict: 'E', + require: '^dtBody', + transclude: true, + replace: true, + template: "
", + link: function link($scope, $elm, $attrs, ctrl) { + var ticking = false, + lastScrollY = 0, + lastScrollX = 0, + parent = $elm.parent(); - angular.forEach(c.attributes, function (attr) { - var attrName = CamelCase(attr.name); + ctrl.options.internal.styleTranslator = new StyleTranslator(ctrl.options.rowHeight); - switch (attrName) { - case 'class': - column.className = attr.value; - break; - case 'name': - case 'prop': - column[attrName] = attr.value; - break; - case 'headerRenderer': - case 'cellRenderer': - case 'cellDataGetter': - column[attrName] = parse(attr.value); - break; - case 'visible': - visible = parse(attr.value)(scope); - break; - default: - column[attrName] = parse(attr.value)(scope); - break; - } - }); + ctrl.options.internal.setYOffset = function (offsetY) { + parent[0].scrollTop = offsetY; + }; - var header = c.getElementsByTagName('column-header'); - if (header.length) { - column.headerTemplate = header[0].innerHTML; - c.removeChild(header[0]); - } + function update() { + ctrl.options.internal.offsetY = lastScrollY; + ctrl.options.internal.offsetX = lastScrollX; + ctrl.updatePage(); - if (c.innerHTML !== '') { - column.template = c.innerHTML; + if (ctrl.options.scrollbarV) { + ctrl.getRows(); } - if (visible) _this5.columns[id].push(column); - }); - }); - - this.dTables = {}; - } - }; - - function ObjectId() { - var timestamp = (new Date().getTime() / 1000 | 0).toString(16); - return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { - return (Math.random() * 16 | 0).toString(16); - }).toLowerCase(); - } + ctrl.options.$outer.$digest(); - function ScaleColumns(colsByGroup, maxWidth, totalFlexGrow) { - angular.forEach(colsByGroup, function (cols) { - cols.forEach(function (column) { - if (!column.canAutoResize) { - maxWidth -= column.width; - totalFlexGrow -= column.flexGrow; - } else { - column.width = 0; + ticking = false; } - }); - }); - var hasMinWidth = {}; - var remainingWidth = maxWidth; - - var _loop = function _loop() { - var widthPerFlexPoint = remainingWidth / totalFlexGrow; - remainingWidth = 0; - angular.forEach(colsByGroup, function (cols) { - cols.forEach(function (column, i) { - if (column.canAutoResize && !hasMinWidth[i]) { - var newWidth = column.width + column.flexGrow * widthPerFlexPoint; - if (column.minWidth !== undefined && newWidth < column.minWidth) { - remainingWidth += newWidth - column.minWidth; - column.width = column.minWidth; - hasMinWidth[i] = true; - } else { - column.width = newWidth; - } + function requestTick() { + if (!ticking) { + requestAnimFrame(update); + ticking = true; } - }); - }); - }; + } - do { - _loop(); - } while (remainingWidth !== 0); - } + parent.on('scroll', function (ev) { + lastScrollY = this.scrollTop; + lastScrollX = this.scrollLeft; + requestTick(); + }); - function ColumnsByPin(cols) { - var ret = { - left: [], - center: [], - right: [] - }; + $scope.$on('$destroy', function () { + parent.off('scroll'); + }); - for (var i = 0, len = cols.length; i < len; i++) { - var c = cols[i]; - if (c.frozenLeft) { - ret.left.push(c); - } else if (c.frozenRight) { - ret.right.push(c); - } else { - ret.center.push(c); + $scope.scrollerStyles = function () { + if (ctrl.options.scrollbarV) { + return { + height: ctrl.count * ctrl.options.rowHeight + 'px' + }; + } + }; } - } - - return ret; + }; } - function GetTotalFlexGrow(columns) { - var totalFlexGrow = 0; - - var _iteratorNormalCompletion = true; - var _didIteratorError = false; - var _iteratorError = undefined; + var KEYS = { + BACKSPACE: 8, + TAB: 9, + RETURN: 13, + ALT: 18, + ESC: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46, + COMMA: 188, + PERIOD: 190, + A: 65, + Z: 90, + ZERO: 48, + NUMPAD_0: 96, + NUMPAD_9: 105 + }; - try { - for (var _iterator = columns[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { - var c = _step.value; + var SelectionController = function () { + function SelectionController($scope) { + _classCallCheck(this, SelectionController); - totalFlexGrow += c.flexGrow || 0; - } - } catch (err) { - _didIteratorError = true; - _iteratorError = err; - } finally { - try { - if (!_iteratorNormalCompletion && _iterator.return) { - _iterator.return(); - } - } finally { - if (_didIteratorError) { - throw _iteratorError; - } - } + this.body = $scope.body; + this.options = $scope.body.options; + this.selected = $scope.body.selected; } - return totalFlexGrow; - } + _createClass(SelectionController, [{ + key: "keyDown", + value: function keyDown(ev, index, row) { + if (KEYS[ev.keyCode]) { + ev.preventDefault(); + } - function ColumnTotalWidth(columns, prop) { - var totalWidth = 0; + if (ev.keyCode === KEYS.DOWN) { + var next = ev.target.nextElementSibling; + if (next) { + next.focus(); + } + } else if (ev.keyCode === KEYS.UP) { + var prev = ev.target.previousElementSibling; + if (prev) { + prev.focus(); + } + } else if (ev.keyCode === KEYS.RETURN) { + this.selectRow(index, row); + } + } + }, { + key: "rowClicked", + value: function rowClicked(event, index, row) { + if (!this.options.checkboxSelection) { + this.selectRow(event, index, row); + } - columns.forEach(function (c) { - var has = prop && c[prop]; - totalWidth = totalWidth + (has ? c[prop] : c.width); - }); + this.body.onRowClick({ row: row }); + } + }, { + key: "rowDblClicked", + value: function rowDblClicked(event, index, row) { + if (!this.options.checkboxSelection) { + event.preventDefault(); + this.selectRow(event, index, row); + } - return totalWidth; - } + this.body.onRowDblClick({ row: row }); + } + }, { + key: "onCheckboxChange", + value: function onCheckboxChange(event, index, row) { + this.selectRow(event, index, row); + } + }, { + key: "selectRow", + value: function selectRow(event, index, row) { + if (this.options.selectable) { + if (this.options.multiSelect) { + var isCtrlKeyDown = event.ctrlKey || event.metaKey, + isShiftKeyDown = event.shiftKey; - function AdjustColumnWidths(allColumns, expectedWidth) { - var columnsWidth = ColumnTotalWidth(allColumns), - totalFlexGrow = GetTotalFlexGrow(allColumns), - colsByGroup = ColumnsByPin(allColumns); + if (isShiftKeyDown) { + this.selectRowsBetween(index, row); + } else { + var idx = this.selected.indexOf(row); + if (idx > -1) { + this.selected.splice(idx, 1); + } else { + if (this.options.multiSelectOnShift && this.selected.length === 1) { + this.selected.splice(0, 1); + } + this.selected.push(row); + this.body.onSelect({ rows: [row] }); + } + } + this.prevIndex = index; + } else { + this.selected = row; + this.body.onSelect({ rows: [row] }); + } + } + } + }, { + key: "selectRowsBetween", + value: function selectRowsBetween(index) { + var reverse = index < this.prevIndex, + selecteds = []; - if (columnsWidth !== expectedWidth) { - ScaleColumns(colsByGroup, expectedWidth, totalFlexGrow); - } - } + for (var i = 0, len = this.body.rows.length; i < len; i++) { + var row = this.body.rows[i], + greater = i >= this.prevIndex && i <= index, + lesser = i <= this.prevIndex && i >= index; - function ForceFillColumnWidths(allColumns, expectedWidth, startIdx) { - var contentWidth = 0, - columnsToResize = startIdx > -1 ? allColumns.slice(startIdx, allColumns.length).filter(function (c) { - return c.canAutoResize; - }) : allColumns.filter(function (c) { - return c.canAutoResize; - }); + var range = {}; + if (reverse) { + range = { + start: index, + end: this.prevIndex - index + }; + } else { + range = { + start: this.prevIndex, + end: index + 1 + }; + } - allColumns.forEach(function (c) { - if (!c.canAutoResize) { - contentWidth += c.width; - } else { - contentWidth += c.$$oldWidth || c.width; - } - }); + if (reverse && lesser || !reverse && greater) { + var idx = this.selected.indexOf(row); - var remainingWidth = expectedWidth - contentWidth, - additionWidthPerColumn = remainingWidth / columnsToResize.length, - exceedsWindow = contentWidth > expectedWidth; + if (reverse && idx > -1) { + this.selected.splice(idx, 1); + continue; + } - columnsToResize.forEach(function (column) { - if (exceedsWindow) { - column.width = column.$$oldWidth || column.width; - } else { - if (!column.$$oldWidth) { - column.$$oldWidth = column.width; + if (i >= range.start && i < range.end) { + if (idx === -1) { + this.selected.push(row); + selecteds.push(row); + } + } + } } - var newSize = column.$$oldWidth + additionWidthPerColumn; - if (column.minWith && newSize < column.minWidth) { - column.width = column.minWidth; - } else if (column.maxWidth && newSize > column.maxWidth) { - column.width = column.maxWidth; - } else { - column.width = newSize; - } + this.body.onSelect({ rows: selecteds }); } - }); - } + }]); - function ColumnGroupWidths(groups, all) { + return SelectionController; + }(); + + function SelectionDirective() { return { - left: ColumnTotalWidth(groups.left), - center: ColumnTotalWidth(groups.center), - right: ColumnTotalWidth(groups.right), - total: ColumnTotalWidth(all) + controller: SelectionController, + restrict: 'A', + require: '^dtBody', + controllerAs: 'selCtrl' }; } - var ColumnDefaults = { - frozenLeft: false, - - frozenRight: false, - - className: undefined, - - headerClassName: undefined, - - flexGrow: 0, - - minWidth: 100, - - maxWidth: undefined, - - width: 150, - - resizable: true, - - comparator: undefined, - - sortable: true, - - sort: undefined, - - sortBy: undefined, - - headerRenderer: undefined, - - cellRenderer: undefined, + var RowController = function () { + function RowController() { + _classCallCheck(this, RowController); + } - cellDataGetter: undefined, + _createClass(RowController, [{ + key: "getValue", + value: function getValue(col) { + if (!col.prop) return ''; + return DeepValueGetter(this.row, col.prop); + } + }, { + key: "onTreeToggled", + value: function onTreeToggled(cell) { + this.onTreeToggle({ + cell: cell, + row: this.row + }); + } + }, { + key: "stylesByGroup", + value: function stylesByGroup(group) { + var styles = { + width: this.columnWidths[group] + 'px' + }; - isTreeColumn: false, + if (group === 'left') { + TranslateXY(styles, this.options.internal.offsetX, 0); + } else if (group === 'right') { + var offset = (this.columnWidths.total - this.options.internal.innerWidth - this.options.internal.offsetX + this.options.internal.scrollBarWidth) * -1; + TranslateXY(styles, offset, 0); + } - isCheckboxColumn: false, + return styles; + } + }, { + key: "onCheckboxChanged", + value: function onCheckboxChanged(ev) { + this.onCheckboxChange({ + $event: ev, + row: this.row + }); + } + }]); - headerCheckbox: false, + return RowController; + }(); - canAutoResize: true + function RowDirective() { + return { + restrict: 'E', + controller: RowController, + controllerAs: 'rowCtrl', + scope: true, + bindToController: { + row: '=', + columns: '=', + columnWidths: '=', + expanded: '=', + selected: '=', + hasChildren: '=', + options: '=', + onCheckboxChange: '&', + onTreeToggle: '&' + }, + link: function link($scope, $elm, $attrs, ctrl) { + if (ctrl.row) { + TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); + } - }; + ctrl.options.internal.styleTranslator.register($scope.$index, $elm); + }, + template: "\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + replace: true + }; + } - var TableDefaults = { - scrollbarV: true, + var GroupRowController = function () { + function GroupRowController() { + _classCallCheck(this, GroupRowController); + } - rowHeight: 30, + _createClass(GroupRowController, [{ + key: "onGroupToggled", + value: function onGroupToggled(evt) { + evt.stopPropagation(); + this.onGroupToggle({ + group: this.row + }); + } + }, { + key: "treeClass", + value: function treeClass() { + return { + 'dt-tree-toggle': true, + 'icon-right': !this.expanded, + 'icon-down': this.expanded + }; + } + }]); - columnMode: 'standard', + return GroupRowController; + }(); - loadingMessage: 'Loading...', + function GroupRowDirective() { + return { + restrict: 'E', + controller: GroupRowController, + controllerAs: 'group', + bindToController: { + row: '=', + onGroupToggle: '&', + expanded: '=', + options: '=' + }, + scope: true, + replace: true, + template: "\n
\n \n \n \n \n
", + link: function link($scope, $elm, $attrs, ctrl) { + TranslateXY($elm[0].style, 0, ctrl.row.$$index * ctrl.options.rowHeight); - emptyMessage: 'No data to display', + ctrl.options.internal.styleTranslator.register($scope.$index, $elm); + } + }; + } - headerHeight: 30, + var CellController = function () { + function CellController() { + _classCallCheck(this, CellController); + } - footerHeight: 0, + _createClass(CellController, [{ + key: "styles", + value: function styles() { + return { + width: this.column.width + 'px', + 'min-width': this.column.width + 'px' + }; + } + }, { + key: "cellClass", + value: function cellClass() { + var style = { + 'dt-tree-col': this.column.isTreeColumn + }; - paging: { - externalPaging: false, + if (this.column.className) { + style[this.column.className] = true; + } - size: undefined, + return style; + } + }, { + key: "treeClass", + value: function treeClass() { + return { + 'dt-tree-toggle': true, + 'icon-right': !this.expanded, + 'icon-down': this.expanded + }; + } + }, { + key: "onTreeToggled", + value: function onTreeToggled(evt) { + evt.stopPropagation(); + this.expanded = !this.expanded; + this.onTreeToggle({ + cell: { + value: this.value, + column: this.column, + expanded: this.expanded + } + }); + } + }, { + key: "onCheckboxChanged", + value: function onCheckboxChanged(event) { + event.stopPropagation(); + this.onCheckboxChange({ $event: event }); + } + }, { + key: "getValue", + value: function getValue() { + var val = this.column.cellDataGetter ? this.column.cellDataGetter(this.value) : this.value; - count: 0, + if (val === undefined || val === null) val = ''; + return val; + } + }]); - offset: 0, + return CellController; + }(); - loadingIndicator: false - }, + function CellDirective($rootScope, $compile, $log, $timeout) { + return { + restrict: 'E', + controller: CellController, + scope: true, + controllerAs: 'cell', + bindToController: { + options: '=', + value: '=', + selected: '=', + column: '=', + row: '=', + expanded: '=', + hasChildren: '=', + onTreeToggle: '&', + onCheckboxChange: '&' + }, + template: "
\n \n \n \n
", + replace: true, + compile: function compile() { + return { + pre: function pre($scope, $elm, $attrs, ctrl) { + var content = _angular2.default.element($elm[0].querySelector('.dt-cell-content')), + cellScope; - selectable: false, + if (ctrl.column.template || ctrl.column.cellRenderer) { + createCellScope(); + } - multiSelect: false, + $scope.$watch('cell.row', function () { + if (cellScope) { + cellScope.$destroy(); - checkboxSelection: false, + createCellScope(); - reorderable: true, + cellScope.$cell = ctrl.value; + cellScope.$row = ctrl.row; + cellScope.$column = ctrl.column; + cellScope.$$watchers = null; + } - internal: { - offsetX: 0, - offsetY: 0, - innerWidth: 0, - bodyHeight: 300 - } + if (ctrl.column.template) { + content.empty(); + var elm = _angular2.default.element("" + ctrl.column.template.trim() + ""); + content.append($compile(elm)(cellScope)); + } else if (ctrl.column.cellRenderer) { + content.empty(); + var elm = _angular2.default.element(ctrl.column.cellRenderer(cellScope, content)); + content.append($compile(elm)(cellScope)); + } else { + content[0].innerHTML = ctrl.getValue(); + } + }, true); - }; + function createCellScope() { + cellScope = ctrl.options.$outer.$new(false); + cellScope.getValue = ctrl.getValue; + } + } + }; + } + }; + } - var DataTableController = function () { - DataTableController.$inject = ["$scope", "$filter", "$log", "$transclude"]; - function DataTableController($scope, $filter, $log, $transclude) { + var FooterController = function () { + function FooterController($scope) { var _this6 = this; - _classCallCheck(this, DataTableController); + _classCallCheck(this, FooterController); - Object.assign(this, { - $scope: $scope, - $filter: $filter, - $log: $log + this.page = this.paging.offset + 1; + $scope.$watch('footer.paging.offset', function (newVal) { + _this6.offsetChanged(newVal); }); + } - this.defaults(); + _createClass(FooterController, [{ + key: "offsetChanged", + value: function offsetChanged(newVal) { + this.page = newVal + 1; + } + }, { + key: "onPaged", + value: function onPaged(page) { + this.paging.offset = page - 1; + this.onPage({ + offset: this.paging.offset, + size: this.paging.size + }); + } + }]); - this.options.$outer = $scope.$parent; + return FooterController; + }(); - $scope.$watch('dt.options.columns', function (newVal, oldVal) { - _this6.transposeColumnDefaults(); + function FooterDirective() { + return { + restrict: 'E', + controller: FooterController, + controllerAs: 'footer', + scope: true, + bindToController: { + paging: '=', + onPage: '&' + }, + template: "
\n
{{footer.paging.count}} total
\n 1\">\n \n
", + replace: true + }; + } - if (newVal.length !== oldVal.length) { - _this6.adjustColumns(); - } + var PagerController = function () { + function PagerController($scope) { + var _this7 = this; - _this6.calculateColumns(); - }, true); + _classCallCheck(this, PagerController); - var watch = $scope.$watch('dt.rows', function (newVal) { - if (newVal) { - watch(); - _this6.onSorted(); - } + $scope.$watch('pager.count', function (newVal) { + _this7.calcTotalPages(_this7.size, _this7.count); + _this7.getPages(_this7.page || 1); }); - } - - _createClass(DataTableController, [{ - key: "defaults", - value: function defaults() { - var _this7 = this; - this.expanded = this.expanded || {}; + $scope.$watch('pager.size', function (newVal) { + _this7.calcTotalPages(_this7.size, _this7.count); + _this7.getPages(_this7.page || 1); + }); - this.options = angular.extend(angular.copy(TableDefaults), this.options); + $scope.$watch('pager.page', function (newVal) { + if (newVal !== 0 && newVal <= _this7.totalPages) { + _this7.getPages(newVal); + } + }); - angular.forEach(TableDefaults.paging, function (v, k) { - if (!_this7.options.paging[k]) { - _this7.options.paging[k] = v; - } - }); + this.getPages(this.page || 1); + } - if (this.options.selectable && this.options.multiSelect) { - this.selected = this.selected || []; - } + _createClass(PagerController, [{ + key: "calcTotalPages", + value: function calcTotalPages(size, count) { + var count = size < 1 ? 1 : Math.ceil(count / size); + this.totalPages = Math.max(count || 0, 1); } }, { - key: "transposeColumnDefaults", - value: function transposeColumnDefaults() { - for (var i = 0, len = this.options.columns.length; i < len; i++) { - var column = this.options.columns[i]; - column.$id = ObjectId(); - - angular.forEach(ColumnDefaults, function (v, k) { - if (!column.hasOwnProperty(k)) { - column[k] = v; - } + key: "selectPage", + value: function selectPage(num) { + if (num > 0 && num <= this.totalPages) { + this.page = num; + this.onPage({ + page: num }); - - if (column.name && !column.prop) { - column.prop = CamelCase(column.name); - } - - this.options.columns[i] = column; } } }, { - key: "calculateColumns", - value: function calculateColumns() { - var columns = this.options.columns; - this.columnsByPin = ColumnsByPin(columns); - this.columnWidths = ColumnGroupWidths(this.columnsByPin, columns); + key: "prevPage", + value: function prevPage() { + if (this.page > 1) { + this.selectPage(--this.page); + } } }, { - key: "tableCss", - value: function tableCss() { - return { - 'fixed': this.options.scrollbarV, - 'selectable': this.options.selectable, - 'checkboxable': this.options.checkboxSelection - }; + key: "nextPage", + value: function nextPage() { + this.selectPage(++this.page); } }, { - key: "adjustColumns", - value: function adjustColumns(forceIdx) { - var width = this.options.internal.innerWidth - this.options.internal.scrollBarWidth; - - if (this.options.columnMode === 'force') { - ForceFillColumnWidths(this.options.columns, width, forceIdx); - } else if (this.options.columnMode === 'flex') { - AdjustColumnWidths(this.options.columns, width); - } + key: "canPrevious", + value: function canPrevious() { + return this.page > 1; } }, { - key: "calculatePageSize", - value: function calculatePageSize() { - this.options.paging.size = Math.ceil(this.options.internal.bodyHeight / this.options.rowHeight) + 1; + key: "canNext", + value: function canNext() { + return this.page < this.totalPages; } }, { - key: "onSorted", - value: function onSorted() { - if (!this.rows) return; + key: "getPages", + value: function getPages(page) { + var pages = [], + startPage = 1, + endPage = this.totalPages, + maxSize = 5, + isMaxSized = maxSize < this.totalPages; - var sorts = this.options.columns.filter(function (c) { - return c.sort; - }).sort(function (a, b) { - if (a.sortPriority && b.sortPriority) { - if (a.sortPriority > b.sortPriority) return 1; - if (a.sortPriority < b.sortPriority) return -1; - } else if (a.sortPriority) { - return -1; - } else if (b.sortPriority) { - return 1; - } + if (isMaxSized) { + startPage = (Math.ceil(page / maxSize) - 1) * maxSize + 1; + endPage = Math.min(startPage + maxSize - 1, this.totalPages); + } - return 0; - }).map(function (c, i) { - c.sortPriority = i + 1; - return c; - }); + for (var number = startPage; number <= endPage; number++) { + pages.push({ + number: number, + text: number, + active: number === page + }); + } - if (sorts.length) { - this.onSort({ sorts: sorts }); + this.pages = pages; + } + }]); + + return PagerController; + }(); + + function PagerDirective() { + return { + restrict: 'E', + controller: PagerController, + controllerAs: 'pager', + scope: true, + bindToController: { + page: '=', + size: '=', + count: '=', + onPage: '&' + }, + template: "
\n
    \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n {{pg.text}}\n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
\n
", + replace: true + }; + } + + function PopoverDirective($q, $timeout, $templateCache, $compile, PopoverRegistry, PositionHelper, $animate) { + function loadTemplate(template, plain) { + if (!template) { + return ''; + } + + if (_angular2.default.isString(template) && plain) { + return template; + } + + return $templateCache.get(template) || $http.get(template, { cache: true }); + } + + function toBoolean(value) { + if (value && value.length !== 0) { + var v = ("" + value).toLowerCase(); + value = v == 'true'; + } else { + value = false; + } + return value; + } + + return { + restrict: 'A', + scope: true, + replace: false, + link: function link($scope, $element, $attributes) { + $scope.popover = null; + $scope.popoverId = Date.now(); + + $scope.options = { + text: $attributes.popoverText, + template: $attributes.popoverTemplate, + plain: toBoolean($attributes.popoverPlain || false), + placement: $attributes.popoverPlacement || 'right', + alignment: $attributes.popoverAlignment || 'center', + group: $attributes.popoverGroup, + spacing: parseInt($attributes.popoverSpacing) || 0, + showCaret: toBoolean($attributes.popoverPlain || false) + }; + + $element.off('mouseenter', display); + $element.on('mouseenter', display); + $element.off('mouseleave', mouseOut); + $element.on('mouseleave', mouseOut); + + function mouseOut() { + $scope.exitTimeout = $timeout(remove, 500); + } + + function display() { + $timeout.cancel($scope.exitTimeout); + + var elm = document.getElementById("#" + $scope.popoverId); + if ($scope.popover && elm) return; - if (this.options.onSort) { - this.options.onSort(sorts); + if ($scope.options.group) { + PopoverRegistry.removeGroup($scope.options.group, $scope.popoverId); } - var clientSorts = []; - for (var i = 0, len = sorts.length; i < len; i++) { - var c = sorts[i]; - if (c.comparator !== false) { - var dir = c.sort === 'asc' ? '' : '-'; - if (c.sortBy !== undefined) { - clientSorts.push(dir + c.sortBy); - } else { - clientSorts.push(dir + c.prop); + if ($scope.options.text && !$scope.options.template) { + $scope.popover = _angular2.default.element("
"); + + $scope.popover.html($scope.options.text); + _angular2.default.element(document.body).append($scope.popover); + positionPopover($element, $scope.popover, $scope.options); + PopoverRegistry.add($scope.popoverId, { element: $element, popover: $scope.popover, group: $scope.options.group }); + } else { + $q.when(loadTemplate($scope.options.template, $scope.options.plain)).then(function (template) { + if (!_angular2.default.isString(template)) { + if (template.data && _angular2.default.isString(template.data)) { + template = template.data; + } else { + template = ''; + } } - } - } - if (clientSorts.length) { - var _rows; + $scope.popover = _angular2.default.element("
"); - var sortedValues = this.$filter('orderBy')(this.rows, clientSorts); - this.rows.splice(0, this.rows.length); - (_rows = this.rows).push.apply(_rows, _toConsumableArray(sortedValues)); + $scope.popover.html(template); + $compile($scope.popover)($scope); + _angular2.default.element(document.body).append($scope.popover); + positionPopover($element, $scope.popover, $scope.options); + + $scope.popover.off('mouseleave', mouseOut); + $scope.popover.on('mouseleave', mouseOut); + $scope.popover.on('mouseenter', function () { + $timeout.cancel($scope.exitTimeout); + }); + + PopoverRegistry.add($scope.popoverId, { + element: $element, + popover: $scope.popover, + group: $scope.options.group + }); + }); } } - this.options.internal.setYOffset(0); - } - }, { - key: "onTreeToggled", - value: function onTreeToggled(row, cell) { - this.onTreeToggle({ - row: row, - cell: cell - }); - } - }, { - key: "onBodyPage", - value: function onBodyPage(offset, size) { - this.onPage({ - offset: offset, - size: size - }); - } - }, { - key: "onFooterPage", - value: function onFooterPage(offset, size) { - var pageBlockSize = this.options.rowHeight * size, - offsetY = pageBlockSize * offset; + function remove() { + if ($scope.popover) { + $scope.popover.remove(); + } - this.options.internal.setYOffset(offsetY); - } - }, { - key: "onHeaderCheckboxChange", - value: function onHeaderCheckboxChange() { - if (this.rows) { - var matches = this.selected.length === this.rows.length; - this.selected.splice(0, this.selected.length); + $scope.popover = undefined; + PopoverRegistry.remove($scope.popoverId); + } - if (!matches) { - var _selected; + function positionPopover(triggerElement, popover, options) { + $timeout(function () { + var elDimensions = triggerElement[0].getBoundingClientRect(), + popoverDimensions = popover[0].getBoundingClientRect(), + top, + left; + + if (options.placement === 'right') { + left = elDimensions.left + elDimensions.width + options.spacing; + top = PositionHelper.calculateVerticalAlignment(elDimensions, popoverDimensions, options.alignment); + } + if (options.placement === 'left') { + left = elDimensions.left - popoverDimensions.width - options.spacing; + top = PositionHelper.calculateVerticalAlignment(elDimensions, popoverDimensions, options.alignment); + } + if (options.placement === 'top') { + top = elDimensions.top - popoverDimensions.height - options.spacing; + left = PositionHelper.calculateHorizontalAlignment(elDimensions, popoverDimensions, options.alignment); + } + if (options.placement === 'bottom') { + top = elDimensions.top + elDimensions.height + options.spacing; + left = PositionHelper.calculateHorizontalAlignment(elDimensions, popoverDimensions, options.alignment); + } - (_selected = this.selected).push.apply(_selected, _toConsumableArray(this.rows)); + popover.css({ + top: top + 'px', + left: left + 'px' + }); + + if ($scope.options.showCaret) { + addCaret($scope.popover, elDimensions, popoverDimensions); + } + + $animate.addClass($scope.popover, 'popover-animation'); + }, 50); + } + + function addCaret(popoverEl, elDimensions, popoverDimensions) { + var caret = _angular2.default.element(""); + popoverEl.append(caret); + var caretDimensions = caret[0].getBoundingClientRect(); + + var left, top; + if ($scope.options.placement === 'right') { + left = -6; + top = PositionHelper.calculateVerticalCaret(elDimensions, popoverDimensions, caretDimensions, $scope.options.alignment); } + if ($scope.options.placement === 'left') { + left = popoverDimensions.width - 2; + top = PositionHelper.calculateVerticalCaret(elDimensions, popoverDimensions, caretDimensions, $scope.options.alignment); + } + if ($scope.options.placement === 'top') { + top = popoverDimensions.height - 5; + left = PositionHelper.calculateHorizontalCaret(elDimensions, popoverDimensions, caretDimensions, $scope.options.alignment); + } + + if ($scope.options.placement === 'bottom') { + top = -8; + left = PositionHelper.calculateHorizontalCaret(elDimensions, popoverDimensions, caretDimensions, $scope.options.alignment); + } + + caret.css({ + top: top + 'px', + left: left + 'px' + }); } } - }, { - key: "isAllRowsSelected", - value: function isAllRowsSelected() { - if (this.rows) return false; - return this.selected.length === this.rows.length; - } - }, { - key: "onResized", - value: function onResized(column, width) { - var idx = this.options.columns.indexOf(column); - if (idx > -1) { - var column = this.options.columns[idx]; - column.width = width; - column.canAutoResize = false; + }; + } - this.adjustColumns(idx); - this.calculateColumns(); + function PopoverRegistry($animate) { + var popovers = {}; + this.add = function (id, object) { + popovers[id] = object; + }; + this.find = function (id) { + popovers[id]; + }; + this.remove = function (id) { + delete popovers[id]; + }; + this.removeGroup = function (group, currentId) { + angular.forEach(popovers, function (popoverOb, id) { + if (id === currentId) return; + + if (popoverOb.group && popoverOb.group === group) { + $animate.removeClass(popoverOb.popover, 'sw-popover-animate').then(function () { + popoverOb.popover.remove(); + delete popovers[id]; + }); } + }); + }; + } - if (this.onColumnResize) { - this.onColumnResize({ - column: column, - width: width - }); + function PositionHelper() { + return { + + calculateVerticalAlignment: function calculateVerticalAlignment(elDimensions, popoverDimensions, alignment) { + if (alignment === 'top') { + return elDimensions.top; + } + if (alignment === 'bottom') { + return elDimensions.top + elDimensions.height - popoverDimensions.height; + } + if (alignment === 'center') { + return elDimensions.top + elDimensions.height / 2 - popoverDimensions.height / 2; + } + }, + + calculateVerticalCaret: function calculateVerticalCaret(elDimensions, popoverDimensions, caretDimensions, alignment) { + if (alignment === 'top') { + return elDimensions.height / 2 - caretDimensions.height / 2 - 1; + } + if (alignment === 'bottom') { + return popoverDimensions.height - elDimensions.height / 2 - caretDimensions.height / 2 - 1; + } + if (alignment === 'center') { + return popoverDimensions.height / 2 - caretDimensions.height / 2 - 1; + } + }, + + calculateHorizontalCaret: function calculateHorizontalCaret(elDimensions, popoverDimensions, caretDimensions, alignment) { + if (alignment === 'left') { + return elDimensions.width / 2 - caretDimensions.height / 2 - 1; + } + if (alignment === 'right') { + return popoverDimensions.width - elDimensions.width / 2 - caretDimensions.height / 2 - 1; + } + if (alignment === 'center') { + return popoverDimensions.width / 2 - caretDimensions.height / 2 - 1; + } + }, + + calculateHorizontalAlignment: function calculateHorizontalAlignment(elDimensions, popoverDimensions, alignment) { + if (alignment === 'left') { + return elDimensions.left; + } + if (alignment === 'right') { + return elDimensions.left + elDimensions.width - popoverDimensions.width; + } + if (alignment === 'center') { + return elDimensions.left + elDimensions.width / 2 - popoverDimensions.width / 2; } } - }, { - key: "onSelected", - value: function onSelected(rows) { - this.onSelect({ - rows: rows + + }; + } + + var popover = _angular2.default.module('dt.popover', []).service('PopoverRegistry', PopoverRegistry).factory('PositionHelper', PositionHelper).directive('popover', PopoverDirective); + + var MenuController = function () { + function MenuController($scope, $timeout) { + _classCallCheck(this, MenuController); + + this.$scope = $scope; + } + + _createClass(MenuController, [{ + key: "getColumnIndex", + value: function getColumnIndex(model) { + return this.$scope.current.findIndex(function (col) { + return model.name == col.name; }); } }, { - key: "onRowClicked", - value: function onRowClicked(row) { - this.onRowClick({ - row: row - }); + key: "isChecked", + value: function isChecked(model) { + return this.getColumnIndex(model) > -1; } }, { - key: "onRowDblClicked", - value: function onRowDblClicked(row) { - this.onRowDblClick({ - row: row - }); + key: "onCheck", + value: function onCheck(model) { + var idx = this.getColumnIndex(model); + if (idx === -1) { + this.$scope.current.push(model); + } else { + this.$scope.current.splice(idx, 1); + } } }]); - return DataTableController; + return MenuController; }(); - function DataTableDirective($window, $timeout, $parse) { + function MenuDirective() { return { restrict: 'E', - replace: true, - controller: DataTableController, - scope: true, - bindToController: { - options: '=', - rows: '=', - selected: '=?', - expanded: '=?', - onSelect: '&', - onSort: '&', - onTreeToggle: '&', - onPage: '&', - onRowClick: '&', - onRowDblClick: '&', - onColumnResize: '&' + controller: 'MenuController', + controllerAs: 'dtm', + scope: { + current: '=', + available: '=' }, - controllerAs: 'dt', - template: function template(element) { - var columns = element[0].getElementsByTagName('column'), - id = ObjectId(); - DataTableService.saveColumns(id, columns); + template: "
\n \n Configure Columns\n \n
\n
\n \n
\n
    \n
  • \n \n
  • \n
\n
\n
" + }; + } - return "
\n \n \n \n \n \n \n
"; - }, - compile: function compile(tElem, tAttrs) { - return { - pre: function pre($scope, $elm, $attrs, ctrl) { - DataTableService.buildColumns($scope, $parse); + var DropdownController = function () { + function DropdownController($scope) { + _classCallCheck(this, DropdownController); - var id = $elm.attr('data-column-id'), - columns = DataTableService.columns[id]; - if (columns) { - ctrl.options.columns = columns; - } + $scope.open = false; + } - ctrl.transposeColumnDefaults(); - ctrl.options.internal.scrollBarWidth = ScrollbarWidth(); + _createClass(DropdownController, [{ + key: "toggle", + value: function toggle(scope) { + scope.open = !scope.open; + } + }]); - function resize() { - var rect = $elm[0].getBoundingClientRect(); + return DropdownController; + }(); - ctrl.options.internal.innerWidth = Math.floor(rect.width); + function DropdownDirective($document, $timeout) { + return { + restrict: 'C', + controller: 'DropdownController', + link: function link($scope, $elm, $attrs) { - if (ctrl.options.scrollbarV) { - var height = rect.height; + function closeDropdown(ev) { + if ($elm[0].contains(ev.target)) { + return; + } - if (ctrl.options.headerHeight) { - height = height - ctrl.options.headerHeight; - } + $timeout(function () { + $scope.open = false; + off(); + }); + } - if (ctrl.options.footerHeight) { - height = height - ctrl.options.footerHeight; - } + function keydown(ev) { + if (ev.which === 27) { + $timeout(function () { + $scope.open = false; + off(); + }); + } + } - ctrl.options.internal.bodyHeight = height; - ctrl.calculatePageSize(); - } + function off() { + $document.unbind('click', closeDropdown); + $document.unbind('keydown', keydown); + } - ctrl.adjustColumns(); - }; + $scope.$watch('open', function (newVal) { + if (newVal) { + $document.bind('click', closeDropdown); + $document.bind('keydown', keydown); + } + }); + } + }; + } - $window.addEventListener('resize', throttle(function () { - $timeout(resize); - })); + function DropdownToggleDirective($timeout) { + return { + restrict: 'C', + controller: 'DropdownController', + require: '?^dropdown', + link: function link($scope, $elm, $attrs, ctrl) { - var checkVisibility = function checkVisibility() { - var bounds = $elm[0].getBoundingClientRect(), - visible = bounds.width && bounds.height; - if (visible) resize();else $timeout(checkVisibility, 100); - }; - checkVisibility(); + function toggleClick(event) { + event.preventDefault(); + $timeout(function () { + ctrl.toggle($scope); + }); + } - $elm.addClass('dt-loaded'); + function toggleDestroy() { + $elm.unbind('click', toggleClick); + } - $scope.$on('$destroy', function () { - angular.element($window).off('resize'); - }); - } - }; + $elm.bind('click', toggleClick); + $scope.$on('$destroy', toggleDestroy); + } + }; + } + + function DropdownMenuDirective($animate) { + return { + restrict: 'C', + require: '?^dropdown', + link: function link($scope, $elm, $attrs, ctrl) { + $scope.$watch('open', function () { + $animate[$scope.open ? 'addClass' : 'removeClass']($elm, 'ddm-open'); + }); } }; } - var dataTable = angular.module('data-table', []).directive('dtable', DataTableDirective).directive('resizable', ResizableDirective).directive('sortable', SortableDirective).directive('dtHeader', HeaderDirective).directive('dtHeaderCell', HeaderCellDirective).directive('dtBody', BodyDirective).directive('dtScroller', ScrollerDirective).directive('dtSeletion', SelectionDirective).directive('dtRow', RowDirective).directive('dtGroupRow', GroupRowDirective).directive('dtCell', CellDirective).directive('dtFooter', FooterDirective).directive('dtPager', PagerDirective); + var dropdown = _angular2.default.module('dt.dropdown', []).controller('DropdownController', DropdownController).directive('dropdown', DropdownDirective).directive('dropdownToggle', DropdownToggleDirective).directive('dropdownMenu', DropdownMenuDirective); + + var menu = _angular2.default.module('dt.menu', [dropdown.name]).controller('MenuController', MenuController).directive('dtm', MenuDirective); + + var dataTable = _angular2.default.module('data-table', []).directive('dtable', DataTableDirective).directive('resizable', ResizableDirective).directive('sortable', SortableDirective).directive('dtHeader', HeaderDirective).directive('dtHeaderCell', HeaderCellDirective).directive('dtBody', BodyDirective).directive('dtScroller', ScrollerDirective).directive('dtSeletion', SelectionDirective).directive('dtRow', RowDirective).directive('dtGroupRow', GroupRowDirective).directive('dtCell', CellDirective).directive('dtFooter', FooterDirective).directive('dtPager', PagerDirective); + exports.dtPopover = popover; + exports.dtMenu = menu; exports.default = dataTable; }); \ No newline at end of file diff --git a/release/dataTable.min.js b/release/dataTable.min.js index a01c68b..76e21e8 100644 --- a/release/dataTable.min.js +++ b/release/dataTable.min.js @@ -4,5 +4,5 @@ * @link http://swimlane.com/ * @license */ -!function(e,t){if("function"==typeof define&&define.amd)define("DataTable",["exports"],t);else if("undefined"!=typeof exports)t(exports);else{var n={exports:{}};t(n.exports),e.DataTable=n.exports}}(this,function(e){"use strict";function t(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t\n
    \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n {{pg.text}}\n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
\n ',replace:!0}}function i(){return{restrict:"E",controller:B,controllerAs:"footer",scope:!0,bindToController:{paging:"=",onPage:"&"},template:'',replace:!0}}function r(e,t,n,o){return{restrict:"E",controller:G,scope:!0,controllerAs:"cell",bindToController:{options:"=",value:"=",selected:"=",column:"=",row:"=",expanded:"=",hasChildren:"=",onTreeToggle:"&",onCheckboxChange:"&"},template:'
\n \n \n \n
',replace:!0,compile:function(){return{pre:function(e,n,o,i){function r(){s=i.options.$outer.$new(!1),s.getValue=i.getValue}var s,l=angular.element(n[0].querySelector(".dt-cell-content"));(i.column.template||i.column.cellRenderer)&&r(),e.$watch("cell.row",function(){if(s&&(s.$destroy(),r(),s.$cell=i.value,s.$row=i.row,s.$column=i.column,s.$$watchers=null),i.column.template){l.empty();var e=angular.element(""+i.column.template.trim()+"");l.append(t(e)(s))}else if(i.column.cellRenderer){l.empty();var e=angular.element(i.column.cellRenderer(s,l));l.append(t(e)(s))}else l[0].innerHTML=i.getValue()},!0)}}}}}function s(e){return e=e.replace(/[^a-zA-Z0-9 ]/g," "),e=e.replace(/([a-z](?=[A-Z]))/g,"$1 "),e=e.replace(/([^a-zA-Z0-9 ])|^[0-9]+/g,"").trim().toLowerCase(),e=e.replace(/([ 0-9]+)([a-zA-Z])/g,function(e,t,n){return t.trim()+n.toUpperCase()})}function l(e){var t=s(e);return D[t]||(void 0!==H[M.css+e]?D[t]=M.css+e:void 0!==H[e]&&(D[t]=e)),D[t]}function a(e,t,n){V?!I&&j?(e[N]="translate3d("+t+"px, "+n+"px, 0)",e[O]="hidden"):e[s(N)]="translate("+t+"px, "+n+"px)":(e.top=n+"px",e.left=t+"px")}function c(){return{restrict:"E",controller:F,controllerAs:"group",bindToController:{row:"=",onGroupToggle:"&",expanded:"=",options:"="},scope:!0,replace:!0,template:'\n
\n \n \n \n \n
',link:function(e,t,n,o){a(t[0].style,0,o.row.$$index*o.options.rowHeight),o.options.internal.styleTranslator.register(e.$index,t)}}}function d(e,t){if(!e||!t)return e;var n=e,o=t.split(".");if(o.length)for(var i=0,r=o.length;r>i;i++)n=n[o[i]];return n}function u(){return{restrict:"E",controller:U,controllerAs:"rowCtrl",scope:!0,bindToController:{row:"=",columns:"=",columnWidths:"=",expanded:"=",selected:"=",hasChildren:"=",options:"=",onCheckboxChange:"&",onTreeToggle:"&"},link:function(e,t,n,o){o.row&&a(t[0].style,0,o.row.$$index*o.options.rowHeight),o.options.internal.styleTranslator.register(e.$index,t)},template:'\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
',replace:!0}}function h(){return{controller:q,restrict:"A",require:"^dtBody",controllerAs:"selCtrl"}}function p(e,t){return{restrict:"E",require:"^dtBody",transclude:!0,replace:!0,template:'
',link:function(e,t,n,o){function i(){o.options.internal.offsetY=l,o.options.internal.offsetX=a,o.updatePage(),o.options.scrollbarV&&o.getRows(),o.options.$outer.$digest(),s=!1}function r(){s||(Y(i),s=!0)}var s=!1,l=0,a=0,c=t.parent();o.options.internal.styleTranslator=new Z(o.options.rowHeight),o.options.internal.setYOffset=function(e){c[0].scrollTop=e},c.on("scroll",function(e){l=this.scrollTop,a=this.scrollLeft,r()}),e.$on("$destroy",function(){c.off("scroll")}),e.scrollerStyles=function(){return o.options.scrollbarV?{height:o.count*o.options.rowHeight+"px"}:void 0}}}}function g(e){return{restrict:"E",controller:_,controllerAs:"body",bindToController:{columns:"=",columnWidths:"=",rows:"=",options:"=",selected:"=?",expanded:"=?",onPage:"&",onTreeToggle:"&",onSelect:"&",onRowClick:"&",onRowDblClick:"&"},scope:!0,template:'\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n
'}}function f(e,t){return"single"===e?"asc"===t?"desc":"asc":t?"asc"===t?"desc":void 0:"asc"}function m(e){return{restrict:"E",controller:K,controllerAs:"hcell",scope:!0,bindToController:{options:"=",column:"=",onCheckboxChange:"&",onSort:"&",sortType:"=",onResize:"&",selected:"="},replace:!0,template:'
\n
\n \n \n \n \n
\n
',compile:function(){return{pre:function(t,n,o,i){var r=n[0].querySelector(".dt-header-cell-label"),s=void 0;if((i.column.headerTemplate||i.column.headerRenderer)&&(s=i.options.$outer.$new(!1),s.$header=i.column.name,s.$index=t.$index),i.column.headerTemplate){var l=angular.element(""+i.column.headerTemplate.trim()+"");angular.element(r).append(e(l)(s))}else if(i.column.headerRenderer){var a=angular.element(i.column.headerRenderer(n));angular.element(r).append(e(a)(s)[0])}else{var c=i.column.name;void 0!==c&&null!==c||(c=""),r.textContent=c}}}}}}function v(e){return{restrict:"E",controller:J,controllerAs:"header",scope:!0,bindToController:{options:"=",columns:"=",columnWidths:"=",onSort:"&",onResize:"&",onCheckboxChange:"&"},template:'\n
\n\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
',replace:!0,link:function(t,n,o,i){t.columnsResorted=function(t,n){var o=r(n),s=angular.element(t.currentTarget),l=-1;angular.forEach(s.children(),function(e,t){n===angular.element(e).attr("data-id")&&(l=t)}),e(function(){angular.forEach(i.columns,function(e){var t=e.indexOf(o);if(t>-1){var n=e[l],r=i.options.columns.indexOf(n),s=i.options.columns.indexOf(o);return i.options.columns.splice(s,1),i.options.columns.splice(r,0,o),!1}})})};var r=function(e){var t=i.columns.left.concat(i.columns.center).concat(i.columns.right);return t.find(function(t){return t.$id===e})}}}}function w(e){return{restrict:"A",scope:{isSortable:"=sortable",onSortableSort:"&"},link:function(e,t,n){function o(e,t){if(e.parentNode==t.parentNode)for(var n=e;n;n=n.previousSibling)if(n===t)return!0;return!1}function i(e){var t=e.target;o(l,t)?t.parentNode.insertBefore(l,t):t.nextSibling&&t.hasAttribute("draggable")&&t.parentNode.insertBefore(l,t.nextSibling.nextSibling)}function r(n){n.preventDefault(),l.classList.remove("dt-clone"),t.off("dragend",r),t.off("dragenter",i),a!==l.nextSibling&&e.onSortableSort({event:n,columnId:angular.element(l).attr("data-id")})}function s(n){return e.isSortable?(n=n.originalEvent||n,l=n.target,a=l.nextSibling,l.classList.add("dt-clone"),n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("Text",l.textContent),t.on("dragenter",i),void t.on("dragend",r)):!1}var l,a;t[0];t.on("dragstart",s),e.$on("$destroy",function(){t.off("dragstart",s)})}}}function y(e,t){return{restrict:"A",scope:{isResizable:"=resizable",minWidth:"=",maxWidth:"=",onResize:"&"},link:function(n,o,i){function r(e){e=e.originalEvent||e;var t=c[0].clientWidth,o=e.movementX||e.mozMovementX||e.screenX-l,i=t+(o||0);l=e.screenX,(!n.minWidth||i>=n.minWidth)&&(!n.maxWidth||i<=n.maxWidth)&&c.css({width:i+"px"})}function s(){n.onResize&&t(function(){var e=c[0].clientWidth;e'),c=o.parent();a.on("mousedown",function(t){return o[0].classList.contains("resizable")?(t.stopPropagation(),t.preventDefault(),e.on("mousemove",r),void e.on("mouseup",s)):!1}),o.append(a)}}}function b(e,t,n){var o,i,r,s=null,l=0;n||(n={});var a=function(){l=n.leading===!1?0:new Date,s=null,r=e.apply(o,i)};return function(){var c=new Date;l||n.leading!==!1||(l=c);var d=t-(c-l);return o=this,i=arguments,0>=d?(clearTimeout(s),s=null,l=c,r=e.apply(o,i)):s||n.trailing===!1||(s=setTimeout(a,d)),r}}function C(){var e=document.createElement("div");e.style.visibility="hidden",e.style.width="100px",e.style.msOverflowStyle="scrollbar",document.body.appendChild(e);var t=e.offsetWidth;e.style.overflow="scroll";var n=document.createElement("div");n.style.width="100%",e.appendChild(n);var o=n.offsetWidth;return e.parentNode.removeChild(e),t-o}function x(){var e=((new Date).getTime()/1e3|0).toString(16);return e+"xxxxxxxxxxxxxxxx".replace(/[x]/g,function(){return(16*Math.random()|0).toString(16)}).toLowerCase()}function k(e,t,n){angular.forEach(e,function(e){e.forEach(function(e){e.canAutoResize?e.width=0:(t-=e.width,n-=e.flexGrow)})});var o={},i=t,r=function(){var t=i/n;i=0,angular.forEach(e,function(e){e.forEach(function(e,n){if(e.canAutoResize&&!o[n]){var r=e.width+e.flexGrow*t;void 0!==e.minWidth&&rn;n++){var i=e[n];i.frozenLeft?t.left.push(i):i.frozenRight?t.right.push(i):t.center.push(i)}return t}function R(e){var t=0,n=!0,o=!1,i=void 0;try{for(var r,s=e[Symbol.iterator]();!(n=(r=s.next()).done);n=!0){var l=r.value;t+=l.flexGrow||0}}catch(a){o=!0,i=a}finally{try{!n&&s["return"]&&s["return"]()}finally{if(o)throw i}}return t}function T(e,t){var n=0;return e.forEach(function(e){var o=t&&e[t];n+=o?e[t]:e.width}),n}function P(e,t){var n=T(e),o=R(e),i=$(e);n!==t&&k(i,t,o)}function S(e,t,n){var o=0,i=n>-1?e.slice(n,e.length).filter(function(e){return e.canAutoResize}):e.filter(function(e){return e.canAutoResize});e.forEach(function(e){o+=e.canAutoResize?e.$$oldWidth||e.width:e.width});var r=t-o,s=r/i.length,l=o>t;i.forEach(function(e){if(l)e.width=e.$$oldWidth||e.width;else{e.$$oldWidth||(e.$$oldWidth=e.width);var t=e.$$oldWidth+s;e.minWith&&te.maxWidth?e.width=e.maxWidth:e.width=t}})}function z(e,t){return{left:T(e.left),center:T(e.center),right:T(e.right),total:T(t)}}function W(e,t,n){return{restrict:"E",replace:!0,controller:ne,scope:!0,bindToController:{options:"=",rows:"=",selected:"=?",expanded:"=?",onSelect:"&",onSort:"&",onTreeToggle:"&",onPage:"&",onRowClick:"&",onRowDblClick:"&",onColumnResize:"&"},controllerAs:"dt",template:function(e){var t=e[0].getElementsByTagName("column"),n=x();return Q.saveColumns(n,t),'
\n \n \n \n \n \n \n
'},compile:function(o,i){return{pre:function(o,i,r,s){function l(){var e=i[0].getBoundingClientRect();if(s.options.internal.innerWidth=Math.floor(e.width),s.options.scrollbarV){var t=e.height;s.options.headerHeight&&(t-=s.options.headerHeight),s.options.footerHeight&&(t-=s.options.footerHeight),s.options.internal.bodyHeight=t,s.calculatePageSize()}s.adjustColumns()}Q.buildColumns(o,n);var a=i.attr("data-column-id"),c=Q.columns[a];c&&(s.options.columns=c),s.transposeColumnDefaults(),s.options.internal.scrollBarWidth=C(),e.addEventListener("resize",b(function(){t(l)}));var d=function u(){var e=i[0].getBoundingClientRect(),n=e.width&&e.height;n?l():t(u,100)};d(),i.addClass("dt-loaded"),o.$on("$destroy",function(){angular.element(e).off("resize")})}}}}}W.$inject=["$window","$timeout","$parse"],y.$inject=["$document","$timeout"],w.$inject=["$timeout"],v.$inject=["$timeout"],m.$inject=["$compile"],g.$inject=["$timeout"],p.$inject=["$timeout","$rootScope"],r.$inject=["$rootScope","$compile","$log","$timeout"],Object.defineProperty(e,"__esModule",{value:!0});var E=function(){function e(e,t){for(var n=0;nn;n++)if(i(this[n],n,this)===!0)return"find"===e?this[n]:n;return"find"!==e?-1:void 0})}for(var t in{find:1,findIndex:1})e(t)}();var A=function(){function e(t){var o=this;n(this,e),t.$watch("pager.count",function(e){o.calcTotalPages(o.size,o.count),o.getPages(o.page||1)}),t.$watch("pager.size",function(e){o.calcTotalPages(o.size,o.count),o.getPages(o.page||1)}),t.$watch("pager.page",function(e){0!==e&&e<=o.totalPages&&o.getPages(e)}),this.getPages(this.page||1)}return e.$inject=["$scope"],E(e,[{key:"calcTotalPages",value:function(e,t){var t=1>e?1:Math.ceil(t/e);this.totalPages=Math.max(t||0,1)}},{key:"selectPage",value:function(e){e>0&&e<=this.totalPages&&(this.page=e,this.onPage({page:e}))}},{key:"prevPage",value:function(){this.page>1&&this.selectPage(--this.page)}},{key:"nextPage",value:function(){this.selectPage(++this.page)}},{key:"canPrevious",value:function(){return this.page>1}},{key:"canNext",value:function(){return this.page=s;s++)t.push({number:s,text:s,active:s===e});this.pages=t}}]),e}(),B=function(){function e(t){var o=this;n(this,e),this.page=this.paging.offset+1,t.$watch("footer.paging.offset",function(e){o.offsetChanged(e)})}return e.$inject=["$scope"],E(e,[{key:"offsetChanged",value:function(e){this.page=e+1}},{key:"onPaged",value:function(e){this.paging.offset=e-1,this.onPage({offset:this.paging.offset,size:this.paging.size})}}]),e}(),G=function(){function e(){n(this,e)}return E(e,[{key:"styles",value:function(){return{width:this.column.width+"px","min-width":this.column.width+"px"}}},{key:"cellClass",value:function(){var e={"dt-tree-col":this.column.isTreeColumn};return this.column.className&&(e[this.column.className]=!0),e}},{key:"treeClass",value:function(){return{"dt-tree-toggle":!0,"icon-right":!this.expanded,"icon-down":this.expanded}}},{key:"onTreeToggled",value:function(e){e.stopPropagation(),this.expanded=!this.expanded,this.onTreeToggle({cell:{value:this.value,column:this.column,expanded:this.expanded}})}},{key:"onCheckboxChanged",value:function(e){e.stopPropagation(),this.onCheckboxChange({$event:e})}},{key:"getValue",value:function(){var e=this.column.cellDataGetter?this.column.cellDataGetter(this.value):this.value;return void 0!==e&&null!==e||(e=""),e}}]),e}(),D={},H=document.createElement("div").style,M=function(){var e=window.getComputedStyle(document.documentElement,""),t=(Array.prototype.slice.call(e).join("").match(/-(moz|webkit|ms)-/)||""===e.OLink&&["","o"])[1],n="WebKit|Moz|MS|O".match(new RegExp("("+t+")","i"))[1];return{dom:n,lowercase:t,css:"-"+t+"-",js:t[0].toUpperCase()+t.substr(1)}}(),N=l("transform"),O=l("backfaceVisibility"),V=!!l("transform"),j=!!l("perspective"),L=window.navigator.userAgent,I=/Safari\//.test(L)&&!/Chrome\//.test(L),F=function(){function e(){n(this,e)}return E(e,[{key:"onGroupToggled",value:function(e){e.stopPropagation(),this.onGroupToggle({group:this.row})}},{key:"treeClass",value:function(){return{"dt-tree-toggle":!0,"icon-right":!this.expanded,"icon-down":this.expanded}}}]),e}(),U=function(){function e(){n(this,e)}return E(e,[{key:"getValue",value:function(e){return e.prop?d(this.row,e.prop):""}},{key:"onTreeToggled",value:function(e){this.onTreeToggle({cell:e,row:this.row})}},{key:"stylesByGroup",value:function(e){var t={width:this.columnWidths[e]+"px"};if("left"===e)a(t,this.options.internal.offsetX,0);else if("right"===e){var n=-1*(this.columnWidths.total-this.options.internal.innerWidth-this.options.internal.offsetX+this.options.internal.scrollBarWidth);a(t,n,0)}return t}},{key:"onCheckboxChanged",value:function(e){this.onCheckboxChange({$event:e,row:this.row})}}]),e}(),X={BACKSPACE:8,TAB:9,RETURN:13,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46,COMMA:188,PERIOD:190,A:65,Z:90,ZERO:48,NUMPAD_0:96,NUMPAD_9:105},q=function(){function e(t){n(this,e),this.body=t.body,this.options=t.body.options,this.selected=t.body.selected}return e.$inject=["$scope"],E(e,[{key:"keyDown",value:function(e,t,n){if(X[e.keyCode]&&e.preventDefault(),e.keyCode===X.DOWN){var o=e.target.nextElementSibling;o&&o.focus()}else if(e.keyCode===X.UP){var i=e.target.previousElementSibling;i&&i.focus()}else e.keyCode===X.RETURN&&this.selectRow(t,n)}},{key:"rowClicked",value:function(e,t,n){this.options.checkboxSelection||this.selectRow(e,t,n),this.body.onRowClick({row:n})}},{key:"rowDblClicked",value:function(e,t,n){this.options.checkboxSelection||(e.preventDefault(),this.selectRow(e,t,n)),this.body.onRowDblClick({row:n})}},{key:"onCheckboxChange",value:function(e,t,n){this.selectRow(e,t,n)}},{key:"selectRow",value:function(e,t,n){if(this.options.selectable)if(this.options.multiSelect){var o=(e.ctrlKey||e.metaKey,e.shiftKey);if(o)this.selectRowsBetween(t,n);else{var i=this.selected.indexOf(n);i>-1?this.selected.splice(i,1):(this.options.multiSelectOnShift&&1===this.selected.length&&this.selected.splice(0,1),this.selected.push(n),this.body.onSelect({rows:[n]}))}this.prevIndex=t}else this.selected=n,this.body.onSelect({rows:[n]})}},{key:"selectRowsBetween",value:function(e){for(var t=eo;o++){var r=this.body.rows[o],s=o>=this.prevIndex&&e>=o,l=o<=this.prevIndex&&o>=e,a={};if(a=t?{start:e,end:this.prevIndex-e}:{start:this.prevIndex,end:e+1},t&&l||!t&&s){var c=this.selected.indexOf(r);if(t&&c>-1){this.selected.splice(c,1);continue}o>=a.start&&ot)&&(i.getRows(),r=!0)}),t.$watch("body.options.paging.count",function(e){i.count=e,i.updatePage()}),t.$watch("body.options.paging.offset",function(e){i.options.paging.size&&i.onPage({offset:e,size:i.options.paging.size})})}}return e.$inject=["$scope","$timeout"],E(e,[{key:"rowsUpdated",value:function(e,n){if(e)if(this.options.paging.externalPaging||(this.options.paging.count=e.length),this.count=this.options.paging.count,(this.treeColumn||this.groupColumn)&&this.buildRowsByGroup(),this.options.scrollbarV){var o=e&&n&&(e.length===n.length||e.lengtho?Math.floor(o):o>n?Math.ceil(o):e,isNaN(o)||(this.options.paging.offset=o)}},{key:"calculateDepth",value:function(e){var t=arguments.length<=1||void 0===arguments[1]?0:arguments[1],n=this.treeColumn?this.treeColumn.relationProp:this.groupColumn.prop,o=this.treeColumn.prop;if(!e[n])return t;if(e.$$depth)return e.$$depth+t;var i=this.index[e[n]];if(i)return t+=1,this.calculateDepth(i,t);for(var r=0,s=this.rows.length;s>r;r++){var l=this.rows[r];if(l[o]==e[n])return t+=1,this.calculateDepth(l,t)}return t}},{key:"buildRowsByGroup",value:function(){this.index={},this.rowsByGroup={};for(var e=this.treeColumn?this.treeColumn.relationProp:this.groupColumn.prop,t=0,n=this.rows.length;n>t;t++){var o=this.rows[t],i=o[e];if(i&&(this.rowsByGroup[i]?this.rowsByGroup[i].push(o):this.rowsByGroup[i]=[o]),this.treeColumn){var r=this.treeColumn.prop;if(this.index[o[r]]=o,void 0===o[e])o.$$depth=0;else{var s=this.index[o[e]];if(void 0===s)for(var l=0;n>l;l++)if(this.rows[l][r]==i){s=this.rows[l]; -break}void 0===s.$$depth&&(s.$$depth=this.calculateDepth(s)),o.$$depth=s.$$depth+1,s.$$children?s.$$children.push(o[r]):s.$$children=[o[r]]}}}}},{key:"buildGroups",value:function(){var e=this,n=[];return angular.forEach(this.rowsByGroup,function(o,i){n.push({name:i,group:!0}),e.expanded[i]&&n.push.apply(n,t(o))}),n}},{key:"isSelected",value:function(e){var t=!1;return this.options.selectable&&(t=this.options.multiSelect?this.selected.indexOf(e)>-1:this.selected===e),t}},{key:"buildTree",value:function(){function e(t,o,i){t.forEach(function(t){var r=t[n.treeColumn.relationProp],s=t[n.treeColumn.prop],l=n.rowsByGroup[s],a=n.expanded[s];(i>0||!r)&&(o.push(t),l&&l.length>0&&a&&e(l,o,i+1))})}var t=[],n=this;return e(this.rows,t,0),t}},{key:"getRows",value:function(e){if((this.treeColumn||this.groupColumn)&&!this.rowsByGroup)return!1;var t;this.treeColumn?(t=this.treeTemp||[],!e&&this.treeTemp||(this.treeTemp=t=this.buildTree(),this.count=t.length,this.tempRows.splice(0,this.tempRows.length))):this.groupColumn?(t=this.groupsTemp||[],!e&&this.groupsTemp||(this.groupsTemp=t=this.buildGroups(),this.count=t.length)):(t=this.rows,e===!0&&this.tempRows.splice(0,this.tempRows.length));var n=0,o=this.getFirstLastIndexes(),i=o.first;for(this.tempRows.splice(0,o.last-o.first);ie;e++){var n=this.options.columns[e];n.$id=x(),angular.forEach(ee,function(e,t){n.hasOwnProperty(t)||(n[t]=e)}),n.name&&!n.prop&&(n.prop=s(n.name)),this.options.columns[e]=n}}},{key:"calculateColumns",value:function(){var e=this.options.columns;this.columnsByPin=$(e),this.columnWidths=z(this.columnsByPin,e)}},{key:"tableCss",value:function(){return{fixed:this.options.scrollbarV,selectable:this.options.selectable,checkboxable:this.options.checkboxSelection}}},{key:"adjustColumns",value:function(e){var t=this.options.internal.innerWidth-this.options.internal.scrollBarWidth;"force"===this.options.columnMode?S(this.options.columns,t,e):"flex"===this.options.columnMode&&P(this.options.columns,t)}},{key:"calculatePageSize",value:function(){this.options.paging.size=Math.ceil(this.options.internal.bodyHeight/this.options.rowHeight)+1}},{key:"onSorted",value:function(){if(this.rows){var e=this.options.columns.filter(function(e){return e.sort}).sort(function(e,t){if(e.sortPriority&&t.sortPriority){if(e.sortPriority>t.sortPriority)return 1;if(e.sortPriorityo;o++){var r=e[o];if(r.comparator!==!1){var s="asc"===r.sort?"":"-";void 0!==r.sortBy?n.push(s+r.sortBy):n.push(s+r.prop)}}if(n.length){var l,a=this.$filter("orderBy")(this.rows,n);this.rows.splice(0,this.rows.length),(l=this.rows).push.apply(l,t(a))}}this.options.internal.setYOffset(0)}}},{key:"onTreeToggled",value:function(e,t){this.onTreeToggle({row:e,cell:t})}},{key:"onBodyPage",value:function(e,t){this.onPage({offset:e,size:t})}},{key:"onFooterPage",value:function(e,t){var n=this.options.rowHeight*t,o=n*e;this.options.internal.setYOffset(o)}},{key:"onHeaderCheckboxChange",value:function(){if(this.rows){var e=this.selected.length===this.rows.length;if(this.selected.splice(0,this.selected.length),!e){var n;(n=this.selected).push.apply(n,t(this.rows))}}}},{key:"isAllRowsSelected",value:function(){return this.rows?!1:this.selected.length===this.rows.length}},{key:"onResized",value:function(e,t){var n=this.options.columns.indexOf(e);if(n>-1){var e=this.options.columns[n];e.width=t,e.canAutoResize=!1,this.adjustColumns(n),this.calculateColumns()}this.onColumnResize&&this.onColumnResize({column:e,width:t})}},{key:"onSelected",value:function(e){this.onSelect({rows:e})}},{key:"onRowClicked",value:function(e){this.onRowClick({row:e})}},{key:"onRowDblClicked",value:function(e){this.onRowDblClick({row:e})}}]),e}(),oe=angular.module("data-table",[]).directive("dtable",W).directive("resizable",y).directive("sortable",w).directive("dtHeader",v).directive("dtHeaderCell",m).directive("dtBody",g).directive("dtScroller",p).directive("dtSeletion",h).directive("dtRow",u).directive("dtGroupRow",c).directive("dtCell",r).directive("dtFooter",i).directive("dtPager",o);e["default"]=oe}); \ No newline at end of file +!function(e,t){if("function"==typeof define&&define.amd)define("DataTable",["exports","angular"],t);else if("undefined"!=typeof exports)t(exports,require("angular"));else{var n={exports:{}};t(n.exports,e.angular),e.DataTable=n.exports}}(this,function(e,t){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function o(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t=n.minWidth)&&(!n.maxWidth||i<=n.maxWidth)&&c.css({width:i+"px"})}function l(){n.onResize&&t(function(){var e=c[0].clientWidth;e'),c=o.parent();a.on("mousedown",function(t){return!!o[0].classList.contains("resizable")&&(t.stopPropagation(),t.preventDefault(),e.on("mousemove",r),void e.on("mouseup",l))}),o.append(a)}}}function l(e){return{restrict:"A",scope:{isSortable:"=sortable",onSortableSort:"&"},link:function(e,t,n){function o(e,t){if(e.parentNode==t.parentNode)for(var n=e;n;n=n.previousSibling)if(n===t)return!0;return!1}function i(e){var t=e.target;o(s,t)?t.parentNode.insertBefore(s,t):t.nextSibling&&t.hasAttribute("draggable")&&t.parentNode.insertBefore(s,t.nextSibling.nextSibling)}function r(n){n.preventDefault(),s.classList.remove("dt-clone"),t.off("dragend",r),t.off("dragenter",i),a!==s.nextSibling&&e.onSortableSort({event:n,columnId:N.default.element(s).attr("data-id")})}function l(n){return!!e.isSortable&&(n=n.originalEvent||n,s=n.target,a=s.nextSibling,s.classList.add("dt-clone"),n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("Text",s.textContent),t.on("dragenter",i),void t.on("dragend",r))}var s,a;t[0];t.on("dragstart",l),e.$on("$destroy",function(){t.off("dragstart",l)})}}}function s(){var e=((new Date).getTime()/1e3|0).toString(16);return e+"xxxxxxxxxxxxxxxx".replace(/[x]/g,function(){return(16*Math.random()|0).toString(16)}).toLowerCase()}function a(e){for(var t={left:[],center:[],right:[]},n=0,o=e.length;n-1?e.slice(n,e.length).filter(function(e){return e.canAutoResize}):e.filter(function(e){return e.canAutoResize});e.forEach(function(e){o+=e.canAutoResize?e.$$oldWidth||e.width:e.width});var r=t-o,l=r/i.length,s=o>t;i.forEach(function(e){if(s)e.width=e.$$oldWidth||e.width;else{e.$$oldWidth||(e.$$oldWidth=e.width);var t=e.$$oldWidth+l;e.minWith&&te.maxWidth?e.width=e.maxWidth:e.width=t}})}function y(e,t,n){var o,i,r,l=null,s=0;n||(n={});var a=function(){s=n.leading===!1?0:new Date,l=null,r=e.apply(o,i)};return function(){var c=new Date;s||n.leading!==!1||(s=c);var d=t-(c-s);return o=this,i=arguments,d<=0?(clearTimeout(l),l=null,s=c,r=e.apply(o,i)):l||n.trailing===!1||(l=setTimeout(a,d)),r}}function b(e,t,n){return{restrict:"E",replace:!0,controller:U,scope:!0,bindToController:{options:"=",rows:"=",selected:"=?",expanded:"=?",onSelect:"&",onSort:"&",onTreeToggle:"&",onPage:"&",onRowClick:"&",onRowDblClick:"&",onColumnResize:"&"},controllerAs:"dt",template:function(e){var t=e[0].getElementsByTagName("column"),n=s();return X.saveColumns(n,t),'
\n \n \n \n \n \n \n
'},compile:function(o,i){return{pre:function(o,i,r,l){function s(){var e=i[0].getBoundingClientRect();if(l.options.internal.innerWidth=Math.floor(e.width),l.options.scrollbarV){var t=e.height;l.options.headerHeight&&(t-=l.options.headerHeight),l.options.footerHeight&&(t-=l.options.footerHeight),l.options.internal.bodyHeight=t,l.calculatePageSize()}l.adjustColumns()}X.buildColumns(o,n);var a=i.attr("data-column-id"),c=X.columns[a];c&&(l.options.columns=c),l.transposeColumnDefaults(),l.options.internal.scrollBarWidth=h(),e.addEventListener("resize",y(function(){t(s)}));var d=function e(){var n=i[0].getBoundingClientRect(),o=n.width&&n.height;o?s():t(e,100)};d(),i.addClass("dt-loaded"),o.$on("$destroy",function(){N.default.element(e).off("resize")})}}}}}function C(e){var t=u(e);return j[t]||(void 0!==_[K.css+e]?j[t]=K.css+e:void 0!==_[e]&&(j[t]=e)),j[t]}function x(e,t,n){J?!te&&Q?(e[Y]="translate3d("+t+"px, "+n+"px, 0)",e[Z]="hidden"):e[u(Y)]="translate("+t+"px, "+n+"px)":(e.top=n+"px",e.left=t+"px")}function k(e){return{restrict:"E",controller:ne,controllerAs:"header",scope:!0,bindToController:{options:"=",columns:"=",columnWidths:"=",onSort:"&",onResize:"&",onCheckboxChange:"&"},template:'\n
\n\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
',replace:!0,link:function(t,n,o,i){t.columnsResorted=function(t,n){var o=r(n),l=N.default.element(t.currentTarget),s=-1;N.default.forEach(l.children(),function(e,t){n===N.default.element(e).attr("data-id")&&(s=t)}),e(function(){N.default.forEach(i.columns,function(e){var t=e.indexOf(o);if(t>-1){var n=e[s],r=i.options.columns.indexOf(n),l=i.options.columns.indexOf(o);return i.options.columns.splice(l,1),i.options.columns.splice(r,0,o),!1}})})};var r=function(e){var t=i.columns.left.concat(i.columns.center).concat(i.columns.right);return t.find(function(t){return t.$id===e})}}}}function $(e){return{restrict:"E",controller:oe,controllerAs:"hcell",scope:!0,bindToController:{options:"=",column:"=",onCheckboxChange:"&",onSort:"&",sortType:"=",onResize:"&",selected:"="},replace:!0,template:'
\n
\n \n \n \n \n
\n
',compile:function(){return{pre:function(t,n,o,i){var r=n[0].querySelector(".dt-header-cell-label"),l=void 0;if((i.column.headerTemplate||i.column.headerRenderer)&&(l=i.options.$outer.$new(!1),l.$header=i.column.name,l.$index=t.$index),i.column.headerTemplate){var s=N.default.element(""+i.column.headerTemplate.trim()+"");N.default.element(r).append(e(s)(l))}else if(i.column.headerRenderer){var a=N.default.element(i.column.headerRenderer(n));N.default.element(r).append(e(a)(l)[0])}else{var c=i.column.name;void 0!==c&&null!==c||(c=""),r.textContent=c}}}}}}function R(e){return{restrict:"E",controller:ie,controllerAs:"body",bindToController:{columns:"=",columnWidths:"=",rows:"=",options:"=",selected:"=?",expanded:"=?",onPage:"&",onTreeToggle:"&",onSelect:"&",onRowClick:"&",onRowDblClick:"&"},scope:!0,template:'\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n
\n
\n
\n
'}}function T(e,t){return{restrict:"E",require:"^dtBody",transclude:!0,replace:!0,template:'
',link:function(e,t,n,o){function i(){o.options.internal.offsetY=s,o.options.internal.offsetX=a,o.updatePage(),o.options.scrollbarV&&o.getRows(),o.options.$outer.$digest(),l=!1}function r(){l||(q(i),l=!0)}var l=!1,s=0,a=0,c=t.parent();o.options.internal.styleTranslator=new re(o.options.rowHeight),o.options.internal.setYOffset=function(e){c[0].scrollTop=e},c.on("scroll",function(e){s=this.scrollTop,a=this.scrollLeft,r()}),e.$on("$destroy",function(){c.off("scroll")}),e.scrollerStyles=function(){if(o.options.scrollbarV)return{height:o.count*o.options.rowHeight+"px"}}}}}function P(){return{controller:se,restrict:"A",require:"^dtBody",controllerAs:"selCtrl"}}function S(){return{restrict:"E",controller:ae,controllerAs:"rowCtrl",scope:!0,bindToController:{row:"=",columns:"=",columnWidths:"=",expanded:"=",selected:"=",hasChildren:"=",options:"=",onCheckboxChange:"&",onTreeToggle:"&"},link:function(e,t,n,o){o.row&&x(t[0].style,0,o.row.$$index*o.options.rowHeight),o.options.internal.styleTranslator.register(e.$index,t)},template:'\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
',replace:!0}}function z(){return{restrict:"E",controller:ce,controllerAs:"group",bindToController:{row:"=",onGroupToggle:"&",expanded:"=",options:"="},scope:!0,replace:!0,template:'\n
\n \n \n \n \n
',link:function(e,t,n,o){x(t[0].style,0,o.row.$$index*o.options.rowHeight),o.options.internal.styleTranslator.register(e.$index,t)}}}function E(e,t,n,o){return{restrict:"E",controller:de,scope:!0,controllerAs:"cell",bindToController:{options:"=",value:"=",selected:"=",column:"=",row:"=",expanded:"=",hasChildren:"=",onTreeToggle:"&",onCheckboxChange:"&"},template:'
\n \n \n \n
',replace:!0,compile:function(){return{pre:function(e,n,o,i){function r(){l=i.options.$outer.$new(!1),l.getValue=i.getValue}var l,s=N.default.element(n[0].querySelector(".dt-cell-content"));(i.column.template||i.column.cellRenderer)&&r(),e.$watch("cell.row",function(){if(l&&(l.$destroy(),r(),l.$cell=i.value,l.$row=i.row,l.$column=i.column,l.$$watchers=null),i.column.template){s.empty();var e=N.default.element(""+i.column.template.trim()+"");s.append(t(e)(l))}else if(i.column.cellRenderer){s.empty();var e=N.default.element(i.column.cellRenderer(l,s));s.append(t(e)(l))}else s[0].innerHTML=i.getValue()},!0)}}}}}function W(){return{restrict:"E",controller:ue,controllerAs:"footer",scope:!0,bindToController:{paging:"=",onPage:"&"},template:'',replace:!0}}function A(){return{restrict:"E",controller:he,controllerAs:"pager",scope:!0,bindToController:{page:"=",size:"=",count:"=",onPage:"&"},template:'
\n
    \n
  • \n \n
  • \n
  • \n \n
  • \n
  • \n {{pg.text}}\n
  • \n
  • \n \n
  • \n
  • \n \n
  • \n
\n
',replace:!0}}function B(e,t,n,o,i,r,l){function s(e,t){return e?N.default.isString(e)&&t?e:n.get(e)||$http.get(e,{cache:!0}):""}function a(e){if(e&&0!==e.length){var t=(""+e).toLowerCase();e="true"==t}else e=!1;return e}return{restrict:"A",scope:!0,replace:!1,link:function(n,c,d){function u(){n.exitTimeout=t(p,500)}function h(){t.cancel(n.exitTimeout);var r=document.getElementById("#"+n.popoverId);n.popover&&r||(n.options.group&&i.removeGroup(n.options.group,n.popoverId),n.options.text&&!n.options.template?(n.popover=N.default.element('
'),n.popover.html(n.options.text),N.default.element(document.body).append(n.popover),g(c,n.popover,n.options),i.add(n.popoverId,{element:c,popover:n.popover,group:n.options.group})):e.when(s(n.options.template,n.options.plain)).then(function(e){N.default.isString(e)||(e=e.data&&N.default.isString(e.data)?e.data:""),n.popover=N.default.element('
'),n.popover.html(e),o(n.popover)(n),N.default.element(document.body).append(n.popover),g(c,n.popover,n.options),n.popover.off("mouseleave",u),n.popover.on("mouseleave",u),n.popover.on("mouseenter",function(){t.cancel(n.exitTimeout)}),i.add(n.popoverId,{element:c,popover:n.popover,group:n.options.group})}))}function p(){n.popover&&n.popover.remove(),n.popover=void 0,i.remove(n.popoverId)}function g(e,o,i){t(function(){var t,s,a=e[0].getBoundingClientRect(),c=o[0].getBoundingClientRect();"right"===i.placement&&(s=a.left+a.width+i.spacing,t=r.calculateVerticalAlignment(a,c,i.alignment)),"left"===i.placement&&(s=a.left-c.width-i.spacing,t=r.calculateVerticalAlignment(a,c,i.alignment)),"top"===i.placement&&(t=a.top-c.height-i.spacing,s=r.calculateHorizontalAlignment(a,c,i.alignment)),"bottom"===i.placement&&(t=a.top+a.height+i.spacing,s=r.calculateHorizontalAlignment(a,c,i.alignment)),o.css({top:t+"px",left:s+"px"}),n.options.showCaret&&f(n.popover,a,c),l.addClass(n.popover,"popover-animation")},50)}function f(e,t,o){var i=N.default.element('');e.append(i);var l,s,a=i[0].getBoundingClientRect();"right"===n.options.placement&&(l=-6,s=r.calculateVerticalCaret(t,o,a,n.options.alignment)),"left"===n.options.placement&&(l=o.width-2,s=r.calculateVerticalCaret(t,o,a,n.options.alignment)),"top"===n.options.placement&&(s=o.height-5,l=r.calculateHorizontalCaret(t,o,a,n.options.alignment)),"bottom"===n.options.placement&&(s=-8,l=r.calculateHorizontalCaret(t,o,a,n.options.alignment)),i.css({top:s+"px",left:l+"px"})}n.popover=null,n.popoverId=Date.now(),n.options={text:d.popoverText,template:d.popoverTemplate,plain:a(d.popoverPlain||!1),placement:d.popoverPlacement||"right",alignment:d.popoverAlignment||"center",group:d.popoverGroup,spacing:parseInt(d.popoverSpacing)||0,showCaret:a(d.popoverPlain||!1)},c.off("mouseenter",h),c.on("mouseenter",h),c.off("mouseleave",u),c.on("mouseleave",u)}}}function G(e){var t={};this.add=function(e,n){t[e]=n},this.find=function(e){t[e]},this.remove=function(e){delete t[e]},this.removeGroup=function(n,o){angular.forEach(t,function(i,r){r!==o&&i.group&&i.group===n&&e.removeClass(i.popover,"sw-popover-animate").then(function(){i.popover.remove(),delete t[r]})})}}function D(){return{calculateVerticalAlignment:function(e,t,n){return"top"===n?e.top:"bottom"===n?e.top+e.height-t.height:"center"===n?e.top+e.height/2-t.height/2:void 0},calculateVerticalCaret:function(e,t,n,o){return"top"===o?e.height/2-n.height/2-1:"bottom"===o?t.height-e.height/2-n.height/2-1:"center"===o?t.height/2-n.height/2-1:void 0},calculateHorizontalCaret:function(e,t,n,o){return"left"===o?e.width/2-n.height/2-1:"right"===o?t.width-e.width/2-n.height/2-1:"center"===o?t.width/2-n.height/2-1:void 0},calculateHorizontalAlignment:function(e,t,n){return"left"===n?e.left:"right"===n?e.left+e.width-t.width:"center"===n?e.left+e.width/2-t.width/2:void 0}}}function H(){return{restrict:"E",controller:"MenuController",controllerAs:"dtm",scope:{current:"=",available:"="},template:''}}function M(e,t){return{restrict:"C",controller:"DropdownController",link:function(n,o,i){function r(e){o[0].contains(e.target)||t(function(){n.open=!1,s()})}function l(e){27===e.which&&t(function(){n.open=!1,s()})}function s(){e.unbind("click",r),e.unbind("keydown",l)}n.$watch("open",function(t){t&&(e.bind("click",r),e.bind("keydown",l))})}}}function I(e){return{restrict:"C",controller:"DropdownController",require:"?^dropdown",link:function(t,n,o,i){function r(n){n.preventDefault(),e(function(){i.toggle(t)})}function l(){n.unbind("click",r)}n.bind("click",r),t.$on("$destroy",l)}}}function V(e){return{restrict:"C",require:"?^dropdown",link:function(t,n,o,i){t.$watch("open",function(){e[t.open?"addClass":"removeClass"](n,"ddm-open")})}}}Object.defineProperty(e,"__esModule",{value:!0}),e.dtMenu=e.dtPopover=void 0;var N=n(t),O=function(){function e(e,t){for(var n=0;nt.sortPriority)return 1;if(e.sortPriority-1){var e=this.options.columns[n];e.width=t,e.canAutoResize=!1,this.adjustColumns(n),this.calculateColumns()}this.onColumnResize&&this.onColumnResize({column:e,width:t})}},{key:"onSelected",value:function(e){this.onSelect({rows:e})}},{key:"onRowClicked",value:function(e){this.onRowClick({row:e})}},{key:"onRowDblClicked",value:function(e){this.onRowDblClick({row:e})}}]),e}(),X={columns:{},dTables:{},saveColumns:function(e,t){if(t&&t.length){var n=[].slice.call(t);this.dTables[e]=n}},buildColumns:function(e,t){var n=this;N.default.forEach(this.dTables,function(o,i){n.columns[i]=[],N.default.forEach(o,function(o){var r={},l=!0;N.default.forEach(o.attributes,function(n){var o=u(n.name);switch(o){case"class":r.className=n.value;break;case"name":case"prop":r[o]=n.value;break;case"headerRenderer":case"cellRenderer":case"cellDataGetter":r[o]=t(n.value);break;case"visible":l=t(n.value)(e);break;default:r[o]=t(n.value)(e)}});var s=o.getElementsByTagName("column-header");s.length&&(r.headerTemplate=s[0].innerHTML,o.removeChild(s[0])),""!==o.innerHTML&&(r.template=o.innerHTML),l&&n.columns[i].push(r)})}),this.dTables={}}},j={},_=document.createElement("div").style,K=function(){var e=window.getComputedStyle(document.documentElement,""),t=(Array.prototype.slice.call(e).join("").match(/-(moz|webkit|ms)-/)||""===e.OLink&&["","o"])[1],n="WebKit|Moz|MS|O".match(new RegExp("("+t+")","i"))[1];return{dom:n,lowercase:t,css:"-"+t+"-",js:t[0].toUpperCase()+t.substr(1)}}(),Y=C("transform"),Z=C("backfaceVisibility"),J=!!C("transform"),Q=!!C("perspective"),ee=window.navigator.userAgent,te=/Safari\//.test(ee)&&!/Chrome\//.test(ee),ne=function(){function e(){i(this,e)}return O(e,[{key:"styles",value:function(){return{width:this.options.internal.innerWidth+"px",height:this.options.headerHeight+"px"}}},{key:"innerStyles",value:function(){return{width:this.columnWidths.total+"px"}}},{key:"onSorted",value:function(e){if("single"===this.options.sortType){var t=function(t){t!==e&&(t.sort=void 0)};this.columns.left.forEach(t),this.columns.center.forEach(t),this.columns.right.forEach(t)}this.onSort({column:e})}},{key:"stylesByGroup",value:function(e){var t={width:this.columnWidths[e]+"px"};if("center"===e)x(t,this.options.internal.offsetX*-1,0);else if("right"===e){var n=(this.columnWidths.total-this.options.internal.innerWidth)*-1;x(t,n,0)}return t}},{key:"onCheckboxChanged",value:function(){this.onCheckboxChange()}},{key:"onResized",value:function(e,t){this.onResize({column:e,width:t})}}]),e}(),oe=function(){function e(){i(this,e)}return O(e,[{key:"styles",value:function(){return{width:this.column.width+"px",minWidth:this.column.minWidth+"px",maxWidth:this.column.maxWidth+"px",height:this.column.height+"px"}}},{key:"cellClass",value:function(){var e={sortable:this.column.sortable,resizable:this.column.resizable};return this.column.headerClassName&&(e[this.column.headerClassName]=!0),e}},{key:"onSorted",value:function(){this.column.sortable&&(this.column.sort=p(this.sortType,this.column.sort),void 0===this.column.sort&&(this.column.sortPriority=void 0),this.onSort({column:this.column}))}},{key:"sortClass",value:function(){return{"sort-btn":!0,"sort-asc icon-down":"asc"===this.column.sort,"sort-desc icon-up":"desc"===this.column.sort}}},{key:"onResized",value:function(e,t){this.onResize({column:t,width:e})}},{key:"onCheckboxChange",value:function(){this.onCheckboxChanged()}}]),e}(),ie=function(){function e(t,n){var o=this;if(i(this,e),this.$scope=t,this.tempRows=[],this.treeColumn=this.options.columns.find(function(e){return e.isTreeColumn}),this.groupColumn=this.options.columns.find(function(e){return e.group}),t.$watchCollection("body.rows",this.rowsUpdated.bind(this)),this.options.scrollbarV||!this.options.scrollbarV&&this.options.paging.externalPaging){var r=!1;t.$watch("body.options.paging.size",function(e,t){(!r||e>t)&&(o.getRows(),r=!0)}),t.$watch("body.options.paging.count",function(e){o.count=e,o.updatePage()}),t.$watch("body.options.paging.offset",function(e){o.options.paging.size&&o.onPage({offset:e,size:o.options.paging.size})})}}return O(e,[{key:"rowsUpdated",value:function(e,t){if(e)if(this.options.paging.externalPaging||(this.options.paging.count=e.length),this.count=this.options.paging.count,(this.treeColumn||this.groupColumn)&&this.buildRowsByGroup(),this.options.scrollbarV){var n=e&&t&&(e.length===t.length||e.lengthn?Math.ceil(o):e,isNaN(o)||(this.options.paging.offset=o)}},{key:"calculateDepth",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=this.treeColumn?this.treeColumn.relationProp:this.groupColumn.prop,o=this.treeColumn.prop;if(!e[n])return t;if(e.$$depth)return e.$$depth+t;var i=this.index[e[n]];if(i)return t+=1,this.calculateDepth(i,t);for(var r=0,l=this.rows.length;r-1:this.selected===e),t}},{key:"buildTree",value:function(){function e(t,o,i){t.forEach(function(t){var r=t[n.treeColumn.relationProp],l=t[n.treeColumn.prop],s=n.rowsByGroup[l],a=n.expanded[l];(i>0||!r)&&(o.push(t),s&&s.length>0&&a&&e(s,o,i+1))})}var t=[],n=this;return e(this.rows,t,0),t}},{key:"getRows",value:function(e){if((this.treeColumn||this.groupColumn)&&!this.rowsByGroup)return!1;var t;this.treeColumn?(t=this.treeTemp||[],!e&&this.treeTemp||(this.treeTemp=t=this.buildTree(),this.count=t.length,this.tempRows.splice(0,this.tempRows.length))):this.groupColumn?(t=this.groupsTemp||[],!e&&this.groupsTemp||(this.groupsTemp=t=this.buildGroups(),this.count=t.length)):(t=this.rows,e===!0&&this.tempRows.splice(0,this.tempRows.length));var n=0,o=this.getFirstLastIndexes(),i=o.first;for(this.tempRows.splice(0,o.last-o.first);i-1?this.selected.splice(i,1):(this.options.multiSelectOnShift&&1===this.selected.length&&this.selected.splice(0,1),this.selected.push(n),this.body.onSelect({rows:[n]}))}this.prevIndex=t}else this.selected=n,this.body.onSelect({rows:[n]})}},{key:"selectRowsBetween",value:function(e){for(var t=e=this.prevIndex&&o<=e,s=o<=this.prevIndex&&o>=e,a={};if(a=t?{start:e,end:this.prevIndex-e}:{start:this.prevIndex,end:e+1},t&&s||!t&&l){var c=this.selected.indexOf(r);if(t&&c>-1){this.selected.splice(c,1);continue}o>=a.start&&o0&&e<=this.totalPages&&(this.page=e,this.onPage({page:e}))}},{key:"prevPage",value:function(){this.page>1&&this.selectPage(--this.page)}},{key:"nextPage",value:function(){this.selectPage(++this.page)}},{key:"canPrevious",value:function(){return this.page>1}},{key:"canNext",value:function(){return this.page-1}},{key:"onCheck",value:function(e){var t=this.getColumnIndex(e);t===-1?this.$scope.current.push(e):this.$scope.current.splice(t,1)}}]),e}(),fe=function(){function e(t){i(this,e),t.open=!1}return O(e,[{key:"toggle",value:function(e){e.open=!e.open}}]),e}(),me=N.default.module("dt.dropdown",[]).controller("DropdownController",fe).directive("dropdown",M).directive("dropdownToggle",I).directive("dropdownMenu",V),ve=N.default.module("dt.menu",[me.name]).controller("MenuController",ge).directive("dtm",H),we=N.default.module("data-table",[]).directive("dtable",b).directive("resizable",r).directive("sortable",l).directive("dtHeader",k).directive("dtHeaderCell",$).directive("dtBody",R).directive("dtScroller",T).directive("dtSeletion",P).directive("dtRow",S).directive("dtGroupRow",z).directive("dtCell",E).directive("dtFooter",W).directive("dtPager",A);e.dtPopover=pe,e.dtMenu=ve,e.default=we}); \ No newline at end of file From 12c11ef40b8ba6db8279a463d64def5848aa4717 Mon Sep 17 00:00:00 2001 From: Jon Shaffer Date: Wed, 26 Oct 2016 15:41:03 -0400 Subject: [PATCH 4/4] tooltip demo fix --- demos/tooltip.html | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/demos/tooltip.html b/demos/tooltip.html index 7d11fc0..d5bdfee 100644 --- a/demos/tooltip.html +++ b/demos/tooltip.html @@ -56,7 +56,7 @@ System.import('components/popover/popover') ]).then(function(modules) { - var module = angular.module('app', [ 'data-table', 'popover' ]); + var module = angular.module('app', [ 'data-table', 'dt.popover' ]); module.controller('HomeController', function($scope, $http){ $scope.options = { @@ -65,26 +65,26 @@ headerHeight: 50, scrollbarV: true, columns: [ - { - name: "Company", - prop: "company", + { + name: "Company", + prop: "company", flexGrow: 2 }, - { - name: "Revenue", + { + name: "Revenue", flexGrow: 1, - prop: "revenue" + prop: "revenue" }, - { - name: "Sales", + { + name: "Sales", flexGrow: 1, prop: "sales" - },{ - name: "Comments", + },{ + name: "Comments", prop: "comments", tooltips: true, cellRenderer: function(scope, elm){ - return "{{value}}"; + return "{{$row.comments}}"; }