UNPKG

ng-grid

Version:

__Contributors:__

1,324 lines (1,300 loc) 116 kB
/*********************************************** * 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/06/2013 17:55 ***********************************************/ (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 = /<.+>/; if (!window.ng) { window.ng = {}; } 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']); ng.moveSelectionHandler = function($scope, elm, evt, grid) { if ($scope.selectionService.selectedItems === undefined) { return true; } var charCode = evt.which || evt.keyCode, newColumnIndex, lastInRow = false, firstInRow = false, rowIndex = $scope.selectionService.lastClickedRow.rowIndex; if ($scope.col) { newColumnIndex = $scope.col.index; } 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 = $scope.col.index == ($scope.columns.length - 1); if(charCode == 37 || charCode == 9 && evt.shiftKey){ if (focusedOnFirstVisibleColumns) { if(focusedOnFirstColumn && charCode == 9 && evt.shiftKey){ grid.$viewport.scrollLeft(grid.$canvas.width()); newColumnIndex = $scope.columns.length - 1; firstInRow = true; } else { grid.$viewport.scrollLeft(grid.$viewport.scrollLeft() - $scope.col.width); } } if(!focusedOnFirstColumn){ newColumnIndex -= 1; } } 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); } } 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.selectionService.lastClickedRow.renderedRowIndex >= $scope.renderedRows.length - EXCESS_ROWS - 2) { grid.$viewport.scrollTop(grid.$viewport.scrollTop() + $scope.rowHeight); } else if ($scope.selectionService.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($scope.columns[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; }; } ng.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) { var propPath = path.split('.'), i = 0; var tempProp = entity[propPath[i]], links = propPath.length; i++; while (tempProp && i < links) { tempProp = tempProp[propPath[i]]; i++; } return tempProp; }, 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]; } }, 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(ng.utils, { isIe6: (function() { return ng.utils.ieVersion === 6; })(), isIe7: (function() { return ng.utils.ieVersion === 7; })(), isIe: (function() { return ng.utils.ieVersion !== undefined; })() }); 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', function() { 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) { 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 (ng.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 (ng.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; }]); ng.Aggregate = 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 ng.Aggregate(self.entity, rowFactory, rowHeight); ret.orig = self; return ret; }; }; ng.Column = function(config, $scope, grid, domUtilityService, $templateCache) { 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.zIndex = function() { return self.pinned ? 5 : 0; }; self.cellFilter = colDef.cellFilter ? colDef.cellFilter : ""; self.field = colDef.field; self.aggLabelFilter = colDef.cellFilter || colDef.aggLabelFilter; self.visible = ng.utils.isNullOrUndefined(colDef.visible) || colDef.visible; self.sortable = false; self.resizable = false; self.pinnable = false; self.pinned = colDef.pinned; self.originalIndex = self.index; self.groupable = ng.utils.isNullOrUndefined(colDef.groupable) || colDef.groupable; if (config.enableSort) { self.sortable = ng.utils.isNullOrUndefined(colDef.sortable) || colDef.sortable; } if (config.enableResize) { self.resizable = ng.utils.isNullOrUndefined(colDef.resizable) || colDef.resizable; } if (config.enablePinning) { self.pinnable = ng.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() { return "col" + self.index + " colt" + self.index; }; 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 ng.Column(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; }; }; ng.Dimension = function(options) { this.outerHeight = null; this.outerWidth = null; $.extend(this, options); }; ng.DomAccessProvider = 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.selectionService.lastClickedRow){ var columnIndex = index != undefined ? index : previousColumn; var elm = $scope.selectionService.lastClickedRow.clone ? $scope.selectionService.lastClickedRow.clone.elm : $scope.selectionService.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 = ng.moveSelectionHandler($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; }); }; }; ng.EventProvider = function(grid, $scope, domUtilityService) { 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); } }); $scope.$evalAsync(self.setDraggables); } 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('columns', self.setDraggables, true); }; 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) { 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); }); }; self.assignGridEventHandlers(); self.assignEvents(); }; ng.Footer = 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); }; }; ng.Grid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache) { 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, excludeProperties: [], filterOptions: { filterText: "", useExternalFilter: false }, footerRowHeight: 55, groups: [], headerRowHeight: 30, headerRowTemplate: undefined, jqueryUIDraggable: false, jqueryUITheme: false, keepLastSelected: true, maintainColumnRatios: undefined, multiSelect: true, pagingOptions: { pageSizes: [250, 500, 1000], pageSize: 250, totalServerItems: 0, currentPage: 1 }, pinSelectionCheckbox: false, plugins: [], rowHeight: 30, rowTemplate: undefined, selectedItems: [], selectWithCheckboxOnly: false, showColumnMenu: false, showFilter: false, showFooter: false, showGroupPanel: false, showSelectionCheckbox: false, sortInfo: undefined, 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" + 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.sortInfo = self.config.sortInfo; 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'); 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 ng.Dimension(); 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; } ng.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 ng.Column({ 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)); } 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 ng.Column({ 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); 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, functio