aboutsummaryrefslogtreecommitdiff
path: root/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0')
-rw-r--r--src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid-1.6.0.debug.js2393
-rw-r--r--src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid-1.6.0.js133
-rw-r--r--src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid.css456
3 files changed, 2982 insertions, 0 deletions
diff --git a/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid-1.6.0.debug.js b/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid-1.6.0.debug.js
new file mode 100644
index 0000000..636fcf1
--- /dev/null
+++ b/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid-1.6.0.debug.js
@@ -0,0 +1,2393 @@
+/***********************************************
+ * 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: 12/27/2012 16:39:37
+ ***********************************************/
+
+(function(window) {
+ 'use strict';
+
+ /***********************************************
+ * FILE: ..\src\namespace.js
+ ***********************************************/
+ 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', []);
+// Declare app level module which depends on filters, and services
+
+ /***********************************************
+ * FILE: ..\src\constants.js
+ ***********************************************/
+ var SELECTED_PROP = '__ng_selected__';
+// the # of rows we want to add to the top and bottom of the rendered grid rows
+ var EXCESS_ROWS = 8;
+ var SCROLL_THRESHOLD = 6;
+ var ASC = "asc";
+// constant for sorting direction
+ var DESC = "desc";
+// constant for sorting direction
+ 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 TEMPLATE_REGEXP = /<.+>/;
+
+ /***********************************************
+ * FILE: ..\src\navigation.js
+ ***********************************************/
+//set event binding on the grid so we can select using the up/down keys
+ ng.moveSelectionHandler = function($scope, grid, evt) {
+ // null checks
+ if (grid === null || grid === undefined) {
+ return true;
+ }
+ if (grid.config.selectedItems === undefined) {
+ return true;
+ }
+ var charCode = evt.which || evt.keyCode;
+ // detect which direction for arrow keys to navigate the grid
+ var offset = (charCode == 38 ? -1 : (charCode == 40 ? 1 : null));
+ if (!offset) {
+ return true;
+ }
+ var items = $scope.renderedRows;
+ var index = items.indexOf(grid.selectionService.lastClickedRow) + offset;
+ if (index < 0 || index >= items.length) {
+ return true;
+ }
+ grid.selectionService.ChangeSelection(items[index], evt);
+ if (index > items.length - EXCESS_ROWS) {
+ grid.$viewport.scrollTop(grid.$viewport.scrollTop() + (grid.config.rowHeight * 2));
+ } else if (index < EXCESS_ROWS) {
+ grid.$viewport.scrollTop(grid.$viewport.scrollTop() - (grid.config.rowHeight * 2));
+ }
+ if (!$scope.$$phase) {
+ $scope.$parent.$digest();
+ }
+ return false;
+ };
+
+ /***********************************************
+ * FILE: ..\src\utils.js
+ ***********************************************/
+ if (!String.prototype.trim) {
+ String.prototype.trim = function() {
+ return this.replace(/^\s+|\s+$/g, '');
+ };
+ }
+ if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function(elt /*, from*/) {
+ 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 /*, thisp */) {
+ "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]; // in case fun mutates this
+ 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];
+ }
+ },
+
+ // we copy KO's ie detection here bc it isn't exported in the min versions of KO
+ // Detect IE versions for workarounds (uses IE conditionals, not UA string, for robustness)
+ ieVersion: (function() {
+ var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
+ // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
+ 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;
+ })()
+ });
+
+ /***********************************************
+ * FILE: ..\src\filters\ngColumns.js
+ ***********************************************/
+ ngGridFilters.filter('ngColumns', function() {
+ return function(input) {
+ return input.filter(function(col) {
+ return !col.isAggCol;
+ });
+ };
+ });
+
+ /***********************************************
+ * FILE: ..\src\filters\checkmark.js
+ ***********************************************/
+ ngGridFilters.filter('checkmark', function() {
+ return function(input) {
+ return input ? '\u2714' : '\u2718';
+ };
+ });
+
+ /***********************************************
+ * FILE: ..\src\services\SortService.js
+ ***********************************************/
+ ngGridServices.factory('SortService', function() {
+ var sortService = {};
+ sortService.colSortFnCache = {}; // cache of sorting functions. Once we create them, we don't want to keep re-doing it
+ sortService.dateRE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; // nasty regex for date parsing
+ // this takes an piece of data from the cell and tries to determine its type and what sorting
+ // function to use for it
+ // @item - the cell data
+ sortService.guessSortFn = function(item) {
+ var sortFn, // sorting function that is guessed
+ itemType, // the typeof item
+ dateParts, // for date parsing
+ month, // for date parsing
+ day; // for date parsing
+
+ if (item === undefined || item === null || item === '') {
+ return null;
+ }
+ itemType = typeof(item);
+ //check for numbers and booleans
+ switch (itemType) {
+ case "number":
+ sortFn = sortService.sortNumber;
+ break;
+ case "boolean":
+ sortFn = sortService.sortBool;
+ break;
+ default:
+ sortFn = undefined;
+ break;
+ }
+ //if we found one, return it
+ if (sortFn) {
+ return sortFn;
+ }
+ //check if the item is a valid Date
+ if (Object.prototype.toString.call(item) === '[object Date]') {
+ return sortService.sortDate;
+ }
+ // if we aren't left with a string, return a basic sorting function...
+ if (itemType !== "string") {
+ return sortService.basicSort;
+ }
+ // now lets string check..
+ //check if the item data is a valid number
+ if (item.match(/^-?[�$�]?[\d,.]+%?$/)) {
+ return sortService.sortNumberStr;
+ }
+ // check for a date: dd/mm/yyyy or dd/mm/yy
+ // can have / or . or - as separator
+ // can be mm/dd as well
+ dateParts = item.match(sortService.dateRE);
+ if (dateParts) {
+ // looks like a date
+ month = parseInt(dateParts[1], 10);
+ day = parseInt(dateParts[2], 10);
+ if (month > 12) {
+ // definitely dd/mm
+ return sortService.sortDDMMStr;
+ } else if (day > 12) {
+ return sortService.sortMMDDStr;
+ } else {
+ // looks like a date, but we can't tell which, so assume that it's MM/DD
+ return sortService.sortMMDDStr;
+ }
+ }
+ //finally just sort the normal string...
+ return sortService.sortAlpha;
+ };
+ //#region Sorting Functions
+ 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;
+ }
+ // we want bad ones to get pushed to the bottom... which effectively is "greater than"
+ 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.sortDDMMStr = function(a, b) {
+ var dateA, dateB, mtch, m, d, y;
+ mtch = a.match(sortService.dateRE);
+ y = mtch[3];
+ m = mtch[2];
+ d = mtch[1];
+ if (m.length == 1) {
+ m = '0' + m;
+ }
+ if (d.length == 1) {
+ d = '0' + d;
+ }
+ dateA = y + m + d;
+ mtch = b.match(sortService.dateRE);
+ y = mtch[3];
+ m = mtch[2];
+ d = mtch[1];
+ if (m.length == 1) {
+ m = '0' + m;
+ }
+ if (d.length == 1) {
+ d = '0' + d;
+ }
+ dateB = y + m + d;
+ if (dateA == dateB) {
+ return 0;
+ }
+ if (dateA < dateB) {
+ return -1;
+ }
+ return 1;
+ };
+ sortService.sortMMDDStr = function(a, b) {
+ var dateA, dateB, mtch, m, d, y;
+ mtch = a.match(sortService.dateRE);
+ y = mtch[3];
+ d = mtch[2];
+ m = mtch[1];
+ if (m.length == 1) {
+ m = '0' + m;
+ }
+ if (d.length == 1) {
+ d = '0' + d;
+ }
+ dateA = y + m + d;
+ mtch = b.match(sortService.dateRE);
+ y = mtch[3];
+ d = mtch[2];
+ m = mtch[1];
+ if (m.length == 1) {
+ m = '0' + m;
+ }
+ if (d.length == 1) {
+ d = '0' + d;
+ }
+ dateB = y + m + d;
+ if (dateA == dateB) {
+ return 0;
+ }
+ if (dateA < dateB) {
+ return -1;
+ }
+ return 1;
+ };
+ //#endregion
+ // the core sorting logic trigger
+ sortService.sortData = function(data /*datasource*/, sortInfo) {
+ // first make sure we are even supposed to do work
+ if (!data || !sortInfo) {
+ return;
+ }
+ // grab the metadata for the rest of the logic
+ var col = sortInfo.column,
+ direction = sortInfo.direction,
+ sortFn,
+ item;
+
+ //see if we already figured out what to use to sort the column
+ 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 { // try and guess what sort function to use
+ item = data[0];
+ if (!item) {
+ return;
+ }
+ sortFn = sortService.guessSortFn(item[col.field]);
+ //cache it
+ if (sortFn) {
+ sortService.colSortFnCache[col.field] = sortFn;
+ } else {
+ // we assign the alpha sort because anything that is null/undefined will never get passed to
+ // the actual sorting function. It will get caught in our null check and returned to be sorted
+ // down to the bottom
+ sortFn = sortService.sortAlpha;
+ }
+ }
+ //now actually sort the data
+ data.sort(function(itemA, itemB) {
+ var propA = ng.utils.evalProperty(itemA, col.field);
+ var propB = ng.utils.evalProperty(itemB, col.field);
+ // we want to force nulls and such to the bottom when we sort... which effectively is "greater than"
+ if (!propB && !propA) {
+ return 0;
+ } else if (!propA) {
+ return 1;
+ } else if (!propB) {
+ return -1;
+ }
+ //made it this far, we don't have to worry about null & undefined
+ if (direction === ASC) {
+ return sortFn(propA, propB);
+ } else {
+ return 0 - sortFn(propA, propB);
+ }
+ });
+ return;
+ };
+ sortService.Sort = function(sortInfo, data) {
+ if (sortService.isSorting) {
+ return;
+ }
+ sortService.isSorting = true;
+ sortService.sortData(data, sortInfo);
+ sortService.isSorting = false;
+ };
+ return sortService;
+ });
+
+ /***********************************************
+ * FILE: ..\src\services\DomUtilityService.js
+ ***********************************************/
+ ngGridServices.factory('DomUtilityService', function() {
+ var domUtilityService = {};
+ var getWidths = function() {
+ var $testContainer = $('<div></div>');
+ $testContainer.appendTo('body');
+ // 1. Run all the following measurements on startup!
+ //measure Scroll Bars
+ $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();
+ //clear styles
+ $testContainer.attr('style', '');
+ //measure letter sizes using a pretty typical font size and fat font-family
+ $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(rootEl, grid) {
+ grid.$root = $(rootEl);
+ //Headers
+ grid.$topPanel = grid.$root.find(".ngTopPanel");
+ grid.$groupPanel = grid.$root.find(".ngGroupPanel");
+ grid.$headerContainer = grid.$topPanel.find(".ngHeaderContainer");
+ grid.$headerScroller = grid.$topPanel.find(".ngHeaderScroller");
+ grid.$headers = grid.$headerScroller.children();
+ //Viewport
+ grid.$viewport = grid.$root.find(".ngViewport");
+ //Canvas
+ grid.$canvas = grid.$viewport.find(".ngCanvas");
+ //Footers
+ grid.$footerPanel = grid.$root.find(".ngFooterPanel");
+ domUtilityService.UpdateGridLayout(grid);
+ };
+ domUtilityService.UpdateGridLayout = function(grid) {
+ //catch this so we can return the viewer to their original scroll after the resize!
+ var scrollTop = grid.$viewport.scrollTop();
+ grid.elementDims.rootMaxW = grid.$root.width();
+ grid.elementDims.rootMaxH = grid.$root.height();
+ //check to see if anything has changed
+ grid.refreshDomSizes();
+ grid.adjustScrollTop(scrollTop, true); //ensure that the user stays scrolled where they were
+ };
+ domUtilityService.numberOfGrids = 0;
+ domUtilityService.BuildStyles = function($scope, grid, digest) {
+ var rowHeight = grid.config.rowHeight,
+ $style = grid.$styleSheet,
+ gridId = grid.gridId,
+ css,
+ cols = $scope.visibleColumns(),
+ 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}";
+ angular.forEach(cols, function(col, i) {
+ css += "." + gridId + " .col" + i + " { width: " + col.width + "px; left: " + sumWidth + "px; right: " + (trw - sumWidth - col.width) + "px; height: " + rowHeight + "px }" +
+ "." + gridId + " .colt" + i + " { width: " + col.width + "px; }";
+ sumWidth += col.width;
+ });
+ if (ng.utils.isIe) { // IE
+ $style[0].styleSheet.cssText = css;
+ } else {
+ $style[0].appendChild(document.createTextNode(css));
+ }
+ grid.$styleSheet = $style;
+ if (digest) {
+ domUtilityService.digest($scope);
+ }
+ };
+
+ domUtilityService.digest = function($scope) {
+ if (!$scope.$$phase) {
+ $scope.$digest();
+ }
+ };
+ domUtilityService.ScrollH = 17; // default in IE, Chrome, & most browsers
+ domUtilityService.ScrollW = 17; // default in IE, Chrome, & most browsers
+ domUtilityService.LetterW = 10;
+ getWidths();
+ return domUtilityService;
+ });
+
+ /***********************************************
+ * FILE: ..\src\templates\gridTemplate.html
+ ***********************************************/
+ ng.defaultGridTemplate = function(){ return '<div class="ngTopPanel" ng-class="{\'ui-widget-header\':jqueryUITheme, \'ui-corner-top\': jqueryUITheme}" ng-style="topPanelStyle()"><div class="ngGroupPanel" ng-show="showGroupPanel()" ng-style="headerStyle()"><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-header-row></div></div><div class="ngHeaderButton" ng-show="showColumnMenu || showFilter" ng-click="toggleShowMenu()"><div class="ngHeaderButtonArrow" ng-click=""></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 type="checkbox" class="ngColListCheckbox" ng-model="col.visible"/>{{col.displayName}}</label><a title="Group By" ng-class="col.groupedByClass()" ng-show="col.groupable" ng-click="groupBy(col)"></a><span class="ngGroupingNumber" ng-show="col.groupIndex > 0">{{col.groupIndex}}</span></li></ul></div></div></div><div class="ngViewport" 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)" class="ngRow" ng-class="row.alternatingRowClass()" ng-row></div></div></div><div class="ngFooterPanel" ng-class="{\'ui-widget-content\': jqueryUITheme, \'ui-corner-bottom\': jqueryUITheme}" ng-style="footerStyle()"><div class="ngTotalSelectContainer" ng-show="footerVisible"><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="footerVisible && 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="text" style="width:50px; height: 24px; margin-top: 1px; padding: 0px 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>';};
+
+ /***********************************************
+ * FILE: ..\src\templates\rowTemplate.html
+ ***********************************************/
+ ng.defaultRowTemplate = function(){ return '<div ng-style="{\'cursor\': row.cursor}" ng-repeat="col in visibleColumns()" class="ngCell col{{$index}} {{col.cellClass}}" ng-cell></div>';};
+
+ /***********************************************
+ * FILE: ..\src\templates\cellTemplate.html
+ ***********************************************/
+ ng.defaultCellTemplate = function(){ return '<div class="ngCellText colt{{$index}}">{{row.getProperty(col.field) CUSTOM_FILTERS}}</div>';};
+
+ /***********************************************
+ * FILE: ..\src\templates\aggregateTemplate.html
+ ***********************************************/
+ ng.aggregateTemplate = function(){ return '<div ng-click="row.toggleExpand()" ng-style="{\'left\': row.offsetleft}" class="ngAggregate"><span class="ngAggregateText">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span><div class="{{row.aggClass()}}"></div></div>';};
+
+ /***********************************************
+ * FILE: ..\src\templates\headerRowTemplate.html
+ ***********************************************/
+ ng.defaultHeaderRowTemplate = function(){ return '<div ng-repeat="col in visibleColumns()" class="ngHeaderCell col{{$index}}" ng-header-cell></div>';};
+
+ /***********************************************
+ * FILE: ..\src\templates\headerCellTemplate.html
+ ***********************************************/
+ ng.defaultHeaderCellTemplate = function(){ return '<div ng-click="col.sort()" class="ngHeaderSortColumn {{col.headerClass}}" ng-style="{\'cursor\': col.cursor}" ng-class="{ \'ngSorted\': !noSortVisible }"><div class="ngHeaderText colt{{$index}}">{{col.displayName}}</div><div class="ngSortButtonDown" ng-show="col.showSortButtonDown()"></div><div class="ngSortButtonUp" ng-show="col.showSortButtonUp()"></div></div><div ng-show="col.resizable" class="ngHeaderGrip" ng-click="col.gripClick($event)" ng-mousedown="col.gripOnMouseDown($event)"></div>';};
+
+ /***********************************************
+ * FILE: ..\src\classes\aggregate.js
+ ***********************************************/
+ ng.Aggregate = function(aggEntity, rowFactory) {
+ var self = this;
+ self.index = 0;
+ self.offsetTop = 0;
+ 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;
+ self.notifyChildren();
+ };
+ self.setExpand = function(state) {
+ self.collapsed = state;
+ self.notifyChildren();
+ };
+ self.notifyChildren = function() {
+ angular.forEach(self.aggChildren, function(child) {
+ child.entity[NG_HIDDEN] = self.collapsed;
+ if (self.collapsed) {
+ child.setExpand(self.collapsed);
+ }
+ });
+ angular.forEach(self.children, function(child) {
+ child[NG_HIDDEN] = self.collapsed;
+ });
+ rowFactory.rowCache = [];
+ var foundMyself = false;
+ angular.forEach(rowFactory.aggCache, function(agg, i) {
+ if (foundMyself) {
+ var offset = (30 * self.children.length);
+ agg.offsetTop = self.collapsed ? agg.offsetTop - offset : agg.offsetTop + offset;
+ } else {
+ if (i == self.aggIndex) {
+ foundMyself = true;
+ }
+ }
+ });
+ 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;
+ }
+ };
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\eventProvider.js
+ ***********************************************/
+ ng.EventProvider = function(grid, $scope, domUtilityService) {
+ var self = this;
+ // The init method gets called during the ng-grid directive execution.
+ self.colToMove = undefined;
+ self.groupToMove = undefined;
+ self.assignEvents = function() {
+ // Here we set the onmousedown event handler to the header container.
+ if (grid.config.jqueryUIDraggable) {
+ 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.$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.dragOver = function(evt) {
+ evt.preventDefault();
+ };
+ //For JQueryUI
+ self.setDraggables = function() {
+ if (!grid.config.jqueryUIDraggable) {
+ grid.$root.find('.ngHeaderSortColumn').attr('draggable', 'true');
+ } 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);
+ // Get the scope from the header container
+ if (groupItem[0].className != 'ngRemoveGroup') {
+ var groupItemScope = angular.element(groupItem).scope();
+ if (groupItemScope) {
+ // set draggable events
+ if (!grid.config.jqueryUIDraggable) {
+ groupItem.attr('draggable', 'true');
+ }
+ // Save the column for later.
+ self.groupToMove = { header: groupItem, groupName: groupItemScope.group, index: groupItemScope.$index };
+ }
+ } else {
+ self.groupToMove = undefined;
+ }
+ };
+ self.onGroupDrop = function(event) {
+ event.stopPropagation();
+ // clear out the colToMove object
+ var groupContainer;
+ var groupScope;
+ if (self.groupToMove) {
+ // Get the closest header to where we dropped
+ groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
+ 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 we have the same column, do nothing.
+ if (self.groupToMove.index != groupScope.$index) {
+ // Splice the columns
+ $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'); // Get the scope from the header.
+ if (groupContainer.context.className == 'ngGroupPanel' || groupContainer.context.className == 'ngGroupPanelDescription') {
+ $scope.groupBy(self.colToMove.col);
+ } else {
+ groupScope = angular.element(groupContainer).scope();
+ if (groupScope) {
+ // Splice the columns
+ $scope.removeGroup(groupScope.$index);
+ }
+ }
+ }
+ self.colToMove = undefined;
+ }
+ if (!$scope.$$phase) {
+ $scope.$apply();
+ }
+ };
+ //Header functions
+ self.onHeaderMouseDown = function(event) {
+ // Get the closest header container from where we clicked.
+ var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
+ // Get the scope from the header container
+ var headerScope = angular.element(headerContainer).scope();
+ if (headerScope) {
+ // Save the column for later.
+ self.colToMove = { header: headerContainer, col: headerScope.col };
+ }
+ };
+ self.onHeaderDrop = function(event) {
+ if (!self.colToMove) {
+ return;
+ }
+ // Get the closest header to where we dropped
+ var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
+ // Get the scope from the header.
+ var headerScope = angular.element(headerContainer).scope();
+ if (headerScope) {
+ // If we have the same column, do nothing.
+ if (self.colToMove.col == headerScope.col) {
+ return;
+ }
+ // Splice the columns
+ $scope.columns.splice(self.colToMove.col.index, 1);
+ $scope.columns.splice(headerScope.col.index, 0, self.colToMove.col);
+ grid.fixColumnIndexes();
+ // Finally, rebuild the CSS styles.
+ domUtilityService.BuildStyles($scope, grid, true);
+ // clear out the colToMove object
+ self.colToMove = undefined;
+ }
+ };
+ // Row functions
+ self.onRowMouseDown = function(event) {
+ // Get the closest row element from where we clicked.
+ var targetRow = $(event.target).closest('.ngRow');
+ // Get the scope from the row element
+ var rowScope = angular.element(targetRow).scope();
+ if (rowScope) {
+ // set draggable events
+ targetRow.attr('draggable', 'true');
+ // Save the row for later.
+ domUtilityService.eventStorage.rowToMove = { targetRow: targetRow, scope: rowScope };
+ }
+ };
+ self.onRowDrop = function(event) {
+ // Get the closest row to where we dropped
+ var targetRow = $(event.target).closest('.ngRow');
+ // Get the scope from the row element.
+ var rowScope = angular.element(targetRow).scope();
+ if (rowScope) {
+ // If we have the same Row, do nothing.
+ var prevRow = domUtilityService.eventStorage.rowToMove;
+ if (prevRow.scope.row == rowScope.row) {
+ return;
+ }
+ // Splice the Rows via the actual datasource
+ var i = grid.sortedData.indexOf(prevRow.scope.row.entity);
+ var j = grid.sortedData.indexOf(rowScope.row.entity);
+ grid.sortedData.splice(i, 1);
+ grid.sortedData.splice(j, 0, prevRow.scope.row.entity);
+ grid.searchProvider.evalFilter();
+ // clear out the rowToMove object
+ domUtilityService.eventStorage.rowToMove = undefined;
+ // if there isn't an apply already in progress lets start one
+ }
+ };
+
+ self.assignGridEventHandlers = function() {
+ grid.$viewport.on('scroll', function(e) {
+ var scrollLeft = e.target.scrollLeft,
+ scrollTop = e.target.scrollTop;
+ grid.adjustScrollLeft(scrollLeft);
+ grid.adjustScrollTop(scrollTop);
+ });
+ var doingKeyDown = false;
+ grid.$viewport.on('keydown', function(e) {
+ if (!doingKeyDown) {
+ doingKeyDown = true;
+ var ret = ng.moveSelectionHandler($scope, grid, e);
+ doingKeyDown = false;
+ return ret;
+ }
+ return false;
+ });
+ //Chrome and firefox both need a tab index so the grid can recieve focus.
+ //need to give the grid a tabindex if it doesn't already have one so
+ //we'll just give it a tab index of the corresponding gridcache index
+ //that way we'll get the same result every time it is run.
+ //configurable within the options.
+ 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.UpdateGridLayout(grid);
+ if (grid.config.maintainColumnRatios) {
+ grid.configureColumnWidths();
+ }
+ });
+ };
+ // In this example we want to assign grid events.
+ self.assignGridEventHandlers();
+ self.assignEvents();
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\column.js
+ ***********************************************/
+ ng.Column = function(config, $scope, grid, domUtilityService) {
+ 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.headerRowHeight = config.headerRowHeight;
+ self.displayName = colDef.displayName || colDef.field;
+ self.index = config.index;
+ self.isAggCol = config.isAggCol;
+ self.cellClass = colDef.cellClass;
+ 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.groupable = ng.utils.isNullOrUndefined(colDef.groupable) || colDef.sortable;
+ if (config.enableSort) {
+ self.sortable = ng.utils.isNullOrUndefined(colDef.sortable) || colDef.sortable;
+ }
+ if (config.enableResize) {
+ self.resizable = ng.utils.isNullOrUndefined(colDef.resizable) || colDef.resizable;
+ }
+ self.sortDirection = undefined;
+ self.sortingAlgorithm = colDef.sortFn;
+ self.headerClass = colDef.headerClass;
+ self.headerCellTemplate = colDef.headerCellTemplate || ng.defaultHeaderCellTemplate();
+ self.cursor = self.sortable ? 'pointer' : 'default';
+ self.cellTemplate = colDef.cellTemplate || ng.defaultCellTemplate().replace(CUSTOM_FILTERS, self.cellFilter ? "|" + self.cellFilter : "");
+ if (colDef.cellTemplate && !TEMPLATE_REGEXP.test(colDef.cellTemplate)) {
+ self.cellTemplate = $.ajax({
+ type: "GET",
+ url: colDef.cellTemplate,
+ async: false
+ }).responseText;
+ }
+ if (colDef.headerCellTemplate && !TEMPLATE_REGEXP.test(colDef.headerCellTemplate)) {
+ self.headerCellTemplate = $.ajax({
+ type: "GET",
+ url: colDef.headerCellTemplate,
+ async: false
+ }).responseText;
+ }
+ 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() {
+ if (!self.sortable) {
+ return true; // column sorting is disabled, do nothing
+ }
+ var dir = self.sortDirection === ASC ? DESC : ASC;
+ self.sortDirection = dir;
+ config.sortCallback(self);
+ return false;
+ };
+ self.gripClick = function() {
+ clicks++; //count clicks
+ if (clicks === 1) {
+ timer = setTimeout(function() {
+ //Here you can add a single click action.
+ clicks = 0; //after action performed, reset counter
+ }, delay);
+ } else {
+ clearTimeout(timer); //prevent single-click action
+ config.resizeOnDataCallback(self); //perform double-click action
+ clicks = 0; //after action performed, reset counter
+ }
+ };
+ self.gripOnMouseDown = function(event) {
+ if (event.ctrlKey) {
+ 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() {
+ $(document).off('mousemove');
+ $(document).off('mouseup');
+ event.target.parentElement.style.cursor = 'default';
+ domUtilityService.digest($scope);
+ return false;
+ };
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\dimension.js
+ ***********************************************/
+ ng.Dimension = function(options) {
+ this.outerHeight = null;
+ this.outerWidth = null;
+ $.extend(this, options);
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\footer.js
+ ***********************************************/
+ ng.Footer = function($scope, grid) {
+ $scope.maxRows = function () {
+ var ret = Math.max($scope.pagingOptions.totalServerItems, grid.sortedData.length);
+ return ret;
+ };
+
+ $scope.multiSelect = (grid.config.canSelectRows && 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.sortedData.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);
+ };
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\rowFactory.js
+ ***********************************************/
+ ng.RowFactory = function(grid, $scope) {
+ var self = this;
+ // we cache rows when they are built, and then blow the cache away when sorting
+ self.rowCache = [];
+ self.aggCache = {};
+ self.parentCache = []; // Used for grouping and is cleared each time groups are calulated.
+ self.dataChanged = true;
+ self.parsedData = [];
+ self.rowConfig = {};
+ self.selectionService = grid.selectionService;
+ self.rowHeight = 30;
+ self.numberOfAggregates = 0;
+ self.groupedData = undefined;
+ self.rowHeight = grid.config.rowHeight;
+ self.rowConfig = {
+ canSelectRows: grid.config.canSelectRows,
+ rowClasses: grid.config.rowClasses,
+ selectedItems: grid.config.selectedItems,
+ selectWithCheckboxOnly: grid.config.selectWithCheckboxOnly,
+ beforeSelectionChangeCallback: grid.config.beforeSelectionChange,
+ afterSelectionChangeCallback: grid.config.afterSelectionChange,
+ jqueryUITheme: grid.config.jqueryUITheme
+ };
+
+ self.renderedRange = new ng.Range(0, grid.minRowsToRender() + EXCESS_ROWS);
+ // Builds rows for each data item in the 'filteredData'
+ // @entity - the data item
+ // @rowIndex - the index of the row
+ self.buildEntityRow = function(entity, rowIndex) {
+ var row = self.rowCache[rowIndex]; // first check to see if we've already built it
+ if (!row) {
+ // build the row
+ row = new ng.Row(entity, self.rowConfig, self.selectionService);
+ row.rowIndex = rowIndex + 1; //not a zero-based rowIndex
+ row.offsetTop = self.rowHeight * rowIndex;
+ row.selected = entity[SELECTED_PROP];
+ // finally cache it for the next round
+ self.rowCache[rowIndex] = row;
+ }
+ return row;
+ };
+
+ self.buildAggregateRow = function(aggEntity, rowIndex) {
+ var agg = self.aggCache[aggEntity.aggIndex]; // first check to see if we've already built it
+ if (!agg) {
+ // build the row
+ agg = new ng.Aggregate(aggEntity, self);
+ self.aggCache[aggEntity.aggIndex] = agg;
+ }
+ agg.index = rowIndex + 1; //not a zero-based rowIndex
+ agg.offsetTop = self.rowHeight * rowIndex;
+ return agg;
+ };
+ self.UpdateViewableRange = function(newRange) {
+ self.renderedRange = newRange;
+ self.renderedChange();
+ };
+ self.filteredDataChanged = function() {
+ // check for latebound autogenerated columns
+ if (grid.lateBoundColumns && grid.filteredData.length > 1) {
+ grid.config.columnDefs = undefined;
+ grid.buildColumns();
+ grid.lateBoundColumns = false;
+ }
+ self.dataChanged = true;
+ self.rowCache = []; //if data source changes, kill this!
+ 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.parentCache = [];
+ var rowArr = [];
+ var dataArray = self.parsedData.filter(function(e) {
+ return e[NG_HIDDEN] === false;
+ }).slice(self.renderedRange.topRow, self.renderedRange.bottomRow);
+ angular.forEach(dataArray, function(item, indx) {
+ var row;
+ if (item.isAggRow) {
+ row = self.buildAggregateRow(item, self.renderedRange.topRow + indx);
+ } else {
+ row = self.buildEntityRow(item, self.renderedRange.topRow + indx);
+ }
+ //add the row to our return array
+ rowArr.push(row);
+ });
+ grid.setRenderedRows(rowArr);
+ };
+
+ self.renderedChangeNoGroups = function() {
+ var rowArr = [];
+ var dataArr = grid.filteredData.slice(self.renderedRange.topRow, self.renderedRange.bottomRow);
+ angular.forEach(dataArr, function(item, i) {
+ var row = self.buildEntityRow(item, self.renderedRange.topRow + i);
+ //add the row to our return array
+ rowArr.push(row);
+ });
+ grid.setRenderedRows(rowArr);
+ };
+
+ //magical recursion. it works. I swear it. I figured it out in the shower one day.
+ self.parseGroupData = function(g) {
+ if (g.values) {
+ angular.forEach(g.values, function(item) {
+ // get the last parent in the array because that's where our children want to be
+ self.parentCache[self.parentCache.length - 1].children.push(item);
+ //add the row to our return array
+ self.parsedData.push(item);
+ });
+ } else {
+ for (var prop in g) {
+ // exclude the meta properties.
+ if (prop == NG_FIELD || prop == NG_DEPTH || prop == NG_COLUMN) {
+ continue;
+ } else if (g.hasOwnProperty(prop)) {
+ //build the aggregate row
+ 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++;
+ //set the aggregate parent to the parent in the array that is one less deep.
+ agg.parent = self.parentCache[agg.depth - 1];
+ // if we have a parent, set the parent to not be collapsed and append the current agg to its children
+ if (agg.parent) {
+ agg.parent.collapsed = false;
+ agg.parent.aggChildren.push(agg);
+ }
+ // add the aggregate row to the parsed data.
+ self.parsedData.push(agg.entity);
+ // the current aggregate now the parent of the current depth
+ self.parentCache[agg.depth] = agg;
+ // dig deeper for more aggregates or children.
+ self.parseGroupData(g[prop]);
+ }
+ }
+ }
+ };
+ //Shuffle the data into their respective groupings.
+ self.getGrouping = function(groups) {
+ self.aggCache = [];
+ self.rowCache = [];
+ self.numberOfAggregates = 0;
+ self.groupedData = {};
+ // Here we set the onmousedown event handler to the header container.
+ var data = grid.filteredData;
+ var maxDepth = groups.length;
+ var cols = $scope.columns;
+
+ angular.forEach(data, function(item) {
+ item[NG_HIDDEN] = true;
+ var ptr = self.groupedData;
+ angular.forEach(groups, function(group, depth) {
+ if (!cols[depth].isAggCol && depth <= maxDepth) {
+ cols.splice(item.gDepth, 0, new ng.Column({
+ colDef: {
+ field: '',
+ width: 25,
+ sortable: false,
+ resizable: false,
+ headerCellTemplate: '<div class="ngAggHeader"></div>'
+ },
+ isAggCol: true,
+ index: item.gDepth,
+ headerRowHeight: grid.config.headerRowHeight
+ }));
+ }
+ var col = cols.filter(function(c) {
+ return c.field == group;
+ })[0];
+ var val = ng.utils.evalProperty(item, 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] = depth;
+ }
+ if (!ptr[NG_COLUMN]) {
+ ptr[NG_COLUMN] = col;
+ }
+ ptr = ptr[val];
+ });
+ if (!ptr.values) {
+ ptr.values = [];
+ }
+ ptr.values.push(item);
+ });
+ grid.fixColumnIndexes();
+ self.parsedData.length = 0;
+ self.parseGroupData(self.groupedData);
+ };
+
+ if (grid.config.groups.length > 0 && grid.filteredData.length > 0) {
+ self.getGrouping(grid.config.groups);
+ }
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\grid.js
+ ***********************************************/
+ ng.Grid = function($scope, options, sortService, domUtilityService, $filter) {
+ var defaults = {
+ //Callback for when you want to validate something after selection.
+ afterSelectionChange: function() {
+ },
+
+ /* Callback if you want to inspect something before selection,
+ return false if you want to cancel the selection. return true otherwise.
+ If you need to wait for an async call to proceed with selection you can
+ use rowItem.continueSelection(event) method after returning false initially.
+ Note: when shift+ Selecting multiple items in the grid this will only get called
+ once and the rowItem will be an array of items that are queued to be selected. */
+ beforeSelectionChange: function() {
+ return true;
+ },
+
+ //To be able to have selectable rows in grid.
+ canSelectRows: true,
+
+ //definitions of columns as an array [], if not defines columns are auto-generated. See github wiki for more details.
+ columnDefs: undefined,
+
+ //*Data being displayed in the grid. Each item in the array is mapped to a row being displayed.
+ data: [],
+
+ //Row selection check boxes appear as the first column.
+ displaySelectionCheckbox: true,
+
+ //Enable or disable resizing of columns
+ enableColumnResize: true,
+
+ //Enable or disable resizing of columns
+ enableColumnReordering: true,
+
+ //Enables the server-side paging feature
+ enablePaging: false,
+
+ //Enable drag and drop row reordering. Only works in HTML5 compliant browsers.
+ enableRowReordering: true,
+
+ //Enables or disables sorting in grid.
+ enableSorting: true,
+
+ /* filterOptions -
+ filterText: The text bound to the built-in search box.
+ useExternalFilter: Bypass internal filtering if you want to roll your own filtering mechanism but want to use builtin search box.
+ */
+ filterOptions: {
+ filterText: "",
+ useExternalFilter: false
+ },
+
+ //Defining the height of the footer in pixels.
+ footerRowHeight: 55,
+
+ //Show or hide the footer alltogether the footer is enabled by default
+ displayFooter: undefined,
+ footerVisible: true, // depricated
+
+ //Initial fields to group data by. Array of field names, not displayName.
+ groups: [],
+
+ //The height of the header row in pixels.
+ headerRowHeight: 30,
+
+ //Define a header row template for further customization. See github wiki for more details.
+ headerRowTemplate: undefined,
+
+ /*Enables the use of jquery UI reaggable/droppable plugin. requires jqueryUI to work if enabled.
+ Useful if you want drag + drop but your users insist on crappy browsers. */
+ jqueryUIDraggable: false,
+
+ //Enable the use jqueryUIThemes
+ jqueryUITheme: false,
+
+ //Prevent unselections when in single selection mode.
+ keepLastSelected: true,
+
+ /*Maintains the column widths while resizing.
+ Defaults to true when using *'s or undefined widths. Can be ovverriden by setting to false.*/
+ maintainColumnRatios: undefined,
+
+ //Set this to false if you only want one item selected at a time
+ multiSelect: true,
+
+ // pagingOptions -
+
+ pagingOptions: {
+ // pageSizes: list of available page sizes.
+ pageSizes: [250, 500, 1000],
+ //pageSize: currently selected page size.
+ pageSize: 250,
+ //totalServerItems: Total items are on the server.
+ totalServerItems: 0,
+ //currentPage: the uhm... current page.
+ currentPage: 1
+ },
+
+ //Array of plugin functions to register in ng-grid
+ plugins: [],
+
+ //Row height of rows in grid.
+ rowHeight: 30,
+
+ //Define a row Template to customize output. See github wiki for more details.
+ rowTemplate: undefined,
+
+ //all of the items selected in the grid. In single select mode there will only be one item in the array.
+ selectedItems: [],
+
+ //Disable row selections by clicking on the row and only when the checkbox is clicked.
+ selectWithCheckboxOnly: false,
+
+ /*Enables menu to choose which columns to display and group by.
+ If both showColumnMenu and showFilter are false the menu button will not display.*/
+ showColumnMenu: true,
+
+ /*Enables display of the filterbox in the column menu.
+ If both showColumnMenu and showFilter are false the menu button will not display.*/
+ showFilter: true,
+
+ //Show the dropzone for drag and drop grouping
+ showGroupPanel: false,
+
+ /*Define a sortInfo object to specify a default sorting state.
+ You can also observe this variable to utilize server-side sorting (see useExternalSorting).
+ Syntax is sortinfo: { field: 'fieldName', direction: 'ASC'/'asc' || 'desc'/'DESC'}*/
+ sortInfo: undefined,
+
+ //Set the tab index of the Vieport.
+ tabIndex: -1,
+ /*Prevents the internal sorting from executing.
+ The sortInfo object will be updated with the sorting information so you can handle sorting (see sortInfo)*/
+ useExternalSorting: false,
+
+ /*i18n language support. choose from the installed or included languages, en, fr, sp, etc...*/
+ i18n: 'en'
+ },
+ self = this;
+
+ self.maxCanvasHt = 0;
+ //self vars
+ self.config = $.extend(defaults, options);
+ if (typeof options.columnDefs == "string") {
+ self.config.columnDefs = $scope.$eval(options.columnDefs);
+ }
+ self.gridId = "ng" + ng.utils.newId();
+ self.$root = null; //this is the root element that is passed in with the binding handler
+ 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.sortedData = [];
+ self.lateBindColumns = false;
+ self.filteredData = [];
+ if (typeof self.config.data == "object") {
+ self.sortedData = self.config.data; // we cannot watch for updates if you don't pass the string name
+ }
+ self.lastSortedColumn = undefined;
+ self.calcMaxCanvasHeight = function() {
+ return (self.config.groups.length > 0) ? (self.rowFactory.parsedData.filter(function(e) {
+ return e[NG_HIDDEN] === false;
+ }).length * self.config.rowHeight) : (self.filteredData.length * self.config.rowHeight);
+ };
+ self.elementDims = {
+ scrollW: 0,
+ scrollH: 0,
+ rowIndexCellW: 25,
+ rowSelectedCellW: 25,
+ rootMaxW: 0,
+ rootMaxH: 0
+ };
+ //self funcs
+ self.setRenderedRows = function(newRows) {
+ $scope.renderedRows = newRows;
+ if (!$scope.$$phase) {
+ $scope.$digest();
+ }
+ 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() {
+ if (!self.config.columnDefs) {
+ self.config.columnDefs = [];
+ }
+ if (!self.sortedData || !self.sortedData[0]) {
+ self.lateBoundColumns = true;
+ return;
+ }
+ var item;
+ item = self.sortedData[0];
+
+ ng.utils.forIn(item, function(prop, propName) {
+ if (propName != SELECTED_PROP) {
+ self.config.columnDefs.push({
+ field: propName
+ });
+ }
+ });
+ };
+ self.buildColumns = function() {
+ var columnDefs = self.config.columnDefs,
+ cols = [],
+ indexOffset = 0;
+
+ if (!columnDefs) {
+ self.buildColumnDefsFromData();
+ columnDefs = self.config.columnDefs;
+ }
+ if (self.config.displaySelectionCheckbox) {
+ indexOffset = 1;
+ cols.push(new ng.Column({
+ colDef: {
+ field: '\u2714',
+ width: self.elementDims.rowSelectedCellW,
+ sortable: false,
+ resizable: false,
+ groupable: false,
+ headerCellTemplate: '<input class="ngSelectionHeader" type="checkbox" ng-show="multiSelect" ng-model="allSelected" ng-change="toggleSelectAll(allSelected)"/>',
+ cellTemplate: '<div class="ngSelectionCell"><input class="ngSelectionCheckbox" type="checkbox" ng-checked="row.selected" /></div>'
+ },
+ index: 0,
+ headerRowHeight: self.config.headerRowHeight,
+ sortCallback: self.sortData,
+ resizeOnDataCallback: self.resizeOnData,
+ enableResize: self.config.enableColumnResize,
+ enableSort: self.config.enableSorting
+ }, $scope, self, domUtilityService, $filter));
+ }
+ if (columnDefs.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
+ }, $scope, self, domUtilityService);
+ cols.push(column);
+ var indx = self.config.groups.indexOf(colDef.field);
+ if (indx != -1) {
+ $scope.configGroups.splice(indx, 0, column);
+ }
+ });
+ $scope.columns = cols;
+ }
+ };
+ self.configureColumnWidths = function() {
+ var cols = self.config.columnDefs;
+ var indexOffset = self.config.displaySelectionCheckbox ? $scope.configGroups.length + 1 : $scope.configGroups.length;
+ var numOfCols = cols.length + indexOffset,
+ asterisksArray = [],
+ percentArray = [],
+ asteriskNum = 0,
+ totalWidth = 0;
+ totalWidth += self.config.displaySelectionCheckbox ? 25 : 0;
+ angular.forEach(cols, function(col, i) {
+ i += indexOffset;
+ var isPercent = false, t = undefined;
+ //if width is not defined, set it to a single star
+ if (ng.utils.isNullOrUndefined(col.width)) {
+ col.width = "*";
+ } else { // get column width
+ isPercent = isNaN(col.width) ? ng.utils.endsWith(col.width, "%") : false;
+ t = isPercent ? col.width : parseInt(col.width, 10);
+ }
+ // check if it is a number
+ if (isNaN(t)) {
+ t = col.width;
+ // figure out if the width is defined or if we need to calculate it
+ if (t == 'auto') { // set it for now until we have data and subscribe when it changes so we can set the width.
+ $scope.columns[i].width = col.minWidth;
+ totalWidth += $scope.columns[i].width;
+ var temp = $scope.columns[i];
+ $scope.$evalAsync(function() {
+ self.resizeOnData(temp, true);
+ });
+ return;
+ } else if (t.indexOf("*") != -1) { // we need to save it until the end to do the calulations on the remaining width.
+ asteriskNum += t.length;
+ col.index = i;
+ asterisksArray.push(col);
+ return;
+ } else if (isPercent) { // If the width is a percentage, save it until the very last.
+ col.index = i;
+ percentArray.push(col);
+ return;
+ } else { // we can't parse the width so lets throw an error.
+ throw "unable to parse column width, use percentage (\"10%\",\"20%\", etc...) or \"*\" to use remaining width of grid";
+ }
+ } else {
+ totalWidth += $scope.columns[i].width = parseInt(col.width, 10);
+ }
+ });
+ // check if we saved any asterisk columns for calculating later
+ if (asterisksArray.length > 0) {
+ self.config.maintainColumnRatios === false ? angular.noop() : self.config.maintainColumnRatios = true;
+ // get the remaining width
+ var remainigWidth = self.rootDim.outerWidth - totalWidth;
+ // calculate the weight of each asterisk rounded down
+ var asteriskVal = Math.floor(remainigWidth / asteriskNum);
+ // set the width of each column based on the number of stars
+ angular.forEach(asterisksArray, function(col) {
+ var t = col.width.length;
+ $scope.columns[col.index].width = asteriskVal * t;
+ //check if we are on the last column
+ if (col.index + 1 == numOfCols) {
+ var offset = 2; //We're going to remove 2 px so we won't overlflow the viwport by default
+ // are we overflowing?
+ if (self.maxCanvasHt > $scope.viewportDimHeight()) {
+ //compensate for scrollbar
+ offset += domUtilityService.ScrollW;
+ }
+ $scope.columns[col.index].width -= offset;
+ }
+ totalWidth += $scope.columns[col.index].width;
+ });
+ }
+ // Now we check if we saved any percentage columns for calculating last
+ if (percentArray.length > 0) {
+ // do the math
+ 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() {
+ //factories and services
+ self.selectionService = new ng.SelectionService(self);
+ self.rowFactory = new ng.RowFactory(self, $scope);
+ self.selectionService.Initialize(self.rowFactory);
+ self.searchProvider = new ng.SearchProvider($scope, self, $filter);
+ self.styleProvider = new ng.StyleProvider($scope, self, domUtilityService);
+ self.buildColumns();
+ $scope.$watch('configGroups', function(a) {
+ var tempArr = [];
+ angular.forEach(a, function(item) {
+ tempArr.push(item.field || item);
+ });
+ self.config.groups = tempArr;
+ self.rowFactory.filteredDataChanged();
+ $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) {
+ ng.utils.seti18n($scope, newLang);
+ });
+ self.maxCanvasHt = self.calcMaxCanvasHeight();
+ if (self.config.sortInfo) {
+ self.config.sortInfo.column = $scope.columns.filter(function(c) {
+ return c.field == self.config.sortInfo.field;
+ })[0];
+ self.config.sortInfo.column.sortDirection = self.config.sortInfo.direction.toUpperCase();
+ self.sortData(self.config.sortInfo.column);
+ }
+ };
+ self.prevScrollTop = 0;
+ self.prevScrollIndex = 0;
+ self.adjustScrollTop = function(scrollTop, force) {
+ if (self.prevScrollTop === scrollTop && !force) {
+ return;
+ }
+ var rowIndex = Math.floor(scrollTop / self.config.rowHeight);
+ // Have we hit the threshold going down?
+ if (self.prevScrollTop < scrollTop && rowIndex < self.prevScrollIndex + SCROLL_THRESHOLD) {
+ return;
+ }
+ //Have we hit the threshold going up?
+ if (self.prevScrollTop > scrollTop && rowIndex > self.prevScrollIndex - SCROLL_THRESHOLD) {
+ return;
+ }
+ self.prevScrollTop = scrollTop;
+ self.rowFactory.UpdateViewableRange(new ng.Range(Math.max(0, rowIndex - EXCESS_ROWS), rowIndex + self.minRowsToRender() + EXCESS_ROWS));
+ self.prevScrollIndex = rowIndex;
+ };
+ self.adjustScrollLeft = function(scrollLeft) {
+ if (self.$headerContainer) {
+ self.$headerContainer.scrollLeft(scrollLeft);
+ }
+ };
+ self.resizeOnData = function(col) {
+ // we calculate the longest data.
+ var longest = col.minWidth;
+ var arr = ng.utils.getElementsByClassName('col' + col.index);
+ angular.forEach(arr, function(elem, index) {
+ var i;
+ if (index === 0) {
+ var kgHeaderText = $(elem).find('.ngHeaderText');
+ i = ng.utils.visualLength(kgHeaderText) + 10; // +10 some margin
+ } else {
+ var ngCellText = $(elem).find('.ngCellText');
+ i = ng.utils.visualLength(ngCellText) + 10; // +10 some margin
+ }
+ if (i > longest) {
+ longest = i;
+ }
+ });
+ col.width = col.longest = Math.min(col.maxWidth, longest + 7); // + 7 px to make it look decent.
+ domUtilityService.BuildStyles($scope, self, true);
+ };
+ self.sortData = function(col) {
+ self.config.sortInfo = {
+ column: col,
+ field: col.field,
+ direction: col.sortDirection
+ };
+ self.clearSortingData(col);
+ if (!self.config.useExternalSorting) {
+ sortService.Sort(self.config.sortInfo, self.sortedData);
+ }
+ self.lastSortedColumn = col;
+ self.searchProvider.evalFilter();
+ $scope.$emit('ngGridEventSorted', col);
+ };
+ self.clearSortingData = function(col) {
+ if (!col) {
+ angular.forEach($scope.columns, function(c) {
+ c.sortDirection = "";
+ });
+ } else if (self.lastSortedColumn && col != self.lastSortedColumn) {
+ self.lastSortedColumn.sortDirection = "";
+ }
+ };
+ self.fixColumnIndexes = function() {
+ //fix column indexes
+ angular.forEach($scope.columns, function(col, i) {
+ col.index = i;
+ });
+ };
+ self.fixGroupIndexes = function() {
+ angular.forEach($scope.configGroups, function(item, i) {
+ item.groupIndex = i + 1;
+ });
+ };
+ //$scope vars
+ $scope.elementsNeedMeasuring = true;
+ $scope.columns = [];
+ $scope.renderedRows = [];
+ $scope.headerRow = null;
+ $scope.rowHeight = self.config.rowHeight;
+ $scope.jqueryUITheme = self.config.jqueryUITheme;
+ $scope.footer = null;
+ $scope.selectedItems = self.config.selectedItems;
+ $scope.multiSelect = self.config.multiSelect;
+ $scope.footerVisible = ng.utils.isNullOrUndefined(self.config.displayFooter) ? self.config.footerVisible : self.config.displayFooter;
+ $scope.footerRowHeight = $scope.footerVisible ? self.config.footerRowHeight : 0;
+ $scope.showColumnMenu = self.config.showColumnMenu;
+ $scope.showMenu = false;
+ $scope.configGroups = [];
+ //Paging
+ $scope.enablePaging = self.config.enablePaging;
+ $scope.pagingOptions = self.config.pagingOptions;
+ //Templates
+ $scope.rowTemplate = self.config.rowTemplate || ng.defaultRowTemplate();
+ $scope.headerRowTemplate = self.config.headerRowTemplate || ng.defaultHeaderRowTemplate();
+ //i18n support
+ $scope.i18n = {};
+ ng.utils.seti18n($scope, self.config.i18n);
+
+ if (self.config.rowTemplate && !TEMPLATE_REGEXP.test(self.config.rowTemplate)) {
+ $scope.rowTemplate = $.ajax({
+ type: "GET",
+ url: self.config.rowTemplate,
+ async: false
+ }).responseText;
+ }
+ if (self.config.headerRowTemplate && !TEMPLATE_REGEXP.test(self.config.headerRowTemplate)) {
+ $scope.headerRowTemplate = $.ajax({
+ type: "GET",
+ url: self.config.headerRowTemplate,
+ async: false
+ }).responseText;
+ }
+
+ //scope funcs
+ $scope.visibleColumns = function() {
+ return $scope.columns.filter(function(col) {
+ return col.visible;
+ });
+ };
+ $scope.toggleShowMenu = function() {
+ $scope.showMenu = !$scope.showMenu;
+ };
+ $scope.toggleSelectAll = function(a) {
+ self.selectionService.toggleSelectAll(a);
+ };
+ $scope.totalFilteredItemsLength = function() {
+ return self.filteredData.length;
+ };
+ $scope.showGroupPanel = function() {
+ return self.config.showGroupPanel;
+ };
+ $scope.topPanelHeight = function() {
+ return self.config.showGroupPanel === true ? self.config.headerRowHeight * 2 : self.config.headerRowHeight;
+ };
+
+ $scope.viewportDimHeight = function() {
+ return Math.max(0, self.rootDim.outerHeight - $scope.topPanelHeight() - $scope.footerRowHeight - 2);
+ };
+ $scope.groupBy = function(col) {
+ if (self.sortedData.length < 1 || !col.groupable) {
+ return;
+ }
+ 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);
+ }
+ };
+ $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.totalRowWidth = function() {
+ var totalWidth = 0,
+ cols = $scope.visibleColumns();
+ angular.forEach(cols, function(col) {
+ totalWidth += col.width;
+ });
+ return totalWidth;
+ };
+ $scope.headerScrollerDim = function() {
+ var viewportH = $scope.viewportDimHeight(),
+ maxHeight = self.maxCanvasHt,
+ vScrollBarIsOpen = (maxHeight > viewportH),
+ newDim = new ng.Dimension();
+
+ newDim.autoFitHeight = true;
+ newDim.outerWidth = $scope.totalRowWidth();
+ if (vScrollBarIsOpen) {
+ newDim.outerWidth += self.elementDims.scrollW;
+ } else if ((maxHeight - viewportH) <= self.elementDims.scrollH) { //if the horizontal scroll is open it forces the viewport to be smaller
+ newDim.outerWidth += self.elementDims.scrollW;
+ }
+ return newDim;
+ };
+ //call init
+ self.init();
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\range.js
+ ***********************************************/
+ ng.Range = function(top, bottom) {
+ this.topRow = top;
+ this.bottomRow = bottom;
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\row.js
+ ***********************************************/
+ ng.Row = function(entity, config, selectionService) {
+ var self = this, // constant for the selection property that we add to each data item
+ canSelectRows = config.canSelectRows;
+
+ self.jqueryUITheme = config.jqueryUITheme;
+ self.rowClasses = config.rowClasses;
+ self.entity = entity;
+ self.selectionService = selectionService;
+ self.selected = false;
+ self.cursor = canSelectRows ? 'pointer' : 'default';
+ self.continueSelection = function(event) {
+ self.selectionService.ChangeSelection(self, event);
+ };
+ self.toggleSelected = function(event) {
+ if (!canSelectRows) {
+ return true;
+ }
+ var element = event.target || event;
+ //check and make sure its not the bubbling up of our checked 'click' event
+ if (element.type == "checkbox" && element.parentElement.className != "ngSelectionCell ng-scope") {
+ return true;
+ }
+ if (config.selectWithCheckboxOnly && element.type != "checkbox") {
+ return true;
+ } else {
+ if (self.beforeSelectionChange(self)) {
+ self.continueSelection(event);
+ return self.afterSelectionChange();
+ }
+ }
+ return false;
+ };
+ self.rowIndex = 0;
+ self.offsetTop = 0;
+ self.rowDisplayIndex = 0;
+ self.alternatingRowClass = function () {
+ var isEven = (self.rowIndex % 2) === 0;
+ var classes = {
+ 'selected': self.selected,
+ 'ui-state-default': self.jqueryUITheme && isEven,
+ 'ui-state-active': self.jqueryUITheme && !isEven,
+ 'even': isEven,
+ 'odd': !isEven
+ };
+ return classes;
+ };
+ self.beforeSelectionChange = config.beforeSelectionChangeCallback;
+ self.afterSelectionChange = config.afterSelectionChangeCallback;
+ self.propertyCache = {};
+ self.getProperty = function(path) {
+ return self.propertyCache[path] || ng.utils.evalProperty(self.entity, path);
+ };
+ //selectify the entity
+ if (self.entity[SELECTED_PROP] === undefined) {
+ self.entity[SELECTED_PROP] = false;
+ } else if (self.entity[SELECTED_PROP]) {
+ // or else maintain the selection set by the entity.
+ self.selectionService.setSelection(self, self.entity[SELECTED_PROP]);
+ }
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\searchProvider.js
+ ***********************************************/
+ ng.SearchProvider = function($scope, grid, $filter) {
+ var self = this,
+ searchConditions = [];
+ self.extFilter = grid.config.filterOptions.useExternalFilter;
+ $scope.showFilter = grid.config.showFilter;
+ $scope.filterText = grid.config.filterOptions.filterText;
+
+ self.fieldMap = {};
+
+ self.evalFilter = function() {
+ if (searchConditions.length === 0) {
+ grid.filteredData = grid.sortedData;
+ } else {
+ grid.filteredData = grid.sortedData.filter(function(item) {
+ for (var i = 0, len = searchConditions.length; i < len; i++) {
+ var condition = searchConditions[i];
+ //Search entire row
+ if (!condition.column) {
+ for (var prop in item) {
+ if (item.hasOwnProperty(prop)) {
+ if (prop == SELECTED_PROP) {
+ continue;
+ }
+ var c = self.fieldMap[prop];
+ var f = (c && c.cellFilter) ? $filter(c.cellFilter) : null;
+ var pVal = item[prop];
+ if (pVal && (condition.regex.test(pVal.toString()) || (f && condition.regex.test(f(pVal).toString())))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ //Search by column.
+ var col = self.fieldMap[condition.columnDisplay];
+ if (!col) {
+ return false;
+ }
+ var filter = col.cellFilter ? $filter(col.cellFilter) : null;
+ var value = item[condition.column] || item[col.field];
+ if ((!value || !condition.regex.test(value.toString())) && !(typeof filter == "function" && condition.regex.test(filter(value)))) {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+ grid.rowFactory.filteredDataChanged();
+ };
+ var getRegExp = function(str, modifiers) {
+ try {
+ return new RegExp(str, modifiers);
+ } catch(err) {
+ //Escape all RegExp metacharacters.
+ return new RegExp(str.replace(/(\^|\$|\(|\)|\<|\>|\[|\]|\{|\}|\\|\||\.|\*|\+|\?)/g, '\\$1'));
+ }
+ };
+ var buildSearchConditions = function(a) {
+ //reset.
+ searchConditions = [];
+ var qStr = '';
+ if (!(qStr = $.trim(a))) {
+ return;
+ }
+ var columnFilters = qStr.split(";");
+ $.each(columnFilters, function(i, filter) {
+ var args = filter.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('filterText', function(a) {
+ if (!self.extFilter) {
+ buildSearchConditions(a);
+ self.evalFilter();
+ }
+ });
+ if (!self.extFilter) {
+ $scope.$watch('columns', function(a) {
+ angular.forEach(a, function(col) {
+ self.fieldMap[col.field] = col;
+ self.fieldMap[col.displayName.toLowerCase().replace(/\s+/g, '')] = col;
+ });
+ });
+ }
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\selectionService.js
+ ***********************************************/
+ ng.SelectionService = function(grid) {
+ var self = this;
+ self.multi = grid.config.multiSelect;
+ self.selectedItems = grid.config.selectedItems;
+ self.selectedIndex = grid.config.selectedIndex;
+ self.lastClickedRow = undefined;
+ self.ignoreSelectedItemChanges = false; // flag to prevent circular event loops keeping single-select var in sync
+
+ self.rowFactory = {};
+ self.Initialize = function(rowFactory) {
+ self.rowFactory = rowFactory;
+ };
+
+ // function to manage the selection action of a data item (entity)
+ self.ChangeSelection = function(rowItem, evt) {
+ if (evt && evt.shiftKey && self.multi) {
+ if (self.lastClickedRow) {
+ var thisIndx = grid.filteredData.indexOf(rowItem.entity);
+ var prevIndx = grid.filteredData.indexOf(self.lastClickedRow.entity);
+ if (thisIndx == prevIndx) {
+ return false;
+ }
+ prevIndx++;
+ if (thisIndx < prevIndx) {
+ thisIndx = thisIndx ^ prevIndx;
+ prevIndx = thisIndx ^ prevIndx;
+ thisIndx = thisIndx ^ prevIndx;
+ }
+ var rows = [];
+ for (; prevIndx <= thisIndx; prevIndx++) {
+ rows.push(self.rowFactory.rowCache[prevIndx]);
+ }
+ if (rows[rows.length - 1].beforeSelectionChange(rows, evt)) {
+ $.each(rows, function(i, ri) {
+ ri.selected = true;
+ ri.entity[SELECTED_PROP] = true;
+ if (self.selectedItems.indexOf(ri.entity) === -1) {
+ self.selectedItems.push(ri.entity);
+ }
+ });
+ rows[rows.length - 1].afterSelectionChange(rows, evt);
+ }
+ self.lastClickedRow = rows[rows.length - 1];
+ return true;
+ }
+ } else if (!self.multi) {
+ if (self.lastClickedRow && self.lastClickedRow != rowItem) {
+ self.setSelection(self.lastClickedRow, false);
+ }
+ self.setSelection(rowItem, grid.config.keepLastSelected ? true : !rowItem.selected);
+ } else {
+ self.setSelection(rowItem, !rowItem.selected);
+ }
+ self.lastClickedRow = rowItem;
+ return true;
+ };
+
+ // just call this func and hand it the rowItem you want to select (or de-select)
+ self.setSelection = function(rowItem, isSelected) {
+ rowItem.selected = isSelected;
+ rowItem.entity[SELECTED_PROP] = isSelected;
+ if (!isSelected) {
+ var indx = self.selectedItems.indexOf(rowItem.entity);
+ self.selectedItems.splice(indx, 1);
+ } else {
+ if (self.selectedItems.indexOf(rowItem.entity) === -1) {
+ self.selectedItems.push(rowItem.entity);
+ }
+ }
+ };
+
+ // @return - boolean indicating if all items are selected or not
+ // @val - boolean indicating whether to select all/de-select all
+ self.toggleSelectAll = function (checkAll) {
+ angular.forEach(grid.filteredData, function(item, i) {
+ grid.rowFactory.buildEntityRow(item, i);
+ });
+ if (grid.config.beforeSelectionChange(grid.rowFactory.rowCache)) {
+ var selectedlength = self.selectedItems.length;
+ if (selectedlength > 0) {
+ self.selectedItems.splice(0, selectedlength);
+ }
+ angular.forEach(grid.filteredData, function (item) {
+ item[SELECTED_PROP] = checkAll;
+ if (checkAll) {
+ self.selectedItems.push(item);
+ }
+ });
+ angular.forEach(self.rowFactory.rowCache, function (row) {
+ row.selected = checkAll;
+ });
+ grid.config.afterSelectionChange(grid.rowFactory.rowCache);
+ }
+ };
+ };
+
+ /***********************************************
+ * FILE: ..\src\classes\styleProvider.js
+ ***********************************************/
+ ng.StyleProvider = 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 - domUtilityService.ScrollW) + "px", "height": grid.config.headerRowHeight + "px" };
+ };
+ $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" };
+ };
+ };
+
+ /***********************************************
+ * FILE: ..\src\directives\ng-grid.js
+ ***********************************************/
+ ngGridDirectives.directive('ngGrid', ['$compile', '$filter', 'SortService', 'DomUtilityService', function($compile, $filter, sortService, domUtilityService) {
+ var ngGrid = {
+ scope: true,
+ compile: function() {
+ return {
+ pre: function($scope, iElement, iAttrs) {
+ var $element = $(iElement);
+ var options = $scope.$eval(iAttrs.ngGrid);
+ options.gridDim = new ng.Dimension({ outerHeight: $($element).height(), outerWidth: $($element).width() });
+ var grid = new ng.Grid($scope, options, sortService, domUtilityService, $filter);
+ // if columndefs are a string of a property ont he scope watch for changes and rebuild columns.
+ if (typeof options.columnDefs == "string") {
+ $scope.$parent.$watch(options.columnDefs, function(a) {
+ $scope.columns = [];
+ grid.config.columnDefs = a;
+ grid.buildColumns();
+ grid.configureColumnWidths();
+ domUtilityService.BuildStyles($scope, grid);
+ grid.eventProvider.assignEvents();
+ });
+ }
+
+ // if it is a string we can watch for data changes. otherwise you won't be able to update the grid data
+ if (typeof options.data == "string") {
+ var prevlength = 0;
+ var dataWatcher = function (a) {
+ prevlength = a ? a.length:0;
+ grid.sortedData = $scope.$eval(options.data) || [];
+ grid.searchProvider.evalFilter();
+ grid.configureColumnWidths();
+ grid.refreshDomSizes();
+ if (grid.config.sortInfo) {
+ if (!grid.config.sortInfo.column) {
+ grid.config.sortInfo.column = $scope.columns.filter(function(c) {
+ return c.field == grid.config.sortInfo.field;
+ })[0];
+ if (!grid.config.sortInfo.column) {
+ return;
+ }
+ }
+ grid.config.sortInfo.column.sortDirection = grid.config.sortInfo.direction.toLowerCase();
+ grid.sortData(grid.config.sortInfo.column);
+ }
+ };
+ $scope.$parent.$watch(options.data, dataWatcher);
+ $scope.$parent.$watch(options.data + '.length', function(a) {
+ if (a != prevlength) {
+ dataWatcher($scope.$eval(options.data));
+ }
+ });
+ }
+ var htmlText = ng.defaultGridTemplate(grid.config);
+ grid.footerController = new ng.Footer($scope, grid);
+ //set the right styling on the container
+ iElement.addClass("ngGrid").addClass(grid.gridId.toString());
+ if (options.jqueryUITheme) {
+ iElement.addClass('ui-widget');
+ }
+ iElement.append($compile(htmlText)($scope)); // make sure that if any of these change, we re-fire the calc logic
+ //walk the element's graph and the correct properties on the grid
+ domUtilityService.AssignGridContainers(iElement, grid);
+ grid.configureColumnWidths();
+ //now use the manager to assign the event handlers
+ grid.eventProvider = new ng.EventProvider(grid, $scope, domUtilityService);
+ //initialize plugins.
+ angular.forEach(options.plugins, function(p) {
+ p.init($scope.$new(), grid, { SortService: sortService, DomUtilityService: domUtilityService });
+ });
+ return null;
+ }
+ };
+ }
+ };
+ return ngGrid;
+ }]);
+
+ /***********************************************
+ * FILE: ..\src\directives\ng-row.js
+ ***********************************************/
+ ngGridDirectives.directive('ngRow', ['$compile', function($compile) {
+ var ngRow = {
+ scope: false,
+ compile: function() {
+ return {
+ pre: function($scope, iElement) {
+ if ($scope.row.isAggRow) {
+ var html = ng.aggregateTemplate();
+ 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($scope.rowTemplate)($scope));
+ }
+ }
+ };
+ }
+ };
+ return ngRow;
+ }]);
+
+ /***********************************************
+ * FILE: ..\src\directives\ng-cell.js
+ ***********************************************/
+ ngGridDirectives.directive('ngCell', ['$compile', function($compile) {
+ var ngCell = {
+ scope: false,
+ compile: function() {
+ return {
+ pre: function($scope, iElement) {
+ iElement.append($compile($scope.col.cellTemplate)($scope));
+ }
+ };
+ }
+ };
+ return ngCell;
+ }]);
+
+ /***********************************************
+ * FILE: ..\src\directives\ng-header-row.js
+ ***********************************************/
+ ngGridDirectives.directive('ngHeaderRow', ['$compile', function($compile) {
+ var ngHeaderRow = {
+ scope: false,
+ compile: function() {
+ return {
+ pre: function($scope, iElement) {
+ if (iElement.children().length === 0) {
+ iElement.append($compile($scope.headerRowTemplate)($scope));
+ }
+ }
+ };
+ }
+ };
+ return ngHeaderRow;
+ }]);
+
+ /***********************************************
+ * FILE: ..\src\directives\ng-header-cell.js
+ ***********************************************/
+ 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;
+ }]);
+
+ /***********************************************
+ * FILE: ..\src\init.js
+ ***********************************************/
+// initialization of services into the main module
+ angular.module('ngGrid', ['ngGrid.services', 'ngGrid.directives', 'ngGrid.filters']);
+
+ /***********************************************
+ * LANGUAGE: en.js
+ ***********************************************/
+ 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));
diff --git a/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid-1.6.0.js b/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid-1.6.0.js
new file mode 100644
index 0000000..d833b6f
--- /dev/null
+++ b/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid-1.6.0.js
@@ -0,0 +1,133 @@
+/***********************************************
+ * 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: 12/27/2012 16:39:37
+ ***********************************************/
+(function(window){'use strict';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',[]);var SELECTED_PROP='__ng_selected__';var EXCESS_ROWS=8;var SCROLL_THRESHOLD=6;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 TEMPLATE_REGEXP=/<.+>/;ng.moveSelectionHandler=function($scope,grid,evt){if(grid===null||grid===undefined){return true;}
+ if(grid.config.selectedItems===undefined){return true;}
+ var charCode=evt.which||evt.keyCode;var offset=(charCode==38?-1:(charCode==40?1:null));if(!offset){return true;}
+ var items=$scope.renderedRows;var index=items.indexOf(grid.selectionService.lastClickedRow)+offset;if(index<0||index>=items.length){return true;}
+ grid.selectionService.ChangeSelection(items[index],evt);if(index>items.length-EXCESS_ROWS){grid.$viewport.scrollTop(grid.$viewport.scrollTop()+(grid.config.rowHeight*2));}else if(index<EXCESS_ROWS){grid.$viewport.scrollTop(grid.$viewport.scrollTop()-(grid.config.rowHeight*2));}
+ if(!$scope.$$phase){$scope.$parent.$digest();}
+ 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('ngColumns',function(){return function(input){return input.filter(function(col){return!col.isAggCol;});};});ngGridFilters.filter('checkmark',function(){return function(input){return input?'\u2714':'\u2718';};});ngGridServices.factory('SortService',function(){var sortService={};sortService.colSortFnCache={};sortService.dateRE=/^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;sortService.guessSortFn=function(item){var sortFn,itemType,dateParts,month,day;if(item===undefined||item===null||item===''){return null;}
+ itemType=typeof(item);switch(itemType){case"number":sortFn=sortService.sortNumber;break;case"boolean":sortFn=sortService.sortBool;break;default:sortFn=undefined;break;}
+ if(sortFn){return sortFn;}
+ if(Object.prototype.toString.call(item)==='[object Date]'){return sortService.sortDate;}
+ if(itemType!=="string"){return sortService.basicSort;}
+ if(item.match(/^-?[�$�]?[\d,.]+%?$/)){return sortService.sortNumberStr;}
+ dateParts=item.match(sortService.dateRE);if(dateParts){month=parseInt(dateParts[1],10);day=parseInt(dateParts[2],10);if(month>12){return sortService.sortDDMMStr;}else if(day>12){return sortService.sortMMDDStr;}else{return sortService.sortMMDDStr;}}
+ return sortService.sortAlpha;};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.sortDDMMStr=function(a,b){var dateA,dateB,mtch,m,d,y;mtch=a.match(sortService.dateRE);y=mtch[3];m=mtch[2];d=mtch[1];if(m.length==1){m='0'+m;}
+ if(d.length==1){d='0'+d;}
+ dateA=y+m+d;mtch=b.match(sortService.dateRE);y=mtch[3];m=mtch[2];d=mtch[1];if(m.length==1){m='0'+m;}
+ if(d.length==1){d='0'+d;}
+ dateB=y+m+d;if(dateA==dateB){return 0;}
+ if(dateA<dateB){return-1;}
+ return 1;};sortService.sortMMDDStr=function(a,b){var dateA,dateB,mtch,m,d,y;mtch=a.match(sortService.dateRE);y=mtch[3];d=mtch[2];m=mtch[1];if(m.length==1){m='0'+m;}
+ if(d.length==1){d='0'+d;}
+ dateA=y+m+d;mtch=b.match(sortService.dateRE);y=mtch[3];d=mtch[2];m=mtch[1];if(m.length==1){m='0'+m;}
+ if(d.length==1){d='0'+d;}
+ dateB=y+m+d;if(dateA==dateB){return 0;}
+ if(dateA<dateB){return-1;}
+ return 1;};sortService.sortData=function(data,sortInfo){if(!data||!sortInfo){return;}
+ var col=sortInfo.column,direction=sortInfo.direction,sortFn,item;if(sortService.colSortFnCache[col.field]){sortFn=sortService.colSortFnCache[col.field];}else if(col.sortingAlgorithm!=undefined){sortFn=col.sortingAlgorithm;sortService.colSortFnCache[col.field]=col.sortingAlgorithm;}else{item=data[0];if(!item){return;}
+ sortFn=sortService.guessSortFn(item[col.field]);if(sortFn){sortService.colSortFnCache[col.field]=sortFn;}else{sortFn=sortService.sortAlpha;}}
+ data.sort(function(itemA,itemB){var propA=ng.utils.evalProperty(itemA,col.field);var propB=ng.utils.evalProperty(itemB,col.field);if(!propB&&!propA){return 0;}else if(!propA){return 1;}else if(!propB){return-1;}
+ if(direction===ASC){return sortFn(propA,propB);}else{return 0-sortFn(propA,propB);}});return;};sortService.Sort=function(sortInfo,data){if(sortService.isSorting){return;}
+ sortService.isSorting=true;sortService.sortData(data,sortInfo);sortService.isSorting=false;};return sortService;});ngGridServices.factory('DomUtilityService',function(){var domUtilityService={};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(rootEl,grid){grid.$root=$(rootEl);grid.$topPanel=grid.$root.find(".ngTopPanel");grid.$groupPanel=grid.$root.find(".ngGroupPanel");grid.$headerContainer=grid.$topPanel.find(".ngHeaderContainer");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");domUtilityService.UpdateGridLayout(grid);};domUtilityService.UpdateGridLayout=function(grid){var scrollTop=grid.$viewport.scrollTop();grid.elementDims.rootMaxW=grid.$root.width();grid.elementDims.rootMaxH=grid.$root.height();grid.refreshDomSizes();grid.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.visibleColumns(),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}";angular.forEach(cols,function(col,i){css+="."+gridId+" .col"+i+" { width: "+col.width+"px; left: "+sumWidth+"px; right: "+(trw-sumWidth-col.width)+"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){domUtilityService.digest($scope);}};domUtilityService.digest=function($scope){if(!$scope.$$phase){$scope.$digest();}};domUtilityService.ScrollH=17;domUtilityService.ScrollW=17;domUtilityService.LetterW=10;getWidths();return domUtilityService;});ng.defaultGridTemplate=function(){return'<div class="ngTopPanel" ng-class="{\'ui-widget-header\':jqueryUITheme, \'ui-corner-top\': jqueryUITheme}" ng-style="topPanelStyle()"><div class="ngGroupPanel" ng-show="showGroupPanel()" ng-style="headerStyle()"><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-header-row></div></div><div class="ngHeaderButton" ng-show="showColumnMenu || showFilter" ng-click="toggleShowMenu()"><div class="ngHeaderButtonArrow" ng-click=""></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 type="checkbox" class="ngColListCheckbox" ng-model="col.visible"/>{{col.displayName}}</label><a title="Group By" ng-class="col.groupedByClass()" ng-show="col.groupable" ng-click="groupBy(col)"></a><span class="ngGroupingNumber" ng-show="col.groupIndex > 0">{{col.groupIndex}}</span></li></ul></div></div></div><div class="ngViewport" 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)" class="ngRow" ng-class="row.alternatingRowClass()" ng-row></div></div></div><div class="ngFooterPanel" ng-class="{\'ui-widget-content\': jqueryUITheme, \'ui-corner-bottom\': jqueryUITheme}" ng-style="footerStyle()"><div class="ngTotalSelectContainer" ng-show="footerVisible"><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="footerVisible && 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="text" style="width:50px; height: 24px; margin-top: 1px; padding: 0px 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>';};ng.defaultRowTemplate=function(){return'<div ng-style="{\'cursor\': row.cursor}" ng-repeat="col in visibleColumns()" class="ngCell col{{$index}} {{col.cellClass}}" ng-cell></div>';};ng.defaultCellTemplate=function(){return'<div class="ngCellText colt{{$index}}">{{row.getProperty(col.field) CUSTOM_FILTERS}}</div>';};ng.aggregateTemplate=function(){return'<div ng-click="row.toggleExpand()" ng-style="{\'left\': row.offsetleft}" class="ngAggregate"><span class="ngAggregateText">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span><div class="{{row.aggClass()}}"></div></div>';};ng.defaultHeaderRowTemplate=function(){return'<div ng-repeat="col in visibleColumns()" class="ngHeaderCell col{{$index}}" ng-header-cell></div>';};ng.defaultHeaderCellTemplate=function(){return'<div ng-click="col.sort()" class="ngHeaderSortColumn {{col.headerClass}}" ng-style="{\'cursor\': col.cursor}" ng-class="{ \'ngSorted\': !noSortVisible }"><div class="ngHeaderText colt{{$index}}">{{col.displayName}}</div><div class="ngSortButtonDown" ng-show="col.showSortButtonDown()"></div><div class="ngSortButtonUp" ng-show="col.showSortButtonUp()"></div></div><div ng-show="col.resizable" class="ngHeaderGrip" ng-click="col.gripClick($event)" ng-mousedown="col.gripOnMouseDown($event)"></div>';};ng.Aggregate=function(aggEntity,rowFactory){var self=this;self.index=0;self.offsetTop=0;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;self.notifyChildren();};self.setExpand=function(state){self.collapsed=state;self.notifyChildren();};self.notifyChildren=function(){angular.forEach(self.aggChildren,function(child){child.entity[NG_HIDDEN]=self.collapsed;if(self.collapsed){child.setExpand(self.collapsed);}});angular.forEach(self.children,function(child){child[NG_HIDDEN]=self.collapsed;});rowFactory.rowCache=[];var foundMyself=false;angular.forEach(rowFactory.aggCache,function(agg,i){if(foundMyself){var offset=(30*self.children.length);agg.offsetTop=self.collapsed?agg.offsetTop-offset:agg.offsetTop+offset;}else{if(i==self.aggIndex){foundMyself=true;}}});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;}};};ng.EventProvider=function(grid,$scope,domUtilityService){var self=this;self.colToMove=undefined;self.groupToMove=undefined;self.assignEvents=function(){if(grid.config.jqueryUIDraggable){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.$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.dragOver=function(evt){evt.preventDefault();};self.setDraggables=function(){if(!grid.config.jqueryUIDraggable){grid.$root.find('.ngHeaderSortColumn').attr('draggable','true');}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');}
+ 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'){$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;}
+ var i=grid.sortedData.indexOf(prevRow.scope.row.entity);var j=grid.sortedData.indexOf(rowScope.row.entity);grid.sortedData.splice(i,1);grid.sortedData.splice(j,0,prevRow.scope.row.entity);grid.searchProvider.evalFilter();domUtilityService.eventStorage.rowToMove=undefined;}};self.assignGridEventHandlers=function(){grid.$viewport.on('scroll',function(e){var scrollLeft=e.target.scrollLeft,scrollTop=e.target.scrollTop;grid.adjustScrollLeft(scrollLeft);grid.adjustScrollTop(scrollTop);});var doingKeyDown=false;grid.$viewport.on('keydown',function(e){if(!doingKeyDown){doingKeyDown=true;var ret=ng.moveSelectionHandler($scope,grid,e);doingKeyDown=false;return ret;}
+ return false;});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.UpdateGridLayout(grid);if(grid.config.maintainColumnRatios){grid.configureColumnWidths();}});};self.assignGridEventHandlers();self.assignEvents();};ng.Column=function(config,$scope,grid,domUtilityService){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.headerRowHeight=config.headerRowHeight;self.displayName=colDef.displayName||colDef.field;self.index=config.index;self.isAggCol=config.isAggCol;self.cellClass=colDef.cellClass;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.groupable=ng.utils.isNullOrUndefined(colDef.groupable)||colDef.sortable;if(config.enableSort){self.sortable=ng.utils.isNullOrUndefined(colDef.sortable)||colDef.sortable;}
+ if(config.enableResize){self.resizable=ng.utils.isNullOrUndefined(colDef.resizable)||colDef.resizable;}
+ self.sortDirection=undefined;self.sortingAlgorithm=colDef.sortFn;self.headerClass=colDef.headerClass;self.headerCellTemplate=colDef.headerCellTemplate||ng.defaultHeaderCellTemplate();self.cursor=self.sortable?'pointer':'default';self.cellTemplate=colDef.cellTemplate||ng.defaultCellTemplate().replace(CUSTOM_FILTERS,self.cellFilter?"|"+self.cellFilter:"");if(colDef.cellTemplate&&!TEMPLATE_REGEXP.test(colDef.cellTemplate)){self.cellTemplate=$.ajax({type:"GET",url:colDef.cellTemplate,async:false}).responseText;}
+ if(colDef.headerCellTemplate&&!TEMPLATE_REGEXP.test(colDef.headerCellTemplate)){self.headerCellTemplate=$.ajax({type:"GET",url:colDef.headerCellTemplate,async:false}).responseText;}
+ 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(){if(!self.sortable){return true;}
+ var dir=self.sortDirection===ASC?DESC:ASC;self.sortDirection=dir;config.sortCallback(self);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.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(){$(document).off('mousemove');$(document).off('mouseup');event.target.parentElement.style.cursor='default';domUtilityService.digest($scope);return false;};};ng.Dimension=function(options){this.outerHeight=null;this.outerWidth=null;$.extend(this,options);};ng.Footer=function($scope,grid){$scope.maxRows=function(){var ret=Math.max($scope.pagingOptions.totalServerItems,grid.sortedData.length);return ret;};$scope.multiSelect=(grid.config.canSelectRows&&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.sortedData.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.RowFactory=function(grid,$scope){var self=this;self.rowCache=[];self.aggCache={};self.parentCache=[];self.dataChanged=true;self.parsedData=[];self.rowConfig={};self.selectionService=grid.selectionService;self.rowHeight=30;self.numberOfAggregates=0;self.groupedData=undefined;self.rowHeight=grid.config.rowHeight;self.rowConfig={canSelectRows:grid.config.canSelectRows,rowClasses:grid.config.rowClasses,selectedItems:grid.config.selectedItems,selectWithCheckboxOnly:grid.config.selectWithCheckboxOnly,beforeSelectionChangeCallback:grid.config.beforeSelectionChange,afterSelectionChangeCallback:grid.config.afterSelectionChange,jqueryUITheme:grid.config.jqueryUITheme};self.renderedRange=new ng.Range(0,grid.minRowsToRender()+EXCESS_ROWS);self.buildEntityRow=function(entity,rowIndex){var row=self.rowCache[rowIndex];if(!row){row=new ng.Row(entity,self.rowConfig,self.selectionService);row.rowIndex=rowIndex+1;row.offsetTop=self.rowHeight*rowIndex;row.selected=entity[SELECTED_PROP];self.rowCache[rowIndex]=row;}
+ return row;};self.buildAggregateRow=function(aggEntity,rowIndex){var agg=self.aggCache[aggEntity.aggIndex];if(!agg){agg=new ng.Aggregate(aggEntity,self);self.aggCache[aggEntity.aggIndex]=agg;}
+ agg.index=rowIndex+1;agg.offsetTop=self.rowHeight*rowIndex;return agg;};self.UpdateViewableRange=function(newRange){self.renderedRange=newRange;self.renderedChange();};self.filteredDataChanged=function(){if(grid.lateBoundColumns&&grid.filteredData.length>1){grid.config.columnDefs=undefined;grid.buildColumns();grid.lateBoundColumns=false;}
+ self.dataChanged=true;self.rowCache=[];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.parentCache=[];var rowArr=[];var dataArray=self.parsedData.filter(function(e){return e[NG_HIDDEN]===false;}).slice(self.renderedRange.topRow,self.renderedRange.bottomRow);angular.forEach(dataArray,function(item,indx){var row;if(item.isAggRow){row=self.buildAggregateRow(item,self.renderedRange.topRow+indx);}else{row=self.buildEntityRow(item,self.renderedRange.topRow+indx);}
+ rowArr.push(row);});grid.setRenderedRows(rowArr);};self.renderedChangeNoGroups=function(){var rowArr=[];var dataArr=grid.filteredData.slice(self.renderedRange.topRow,self.renderedRange.bottomRow);angular.forEach(dataArr,function(item,i){var row=self.buildEntityRow(item,self.renderedRange.topRow+i);rowArr.push(row);});grid.setRenderedRows(rowArr);};self.parseGroupData=function(g){if(g.values){angular.forEach(g.values,function(item){self.parentCache[self.parentCache.length-1].children.push(item);self.parsedData.push(item);});}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.entity);self.parentCache[agg.depth]=agg;self.parseGroupData(g[prop]);}}}};self.getGrouping=function(groups){self.aggCache=[];self.rowCache=[];self.numberOfAggregates=0;self.groupedData={};var data=grid.filteredData;var maxDepth=groups.length;var cols=$scope.columns;angular.forEach(data,function(item){item[NG_HIDDEN]=true;var ptr=self.groupedData;angular.forEach(groups,function(group,depth){if(!cols[depth].isAggCol&&depth<=maxDepth){cols.splice(item.gDepth,0,new ng.Column({colDef:{field:'',width:25,sortable:false,resizable:false,headerCellTemplate:'<div class="ngAggHeader"></div>'},isAggCol:true,index:item.gDepth,headerRowHeight:grid.config.headerRowHeight}));}
+ var col=cols.filter(function(c){return c.field==group;})[0];var val=ng.utils.evalProperty(item,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]=depth;}
+ if(!ptr[NG_COLUMN]){ptr[NG_COLUMN]=col;}
+ ptr=ptr[val];});if(!ptr.values){ptr.values=[];}
+ ptr.values.push(item);});grid.fixColumnIndexes();self.parsedData.length=0;self.parseGroupData(self.groupedData);};if(grid.config.groups.length>0&&grid.filteredData.length>0){self.getGrouping(grid.config.groups);}};ng.Grid=function($scope,options,sortService,domUtilityService,$filter){var defaults={afterSelectionChange:function(){},beforeSelectionChange:function(){return true;},canSelectRows:true,columnDefs:undefined,data:[],displaySelectionCheckbox:true,enableColumnResize:true,enableColumnReordering:true,enablePaging:false,enableRowReordering:true,enableSorting:true,filterOptions:{filterText:"",useExternalFilter:false},footerRowHeight:55,displayFooter:undefined,footerVisible:true,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},plugins:[],rowHeight:30,rowTemplate:undefined,selectedItems:[],selectWithCheckboxOnly:false,showColumnMenu:true,showFilter:true,showGroupPanel:false,sortInfo:undefined,tabIndex:-1,useExternalSorting:false,i18n:'en'},self=this;self.maxCanvasHt=0;self.config=$.extend(defaults,options);if(typeof options.columnDefs=="string"){self.config.columnDefs=$scope.$eval(options.columnDefs);}
+ 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.sortedData=[];self.lateBindColumns=false;self.filteredData=[];if(typeof self.config.data=="object"){self.sortedData=self.config.data;}
+ self.lastSortedColumn=undefined;self.calcMaxCanvasHeight=function(){return(self.config.groups.length>0)?(self.rowFactory.parsedData.filter(function(e){return e[NG_HIDDEN]===false;}).length*self.config.rowHeight):(self.filteredData.length*self.config.rowHeight);};self.elementDims={scrollW:0,scrollH:0,rowIndexCellW:25,rowSelectedCellW:25,rootMaxW:0,rootMaxH:0};self.setRenderedRows=function(newRows){$scope.renderedRows=newRows;if(!$scope.$$phase){$scope.$digest();}
+ 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(){if(!self.config.columnDefs){self.config.columnDefs=[];}
+ if(!self.sortedData||!self.sortedData[0]){self.lateBoundColumns=true;return;}
+ var item;item=self.sortedData[0];ng.utils.forIn(item,function(prop,propName){if(propName!=SELECTED_PROP){self.config.columnDefs.push({field:propName});}});};self.buildColumns=function(){var columnDefs=self.config.columnDefs,cols=[],indexOffset=0;if(!columnDefs){self.buildColumnDefsFromData();columnDefs=self.config.columnDefs;}
+ if(self.config.displaySelectionCheckbox){indexOffset=1;cols.push(new ng.Column({colDef:{field:'\u2714',width:self.elementDims.rowSelectedCellW,sortable:false,resizable:false,groupable:false,headerCellTemplate:'<input class="ngSelectionHeader" type="checkbox" ng-show="multiSelect" ng-model="allSelected" ng-change="toggleSelectAll(allSelected)"/>',cellTemplate:'<div class="ngSelectionCell"><input class="ngSelectionCheckbox" type="checkbox" ng-checked="row.selected" /></div>'},index:0,headerRowHeight:self.config.headerRowHeight,sortCallback:self.sortData,resizeOnDataCallback:self.resizeOnData,enableResize:self.config.enableColumnResize,enableSort:self.config.enableSorting},$scope,self,domUtilityService,$filter));}
+ if(columnDefs.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},$scope,self,domUtilityService);cols.push(column);var indx=self.config.groups.indexOf(colDef.field);if(indx!=-1){$scope.configGroups.splice(indx,0,column);}});$scope.columns=cols;}};self.configureColumnWidths=function(){var cols=self.config.columnDefs;var indexOffset=self.config.displaySelectionCheckbox?$scope.configGroups.length+1:$scope.configGroups.length;var numOfCols=cols.length+indexOffset,asterisksArray=[],percentArray=[],asteriskNum=0,totalWidth=0;totalWidth+=self.config.displaySelectionCheckbox?25:0;angular.forEach(cols,function(col,i){i+=indexOffset;var isPercent=false,t=undefined;if(ng.utils.isNullOrUndefined(col.width)){col.width="*";}else{isPercent=isNaN(col.width)?ng.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];$scope.$evalAsync(function(){self.resizeOnData(temp,true);});return;}else if(t.indexOf("*")!=-1){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{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;}
+ 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(){self.selectionService=new ng.SelectionService(self);self.rowFactory=new ng.RowFactory(self,$scope);self.selectionService.Initialize(self.rowFactory);self.searchProvider=new ng.SearchProvider($scope,self,$filter);self.styleProvider=new ng.StyleProvider($scope,self,domUtilityService);self.buildColumns();$scope.$watch('configGroups',function(a){var tempArr=[];angular.forEach(a,function(item){tempArr.push(item.field||item);});self.config.groups=tempArr;self.rowFactory.filteredDataChanged();$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){ng.utils.seti18n($scope,newLang);});self.maxCanvasHt=self.calcMaxCanvasHeight();if(self.config.sortInfo){self.config.sortInfo.column=$scope.columns.filter(function(c){return c.field==self.config.sortInfo.field;})[0];self.config.sortInfo.column.sortDirection=self.config.sortInfo.direction.toUpperCase();self.sortData(self.config.sortInfo.column);}};self.prevScrollTop=0;self.prevScrollIndex=0;self.adjustScrollTop=function(scrollTop,force){if(self.prevScrollTop===scrollTop&&!force){return;}
+ var rowIndex=Math.floor(scrollTop/self.config.rowHeight);if(self.prevScrollTop<scrollTop&&rowIndex<self.prevScrollIndex+SCROLL_THRESHOLD){return;}
+ if(self.prevScrollTop>scrollTop&&rowIndex>self.prevScrollIndex-SCROLL_THRESHOLD){return;}
+ self.prevScrollTop=scrollTop;self.rowFactory.UpdateViewableRange(new ng.Range(Math.max(0,rowIndex-EXCESS_ROWS),rowIndex+self.minRowsToRender()+EXCESS_ROWS));self.prevScrollIndex=rowIndex;};self.adjustScrollLeft=function(scrollLeft){if(self.$headerContainer){self.$headerContainer.scrollLeft(scrollLeft);}};self.resizeOnData=function(col){var longest=col.minWidth;var arr=ng.utils.getElementsByClassName('col'+col.index);angular.forEach(arr,function(elem,index){var i;if(index===0){var kgHeaderText=$(elem).find('.ngHeaderText');i=ng.utils.visualLength(kgHeaderText)+10;}else{var ngCellText=$(elem).find('.ngCellText');i=ng.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.sortData=function(col){self.config.sortInfo={column:col,field:col.field,direction:col.sortDirection};self.clearSortingData(col);if(!self.config.useExternalSorting){sortService.Sort(self.config.sortInfo,self.sortedData);}
+ self.lastSortedColumn=col;self.searchProvider.evalFilter();$scope.$emit('ngGridEventSorted',col);};self.clearSortingData=function(col){if(!col){angular.forEach($scope.columns,function(c){c.sortDirection="";});}else if(self.lastSortedColumn&&col!=self.lastSortedColumn){self.lastSortedColumn.sortDirection="";}};self.fixColumnIndexes=function(){angular.forEach($scope.columns,function(col,i){col.index=i;});};self.fixGroupIndexes=function(){angular.forEach($scope.configGroups,function(item,i){item.groupIndex=i+1;});};$scope.elementsNeedMeasuring=true;$scope.columns=[];$scope.renderedRows=[];$scope.headerRow=null;$scope.rowHeight=self.config.rowHeight;$scope.jqueryUITheme=self.config.jqueryUITheme;$scope.footer=null;$scope.selectedItems=self.config.selectedItems;$scope.multiSelect=self.config.multiSelect;$scope.footerVisible=ng.utils.isNullOrUndefined(self.config.displayFooter)?self.config.footerVisible:self.config.displayFooter;$scope.footerRowHeight=$scope.footerVisible?self.config.footerRowHeight:0;$scope.showColumnMenu=self.config.showColumnMenu;$scope.showMenu=false;$scope.configGroups=[];$scope.enablePaging=self.config.enablePaging;$scope.pagingOptions=self.config.pagingOptions;$scope.rowTemplate=self.config.rowTemplate||ng.defaultRowTemplate();$scope.headerRowTemplate=self.config.headerRowTemplate||ng.defaultHeaderRowTemplate();$scope.i18n={};ng.utils.seti18n($scope,self.config.i18n);if(self.config.rowTemplate&&!TEMPLATE_REGEXP.test(self.config.rowTemplate)){$scope.rowTemplate=$.ajax({type:"GET",url:self.config.rowTemplate,async:false}).responseText;}
+ if(self.config.headerRowTemplate&&!TEMPLATE_REGEXP.test(self.config.headerRowTemplate)){$scope.headerRowTemplate=$.ajax({type:"GET",url:self.config.headerRowTemplate,async:false}).responseText;}
+ $scope.visibleColumns=function(){return $scope.columns.filter(function(col){return col.visible;});};$scope.toggleShowMenu=function(){$scope.showMenu=!$scope.showMenu;};$scope.toggleSelectAll=function(a){self.selectionService.toggleSelectAll(a);};$scope.totalFilteredItemsLength=function(){return self.filteredData.length;};$scope.showGroupPanel=function(){return self.config.showGroupPanel;};$scope.topPanelHeight=function(){return self.config.showGroupPanel===true?self.config.headerRowHeight*2:self.config.headerRowHeight;};$scope.viewportDimHeight=function(){return Math.max(0,self.rootDim.outerHeight-$scope.topPanelHeight()-$scope.footerRowHeight-2);};$scope.groupBy=function(col){if(self.sortedData.length<1||!col.groupable){return;}
+ 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);}};$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.totalRowWidth=function(){var totalWidth=0,cols=$scope.visibleColumns();angular.forEach(cols,function(col){totalWidth+=col.width;});return totalWidth;};$scope.headerScrollerDim=function(){var viewportH=$scope.viewportDimHeight(),maxHeight=self.maxCanvasHt,vScrollBarIsOpen=(maxHeight>viewportH),newDim=new ng.Dimension();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();};ng.Range=function(top,bottom){this.topRow=top;this.bottomRow=bottom;};ng.Row=function(entity,config,selectionService){var self=this,canSelectRows=config.canSelectRows;self.jqueryUITheme=config.jqueryUITheme;self.rowClasses=config.rowClasses;self.entity=entity;self.selectionService=selectionService;self.selected=false;self.cursor=canSelectRows?'pointer':'default';self.continueSelection=function(event){self.selectionService.ChangeSelection(self,event);};self.toggleSelected=function(event){if(!canSelectRows){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"){return true;}else{if(self.beforeSelectionChange(self)){self.continueSelection(event);return self.afterSelectionChange();}}
+ return false;};self.rowIndex=0;self.offsetTop=0;self.rowDisplayIndex=0;self.alternatingRowClass=function(){var isEven=(self.rowIndex%2)===0;var classes={'selected':self.selected,'ui-state-default':self.jqueryUITheme&&isEven,'ui-state-active':self.jqueryUITheme&&!isEven,'even':isEven,'odd':!isEven};return classes;};self.beforeSelectionChange=config.beforeSelectionChangeCallback;self.afterSelectionChange=config.afterSelectionChangeCallback;self.propertyCache={};self.getProperty=function(path){return self.propertyCache[path]||ng.utils.evalProperty(self.entity,path);};if(self.entity[SELECTED_PROP]===undefined){self.entity[SELECTED_PROP]=false;}else if(self.entity[SELECTED_PROP]){self.selectionService.setSelection(self,self.entity[SELECTED_PROP]);}};ng.SearchProvider=function($scope,grid,$filter){var self=this,searchConditions=[];self.extFilter=grid.config.filterOptions.useExternalFilter;$scope.showFilter=grid.config.showFilter;$scope.filterText=grid.config.filterOptions.filterText;self.fieldMap={};self.evalFilter=function(){if(searchConditions.length===0){grid.filteredData=grid.sortedData;}else{grid.filteredData=grid.sortedData.filter(function(item){for(var i=0,len=searchConditions.length;i<len;i++){var condition=searchConditions[i];if(!condition.column){for(var prop in item){if(item.hasOwnProperty(prop)){if(prop==SELECTED_PROP){continue;}
+ var c=self.fieldMap[prop];var f=(c&&c.cellFilter)?$filter(c.cellFilter):null;var pVal=item[prop];if(pVal&&(condition.regex.test(pVal.toString())||(f&&condition.regex.test(f(pVal).toString())))){return true;}}}
+ return false;}
+ var col=self.fieldMap[condition.columnDisplay];if(!col){return false;}
+ var filter=col.cellFilter?$filter(col.cellFilter):null;var value=item[condition.column]||item[col.field];if((!value||!condition.regex.test(value.toString()))&&!(typeof filter=="function"&&condition.regex.test(filter(value)))){return false;}}
+ return true;});}
+ grid.rowFactory.filteredDataChanged();};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(";");$.each(columnFilters,function(i,filter){var args=filter.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('filterText',function(a){if(!self.extFilter){buildSearchConditions(a);self.evalFilter();}});if(!self.extFilter){$scope.$watch('columns',function(a){angular.forEach(a,function(col){self.fieldMap[col.field]=col;self.fieldMap[col.displayName.toLowerCase().replace(/\s+/g,'')]=col;});});}};ng.SelectionService=function(grid){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.rowFactory={};self.Initialize=function(rowFactory){self.rowFactory=rowFactory;};self.ChangeSelection=function(rowItem,evt){if(evt&&evt.shiftKey&&self.multi){if(self.lastClickedRow){var thisIndx=grid.filteredData.indexOf(rowItem.entity);var prevIndx=grid.filteredData.indexOf(self.lastClickedRow.entity);if(thisIndx==prevIndx){return false;}
+ prevIndx++;if(thisIndx<prevIndx){thisIndx=thisIndx^prevIndx;prevIndx=thisIndx^prevIndx;thisIndx=thisIndx^prevIndx;}
+ var rows=[];for(;prevIndx<=thisIndx;prevIndx++){rows.push(self.rowFactory.rowCache[prevIndx]);}
+ if(rows[rows.length-1].beforeSelectionChange(rows,evt)){$.each(rows,function(i,ri){ri.selected=true;ri.entity[SELECTED_PROP]=true;if(self.selectedItems.indexOf(ri.entity)===-1){self.selectedItems.push(ri.entity);}});rows[rows.length-1].afterSelectionChange(rows,evt);}
+ self.lastClickedRow=rows[rows.length-1];return true;}}else if(!self.multi){if(self.lastClickedRow&&self.lastClickedRow!=rowItem){self.setSelection(self.lastClickedRow,false);}
+ self.setSelection(rowItem,grid.config.keepLastSelected?true:!rowItem.selected);}else{self.setSelection(rowItem,!rowItem.selected);}
+ self.lastClickedRow=rowItem;return true;};self.setSelection=function(rowItem,isSelected){rowItem.selected=isSelected;rowItem.entity[SELECTED_PROP]=isSelected;if(!isSelected){var indx=self.selectedItems.indexOf(rowItem.entity);self.selectedItems.splice(indx,1);}else{if(self.selectedItems.indexOf(rowItem.entity)===-1){self.selectedItems.push(rowItem.entity);}}};self.toggleSelectAll=function(checkAll){angular.forEach(grid.filteredData,function(item,i){grid.rowFactory.buildEntityRow(item,i);});if(grid.config.beforeSelectionChange(grid.rowFactory.rowCache)){var selectedlength=self.selectedItems.length;if(selectedlength>0){self.selectedItems.splice(0,selectedlength);}
+ angular.forEach(grid.filteredData,function(item){item[SELECTED_PROP]=checkAll;if(checkAll){self.selectedItems.push(item);}});angular.forEach(self.rowFactory.rowCache,function(row){row.selected=checkAll;});grid.config.afterSelectionChange(grid.rowFactory.rowCache);}};};ng.StyleProvider=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-domUtilityService.ScrollW)+"px","height":grid.config.headerRowHeight+"px"};};$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('ngGrid',['$compile','$filter','SortService','DomUtilityService',function($compile,$filter,sortService,domUtilityService){var ngGrid={scope:true,compile:function(){return{pre:function($scope,iElement,iAttrs){var $element=$(iElement);var options=$scope.$eval(iAttrs.ngGrid);options.gridDim=new ng.Dimension({outerHeight:$($element).height(),outerWidth:$($element).width()});var grid=new ng.Grid($scope,options,sortService,domUtilityService,$filter);if(typeof options.columnDefs=="string"){$scope.$parent.$watch(options.columnDefs,function(a){$scope.columns=[];grid.config.columnDefs=a;grid.buildColumns();grid.configureColumnWidths();domUtilityService.BuildStyles($scope,grid);grid.eventProvider.assignEvents();});}
+ if(typeof options.data=="string"){var prevlength=0;var dataWatcher=function(a){prevlength=a?a.length:0;grid.sortedData=$scope.$eval(options.data)||[];grid.searchProvider.evalFilter();grid.configureColumnWidths();grid.refreshDomSizes();if(grid.config.sortInfo){if(!grid.config.sortInfo.column){grid.config.sortInfo.column=$scope.columns.filter(function(c){return c.field==grid.config.sortInfo.field;})[0];if(!grid.config.sortInfo.column){return;}}
+ grid.config.sortInfo.column.sortDirection=grid.config.sortInfo.direction.toLowerCase();grid.sortData(grid.config.sortInfo.column);}};$scope.$parent.$watch(options.data,dataWatcher);$scope.$parent.$watch(options.data+'.length',function(a){if(a!=prevlength){dataWatcher($scope.$eval(options.data));}});}
+ var htmlText=ng.defaultGridTemplate(grid.config);grid.footerController=new ng.Footer($scope,grid);iElement.addClass("ngGrid").addClass(grid.gridId.toString());if(options.jqueryUITheme){iElement.addClass('ui-widget');}
+ iElement.append($compile(htmlText)($scope));domUtilityService.AssignGridContainers(iElement,grid);grid.configureColumnWidths();grid.eventProvider=new ng.EventProvider(grid,$scope,domUtilityService);angular.forEach(options.plugins,function(p){p.init($scope.$new(),grid,{SortService:sortService,DomUtilityService:domUtilityService});});return null;}};}};return ngGrid;}]);ngGridDirectives.directive('ngRow',['$compile',function($compile){var ngRow={scope:false,compile:function(){return{pre:function($scope,iElement){if($scope.row.isAggRow){var html=ng.aggregateTemplate();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($scope.rowTemplate)($scope));}}};}};return ngRow;}]);ngGridDirectives.directive('ngCell',['$compile',function($compile){var ngCell={scope:false,compile:function(){return{pre:function($scope,iElement){iElement.append($compile($scope.col.cellTemplate)($scope));}};}};return ngCell;}]);ngGridDirectives.directive('ngHeaderRow',['$compile',function($compile){var ngHeaderRow={scope:false,compile:function(){return{pre:function($scope,iElement){if(iElement.children().length===0){iElement.append($compile($scope.headerRowTemplate)($scope));}}};}};return ngHeaderRow;}]);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;}]);angular.module('ngGrid',['ngGrid.services','ngGrid.directives','ngGrid.filters']);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)); \ No newline at end of file
diff --git a/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid.css b/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid.css
new file mode 100644
index 0000000..a8ab555
--- /dev/null
+++ b/src/main/resources/webapp/external/angular-ui/ng-grid-1.6.0/ng-grid.css
@@ -0,0 +1,456 @@
+
+/******** Grid Global ********/
+.nglabel {
+ display: block;
+ float: left;
+ font-weight: bold;
+ padding-right: 5px;
+}
+.ngNoSelect{
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+/******** Grid ********/
+
+.ngGrid{
+ background-color: rgb(253, 253, 253);
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/******** Header ********/
+
+.ngGroupPanel{
+ background-color: rgb(234, 234, 234);
+ overflow: hidden;
+ border-bottom: 1px solid rgb(212,212,212);
+}
+
+.ngGroupPanelDescription{
+ margin-top: 5px;
+ margin-left: 5px;
+}
+
+.ngGroupList {
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+}
+
+.ngGroupItem {
+ float: left;
+}
+
+.ngGroupElement {
+ float: left;
+ height: 100%;
+ width: 100%;
+}
+
+.ngGroupName {
+ background-color: rgb(247,247,247);
+ border: 1px solid rgb(212,212,212);
+ padding: 3px 10px;
+ float: left;
+ margin-left: 0;
+ margin-top: 2px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ font-weight: bold;
+}
+
+.ngGroupItem:first-child{
+ margin-left: 2px;
+}
+
+.ngRemoveGroup {
+ width: 5px;
+ float: right;
+ -moz-opacity: 0.4;
+ opacity: 0.4;
+ margin-top: -1px;
+ margin-left: 5px;
+}
+.ngRemoveGroup:hover {
+ color: black;
+ text-decoration: none;
+ cursor: pointer;
+ -moz-opacity: 0.7;
+ opacity: 0.7;
+}
+.ngGroupArrow {
+ width: 0;
+ height: 0;
+ border-top: 6px solid transparent;
+ border-bottom: 6px solid transparent;
+ border-left: 6px solid black;
+ margin-top: 10px;
+ margin-left: 5px;
+ margin-right: 5px;
+ float: right;
+}
+
+.ngTopPanel {
+ position: relative;
+ z-index:5;
+ background-color: rgb(234, 234, 234);
+ border-bottom: 1px solid rgb(212,212,212);
+}
+.ngHeaderContainer {
+ position: relative;
+ overflow: hidden;
+ font-weight: bold;
+}
+
+.ngHeaderScroller {
+ position:absolute;
+}
+.ngHeaderSortColumn{
+ position:absolute;
+ overflow: hidden;
+}
+
+.ngHeaderCell{
+ border-left: 1px solid rgb(212,212,212);
+ position: absolute;
+ top: 0;
+ bottom: 0;
+}
+
+.ngHeaderCell:first-child{
+ border-left: 0;
+}
+
+.ngHeaderCell:last-child{
+ border-right: 1px solid rgb(212,212,212);
+}
+
+.ngSortButtonUp {
+ position: absolute;
+ top: 3px;
+ left: 0;
+ right: 0;
+ margin-left: auto;
+ margin-right: auto;
+ border-color: gray transparent;
+ border-style: solid;
+ border-width: 0 5px 5px 5px;
+ height: 0;
+ width: 0;
+}
+.ngSortButtonDown {
+ position: absolute;
+ top: 3px;
+ left: 0;
+ right: 0;
+ margin-left: auto;
+ margin-right: auto;
+ border-color: gray transparent;
+ border-style: solid;
+ border-width: 5px 5px 0 5px;
+ height: 0;
+ width: 0;
+}
+.ngHeaderGrip {
+ cursor: col-resize;
+ width: 10px;
+ right: -5px;
+ top: 0;
+ height: 100%;
+ position: absolute;
+ z-index: 5;
+}
+.ngHeaderText {
+ padding: 5px;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ white-space: nowrap;
+ -ms-text-overflow: ellipsis;
+ -o-text-overflow: ellipsis;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+/******** Viewport ********/
+.ngViewport{
+ overflow: auto;
+ min-height: 20px;
+}
+
+.ngCanvas{
+ position: relative;
+}
+
+/******** Rows ********/
+.ngRow {
+ position: absolute;
+ border-bottom: 1px solid rgb(229, 229, 229);
+}
+.ngRow.even {
+ background-color: rgb(243, 243, 243);
+}
+.ngRow.odd {
+ background-color: rgb(253, 253, 253);
+}
+.ngRow.selected {
+ background-color: rgb(189, 208, 203);
+}
+.ngRow.canSelect {
+ cursor: pointer;
+}
+
+/******** Cells ********/
+
+.ngCell {
+ overflow: hidden;
+ position: absolute;
+ border-left: 1px solid rgb(212,212,212);
+ top: 0;
+ bottom: 0;
+}
+
+.ngCell:first-child{
+ border-left: 0;
+}
+
+.ngCell:last-child {
+ border-right: 1px solid rgb(212,212,212);
+}
+
+.ngCellText {
+ padding: 5px;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ white-space: nowrap;
+ -ms-text-overflow: ellipsis;
+ -o-text-overflow: ellipsis;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+.ngSelectionHeader {
+ position: absolute;
+ top: 11px;
+ left: 6px;
+}
+input[type="checkbox"] {
+ margin: 0;
+ padding: 0;
+}
+input {
+ vertical-align:top;
+}
+.ngSelectionCell{
+ margin-top: 9px;
+ margin-left: 6px;
+}
+.ngNoSort {
+ cursor:default;
+}
+
+/******** Footer ********/
+.ngFooterPanel{
+ background-color: rgb(234, 234, 234);
+ padding: 0;
+ border-top: 1px solid rgb(212,212,212);
+ position: relative;
+}
+.ngTotalSelectContainer {
+ float: left;
+ margin: 5px;
+ margin-top: 7px;
+}
+.ngFooterSelectedItems {
+ padding: 2px;
+}
+.ngFooterTotalItems {
+ padding: 2px;
+}
+.ngFooterTotalItems.ngnoMultiSelect {
+ padding: 0 !important;
+}
+
+/* Aggregates */
+.ngAggHeader {
+ position: absolute;
+ border: none;
+}
+.ngAggregate {
+ position: absolute;
+ background-color: rgb(179, 191, 188);
+ border-bottom: 1px solid beige;
+ overflow: hidden;
+ top: 0;
+ bottom: 0;
+ right: -1px;
+ left: 0;
+}
+.ngAggregateText {
+ position: absolute;
+ left: 27px;
+ top: 5px;
+ line-height: 20px;
+ white-space:nowrap;
+}
+.ngAggArrowExpanded {
+ position: absolute;
+ left: 8px;
+ bottom: 10px;
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 0 0 9px 9px;
+ border-color: transparent transparent #000000 transparent;
+}
+.ngAggArrowCollapsed {
+ position: absolute;
+ left: 8px;
+ bottom: 10px;
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 5px 0 5px 8.7px;
+ border-color: transparent transparent transparent #000000;
+}
+
+.ngHeaderButton {
+ position: absolute;
+ right: 2px;
+ top: 8px;
+ -moz-border-radius: 50%;
+ -webkit-border-radius: 50%;
+ border-radius: 50%;
+ width: 14px;
+ height: 14px;
+ z-index: 5;
+ background-color: rgb(179, 191, 188);
+ cursor: pointer;
+ /* width and height can be anything, as long as they're equal */
+}
+.ngHeaderButtonArrow {
+ position: absolute;
+ top: 4px;
+ left: 3px;
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 6.5px 4.5px 0 4.5px;
+ border-color: #000 transparent transparent transparent;
+ /* width and height can be anything, as long as they're equal */
+}
+.ngColMenu {
+ right: 2px;
+ padding: 5px;
+ top: 25px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background-color: #BDD0CB;
+ position: absolute;
+ border: 2px solid rgb(212,212,212);
+ z-index: 5;
+}
+.ngMenuText {
+ position: relative;
+ top: 2px;
+ left: 2px;
+}
+.ngColList {
+ list-style-type: none;
+}
+
+.ngColListItem {
+ position: relative;
+ right: 17px;
+ top: 2px;
+ white-space:nowrap;
+}
+.ngColListCheckbox {
+ position: relative;
+ right: 3px;
+ top: 4px;
+}
+
+/********Paging Styles **********/
+
+.ngPagerButton{
+ height: 25px;
+ min-width: 26px;
+}
+
+.ngPagerFirstTriangle{
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 5px 8.7px 5px 0;
+ border-color: transparent #000000 transparent transparent;
+ margin-left: 2px;
+}
+
+.ngPagerFirstBar{
+ width: 10px;
+ border-left: 2px solid black;
+ margin-top: -6px;
+ height: 12px;
+ margin-left: -3px;
+}
+
+.ngPagerLastTriangle{
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 5px 0 5px 8.7px;
+ border-color: transparent transparent transparent #000000;
+ margin-left: -1px;
+}
+
+.ngPagerLastBar{
+ width: 10px;
+ border-left: 2px solid black;
+ margin-top: -6px;
+ height: 12px;
+ margin-left: 1px;
+}
+
+.ngPagerPrevTriangle{
+ margin-left: 0;
+}
+
+.ngPagerNextTriangle{
+ margin-left: 1px;
+}
+.ngGroupIcon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAYAAACZ3F9/AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAEFJREFUKFNjoAhISkr+h2J5JDZODNXGwGBsbPwfhIGAA8bGh6HaGBiAGhxAGJmND4M1gQCSM0adCsVQbcPcqQwMALWDGyDvWPefAAAAAElFTkSuQmCC);
+ background-repeat:no-repeat;
+ height: 15px;
+ width: 15px;
+ position: absolute;
+ right: -2px;
+ top: 2px;
+}
+
+.ngGroupedByIcon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAANCAYAAACZ3F9/AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAElJREFUKFNjoAhISkr+R8LyaHwMDNXGwGBsbPwfhoGAA5mPDUO1oWpE52PDYE0gALTFAYbR+dgwWBMIoPlh1I9ADNU2NPzIwAAAFQYI9E4OLvEAAAAASUVORK5CYII=);
+ background-repeat:no-repeat;
+ height: 15px;
+ width: 15px;
+ position: absolute;
+ right: -2px;
+ top: 2px;
+}
+
+.ngGroupingNumber {
+ position: absolute;
+ right: -10px;
+ top: -2px;
+}