/*********************************************** * ng-grid JavaScript Library * Authors: https://github.com/angular-ui/ng-grid/blob/master/README.md * License: MIT (http://www.opensource.org/licenses/mit-license.php) * Compiled At: 07/06/2013 13:50 ***********************************************/ (function(window, $) { 'use strict'; var EXCESS_ROWS = 6; var SCROLL_THRESHOLD = 4; var ASC = "asc"; var DESC = "desc"; var NG_FIELD = '_ng_field_'; var NG_DEPTH = '_ng_depth_'; var NG_HIDDEN = '_ng_hidden_'; var NG_COLUMN = '_ng_column_'; var CUSTOM_FILTERS = /CUSTOM_FILTERS/g; var COL_FIELD = /COL_FIELD/g; var DISPLAY_CELL_TEMPLATE = /DISPLAY_CELL_TEMPLATE/g; var EDITABLE_CELL_TEMPLATE = /EDITABLE_CELL_TEMPLATE/g; var TEMPLATE_REGEXP = /<.+>/; window.ngGrid = {}; window.ngGrid.i18n = {}; var ngGridServices = angular.module('ngGrid.services', []); var ngGridDirectives = angular.module('ngGrid.directives', []); var ngGridFilters = angular.module('ngGrid.filters', []); angular.module('ngGrid', ['ngGrid.services', 'ngGrid.directives', 'ngGrid.filters']); var ngMoveSelectionHandler = function($scope, elm, evt, grid) { if ($scope.selectionProvider.selectedItems === undefined) { return true; } var charCode = evt.which || evt.keyCode, newColumnIndex, lastInRow = false, firstInRow = false, rowIndex = $scope.selectionProvider.lastClickedRow === undefined ? 1 : $scope.selectionProvider.lastClickedRow.rowIndex, visibleCols = $scope.columns.filter(function(c) { return c.visible; }), pinnedCols = $scope.columns.filter(function(c) { return c.pinned; }); if ($scope.col) { newColumnIndex = visibleCols.indexOf($scope.col); } if (charCode !== 37 && charCode !== 38 && charCode !== 39 && charCode !== 40 && charCode !== 9 && charCode !== 13) { return true; } if ($scope.enableCellSelection) { if (charCode === 9) { evt.preventDefault(); } var focusedOnFirstColumn = $scope.showSelectionCheckbox ? $scope.col.index === 1 : $scope.col.index === 0; var focusedOnFirstVisibleColumns = $scope.$index === 1 || $scope.$index === 0; var focusedOnLastVisibleColumns = $scope.$index === ($scope.renderedColumns.length - 1) || $scope.$index === ($scope.renderedColumns.length - 2); var focusedOnLastColumn = visibleCols.indexOf($scope.col) === (visibleCols.length - 1); var focusedOnLastPinnedColumn = pinnedCols.indexOf($scope.col) === (pinnedCols.length - 1); if (charCode === 37 || charCode === 9 && evt.shiftKey) { var scrollTo = 0; if (!focusedOnFirstColumn) { newColumnIndex -= 1; } if (focusedOnFirstVisibleColumns) { if (focusedOnFirstColumn && charCode === 9 && evt.shiftKey){ scrollTo = grid.$canvas.width(); newColumnIndex = visibleCols.length - 1; firstInRow = true; } else { scrollTo = grid.$viewport.scrollLeft() - $scope.col.width; } } else if (pinnedCols.length > 0) { scrollTo = grid.$viewport.scrollLeft() - visibleCols[newColumnIndex].width; } grid.$viewport.scrollLeft(scrollTo); } else if (charCode === 39 || charCode === 9 && !evt.shiftKey) { if (focusedOnLastVisibleColumns) { if (focusedOnLastColumn && charCode === 9 && !evt.shiftKey) { grid.$viewport.scrollLeft(0); newColumnIndex = $scope.showSelectionCheckbox ? 1 : 0; lastInRow = true; } else { grid.$viewport.scrollLeft(grid.$viewport.scrollLeft() + $scope.col.width); } } else if (focusedOnLastPinnedColumn) { grid.$viewport.scrollLeft(0); } if (!focusedOnLastColumn) { newColumnIndex += 1; } } } var items; if ($scope.configGroups.length > 0) { items = grid.rowFactory.parsedData.filter(function (row) { return !row.isAggRow; }); } else { items = grid.filteredRows; } var offset = 0; if (rowIndex !== 0 && (charCode === 38 || charCode === 13 && evt.shiftKey || charCode === 9 && evt.shiftKey && firstInRow)) { offset = -1; } else if (rowIndex !== items.length - 1 && (charCode === 40 || charCode === 13 && !evt.shiftKey || charCode === 9 && lastInRow)) { offset = 1; } if (offset) { var r = items[rowIndex + offset]; if (r.beforeSelectionChange(r, evt)) { r.continueSelection(evt); $scope.$emit('ngGridEventDigestGridParent'); if ($scope.selectionProvider.lastClickedRow.renderedRowIndex >= $scope.renderedRows.length - EXCESS_ROWS - 2) { grid.$viewport.scrollTop(grid.$viewport.scrollTop() + $scope.rowHeight); } else if ($scope.selectionProvider.lastClickedRow.renderedRowIndex <= EXCESS_ROWS + 2) { grid.$viewport.scrollTop(grid.$viewport.scrollTop() - $scope.rowHeight); } } } if ($scope.enableCellSelection) { setTimeout(function(){ $scope.domAccessProvider.focusCellElement($scope, $scope.renderedColumns.indexOf(visibleCols[newColumnIndex])); }, 3); } return false; }; if (!String.prototype.trim) { String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }; } if (!Array.prototype.indexOf) { Array.prototype.indexOf = function(elt ) { var len = this.length >>> 0; var from = Number(arguments[1]) || 0; from = (from < 0) ? Math.ceil(from) : Math.floor(from); if (from < 0) { from += len; } for (; from < len; from++) { if (from in this && this[from] === elt) { return from; } } return -1; }; } if (!Array.prototype.filter) { Array.prototype.filter = function(fun ) { "use strict"; var t = Object(this); var len = t.length >>> 0; if (typeof fun !== "function") { throw new TypeError(); } var res = []; var thisp = arguments[1]; for (var i = 0; i < len; i++) { if (i in t) { var val = t[i]; if (fun.call(thisp, val, i, t)) { res.push(val); } } } return res; }; } ngGridFilters.filter('checkmark', function() { return function(input) { return input ? '\u2714' : '\u2718'; }; }); ngGridFilters.filter('ngColumns', function() { return function(input) { return input.filter(function(col) { return !col.isAggCol; }); }; }); angular.module('ngGrid.services').factory('$domUtilityService',['$utilityService', function($utils) { var domUtilityService = {}; var regexCache = {}; var getWidths = function() { var $testContainer = $('
'); $testContainer.appendTo('body'); $testContainer.height(100).width(100).css("position", "absolute").css("overflow", "scroll"); $testContainer.append(''); domUtilityService.ScrollH = ($testContainer.height() - $testContainer[0].clientHeight); domUtilityService.ScrollW = ($testContainer.width() - $testContainer[0].clientWidth); $testContainer.empty(); $testContainer.attr('style', ''); $testContainer.append('M'); domUtilityService.LetterW = $testContainer.children().first().width(); $testContainer.remove(); }; domUtilityService.eventStorage = {}; domUtilityService.AssignGridContainers = function($scope, rootEl, grid) { grid.$root = $(rootEl); grid.$topPanel = grid.$root.find(".ngTopPanel"); grid.$groupPanel = grid.$root.find(".ngGroupPanel"); grid.$headerContainer = grid.$topPanel.find(".ngHeaderContainer"); $scope.$headerContainer = grid.$headerContainer; grid.$headerScroller = grid.$topPanel.find(".ngHeaderScroller"); grid.$headers = grid.$headerScroller.children(); grid.$viewport = grid.$root.find(".ngViewport"); grid.$canvas = grid.$viewport.find(".ngCanvas"); grid.$footerPanel = grid.$root.find(".ngFooterPanel"); $scope.$watch(function () { return grid.$viewport.scrollLeft(); }, function (newLeft) { return grid.$headerContainer.scrollLeft(newLeft); }); domUtilityService.UpdateGridLayout($scope, grid); }; domUtilityService.getRealWidth = function (obj) { var width = 0; var props = { visibility: "hidden", display: "block" }; var hiddenParents = obj.parents().andSelf().not(':visible'); $.swap(hiddenParents[0], props, function () { width = obj.outerWidth(); }); return width; }; domUtilityService.UpdateGridLayout = function($scope, grid) { var scrollTop = grid.$viewport.scrollTop(); grid.elementDims.rootMaxW = grid.$root.width(); if (grid.$root.is(':hidden')) { grid.elementDims.rootMaxW = domUtilityService.getRealWidth(grid.$root); } grid.elementDims.rootMaxH = grid.$root.height(); grid.refreshDomSizes(); $scope.adjustScrollTop(scrollTop, true); }; domUtilityService.numberOfGrids = 0; domUtilityService.BuildStyles = function($scope, grid, digest) { var rowHeight = grid.config.rowHeight, $style = grid.$styleSheet, gridId = grid.gridId, css, cols = $scope.columns, sumWidth = 0; if (!$style) { $style = $('#' + gridId); if (!$style[0]) { $style = $("").appendTo(grid.$root); } } $style.empty(); var trw = $scope.totalRowWidth(); css = "." + gridId + " .ngCanvas { width: " + trw + "px; }" + "." + gridId + " .ngRow { width: " + trw + "px; }" + "." + gridId + " .ngCanvas { width: " + trw + "px; }" + "." + gridId + " .ngHeaderScroller { width: " + (trw + domUtilityService.ScrollH) + "px}"; for (var i = 0; i < cols.length; i++) { var col = cols[i]; if (col.visible !== false) { css += "." + gridId + " .col" + i + " { width: " + col.width + "px; left: " + sumWidth + "px; height: " + rowHeight + "px }" + "." + gridId + " .colt" + i + " { width: " + col.width + "px; }"; sumWidth += col.width; } } if ($utils.isIe) { $style[0].styleSheet.cssText = css; } else { $style[0].appendChild(document.createTextNode(css)); } grid.$styleSheet = $style; $scope.adjustScrollLeft(grid.$viewport.scrollLeft()); if (digest) { domUtilityService.digest($scope); } }; domUtilityService.setColLeft = function(col, colLeft, grid) { if (grid.$styleSheet) { var regex = regexCache[col.index]; if (!regex) { regex = regexCache[col.index] = new RegExp(".col" + col.index + " { width: [0-9]+px; left: [0-9]+px"); } var str = grid.$styleSheet.html(); var newStr = str.replace(regex, ".col" + col.index + " { width: " + col.width + "px; left: " + colLeft + "px"); if ($utils.isIe) { setTimeout(function() { grid.$styleSheet.html(newStr); }); } else { grid.$styleSheet.html(newStr); } } }; domUtilityService.setColLeft.immediate = 1; domUtilityService.RebuildGrid = function($scope, grid){ domUtilityService.UpdateGridLayout($scope, grid); if (grid.config.maintainColumnRatios == null || grid.config.maintainColumnRatios) { grid.configureColumnWidths(); } $scope.adjustScrollLeft(grid.$viewport.scrollLeft()); domUtilityService.BuildStyles($scope, grid, true); }; domUtilityService.digest = function($scope) { if (!$scope.$root.$$phase) { $scope.$digest(); } }; domUtilityService.ScrollH = 17; domUtilityService.ScrollW = 17; domUtilityService.LetterW = 10; getWidths(); return domUtilityService; }]); angular.module('ngGrid.services').factory('$sortService', ['$parse', function($parse) { var sortService = {}; sortService.colSortFnCache = {}; sortService.guessSortFn = function(item) { var itemType = typeof(item); switch (itemType) { case "number": return sortService.sortNumber; case "boolean": return sortService.sortBool; case "string": return item.match(/^[-+]?[£$¤]?[\d,.]+%?$/) ? sortService.sortNumberStr : sortService.sortAlpha; default: if (Object.prototype.toString.call(item) === '[object Date]') { return sortService.sortDate; } else { return sortService.basicSort; } } }; sortService.basicSort = function(a, b) { if (a === b) { return 0; } if (a < b) { return -1; } return 1; }; sortService.sortNumber = function(a, b) { return a - b; }; sortService.sortNumberStr = function(a, b) { var numA, numB, badA = false, badB = false; numA = parseFloat(a.replace(/[^0-9.-]/g, '')); if (isNaN(numA)) { badA = true; } numB = parseFloat(b.replace(/[^0-9.-]/g, '')); if (isNaN(numB)) { badB = true; } if (badA && badB) { return 0; } if (badA) { return 1; } if (badB) { return -1; } return numA - numB; }; sortService.sortAlpha = function(a, b) { var strA = a.toLowerCase(), strB = b.toLowerCase(); return strA === strB ? 0 : (strA < strB ? -1 : 1); }; sortService.sortDate = function(a, b) { var timeA = a.getTime(), timeB = b.getTime(); return timeA === timeB ? 0 : (timeA < timeB ? -1 : 1); }; sortService.sortBool = function(a, b) { if (a && b) { return 0; } if (!a && !b) { return 0; } else { return a ? 1 : -1; } }; sortService.sortData = function(sortInfo, data ) { if (!data || !sortInfo) { return; } var l = sortInfo.fields.length, order = sortInfo.fields, col, direction, d = data.slice(0); data.sort(function (itemA, itemB) { var tem = 0, indx = 0, sortFn; while (tem === 0 && indx < l) { col = sortInfo.columns[indx]; direction = sortInfo.directions[indx]; sortFn = sortService.getSortFn(col, d); var propA = $parse(order[indx])(itemA); var propB = $parse(order[indx])(itemB); if ((!propA && propA !== 0) || (!propB && propB !== 0)) { if (!propB && !propA) { tem = 0; } else if (!propA) { tem = 1; } else if (!propB) { tem = -1; } } else { tem = sortFn(propA, propB); } indx++; } if (direction === ASC) { return tem; } else { return 0 - tem; } }); }; sortService.Sort = function(sortInfo, data) { if (sortService.isSorting) { return; } sortService.isSorting = true; sortService.sortData(sortInfo, data); sortService.isSorting = false; }; sortService.getSortFn = function(col, data) { var sortFn, item; if (sortService.colSortFnCache[col.field]) { sortFn = sortService.colSortFnCache[col.field]; } else if (col.sortingAlgorithm !== undefined) { sortFn = col.sortingAlgorithm; sortService.colSortFnCache[col.field] = col.sortingAlgorithm; } else { item = data[0]; if (!item) { return sortFn; } sortFn = sortService.guessSortFn($parse(col.field)(item)); if (sortFn) { sortService.colSortFnCache[col.field] = sortFn; } else { sortFn = sortService.sortAlpha; } } return sortFn; }; return sortService; }]); angular.module('ngGrid.services').factory('$utilityService', ['$parse', function ($parse) { var funcNameRegex = /function (.{1,})\(/; var utils = { visualLength: function(node) { var elem = document.getElementById('testDataLength'); if (!elem) { elem = document.createElement('SPAN'); elem.id = "testDataLength"; elem.style.visibility = "hidden"; document.body.appendChild(elem); } $(elem).css('font', $(node).css('font')); $(elem).css('font-size', $(node).css('font-size')); $(elem).css('font-family', $(node).css('font-family')); elem.innerHTML = $(node).text(); return elem.offsetWidth; }, forIn: function(obj, action) { for (var prop in obj) { if (obj.hasOwnProperty(prop)) { action(obj[prop], prop); } } }, evalProperty: function (entity, path) { return $parse(path)(entity); }, endsWith: function(str, suffix) { if (!str || !suffix || typeof str !== "string") { return false; } return str.indexOf(suffix, str.length - suffix.length) !== -1; }, isNullOrUndefined: function(obj) { if (obj === undefined || obj === null) { return true; } return false; }, getElementsByClassName: function(cl) { var retnode = []; var myclass = new RegExp('\\b' + cl + '\\b'); var elem = document.getElementsByTagName('*'); for (var i = 0; i < elem.length; i++) { var classes = elem[i].className; if (myclass.test(classes)) { retnode.push(elem[i]); } } return retnode; }, newId: (function() { var seedId = new Date().getTime(); return function() { return seedId += 1; }; })(), seti18n: function($scope, language) { var $langPack = window.ngGrid.i18n[language]; for (var label in $langPack) { $scope.i18n[label] = $langPack[label]; } }, getInstanceType: function (o) { var results = (funcNameRegex).exec(o.constructor.toString()); if (results && results.length > 1) { var instanceType = results[1].replace(/^\s+|\s+$/g, ""); return instanceType; } else { return ""; } }, ieVersion: (function() { var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i'); do{ div.innerHTML = ''; }while(iElems[0]); return version > 4 ? version : undefined; })() }; $.extend(utils, { isIe: (function() { return utils.ieVersion !== undefined; })() }); return utils; }]); var ngAggregate = function (aggEntity, rowFactory, rowHeight, groupInitState) { this.rowIndex = 0; this.offsetTop = this.rowIndex * rowHeight; this.entity = aggEntity; this.label = aggEntity.gLabel; this.field = aggEntity.gField; this.depth = aggEntity.gDepth; this.parent = aggEntity.parent; this.children = aggEntity.children; this.aggChildren = aggEntity.aggChildren; this.aggIndex = aggEntity.aggIndex; this.collapsed = groupInitState; this.groupInitState = groupInitState; this.rowFactory = rowFactory; this.rowHeight = rowHeight; this.isAggRow = true; this.offsetLeft = aggEntity.gDepth * 25; this.aggLabelFilter = aggEntity.aggLabelFilter; }; ngAggregate.prototype.toggleExpand = function () { this.collapsed = this.collapsed ? false : true; if (this.orig) { this.orig.collapsed = this.collapsed; } this.notifyChildren(); }; ngAggregate.prototype.setExpand = function (state) { this.collapsed = state; this.notifyChildren(); }; ngAggregate.prototype.notifyChildren = function () { var longest = Math.max(this.rowFactory.aggCache.length, this.children.length); for (var i = 0; i < longest; i++) { if (this.aggChildren[i]) { this.aggChildren[i].entity[NG_HIDDEN] = this.collapsed; if (this.collapsed) { this.aggChildren[i].setExpand(this.collapsed); } } if (this.children[i]) { this.children[i][NG_HIDDEN] = this.collapsed; } if (i > this.aggIndex && this.rowFactory.aggCache[i]) { var agg = this.rowFactory.aggCache[i]; var offset = (30 * this.children.length); agg.offsetTop = this.collapsed ? agg.offsetTop - offset : agg.offsetTop + offset; } } this.rowFactory.renderedChange(); }; ngAggregate.prototype.aggClass = function () { return this.collapsed ? "ngAggArrowCollapsed" : "ngAggArrowExpanded"; }; ngAggregate.prototype.totalChildren = function () { if (this.aggChildren.length > 0) { var i = 0; var recurse = function (cur) { if (cur.aggChildren.length > 0) { angular.forEach(cur.aggChildren, function (a) { recurse(a); }); } else { i += cur.children.length; } }; recurse(this); return i; } else { return this.children.length; } }; ngAggregate.prototype.copy = function () { var ret = new ngAggregate(this.entity, this.rowFactory, this.rowHeight, this.groupInitState); ret.orig = this; return ret; }; var ngColumn = function (config, $scope, grid, domUtilityService, $templateCache, $utils) { var self = this, colDef = config.colDef, delay = 500, clicks = 0, timer = null; self.colDef = config.colDef; self.width = colDef.width; self.groupIndex = 0; self.isGroupedBy = false; self.minWidth = !colDef.minWidth ? 50 : colDef.minWidth; self.maxWidth = !colDef.maxWidth ? 9000 : colDef.maxWidth; self.enableCellEdit = colDef.enableCellEdit !== undefined ? colDef.enableCellEdit : (config.enableCellEdit || config.enableCellEditOnFocus); self.headerRowHeight = config.headerRowHeight; self.displayName = (colDef.displayName === undefined) ? colDef.field : colDef.displayName; self.index = config.index; self.isAggCol = config.isAggCol; self.cellClass = colDef.cellClass; self.sortPriority = undefined; self.cellFilter = colDef.cellFilter ? colDef.cellFilter : ""; self.field = colDef.field; self.aggLabelFilter = colDef.cellFilter || colDef.aggLabelFilter; self.visible = $utils.isNullOrUndefined(colDef.visible) || colDef.visible; self.sortable = false; self.resizable = false; self.pinnable = false; self.pinned = (config.enablePinning && colDef.pinned); self.originalIndex = config.originalIndex == null ? self.index : config.originalIndex; self.groupable = $utils.isNullOrUndefined(colDef.groupable) || colDef.groupable; if (config.enableSort) { self.sortable = $utils.isNullOrUndefined(colDef.sortable) || colDef.sortable; } if (config.enableResize) { self.resizable = $utils.isNullOrUndefined(colDef.resizable) || colDef.resizable; } if (config.enablePinning) { self.pinnable = $utils.isNullOrUndefined(colDef.pinnable) || colDef.pinnable; } self.sortDirection = undefined; self.sortingAlgorithm = colDef.sortFn; self.headerClass = colDef.headerClass; self.cursor = self.sortable ? 'pointer' : 'default'; self.headerCellTemplate = colDef.headerCellTemplate || $templateCache.get('headerCellTemplate.html'); self.cellTemplate = colDef.cellTemplate || $templateCache.get('cellTemplate.html').replace(CUSTOM_FILTERS, self.cellFilter ? "|" + self.cellFilter : ""); if(self.enableCellEdit) { self.cellEditTemplate = $templateCache.get('cellEditTemplate.html'); self.editableCellTemplate = colDef.editableCellTemplate || $templateCache.get('editableCellTemplate.html'); } if (colDef.cellTemplate && !TEMPLATE_REGEXP.test(colDef.cellTemplate)) { self.cellTemplate = $.ajax({ type: "GET", url: colDef.cellTemplate, async: false }).responseText; } if (self.enableCellEdit && colDef.editableCellTemplate && !TEMPLATE_REGEXP.test(colDef.editableCellTemplate)) { self.editableCellTemplate = $.ajax({ type: "GET", url: colDef.editableCellTemplate, async: false }).responseText; } if (colDef.headerCellTemplate && !TEMPLATE_REGEXP.test(colDef.headerCellTemplate)) { self.headerCellTemplate = $.ajax({ type: "GET", url: colDef.headerCellTemplate, async: false }).responseText; } self.colIndex = function () { var classes = self.pinned ? "pinned " : ""; classes += "col" + self.index + " colt" + self.index; if (self.cellClass) { classes += " " + self.cellClass; } return classes; }; self.groupedByClass = function() { return self.isGroupedBy ? "ngGroupedByIcon" : "ngGroupIcon"; }; self.toggleVisible = function() { self.visible = !self.visible; }; self.showSortButtonUp = function() { return self.sortable ? self.sortDirection === DESC : self.sortable; }; self.showSortButtonDown = function() { return self.sortable ? self.sortDirection === ASC : self.sortable; }; self.noSortVisible = function() { return !self.sortDirection; }; self.sort = function(evt) { if (!self.sortable) { return true; } var dir = self.sortDirection === ASC ? DESC : ASC; self.sortDirection = dir; config.sortCallback(self, evt); return false; }; self.gripClick = function() { clicks++; if (clicks === 1) { timer = setTimeout(function() { clicks = 0; }, delay); } else { clearTimeout(timer); config.resizeOnDataCallback(self); clicks = 0; } }; self.gripOnMouseDown = function(event) { $scope.isColumnResizing = true; if (event.ctrlKey && !self.pinned) { self.toggleVisible(); domUtilityService.BuildStyles($scope, grid); return true; } event.target.parentElement.style.cursor = 'col-resize'; self.startMousePosition = event.clientX; self.origWidth = self.width; $(document).mousemove(self.onMouseMove); $(document).mouseup(self.gripOnMouseUp); return false; }; self.onMouseMove = function(event) { var diff = event.clientX - self.startMousePosition; var newWidth = diff + self.origWidth; self.width = (newWidth < self.minWidth ? self.minWidth : (newWidth > self.maxWidth ? self.maxWidth : newWidth)); $scope.hasUserChangedGridColumnWidths = true; domUtilityService.BuildStyles($scope, grid); return false; }; self.gripOnMouseUp = function (event) { $(document).off('mousemove', self.onMouseMove); $(document).off('mouseup', self.gripOnMouseUp); event.target.parentElement.style.cursor = 'default'; domUtilityService.digest($scope); $scope.isColumnResizing = false; return false; }; self.copy = function() { var ret = new ngColumn(config, $scope, grid, domUtilityService, $templateCache); ret.isClone = true; ret.orig = self; return ret; }; self.setVars = function (fromCol) { self.orig = fromCol; self.width = fromCol.width; self.groupIndex = fromCol.groupIndex; self.isGroupedBy = fromCol.isGroupedBy; self.displayName = fromCol.displayName; self.index = fromCol.index; self.isAggCol = fromCol.isAggCol; self.cellClass = fromCol.cellClass; self.cellFilter = fromCol.cellFilter; self.field = fromCol.field; self.aggLabelFilter = fromCol.aggLabelFilter; self.visible = fromCol.visible; self.sortable = fromCol.sortable; self.resizable = fromCol.resizable; self.pinnable = fromCol.pinnable; self.pinned = fromCol.pinned; self.originalIndex = fromCol.originalIndex; self.sortDirection = fromCol.sortDirection; self.sortingAlgorithm = fromCol.sortingAlgorithm; self.headerClass = fromCol.headerClass; self.headerCellTemplate = fromCol.headerCellTemplate; self.cellTemplate = fromCol.cellTemplate; self.cellEditTemplate = fromCol.cellEditTemplate; }; }; var ngDimension = function (options) { this.outerHeight = null; this.outerWidth = null; $.extend(this, options); }; var ngDomAccessProvider = function (grid) { this.previousColumn = null; this.grid = grid; }; ngDomAccessProvider.prototype.changeUserSelect = function (elm, value) { elm.css({ '-webkit-touch-callout': value, '-webkit-user-select': value, '-khtml-user-select': value, '-moz-user-select': value === 'none' ? '-moz-none' : value, '-ms-user-select': value, 'user-select': value }); }; ngDomAccessProvider.prototype.focusCellElement = function ($scope, index) { if ($scope.selectionProvider.lastClickedRow) { var columnIndex = index !== undefined ? index : this.previousColumn; var elm = $scope.selectionProvider.lastClickedRow.clone ? $scope.selectionProvider.lastClickedRow.clone.elm : $scope.selectionProvider.lastClickedRow.elm; if (columnIndex !== undefined && elm) { var columns = angular.element(elm[0].children).filter(function () { return this.nodeType !== 8; }); var i = Math.max(Math.min($scope.renderedColumns.length - 1, columnIndex), 0); if (this.grid.config.showSelectionCheckbox && angular.element(columns[i]).scope() && angular.element(columns[i]).scope().col.index === 0) { i = 1; } if (columns[i]) { columns[i].children[1].children[0].focus(); } this.previousColumn = columnIndex; } } }; ngDomAccessProvider.prototype.selectionHandlers = function ($scope, elm) { var doingKeyDown = false; var self = this; elm.bind('keydown', function (evt) { if (evt.keyCode === 16) { self.changeUserSelect(elm, 'none', evt); return true; } else if (!doingKeyDown) { doingKeyDown = true; var ret = ngMoveSelectionHandler($scope, elm, evt, self.grid); doingKeyDown = false; return ret; } return true; }); elm.bind('keyup', function (evt) { if (evt.keyCode === 16) { self.changeUserSelect(elm, 'text', evt); } return true; }); }; var ngEventProvider = function (grid, $scope, domUtilityService, $timeout) { var self = this; self.colToMove = undefined; self.groupToMove = undefined; self.assignEvents = function() { if (grid.config.jqueryUIDraggable && !grid.config.enablePinning) { grid.$groupPanel.droppable({ addClasses: false, drop: function(event) { self.onGroupDrop(event); } }); } else { grid.$groupPanel.on('mousedown', self.onGroupMouseDown).on('dragover', self.dragOver).on('drop', self.onGroupDrop); grid.$headerScroller.on('mousedown', self.onHeaderMouseDown).on('dragover', self.dragOver); if (grid.config.enableColumnReordering && !grid.config.enablePinning) { grid.$headerScroller.on('drop', self.onHeaderDrop); } } $scope.$watch('renderedColumns', function() { $timeout(self.setDraggables); }); }; self.dragStart = function(evt){ evt.dataTransfer.setData('text', ''); }; self.dragOver = function(evt) { evt.preventDefault(); }; self.setDraggables = function() { if (!grid.config.jqueryUIDraggable) { var columns = grid.$root.find('.ngHeaderSortColumn'); angular.forEach(columns, function(col){ if(col.className && col.className.indexOf("ngHeaderSortColumn") !== -1){ col.setAttribute('draggable', 'true'); if (col.addEventListener) { col.addEventListener('dragstart', self.dragStart); } } }); if (navigator.userAgent.indexOf("MSIE") !== -1){ grid.$root.find('.ngHeaderSortColumn').bind('selectstart', function () { this.dragDrop(); return false; }); } } else { grid.$root.find('.ngHeaderSortColumn').draggable({ helper: 'clone', appendTo: 'body', stack: 'div', addClasses: false, start: function(event) { self.onHeaderMouseDown(event); } }).droppable({ drop: function(event) { self.onHeaderDrop(event); } }); } }; self.onGroupMouseDown = function(event) { var groupItem = $(event.target); if (groupItem[0].className !== 'ngRemoveGroup') { var groupItemScope = angular.element(groupItem).scope(); if (groupItemScope) { if (!grid.config.jqueryUIDraggable) { groupItem.attr('draggable', 'true'); if(this.addEventListener){ this.addEventListener('dragstart', self.dragStart); } if (navigator.userAgent.indexOf("MSIE") !== -1){ groupItem.bind('selectstart', function () { this.dragDrop(); return false; }); } } self.groupToMove = { header: groupItem, groupName: groupItemScope.group, index: groupItemScope.$index }; } } else { self.groupToMove = undefined; } }; self.onGroupDrop = function(event) { event.stopPropagation(); var groupContainer; var groupScope; if (self.groupToMove) { groupContainer = $(event.target).closest('.ngGroupElement'); if (groupContainer.context.className === 'ngGroupPanel') { $scope.configGroups.splice(self.groupToMove.index, 1); $scope.configGroups.push(self.groupToMove.groupName); } else { groupScope = angular.element(groupContainer).scope(); if (groupScope) { if (self.groupToMove.index !== groupScope.$index) { $scope.configGroups.splice(self.groupToMove.index, 1); $scope.configGroups.splice(groupScope.$index, 0, self.groupToMove.groupName); } } } self.groupToMove = undefined; grid.fixGroupIndexes(); } else if (self.colToMove) { if ($scope.configGroups.indexOf(self.colToMove.col) === -1) { groupContainer = $(event.target).closest('.ngGroupElement'); if (groupContainer.context.className === 'ngGroupPanel' || groupContainer.context.className === 'ngGroupPanelDescription ng-binding') { $scope.groupBy(self.colToMove.col); } else { groupScope = angular.element(groupContainer).scope(); if (groupScope) { $scope.removeGroup(groupScope.$index); } } } self.colToMove = undefined; } if (!$scope.$$phase) { $scope.$apply(); } }; self.onHeaderMouseDown = function(event) { var headerContainer = $(event.target).closest('.ngHeaderSortColumn'); var headerScope = angular.element(headerContainer).scope(); if (headerScope) { self.colToMove = { header: headerContainer, col: headerScope.col }; } }; self.onHeaderDrop = function(event) { if (!self.colToMove || self.colToMove.col.pinned) { return; } var headerContainer = $(event.target).closest('.ngHeaderSortColumn'); var headerScope = angular.element(headerContainer).scope(); if (headerScope) { if (self.colToMove.col === headerScope.col) { return; } $scope.columns.splice(self.colToMove.col.index, 1); $scope.columns.splice(headerScope.col.index, 0, self.colToMove.col); grid.fixColumnIndexes(); self.colToMove = undefined; domUtilityService.digest($scope); } }; self.assignGridEventHandlers = function() { if (grid.config.tabIndex === -1) { grid.$viewport.attr('tabIndex', domUtilityService.numberOfGrids); domUtilityService.numberOfGrids++; } else { grid.$viewport.attr('tabIndex', grid.config.tabIndex); } var windowThrottle; $(window).resize(function(){ clearTimeout(windowThrottle); windowThrottle = setTimeout(function() { domUtilityService.RebuildGrid($scope,grid); }, 100); }); var parentThrottle; $(grid.$root.parent()).on('resize', function() { clearTimeout(parentThrottle); parentThrottle = setTimeout(function() { domUtilityService.RebuildGrid($scope,grid); }, 100); }); }; self.assignGridEventHandlers(); self.assignEvents(); }; var ngFooter = function ($scope, grid) { $scope.maxRows = function () { var ret = Math.max($scope.totalServerItems, grid.data.length); return ret; }; $scope.multiSelect = (grid.config.enableRowSelection && grid.config.multiSelect); $scope.selectedItemCount = grid.selectedItemCount; $scope.maxPages = function () { return Math.ceil($scope.maxRows() / $scope.pagingOptions.pageSize); }; $scope.pageForward = function() { var page = $scope.pagingOptions.currentPage; if ($scope.totalServerItems > 0) { $scope.pagingOptions.currentPage = Math.min(page + 1, $scope.maxPages()); } else { $scope.pagingOptions.currentPage++; } }; $scope.pageBackward = function() { var page = $scope.pagingOptions.currentPage; $scope.pagingOptions.currentPage = Math.max(page - 1, 1); }; $scope.pageToFirst = function() { $scope.pagingOptions.currentPage = 1; }; $scope.pageToLast = function() { var maxPages = $scope.maxPages(); $scope.pagingOptions.currentPage = maxPages; }; $scope.cantPageForward = function() { var curPage = $scope.pagingOptions.currentPage; var maxPages = $scope.maxPages(); if ($scope.totalServerItems > 0) { return curPage >= maxPages; } else { return grid.data.length < 1; } }; $scope.cantPageToLast = function() { if ($scope.totalServerItems > 0) { return $scope.cantPageForward(); } else { return true; } }; $scope.cantPageBackward = function() { var curPage = $scope.pagingOptions.currentPage; return curPage <= 1; }; }; var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q) { var defaults = { aggregateTemplate: undefined, afterSelectionChange: function() { }, beforeSelectionChange: function() { return true; }, checkboxCellTemplate: undefined, checkboxHeaderTemplate: undefined, columnDefs: undefined, data: [], dataUpdated: function() { }, enableCellEdit: false, enableCellEditOnFocus: false, enableCellSelection: false, enableColumnResize: false, enableColumnReordering: false, enableColumnHeavyVirt: false, enablePaging: false, enablePinning: false, enableRowSelection: true, enableSorting: true, enableHighlighting: false, excludeProperties: [], filterOptions: { filterText: "", useExternalFilter: false }, footerRowHeight: 55, footerTemplate: undefined, groups: [], groupsCollapsedByDefault: true, headerRowHeight: 30, headerRowTemplate: undefined, jqueryUIDraggable: false, jqueryUITheme: false, keepLastSelected: true, maintainColumnRatios: undefined, menuTemplate: undefined, multiSelect: true, pagingOptions: { pageSizes: [250, 500, 1000], pageSize: 250, currentPage: 1 }, pinSelectionCheckbox: false, plugins: [], primaryKey: undefined, rowHeight: 30, rowTemplate: undefined, selectedItems: [], selectWithCheckboxOnly: false, showColumnMenu: false, showFilter: false, showFooter: false, showGroupPanel: false, showSelectionCheckbox: false, sortInfo: {fields: [], columns: [], directions: [] }, tabIndex: -1, totalServerItems: 0, useExternalSorting: false, i18n: 'en', virtualizationThreshold: 50 }, self = this; self.maxCanvasHt = 0; self.config = $.extend(defaults, window.ngGrid.config, options); self.config.showSelectionCheckbox = (self.config.showSelectionCheckbox && self.config.enableColumnHeavyVirt === false); self.config.enablePinning = (self.config.enablePinning && self.config.enableColumnHeavyVirt === false); self.config.selectWithCheckboxOnly = (self.config.selectWithCheckboxOnly && self.config.showSelectionCheckbox !== false); self.config.pinSelectionCheckbox = self.config.enablePinning; if (typeof options.columnDefs === "string") { self.config.columnDefs = $scope.$eval(options.columnDefs); } self.rowCache = []; self.rowMap = []; self.gridId = "ng" + $utils.newId(); self.$root = null; self.$groupPanel = null; self.$topPanel = null; self.$headerContainer = null; self.$headerScroller = null; self.$headers = null; self.$viewport = null; self.$canvas = null; self.rootDim = self.config.gridDim; self.data = []; self.lateBindColumns = false; self.filteredRows = []; self.initTemplates = function() { var templates = ['rowTemplate', 'aggregateTemplate', 'headerRowTemplate', 'checkboxCellTemplate', 'checkboxHeaderTemplate', 'menuTemplate', 'footerTemplate']; var promises = []; angular.forEach(templates, function(template) { promises.push( self.getTemplate(template) ); }); return $q.all(promises); }; self.getTemplate = function (key) { var t = self.config[key]; var uKey = self.gridId + key + ".html"; var p = $q.defer(); if (t && !TEMPLATE_REGEXP.test(t)) { $http.get(t, { cache: $templateCache }) .success(function(data){ $templateCache.put(uKey, data); p.resolve(); }) .error(function(err){ p.reject("Could not load template: " + t); }); } else if (t) { $templateCache.put(uKey, t); p.resolve(); } else { var dKey = key + ".html"; $templateCache.put(uKey, $templateCache.get(dKey)); p.resolve(); } return p.promise; }; if (typeof self.config.data === "object") { self.data = self.config.data; } self.calcMaxCanvasHeight = function() { var calculatedHeight; if(self.config.groups.length > 0){ calculatedHeight = self.rowFactory.parsedData.filter(function(e) { return !e[NG_HIDDEN]; }).length * self.config.rowHeight; } else { calculatedHeight = self.filteredRows.length * self.config.rowHeight; } return calculatedHeight; }; self.elementDims = { scrollW: 0, scrollH: 0, rowIndexCellW: 25, rowSelectedCellW: 25, rootMaxW: 0, rootMaxH: 0 }; self.setRenderedRows = function (newRows) { $scope.renderedRows.length = newRows.length; for (var i = 0; i < newRows.length; i++) { if (!$scope.renderedRows[i] || (newRows[i].isAggRow || $scope.renderedRows[i].isAggRow)) { $scope.renderedRows[i] = newRows[i].copy(); $scope.renderedRows[i].collapsed = newRows[i].collapsed; if (!newRows[i].isAggRow) { $scope.renderedRows[i].setVars(newRows[i]); } } else { $scope.renderedRows[i].setVars(newRows[i]); } $scope.renderedRows[i].rowIndex = newRows[i].rowIndex; $scope.renderedRows[i].offsetTop = newRows[i].offsetTop; $scope.renderedRows[i].selected = newRows[i].selected; newRows[i].renderedRowIndex = i; } self.refreshDomSizes(); $scope.$emit('ngGridEventRows', newRows); }; self.minRowsToRender = function() { var viewportH = $scope.viewportDimHeight() || 1; return Math.floor(viewportH / self.config.rowHeight); }; self.refreshDomSizes = function() { var dim = new ngDimension(); dim.outerWidth = self.elementDims.rootMaxW; dim.outerHeight = self.elementDims.rootMaxH; self.rootDim = dim; self.maxCanvasHt = self.calcMaxCanvasHeight(); }; self.buildColumnDefsFromData = function () { self.config.columnDefs = []; var item = self.data[0]; if (!item) { self.lateBoundColumns = true; return; } $utils.forIn(item, function (prop, propName) { if (self.config.excludeProperties.indexOf(propName) === -1) { self.config.columnDefs.push({ field: propName }); } }); }; self.buildColumns = function() { var columnDefs = self.config.columnDefs, cols = []; if (!columnDefs) { self.buildColumnDefsFromData(); columnDefs = self.config.columnDefs; } if (self.config.showSelectionCheckbox) { cols.push(new ngColumn({ colDef: { field: '\u2714', width: self.elementDims.rowSelectedCellW, sortable: false, resizable: false, groupable: false, headerCellTemplate: $templateCache.get($scope.gridId + 'checkboxHeaderTemplate.html'), cellTemplate: $templateCache.get($scope.gridId + 'checkboxCellTemplate.html'), pinned: self.config.pinSelectionCheckbox }, index: 0, headerRowHeight: self.config.headerRowHeight, sortCallback: self.sortData, resizeOnDataCallback: self.resizeOnData, enableResize: self.config.enableColumnResize, enableSort: self.config.enableSorting, enablePinning: self.config.enablePinning }, $scope, self, domUtilityService, $templateCache, $utils)); } if (columnDefs.length > 0) { var checkboxOffset = self.config.showSelectionCheckbox ? 1 : 0; var groupOffset = $scope.configGroups.length; $scope.configGroups.length = 0; angular.forEach(columnDefs, function(colDef, i) { i += checkboxOffset; var column = new ngColumn({ colDef: colDef, index: i + groupOffset, originalIndex: i, headerRowHeight: self.config.headerRowHeight, sortCallback: self.sortData, resizeOnDataCallback: self.resizeOnData, enableResize: self.config.enableColumnResize, enableSort: self.config.enableSorting, enablePinning: self.config.enablePinning, enableCellEdit: self.config.enableCellEdit || self.config.enableCellEditOnFocus }, $scope, self, domUtilityService, $templateCache, $utils); var indx = self.config.groups.indexOf(colDef.field); if (indx !== -1) { column.isGroupedBy = true; $scope.configGroups.splice(indx, 0, column); column.groupIndex = $scope.configGroups.length; } cols.push(column); }); $scope.columns = cols; if (self.config.groups.length > 0) { self.rowFactory.getGrouping(self.config.groups); } } }; self.configureColumnWidths = function() { var asterisksArray = [], percentArray = [], asteriskNum = 0, totalWidth = 0; var indexMap = {}; angular.forEach($scope.columns, function(ngCol, i) { if (!$utils.isNullOrUndefined(ngCol.originalIndex)) { var origIndex = ngCol.originalIndex; if (self.config.showSelectionCheckbox) { if(ngCol.originalIndex === 0 && ngCol.visible){ totalWidth += 25; } origIndex--; } indexMap[origIndex] = i; } }); angular.forEach(self.config.columnDefs, function(colDef, i) { var ngColumn = $scope.columns[indexMap[i]]; colDef.index = i; var isPercent = false, t; if ($utils.isNullOrUndefined(colDef.width)) { colDef.width = "*"; } else { isPercent = isNaN(colDef.width) ? $utils.endsWith(colDef.width, "%") : false; t = isPercent ? colDef.width : parseInt(colDef.width, 10); } if (isNaN(t) && !$scope.hasUserChangedGridColumnWidths) { t = colDef.width; if (t === 'auto') { ngColumn.width = ngColumn.minWidth; totalWidth += ngColumn.width; var temp = ngColumn; $scope.$on("ngGridEventData", function () { self.resizeOnData(temp); }); return; } else if (t.indexOf("*") !== -1) { if (ngColumn.visible !== false) { asteriskNum += t.length; } asterisksArray.push(colDef); return; } else if (isPercent) { percentArray.push(colDef); return; } else { throw "unable to parse column width, use percentage (\"10%\",\"20%\", etc...) or \"*\" to use remaining width of grid"; } } else if (ngColumn.visible !== false) { totalWidth += ngColumn.width = parseInt(ngColumn.width, 10); } }); if (percentArray.length > 0) { self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false; var percentWidth = 0; var hiddenPercent = 0; angular.forEach(percentArray, function(colDef) { var ngColumn = $scope.columns[indexMap[colDef.index]]; var t = colDef.width; var percent = parseInt(t.slice(0, -1), 10) / 100; percentWidth += percent; if (!ngColumn.visible) { hiddenPercent += percent; } }); var percentWidthUsed = percentWidth - hiddenPercent; angular.forEach(percentArray, function(colDef) { var ngColumn = $scope.columns[indexMap[colDef.index]]; var t = colDef.width; var percent = parseInt(t.slice(0, -1), 10) / 100; if (hiddenPercent > 0) { percent = percent / percentWidthUsed; } else { percent = percent / percentWidth; } var pixelsForPercentBasedWidth = self.rootDim.outerWidth * percentWidth; ngColumn.width = Math.floor(pixelsForPercentBasedWidth * percent); totalWidth += ngColumn.width; }); } if (asterisksArray.length > 0) { self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false; var remainingWidth = self.rootDim.outerWidth - totalWidth; if (self.maxCanvasHt > $scope.viewportDimHeight()) { remainingWidth -= domUtilityService.ScrollW; } var asteriskVal = Math.floor(remainingWidth / asteriskNum); angular.forEach(asterisksArray, function(colDef, i) { var ngColumn = $scope.columns[indexMap[colDef.index]]; ngColumn.width = asteriskVal * colDef.width.length; if (ngColumn.visible !== false) { totalWidth += ngColumn.width; } var isLast = (i === (asterisksArray.length - 1)); if(isLast && totalWidth < self.rootDim.outerWidth){ var gridWidthDifference = self.rootDim.outerWidth - totalWidth; if(self.maxCanvasHt > $scope.viewportDimHeight()){ gridWidthDifference -= domUtilityService.ScrollW; } ngColumn.width += gridWidthDifference; } }); } }; self.init = function() { return self.initTemplates().then(function(){ $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse); $scope.domAccessProvider = new ngDomAccessProvider(self); self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils); self.searchProvider = new ngSearchProvider($scope, self, $filter); self.styleProvider = new ngStyleProvider($scope, self); $scope.$watch('configGroups', function(a) { var tempArr = []; angular.forEach(a, function(item) { tempArr.push(item.field || item); }); self.config.groups = tempArr; self.rowFactory.filteredRowsChanged(); $scope.$emit('ngGridEventGroups', a); }, true); $scope.$watch('columns', function (a) { if(!$scope.isColumnResizing){ domUtilityService.RebuildGrid($scope, self); } $scope.$emit('ngGridEventColumns', a); }, true); $scope.$watch(function() { return options.i18n; }, function(newLang) { $utils.seti18n($scope, newLang); }); self.maxCanvasHt = self.calcMaxCanvasHeight(); if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) { $scope.$watch(function() { return self.config.sortInfo; }, function(sortInfo){ if (!sortService.isSorting) { self.sortColumnsInit(); $scope.$emit('ngGridEventSorted', self.config.sortInfo); } },true); } }); }; self.resizeOnData = function(col) { var longest = col.minWidth; var arr = $utils.getElementsByClassName('col' + col.index); angular.forEach(arr, function(elem, index) { var i; if (index === 0) { var kgHeaderText = $(elem).find('.ngHeaderText'); i = $utils.visualLength(kgHeaderText) + 10; } else { var ngCellText = $(elem).find('.ngCellText'); i = $utils.visualLength(ngCellText) + 10; } if (i > longest) { longest = i; } }); col.width = col.longest = Math.min(col.maxWidth, longest + 7); domUtilityService.BuildStyles($scope, self, true); }; self.lastSortedColumns = []; self.sortData = function(col, evt) { if (evt && evt.shiftKey && self.config.sortInfo) { var indx = self.config.sortInfo.columns.indexOf(col); if (indx === -1) { if (self.config.sortInfo.columns.length === 1) { self.config.sortInfo.columns[0].sortPriority = 1; } self.config.sortInfo.columns.push(col); col.sortPriority = self.config.sortInfo.columns.length; self.config.sortInfo.fields.push(col.field); self.config.sortInfo.directions.push(col.sortDirection); self.lastSortedColumns.push(col); } else { self.config.sortInfo.directions[indx] = col.sortDirection; } } else { var isArr = $.isArray(col); self.config.sortInfo.columns.length = 0; self.config.sortInfo.fields.length = 0; self.config.sortInfo.directions.length = 0; var push = function (c) { self.config.sortInfo.columns.push(c); self.config.sortInfo.fields.push(c.field); self.config.sortInfo.directions.push(c.sortDirection); self.lastSortedColumns.push(c); }; if (isArr) { self.clearSortingData(); angular.forEach(col, function (c, i) { c.sortPriority = i + 1; push(c); }); } else { self.clearSortingData(col); col.sortPriority = undefined; push(col); } } self.sortActual(); self.searchProvider.evalFilter(); $scope.$emit('ngGridEventSorted', self.config.sortInfo); }; self.sortColumnsInit = function() { if (self.config.sortInfo.columns) { self.config.sortInfo.columns.length = 0; } else { self.config.sortInfo.columns = []; } angular.forEach($scope.columns, function(c) { var i = self.config.sortInfo.fields.indexOf(c.field); if (i !== -1) { c.sortDirection = self.config.sortInfo.directions[i] || 'asc'; self.config.sortInfo.columns[i] = c; } }); angular.forEach(self.config.sortInfo.columns, function(c){ self.sortData(c); }); }; self.sortActual = function() { if (!self.config.useExternalSorting) { var tempData = self.data.slice(0); angular.forEach(tempData, function(item, i) { var e = self.rowMap[i]; if (e !== undefined) { var v = self.rowCache[e]; if (v !== undefined) { item.preSortSelected = v.selected; item.preSortIndex = i; } } }); sortService.Sort(self.config.sortInfo, tempData); angular.forEach(tempData, function(item, i) { self.rowCache[i].entity = item; self.rowCache[i].selected = item.preSortSelected; self.rowMap[item.preSortIndex] = i; delete item.preSortSelected; delete item.preSortIndex; }); } }; self.clearSortingData = function (col) { if (!col) { angular.forEach(self.lastSortedColumns, function (c) { c.sortDirection = ""; c.sortPriority = null; }); self.lastSortedColumns = []; } else { angular.forEach(self.lastSortedColumns, function (c) { if (col.index !== c.index) { c.sortDirection = ""; c.sortPriority = null; } }); self.lastSortedColumns[0] = col; self.lastSortedColumns.length = 1; } }; self.fixColumnIndexes = function() { for (var i = 0; i < $scope.columns.length; i++) { $scope.columns[i].index = i; } }; self.fixGroupIndexes = function() { angular.forEach($scope.configGroups, function(item, i) { item.groupIndex = i + 1; }); }; $scope.elementsNeedMeasuring = true; $scope.columns = []; $scope.renderedRows = []; $scope.renderedColumns = []; $scope.headerRow = null; $scope.rowHeight = self.config.rowHeight; $scope.jqueryUITheme = self.config.jqueryUITheme; $scope.showSelectionCheckbox = self.config.showSelectionCheckbox; $scope.enableCellSelection = self.config.enableCellSelection; $scope.enableCellEditOnFocus = self.config.enableCellEditOnFocus; $scope.footer = null; $scope.selectedItems = self.config.selectedItems; $scope.multiSelect = self.config.multiSelect; $scope.showFooter = self.config.showFooter; $scope.footerRowHeight = $scope.showFooter ? self.config.footerRowHeight : 0; $scope.showColumnMenu = self.config.showColumnMenu; $scope.showMenu = false; $scope.configGroups = []; $scope.gridId = self.gridId; $scope.enablePaging = self.config.enablePaging; $scope.pagingOptions = self.config.pagingOptions; $scope.i18n = {}; $utils.seti18n($scope, self.config.i18n); $scope.adjustScrollLeft = function (scrollLeft) { var colwidths = 0, totalLeft = 0, x = $scope.columns.length, newCols = [], dcv = !self.config.enableColumnHeavyVirt; var r = 0; var addCol = function (c) { if (dcv) { newCols.push(c); } else { if (!$scope.renderedColumns[r]) { $scope.renderedColumns[r] = c.copy(); } else { $scope.renderedColumns[r].setVars(c); } } r++; }; for (var i = 0; i < x; i++) { var col = $scope.columns[i]; if (col.visible !== false) { var w = col.width + colwidths; if (col.pinned) { addCol(col); var newLeft = i > 0 ? (scrollLeft + totalLeft) : scrollLeft; domUtilityService.setColLeft(col, newLeft, self); totalLeft += col.width; } else { if (w >= scrollLeft) { if (colwidths <= scrollLeft + self.rootDim.outerWidth) { addCol(col); } } } colwidths += col.width; } } if (dcv) { $scope.renderedColumns = newCols; } }; self.prevScrollTop = 0; self.prevScrollIndex = 0; $scope.adjustScrollTop = function(scrollTop, force) { if (self.prevScrollTop === scrollTop && !force) { return; } if (scrollTop > 0 && self.$viewport[0].scrollHeight - scrollTop <= self.$viewport.outerHeight()) { $scope.$emit('ngGridEventScroll'); } var rowIndex = Math.floor(scrollTop / self.config.rowHeight); var newRange; if (self.filteredRows.length > self.config.virtualizationThreshold) { if (self.prevScrollTop < scrollTop && rowIndex < self.prevScrollIndex + SCROLL_THRESHOLD) { return; } if (self.prevScrollTop > scrollTop && rowIndex > self.prevScrollIndex - SCROLL_THRESHOLD) { return; } newRange = new ngRange(Math.max(0, rowIndex - EXCESS_ROWS), rowIndex + self.minRowsToRender() + EXCESS_ROWS); } else { var maxLen = $scope.configGroups.length > 0 ? self.rowFactory.parsedData.length : self.data.length; newRange = new ngRange(0, Math.max(maxLen, self.minRowsToRender() + EXCESS_ROWS)); } self.prevScrollTop = scrollTop; self.rowFactory.UpdateViewableRange(newRange); self.prevScrollIndex = rowIndex; }; $scope.toggleShowMenu = function() { $scope.showMenu = !$scope.showMenu; }; $scope.toggleSelectAll = function(state, selectOnlyVisible) { $scope.selectionProvider.toggleSelectAll(state, false, selectOnlyVisible); }; $scope.totalFilteredItemsLength = function() { return self.filteredRows.length; }; $scope.showGroupPanel = function() { return self.config.showGroupPanel; }; $scope.topPanelHeight = function() { return self.config.showGroupPanel === true ? self.config.headerRowHeight + 32 : self.config.headerRowHeight; }; $scope.viewportDimHeight = function() { return Math.max(0, self.rootDim.outerHeight - $scope.topPanelHeight() - $scope.footerRowHeight - 2); }; $scope.groupBy = function (col) { if (self.data.length < 1 || !col.groupable || !col.field) { return; } if (!col.sortDirection) { col.sort({ shiftKey: $scope.configGroups.length > 0 ? true : false }); } var indx = $scope.configGroups.indexOf(col); if (indx === -1) { col.isGroupedBy = true; $scope.configGroups.push(col); col.groupIndex = $scope.configGroups.length; } else { $scope.removeGroup(indx); } self.$viewport.scrollTop(0); domUtilityService.digest($scope); }; $scope.removeGroup = function(index) { var col = $scope.columns.filter(function(item) { return item.groupIndex === (index + 1); })[0]; col.isGroupedBy = false; col.groupIndex = 0; if ($scope.columns[index].isAggCol) { $scope.columns.splice(index, 1); $scope.configGroups.splice(index, 1); self.fixGroupIndexes(); } if ($scope.configGroups.length === 0) { self.fixColumnIndexes(); domUtilityService.digest($scope); } $scope.adjustScrollLeft(0); }; $scope.togglePin = function (col) { var indexFrom = col.index; var indexTo = 0; for (var i = 0; i < $scope.columns.length; i++) { if (!$scope.columns[i].pinned) { break; } indexTo++; } if (col.pinned) { indexTo = Math.max(col.originalIndex, indexTo - 1); } col.pinned = !col.pinned; $scope.columns.splice(indexFrom, 1); $scope.columns.splice(indexTo, 0, col); self.fixColumnIndexes(); domUtilityService.BuildStyles($scope, self, true); self.$viewport.scrollLeft(self.$viewport.scrollLeft() - col.width); }; $scope.totalRowWidth = function() { var totalWidth = 0, cols = $scope.columns; for (var i = 0; i < cols.length; i++) { if (cols[i].visible !== false) { totalWidth += cols[i].width; } } return totalWidth; }; $scope.headerScrollerDim = function() { var viewportH = $scope.viewportDimHeight(), maxHeight = self.maxCanvasHt, vScrollBarIsOpen = (maxHeight > viewportH), newDim = new ngDimension(); newDim.autoFitHeight = true; newDim.outerWidth = $scope.totalRowWidth(); if (vScrollBarIsOpen) { newDim.outerWidth += self.elementDims.scrollW; } else if ((maxHeight - viewportH) <= self.elementDims.scrollH) { newDim.outerWidth += self.elementDims.scrollW; } return newDim; }; }; var ngRange = function (top, bottom) { this.topRow = top; this.bottomRow = bottom; }; var ngRow = function (entity, config, selectionProvider, rowIndex, $utils) { this.entity = entity; this.config = config; this.selectionProvider = selectionProvider; this.rowIndex = rowIndex; this.utils = $utils; this.selected = selectionProvider.getSelection(entity); this.cursor = this.config.enableRowSelection ? 'pointer' : 'default'; this.beforeSelectionChange = config.beforeSelectionChangeCallback; this.afterSelectionChange = config.afterSelectionChangeCallback; this.offsetTop = this.rowIndex * config.rowHeight; this.rowDisplayIndex = 0; }; ngRow.prototype.setSelection = function (isSelected) { this.selectionProvider.setSelection(this, isSelected); this.selectionProvider.lastClickedRow = this; }; ngRow.prototype.continueSelection = function (event) { this.selectionProvider.ChangeSelection(this, event); }; ngRow.prototype.ensureEntity = function (expected) { if (this.entity !== expected) { this.entity = expected; this.selected = this.selectionProvider.getSelection(this.entity); } }; ngRow.prototype.toggleSelected = function (event) { if (!this.config.enableRowSelection && !this.config.enableCellSelection) { return true; } var element = event.target || event; if (element.type === "checkbox" && element.parentElement.className !== "ngSelectionCell ng-scope") { return true; } if (this.config.selectWithCheckboxOnly && element.type !== "checkbox") { this.selectionProvider.lastClickedRow = this; return true; } if (this.beforeSelectionChange(this, event)) { this.continueSelection(event); } return false; }; ngRow.prototype.alternatingRowClass = function () { var isEven = (this.rowIndex % 2) === 0; var classes = { 'ngRow' : true, 'selected': this.selected, 'even': isEven, 'odd': !isEven, 'ui-state-default': this.config.jqueryUITheme && isEven, 'ui-state-active': this.config.jqueryUITheme && !isEven }; return classes; }; ngRow.prototype.getProperty = function (path) { return this.utils.evalProperty(this.entity, path); }; ngRow.prototype.copy = function () { this.clone = new ngRow(this.entity, this.config, this.selectionProvider, this.rowIndex, this.utils); this.clone.isClone = true; this.clone.elm = this.elm; this.clone.orig = this; return this.clone; }; ngRow.prototype.setVars = function (fromRow) { fromRow.clone = this; this.entity = fromRow.entity; this.selected = fromRow.selected; this.orig = fromRow; }; var ngRowFactory = function (grid, $scope, domUtilityService, $templateCache, $utils) { var self = this; self.aggCache = {}; self.parentCache = []; self.dataChanged = true; self.parsedData = []; self.rowConfig = {}; self.selectionProvider = $scope.selectionProvider; self.rowHeight = 30; self.numberOfAggregates = 0; self.groupedData = undefined; self.rowHeight = grid.config.rowHeight; self.rowConfig = { enableRowSelection: grid.config.enableRowSelection, rowClasses: grid.config.rowClasses, selectedItems: $scope.selectedItems, selectWithCheckboxOnly: grid.config.selectWithCheckboxOnly, beforeSelectionChangeCallback: grid.config.beforeSelectionChange, afterSelectionChangeCallback: grid.config.afterSelectionChange, jqueryUITheme: grid.config.jqueryUITheme, enableCellSelection: grid.config.enableCellSelection, rowHeight: grid.config.rowHeight }; self.renderedRange = new ngRange(0, grid.minRowsToRender() + EXCESS_ROWS); self.buildEntityRow = function(entity, rowIndex) { return new ngRow(entity, self.rowConfig, self.selectionProvider, rowIndex, $utils); }; self.buildAggregateRow = function(aggEntity, rowIndex) { var agg = self.aggCache[aggEntity.aggIndex]; if (!agg) { agg = new ngAggregate(aggEntity, self, self.rowConfig.rowHeight, grid.config.groupsCollapsedByDefault); self.aggCache[aggEntity.aggIndex] = agg; } agg.rowIndex = rowIndex; agg.offsetTop = rowIndex * self.rowConfig.rowHeight; return agg; }; self.UpdateViewableRange = function(newRange) { self.renderedRange = newRange; self.renderedChange(); }; self.filteredRowsChanged = function() { if (grid.lateBoundColumns && grid.filteredRows.length > 0) { grid.config.columnDefs = undefined; grid.buildColumns(); grid.lateBoundColumns = false; $scope.$evalAsync(function() { $scope.adjustScrollLeft(0); }); } self.dataChanged = true; if (grid.config.groups.length > 0) { self.getGrouping(grid.config.groups); } self.UpdateViewableRange(self.renderedRange); }; self.renderedChange = function() { if (!self.groupedData || grid.config.groups.length < 1) { self.renderedChangeNoGroups(); grid.refreshDomSizes(); return; } self.wasGrouped = true; self.parentCache = []; var x = 0; var temp = self.parsedData.filter(function (e) { if (e.isAggRow) { if (e.parent && e.parent.collapsed) { return false; } return true; } if (!e[NG_HIDDEN]) { e.rowIndex = x++; } return !e[NG_HIDDEN]; }); self.totalRows = temp.length; var rowArr = []; for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) { if (temp[i]) { temp[i].offsetTop = i * grid.config.rowHeight; rowArr.push(temp[i]); } } grid.setRenderedRows(rowArr); }; self.renderedChangeNoGroups = function () { var rowArr = []; for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) { if (grid.filteredRows[i]) { grid.filteredRows[i].rowIndex = i; grid.filteredRows[i].offsetTop = i * grid.config.rowHeight; rowArr.push(grid.filteredRows[i]); } } grid.setRenderedRows(rowArr); }; self.fixRowCache = function () { var newLen = grid.data.length; var diff = newLen - grid.rowCache.length; if (diff < 0) { grid.rowCache.length = grid.rowMap.length = newLen; } else { for (var i = grid.rowCache.length; i < newLen; i++) { grid.rowCache[i] = grid.rowFactory.buildEntityRow(grid.data[i], i); } } }; self.parseGroupData = function(g) { if (g.values) { for (var x = 0; x < g.values.length; x++){ self.parentCache[self.parentCache.length - 1].children.push(g.values[x]); self.parsedData.push(g.values[x]); } } else { for (var prop in g) { if (prop === NG_FIELD || prop === NG_DEPTH || prop === NG_COLUMN) { continue; } else if (g.hasOwnProperty(prop)) { var agg = self.buildAggregateRow({ gField: g[NG_FIELD], gLabel: prop, gDepth: g[NG_DEPTH], isAggRow: true, '_ng_hidden_': false, children: [], aggChildren: [], aggIndex: self.numberOfAggregates, aggLabelFilter: g[NG_COLUMN].aggLabelFilter }, 0); self.numberOfAggregates++; agg.parent = self.parentCache[agg.depth - 1]; if (agg.parent) { agg.parent.collapsed = false; agg.parent.aggChildren.push(agg); } self.parsedData.push(agg); self.parentCache[agg.depth] = agg; self.parseGroupData(g[prop]); } } } }; self.getGrouping = function(groups) { self.aggCache = []; self.numberOfAggregates = 0; self.groupedData = {}; var rows = grid.filteredRows, maxDepth = groups.length, cols = $scope.columns; function filterCols(cols, group) { return cols.filter(function(c) { return c.field === group; }); } for (var x = 0; x < rows.length; x++) { var model = rows[x].entity; if (!model) { return; } rows[x][NG_HIDDEN] = grid.config.groupsCollapsedByDefault; var ptr = self.groupedData; for (var y = 0; y < groups.length; y++) { var group = groups[y]; var col = filterCols(cols, group)[0]; var val = $utils.evalProperty(model, group); val = val ? val.toString() : 'null'; if (!ptr[val]) { ptr[val] = {}; } if (!ptr[NG_FIELD]) { ptr[NG_FIELD] = group; } if (!ptr[NG_DEPTH]) { ptr[NG_DEPTH] = y; } if (!ptr[NG_COLUMN]) { ptr[NG_COLUMN] = col; } ptr = ptr[val]; } if (!ptr.values) { ptr.values = []; } ptr.values.push(rows[x]); } if(cols.length > 0) { for (var z = 0; z < groups.length; z++) { if (!cols[z].isAggCol && z <= maxDepth) { cols.splice(0, 0, new ngColumn({ colDef: { field: '', width: 25, sortable: false, resizable: false, headerCellTemplate: '', pinned: grid.config.pinSelectionCheckbox }, enablePinning: grid.config.enablePinning, isAggCol: true, headerRowHeight: grid.config.headerRowHeight }, $scope, grid, domUtilityService, $templateCache, $utils)); } } } grid.fixColumnIndexes(); $scope.adjustScrollLeft(0); self.parsedData.length = 0; self.parseGroupData(self.groupedData); self.fixRowCache(); }; if (grid.config.groups.length > 0 && grid.filteredRows.length > 0) { self.getGrouping(grid.config.groups); } }; var ngSearchProvider = function ($scope, grid, $filter) { var self = this, searchConditions = []; self.extFilter = grid.config.filterOptions.useExternalFilter; $scope.showFilter = grid.config.showFilter; $scope.filterText = ''; self.fieldMap = {}; var searchEntireRow = function(condition, item, fieldMap){ var result; for (var prop in item) { if (item.hasOwnProperty(prop)) { var c = fieldMap[prop.toLowerCase()]; if (!c) { continue; } var pVal = item[prop]; if(typeof pVal === 'object'){ return searchEntireRow(condition, pVal, c); } else { var f = null, s = null; if (c && c.cellFilter) { s = c.cellFilter.split(':'); f = $filter(s[0]); } if (pVal !== null && pVal !== undefined) { if (typeof f === "function") { var filterRes = f(pVal, s[1]).toString(); result = condition.regex.test(filterRes); } else { result = condition.regex.test(pVal.toString()); } if (result) { return true; } } } } } return false; }; var searchColumn = function(condition, item){ var result; var col = self.fieldMap[condition.columnDisplay]; if (!col) { return false; } var sp = col.cellFilter.split(':'); var filter = col.cellFilter ? $filter(sp[0]) : null; var value = item[condition.column] || item[col.field.split('.')[0]]; if (value === null || value === undefined) { return false; } if (typeof filter === "function") { var filterResults = filter(typeof value === "object" ? evalObject(value, col.field) : value, sp[1]).toString(); result = condition.regex.test(filterResults); } else { result = condition.regex.test(typeof value === "object" ? evalObject(value, col.field).toString() : value.toString()); } if (result) { return true; } return false; }; var filterFunc = function(item) { for (var x = 0, len = searchConditions.length; x < len; x++) { var condition = searchConditions[x]; var result; if (!condition.column) { result = searchEntireRow(condition, item, self.fieldMap); } else { result = searchColumn(condition, item); } if(!result) { return false; } } return true; }; self.evalFilter = function () { if (searchConditions.length === 0) { grid.filteredRows = grid.rowCache; } else { grid.filteredRows = grid.rowCache.filter(function(row) { return filterFunc(row.entity); }); } for (var i = 0; i < grid.filteredRows.length; i++) { grid.filteredRows[i].rowIndex = i; } grid.rowFactory.filteredRowsChanged(); }; var evalObject = function (obj, columnName) { if (typeof obj !== "object" || typeof columnName !== "string") { return obj; } var args = columnName.split('.'); var cObj = obj; if (args.length > 1) { for (var i = 1, len = args.length; i < len; i++) { cObj = cObj[args[i]]; if (!cObj) { return obj; } } return cObj; } return obj; }; var getRegExp = function (str, modifiers) { try { return new RegExp(str, modifiers); } catch (err) { return new RegExp(str.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\\|\||\.|\*|\+|\?)/g, '\\$1')); } }; var buildSearchConditions = function (a) { searchConditions = []; var qStr; if (!(qStr = $.trim(a))) { return; } var columnFilters = qStr.split(";"); for (var i = 0; i < columnFilters.length; i++) { var args = columnFilters[i].split(':'); if (args.length > 1) { var columnName = $.trim(args[0]); var columnValue = $.trim(args[1]); if (columnName && columnValue) { searchConditions.push({ column: columnName, columnDisplay: columnName.replace(/\s+/g, '').toLowerCase(), regex: getRegExp(columnValue, 'i') }); } } else { var val = $.trim(args[0]); if (val) { searchConditions.push({ column: '', regex: getRegExp(val, 'i') }); } } } }; if (!self.extFilter) { $scope.$watch('columns', function (cs) { for (var i = 0; i < cs.length; i++) { var col = cs[i]; if (col.field) { if(col.field.match(/\./g)){ var properties = col.field.split('.'); var currentProperty = self.fieldMap; for(var j = 0; j < properties.length - 1; j++) { currentProperty[ properties[j] ] = currentProperty[ properties[j] ] || {}; currentProperty = currentProperty[properties[j]]; } currentProperty[ properties[properties.length - 1] ] = col; } else { self.fieldMap[col.field.toLowerCase()] = col; } } if (col.displayName) { self.fieldMap[col.displayName.toLowerCase().replace(/\s+/g, '')] = col; } } }); } $scope.$watch( function () { return grid.config.filterOptions.filterText; }, function (a) { $scope.filterText = a; } ); $scope.$watch('filterText', function(a){ if (!self.extFilter) { $scope.$emit('ngGridEventFilter', a); buildSearchConditions(a); self.evalFilter(); } }); }; var ngSelectionProvider = function (grid, $scope, $parse) { var self = this; self.multi = grid.config.multiSelect; self.selectedItems = grid.config.selectedItems; self.selectedIndex = grid.config.selectedIndex; self.lastClickedRow = undefined; self.ignoreSelectedItemChanges = false; self.pKeyParser = $parse(grid.config.primaryKey); self.ChangeSelection = function (rowItem, evt) { var charCode = evt.which || evt.keyCode; var isUpDownKeyPress = (charCode === 40 || charCode === 38); if (evt && evt.shiftKey && !evt.keyCode && self.multi && grid.config.enableRowSelection) { if (self.lastClickedRow) { var rowsArr; if ($scope.configGroups.length > 0) { rowsArr = grid.rowFactory.parsedData.filter(function(row) { return !row.isAggRow; }); } else { rowsArr = grid.filteredRows; } var thisIndx = rowItem.rowIndex; var prevIndx = self.lastClickedRowIndex; if (thisIndx === prevIndx) { return false; } if (thisIndx < prevIndx) { thisIndx = thisIndx ^ prevIndx; prevIndx = thisIndx ^ prevIndx; thisIndx = thisIndx ^ prevIndx; thisIndx--; } else { prevIndx++; } var rows = []; for (; prevIndx <= thisIndx; prevIndx++) { rows.push(rowsArr[prevIndx]); } if (rows[rows.length - 1].beforeSelectionChange(rows, evt)) { for (var i = 0; i < rows.length; i++) { var ri = rows[i]; var selectionState = ri.selected; ri.selected = !selectionState; if (ri.clone) { ri.clone.selected = ri.selected; } var index = self.selectedItems.indexOf(ri.entity); if (index === -1) { self.selectedItems.push(ri.entity); } else { self.selectedItems.splice(index, 1); } } rows[rows.length - 1].afterSelectionChange(rows, evt); } self.lastClickedRow = rowItem; self.lastClickedRowIndex = rowItem.rowIndex; return true; } } else if (!self.multi) { if (self.lastClickedRow === rowItem) { self.setSelection(self.lastClickedRow, grid.config.keepLastSelected ? true : !rowItem.selected); } else { if (self.lastClickedRow) { self.setSelection(self.lastClickedRow, false); } self.setSelection(rowItem, !rowItem.selected); } } else if (!evt.keyCode || isUpDownKeyPress && !grid.config.selectWithCheckboxOnly) { self.setSelection(rowItem, !rowItem.selected); } self.lastClickedRow = rowItem; self.lastClickedRowIndex = rowItem.rowIndex; return true; }; self.getSelection = function (entity) { var isSelected = false; if (grid.config.primaryKey) { var val = self.pKeyParser(entity); angular.forEach(self.selectedItems, function (c) { if (val === self.pKeyParser(c)) { isSelected = true; } }); } else { isSelected = self.selectedItems.indexOf(entity) !== -1; } return isSelected; }; self.setSelection = function (rowItem, isSelected) { if(grid.config.enableRowSelection){ if (!isSelected) { var indx = self.selectedItems.indexOf(rowItem.entity); if (indx !== -1) { self.selectedItems.splice(indx, 1); } } else { if (self.selectedItems.indexOf(rowItem.entity) === -1) { if (!self.multi && self.selectedItems.length > 0) { self.toggleSelectAll(false, true); } self.selectedItems.push(rowItem.entity); } } rowItem.selected = isSelected; if (rowItem.orig) { rowItem.orig.selected = isSelected; } if (rowItem.clone) { rowItem.clone.selected = isSelected; } rowItem.afterSelectionChange(rowItem); } }; self.toggleSelectAll = function (checkAll, bypass, selectFiltered) { var rows = selectFiltered ? grid.filteredRows : grid.rowCache; if (bypass || grid.config.beforeSelectionChange(rows, checkAll)) { var selectedlength = self.selectedItems.length; if (selectedlength > 0) { self.selectedItems.length = 0; } for (var i = 0; i < rows.length; i++) { rows[i].selected = checkAll; if (rows[i].clone) { rows[i].clone.selected = checkAll; } if (checkAll) { self.selectedItems.push(rows[i].entity); } } if (!bypass) { grid.config.afterSelectionChange(rows, checkAll); } } }; }; var ngStyleProvider = function($scope, grid) { $scope.headerCellStyle = function(col) { return { "height": col.headerRowHeight + "px" }; }; $scope.rowStyle = function (row) { var ret = { "top": row.offsetTop + "px", "height": $scope.rowHeight + "px" }; if (row.isAggRow) { ret.left = row.offsetLeft; } return ret; }; $scope.canvasStyle = function() { return { "height": grid.maxCanvasHt + "px" }; }; $scope.headerScrollerStyle = function() { return { "height": grid.config.headerRowHeight + "px" }; }; $scope.topPanelStyle = function() { return { "width": grid.rootDim.outerWidth + "px", "height": $scope.topPanelHeight() + "px" }; }; $scope.headerStyle = function() { return { "width": grid.rootDim.outerWidth + "px", "height": grid.config.headerRowHeight + "px" }; }; $scope.groupPanelStyle = function () { return { "width": grid.rootDim.outerWidth + "px", "height": "32px" }; }; $scope.viewportStyle = function() { return { "width": grid.rootDim.outerWidth + "px", "height": $scope.viewportDimHeight() + "px" }; }; $scope.footerStyle = function() { return { "width": grid.rootDim.outerWidth + "px", "height": $scope.footerRowHeight + "px" }; }; }; ngGridDirectives.directive('ngCellHasFocus', ['$domUtilityService', function (domUtilityService) { var focusOnInputElement = function($scope, elm) { $scope.isFocused = true; domUtilityService.digest($scope); $scope.$broadcast('ngGridEventStartCellEdit'); $scope.$on('ngGridEventEndCellEdit', function() { $scope.isFocused = false; domUtilityService.digest($scope); }); }; return function($scope, elm) { var isFocused = false; var isCellEditableOnMouseDown = false; $scope.editCell = function() { if(!$scope.enableCellEditOnFocus) { setTimeout(function() { focusOnInputElement($scope,elm); }, 0); } }; elm.bind('mousedown', function(evt) { if($scope.enableCellEditOnFocus) { isCellEditableOnMouseDown = true; } else { elm.focus(); } return true; }); elm.bind('click', function(evt) { if($scope.enableCellEditOnFocus) { evt.preventDefault(); isCellEditableOnMouseDown = false; focusOnInputElement($scope,elm); } }); elm.bind('focus', function(evt) { isFocused = true; if($scope.enableCellEditOnFocus && !isCellEditableOnMouseDown) { focusOnInputElement($scope,elm); } return true; }); elm.bind('blur', function() { isFocused = false; return true; }); elm.bind('keydown', function(evt) { if(!$scope.enableCellEditOnFocus) { if (isFocused && evt.keyCode !== 37 && evt.keyCode !== 38 && evt.keyCode !== 39 && evt.keyCode !== 40 && evt.keyCode !== 9 && !evt.shiftKey && evt.keyCode !== 13) { focusOnInputElement($scope,elm); } if (isFocused && evt.shiftKey && (evt.keyCode >= 65 && evt.keyCode <= 90)) { focusOnInputElement($scope, elm); } if (evt.keyCode === 27) { elm.focus(); } } return true; }); }; }]); ngGridDirectives.directive('ngCellText', function () { return function(scope, elm) { elm.bind('mouseover', function(evt) { evt.preventDefault(); elm.css({ 'cursor': 'text' }); }); elm.bind('mouseleave', function(evt) { evt.preventDefault(); elm.css({ 'cursor': 'default' }); }); }; }); ngGridDirectives.directive('ngCell', ['$compile', '$domUtilityService', function ($compile, domUtilityService) { var ngCell = { scope: false, compile: function() { return { pre: function($scope, iElement) { var html; var cellTemplate = $scope.col.cellTemplate.replace(COL_FIELD, 'row.entity.' + $scope.col.field); if ($scope.col.enableCellEdit) { html = $scope.col.cellEditTemplate; html = html.replace(DISPLAY_CELL_TEMPLATE, cellTemplate); html = html.replace(EDITABLE_CELL_TEMPLATE, $scope.col.editableCellTemplate.replace(COL_FIELD, 'row.entity.' + $scope.col.field)); } else { html = cellTemplate; } var cellElement = $compile(html)($scope); if ($scope.enableCellSelection && cellElement[0].className.indexOf('ngSelectionCell') === -1) { cellElement[0].setAttribute('tabindex', 0); cellElement.addClass('ngCellElement'); } iElement.append(cellElement); }, post: function($scope, iElement) { if ($scope.enableCellSelection) { $scope.domAccessProvider.selectionHandlers($scope, iElement); } $scope.$on('ngGridEventDigestCell', function() { domUtilityService.digest($scope); }); } }; } }; return ngCell; }]); ngGridDirectives.directive('ngEditCellIf', [function () { return { transclude: 'element', priority: 1000, terminal: true, restrict: 'A', compile: function (e, a, transclude) { return function (scope, element, attr) { var childElement; var childScope; scope.$watch(attr['ngEditCellIf'], function (newValue) { if (childElement) { childElement.remove(); childElement = undefined; } if (childScope) { childScope.$destroy(); childScope = undefined; } if (newValue) { childScope = scope.$new(); transclude(childScope, function (clone) { childElement = clone; element.after(clone); }); } }); }; } }; }]); ngGridDirectives.directive('ngGridFooter', ['$compile', '$templateCache', function ($compile, $templateCache) { var ngGridFooter = { scope: false, compile: function () { return { pre: function ($scope, iElement) { if (iElement.children().length === 0) { iElement.append($compile($templateCache.get($scope.gridId + 'footerTemplate.html'))($scope)); } } }; } }; return ngGridFooter; }]); ngGridDirectives.directive('ngGridMenu', ['$compile', '$templateCache', function ($compile, $templateCache) { var ngGridMenu = { scope: false, compile: function () { return { pre: function ($scope, iElement) { if (iElement.children().length === 0) { iElement.append($compile($templateCache.get($scope.gridId + 'menuTemplate.html'))($scope)); } } }; } }; return ngGridMenu; }]); ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', '$http', '$q', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse, $http, $q) { var ngGridDirective = { scope: true, compile: function() { return { pre: function($scope, iElement, iAttrs) { var $element = $(iElement); var options = $scope.$eval(iAttrs.ngGrid); options.gridDim = new ngDimension({ outerHeight: $($element).height(), outerWidth: $($element).width() }); var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q); return grid.init().then(function() { if (typeof options.columnDefs === "string") { $scope.$parent.$watch(options.columnDefs, function (a) { if (!a) { grid.refreshDomSizes(); grid.buildColumns(); return; } grid.lateBoundColumns = false; $scope.columns = []; grid.config.columnDefs = a; grid.buildColumns(); grid.eventProvider.assignEvents(); domUtilityService.RebuildGrid($scope, grid); }, true); } else { grid.buildColumns(); } if (typeof options.totalServerItems === "string") { $scope.$parent.$watch(options.totalServerItems, function (newTotal, oldTotal) { if (!angular.isDefined(newTotal)) { $scope.totalServerItems = 0; } else { $scope.totalServerItems = newTotal; } }); } else { $scope.totalServerItems = 0; } if (typeof options.data === "string") { var dataWatcher = function (a) { grid.data = $.extend([], a); grid.rowFactory.fixRowCache(); angular.forEach(grid.data, function (item, j) { var indx = grid.rowMap[j] || j; if (grid.rowCache[indx]) { grid.rowCache[indx].ensureEntity(item); } grid.rowMap[indx] = j; }); grid.searchProvider.evalFilter(); grid.configureColumnWidths(); grid.refreshDomSizes(); if (grid.config.sortInfo.fields.length > 0) { grid.sortColumnsInit(); $scope.$emit('ngGridEventSorted', grid.config.sortInfo); } $scope.$emit("ngGridEventData", grid.gridId); }; $scope.$parent.$watch(options.data, dataWatcher); $scope.$parent.$watch(options.data + '.length', function() { dataWatcher($scope.$eval(options.data)); }); } grid.footerController = new ngFooter($scope, grid); iElement.addClass("ngGrid").addClass(grid.gridId.toString()); if (!options.enableHighlighting) { iElement.addClass("unselectable"); } if (options.jqueryUITheme) { iElement.addClass('ui-widget'); } iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); domUtilityService.AssignGridContainers($scope, iElement, grid); grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout); options.selectRow = function (rowIndex, state) { if (grid.rowCache[rowIndex]) { if (grid.rowCache[rowIndex].clone) { grid.rowCache[rowIndex].clone.setSelection(state ? true : false); } grid.rowCache[rowIndex].setSelection(state ? true : false); } }; options.selectItem = function (itemIndex, state) { options.selectRow(grid.rowMap[itemIndex], state); }; options.selectAll = function (state) { $scope.toggleSelectAll(state); }; options.selectVisible = function (state) { $scope.toggleSelectAll(state, true); }; options.groupBy = function (field) { if (field) { $scope.groupBy($scope.columns.filter(function(c) { return c.field === field; })[0]); } else { var arr = $.extend(true, [], $scope.configGroups); angular.forEach(arr, $scope.groupBy); } }; options.sortBy = function (field) { var col = $scope.columns.filter(function (c) { return c.field === field; })[0]; if (col) { col.sort(); } }; options.gridId = grid.gridId; options.ngGrid = grid; options.$gridScope = $scope; options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService, UtilityService: $utils }; $scope.$on('ngGridEventDigestGrid', function(){ domUtilityService.digest($scope.$parent); }); $scope.$on('ngGridEventDigestGridParent', function(){ domUtilityService.digest($scope.$parent); }); $scope.$evalAsync(function() { $scope.adjustScrollLeft(0); }); angular.forEach(options.plugins, function (p) { if (typeof p === "function") { p = new p(); } p.init($scope.$new(), grid, options.$gridServices); options.plugins[$utils.getInstanceType(p)] = p; }); if (typeof options.init === "function") { options.init(grid, $scope); } return null; }); } }; } }; return ngGridDirective; }]); ngGridDirectives.directive('ngHeaderCell', ['$compile', function($compile) { var ngHeaderCell = { scope: false, compile: function() { return { pre: function($scope, iElement) { iElement.append($compile($scope.col.headerCellTemplate)($scope)); } }; } }; return ngHeaderCell; }]); ngGridDirectives.directive('ngInput', [function() { return { require: 'ngModel', link: function (scope, elm, attrs, ngModel) { var oldCellValue; var dereg = scope.$watch('ngModel', function() { oldCellValue = ngModel.$modelValue; dereg(); }); elm.bind('keydown', function(evt) { switch (evt.keyCode) { case 37: case 38: case 39: case 40: evt.stopPropagation(); break; case 27: if (!scope.$$phase) { scope.$apply(function() { ngModel.$setViewValue(oldCellValue); elm.blur(); }); } break; case 13: if(scope.enableCellEditOnFocus && scope.totalFilteredItemsLength() - 1 > scope.row.rowIndex && scope.row.rowIndex > 0 || scope.enableCellEdit) { elm.blur(); } break; } return true; }); elm.bind('click', function(evt) { evt.stopPropagation(); }); elm.bind('mousedown', function(evt) { evt.stopPropagation(); }); scope.$on('ngGridEventStartCellEdit', function () { elm.focus(); elm.select(); }); angular.element(elm).bind('blur', function () { scope.$emit('ngGridEventEndCellEdit'); }); } }; }]); ngGridDirectives.directive('ngRow', ['$compile', '$domUtilityService', '$templateCache', function ($compile, domUtilityService, $templateCache) { var ngRow = { scope: false, compile: function() { return { pre: function($scope, iElement) { $scope.row.elm = iElement; if ($scope.row.clone) { $scope.row.clone.elm = iElement; } if ($scope.row.isAggRow) { var html = $templateCache.get($scope.gridId + 'aggregateTemplate.html'); if ($scope.row.aggLabelFilter) { html = html.replace(CUSTOM_FILTERS, '| ' + $scope.row.aggLabelFilter); } else { html = html.replace(CUSTOM_FILTERS, ""); } iElement.append($compile(html)($scope)); } else { iElement.append($compile($templateCache.get($scope.gridId + 'rowTemplate.html'))($scope)); } $scope.$on('ngGridEventDigestRow', function(){ domUtilityService.digest($scope); }); } }; } }; return ngRow; }]); ngGridDirectives.directive('ngViewport', [function() { return function($scope, elm) { var isMouseWheelActive; var prevScollLeft; var prevScollTop = 0; elm.bind('scroll', function(evt) { var scrollLeft = evt.target.scrollLeft, scrollTop = evt.target.scrollTop; if ($scope.$headerContainer) { $scope.$headerContainer.scrollLeft(scrollLeft); } $scope.adjustScrollLeft(scrollLeft); $scope.adjustScrollTop(scrollTop); if (!$scope.$root.$$phase) { $scope.$digest(); } prevScollLeft = scrollLeft; prevScollTop = scrollTop; isMouseWheelActive = false; return true; }); elm.bind("mousewheel DOMMouseScroll", function() { isMouseWheelActive = true; if (elm.focus) { elm.focus(); } return true; }); if (!$scope.enableCellSelection) { $scope.domAccessProvider.selectionHandlers($scope, elm); } }; }]); window.ngGrid.i18n['da'] = { ngAggregateLabel: 'artikler', ngGroupPanelDescription: 'Grupér rækker udfra en kolonne ved at trække dens overskift hertil.', ngSearchPlaceHolder: 'Søg...', ngMenuText: 'Vælg kolonner:', ngShowingItemsLabel: 'Viste rækker:', ngTotalItemsLabel: 'Rækker totalt:', ngSelectedItemsLabel: 'Valgte rækker:', ngPageSizeLabel: 'Side størrelse:', ngPagerFirstTitle: 'Første side', ngPagerNextTitle: 'Næste side', ngPagerPrevTitle: 'Forrige side', ngPagerLastTitle: 'Sidste side' }; window.ngGrid.i18n['de'] = { ngAggregateLabel: 'artikel', ngGroupPanelDescription: 'Ziehen Sie eine Spaltenüberschrift hier und legen Sie es der Gruppe nach dieser Spalte.', ngSearchPlaceHolder: 'Suche...', ngMenuText: 'Spalten auswählen:', ngShowingItemsLabel: 'Zeige Artikel:', ngTotalItemsLabel: 'Meiste Artikel:', ngSelectedItemsLabel: 'Ausgewählte Artikel:', ngPageSizeLabel: 'Größe Seite:', ngPagerFirstTitle: 'Erste Page', ngPagerNextTitle: 'Nächste Page', ngPagerPrevTitle: 'Vorherige Page', ngPagerLastTitle: 'Letzte Page' }; window.ngGrid.i18n['en'] = { ngAggregateLabel: 'items', ngGroupPanelDescription: 'Drag a column header here and drop it to group by that column.', ngSearchPlaceHolder: 'Search...', ngMenuText: 'Choose Columns:', ngShowingItemsLabel: 'Showing Items:', ngTotalItemsLabel: 'Total Items:', ngSelectedItemsLabel: 'Selected Items:', ngPageSizeLabel: 'Page Size:', ngPagerFirstTitle: 'First Page', ngPagerNextTitle: 'Next Page', ngPagerPrevTitle: 'Previous Page', ngPagerLastTitle: 'Last Page' }; window.ngGrid.i18n['es'] = { ngAggregateLabel: 'Artículos', ngGroupPanelDescription: 'Arrastre un encabezado de columna aquí y soltarlo para agrupar por esa columna.', ngSearchPlaceHolder: 'Buscar...', ngMenuText: 'Elegir columnas:', ngShowingItemsLabel: 'Artículos Mostrando:', ngTotalItemsLabel: 'Artículos Totales:', ngSelectedItemsLabel: 'Artículos Seleccionados:', ngPageSizeLabel: 'Tamaño de Página:', ngPagerFirstTitle: 'Primera Página', ngPagerNextTitle: 'Página Siguiente', ngPagerPrevTitle: 'Página Anterior', ngPagerLastTitle: 'Última Página' }; window.ngGrid.i18n['fr'] = { ngAggregateLabel: 'articles', ngGroupPanelDescription: 'Faites glisser un en-tête de colonne ici et déposez-le vers un groupe par cette colonne.', ngSearchPlaceHolder: 'Recherche...', ngMenuText: 'Choisir des colonnes:', ngShowingItemsLabel: 'Articles Affichage des:', ngTotalItemsLabel: 'Nombre total d\'articles:', ngSelectedItemsLabel: 'Éléments Articles:', ngPageSizeLabel: 'Taille de page:', ngPagerFirstTitle: 'Première page', ngPagerNextTitle: 'Page Suivante', ngPagerPrevTitle: 'Page précédente', ngPagerLastTitle: 'Dernière page' }; window.ngGrid.i18n['pt-br'] = { ngAggregateLabel: 'items', ngGroupPanelDescription: 'Arraste e solte uma coluna aqui para agrupar por essa coluna', ngSearchPlaceHolder: 'Procurar...', ngMenuText: 'Selecione as colunas:', ngShowingItemsLabel: 'Mostrando os Items:', ngTotalItemsLabel: 'Total de Items:', ngSelectedItemsLabel: 'Items Selecionados:', ngPageSizeLabel: 'Tamanho da Página:', ngPagerFirstTitle: 'Primeira Página', ngPagerNextTitle: 'Próxima Página', ngPagerPrevTitle: 'Página Anterior', ngPagerLastTitle: 'Última Página' }; window.ngGrid.i18n['zh-cn'] = { ngAggregateLabel: '条目', ngGroupPanelDescription: '拖曳表头到此处以进行分组', ngSearchPlaceHolder: '搜索...', ngMenuText: '数据分组与选择列:', ngShowingItemsLabel: '当前显示条目:', ngTotalItemsLabel: '条目总数:', ngSelectedItemsLabel: '选中条目:', ngPageSizeLabel: '每页显示数:', ngPagerFirstTitle: '回到首页', ngPagerNextTitle: '下一页', ngPagerPrevTitle: '上一页', ngPagerLastTitle: '前往尾页' }; window.ngGrid.i18n['zh-tw'] = { ngAggregateLabel: '筆', ngGroupPanelDescription: '拖拉表頭到此處以進行分組', ngSearchPlaceHolder: '搜尋...', ngMenuText: '選擇欄位:', ngShowingItemsLabel: '目前顯示筆數:', ngTotalItemsLabel: '總筆數:', ngSelectedItemsLabel: '選取筆數:', ngPageSizeLabel: '每頁顯示:', ngPagerFirstTitle: '第一頁', ngPagerNextTitle: '下一頁', ngPagerPrevTitle: '上一頁', ngPagerLastTitle: '最後頁' }; angular.module("ngGrid").run(["$templateCache", function($templateCache) { $templateCache.put("aggregateTemplate.html", "