/*********************************************** * 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: 03/29/2013 17:16 ***********************************************/ (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.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; }); }; }); ngGridServices.factory('$domUtilityService',['$utilityService', function($utils) { var domUtilityService = {}; var regexCache = {}; var getWidths = function() { var $testContainer = $('<div></div>'); $testContainer.appendTo('body'); $testContainer.height(100).width(100).css("position", "absolute").css("overflow", "scroll"); $testContainer.append('<div style="height: 400px; width: 400px;"></div>'); domUtilityService.ScrollH = ($testContainer.height() - $testContainer[0].clientHeight); domUtilityService.ScrollW = ($testContainer.width() - $testContainer[0].clientWidth); $testContainer.empty(); $testContainer.attr('style', ''); $testContainer.append('<span style="font-family: Verdana, Helvetica, Sans-Serif; font-size: 14px;"><strong>M</strong></span>'); 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 = $("<style id='" + gridId + "' type='text/css' rel='stylesheet' />").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 + 2) + "px}"; for (var i = 0; i < cols.length; i++) { var col = cols[i]; if (col.visible !== false) { var colLeft = col.pinned ? grid.$viewport.scrollLeft() + sumWidth : sumWidth; css += "." + gridId + " .col" + i + " { width: " + col.width + "px; left: " + colLeft + "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; if (digest) { $scope.adjustScrollLeft(grid.$viewport.scrollLeft()); 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) { 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; }]); ngGridServices.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 = undefined, 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; }]); ngGridServices.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.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()); return (results && results.length > 1) ? results[1] : ""; }, ieVersion: (function() { var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i'); while (div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->', iElems[0]) ; return version > 4 ? version : undefined; })() }; $.extend(utils, { isIe: (function() { return utils.ieVersion !== undefined; })() }); return utils; }]); var ngAggregate = function (aggEntity, rowFactory, rowHeight) { var self = this; self.rowIndex = 0; self.offsetTop = self.rowIndex * rowHeight; self.entity = aggEntity; self.label = aggEntity.gLabel; self.field = aggEntity.gField; self.depth = aggEntity.gDepth; self.parent = aggEntity.parent; self.children = aggEntity.children; self.aggChildren = aggEntity.aggChildren; self.aggIndex = aggEntity.aggIndex; self.collapsed = true; self.isAggRow = true; self.offsetleft = aggEntity.gDepth * 25; self.aggLabelFilter = aggEntity.aggLabelFilter; self.toggleExpand = function() { self.collapsed = self.collapsed ? false : true; if (self.orig) { self.orig.collapsed = self.collapsed; } self.notifyChildren(); }; self.setExpand = function(state) { self.collapsed = state; self.notifyChildren(); }; self.notifyChildren = function () { var longest = Math.max(rowFactory.aggCache.length, self.children.length); for (var i = 0; i < longest; i++) { if (self.aggChildren[i]) { self.aggChildren[i].entity[NG_HIDDEN] = self.collapsed; if (self.collapsed) { self.aggChildren[i].setExpand(self.collapsed); } } if (self.children[i]) { self.children[i][NG_HIDDEN] = self.collapsed; } if (i > self.aggIndex && rowFactory.aggCache[i]) { var agg = rowFactory.aggCache[i]; var offset = (30 * self.children.length); agg.offsetTop = self.collapsed ? agg.offsetTop - offset : agg.offsetTop + offset; } }; rowFactory.renderedChange(); }; self.aggClass = function() { return self.collapsed ? "ngAggArrowCollapsed" : "ngAggArrowExpanded"; }; self.totalChildren = function() { if (self.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(self); return i; } else { return self.children.length; } }; self.copy = function () { var ret = new ngAggregate(self.entity, rowFactory, rowHeight); ret.orig = self; return ret; }; }; var ngColumn = function (config, $scope, grid, domUtilityService, $templateCache, $utils) { var self = this, colDef = config.colDef, delay = 500, clicks = 0, timer = null; 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 = config.enableCellEdit || colDef.enableCellEdit; self.headerRowHeight = config.headerRowHeight; self.displayName = colDef.displayName || colDef.field; 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 = self.index; 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; 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) { 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)); 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'; $scope.adjustScrollLeft(0); domUtilityService.digest($scope); 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) { var self = this, previousColumn; self.selectInputElement = function(elm){ var node = elm.nodeName.toLowerCase(); if(node == 'input' || node == 'textarea'){ elm.select(); } }; self.focusCellElement = function($scope, index){ if($scope.selectionProvider.lastClickedRow){ var columnIndex = index != undefined ? index : 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(grid.config.showSelectionCheckbox && angular.element(columns[i]).scope() && angular.element(columns[i]).scope().col.index == 0){ i = 1; } if (columns[i]) { columns[i].children[0].focus(); } previousColumn = columnIndex; } } }; var 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 }); }; self.selectionHandlers = function($scope, elm){ var doingKeyDown = false; elm.bind('keydown', function(evt) { if (evt.keyCode == 16) { changeUserSelect(elm, 'none', evt); return true; } else if (!doingKeyDown) { doingKeyDown = true; var ret = ngMoveSelectionHandler($scope, elm, evt, grid); doingKeyDown = false; return ret; } return true; }); elm.bind('keyup', function(evt) { if (evt.keyCode == 16) { 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); } if (grid.config.enableRowReordering) { grid.$viewport.on('mousedown', self.onRowMouseDown).on('dragover', self.dragOver).on('drop', self.onRowDrop); } } $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){ 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(); domUtilityService.BuildStyles($scope, grid, true); self.colToMove = undefined; } }; self.onRowMouseDown = function(event) { var targetRow = $(event.target).closest('.ngRow'); var rowScope = angular.element(targetRow).scope(); if (rowScope) { targetRow.attr('draggable', 'true'); domUtilityService.eventStorage.rowToMove = { targetRow: targetRow, scope: rowScope }; } }; self.onRowDrop = function(event) { var targetRow = $(event.target).closest('.ngRow'); var rowScope = angular.element(targetRow).scope(); if (rowScope) { var prevRow = domUtilityService.eventStorage.rowToMove; if (prevRow.scope.row == rowScope.row) { return; } grid.changeRowOrder(prevRow.scope.row, rowScope.row); grid.searchProvider.evalFilter(); domUtilityService.eventStorage.rowToMove = undefined; domUtilityService.digest(rowScope.$root); } }; self.assignGridEventHandlers = function() { if (grid.config.tabIndex === -1) { grid.$viewport.attr('tabIndex', domUtilityService.numberOfGrids); domUtilityService.numberOfGrids++; } else { grid.$viewport.attr('tabIndex', grid.config.tabIndex); } $(window).resize(function() { domUtilityService.RebuildGrid($scope,grid); }); $(grid.$root.parent()).on('resize', function() { domUtilityService.RebuildGrid($scope, grid); }); }; self.assignGridEventHandlers(); self.assignEvents(); }; var ngFooter = function ($scope, grid) { $scope.maxRows = function () { var ret = Math.max($scope.pagingOptions.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.pagingOptions.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.pagingOptions.totalServerItems > 0) { return !(curPage < maxPages); } else { return grid.data.length < 1; } }; $scope.cantPageToLast = function() { if ($scope.pagingOptions.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) { var defaults = { aggregateTemplate: undefined, afterSelectionChange: function() { }, beforeSelectionChange: function() { return true; }, checkboxCellTemplate: undefined, checkboxHeaderTemplate: undefined, columnDefs: undefined, data: [], dataUpdated: function() { }, enableCellEdit: false, enableCellSelection: false, enableColumnResize: false, enableColumnReordering: false, enableColumnHeavyVirt: false, enablePaging: false, enablePinning: false, enableRowReordering: false, enableRowSelection: true, enableSorting: true, enableHighlighting: false, excludeProperties: [], filterOptions: { filterText: "", useExternalFilter: false }, footerRowHeight: 55, footerTemplate: undefined, groups: [], headerRowHeight: 30, headerRowTemplate: undefined, jqueryUIDraggable: false, jqueryUITheme: false, keepLastSelected: true, maintainColumnRatios: undefined, menuTemplate: undefined, multiSelect: true, pagingOptions: { pageSizes: [250, 500, 1000], pageSize: 250, totalServerItems: 0, 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, 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 = []; var getTemplate = function (key) { var t = self.config[key]; var uKey = self.gridId + key + ".html"; if (t && !TEMPLATE_REGEXP.test(t)) { $templateCache.put(uKey, $.ajax({ type: "GET", url: t, async: false }).responseText); } else if (t) { $templateCache.put(uKey, t); } else { var dKey = key + ".html"; $templateCache.put(uKey, $templateCache.get(dKey)); } }; getTemplate('rowTemplate'); getTemplate('aggregateTemplate'); getTemplate('headerRowTemplate'); getTemplate('checkboxCellTemplate'); getTemplate('checkboxHeaderTemplate'); getTemplate('menuTemplate'); getTemplate('footerTemplate'); if (typeof self.config.data == "object") { self.data = self.config.data; } self.calcMaxCanvasHeight = function() { return (self.config.groups.length > 0) ? (self.rowFactory.parsedData.filter(function(e) { return !e[NG_HIDDEN]; }).length * self.config.rowHeight) : (self.filteredRows.length * self.config.rowHeight); }; 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; 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 }, $scope, self, domUtilityService, $templateCache, $utils)); } if (columnDefs.length > 0) { var indexOffset = self.config.showSelectionCheckbox ? self.config.groups.length + 1 : self.config.groups.length; $scope.configGroups.length = 0; angular.forEach(columnDefs, function(colDef, i) { i += indexOffset; var column = new ngColumn({ colDef: colDef, index: 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 }, $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; } }; self.configureColumnWidths = function() { var cols = self.config.columnDefs; var indexOffset = self.config.showSelectionCheckbox ? $scope.configGroups.length + 1 : $scope.configGroups.length; var numOfCols = cols.length + indexOffset, asterisksArray = [], percentArray = [], asteriskNum = 0, totalWidth = 0; totalWidth += self.config.showSelectionCheckbox ? 25 : 0; angular.forEach(cols, function(col, i) { i += indexOffset; var isPercent = false, t = undefined; if ($utils.isNullOrUndefined(col.width)) { col.width = "*"; } else { isPercent = isNaN(col.width) ? $utils.endsWith(col.width, "%") : false; t = isPercent ? col.width : parseInt(col.width, 10); } if (isNaN(t)) { t = col.width; if (t == 'auto') { $scope.columns[i].width = col.minWidth; totalWidth += $scope.columns[i].width; var temp = $scope.columns[i]; $timeout(function () { self.resizeOnData(temp, true); }); return; } else if (t.indexOf("*") != -1) { if (col.visible !== false) { asteriskNum += t.length; } col.index = i; asterisksArray.push(col); return; } else if (isPercent) { col.index = i; percentArray.push(col); return; } else { throw "unable to parse column width, use percentage (\"10%\",\"20%\", etc...) or \"*\" to use remaining width of grid"; } } else if (col.visible !== false) { totalWidth += $scope.columns[i].width = parseInt(col.width, 10); } }); if (asterisksArray.length > 0) { self.config.maintainColumnRatios === false ? angular.noop() : self.config.maintainColumnRatios = true; var remainigWidth = self.rootDim.outerWidth - totalWidth; var asteriskVal = Math.floor(remainigWidth / asteriskNum); angular.forEach(asterisksArray, function(col) { var t = col.width.length; $scope.columns[col.index].width = asteriskVal * t; if (col.index + 1 == numOfCols) { var offset = 2; if (self.maxCanvasHt > $scope.viewportDimHeight()) { offset += domUtilityService.ScrollW; } $scope.columns[col.index].width -= offset; } if (col.visible !== false) { totalWidth += $scope.columns[col.index].width; } }); } if (percentArray.length > 0) { angular.forEach(percentArray, function(col) { var t = col.width; $scope.columns[col.index].width = Math.floor(self.rootDim.outerWidth * (parseInt(t.slice(0, -1), 10) / 100)); }); } }; self.init = 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, domUtilityService); $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) { domUtilityService.BuildStyles($scope, self, true); $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) { self.getColsFromFields(); self.sortActual(); } }; 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.changeRowOrder = function(prevRow, targetRow) { var i = self.rowCache.indexOf(prevRow); var j = self.rowCache.indexOf(targetRow); self.rowCache.splice(i, 1); self.rowCache.splice(j, 0, prevRow); $scope.$emit('ngGridEventChangeOrder', self.rowCache); }; self.sortData = function(col, evt) { if (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.getColsFromFields = 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.push(c); } return false; }); }; self.sortActual = function() { if (!self.config.useExternalSorting) { var tempData = self.data.slice(0); angular.forEach(tempData, function(item, i) { item.preSortSelected = self.rowCache[self.rowMap[i]].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++) { if ($scope.columns[i].visible !== false) { $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.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(a) { $scope.selectionProvider.toggleSelectAll(a); }; $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: 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; }; self.init(); }; var ngRange = function (top, bottom) { this.topRow = top; this.bottomRow = bottom; }; var ngRow = function (entity, config, selectionProvider, rowIndex, $utils) { var self = this, enableRowSelection = config.enableRowSelection; self.jqueryUITheme = config.jqueryUITheme; self.rowClasses = config.rowClasses; self.entity = entity; self.selectionProvider = selectionProvider; self.selected = selectionProvider.getSelection(entity); self.cursor = enableRowSelection ? 'pointer' : 'default'; self.setSelection = function(isSelected) { self.selectionProvider.setSelection(self, isSelected); self.selectionProvider.lastClickedRow = self; }; self.continueSelection = function(event) { self.selectionProvider.ChangeSelection(self, event); }; self.ensureEntity = function(expected) { if (self.entity != expected) { self.entity = expected; self.selected = self.selectionProvider.getSelection(self.entity); } }; self.toggleSelected = function(event) { if (!enableRowSelection && !config.enableCellSelection) { return true; } var element = event.target || event; if (element.type == "checkbox" && element.parentElement.className != "ngSelectionCell ng-scope") { return true; } if (config.selectWithCheckboxOnly && element.type != "checkbox") { self.selectionProvider.lastClickedRow = self; return true; } else { if (self.beforeSelectionChange(self, event)) { self.continueSelection(event); } } return false; }; self.rowIndex = rowIndex; self.offsetTop = self.rowIndex * config.rowHeight; self.rowDisplayIndex = 0; self.alternatingRowClass = function () { var isEven = (self.rowIndex % 2) === 0; var classes = { 'ngRow' : true, 'selected': self.selected, 'even': isEven, 'odd': !isEven, 'ui-state-default': self.jqueryUITheme && isEven, 'ui-state-active': self.jqueryUITheme && !isEven }; return classes; }; self.beforeSelectionChange = config.beforeSelectionChangeCallback; self.afterSelectionChange = config.afterSelectionChangeCallback; self.getProperty = function(path) { return $utils.evalProperty(self.entity, path); }; self.copy = function () { self.clone = new ngRow(entity, config, selectionProvider, rowIndex, $utils); self.clone.isClone = true; self.clone.elm = self.elm; return self.clone; }; self.setVars = function (fromRow) { fromRow.clone = self; self.entity = fromRow.entity; self.selected = fromRow.selected; }; }; 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); 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; for (var x = 0; x < rows.length; x++){ var model = rows[x].entity; if (!model) return; rows[x][NG_HIDDEN] = true; var ptr = self.groupedData; for (var y = 0; y < groups.length; y++) { var group = groups[y]; var col = cols.filter(function(c) { return c.field == 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]); }; 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: '<div class="ngAggHeader"></div>', pinned: grid.config.pinSelectionCheckbox }, enablePinning: grid.config.enablePinning, isAggCol: true, headerRowHeight: grid.config.headerRowHeight, }, $scope, grid, domUtilityService, $templateCache, $utils)); } } domUtilityService.BuildStyles($scope, grid, true); 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 = {}; self.evalFilter = function () { var filterFunc = function(item) { for (var x = 0, len = searchConditions.length; x < len; x++) { var condition = searchConditions[x]; var result; if (!condition.column) { for (var prop in item) { if (item.hasOwnProperty(prop)) { var c = self.fieldMap[prop]; if (!c) continue; var f = null, s = null; if (c && c.cellFilter) { s = c.cellFilter.split(':'); f = $filter(s[0]); } var pVal = item[prop]; if (pVal != null) { if (typeof f == 'function') { var filterRes = f(typeof pVal === 'object' ? evalObject(pVal, c.field) : pVal, s[1]).toString(); result = condition.regex.test(filterRes); } else { result = condition.regex.test(typeof pVal === 'object' ? evalObject(pVal, c.field).toString() : pVal.toString()); } if (pVal && result) { return true; } } } } return false; } 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) 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 (!value || !result) { return false; } } return true; }; 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') }); } } }; }; $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(); } }); if (!self.extFilter) { $scope.$watch('columns', function (cs) { for (var i = 0; i < cs.length; i++) { var col = cs[i]; if(col.field) self.fieldMap[col.field.split('.')[0]] = col; if(col.displayName) self.fieldMap[col.displayName.toLowerCase().replace(/\s+/g, '')] = col; }; }); } }; 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) { if (evt && !evt.ctrlKey && !evt.shiftKey && evt.originalEvent.constructor.name == "MouseEvent") { self.toggleSelectAll(false, true); } 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.lastClickedRow.rowIndex; self.lastClickedRow = rowItem; 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); } 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) { self.setSelection(rowItem, !rowItem.selected); } self.lastClickedRow = rowItem; 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){ rowItem.selected = isSelected; if (rowItem.clone) { rowItem.clone.selected = isSelected; } 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); rowItem.selected = isSelected; if (rowItem.clone) { rowItem.clone.selected = isSelected; } } self.selectedItems.push(rowItem.entity); } } rowItem.afterSelectionChange(rowItem); } }; self.toggleSelectAll = function (checkAll, bypass) { if (bypass || grid.config.beforeSelectionChange(grid.filteredRows)) { var selectedlength = self.selectedItems.length; if (selectedlength > 0) { self.selectedItems.length = 0; } for (var i = 0; i < grid.filteredRows.length; i++) { grid.filteredRows[i].selected = checkAll; if (grid.filteredRows[i].clone) { grid.filteredRows[i].clone.selected = checkAll; } if (checkAll) { self.selectedItems.push(grid.filteredRows[i].entity); } } if (!bypass) { grid.config.afterSelectionChange(grid.filteredRows); } } }; }; var ngStyleProvider = function($scope, grid, domUtilityService) { $scope.headerCellStyle = function(col) { return { "height": col.headerRowHeight + "px" }; }; $scope.rowStyle = function(row) { return { "top": row.offsetTop + "px", "height": $scope.rowHeight + "px" }; }; $scope.canvasStyle = function() { return { "height": grid.maxCanvasHt.toString() + "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); var elementWithoutComments = angular.element(elm[0].children).filter(function () { return this.nodeType != 8; }); var inputElement = angular.element(elementWithoutComments[0].children[0]); if(inputElement.length > 0){ angular.element(inputElement).focus(); $scope.domAccessProvider.selectInputElement(inputElement[0]); angular.element(inputElement).bind('blur', function(){ $scope.isFocused = false; domUtilityService.digest($scope); return true; }); } }; return function($scope, elm) { var isFocused = false; $scope.editCell = function(){ setTimeout(function() { focusOnInputElement($scope,elm); }, 0); }; elm.bind('mousedown', function(){ elm.focus(); return true; }); elm.bind('focus', function(){ isFocused = true; return true; }); elm.bind('blur', function(){ isFocused = false; return true; }); elm.bind('keydown', function(evt){ 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(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, '$eval(\'row.entity.\' + 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, "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('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', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse) { 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); 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.configureColumnWidths(); grid.eventProvider.assignEvents(); domUtilityService.RebuildGrid($scope, grid); }); } else { grid.buildColumns(); } 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.getColsFromFields(); grid.sortActual(); grid.searchProvider.evalFilter(); $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.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; $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 = p.call(this); } p.init($scope.$new(), grid, { SortService: sortService, DomUtilityService: domUtilityService }); options.plugins[$utils.getInstanceType(p)] = p; }); if (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('ngIf', [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['ngIf'], 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('ngInput',['$parse', function($parse) { return function ($scope, elm, attrs) { var getter = $parse($scope.$eval(attrs.ngInput)); var setter = getter.assign; var oldCellValue = getter($scope.row.entity); elm.val(oldCellValue); elm.bind('keyup', function() { var newVal = elm.val(); if (!$scope.$root.$$phase) { $scope.$apply(function(){setter($scope.row.entity,newVal); }); } }); elm.bind('keydown', function(evt){ switch(evt.keyCode){ case 37: case 38: case 39: case 40: evt.stopPropagation(); break; case 27: if (!$scope.$root.$$phase) { $scope.$apply(function(){ setter($scope.row.entity,oldCellValue); elm.val(oldCellValue); elm.blur(); }); } default: break; } return true; }); }; }]); 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 = prevScollTop; isMouseWheelActive = false; return true; }); elm.bind("mousewheel DOMMouseScroll", function() { isMouseWheelActive = true; elm.focus(); return true; }); if (!$scope.enableCellSelection) { $scope.domAccessProvider.selectionHandlers($scope, elm); } }; }]); 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['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['ge'] = { 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['sp'] = { 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['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", "<div ng-click=\"row.toggleExpand()\" ng-style=\"rowStyle(row)\" ng-style=\"{'left': row.offsetleft}\" class=\"ngAggregate\">" + " <span class=\"ngAggregateText\">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span>" + " <div class=\"{{row.aggClass()}}\"></div>" + "</div>" + "" ); $templateCache.put("cellEditTemplate.html", "<div ng-cell-has-focus ng-dblclick=\"editCell()\">" + " <div ng-if=\"!isFocused\">" + " DISPLAY_CELL_TEMPLATE" + " </div>" + " <div ng-if=\"isFocused\">" + " EDITABLE_CELL_TEMPLATE" + " </div>" + "</div>" ); $templateCache.put("cellTemplate.html", "<div class=\"ngCellText\" ng-class=\"col.colIndex()\"><span ng-cell-text>{{COL_FIELD CUSTOM_FILTERS}}</span></div>" ); $templateCache.put("checkboxCellTemplate.html", "<div class=\"ngSelectionCell\"><input tabindex=\"-1\" class=\"ngSelectionCheckbox\" type=\"checkbox\" ng-checked=\"row.selected\" /></div>" ); $templateCache.put("checkboxHeaderTemplate.html", "<input class=\"ngSelectionHeader\" type=\"checkbox\" ng-show=\"multiSelect\" ng-model=\"allSelected\" ng-change=\"toggleSelectAll(allSelected)\"/>" ); $templateCache.put("editableCellTemplate.html", "<input ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" />" ); $templateCache.put("footerTemplate.html", "<div ng-show=\"showFooter\" class=\"ngFooterPanel\" ng-class=\"{'ui-widget-content': jqueryUITheme, 'ui-corner-bottom': jqueryUITheme}\" ng-style=\"footerStyle()\">" + " <div class=\"ngTotalSelectContainer\" >" + " <div class=\"ngFooterTotalItems\" ng-class=\"{'ngNoMultiSelect': !multiSelect}\" >" + " <span class=\"ngLabel\">{{i18n.ngTotalItemsLabel}} {{maxRows()}}</span><span ng-show=\"filterText.length > 0\" class=\"ngLabel\">({{i18n.ngShowingItemsLabel}} {{totalFilteredItemsLength()}})</span>" + " </div>" + " <div class=\"ngFooterSelectedItems\" ng-show=\"multiSelect\">" + " <span class=\"ngLabel\">{{i18n.ngSelectedItemsLabel}} {{selectedItems.length}}</span>" + " </div>" + " </div>" + " <div class=\"ngPagerContainer\" style=\"float: right; margin-top: 10px;\" ng-show=\"enablePaging\" ng-class=\"{'ngNoMultiSelect': !multiSelect}\">" + " <div style=\"float:left; margin-right: 10px;\" class=\"ngRowCountPicker\">" + " <span style=\"float: left; margin-top: 3px;\" class=\"ngLabel\">{{i18n.ngPageSizeLabel}}</span>" + " <select style=\"float: left;height: 27px; width: 100px\" ng-model=\"pagingOptions.pageSize\" >" + " <option ng-repeat=\"size in pagingOptions.pageSizes\">{{size}}</option>" + " </select>" + " </div>" + " <div style=\"float:left; margin-right: 10px; line-height:25px;\" class=\"ngPagerControl\" style=\"float: left; min-width: 135px;\">" + " <button class=\"ngPagerButton\" ng-click=\"pageToFirst()\" ng-disabled=\"cantPageBackward()\" title=\"{{i18n.ngPagerFirstTitle}}\"><div class=\"ngPagerFirstTriangle\"><div class=\"ngPagerFirstBar\"></div></div></button>" + " <button class=\"ngPagerButton\" ng-click=\"pageBackward()\" ng-disabled=\"cantPageBackward()\" title=\"{{i18n.ngPagerPrevTitle}}\"><div class=\"ngPagerFirstTriangle ngPagerPrevTriangle\"></div></button>" + " <input class=\"ngPagerCurrent\" type=\"number\" style=\"width:50px; height: 24px; margin-top: 1px; padding: 0 4px;\" ng-model=\"pagingOptions.currentPage\"/>" + " <button class=\"ngPagerButton\" ng-click=\"pageForward()\" ng-disabled=\"cantPageForward()\" title=\"{{i18n.ngPagerNextTitle}}\"><div class=\"ngPagerLastTriangle ngPagerNextTriangle\"></div></button>" + " <button class=\"ngPagerButton\" ng-click=\"pageToLast()\" ng-disabled=\"cantPageToLast()\" title=\"{{i18n.ngPagerLastTitle}}\"><div class=\"ngPagerLastTriangle\"><div class=\"ngPagerLastBar\"></div></div></button>" + " </div>" + " </div>" + "</div>" ); $templateCache.put("gridTemplate.html", "<div class=\"ngTopPanel\" ng-class=\"{'ui-widget-header':jqueryUITheme, 'ui-corner-top': jqueryUITheme}\" ng-style=\"topPanelStyle()\">" + " <div class=\"ngGroupPanel\" ng-show=\"showGroupPanel()\" ng-style=\"groupPanelStyle()\">" + " <div class=\"ngGroupPanelDescription\" ng-show=\"configGroups.length == 0\">{{i18n.ngGroupPanelDescription}}</div>" + " <ul ng-show=\"configGroups.length > 0\" class=\"ngGroupList\">" + " <li class=\"ngGroupItem\" ng-repeat=\"group in configGroups\">" + " <span class=\"ngGroupElement\">" + " <span class=\"ngGroupName\">{{group.displayName}}" + " <span ng-click=\"removeGroup($index)\" class=\"ngRemoveGroup\">x</span>" + " </span>" + " <span ng-hide=\"$last\" class=\"ngGroupArrow\"></span>" + " </span>" + " </li>" + " </ul>" + " </div>" + " <div class=\"ngHeaderContainer\" ng-style=\"headerStyle()\">" + " <div class=\"ngHeaderScroller\" ng-style=\"headerScrollerStyle()\" ng-include=\"gridId + 'headerRowTemplate.html'\"></div>" + " </div>" + " <div ng-grid-menu></div>" + "</div>" + "<div class=\"ngViewport\" unselectable=\"on\" ng-viewport ng-class=\"{'ui-widget-content': jqueryUITheme}\" ng-style=\"viewportStyle()\">" + " <div class=\"ngCanvas\" ng-style=\"canvasStyle()\">" + " <div ng-style=\"rowStyle(row)\" ng-repeat=\"row in renderedRows\" ng-click=\"row.toggleSelected($event)\" ng-class=\"row.alternatingRowClass()\" ng-row></div>" + " </div>" + "</div>" + "<div ng-grid-footer></div>" + "" ); $templateCache.put("headerCellTemplate.html", "<div class=\"ngHeaderSortColumn {{col.headerClass}}\" ng-style=\"{'cursor': col.cursor}\" ng-class=\"{ 'ngSorted': !noSortVisible }\">" + " <div ng-click=\"col.sort($event)\" ng-class=\"'colt' + col.index\" class=\"ngHeaderText\">{{col.displayName}}</div>" + " <div class=\"ngSortButtonDown\" ng-show=\"col.showSortButtonDown()\"></div>" + " <div class=\"ngSortButtonUp\" ng-show=\"col.showSortButtonUp()\"></div>" + " <div class=\"ngSortPriority\">{{col.sortPriority}}</div>" + " <div ng-class=\"{ ngPinnedIcon: col.pinned, ngUnPinnedIcon: !col.pinned }\" ng-click=\"togglePin(col)\" ng-show=\"col.pinnable\"></div>" + "</div>" + "<div ng-show=\"col.resizable\" class=\"ngHeaderGrip\" ng-click=\"col.gripClick($event)\" ng-mousedown=\"col.gripOnMouseDown($event)\"></div>" ); $templateCache.put("headerRowTemplate.html", "<div ng-style=\"{ height: col.headerRowHeight }\" ng-repeat=\"col in renderedColumns\" ng-class=\"col.colIndex()\" class=\"ngHeaderCell\" ng-header-cell></div>" ); $templateCache.put("menuTemplate.html", "<div ng-show=\"showColumnMenu || showFilter\" class=\"ngHeaderButton\" ng-click=\"toggleShowMenu()\">" + " <div class=\"ngHeaderButtonArrow\"></div>" + "</div>" + "<div ng-show=\"showMenu\" class=\"ngColMenu\">" + " <div ng-show=\"showFilter\">" + " <input placeholder=\"{{i18n.ngSearchPlaceHolder}}\" type=\"text\" ng-model=\"filterText\"/>" + " </div>" + " <div ng-show=\"showColumnMenu\">" + " <span class=\"ngMenuText\">{{i18n.ngMenuText}}</span>" + " <ul class=\"ngColList\">" + " <li class=\"ngColListItem\" ng-repeat=\"col in columns | ngColumns\">" + " <label><input ng-disabled=\"col.pinned\" type=\"checkbox\" class=\"ngColListCheckbox\" ng-model=\"col.visible\"/>{{col.displayName}}</label>" + " <a title=\"Group By\" ng-class=\"col.groupedByClass()\" ng-show=\"col.groupable && col.visible\" ng-click=\"groupBy(col)\"></a>" + " <span class=\"ngGroupingNumber\" ng-show=\"col.groupIndex > 0\">{{col.groupIndex}}</span> " + " </li>" + " </ul>" + " </div>" + "</div>" ); $templateCache.put("rowTemplate.html", "<div ng-style=\"{ 'cursor': row.cursor }\" ng-repeat=\"col in renderedColumns\" ng-class=\"col.colIndex()\" class=\"ngCell {{col.cellClass}}\" ng-cell></div>" ); }]); }(window, jQuery));