ng-grid
Version:
__Contributors:__
1,324 lines (1,300 loc) • 116 kB
JavaScript
/***********************************************
* 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