gookou_angular-ng-grid-rails 2.0.7.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 iniciontingookou
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # angular-ng-grid-rails
2
+
3
+ angular-ng-grid-rails wraps the [AngularJS ng-grid](https://github.com/angular-ui/ng-grid) component for use in Rails 3.1 and above. Assets will minify automatically during production.
4
+
5
+ ## Usage
6
+
7
+ Add the following to your gemfile:
8
+
9
+ gem 'gookou_angular-ng-grid-rails'
10
+
11
+ Add the following directive to your Javascript manifest file (application.js):
12
+
13
+ //= require angular-ng-grid-rails
14
+
15
+ (Optional) require [AngularJS ng-grid plugins](https://github.com/angular-ui/ng-grid/tree/master/plugins) add the following directive to your Javascript manifest file (application.js):
16
+
17
+ //= require angular-ng-grid-plugins-rails
18
+
19
+ ## Version
20
+
21
+ The gem version will try match the original ng-grid release version.
22
+
23
+ # License
24
+
25
+ MIT
26
+
27
+ # Thanks
28
+
29
+ Gem based on Angularjs-rails(https://github.com/confuseddesi/angularjs-rails) by Hirav Gandhi
@@ -0,0 +1,8 @@
1
+ require "gookou_angular-ng-grid-rails/version"
2
+
3
+ module GookouAngularNgGrid
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module GookouAngularNgGrid
2
+ module Rails
3
+ VERSION = "2.0.7.2"
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ //= require plugins/ng-grid-csv-export
2
+ //= require plugins/ng-grid-flexible-height
3
+ //= require plugins/ng-grid-layout
4
+ //= require plugins/ng-grid-reorderable
5
+ //= require plugins/ng-grid-wysiwyg-export
@@ -0,0 +1,3586 @@
1
+ /***********************************************
2
+ * ng-grid JavaScript Library
3
+ * Authors: https://github.com/angular-ui/ng-grid/blob/master/README.md
4
+ * License: MIT (http://www.opensource.org/licenses/mit-license.php)
5
+ * Compiled At: 07/01/2013 21:49
6
+ ***********************************************/
7
+ (function(window, $) {
8
+ 'use strict';
9
+ // the # of rows we want to add to the top and bottom of the rendered grid rows
10
+ var EXCESS_ROWS = 6;
11
+ var SCROLL_THRESHOLD = 4;
12
+ var ASC = "asc";
13
+ // constant for sorting direction
14
+ var DESC = "desc";
15
+ // constant for sorting direction
16
+ var NG_FIELD = '_ng_field_';
17
+ var NG_DEPTH = '_ng_depth_';
18
+ var NG_HIDDEN = '_ng_hidden_';
19
+ var NG_COLUMN = '_ng_column_';
20
+ var CUSTOM_FILTERS = /CUSTOM_FILTERS/g;
21
+ var COL_FIELD = /COL_FIELD/g;
22
+ var DISPLAY_CELL_TEMPLATE = /DISPLAY_CELL_TEMPLATE/g;
23
+ var EDITABLE_CELL_TEMPLATE = /EDITABLE_CELL_TEMPLATE/g;
24
+ var TEMPLATE_REGEXP = /<.+>/;
25
+ window.ngGrid = {};
26
+ window.ngGrid.i18n = {};
27
+
28
+ // Declare app level module which depends on filters, and services
29
+ var ngGridServices = angular.module('ngGrid.services', []);
30
+ var ngGridDirectives = angular.module('ngGrid.directives', []);
31
+ var ngGridFilters = angular.module('ngGrid.filters', []);
32
+ // initialization of services into the main module
33
+ angular.module('ngGrid', ['ngGrid.services', 'ngGrid.directives', 'ngGrid.filters']);
34
+ //set event binding on the grid so we can select using the up/down keys
35
+ var ngMoveSelectionHandler = function($scope, elm, evt, grid) {
36
+ if ($scope.selectionProvider.selectedItems === undefined) {
37
+ return true;
38
+ }
39
+
40
+ var charCode = evt.which || evt.keyCode,
41
+ newColumnIndex,
42
+ lastInRow = false,
43
+ firstInRow = false,
44
+ rowIndex = $scope.selectionProvider.lastClickedRow === undefined ? 1 : $scope.selectionProvider.lastClickedRow.rowIndex,
45
+ visibleCols = $scope.columns.filter(function(c) { return c.visible; }),
46
+ pinnedCols = $scope.columns.filter(function(c) { return c.pinned; });
47
+
48
+ if ($scope.col) {
49
+ newColumnIndex = visibleCols.indexOf($scope.col);
50
+ }
51
+
52
+ if (charCode !== 37 && charCode !== 38 && charCode !== 39 && charCode !== 40 && charCode !== 9 && charCode !== 13) {
53
+ return true;
54
+ }
55
+
56
+ if ($scope.enableCellSelection) {
57
+ if (charCode === 9) { //tab key
58
+ evt.preventDefault();
59
+ }
60
+
61
+ var focusedOnFirstColumn = $scope.showSelectionCheckbox ? $scope.col.index === 1 : $scope.col.index === 0;
62
+ var focusedOnFirstVisibleColumns = $scope.$index === 1 || $scope.$index === 0;
63
+ var focusedOnLastVisibleColumns = $scope.$index === ($scope.renderedColumns.length - 1) || $scope.$index === ($scope.renderedColumns.length - 2);
64
+ var focusedOnLastColumn = visibleCols.indexOf($scope.col) === (visibleCols.length - 1);
65
+ var focusedOnLastPinnedColumn = pinnedCols.indexOf($scope.col) === (pinnedCols.length - 1);
66
+
67
+ if (charCode === 37 || charCode === 9 && evt.shiftKey) {
68
+ var scrollTo = 0;
69
+
70
+ if (!focusedOnFirstColumn) {
71
+ newColumnIndex -= 1;
72
+ }
73
+
74
+ if (focusedOnFirstVisibleColumns) {
75
+ if (focusedOnFirstColumn && charCode === 9 && evt.shiftKey){
76
+ scrollTo = grid.$canvas.width();
77
+ newColumnIndex = visibleCols.length - 1;
78
+ firstInRow = true;
79
+ }
80
+ else {
81
+ scrollTo = grid.$viewport.scrollLeft() - $scope.col.width;
82
+ }
83
+ }
84
+ else if (pinnedCols.length > 0) {
85
+ scrollTo = grid.$viewport.scrollLeft() - visibleCols[newColumnIndex].width;
86
+ }
87
+
88
+ grid.$viewport.scrollLeft(scrollTo);
89
+
90
+ }
91
+ else if (charCode === 39 || charCode === 9 && !evt.shiftKey) {
92
+ if (focusedOnLastVisibleColumns) {
93
+ if (focusedOnLastColumn && charCode === 9 && !evt.shiftKey) {
94
+ grid.$viewport.scrollLeft(0);
95
+ newColumnIndex = $scope.showSelectionCheckbox ? 1 : 0;
96
+ lastInRow = true;
97
+ }
98
+ else {
99
+ grid.$viewport.scrollLeft(grid.$viewport.scrollLeft() + $scope.col.width);
100
+ }
101
+ }
102
+ else if (focusedOnLastPinnedColumn) {
103
+ grid.$viewport.scrollLeft(0);
104
+ }
105
+
106
+ if (!focusedOnLastColumn) {
107
+ newColumnIndex += 1;
108
+ }
109
+ }
110
+ }
111
+
112
+ var items;
113
+ if ($scope.configGroups.length > 0) {
114
+ items = grid.rowFactory.parsedData.filter(function (row) {
115
+ return !row.isAggRow;
116
+ });
117
+ }
118
+ else {
119
+ items = grid.filteredRows;
120
+ }
121
+
122
+ var offset = 0;
123
+ if (rowIndex !== 0 && (charCode === 38 || charCode === 13 && evt.shiftKey || charCode === 9 && evt.shiftKey && firstInRow)) { //arrow key up or shift enter or tab key and first item in row
124
+ offset = -1;
125
+ }
126
+ else if (rowIndex !== items.length - 1 && (charCode === 40 || charCode === 13 && !evt.shiftKey || charCode === 9 && lastInRow)) {//arrow key down, enter, or tab key and last item in row?
127
+ offset = 1;
128
+ }
129
+
130
+ if (offset) {
131
+ var r = items[rowIndex + offset];
132
+ if (r.beforeSelectionChange(r, evt)) {
133
+ r.continueSelection(evt);
134
+ $scope.$emit('ngGridEventDigestGridParent');
135
+
136
+ if ($scope.selectionProvider.lastClickedRow.renderedRowIndex >= $scope.renderedRows.length - EXCESS_ROWS - 2) {
137
+ grid.$viewport.scrollTop(grid.$viewport.scrollTop() + $scope.rowHeight);
138
+ }
139
+ else if ($scope.selectionProvider.lastClickedRow.renderedRowIndex <= EXCESS_ROWS + 2) {
140
+ grid.$viewport.scrollTop(grid.$viewport.scrollTop() - $scope.rowHeight);
141
+ }
142
+ }
143
+ }
144
+
145
+ if ($scope.enableCellSelection) {
146
+ setTimeout(function(){
147
+ $scope.domAccessProvider.focusCellElement($scope, $scope.renderedColumns.indexOf(visibleCols[newColumnIndex]));
148
+ }, 3);
149
+ }
150
+
151
+ return false;
152
+ };
153
+
154
+ if (!String.prototype.trim) {
155
+ String.prototype.trim = function() {
156
+ return this.replace(/^\s+|\s+$/g, '');
157
+ };
158
+ }
159
+ if (!Array.prototype.indexOf) {
160
+ Array.prototype.indexOf = function(elt /*, from*/) {
161
+ var len = this.length >>> 0;
162
+ var from = Number(arguments[1]) || 0;
163
+ from = (from < 0) ? Math.ceil(from) : Math.floor(from);
164
+ if (from < 0) {
165
+ from += len;
166
+ }
167
+ for (; from < len; from++) {
168
+ if (from in this && this[from] === elt) {
169
+ return from;
170
+ }
171
+ }
172
+ return -1;
173
+ };
174
+ }
175
+ if (!Array.prototype.filter) {
176
+ Array.prototype.filter = function(fun /*, thisp */) {
177
+ "use strict";
178
+ var t = Object(this);
179
+ var len = t.length >>> 0;
180
+ if (typeof fun !== "function") {
181
+ throw new TypeError();
182
+ }
183
+ var res = [];
184
+ var thisp = arguments[1];
185
+ for (var i = 0; i < len; i++) {
186
+ if (i in t) {
187
+ var val = t[i]; // in case fun mutates this
188
+ if (fun.call(thisp, val, i, t)) {
189
+ res.push(val);
190
+ }
191
+ }
192
+ }
193
+ return res;
194
+ };
195
+ }
196
+ ngGridFilters.filter('checkmark', function() {
197
+ return function(input) {
198
+ return input ? '\u2714' : '\u2718';
199
+ };
200
+ });
201
+ ngGridFilters.filter('ngColumns', function() {
202
+ return function(input) {
203
+ return input.filter(function(col) {
204
+ return !col.isAggCol;
205
+ });
206
+ };
207
+ });
208
+ angular.module('ngGrid.services').factory('$domUtilityService',['$utilityService', function($utils) {
209
+ var domUtilityService = {};
210
+ var regexCache = {};
211
+ var getWidths = function() {
212
+ var $testContainer = $('<div></div>');
213
+ $testContainer.appendTo('body');
214
+ // 1. Run all the following measurements on startup!
215
+ //measure Scroll Bars
216
+ $testContainer.height(100).width(100).css("position", "absolute").css("overflow", "scroll");
217
+ $testContainer.append('<div style="height: 400px; width: 400px;"></div>');
218
+ domUtilityService.ScrollH = ($testContainer.height() - $testContainer[0].clientHeight);
219
+ domUtilityService.ScrollW = ($testContainer.width() - $testContainer[0].clientWidth);
220
+ $testContainer.empty();
221
+ //clear styles
222
+ $testContainer.attr('style', '');
223
+ //measure letter sizes using a pretty typical font size and fat font-family
224
+ $testContainer.append('<span style="font-family: Verdana, Helvetica, Sans-Serif; font-size: 14px;"><strong>M</strong></span>');
225
+ domUtilityService.LetterW = $testContainer.children().first().width();
226
+ $testContainer.remove();
227
+ };
228
+ domUtilityService.eventStorage = {};
229
+ domUtilityService.AssignGridContainers = function($scope, rootEl, grid) {
230
+ grid.$root = $(rootEl);
231
+ //Headers
232
+ grid.$topPanel = grid.$root.find(".ngTopPanel");
233
+ grid.$groupPanel = grid.$root.find(".ngGroupPanel");
234
+ grid.$headerContainer = grid.$topPanel.find(".ngHeaderContainer");
235
+ $scope.$headerContainer = grid.$headerContainer;
236
+
237
+ grid.$headerScroller = grid.$topPanel.find(".ngHeaderScroller");
238
+ grid.$headers = grid.$headerScroller.children();
239
+ //Viewport
240
+ grid.$viewport = grid.$root.find(".ngViewport");
241
+ //Canvas
242
+ grid.$canvas = grid.$viewport.find(".ngCanvas");
243
+ //Footers
244
+ grid.$footerPanel = grid.$root.find(".ngFooterPanel");
245
+
246
+ $scope.$watch(function () {
247
+ return grid.$viewport.scrollLeft();
248
+ }, function (newLeft) {
249
+ return grid.$headerContainer.scrollLeft(newLeft);
250
+ });
251
+ domUtilityService.UpdateGridLayout($scope, grid);
252
+ };
253
+ domUtilityService.getRealWidth = function (obj) {
254
+ var width = 0;
255
+ var props = { visibility: "hidden", display: "block" };
256
+ var hiddenParents = obj.parents().andSelf().not(':visible');
257
+ $.swap(hiddenParents[0], props, function () {
258
+ width = obj.outerWidth();
259
+ });
260
+ return width;
261
+ };
262
+ domUtilityService.UpdateGridLayout = function($scope, grid) {
263
+ //catch this so we can return the viewer to their original scroll after the resize!
264
+ var scrollTop = grid.$viewport.scrollTop();
265
+ grid.elementDims.rootMaxW = grid.$root.width();
266
+ if (grid.$root.is(':hidden')) {
267
+ grid.elementDims.rootMaxW = domUtilityService.getRealWidth(grid.$root);
268
+ }
269
+ grid.elementDims.rootMaxH = grid.$root.height();
270
+ //check to see if anything has changed
271
+ grid.refreshDomSizes();
272
+ $scope.adjustScrollTop(scrollTop, true); //ensure that the user stays scrolled where they were
273
+ };
274
+ domUtilityService.numberOfGrids = 0;
275
+ domUtilityService.BuildStyles = function($scope, grid, digest) {
276
+ var rowHeight = grid.config.rowHeight,
277
+ $style = grid.$styleSheet,
278
+ gridId = grid.gridId,
279
+ css,
280
+ cols = $scope.columns,
281
+ sumWidth = 0;
282
+
283
+ if (!$style) {
284
+ $style = $('#' + gridId);
285
+ if (!$style[0]) {
286
+ $style = $("<style id='" + gridId + "' type='text/css' rel='stylesheet' />").appendTo(grid.$root);
287
+ }
288
+ }
289
+ $style.empty();
290
+ var trw = $scope.totalRowWidth();
291
+ css = "." + gridId + " .ngCanvas { width: " + trw + "px; }" +
292
+ "." + gridId + " .ngRow { width: " + trw + "px; }" +
293
+ "." + gridId + " .ngCanvas { width: " + trw + "px; }" +
294
+ "." + gridId + " .ngHeaderScroller { width: " + (trw + domUtilityService.ScrollH) + "px}";
295
+
296
+ for (var i = 0; i < cols.length; i++) {
297
+ var col = cols[i];
298
+ if (col.visible !== false) {
299
+ css += "." + gridId + " .col" + i + " { width: " + col.width + "px; left: " + sumWidth + "px; height: " + rowHeight + "px }" +
300
+ "." + gridId + " .colt" + i + " { width: " + col.width + "px; }";
301
+ sumWidth += col.width;
302
+ }
303
+ }
304
+
305
+ if ($utils.isIe) { // IE
306
+ $style[0].styleSheet.cssText = css;
307
+ }
308
+
309
+ else {
310
+ $style[0].appendChild(document.createTextNode(css));
311
+ }
312
+
313
+ grid.$styleSheet = $style;
314
+
315
+ $scope.adjustScrollLeft(grid.$viewport.scrollLeft());
316
+ if (digest) {
317
+ domUtilityService.digest($scope);
318
+ }
319
+ };
320
+ domUtilityService.setColLeft = function(col, colLeft, grid) {
321
+ if (grid.$styleSheet) {
322
+ var regex = regexCache[col.index];
323
+ if (!regex) {
324
+ regex = regexCache[col.index] = new RegExp(".col" + col.index + " { width: [0-9]+px; left: [0-9]+px");
325
+ }
326
+ var str = grid.$styleSheet.html();
327
+ var newStr = str.replace(regex, ".col" + col.index + " { width: " + col.width + "px; left: " + colLeft + "px");
328
+ if ($utils.isIe) { // IE
329
+ setTimeout(function() {
330
+ grid.$styleSheet.html(newStr);
331
+ });
332
+ }
333
+ else {
334
+ grid.$styleSheet.html(newStr);
335
+ }
336
+ }
337
+ };
338
+ domUtilityService.setColLeft.immediate = 1;
339
+ domUtilityService.RebuildGrid = function($scope, grid){
340
+ domUtilityService.UpdateGridLayout($scope, grid);
341
+ if (grid.config.maintainColumnRatios == null || grid.config.maintainColumnRatios) {
342
+ grid.configureColumnWidths();
343
+ }
344
+ $scope.adjustScrollLeft(grid.$viewport.scrollLeft());
345
+ domUtilityService.BuildStyles($scope, grid, true);
346
+ };
347
+
348
+ domUtilityService.digest = function($scope) {
349
+ if (!$scope.$root.$$phase) {
350
+ $scope.$digest();
351
+ }
352
+ };
353
+ domUtilityService.ScrollH = 17; // default in IE, Chrome, & most browsers
354
+ domUtilityService.ScrollW = 17; // default in IE, Chrome, & most browsers
355
+ domUtilityService.LetterW = 10;
356
+ getWidths();
357
+ return domUtilityService;
358
+ }]);
359
+ angular.module('ngGrid.services').factory('$sortService', ['$parse', function($parse) {
360
+ var sortService = {};
361
+ sortService.colSortFnCache = {}; // cache of sorting functions. Once we create them, we don't want to keep re-doing it
362
+ // this takes an piece of data from the cell and tries to determine its type and what sorting
363
+ // function to use for it
364
+ // @item - the cell data
365
+ sortService.guessSortFn = function(item) {
366
+ var itemType = typeof(item);
367
+ //check for numbers and booleans
368
+ switch (itemType) {
369
+ case "number":
370
+ return sortService.sortNumber;
371
+ case "boolean":
372
+ return sortService.sortBool;
373
+ case "string":
374
+ // if number string return number string sort fn. else return the str
375
+ return item.match(/^[-+]?[£$¤]?[\d,.]+%?$/) ? sortService.sortNumberStr : sortService.sortAlpha;
376
+ default:
377
+ //check if the item is a valid Date
378
+ if (Object.prototype.toString.call(item) === '[object Date]') {
379
+ return sortService.sortDate;
380
+ }
381
+ else {
382
+ //finally just sort the basic sort...
383
+ return sortService.basicSort;
384
+ }
385
+ }
386
+ };
387
+ //#region Sorting Functions
388
+ sortService.basicSort = function(a, b) {
389
+ if (a === b) {
390
+ return 0;
391
+ }
392
+ if (a < b) {
393
+ return -1;
394
+ }
395
+ return 1;
396
+ };
397
+ sortService.sortNumber = function(a, b) {
398
+ return a - b;
399
+ };
400
+ sortService.sortNumberStr = function(a, b) {
401
+ var numA, numB, badA = false, badB = false;
402
+ numA = parseFloat(a.replace(/[^0-9.-]/g, ''));
403
+ if (isNaN(numA)) {
404
+ badA = true;
405
+ }
406
+ numB = parseFloat(b.replace(/[^0-9.-]/g, ''));
407
+ if (isNaN(numB)) {
408
+ badB = true;
409
+ }
410
+ // we want bad ones to get pushed to the bottom... which effectively is "greater than"
411
+ if (badA && badB) {
412
+ return 0;
413
+ }
414
+ if (badA) {
415
+ return 1;
416
+ }
417
+ if (badB) {
418
+ return -1;
419
+ }
420
+ return numA - numB;
421
+ };
422
+ sortService.sortAlpha = function(a, b) {
423
+ var strA = a.toLowerCase(),
424
+ strB = b.toLowerCase();
425
+ return strA === strB ? 0 : (strA < strB ? -1 : 1);
426
+ };
427
+ sortService.sortDate = function(a, b) {
428
+ var timeA = a.getTime(),
429
+ timeB = b.getTime();
430
+ return timeA === timeB ? 0 : (timeA < timeB ? -1 : 1);
431
+ };
432
+ sortService.sortBool = function(a, b) {
433
+ if (a && b) {
434
+ return 0;
435
+ }
436
+ if (!a && !b) {
437
+ return 0;
438
+ } else {
439
+ return a ? 1 : -1;
440
+ }
441
+ };
442
+ //#endregion
443
+ // the core sorting logic trigger
444
+ sortService.sortData = function(sortInfo, data /*datasource*/) {
445
+ // first make sure we are even supposed to do work
446
+ if (!data || !sortInfo) {
447
+ return;
448
+ }
449
+ var l = sortInfo.fields.length,
450
+ order = sortInfo.fields,
451
+ col,
452
+ direction,
453
+ // IE9 HACK.... omg, I can't reference data array within the sort fn below. has to be a separate reference....!!!!
454
+ d = data.slice(0);
455
+ //now actually sort the data
456
+ data.sort(function (itemA, itemB) {
457
+ var tem = 0,
458
+ indx = 0,
459
+ sortFn;
460
+ while (tem === 0 && indx < l) {
461
+ // grab the metadata for the rest of the logic
462
+ col = sortInfo.columns[indx];
463
+ direction = sortInfo.directions[indx];
464
+ sortFn = sortService.getSortFn(col, d);
465
+
466
+ var propA = $parse(order[indx])(itemA);
467
+ var propB = $parse(order[indx])(itemB);
468
+ // we want to allow zero values to be evaluated in the sort function
469
+ if ((!propA && propA !== 0) || (!propB && propB !== 0)) {
470
+ // we want to force nulls and such to the bottom when we sort... which effectively is "greater than"
471
+ if (!propB && !propA) {
472
+ tem = 0;
473
+ }
474
+ else if (!propA) {
475
+ tem = 1;
476
+ }
477
+ else if (!propB) {
478
+ tem = -1;
479
+ }
480
+ }
481
+ else {
482
+ tem = sortFn(propA, propB);
483
+ }
484
+ indx++;
485
+ }
486
+ //made it this far, we don't have to worry about null & undefined
487
+ if (direction === ASC) {
488
+ return tem;
489
+ } else {
490
+ return 0 - tem;
491
+ }
492
+ });
493
+ };
494
+ sortService.Sort = function(sortInfo, data) {
495
+ if (sortService.isSorting) {
496
+ return;
497
+ }
498
+ sortService.isSorting = true;
499
+ sortService.sortData(sortInfo, data);
500
+ sortService.isSorting = false;
501
+ };
502
+ sortService.getSortFn = function(col, data) {
503
+ var sortFn, item;
504
+ //see if we already figured out what to use to sort the column
505
+ if (sortService.colSortFnCache[col.field]) {
506
+ sortFn = sortService.colSortFnCache[col.field];
507
+ }
508
+ else if (col.sortingAlgorithm !== undefined) {
509
+ sortFn = col.sortingAlgorithm;
510
+ sortService.colSortFnCache[col.field] = col.sortingAlgorithm;
511
+ }
512
+ else { // try and guess what sort function to use
513
+ item = data[0];
514
+ if (!item) {
515
+ return sortFn;
516
+ }
517
+ sortFn = sortService.guessSortFn($parse(col.field)(item));
518
+ //cache it
519
+ if (sortFn) {
520
+ sortService.colSortFnCache[col.field] = sortFn;
521
+ } else {
522
+ // we assign the alpha sort because anything that is null/undefined will never get passed to
523
+ // the actual sorting function. It will get caught in our null check and returned to be sorted
524
+ // down to the bottom
525
+ sortFn = sortService.sortAlpha;
526
+ }
527
+ }
528
+ return sortFn;
529
+ };
530
+ return sortService;
531
+ }]);
532
+
533
+ angular.module('ngGrid.services').factory('$utilityService', ['$parse', function ($parse) {
534
+ var funcNameRegex = /function (.{1,})\(/;
535
+ var utils = {
536
+ visualLength: function(node) {
537
+ var elem = document.getElementById('testDataLength');
538
+ if (!elem) {
539
+ elem = document.createElement('SPAN');
540
+ elem.id = "testDataLength";
541
+ elem.style.visibility = "hidden";
542
+ document.body.appendChild(elem);
543
+ }
544
+ $(elem).css('font', $(node).css('font'));
545
+ $(elem).css('font-size', $(node).css('font-size'));
546
+ $(elem).css('font-family', $(node).css('font-family'));
547
+ elem.innerHTML = $(node).text();
548
+ return elem.offsetWidth;
549
+ },
550
+ forIn: function(obj, action) {
551
+ for (var prop in obj) {
552
+ if (obj.hasOwnProperty(prop)) {
553
+ action(obj[prop], prop);
554
+ }
555
+ }
556
+ },
557
+ evalProperty: function (entity, path) {
558
+ return $parse(path)(entity);
559
+ },
560
+ endsWith: function(str, suffix) {
561
+ if (!str || !suffix || typeof str !== "string") {
562
+ return false;
563
+ }
564
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
565
+ },
566
+ isNullOrUndefined: function(obj) {
567
+ if (obj === undefined || obj === null) {
568
+ return true;
569
+ }
570
+ return false;
571
+ },
572
+ getElementsByClassName: function(cl) {
573
+ var retnode = [];
574
+ var myclass = new RegExp('\\b' + cl + '\\b');
575
+ var elem = document.getElementsByTagName('*');
576
+ for (var i = 0; i < elem.length; i++) {
577
+ var classes = elem[i].className;
578
+ if (myclass.test(classes)) {
579
+ retnode.push(elem[i]);
580
+ }
581
+ }
582
+ return retnode;
583
+ },
584
+ newId: (function() {
585
+ var seedId = new Date().getTime();
586
+ return function() {
587
+ return seedId += 1;
588
+ };
589
+ })(),
590
+ seti18n: function($scope, language) {
591
+ var $langPack = window.ngGrid.i18n[language];
592
+ for (var label in $langPack) {
593
+ $scope.i18n[label] = $langPack[label];
594
+ }
595
+ },
596
+ getInstanceType: function (o) {
597
+ var results = (funcNameRegex).exec(o.constructor.toString());
598
+ if (results && results.length > 1) {
599
+ var instanceType = results[1].replace(/^\s+|\s+$/g, ""); // Trim surrounding whitespace; IE appears to add a space at the end
600
+ return instanceType;
601
+ }
602
+ else {
603
+ return "";
604
+ }
605
+ },
606
+ // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
607
+ // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
608
+ // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
609
+ // If there is a future need to detect specific versions of IE10+, we will amend this.
610
+ ieVersion: (function() {
611
+ var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');
612
+
613
+ // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
614
+ do{
615
+ div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->';
616
+ }while(iElems[0]);
617
+ return version > 4 ? version : undefined;
618
+ })()
619
+ };
620
+
621
+ $.extend(utils, {
622
+ isIe: (function() {
623
+ return utils.ieVersion !== undefined;
624
+ })()
625
+ });
626
+ return utils;
627
+ }]);
628
+
629
+ var ngAggregate = function (aggEntity, rowFactory, rowHeight, groupInitState) {
630
+ this.rowIndex = 0;
631
+ this.offsetTop = this.rowIndex * rowHeight;
632
+ this.entity = aggEntity;
633
+ this.label = aggEntity.gLabel;
634
+ this.field = aggEntity.gField;
635
+ this.depth = aggEntity.gDepth;
636
+ this.parent = aggEntity.parent;
637
+ this.children = aggEntity.children;
638
+ this.aggChildren = aggEntity.aggChildren;
639
+ this.aggIndex = aggEntity.aggIndex;
640
+ this.collapsed = groupInitState;
641
+ this.groupInitState = groupInitState;
642
+ this.rowFactory = rowFactory;
643
+ this.rowHeight = rowHeight;
644
+ this.isAggRow = true;
645
+ this.offsetLeft = aggEntity.gDepth * 25;
646
+ this.aggLabelFilter = aggEntity.aggLabelFilter;
647
+ };
648
+
649
+ ngAggregate.prototype.toggleExpand = function () {
650
+ this.collapsed = this.collapsed ? false : true;
651
+ if (this.orig) {
652
+ this.orig.collapsed = this.collapsed;
653
+ }
654
+ this.notifyChildren();
655
+ };
656
+ ngAggregate.prototype.setExpand = function (state) {
657
+ this.collapsed = state;
658
+ this.notifyChildren();
659
+ };
660
+ ngAggregate.prototype.notifyChildren = function () {
661
+ var longest = Math.max(this.rowFactory.aggCache.length, this.children.length);
662
+ for (var i = 0; i < longest; i++) {
663
+ if (this.aggChildren[i]) {
664
+ this.aggChildren[i].entity[NG_HIDDEN] = this.collapsed;
665
+ if (this.collapsed) {
666
+ this.aggChildren[i].setExpand(this.collapsed);
667
+ }
668
+ }
669
+ if (this.children[i]) {
670
+ this.children[i][NG_HIDDEN] = this.collapsed;
671
+ }
672
+ if (i > this.aggIndex && this.rowFactory.aggCache[i]) {
673
+ var agg = this.rowFactory.aggCache[i];
674
+ var offset = (30 * this.children.length);
675
+ agg.offsetTop = this.collapsed ? agg.offsetTop - offset : agg.offsetTop + offset;
676
+ }
677
+ }
678
+ this.rowFactory.renderedChange();
679
+ };
680
+ ngAggregate.prototype.aggClass = function () {
681
+ return this.collapsed ? "ngAggArrowCollapsed" : "ngAggArrowExpanded";
682
+ };
683
+ ngAggregate.prototype.totalChildren = function () {
684
+ if (this.aggChildren.length > 0) {
685
+ var i = 0;
686
+ var recurse = function (cur) {
687
+ if (cur.aggChildren.length > 0) {
688
+ angular.forEach(cur.aggChildren, function (a) {
689
+ recurse(a);
690
+ });
691
+ } else {
692
+ i += cur.children.length;
693
+ }
694
+ };
695
+ recurse(this);
696
+ return i;
697
+ } else {
698
+ return this.children.length;
699
+ }
700
+ };
701
+ ngAggregate.prototype.copy = function () {
702
+ var ret = new ngAggregate(this.entity, this.rowFactory, this.rowHeight, this.groupInitState);
703
+ ret.orig = this;
704
+ return ret;
705
+ };
706
+ var ngColumn = function (config, $scope, grid, domUtilityService, $templateCache, $utils) {
707
+ var self = this,
708
+ colDef = config.colDef,
709
+ delay = 500,
710
+ clicks = 0,
711
+ timer = null;
712
+ self.colDef = config.colDef;
713
+ self.width = colDef.width;
714
+ self.groupIndex = 0;
715
+ self.isGroupedBy = false;
716
+ self.minWidth = !colDef.minWidth ? 50 : colDef.minWidth;
717
+ self.maxWidth = !colDef.maxWidth ? 9000 : colDef.maxWidth;
718
+
719
+ // TODO: Use the column's definition for enabling cell editing
720
+ // self.enableCellEdit = config.enableCellEdit || colDef.enableCellEdit;
721
+ self.enableCellEdit = colDef.enableCellEdit !== undefined ? colDef.enableCellEdit : (config.enableCellEdit || config.enableCellEditOnFocus);
722
+
723
+ self.headerRowHeight = config.headerRowHeight;
724
+
725
+ // Use colDef.displayName as long as it's not undefined, otherwise default to the field name
726
+ self.displayName = (colDef.displayName === undefined) ? colDef.field : colDef.displayName;
727
+
728
+ self.index = config.index;
729
+ self.isAggCol = config.isAggCol;
730
+ self.cellClass = colDef.cellClass;
731
+ self.sortPriority = undefined;
732
+ self.cellFilter = colDef.cellFilter ? colDef.cellFilter : "";
733
+ self.field = colDef.field;
734
+ self.aggLabelFilter = colDef.cellFilter || colDef.aggLabelFilter;
735
+ self.visible = $utils.isNullOrUndefined(colDef.visible) || colDef.visible;
736
+ self.sortable = false;
737
+ self.resizable = false;
738
+ self.pinnable = false;
739
+ self.pinned = (config.enablePinning && colDef.pinned);
740
+ self.originalIndex = config.originalIndex == null ? self.index : config.originalIndex;
741
+ self.groupable = $utils.isNullOrUndefined(colDef.groupable) || colDef.groupable;
742
+ if (config.enableSort) {
743
+ self.sortable = $utils.isNullOrUndefined(colDef.sortable) || colDef.sortable;
744
+ }
745
+ if (config.enableResize) {
746
+ self.resizable = $utils.isNullOrUndefined(colDef.resizable) || colDef.resizable;
747
+ }
748
+ if (config.enablePinning) {
749
+ self.pinnable = $utils.isNullOrUndefined(colDef.pinnable) || colDef.pinnable;
750
+ }
751
+ self.sortDirection = undefined;
752
+ self.sortingAlgorithm = colDef.sortFn;
753
+ self.headerClass = colDef.headerClass;
754
+ self.cursor = self.sortable ? 'pointer' : 'default';
755
+ self.headerCellTemplate = colDef.headerCellTemplate || $templateCache.get('headerCellTemplate.html');
756
+ self.cellTemplate = colDef.cellTemplate || $templateCache.get('cellTemplate.html').replace(CUSTOM_FILTERS, self.cellFilter ? "|" + self.cellFilter : "");
757
+ if(self.enableCellEdit) {
758
+ self.cellEditTemplate = $templateCache.get('cellEditTemplate.html');
759
+ self.editableCellTemplate = colDef.editableCellTemplate || $templateCache.get('editableCellTemplate.html');
760
+ }
761
+ if (colDef.cellTemplate && !TEMPLATE_REGEXP.test(colDef.cellTemplate)) {
762
+ self.cellTemplate = $.ajax({
763
+ type: "GET",
764
+ url: colDef.cellTemplate,
765
+ async: false
766
+ }).responseText;
767
+ }
768
+ if (self.enableCellEdit && colDef.editableCellTemplate && !TEMPLATE_REGEXP.test(colDef.editableCellTemplate)) {
769
+ self.editableCellTemplate = $.ajax({
770
+ type: "GET",
771
+ url: colDef.editableCellTemplate,
772
+ async: false
773
+ }).responseText;
774
+ }
775
+ if (colDef.headerCellTemplate && !TEMPLATE_REGEXP.test(colDef.headerCellTemplate)) {
776
+ self.headerCellTemplate = $.ajax({
777
+ type: "GET",
778
+ url: colDef.headerCellTemplate,
779
+ async: false
780
+ }).responseText;
781
+ }
782
+ self.colIndex = function () {
783
+ var classes = self.pinned ? "pinned " : "";
784
+ classes += "col" + self.index + " colt" + self.index;
785
+ if (self.cellClass) {
786
+ classes += " " + self.cellClass;
787
+ }
788
+ return classes;
789
+ };
790
+ self.groupedByClass = function() {
791
+ return self.isGroupedBy ? "ngGroupedByIcon" : "ngGroupIcon";
792
+ };
793
+ self.toggleVisible = function() {
794
+ self.visible = !self.visible;
795
+ };
796
+ self.showSortButtonUp = function() {
797
+ return self.sortable ? self.sortDirection === DESC : self.sortable;
798
+ };
799
+ self.showSortButtonDown = function() {
800
+ return self.sortable ? self.sortDirection === ASC : self.sortable;
801
+ };
802
+ self.noSortVisible = function() {
803
+ return !self.sortDirection;
804
+ };
805
+ self.sort = function(evt) {
806
+ if (!self.sortable) {
807
+ return true; // column sorting is disabled, do nothing
808
+ }
809
+ var dir = self.sortDirection === ASC ? DESC : ASC;
810
+ self.sortDirection = dir;
811
+ config.sortCallback(self, evt);
812
+ return false;
813
+ };
814
+ self.gripClick = function() {
815
+ clicks++; //count clicks
816
+ if (clicks === 1) {
817
+ timer = setTimeout(function() {
818
+ //Here you can add a single click action.
819
+ clicks = 0; //after action performed, reset counter
820
+ }, delay);
821
+ } else {
822
+ clearTimeout(timer); //prevent single-click action
823
+ config.resizeOnDataCallback(self); //perform double-click action
824
+ clicks = 0; //after action performed, reset counter
825
+ }
826
+ };
827
+ self.gripOnMouseDown = function(event) {
828
+ $scope.isColumnResizing = true;
829
+ if (event.ctrlKey && !self.pinned) {
830
+ self.toggleVisible();
831
+ domUtilityService.BuildStyles($scope, grid);
832
+ return true;
833
+ }
834
+ event.target.parentElement.style.cursor = 'col-resize';
835
+ self.startMousePosition = event.clientX;
836
+ self.origWidth = self.width;
837
+ $(document).mousemove(self.onMouseMove);
838
+ $(document).mouseup(self.gripOnMouseUp);
839
+ return false;
840
+ };
841
+ self.onMouseMove = function(event) {
842
+ var diff = event.clientX - self.startMousePosition;
843
+ var newWidth = diff + self.origWidth;
844
+ self.width = (newWidth < self.minWidth ? self.minWidth : (newWidth > self.maxWidth ? self.maxWidth : newWidth));
845
+ $scope.hasUserChangedGridColumnWidths = true;
846
+ domUtilityService.BuildStyles($scope, grid);
847
+ return false;
848
+ };
849
+ self.gripOnMouseUp = function (event) {
850
+ $(document).off('mousemove', self.onMouseMove);
851
+ $(document).off('mouseup', self.gripOnMouseUp);
852
+ event.target.parentElement.style.cursor = 'default';
853
+ domUtilityService.digest($scope);
854
+ $scope.isColumnResizing = false;
855
+ return false;
856
+ };
857
+ self.copy = function() {
858
+ var ret = new ngColumn(config, $scope, grid, domUtilityService, $templateCache);
859
+ ret.isClone = true;
860
+ ret.orig = self;
861
+ return ret;
862
+ };
863
+ self.setVars = function (fromCol) {
864
+ self.orig = fromCol;
865
+ self.width = fromCol.width;
866
+ self.groupIndex = fromCol.groupIndex;
867
+ self.isGroupedBy = fromCol.isGroupedBy;
868
+ self.displayName = fromCol.displayName;
869
+ self.index = fromCol.index;
870
+ self.isAggCol = fromCol.isAggCol;
871
+ self.cellClass = fromCol.cellClass;
872
+ self.cellFilter = fromCol.cellFilter;
873
+ self.field = fromCol.field;
874
+ self.aggLabelFilter = fromCol.aggLabelFilter;
875
+ self.visible = fromCol.visible;
876
+ self.sortable = fromCol.sortable;
877
+ self.resizable = fromCol.resizable;
878
+ self.pinnable = fromCol.pinnable;
879
+ self.pinned = fromCol.pinned;
880
+ self.originalIndex = fromCol.originalIndex;
881
+ self.sortDirection = fromCol.sortDirection;
882
+ self.sortingAlgorithm = fromCol.sortingAlgorithm;
883
+ self.headerClass = fromCol.headerClass;
884
+ self.headerCellTemplate = fromCol.headerCellTemplate;
885
+ self.cellTemplate = fromCol.cellTemplate;
886
+ self.cellEditTemplate = fromCol.cellEditTemplate;
887
+ };
888
+ };
889
+
890
+ var ngDimension = function (options) {
891
+ this.outerHeight = null;
892
+ this.outerWidth = null;
893
+ $.extend(this, options);
894
+ };
895
+ var ngDomAccessProvider = function (grid) {
896
+ this.previousColumn = null;
897
+ this.grid = grid;
898
+
899
+ };
900
+
901
+ ngDomAccessProvider.prototype.changeUserSelect = function (elm, value) {
902
+ elm.css({
903
+ '-webkit-touch-callout': value,
904
+ '-webkit-user-select': value,
905
+ '-khtml-user-select': value,
906
+ '-moz-user-select': value === 'none' ? '-moz-none' : value,
907
+ '-ms-user-select': value,
908
+ 'user-select': value
909
+ });
910
+ };
911
+ ngDomAccessProvider.prototype.focusCellElement = function ($scope, index) {
912
+ if ($scope.selectionProvider.lastClickedRow) {
913
+ var columnIndex = index !== undefined ? index : this.previousColumn;
914
+ var elm = $scope.selectionProvider.lastClickedRow.clone ? $scope.selectionProvider.lastClickedRow.clone.elm : $scope.selectionProvider.lastClickedRow.elm;
915
+ if (columnIndex !== undefined && elm) {
916
+ var columns = angular.element(elm[0].children).filter(function () { return this.nodeType !== 8; }); //Remove html comments for IE8
917
+ var i = Math.max(Math.min($scope.renderedColumns.length - 1, columnIndex), 0);
918
+ if (this.grid.config.showSelectionCheckbox && angular.element(columns[i]).scope() && angular.element(columns[i]).scope().col.index === 0) {
919
+ i = 1; //don't want to focus on checkbox
920
+ }
921
+ if (columns[i]) {
922
+ columns[i].children[1].children[0].focus();
923
+ }
924
+ this.previousColumn = columnIndex;
925
+ }
926
+ }
927
+ };
928
+ ngDomAccessProvider.prototype.selectionHandlers = function ($scope, elm) {
929
+ var doingKeyDown = false;
930
+ var self = this;
931
+ elm.bind('keydown', function (evt) {
932
+ if (evt.keyCode === 16) { //shift key
933
+ self.changeUserSelect(elm, 'none', evt);
934
+ return true;
935
+ } else if (!doingKeyDown) {
936
+ doingKeyDown = true;
937
+ var ret = ngMoveSelectionHandler($scope, elm, evt, self.grid);
938
+ doingKeyDown = false;
939
+ return ret;
940
+ }
941
+ return true;
942
+ });
943
+ elm.bind('keyup', function (evt) {
944
+ if (evt.keyCode === 16) { //shift key
945
+ self.changeUserSelect(elm, 'text', evt);
946
+ }
947
+ return true;
948
+ });
949
+ };
950
+ var ngEventProvider = function (grid, $scope, domUtilityService, $timeout) {
951
+ var self = this;
952
+ // The init method gets called during the ng-grid directive execution.
953
+ self.colToMove = undefined;
954
+ self.groupToMove = undefined;
955
+ self.assignEvents = function() {
956
+ // Here we set the onmousedown event handler to the header container.
957
+ if (grid.config.jqueryUIDraggable && !grid.config.enablePinning) {
958
+ grid.$groupPanel.droppable({
959
+ addClasses: false,
960
+ drop: function(event) {
961
+ self.onGroupDrop(event);
962
+ }
963
+ });
964
+ } else {
965
+ grid.$groupPanel.on('mousedown', self.onGroupMouseDown).on('dragover', self.dragOver).on('drop', self.onGroupDrop);
966
+ grid.$headerScroller.on('mousedown', self.onHeaderMouseDown).on('dragover', self.dragOver);
967
+ if (grid.config.enableColumnReordering && !grid.config.enablePinning) {
968
+ grid.$headerScroller.on('drop', self.onHeaderDrop);
969
+ }
970
+ }
971
+ $scope.$watch('renderedColumns', function() {
972
+ $timeout(self.setDraggables);
973
+ });
974
+ };
975
+ self.dragStart = function(evt){
976
+ //FireFox requires there to be dataTransfer if you want to drag and drop.
977
+ evt.dataTransfer.setData('text', ''); //cannot be empty string
978
+ };
979
+ self.dragOver = function(evt) {
980
+ evt.preventDefault();
981
+ };
982
+ //For JQueryUI
983
+ self.setDraggables = function() {
984
+ if (!grid.config.jqueryUIDraggable) {
985
+ //Fix for FireFox. Instead of using jQuery on('dragstart', function) on find, we have to use addEventListeners for each column.
986
+ var columns = grid.$root.find('.ngHeaderSortColumn'); //have to iterate if using addEventListener
987
+ angular.forEach(columns, function(col){
988
+ if(col.className && col.className.indexOf("ngHeaderSortColumn") !== -1){
989
+ col.setAttribute('draggable', 'true');
990
+ //jQuery 'on' function doesn't have dataTransfer as part of event in handler unless added to event props, which is not recommended
991
+ //See more here: http://api.jquery.com/category/events/event-object/
992
+ if (col.addEventListener) { //IE8 doesn't have drag drop or event listeners
993
+ col.addEventListener('dragstart', self.dragStart);
994
+ }
995
+ }
996
+ });
997
+ if (navigator.userAgent.indexOf("MSIE") !== -1){
998
+ //call native IE dragDrop() to start dragging
999
+ grid.$root.find('.ngHeaderSortColumn').bind('selectstart', function () {
1000
+ this.dragDrop();
1001
+ return false;
1002
+ });
1003
+ }
1004
+ } else {
1005
+ grid.$root.find('.ngHeaderSortColumn').draggable({
1006
+ helper: 'clone',
1007
+ appendTo: 'body',
1008
+ stack: 'div',
1009
+ addClasses: false,
1010
+ start: function(event) {
1011
+ self.onHeaderMouseDown(event);
1012
+ }
1013
+ }).droppable({
1014
+ drop: function(event) {
1015
+ self.onHeaderDrop(event);
1016
+ }
1017
+ });
1018
+ }
1019
+ };
1020
+ self.onGroupMouseDown = function(event) {
1021
+ var groupItem = $(event.target);
1022
+ // Get the scope from the header container
1023
+ if (groupItem[0].className !== 'ngRemoveGroup') {
1024
+ var groupItemScope = angular.element(groupItem).scope();
1025
+ if (groupItemScope) {
1026
+ // set draggable events
1027
+ if (!grid.config.jqueryUIDraggable) {
1028
+ groupItem.attr('draggable', 'true');
1029
+ if(this.addEventListener){//IE8 doesn't have drag drop or event listeners
1030
+ this.addEventListener('dragstart', self.dragStart);
1031
+ }
1032
+ if (navigator.userAgent.indexOf("MSIE") !== -1){
1033
+ //call native IE dragDrop() to start dragging
1034
+ groupItem.bind('selectstart', function () {
1035
+ this.dragDrop();
1036
+ return false;
1037
+ });
1038
+ }
1039
+ }
1040
+ // Save the column for later.
1041
+ self.groupToMove = { header: groupItem, groupName: groupItemScope.group, index: groupItemScope.$index };
1042
+ }
1043
+ } else {
1044
+ self.groupToMove = undefined;
1045
+ }
1046
+ };
1047
+ self.onGroupDrop = function(event) {
1048
+ event.stopPropagation();
1049
+ // clear out the colToMove object
1050
+ var groupContainer;
1051
+ var groupScope;
1052
+ if (self.groupToMove) {
1053
+ // Get the closest header to where we dropped
1054
+ groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
1055
+ if (groupContainer.context.className === 'ngGroupPanel') {
1056
+ $scope.configGroups.splice(self.groupToMove.index, 1);
1057
+ $scope.configGroups.push(self.groupToMove.groupName);
1058
+ } else {
1059
+ groupScope = angular.element(groupContainer).scope();
1060
+ if (groupScope) {
1061
+ // If we have the same column, do nothing.
1062
+ if (self.groupToMove.index !== groupScope.$index) {
1063
+ // Splice the columns
1064
+ $scope.configGroups.splice(self.groupToMove.index, 1);
1065
+ $scope.configGroups.splice(groupScope.$index, 0, self.groupToMove.groupName);
1066
+ }
1067
+ }
1068
+ }
1069
+ self.groupToMove = undefined;
1070
+ grid.fixGroupIndexes();
1071
+ } else if (self.colToMove) {
1072
+ if ($scope.configGroups.indexOf(self.colToMove.col) === -1) {
1073
+ groupContainer = $(event.target).closest('.ngGroupElement'); // Get the scope from the header.
1074
+ if (groupContainer.context.className === 'ngGroupPanel' || groupContainer.context.className === 'ngGroupPanelDescription ng-binding') {
1075
+ $scope.groupBy(self.colToMove.col);
1076
+ } else {
1077
+ groupScope = angular.element(groupContainer).scope();
1078
+ if (groupScope) {
1079
+ // Splice the columns
1080
+ $scope.removeGroup(groupScope.$index);
1081
+ }
1082
+ }
1083
+ }
1084
+ self.colToMove = undefined;
1085
+ }
1086
+ if (!$scope.$$phase) {
1087
+ $scope.$apply();
1088
+ }
1089
+ };
1090
+ //Header functions
1091
+ self.onHeaderMouseDown = function(event) {
1092
+ // Get the closest header container from where we clicked.
1093
+ var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
1094
+ // Get the scope from the header container
1095
+ var headerScope = angular.element(headerContainer).scope();
1096
+ if (headerScope) {
1097
+ // Save the column for later.
1098
+ self.colToMove = { header: headerContainer, col: headerScope.col };
1099
+ }
1100
+ };
1101
+ self.onHeaderDrop = function(event) {
1102
+ if (!self.colToMove || self.colToMove.col.pinned) {
1103
+ return;
1104
+ }
1105
+ // Get the closest header to where we dropped
1106
+ var headerContainer = $(event.target).closest('.ngHeaderSortColumn');
1107
+ // Get the scope from the header.
1108
+ var headerScope = angular.element(headerContainer).scope();
1109
+ if (headerScope) {
1110
+ // If we have the same column, do nothing.
1111
+ if (self.colToMove.col === headerScope.col) {
1112
+ return;
1113
+ }
1114
+ // Splice the columns
1115
+ $scope.columns.splice(self.colToMove.col.index, 1);
1116
+ $scope.columns.splice(headerScope.col.index, 0, self.colToMove.col);
1117
+ grid.fixColumnIndexes();
1118
+ // clear out the colToMove object
1119
+ self.colToMove = undefined;
1120
+ domUtilityService.digest($scope);
1121
+ }
1122
+ };
1123
+
1124
+ self.assignGridEventHandlers = function() {
1125
+ //Chrome and firefox both need a tab index so the grid can recieve focus.
1126
+ //need to give the grid a tabindex if it doesn't already have one so
1127
+ //we'll just give it a tab index of the corresponding gridcache index
1128
+ //that way we'll get the same result every time it is run.
1129
+ //configurable within the options.
1130
+ if (grid.config.tabIndex === -1) {
1131
+ grid.$viewport.attr('tabIndex', domUtilityService.numberOfGrids);
1132
+ domUtilityService.numberOfGrids++;
1133
+ } else {
1134
+ grid.$viewport.attr('tabIndex', grid.config.tabIndex);
1135
+ }
1136
+ // resize on window resize
1137
+ var windowThrottle;
1138
+ $(window).resize(function(){
1139
+ clearTimeout(windowThrottle);
1140
+ windowThrottle = setTimeout(function() {
1141
+ //in function for IE8 compatibility
1142
+ domUtilityService.RebuildGrid($scope,grid);
1143
+ }, 100);
1144
+ });
1145
+ // resize on parent resize as well.
1146
+ var parentThrottle;
1147
+ $(grid.$root.parent()).on('resize', function() {
1148
+ clearTimeout(parentThrottle);
1149
+ parentThrottle = setTimeout(function() {
1150
+ //in function for IE8 compatibility
1151
+ domUtilityService.RebuildGrid($scope,grid);
1152
+ }, 100);
1153
+ });
1154
+ };
1155
+ // In this example we want to assign grid events.
1156
+ self.assignGridEventHandlers();
1157
+ self.assignEvents();
1158
+ };
1159
+
1160
+ var ngFooter = function ($scope, grid) {
1161
+ $scope.maxRows = function () {
1162
+ var ret = Math.max($scope.totalServerItems, grid.data.length);
1163
+ return ret;
1164
+ };
1165
+
1166
+ $scope.multiSelect = (grid.config.enableRowSelection && grid.config.multiSelect);
1167
+ $scope.selectedItemCount = grid.selectedItemCount;
1168
+ $scope.maxPages = function () {
1169
+ return Math.ceil($scope.maxRows() / $scope.pagingOptions.pageSize);
1170
+ };
1171
+
1172
+ $scope.pageForward = function() {
1173
+ var page = $scope.pagingOptions.currentPage;
1174
+ if ($scope.totalServerItems > 0) {
1175
+ $scope.pagingOptions.currentPage = Math.min(page + 1, $scope.maxPages());
1176
+ } else {
1177
+ $scope.pagingOptions.currentPage++;
1178
+ }
1179
+ };
1180
+
1181
+ $scope.pageBackward = function() {
1182
+ var page = $scope.pagingOptions.currentPage;
1183
+ $scope.pagingOptions.currentPage = Math.max(page - 1, 1);
1184
+ };
1185
+
1186
+ $scope.pageToFirst = function() {
1187
+ $scope.pagingOptions.currentPage = 1;
1188
+ };
1189
+
1190
+ $scope.pageToLast = function() {
1191
+ var maxPages = $scope.maxPages();
1192
+ $scope.pagingOptions.currentPage = maxPages;
1193
+ };
1194
+
1195
+ $scope.cantPageForward = function() {
1196
+ var curPage = $scope.pagingOptions.currentPage;
1197
+ var maxPages = $scope.maxPages();
1198
+ if ($scope.totalServerItems > 0) {
1199
+ return curPage >= maxPages;
1200
+ } else {
1201
+ return grid.data.length < 1;
1202
+ }
1203
+
1204
+ };
1205
+ $scope.cantPageToLast = function() {
1206
+ if ($scope.totalServerItems > 0) {
1207
+ return $scope.cantPageForward();
1208
+ } else {
1209
+ return true;
1210
+ }
1211
+ };
1212
+
1213
+ $scope.cantPageBackward = function() {
1214
+ var curPage = $scope.pagingOptions.currentPage;
1215
+ return curPage <= 1;
1216
+ };
1217
+ };
1218
+ /// <reference path="footer.js" />
1219
+ /// <reference path="../services/SortService.js" />
1220
+ /// <reference path="../../lib/jquery-1.8.2.min" />
1221
+ var ngGrid = function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q) {
1222
+ var defaults = {
1223
+ //Define an aggregate template to customize the rows when grouped. See github wiki for more details.
1224
+ aggregateTemplate: undefined,
1225
+
1226
+ //Callback for when you want to validate something after selection.
1227
+ afterSelectionChange: function() {
1228
+ },
1229
+
1230
+ /* Callback if you want to inspect something before selection,
1231
+ return false if you want to cancel the selection. return true otherwise.
1232
+ If you need to wait for an async call to proceed with selection you can
1233
+ use rowItem.changeSelection(event) method after returning false initially.
1234
+ Note: when shift+ Selecting multiple items in the grid this will only get called
1235
+ once and the rowItem will be an array of items that are queued to be selected. */
1236
+ beforeSelectionChange: function() {
1237
+ return true;
1238
+ },
1239
+
1240
+ //checkbox templates.
1241
+ checkboxCellTemplate: undefined,
1242
+ checkboxHeaderTemplate: undefined,
1243
+
1244
+ //definitions of columns as an array [], if not defines columns are auto-generated. See github wiki for more details.
1245
+ columnDefs: undefined,
1246
+
1247
+ //*Data being displayed in the grid. Each item in the array is mapped to a row being displayed.
1248
+ data: [],
1249
+
1250
+ //Data updated callback, fires every time the data is modified from outside the grid.
1251
+ dataUpdated: function() {
1252
+ },
1253
+
1254
+ //Enables cell editing.
1255
+ enableCellEdit: false,
1256
+
1257
+ //Enables cell editing on focus
1258
+ enableCellEditOnFocus: false,
1259
+
1260
+ //Enables cell selection.
1261
+ enableCellSelection: false,
1262
+
1263
+ //Enable or disable resizing of columns
1264
+ enableColumnResize: false,
1265
+
1266
+ //Enable or disable reordering of columns
1267
+ enableColumnReordering: false,
1268
+
1269
+ //Enable or disable HEAVY column virtualization. This turns off selection checkboxes and column pinning and is designed for spreadsheet-like data.
1270
+ enableColumnHeavyVirt: false,
1271
+
1272
+ //Enables the server-side paging feature
1273
+ enablePaging: false,
1274
+
1275
+ //Enable column pinning
1276
+ enablePinning: false,
1277
+
1278
+ //To be able to have selectable rows in grid.
1279
+ enableRowSelection: true,
1280
+
1281
+ //Enables or disables sorting in grid.
1282
+ enableSorting: true,
1283
+
1284
+ //Enables or disables text highlighting in grid by adding the "unselectable" class (See CSS file)
1285
+ enableHighlighting: false,
1286
+
1287
+ // string list of properties to exclude when auto-generating columns.
1288
+ excludeProperties: [],
1289
+
1290
+ /* filterOptions -
1291
+ filterText: The text bound to the built-in search box.
1292
+ useExternalFilter: Bypass internal filtering if you want to roll your own filtering mechanism but want to use builtin search box.
1293
+ */
1294
+ filterOptions: {
1295
+ filterText: "",
1296
+ useExternalFilter: false
1297
+ },
1298
+
1299
+ //Defining the height of the footer in pixels.
1300
+ footerRowHeight: 55,
1301
+
1302
+ // the template for the column menu and filter, including the button.
1303
+ footerTemplate: undefined,
1304
+
1305
+ //Initial fields to group data by. Array of field names, not displayName.
1306
+ groups: [],
1307
+
1308
+ // set the initial state of aggreagate grouping. "true" means they will be collapsed when grouping changes, "false" means they will be expanded by default.
1309
+ groupsCollapsedByDefault: true,
1310
+
1311
+ //The height of the header row in pixels.
1312
+ headerRowHeight: 30,
1313
+
1314
+ //Define a header row template for further customization. See github wiki for more details.
1315
+ headerRowTemplate: undefined,
1316
+
1317
+ /*Enables the use of jquery UI reaggable/droppable plugin. requires jqueryUI to work if enabled.
1318
+ Useful if you want drag + drop but your users insist on crappy browsers. */
1319
+ jqueryUIDraggable: false,
1320
+
1321
+ //Enable the use jqueryUIThemes
1322
+ jqueryUITheme: false,
1323
+
1324
+ //Prevent unselections when in single selection mode.
1325
+ keepLastSelected: true,
1326
+
1327
+ /*Maintains the column widths while resizing.
1328
+ Defaults to true when using *'s or undefined widths. Can be ovverriden by setting to false.*/
1329
+ maintainColumnRatios: undefined,
1330
+
1331
+ // the template for the column menu and filter, including the button.
1332
+ menuTemplate: undefined,
1333
+
1334
+ //Set this to false if you only want one item selected at a time
1335
+ multiSelect: true,
1336
+
1337
+ // pagingOptions -
1338
+ pagingOptions: {
1339
+ // pageSizes: list of available page sizes.
1340
+ pageSizes: [250, 500, 1000],
1341
+ //pageSize: currently selected page size.
1342
+ pageSize: 250,
1343
+ //currentPage: the uhm... current page.
1344
+ currentPage: 1
1345
+ },
1346
+
1347
+ //the selection checkbox is pinned to the left side of the viewport or not.
1348
+ pinSelectionCheckbox: false,
1349
+
1350
+ //Array of plugin functions to register in ng-grid
1351
+ plugins: [],
1352
+
1353
+ //User defined unique ID field that allows for better handling of selections and for server-side paging
1354
+ primaryKey: undefined,
1355
+
1356
+ //Row height of rows in grid.
1357
+ rowHeight: 30,
1358
+
1359
+ //Define a row template to customize output. See github wiki for more details.
1360
+ rowTemplate: undefined,
1361
+
1362
+ //all of the items selected in the grid. In single select mode there will only be one item in the array.
1363
+ selectedItems: [],
1364
+
1365
+ //Disable row selections by clicking on the row and only when the checkbox is clicked.
1366
+ selectWithCheckboxOnly: false,
1367
+
1368
+ /*Enables menu to choose which columns to display and group by.
1369
+ If both showColumnMenu and showFilter are false the menu button will not display.*/
1370
+ showColumnMenu: false,
1371
+
1372
+ /*Enables display of the filterbox in the column menu.
1373
+ If both showColumnMenu and showFilter are false the menu button will not display.*/
1374
+ showFilter: false,
1375
+
1376
+ //Show or hide the footer alltogether the footer is enabled by default
1377
+ showFooter: false,
1378
+
1379
+ //Show the dropzone for drag and drop grouping
1380
+ showGroupPanel: false,
1381
+
1382
+ //Row selection check boxes appear as the first column.
1383
+ showSelectionCheckbox: false,
1384
+
1385
+ /*Define a sortInfo object to specify a default sorting state.
1386
+ You can also observe this variable to utilize server-side sorting (see useExternalSorting).
1387
+ Syntax is sortinfo: { fields: ['fieldName1',' fieldName2'], direction: 'ASC'/'asc' || 'desc'/'DESC'}*/
1388
+ sortInfo: {fields: [], columns: [], directions: [] },
1389
+
1390
+ //Set the tab index of the Vieport.
1391
+ tabIndex: -1,
1392
+
1393
+ //totalServerItems: Total items are on the server.
1394
+ totalServerItems: 0,
1395
+
1396
+ /*Prevents the internal sorting from executing.
1397
+ The sortInfo object will be updated with the sorting information so you can handle sorting (see sortInfo)*/
1398
+ useExternalSorting: false,
1399
+
1400
+ /*i18n language support. choose from the installed or included languages, en, fr, sp, etc...*/
1401
+ i18n: 'en',
1402
+
1403
+ //the threshold in rows to force virtualization on
1404
+ virtualizationThreshold: 50
1405
+ },
1406
+ self = this;
1407
+ self.maxCanvasHt = 0;
1408
+ //self vars
1409
+ self.config = $.extend(defaults, window.ngGrid.config, options);
1410
+
1411
+ // override conflicting settings
1412
+ self.config.showSelectionCheckbox = (self.config.showSelectionCheckbox && self.config.enableColumnHeavyVirt === false);
1413
+ self.config.enablePinning = (self.config.enablePinning && self.config.enableColumnHeavyVirt === false);
1414
+ self.config.selectWithCheckboxOnly = (self.config.selectWithCheckboxOnly && self.config.showSelectionCheckbox !== false);
1415
+ self.config.pinSelectionCheckbox = self.config.enablePinning;
1416
+
1417
+ if (typeof options.columnDefs === "string") {
1418
+ self.config.columnDefs = $scope.$eval(options.columnDefs);
1419
+ }
1420
+ self.rowCache = [];
1421
+ self.rowMap = [];
1422
+ self.gridId = "ng" + $utils.newId();
1423
+ self.$root = null; //this is the root element that is passed in with the binding handler
1424
+ self.$groupPanel = null;
1425
+ self.$topPanel = null;
1426
+ self.$headerContainer = null;
1427
+ self.$headerScroller = null;
1428
+ self.$headers = null;
1429
+ self.$viewport = null;
1430
+ self.$canvas = null;
1431
+ self.rootDim = self.config.gridDim;
1432
+ self.data = [];
1433
+ self.lateBindColumns = false;
1434
+ self.filteredRows = [];
1435
+
1436
+ self.initTemplates = function() {
1437
+ var templates = ['rowTemplate', 'aggregateTemplate', 'headerRowTemplate', 'checkboxCellTemplate', 'checkboxHeaderTemplate', 'menuTemplate', 'footerTemplate'];
1438
+
1439
+ var promises = [];
1440
+ angular.forEach(templates, function(template) {
1441
+ promises.push( self.getTemplate(template) );
1442
+ });
1443
+
1444
+ return $q.all(promises);
1445
+ };
1446
+
1447
+ //Templates
1448
+ // test templates for urls and get the tempaltes via synchronous ajax calls
1449
+ self.getTemplate = function (key) {
1450
+ var t = self.config[key];
1451
+ var uKey = self.gridId + key + ".html";
1452
+ var p = $q.defer();
1453
+ if (t && !TEMPLATE_REGEXP.test(t)) {
1454
+ $http.get(t, {
1455
+ cache: $templateCache
1456
+ })
1457
+ .success(function(data){
1458
+ $templateCache.put(uKey, data);
1459
+ p.resolve();
1460
+ })
1461
+ .error(function(err){
1462
+ p.reject("Could not load template: " + t);
1463
+ });
1464
+ } else if (t) {
1465
+ $templateCache.put(uKey, t);
1466
+ p.resolve();
1467
+ } else {
1468
+ var dKey = key + ".html";
1469
+ $templateCache.put(uKey, $templateCache.get(dKey));
1470
+ p.resolve();
1471
+ }
1472
+
1473
+ return p.promise;
1474
+ };
1475
+
1476
+ if (typeof self.config.data === "object") {
1477
+ self.data = self.config.data; // we cannot watch for updates if you don't pass the string name
1478
+ }
1479
+ self.calcMaxCanvasHeight = function() {
1480
+ var calculatedHeight;
1481
+ if(self.config.groups.length > 0){
1482
+ calculatedHeight = self.rowFactory.parsedData.filter(function(e) {
1483
+ return !e[NG_HIDDEN];
1484
+ }).length * self.config.rowHeight;
1485
+ } else {
1486
+ calculatedHeight = self.filteredRows.length * self.config.rowHeight;
1487
+ }
1488
+ return calculatedHeight;
1489
+ };
1490
+ self.elementDims = {
1491
+ scrollW: 0,
1492
+ scrollH: 0,
1493
+ rowIndexCellW: 25,
1494
+ rowSelectedCellW: 25,
1495
+ rootMaxW: 0,
1496
+ rootMaxH: 0
1497
+ };
1498
+ //self funcs
1499
+ self.setRenderedRows = function (newRows) {
1500
+ $scope.renderedRows.length = newRows.length;
1501
+ for (var i = 0; i < newRows.length; i++) {
1502
+ if (!$scope.renderedRows[i] || (newRows[i].isAggRow || $scope.renderedRows[i].isAggRow)) {
1503
+ $scope.renderedRows[i] = newRows[i].copy();
1504
+ $scope.renderedRows[i].collapsed = newRows[i].collapsed;
1505
+ if (!newRows[i].isAggRow) {
1506
+ $scope.renderedRows[i].setVars(newRows[i]);
1507
+ }
1508
+ } else {
1509
+ $scope.renderedRows[i].setVars(newRows[i]);
1510
+ }
1511
+ $scope.renderedRows[i].rowIndex = newRows[i].rowIndex;
1512
+ $scope.renderedRows[i].offsetTop = newRows[i].offsetTop;
1513
+ $scope.renderedRows[i].selected = newRows[i].selected;
1514
+ newRows[i].renderedRowIndex = i;
1515
+ }
1516
+ self.refreshDomSizes();
1517
+ $scope.$emit('ngGridEventRows', newRows);
1518
+ };
1519
+ self.minRowsToRender = function() {
1520
+ var viewportH = $scope.viewportDimHeight() || 1;
1521
+ return Math.floor(viewportH / self.config.rowHeight);
1522
+ };
1523
+ self.refreshDomSizes = function() {
1524
+ var dim = new ngDimension();
1525
+ dim.outerWidth = self.elementDims.rootMaxW;
1526
+ dim.outerHeight = self.elementDims.rootMaxH;
1527
+ self.rootDim = dim;
1528
+ self.maxCanvasHt = self.calcMaxCanvasHeight();
1529
+ };
1530
+ self.buildColumnDefsFromData = function () {
1531
+ self.config.columnDefs = [];
1532
+ var item = self.data[0];
1533
+ if (!item) {
1534
+ self.lateBoundColumns = true;
1535
+ return;
1536
+ }
1537
+ $utils.forIn(item, function (prop, propName) {
1538
+ if (self.config.excludeProperties.indexOf(propName) === -1) {
1539
+ self.config.columnDefs.push({
1540
+ field: propName
1541
+ });
1542
+ }
1543
+ });
1544
+ };
1545
+ self.buildColumns = function() {
1546
+ var columnDefs = self.config.columnDefs,
1547
+ cols = [];
1548
+ if (!columnDefs) {
1549
+ self.buildColumnDefsFromData();
1550
+ columnDefs = self.config.columnDefs;
1551
+ }
1552
+ if (self.config.showSelectionCheckbox) {
1553
+ cols.push(new ngColumn({
1554
+ colDef: {
1555
+ field: '\u2714',
1556
+ width: self.elementDims.rowSelectedCellW,
1557
+ sortable: false,
1558
+ resizable: false,
1559
+ groupable: false,
1560
+ headerCellTemplate: $templateCache.get($scope.gridId + 'checkboxHeaderTemplate.html'),
1561
+ cellTemplate: $templateCache.get($scope.gridId + 'checkboxCellTemplate.html'),
1562
+ pinned: self.config.pinSelectionCheckbox
1563
+ },
1564
+ index: 0,
1565
+ headerRowHeight: self.config.headerRowHeight,
1566
+ sortCallback: self.sortData,
1567
+ resizeOnDataCallback: self.resizeOnData,
1568
+ enableResize: self.config.enableColumnResize,
1569
+ enableSort: self.config.enableSorting,
1570
+ enablePinning: self.config.enablePinning
1571
+ }, $scope, self, domUtilityService, $templateCache, $utils));
1572
+ }
1573
+ if (columnDefs.length > 0) {
1574
+ var checkboxOffset = self.config.showSelectionCheckbox ? 1 : 0;
1575
+ var groupOffset = $scope.configGroups.length;
1576
+ $scope.configGroups.length = 0;
1577
+ angular.forEach(columnDefs, function(colDef, i) {
1578
+ i += checkboxOffset;
1579
+ var column = new ngColumn({
1580
+ colDef: colDef,
1581
+ index: i + groupOffset,
1582
+ originalIndex: i,
1583
+ headerRowHeight: self.config.headerRowHeight,
1584
+ sortCallback: self.sortData,
1585
+ resizeOnDataCallback: self.resizeOnData,
1586
+ enableResize: self.config.enableColumnResize,
1587
+ enableSort: self.config.enableSorting,
1588
+ enablePinning: self.config.enablePinning,
1589
+ enableCellEdit: self.config.enableCellEdit || self.config.enableCellEditOnFocus
1590
+ }, $scope, self, domUtilityService, $templateCache, $utils);
1591
+ var indx = self.config.groups.indexOf(colDef.field);
1592
+ if (indx !== -1) {
1593
+ column.isGroupedBy = true;
1594
+ $scope.configGroups.splice(indx, 0, column);
1595
+ column.groupIndex = $scope.configGroups.length;
1596
+ }
1597
+ cols.push(column);
1598
+ });
1599
+ $scope.columns = cols;
1600
+ if ($scope.configGroups.length > 0) {
1601
+ self.rowFactory.getGrouping($scope.configGroups);
1602
+ }
1603
+ }
1604
+ };
1605
+ self.configureColumnWidths = function() {
1606
+ var asterisksArray = [],
1607
+ percentArray = [],
1608
+ asteriskNum = 0,
1609
+ totalWidth = 0;
1610
+
1611
+ // When rearranging columns, their index in $scope.columns will no longer match the original column order from columnDefs causing
1612
+ // their width config to be out of sync. We can use "originalIndex" on the ngColumns to get hold of the correct setup from columnDefs, but to
1613
+ // avoid O(n) lookups in $scope.columns per column we setup a map.
1614
+ var indexMap = {};
1615
+ // Build a map of columnDefs column indices -> ngColumn indices (via the "originalIndex" property on ngColumns).
1616
+ angular.forEach($scope.columns, function(ngCol, i) {
1617
+ // Disregard columns created by grouping (the grouping columns don't match a column from columnDefs)
1618
+ if (!$utils.isNullOrUndefined(ngCol.originalIndex)) {
1619
+ var origIndex = ngCol.originalIndex;
1620
+ if (self.config.showSelectionCheckbox) {
1621
+ //if visible, takes up 25 pixels
1622
+ if(ngCol.originalIndex === 0 && ngCol.visible){
1623
+ totalWidth += 25;
1624
+ }
1625
+ // The originalIndex will be offset 1 when including the selection column
1626
+ origIndex--;
1627
+ }
1628
+ indexMap[origIndex] = i;
1629
+ }
1630
+ });
1631
+
1632
+ angular.forEach(self.config.columnDefs, function(colDef, i) {
1633
+ // Get the ngColumn that matches the current column from columnDefs
1634
+ var ngColumn = $scope.columns[indexMap[i]];
1635
+
1636
+ colDef.index = i;
1637
+
1638
+ var isPercent = false, t;
1639
+ //if width is not defined, set it to a single star
1640
+ if ($utils.isNullOrUndefined(colDef.width)) {
1641
+ colDef.width = "*";
1642
+ } else { // get column width
1643
+ isPercent = isNaN(colDef.width) ? $utils.endsWith(colDef.width, "%") : false;
1644
+ t = isPercent ? colDef.width : parseInt(colDef.width, 10);
1645
+ }
1646
+
1647
+ // check if it is a number
1648
+ if (isNaN(t) && !$scope.hasUserChangedGridColumnWidths) {
1649
+ t = colDef.width;
1650
+ // figure out if the width is defined or if we need to calculate it
1651
+ if (t === 'auto') { // set it for now until we have data and subscribe when it changes so we can set the width.
1652
+ ngColumn.width = ngColumn.minWidth;
1653
+ totalWidth += ngColumn.width;
1654
+ var temp = ngColumn;
1655
+
1656
+ $scope.$on("ngGridEventData", function () {
1657
+ self.resizeOnData(temp);
1658
+ });
1659
+ return;
1660
+ } else if (t.indexOf("*") !== -1) { // we need to save it until the end to do the calulations on the remaining width.
1661
+ if (ngColumn.visible !== false) {
1662
+ asteriskNum += t.length;
1663
+ }
1664
+ asterisksArray.push(colDef);
1665
+ return;
1666
+ } else if (isPercent) { // If the width is a percentage, save it until the very last.
1667
+ percentArray.push(colDef);
1668
+ return;
1669
+ } else { // we can't parse the width so lets throw an error.
1670
+ throw "unable to parse column width, use percentage (\"10%\",\"20%\", etc...) or \"*\" to use remaining width of grid";
1671
+ }
1672
+ } else if (ngColumn.visible !== false) {
1673
+ totalWidth += ngColumn.width = parseInt(ngColumn.width, 10);
1674
+ }
1675
+ });
1676
+
1677
+ // Now we check if we saved any percentage columns for calculating last
1678
+ if (percentArray.length > 0) {
1679
+ //If they specificy for maintain column ratios to be false in grid config, then it will remain false. If not specifiied or true, will be true.
1680
+ self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false;
1681
+ // If any columns with % widths have been hidden, then let other % based columns use their width
1682
+ var percentWidth = 0; // The total % value for all columns setting their width using % (will e.g. be 40 for 2 columns with 20% each)
1683
+ var hiddenPercent = 0; // The total % value for all columns setting their width using %, but which have been hidden
1684
+ angular.forEach(percentArray, function(colDef) {
1685
+ // Get the ngColumn that matches the current column from columnDefs
1686
+ var ngColumn = $scope.columns[indexMap[colDef.index]];
1687
+ var t = colDef.width;
1688
+ var percent = parseInt(t.slice(0, -1), 10) / 100;
1689
+ percentWidth += percent;
1690
+
1691
+ if (!ngColumn.visible) {
1692
+ hiddenPercent += percent;
1693
+ }
1694
+ });
1695
+ var percentWidthUsed = percentWidth - hiddenPercent;
1696
+
1697
+ // do the math
1698
+ angular.forEach(percentArray, function(colDef) {
1699
+ // Get the ngColumn that matches the current column from columnDefs
1700
+ var ngColumn = $scope.columns[indexMap[colDef.index]];
1701
+
1702
+ // Calc the % relative to the amount of % reserved for the visible columns (that use % based widths)
1703
+ var t = colDef.width;
1704
+ var percent = parseInt(t.slice(0, -1), 10) / 100;
1705
+ if (hiddenPercent > 0) {
1706
+ percent = percent / percentWidthUsed;
1707
+ }
1708
+ else {
1709
+ percent = percent / percentWidth;
1710
+ }
1711
+
1712
+ var pixelsForPercentBasedWidth = self.rootDim.outerWidth * percentWidth;
1713
+ ngColumn.width = Math.floor(pixelsForPercentBasedWidth * percent);
1714
+ totalWidth += ngColumn.width;
1715
+ });
1716
+ }
1717
+
1718
+ // check if we saved any asterisk columns for calculating later
1719
+ if (asterisksArray.length > 0) {
1720
+ //If they specificy for maintain column ratios to be false in grid config, then it will remain false. If not specifiied or true, will be true.
1721
+ self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false;
1722
+ // get the remaining width
1723
+ var remainingWidth = self.rootDim.outerWidth - totalWidth;
1724
+ // are we overflowing vertically?
1725
+ if (self.maxCanvasHt > $scope.viewportDimHeight()) {
1726
+ //compensate for scrollbar
1727
+ remainingWidth -= domUtilityService.ScrollW;
1728
+ }
1729
+ // calculate the weight of each asterisk rounded down
1730
+ var asteriskVal = Math.floor(remainingWidth / asteriskNum);
1731
+
1732
+ // set the width of each column based on the number of stars
1733
+ angular.forEach(asterisksArray, function(colDef, i) {
1734
+ // Get the ngColumn that matches the current column from columnDefs
1735
+ var ngColumn = $scope.columns[indexMap[colDef.index]];
1736
+ ngColumn.width = asteriskVal * colDef.width.length;
1737
+ if (ngColumn.visible !== false) {
1738
+ totalWidth += ngColumn.width;
1739
+ }
1740
+
1741
+ var isLast = (i === (asterisksArray.length - 1));
1742
+ //if last asterisk and doesn't fill width of grid, add the difference
1743
+ if(isLast && totalWidth < self.rootDim.outerWidth){
1744
+ var gridWidthDifference = self.rootDim.outerWidth - totalWidth;
1745
+ if(self.maxCanvasHt > $scope.viewportDimHeight()){
1746
+ gridWidthDifference -= domUtilityService.ScrollW;
1747
+ }
1748
+ ngColumn.width += gridWidthDifference;
1749
+ }
1750
+ });
1751
+ }
1752
+ };
1753
+ self.init = function() {
1754
+ return self.initTemplates().then(function(){
1755
+ //factories and services
1756
+ $scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse);
1757
+ $scope.domAccessProvider = new ngDomAccessProvider(self);
1758
+ self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils);
1759
+ self.searchProvider = new ngSearchProvider($scope, self, $filter);
1760
+ self.styleProvider = new ngStyleProvider($scope, self);
1761
+ $scope.$watch('configGroups', function(a) {
1762
+ var tempArr = [];
1763
+ angular.forEach(a, function(item) {
1764
+ tempArr.push(item.field || item);
1765
+ });
1766
+ self.config.groups = tempArr;
1767
+ self.rowFactory.filteredRowsChanged();
1768
+ $scope.$emit('ngGridEventGroups', a);
1769
+ }, true);
1770
+ $scope.$watch('columns', function (a) {
1771
+ if(!$scope.isColumnResizing){
1772
+ domUtilityService.RebuildGrid($scope, self);
1773
+ }
1774
+ $scope.$emit('ngGridEventColumns', a);
1775
+ }, true);
1776
+ $scope.$watch(function() {
1777
+ return options.i18n;
1778
+ }, function(newLang) {
1779
+ $utils.seti18n($scope, newLang);
1780
+ });
1781
+ self.maxCanvasHt = self.calcMaxCanvasHeight();
1782
+
1783
+ if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) {
1784
+ $scope.$watch(function() {
1785
+ return self.config.sortInfo;
1786
+ }, function(sortInfo){
1787
+ if (!sortService.isSorting) {
1788
+ self.getColsFromFields();
1789
+ self.sortActual();
1790
+ self.searchProvider.evalFilter();
1791
+ $scope.$emit('ngGridEventSorted', self.config.sortInfo);
1792
+ }
1793
+ },true);
1794
+ }
1795
+ });
1796
+
1797
+ // var p = $q.defer();
1798
+ // p.resolve();
1799
+ // return p.promise;
1800
+ };
1801
+
1802
+ self.resizeOnData = function(col) {
1803
+ // we calculate the longest data.
1804
+ var longest = col.minWidth;
1805
+ var arr = $utils.getElementsByClassName('col' + col.index);
1806
+ angular.forEach(arr, function(elem, index) {
1807
+ var i;
1808
+ if (index === 0) {
1809
+ var kgHeaderText = $(elem).find('.ngHeaderText');
1810
+ i = $utils.visualLength(kgHeaderText) + 10; // +10 some margin
1811
+ } else {
1812
+ var ngCellText = $(elem).find('.ngCellText');
1813
+ i = $utils.visualLength(ngCellText) + 10; // +10 some margin
1814
+ }
1815
+ if (i > longest) {
1816
+ longest = i;
1817
+ }
1818
+ });
1819
+ col.width = col.longest = Math.min(col.maxWidth, longest + 7); // + 7 px to make it look decent.
1820
+ domUtilityService.BuildStyles($scope, self, true);
1821
+ };
1822
+ self.lastSortedColumns = [];
1823
+ self.sortData = function(col, evt) {
1824
+ if (evt && evt.shiftKey && self.config.sortInfo) {
1825
+ var indx = self.config.sortInfo.columns.indexOf(col);
1826
+ if (indx === -1) {
1827
+ if (self.config.sortInfo.columns.length === 1) {
1828
+ self.config.sortInfo.columns[0].sortPriority = 1;
1829
+ }
1830
+ self.config.sortInfo.columns.push(col);
1831
+ col.sortPriority = self.config.sortInfo.columns.length;
1832
+ self.config.sortInfo.fields.push(col.field);
1833
+ self.config.sortInfo.directions.push(col.sortDirection);
1834
+ self.lastSortedColumns.push(col);
1835
+ } else {
1836
+ self.config.sortInfo.directions[indx] = col.sortDirection;
1837
+ }
1838
+ } else {
1839
+ var isArr = $.isArray(col);
1840
+ self.config.sortInfo.columns.length = 0;
1841
+ self.config.sortInfo.fields.length = 0;
1842
+ self.config.sortInfo.directions.length = 0;
1843
+ var push = function (c) {
1844
+ self.config.sortInfo.columns.push(c);
1845
+ self.config.sortInfo.fields.push(c.field);
1846
+ self.config.sortInfo.directions.push(c.sortDirection);
1847
+ self.lastSortedColumns.push(c);
1848
+ };
1849
+ if (isArr) {
1850
+ self.clearSortingData();
1851
+ angular.forEach(col, function (c, i) {
1852
+ c.sortPriority = i + 1;
1853
+ push(c);
1854
+ });
1855
+ } else {
1856
+ self.clearSortingData(col);
1857
+ col.sortPriority = undefined;
1858
+ push(col);
1859
+ }
1860
+ }
1861
+ self.sortActual();
1862
+ self.searchProvider.evalFilter();
1863
+ $scope.$emit('ngGridEventSorted', self.config.sortInfo);
1864
+ };
1865
+ self.getColsFromFields = function() {
1866
+ if (self.config.sortInfo.columns) {
1867
+ self.config.sortInfo.columns.length = 0;
1868
+ } else {
1869
+ self.config.sortInfo.columns = [];
1870
+ }
1871
+ angular.forEach($scope.columns, function(c) {
1872
+ var i = self.config.sortInfo.fields.indexOf(c.field);
1873
+ if (i !== -1) {
1874
+ c.sortDirection = self.config.sortInfo.directions[i] || 'asc';
1875
+ self.config.sortInfo.columns[i] = c;
1876
+ }
1877
+ return false;
1878
+ });
1879
+ };
1880
+ self.sortActual = function() {
1881
+ if (!self.config.useExternalSorting) {
1882
+ var tempData = self.data.slice(0);
1883
+ angular.forEach(tempData, function(item, i) {
1884
+ var e = self.rowMap[i];
1885
+ if (e !== undefined) {
1886
+ var v = self.rowCache[e];
1887
+ if (v !== undefined) {
1888
+ item.preSortSelected = v.selected;
1889
+ item.preSortIndex = i;
1890
+ }
1891
+ }
1892
+ });
1893
+ sortService.Sort(self.config.sortInfo, tempData);
1894
+ angular.forEach(tempData, function(item, i) {
1895
+ self.rowCache[i].entity = item;
1896
+ self.rowCache[i].selected = item.preSortSelected;
1897
+ self.rowMap[item.preSortIndex] = i;
1898
+ delete item.preSortSelected;
1899
+ delete item.preSortIndex;
1900
+ });
1901
+ }
1902
+ };
1903
+
1904
+ self.clearSortingData = function (col) {
1905
+ if (!col) {
1906
+ angular.forEach(self.lastSortedColumns, function (c) {
1907
+ c.sortDirection = "";
1908
+ c.sortPriority = null;
1909
+ });
1910
+ self.lastSortedColumns = [];
1911
+ } else {
1912
+ angular.forEach(self.lastSortedColumns, function (c) {
1913
+ if (col.index !== c.index) {
1914
+ c.sortDirection = "";
1915
+ c.sortPriority = null;
1916
+ }
1917
+ });
1918
+ self.lastSortedColumns[0] = col;
1919
+ self.lastSortedColumns.length = 1;
1920
+ }
1921
+ };
1922
+ self.fixColumnIndexes = function() {
1923
+ //fix column indexes
1924
+ for (var i = 0; i < $scope.columns.length; i++) {
1925
+ $scope.columns[i].index = i;
1926
+ }
1927
+ };
1928
+ self.fixGroupIndexes = function() {
1929
+ angular.forEach($scope.configGroups, function(item, i) {
1930
+ item.groupIndex = i + 1;
1931
+ });
1932
+ };
1933
+ //$scope vars
1934
+ $scope.elementsNeedMeasuring = true;
1935
+ $scope.columns = [];
1936
+ $scope.renderedRows = [];
1937
+ $scope.renderedColumns = [];
1938
+ $scope.headerRow = null;
1939
+ $scope.rowHeight = self.config.rowHeight;
1940
+ $scope.jqueryUITheme = self.config.jqueryUITheme;
1941
+ $scope.showSelectionCheckbox = self.config.showSelectionCheckbox;
1942
+ $scope.enableCellSelection = self.config.enableCellSelection;
1943
+ $scope.enableCellEditOnFocus = self.config.enableCellEditOnFocus;
1944
+ $scope.footer = null;
1945
+ $scope.selectedItems = self.config.selectedItems;
1946
+ $scope.multiSelect = self.config.multiSelect;
1947
+ $scope.showFooter = self.config.showFooter;
1948
+ $scope.footerRowHeight = $scope.showFooter ? self.config.footerRowHeight : 0;
1949
+ $scope.showColumnMenu = self.config.showColumnMenu;
1950
+ $scope.showMenu = false;
1951
+ $scope.configGroups = [];
1952
+ $scope.gridId = self.gridId;
1953
+ //Paging
1954
+ $scope.enablePaging = self.config.enablePaging;
1955
+ $scope.pagingOptions = self.config.pagingOptions;
1956
+
1957
+ //i18n support
1958
+ $scope.i18n = {};
1959
+ $utils.seti18n($scope, self.config.i18n);
1960
+ $scope.adjustScrollLeft = function (scrollLeft) {
1961
+ var colwidths = 0,
1962
+ totalLeft = 0,
1963
+ x = $scope.columns.length,
1964
+ newCols = [],
1965
+ dcv = !self.config.enableColumnHeavyVirt;
1966
+ var r = 0;
1967
+ var addCol = function (c) {
1968
+ if (dcv) {
1969
+ newCols.push(c);
1970
+ } else {
1971
+ if (!$scope.renderedColumns[r]) {
1972
+ $scope.renderedColumns[r] = c.copy();
1973
+ } else {
1974
+ $scope.renderedColumns[r].setVars(c);
1975
+ }
1976
+ }
1977
+ r++;
1978
+ };
1979
+ for (var i = 0; i < x; i++) {
1980
+ var col = $scope.columns[i];
1981
+ if (col.visible !== false) {
1982
+ var w = col.width + colwidths;
1983
+ if (col.pinned) {
1984
+ addCol(col);
1985
+ var newLeft = i > 0 ? (scrollLeft + totalLeft) : scrollLeft;
1986
+ domUtilityService.setColLeft(col, newLeft, self);
1987
+ totalLeft += col.width;
1988
+ } else {
1989
+ if (w >= scrollLeft) {
1990
+ if (colwidths <= scrollLeft + self.rootDim.outerWidth) {
1991
+ addCol(col);
1992
+ }
1993
+ }
1994
+ }
1995
+ colwidths += col.width;
1996
+ }
1997
+ }
1998
+ if (dcv) {
1999
+ $scope.renderedColumns = newCols;
2000
+ }
2001
+ };
2002
+ self.prevScrollTop = 0;
2003
+ self.prevScrollIndex = 0;
2004
+ $scope.adjustScrollTop = function(scrollTop, force) {
2005
+ if (self.prevScrollTop === scrollTop && !force) {
2006
+ return;
2007
+ }
2008
+ if (scrollTop > 0 && self.$viewport[0].scrollHeight - scrollTop <= self.$viewport.outerHeight()) {
2009
+ $scope.$emit('ngGridEventScroll');
2010
+ }
2011
+ var rowIndex = Math.floor(scrollTop / self.config.rowHeight);
2012
+ var newRange;
2013
+ if (self.filteredRows.length > self.config.virtualizationThreshold) {
2014
+ // Have we hit the threshold going down?
2015
+ if (self.prevScrollTop < scrollTop && rowIndex < self.prevScrollIndex + SCROLL_THRESHOLD) {
2016
+ return;
2017
+ }
2018
+ //Have we hit the threshold going up?
2019
+ if (self.prevScrollTop > scrollTop && rowIndex > self.prevScrollIndex - SCROLL_THRESHOLD) {
2020
+ return;
2021
+ }
2022
+ newRange = new ngRange(Math.max(0, rowIndex - EXCESS_ROWS), rowIndex + self.minRowsToRender() + EXCESS_ROWS);
2023
+ } else {
2024
+ var maxLen = $scope.configGroups.length > 0 ? self.rowFactory.parsedData.length : self.data.length;
2025
+ newRange = new ngRange(0, Math.max(maxLen, self.minRowsToRender() + EXCESS_ROWS));
2026
+ }
2027
+ self.prevScrollTop = scrollTop;
2028
+ self.rowFactory.UpdateViewableRange(newRange);
2029
+ self.prevScrollIndex = rowIndex;
2030
+ };
2031
+
2032
+ //scope funcs
2033
+ $scope.toggleShowMenu = function() {
2034
+ $scope.showMenu = !$scope.showMenu;
2035
+ };
2036
+ $scope.toggleSelectAll = function(state, selectOnlyVisible) {
2037
+ $scope.selectionProvider.toggleSelectAll(state, false, selectOnlyVisible);
2038
+ };
2039
+ $scope.totalFilteredItemsLength = function() {
2040
+ return self.filteredRows.length;
2041
+ };
2042
+ $scope.showGroupPanel = function() {
2043
+ return self.config.showGroupPanel;
2044
+ };
2045
+ $scope.topPanelHeight = function() {
2046
+ return self.config.showGroupPanel === true ? self.config.headerRowHeight + 32 : self.config.headerRowHeight;
2047
+ };
2048
+
2049
+ $scope.viewportDimHeight = function() {
2050
+ return Math.max(0, self.rootDim.outerHeight - $scope.topPanelHeight() - $scope.footerRowHeight - 2);
2051
+ };
2052
+ $scope.groupBy = function (col) {
2053
+ if (self.data.length < 1 || !col.groupable || !col.field) {
2054
+ return;
2055
+ }
2056
+ //first sort the column
2057
+ if (!col.sortDirection) {
2058
+ col.sort({ shiftKey: $scope.configGroups.length > 0 ? true : false });
2059
+ }
2060
+
2061
+ var indx = $scope.configGroups.indexOf(col);
2062
+ if (indx === -1) {
2063
+ col.isGroupedBy = true;
2064
+ $scope.configGroups.push(col);
2065
+ col.groupIndex = $scope.configGroups.length;
2066
+ } else {
2067
+ $scope.removeGroup(indx);
2068
+ }
2069
+ self.$viewport.scrollTop(0);
2070
+ domUtilityService.digest($scope);
2071
+ };
2072
+ $scope.removeGroup = function(index) {
2073
+ var col = $scope.columns.filter(function(item) {
2074
+ return item.groupIndex === (index + 1);
2075
+ })[0];
2076
+ col.isGroupedBy = false;
2077
+ col.groupIndex = 0;
2078
+ if ($scope.columns[index].isAggCol) {
2079
+ $scope.columns.splice(index, 1);
2080
+ $scope.configGroups.splice(index, 1);
2081
+ self.fixGroupIndexes();
2082
+ }
2083
+ if ($scope.configGroups.length === 0) {
2084
+ self.fixColumnIndexes();
2085
+ domUtilityService.digest($scope);
2086
+ }
2087
+ $scope.adjustScrollLeft(0);
2088
+ };
2089
+ $scope.togglePin = function (col) {
2090
+ var indexFrom = col.index;
2091
+ var indexTo = 0;
2092
+ for (var i = 0; i < $scope.columns.length; i++) {
2093
+ if (!$scope.columns[i].pinned) {
2094
+ break;
2095
+ }
2096
+ indexTo++;
2097
+ }
2098
+ if (col.pinned) {
2099
+ indexTo = Math.max(col.originalIndex, indexTo - 1);
2100
+ }
2101
+ col.pinned = !col.pinned;
2102
+ // Splice the columns
2103
+ $scope.columns.splice(indexFrom, 1);
2104
+ $scope.columns.splice(indexTo, 0, col);
2105
+ self.fixColumnIndexes();
2106
+ // Finally, rebuild the CSS styles.
2107
+ domUtilityService.BuildStyles($scope, self, true);
2108
+ self.$viewport.scrollLeft(self.$viewport.scrollLeft() - col.width);
2109
+ };
2110
+ $scope.totalRowWidth = function() {
2111
+ var totalWidth = 0,
2112
+ cols = $scope.columns;
2113
+ for (var i = 0; i < cols.length; i++) {
2114
+ if (cols[i].visible !== false) {
2115
+ totalWidth += cols[i].width;
2116
+ }
2117
+ }
2118
+ return totalWidth;
2119
+ };
2120
+ $scope.headerScrollerDim = function() {
2121
+ var viewportH = $scope.viewportDimHeight(),
2122
+ maxHeight = self.maxCanvasHt,
2123
+ vScrollBarIsOpen = (maxHeight > viewportH),
2124
+ newDim = new ngDimension();
2125
+
2126
+ newDim.autoFitHeight = true;
2127
+ newDim.outerWidth = $scope.totalRowWidth();
2128
+ if (vScrollBarIsOpen) {
2129
+ newDim.outerWidth += self.elementDims.scrollW;
2130
+ } else if ((maxHeight - viewportH) <= self.elementDims.scrollH) { //if the horizontal scroll is open it forces the viewport to be smaller
2131
+ newDim.outerWidth += self.elementDims.scrollW;
2132
+ }
2133
+ return newDim;
2134
+ };
2135
+ };
2136
+
2137
+ var ngRange = function (top, bottom) {
2138
+ this.topRow = top;
2139
+ this.bottomRow = bottom;
2140
+ };
2141
+ var ngRow = function (entity, config, selectionProvider, rowIndex, $utils) {
2142
+ this.entity = entity;
2143
+ this.config = config;
2144
+ this.selectionProvider = selectionProvider;
2145
+ this.rowIndex = rowIndex;
2146
+ this.utils = $utils;
2147
+ this.selected = selectionProvider.getSelection(entity);
2148
+ this.cursor = this.config.enableRowSelection ? 'pointer' : 'default';
2149
+ this.beforeSelectionChange = config.beforeSelectionChangeCallback;
2150
+ this.afterSelectionChange = config.afterSelectionChangeCallback;
2151
+ this.offsetTop = this.rowIndex * config.rowHeight;
2152
+ this.rowDisplayIndex = 0;
2153
+ };
2154
+
2155
+ ngRow.prototype.setSelection = function (isSelected) {
2156
+ this.selectionProvider.setSelection(this, isSelected);
2157
+ this.selectionProvider.lastClickedRow = this;
2158
+ };
2159
+ ngRow.prototype.continueSelection = function (event) {
2160
+ this.selectionProvider.ChangeSelection(this, event);
2161
+ };
2162
+ ngRow.prototype.ensureEntity = function (expected) {
2163
+ if (this.entity !== expected) {
2164
+ // Update the entity and determine our selected property
2165
+ this.entity = expected;
2166
+ this.selected = this.selectionProvider.getSelection(this.entity);
2167
+ }
2168
+ };
2169
+ ngRow.prototype.toggleSelected = function (event) {
2170
+ if (!this.config.enableRowSelection && !this.config.enableCellSelection) {
2171
+ return true;
2172
+ }
2173
+ var element = event.target || event;
2174
+ //check and make sure its not the bubbling up of our checked 'click' event
2175
+ if (element.type === "checkbox" && element.parentElement.className !== "ngSelectionCell ng-scope") {
2176
+ return true;
2177
+ }
2178
+ if (this.config.selectWithCheckboxOnly && element.type !== "checkbox") {
2179
+ this.selectionProvider.lastClickedRow = this;
2180
+ return true;
2181
+ }
2182
+ if (this.beforeSelectionChange(this, event)) {
2183
+ this.continueSelection(event);
2184
+ }
2185
+ return false;
2186
+ };
2187
+ ngRow.prototype.alternatingRowClass = function () {
2188
+ var isEven = (this.rowIndex % 2) === 0;
2189
+ var classes = {
2190
+ 'ngRow' : true,
2191
+ 'selected': this.selected,
2192
+ 'even': isEven,
2193
+ 'odd': !isEven,
2194
+ 'ui-state-default': this.config.jqueryUITheme && isEven,
2195
+ 'ui-state-active': this.config.jqueryUITheme && !isEven
2196
+ };
2197
+ return classes;
2198
+ };
2199
+ ngRow.prototype.getProperty = function (path) {
2200
+ return this.utils.evalProperty(this.entity, path);
2201
+ };
2202
+ ngRow.prototype.copy = function () {
2203
+ this.clone = new ngRow(this.entity, this.config, this.selectionProvider, this.rowIndex, this.utils);
2204
+ this.clone.isClone = true;
2205
+ this.clone.elm = this.elm;
2206
+ this.clone.orig = this;
2207
+ return this.clone;
2208
+ };
2209
+ ngRow.prototype.setVars = function (fromRow) {
2210
+ fromRow.clone = this;
2211
+ this.entity = fromRow.entity;
2212
+ this.selected = fromRow.selected;
2213
+ this.orig = fromRow;
2214
+ };
2215
+ var ngRowFactory = function (grid, $scope, domUtilityService, $templateCache, $utils) {
2216
+ var self = this;
2217
+ // we cache rows when they are built, and then blow the cache away when sorting
2218
+ self.aggCache = {};
2219
+ self.parentCache = []; // Used for grouping and is cleared each time groups are calulated.
2220
+ self.dataChanged = true;
2221
+ self.parsedData = [];
2222
+ self.rowConfig = {};
2223
+ self.selectionProvider = $scope.selectionProvider;
2224
+ self.rowHeight = 30;
2225
+ self.numberOfAggregates = 0;
2226
+ self.groupedData = undefined;
2227
+ self.rowHeight = grid.config.rowHeight;
2228
+ self.rowConfig = {
2229
+ enableRowSelection: grid.config.enableRowSelection,
2230
+ rowClasses: grid.config.rowClasses,
2231
+ selectedItems: $scope.selectedItems,
2232
+ selectWithCheckboxOnly: grid.config.selectWithCheckboxOnly,
2233
+ beforeSelectionChangeCallback: grid.config.beforeSelectionChange,
2234
+ afterSelectionChangeCallback: grid.config.afterSelectionChange,
2235
+ jqueryUITheme: grid.config.jqueryUITheme,
2236
+ enableCellSelection: grid.config.enableCellSelection,
2237
+ rowHeight: grid.config.rowHeight
2238
+ };
2239
+
2240
+ self.renderedRange = new ngRange(0, grid.minRowsToRender() + EXCESS_ROWS);
2241
+
2242
+ // @entity - the data item
2243
+ // @rowIndex - the index of the row
2244
+ self.buildEntityRow = function(entity, rowIndex) {
2245
+ // build the row
2246
+ return new ngRow(entity, self.rowConfig, self.selectionProvider, rowIndex, $utils);
2247
+ };
2248
+
2249
+ self.buildAggregateRow = function(aggEntity, rowIndex) {
2250
+ var agg = self.aggCache[aggEntity.aggIndex]; // first check to see if we've already built it
2251
+ if (!agg) {
2252
+ // build the row
2253
+ agg = new ngAggregate(aggEntity, self, self.rowConfig.rowHeight, grid.config.groupsCollapsedByDefault);
2254
+ self.aggCache[aggEntity.aggIndex] = agg;
2255
+ }
2256
+ agg.rowIndex = rowIndex;
2257
+ agg.offsetTop = rowIndex * self.rowConfig.rowHeight;
2258
+ return agg;
2259
+ };
2260
+ self.UpdateViewableRange = function(newRange) {
2261
+ self.renderedRange = newRange;
2262
+ self.renderedChange();
2263
+ };
2264
+ self.filteredRowsChanged = function() {
2265
+ // check for latebound autogenerated columns
2266
+ if (grid.lateBoundColumns && grid.filteredRows.length > 0) {
2267
+ grid.config.columnDefs = undefined;
2268
+ grid.buildColumns();
2269
+ grid.lateBoundColumns = false;
2270
+ $scope.$evalAsync(function() {
2271
+ $scope.adjustScrollLeft(0);
2272
+ });
2273
+ }
2274
+ self.dataChanged = true;
2275
+ if (grid.config.groups.length > 0) {
2276
+ self.getGrouping(grid.config.groups);
2277
+ }
2278
+ self.UpdateViewableRange(self.renderedRange);
2279
+ };
2280
+
2281
+ self.renderedChange = function() {
2282
+ if (!self.groupedData || grid.config.groups.length < 1) {
2283
+ self.renderedChangeNoGroups();
2284
+ grid.refreshDomSizes();
2285
+ return;
2286
+ }
2287
+ self.wasGrouped = true;
2288
+ self.parentCache = [];
2289
+ var x = 0;
2290
+ var temp = self.parsedData.filter(function (e) {
2291
+ if (e.isAggRow) {
2292
+ if (e.parent && e.parent.collapsed) {
2293
+ return false;
2294
+ }
2295
+ return true;
2296
+ }
2297
+ if (!e[NG_HIDDEN]) {
2298
+ e.rowIndex = x++;
2299
+ }
2300
+ return !e[NG_HIDDEN];
2301
+ });
2302
+ self.totalRows = temp.length;
2303
+ var rowArr = [];
2304
+ for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) {
2305
+ if (temp[i]) {
2306
+ temp[i].offsetTop = i * grid.config.rowHeight;
2307
+ rowArr.push(temp[i]);
2308
+ }
2309
+ }
2310
+ grid.setRenderedRows(rowArr);
2311
+ };
2312
+
2313
+ self.renderedChangeNoGroups = function () {
2314
+ var rowArr = [];
2315
+ for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) {
2316
+ if (grid.filteredRows[i]) {
2317
+ grid.filteredRows[i].rowIndex = i;
2318
+ grid.filteredRows[i].offsetTop = i * grid.config.rowHeight;
2319
+ rowArr.push(grid.filteredRows[i]);
2320
+ }
2321
+ }
2322
+ grid.setRenderedRows(rowArr);
2323
+ };
2324
+
2325
+ self.fixRowCache = function () {
2326
+ var newLen = grid.data.length;
2327
+ var diff = newLen - grid.rowCache.length;
2328
+ if (diff < 0) {
2329
+ grid.rowCache.length = grid.rowMap.length = newLen;
2330
+ } else {
2331
+ for (var i = grid.rowCache.length; i < newLen; i++) {
2332
+ grid.rowCache[i] = grid.rowFactory.buildEntityRow(grid.data[i], i);
2333
+ }
2334
+ }
2335
+ };
2336
+
2337
+ //magical recursion. it works. I swear it. I figured it out in the shower one day.
2338
+ self.parseGroupData = function(g) {
2339
+ if (g.values) {
2340
+ for (var x = 0; x < g.values.length; x++){
2341
+ // get the last parent in the array because that's where our children want to be
2342
+ self.parentCache[self.parentCache.length - 1].children.push(g.values[x]);
2343
+ //add the row to our return array
2344
+ self.parsedData.push(g.values[x]);
2345
+ }
2346
+ } else {
2347
+ for (var prop in g) {
2348
+ // exclude the meta properties.
2349
+ if (prop === NG_FIELD || prop === NG_DEPTH || prop === NG_COLUMN) {
2350
+ continue;
2351
+ } else if (g.hasOwnProperty(prop)) {
2352
+ //build the aggregate row
2353
+ var agg = self.buildAggregateRow({
2354
+ gField: g[NG_FIELD],
2355
+ gLabel: prop,
2356
+ gDepth: g[NG_DEPTH],
2357
+ isAggRow: true,
2358
+ '_ng_hidden_': false,
2359
+ children: [],
2360
+ aggChildren: [],
2361
+ aggIndex: self.numberOfAggregates,
2362
+ aggLabelFilter: g[NG_COLUMN].aggLabelFilter
2363
+ }, 0);
2364
+ self.numberOfAggregates++;
2365
+ //set the aggregate parent to the parent in the array that is one less deep.
2366
+ agg.parent = self.parentCache[agg.depth - 1];
2367
+ // if we have a parent, set the parent to not be collapsed and append the current agg to its children
2368
+ if (agg.parent) {
2369
+ agg.parent.collapsed = false;
2370
+ agg.parent.aggChildren.push(agg);
2371
+ }
2372
+ // add the aggregate row to the parsed data.
2373
+ self.parsedData.push(agg);
2374
+ // the current aggregate now the parent of the current depth
2375
+ self.parentCache[agg.depth] = agg;
2376
+ // dig deeper for more aggregates or children.
2377
+ self.parseGroupData(g[prop]);
2378
+ }
2379
+ }
2380
+ }
2381
+ };
2382
+ //Shuffle the data into their respective groupings.
2383
+ self.getGrouping = function(groups) {
2384
+ self.aggCache = [];
2385
+ self.numberOfAggregates = 0;
2386
+ self.groupedData = {};
2387
+ // Here we set the onmousedown event handler to the header container.
2388
+ var rows = grid.filteredRows,
2389
+ maxDepth = groups.length,
2390
+ cols = $scope.columns;
2391
+
2392
+ function filterCols(cols, group) {
2393
+ return cols.filter(function(c) {
2394
+ return c.field === group;
2395
+ });
2396
+ }
2397
+
2398
+ for (var x = 0; x < rows.length; x++) {
2399
+ var model = rows[x].entity;
2400
+ if (!model) {
2401
+ return;
2402
+ }
2403
+ rows[x][NG_HIDDEN] = grid.config.groupsCollapsedByDefault;
2404
+ var ptr = self.groupedData;
2405
+
2406
+ for (var y = 0; y < groups.length; y++) {
2407
+ var group = groups[y];
2408
+
2409
+ var col = filterCols(cols, group)[0];
2410
+
2411
+ var val = $utils.evalProperty(model, group);
2412
+ val = val ? val.toString() : 'null';
2413
+ if (!ptr[val]) {
2414
+ ptr[val] = {};
2415
+ }
2416
+ if (!ptr[NG_FIELD]) {
2417
+ ptr[NG_FIELD] = group;
2418
+ }
2419
+ if (!ptr[NG_DEPTH]) {
2420
+ ptr[NG_DEPTH] = y;
2421
+ }
2422
+ if (!ptr[NG_COLUMN]) {
2423
+ ptr[NG_COLUMN] = col;
2424
+ }
2425
+ ptr = ptr[val];
2426
+ }
2427
+ if (!ptr.values) {
2428
+ ptr.values = [];
2429
+ }
2430
+ ptr.values.push(rows[x]);
2431
+ }
2432
+
2433
+ //moved out of above loops due to if no data initially, but has initial grouping, columns won't be added
2434
+ for (var z = 0; z < groups.length; z++) {
2435
+ if (!cols[z].isAggCol && z <= maxDepth) {
2436
+ cols.splice(0, 0, new ngColumn({
2437
+ colDef: {
2438
+ field: '',
2439
+ width: 25,
2440
+ sortable: false,
2441
+ resizable: false,
2442
+ headerCellTemplate: '<div class="ngAggHeader"></div>',
2443
+ pinned: grid.config.pinSelectionCheckbox
2444
+
2445
+ },
2446
+ enablePinning: grid.config.enablePinning,
2447
+ isAggCol: true,
2448
+ headerRowHeight: grid.config.headerRowHeight
2449
+
2450
+ }, $scope, grid, domUtilityService, $templateCache, $utils));
2451
+ }
2452
+ }
2453
+
2454
+ domUtilityService.BuildStyles($scope, grid, true);
2455
+ grid.fixColumnIndexes();
2456
+ $scope.adjustScrollLeft(0);
2457
+ self.parsedData.length = 0;
2458
+ self.parseGroupData(self.groupedData);
2459
+ self.fixRowCache();
2460
+ };
2461
+
2462
+ if (grid.config.groups.length > 0 && grid.filteredRows.length > 0) {
2463
+ self.getGrouping(grid.config.groups);
2464
+ }
2465
+ };
2466
+ var ngSearchProvider = function ($scope, grid, $filter) {
2467
+ var self = this,
2468
+ searchConditions = [];
2469
+
2470
+ self.extFilter = grid.config.filterOptions.useExternalFilter;
2471
+ $scope.showFilter = grid.config.showFilter;
2472
+ $scope.filterText = '';
2473
+
2474
+ self.fieldMap = {};
2475
+
2476
+ self.evalFilter = function () {
2477
+ var filterFunc = function(item) {
2478
+ for (var x = 0, len = searchConditions.length; x < len; x++) {
2479
+ var condition = searchConditions[x];
2480
+ //Search entire row
2481
+ var result;
2482
+ if (!condition.column) {
2483
+ for (var prop in item) {
2484
+ if (item.hasOwnProperty(prop)) {
2485
+ var c = self.fieldMap[prop.toLowerCase()];
2486
+ if (!c) {
2487
+ continue;
2488
+ }
2489
+ var f = null,
2490
+ s = null;
2491
+ if (c && c.cellFilter) {
2492
+ s = c.cellFilter.split(':');
2493
+ f = $filter(s[0]);
2494
+ }
2495
+ var pVal = item[prop];
2496
+ if (pVal !== null && pVal !== undefined) {
2497
+ if (typeof f === "function") {
2498
+ var filterRes = f(typeof pVal === 'object' ? evalObject(pVal, c.field) : pVal, s[1]).toString();
2499
+ result = condition.regex.test(filterRes);
2500
+ } else {
2501
+ result = condition.regex.test(typeof pVal === 'object' ? evalObject(pVal, c.field).toString() : pVal.toString());
2502
+ }
2503
+ if (result) {
2504
+ return true;
2505
+ }
2506
+ }
2507
+ }
2508
+ }
2509
+ return false;
2510
+ }
2511
+ //Search by column.
2512
+ var col = self.fieldMap[condition.columnDisplay];
2513
+ if (!col) {
2514
+ return false;
2515
+ }
2516
+ var sp = col.cellFilter.split(':');
2517
+ var filter = col.cellFilter ? $filter(sp[0]) : null;
2518
+ var value = item[condition.column] || item[col.field.split('.')[0]];
2519
+ if (value === null || value === undefined) {
2520
+ return false;
2521
+ }
2522
+ if (typeof filter === "function") {
2523
+ var filterResults = filter(typeof value === "object" ? evalObject(value, col.field) : value, sp[1]).toString();
2524
+ result = condition.regex.test(filterResults);
2525
+ }
2526
+ else {
2527
+ result = condition.regex.test(typeof value === "object" ? evalObject(value, col.field).toString() : value.toString());
2528
+ }
2529
+ if (!result) {
2530
+ return false;
2531
+ }
2532
+ }
2533
+ return true;
2534
+ };
2535
+ if (searchConditions.length === 0) {
2536
+ grid.filteredRows = grid.rowCache;
2537
+ } else {
2538
+ grid.filteredRows = grid.rowCache.filter(function(row) {
2539
+ return filterFunc(row.entity);
2540
+ });
2541
+ }
2542
+ for (var i = 0; i < grid.filteredRows.length; i++)
2543
+ {
2544
+ grid.filteredRows[i].rowIndex = i;
2545
+
2546
+ }
2547
+ grid.rowFactory.filteredRowsChanged();
2548
+ };
2549
+
2550
+ //Traversing through the object to find the value that we want. If fail, then return the original object.
2551
+ var evalObject = function (obj, columnName) {
2552
+ if (typeof obj !== "object" || typeof columnName !== "string") {
2553
+ return obj;
2554
+ }
2555
+ var args = columnName.split('.');
2556
+ var cObj = obj;
2557
+ if (args.length > 1) {
2558
+ for (var i = 1, len = args.length; i < len; i++) {
2559
+ cObj = cObj[args[i]];
2560
+ if (!cObj) {
2561
+ return obj;
2562
+ }
2563
+ }
2564
+ return cObj;
2565
+ }
2566
+ return obj;
2567
+ };
2568
+ var getRegExp = function (str, modifiers) {
2569
+ try {
2570
+ return new RegExp(str, modifiers);
2571
+ } catch (err) {
2572
+ //Escape all RegExp metacharacters.
2573
+ return new RegExp(str.replace(/(\^|\$|\(|\)|<|>|\[|\]|\{|\}|\\|\||\.|\*|\+|\?)/g, '\\$1'));
2574
+ }
2575
+ };
2576
+ var buildSearchConditions = function (a) {
2577
+ //reset.
2578
+ searchConditions = [];
2579
+ var qStr;
2580
+ if (!(qStr = $.trim(a))) {
2581
+ return;
2582
+ }
2583
+ var columnFilters = qStr.split(";");
2584
+ for (var i = 0; i < columnFilters.length; i++) {
2585
+ var args = columnFilters[i].split(':');
2586
+ if (args.length > 1) {
2587
+ var columnName = $.trim(args[0]);
2588
+ var columnValue = $.trim(args[1]);
2589
+ if (columnName && columnValue) {
2590
+ searchConditions.push({
2591
+ column: columnName,
2592
+ columnDisplay: columnName.replace(/\s+/g, '').toLowerCase(),
2593
+ regex: getRegExp(columnValue, 'i')
2594
+ });
2595
+ }
2596
+ } else {
2597
+ var val = $.trim(args[0]);
2598
+ if (val) {
2599
+ searchConditions.push({
2600
+ column: '',
2601
+ regex: getRegExp(val, 'i')
2602
+ });
2603
+ }
2604
+ }
2605
+ }
2606
+ };
2607
+
2608
+ $scope.$watch(
2609
+ function () {
2610
+ return grid.config.filterOptions.filterText;
2611
+ },
2612
+ function (a) {
2613
+ $scope.filterText = a;
2614
+ }
2615
+ );
2616
+
2617
+ $scope.$watch('filterText', function(a){
2618
+ if (!self.extFilter) {
2619
+ $scope.$emit('ngGridEventFilter', a);
2620
+ buildSearchConditions(a);
2621
+ self.evalFilter();
2622
+ }
2623
+ });
2624
+
2625
+ if (!self.extFilter) {
2626
+ $scope.$watch('columns', function (cs) {
2627
+ for (var i = 0; i < cs.length; i++) {
2628
+ var col = cs[i];
2629
+ if (col.field) {
2630
+ self.fieldMap[col.field.split('.')[0].toLowerCase()] = col;
2631
+ }
2632
+ if (col.displayName) {
2633
+ self.fieldMap[col.displayName.toLowerCase().replace(/\s+/g, '')] = col;
2634
+ }
2635
+ }
2636
+ });
2637
+ }
2638
+ };
2639
+ var ngSelectionProvider = function (grid, $scope, $parse) {
2640
+ var self = this;
2641
+ self.multi = grid.config.multiSelect;
2642
+ self.selectedItems = grid.config.selectedItems;
2643
+ self.selectedIndex = grid.config.selectedIndex;
2644
+ self.lastClickedRow = undefined;
2645
+ self.ignoreSelectedItemChanges = false; // flag to prevent circular event loops keeping single-select var in sync
2646
+ self.pKeyParser = $parse(grid.config.primaryKey);
2647
+
2648
+ // function to manage the selection action of a data item (entity)
2649
+ self.ChangeSelection = function (rowItem, evt) {
2650
+ // ctrl-click + shift-click multi-selections
2651
+ // up/down key navigation in multi-selections
2652
+ var charCode = evt.which || evt.keyCode;
2653
+ var isUpDownKeyPress = (charCode === 40 || charCode === 38);
2654
+
2655
+ if (evt && evt.shiftKey && !evt.keyCode && self.multi && grid.config.enableRowSelection) {
2656
+ if (self.lastClickedRow) {
2657
+ var rowsArr;
2658
+ if ($scope.configGroups.length > 0) {
2659
+ rowsArr = grid.rowFactory.parsedData.filter(function(row) {
2660
+ return !row.isAggRow;
2661
+ });
2662
+ }
2663
+ else {
2664
+ rowsArr = grid.filteredRows;
2665
+ }
2666
+
2667
+ var thisIndx = rowItem.rowIndex;
2668
+ var prevIndx = self.lastClickedRowIndex;
2669
+
2670
+ if (thisIndx === prevIndx) {
2671
+ return false;
2672
+ }
2673
+
2674
+ if (thisIndx < prevIndx) {
2675
+ thisIndx = thisIndx ^ prevIndx;
2676
+ prevIndx = thisIndx ^ prevIndx;
2677
+ thisIndx = thisIndx ^ prevIndx;
2678
+ thisIndx--;
2679
+ }
2680
+ else {
2681
+ prevIndx++;
2682
+ }
2683
+
2684
+ var rows = [];
2685
+ for (; prevIndx <= thisIndx; prevIndx++) {
2686
+ rows.push(rowsArr[prevIndx]);
2687
+ }
2688
+
2689
+ if (rows[rows.length - 1].beforeSelectionChange(rows, evt)) {
2690
+ for (var i = 0; i < rows.length; i++) {
2691
+ var ri = rows[i];
2692
+ var selectionState = ri.selected;
2693
+ ri.selected = !selectionState;
2694
+ if (ri.clone) {
2695
+ ri.clone.selected = ri.selected;
2696
+ }
2697
+ var index = self.selectedItems.indexOf(ri.entity);
2698
+ if (index === -1) {
2699
+ self.selectedItems.push(ri.entity);
2700
+ }
2701
+ else {
2702
+ self.selectedItems.splice(index, 1);
2703
+ }
2704
+ }
2705
+ rows[rows.length - 1].afterSelectionChange(rows, evt);
2706
+ }
2707
+ self.lastClickedRow = rowItem;
2708
+ self.lastClickedRowIndex = rowItem.rowIndex;
2709
+
2710
+ return true;
2711
+ }
2712
+ }
2713
+ else if (!self.multi) {
2714
+ if (self.lastClickedRow === rowItem) {
2715
+ self.setSelection(self.lastClickedRow, grid.config.keepLastSelected ? true : !rowItem.selected);
2716
+ } else {
2717
+ if (self.lastClickedRow) {
2718
+ self.setSelection(self.lastClickedRow, false);
2719
+ }
2720
+ self.setSelection(rowItem, !rowItem.selected);
2721
+ }
2722
+ }
2723
+ else if (!evt.keyCode || isUpDownKeyPress && !grid.config.selectWithCheckboxOnly) {
2724
+ self.setSelection(rowItem, !rowItem.selected);
2725
+ }
2726
+ self.lastClickedRow = rowItem;
2727
+ self.lastClickedRowIndex = rowItem.rowIndex;
2728
+ return true;
2729
+ };
2730
+
2731
+ self.getSelection = function (entity) {
2732
+ var isSelected = false;
2733
+ if (grid.config.primaryKey) {
2734
+ var val = self.pKeyParser(entity);
2735
+ angular.forEach(self.selectedItems, function (c) {
2736
+ if (val === self.pKeyParser(c)) {
2737
+ isSelected = true;
2738
+ }
2739
+ });
2740
+ }
2741
+ else {
2742
+ isSelected = self.selectedItems.indexOf(entity) !== -1;
2743
+ }
2744
+ return isSelected;
2745
+ };
2746
+
2747
+ // just call this func and hand it the rowItem you want to select (or de-select)
2748
+ self.setSelection = function (rowItem, isSelected) {
2749
+ if(grid.config.enableRowSelection){
2750
+ if (!isSelected) {
2751
+ var indx = self.selectedItems.indexOf(rowItem.entity);
2752
+ if (indx !== -1) {
2753
+ self.selectedItems.splice(indx, 1);
2754
+ }
2755
+ }
2756
+ else {
2757
+ if (self.selectedItems.indexOf(rowItem.entity) === -1) {
2758
+ if (!self.multi && self.selectedItems.length > 0) {
2759
+ self.toggleSelectAll(false, true);
2760
+ }
2761
+ self.selectedItems.push(rowItem.entity);
2762
+ }
2763
+ }
2764
+ rowItem.selected = isSelected;
2765
+ if (rowItem.orig) {
2766
+ rowItem.orig.selected = isSelected;
2767
+ }
2768
+ if (rowItem.clone) {
2769
+ rowItem.clone.selected = isSelected;
2770
+ }
2771
+ rowItem.afterSelectionChange(rowItem);
2772
+ }
2773
+ };
2774
+
2775
+ // @return - boolean indicating if all items are selected or not
2776
+ // @val - boolean indicating whether to select all/de-select all
2777
+ self.toggleSelectAll = function (checkAll, bypass, selectFiltered) {
2778
+ var rows = selectFiltered ? grid.filteredRows : grid.rowCache;
2779
+ if (bypass || grid.config.beforeSelectionChange(rows, checkAll)) {
2780
+ var selectedlength = self.selectedItems.length;
2781
+ if (selectedlength > 0) {
2782
+ self.selectedItems.length = 0;
2783
+ }
2784
+ for (var i = 0; i < rows.length; i++) {
2785
+ rows[i].selected = checkAll;
2786
+ if (rows[i].clone) {
2787
+ rows[i].clone.selected = checkAll;
2788
+ }
2789
+ if (checkAll) {
2790
+ self.selectedItems.push(rows[i].entity);
2791
+ }
2792
+ }
2793
+ if (!bypass) {
2794
+ grid.config.afterSelectionChange(rows, checkAll);
2795
+ }
2796
+ }
2797
+ };
2798
+ };
2799
+ var ngStyleProvider = function($scope, grid) {
2800
+ $scope.headerCellStyle = function(col) {
2801
+ return { "height": col.headerRowHeight + "px" };
2802
+ };
2803
+ $scope.rowStyle = function (row) {
2804
+ var ret = { "top": row.offsetTop + "px", "height": $scope.rowHeight + "px" };
2805
+ if (row.isAggRow) {
2806
+ ret.left = row.offsetLeft;
2807
+ }
2808
+ return ret;
2809
+ };
2810
+ $scope.canvasStyle = function() {
2811
+ return { "height": grid.maxCanvasHt + "px" };
2812
+ };
2813
+ $scope.headerScrollerStyle = function() {
2814
+ return { "height": grid.config.headerRowHeight + "px" };
2815
+ };
2816
+ $scope.topPanelStyle = function() {
2817
+ return { "width": grid.rootDim.outerWidth + "px", "height": $scope.topPanelHeight() + "px" };
2818
+ };
2819
+ $scope.headerStyle = function() {
2820
+ return { "width": grid.rootDim.outerWidth + "px", "height": grid.config.headerRowHeight + "px" };
2821
+ };
2822
+ $scope.groupPanelStyle = function () {
2823
+ return { "width": grid.rootDim.outerWidth + "px", "height": "32px" };
2824
+ };
2825
+ $scope.viewportStyle = function() {
2826
+ return { "width": grid.rootDim.outerWidth + "px", "height": $scope.viewportDimHeight() + "px" };
2827
+ };
2828
+ $scope.footerStyle = function() {
2829
+ return { "width": grid.rootDim.outerWidth + "px", "height": $scope.footerRowHeight + "px" };
2830
+ };
2831
+ };
2832
+ ngGridDirectives.directive('ngCellHasFocus', ['$domUtilityService',
2833
+ function (domUtilityService) {
2834
+ var focusOnInputElement = function($scope, elm) {
2835
+ $scope.isFocused = true;
2836
+ domUtilityService.digest($scope);
2837
+
2838
+ $scope.$broadcast('ngGridEventStartCellEdit');
2839
+
2840
+ $scope.$on('ngGridEventEndCellEdit', function() {
2841
+ $scope.isFocused = false;
2842
+ domUtilityService.digest($scope);
2843
+ });
2844
+ };
2845
+
2846
+ return function($scope, elm) {
2847
+ var isFocused = false;
2848
+ var isCellEditableOnMouseDown = false;
2849
+
2850
+ $scope.editCell = function() {
2851
+ if(!$scope.enableCellEditOnFocus) {
2852
+ setTimeout(function() {
2853
+ focusOnInputElement($scope,elm);
2854
+ }, 0);
2855
+ }
2856
+ };
2857
+ elm.bind('mousedown', function(evt) {
2858
+ if($scope.enableCellEditOnFocus) {
2859
+ isCellEditableOnMouseDown = true;
2860
+ } else {
2861
+ elm.focus();
2862
+ }
2863
+ return true;
2864
+ });
2865
+ elm.bind('click', function(evt) {
2866
+ if($scope.enableCellEditOnFocus) {
2867
+ evt.preventDefault();
2868
+ isCellEditableOnMouseDown = false;
2869
+ focusOnInputElement($scope,elm);
2870
+ }
2871
+ });
2872
+ elm.bind('focus', function(evt) {
2873
+ isFocused = true;
2874
+ if($scope.enableCellEditOnFocus && !isCellEditableOnMouseDown) {
2875
+ focusOnInputElement($scope,elm);
2876
+ }
2877
+ return true;
2878
+ });
2879
+ elm.bind('blur', function() {
2880
+ isFocused = false;
2881
+ return true;
2882
+ });
2883
+ elm.bind('keydown', function(evt) {
2884
+ if(!$scope.enableCellEditOnFocus) {
2885
+ if (isFocused && evt.keyCode !== 37 && evt.keyCode !== 38 && evt.keyCode !== 39 && evt.keyCode !== 40 && evt.keyCode !== 9 && !evt.shiftKey && evt.keyCode !== 13) {
2886
+ focusOnInputElement($scope,elm);
2887
+ }
2888
+ if (evt.shiftKey && (evt.keyCode >= 65 && evt.keyCode <= 90)) {
2889
+ focusOnInputElement($scope, elm);
2890
+ }
2891
+ if (evt.keyCode === 27) {
2892
+ elm.focus();
2893
+ }
2894
+ }
2895
+ return true;
2896
+ });
2897
+ };
2898
+ }]);
2899
+ ngGridDirectives.directive('ngCellText',
2900
+ function () {
2901
+ return function(scope, elm) {
2902
+ elm.bind('mouseover', function(evt) {
2903
+ evt.preventDefault();
2904
+ elm.css({
2905
+ 'cursor': 'text'
2906
+ });
2907
+ });
2908
+ elm.bind('mouseleave', function(evt) {
2909
+ evt.preventDefault();
2910
+ elm.css({
2911
+ 'cursor': 'default'
2912
+ });
2913
+ });
2914
+ };
2915
+ });
2916
+ ngGridDirectives.directive('ngCell', ['$compile', '$domUtilityService', function ($compile, domUtilityService) {
2917
+ var ngCell = {
2918
+ scope: false,
2919
+ compile: function() {
2920
+ return {
2921
+ pre: function($scope, iElement) {
2922
+ var html;
2923
+ var cellTemplate = $scope.col.cellTemplate.replace(COL_FIELD, 'row.entity.' + $scope.col.field);
2924
+
2925
+ if ($scope.col.enableCellEdit) {
2926
+ html = $scope.col.cellEditTemplate;
2927
+ html = html.replace(DISPLAY_CELL_TEMPLATE, cellTemplate);
2928
+ html = html.replace(EDITABLE_CELL_TEMPLATE, $scope.col.editableCellTemplate.replace(COL_FIELD, 'row.entity.' + $scope.col.field));
2929
+ } else {
2930
+ html = cellTemplate;
2931
+ }
2932
+
2933
+ var cellElement = $compile(html)($scope);
2934
+
2935
+ if ($scope.enableCellSelection && cellElement[0].className.indexOf('ngSelectionCell') === -1) {
2936
+ cellElement[0].setAttribute('tabindex', 0);
2937
+ cellElement.addClass('ngCellElement');
2938
+ }
2939
+
2940
+ iElement.append(cellElement);
2941
+ },
2942
+ post: function($scope, iElement) {
2943
+ if ($scope.enableCellSelection) {
2944
+ $scope.domAccessProvider.selectionHandlers($scope, iElement);
2945
+ }
2946
+
2947
+ $scope.$on('ngGridEventDigestCell', function() {
2948
+ domUtilityService.digest($scope);
2949
+ });
2950
+ }
2951
+ };
2952
+ }
2953
+ };
2954
+
2955
+ return ngCell;
2956
+ }]);
2957
+ /*
2958
+ * Defines the ui-if tag. This removes/adds an element from the dom depending on a condition
2959
+ * Originally created by @tigbro, for the @jquery-mobile-angular-adapter
2960
+ * https://github.com/tigbro/jquery-mobile-angular-adapter
2961
+ */
2962
+ ngGridDirectives.directive('ngEditCellIf', [function () {
2963
+ return {
2964
+ transclude: 'element',
2965
+ priority: 1000,
2966
+ terminal: true,
2967
+ restrict: 'A',
2968
+ compile: function (e, a, transclude) {
2969
+ return function (scope, element, attr) {
2970
+
2971
+ var childElement;
2972
+ var childScope;
2973
+
2974
+ scope.$watch(attr['ngEditCellIf'], function (newValue) {
2975
+ if (childElement) {
2976
+ childElement.remove();
2977
+ childElement = undefined;
2978
+ }
2979
+ if (childScope) {
2980
+ childScope.$destroy();
2981
+ childScope = undefined;
2982
+ }
2983
+
2984
+ if (newValue) {
2985
+ childScope = scope.$new();
2986
+ transclude(childScope, function (clone) {
2987
+ childElement = clone;
2988
+ element.after(clone);
2989
+ });
2990
+ }
2991
+ });
2992
+ };
2993
+ }
2994
+ };
2995
+ }]);
2996
+ ngGridDirectives.directive('ngGridFooter', ['$compile', '$templateCache', function ($compile, $templateCache) {
2997
+ var ngGridFooter = {
2998
+ scope: false,
2999
+ compile: function () {
3000
+ return {
3001
+ pre: function ($scope, iElement) {
3002
+ if (iElement.children().length === 0) {
3003
+ iElement.append($compile($templateCache.get($scope.gridId + 'footerTemplate.html'))($scope));
3004
+ }
3005
+ }
3006
+ };
3007
+ }
3008
+ };
3009
+ return ngGridFooter;
3010
+ }]);
3011
+ ngGridDirectives.directive('ngGridMenu', ['$compile', '$templateCache', function ($compile, $templateCache) {
3012
+ var ngGridMenu = {
3013
+ scope: false,
3014
+ compile: function () {
3015
+ return {
3016
+ pre: function ($scope, iElement) {
3017
+ if (iElement.children().length === 0) {
3018
+ iElement.append($compile($templateCache.get($scope.gridId + 'menuTemplate.html'))($scope));
3019
+ }
3020
+ }
3021
+ };
3022
+ }
3023
+ };
3024
+ return ngGridMenu;
3025
+ }]);
3026
+ ngGridDirectives.directive('ngGrid', ['$compile', '$filter', '$templateCache', '$sortService', '$domUtilityService', '$utilityService', '$timeout', '$parse', '$http', '$q', function ($compile, $filter, $templateCache, sortService, domUtilityService, $utils, $timeout, $parse, $http, $q) {
3027
+ var ngGridDirective = {
3028
+ scope: true,
3029
+ compile: function() {
3030
+ return {
3031
+ pre: function($scope, iElement, iAttrs) {
3032
+ var $element = $(iElement);
3033
+ var options = $scope.$eval(iAttrs.ngGrid);
3034
+ options.gridDim = new ngDimension({ outerHeight: $($element).height(), outerWidth: $($element).width() });
3035
+
3036
+ var grid = new ngGrid($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q);
3037
+ return grid.init().then(function() {
3038
+ // if columndefs are a string of a property ont he scope watch for changes and rebuild columns.
3039
+ if (typeof options.columnDefs === "string") {
3040
+ $scope.$parent.$watch(options.columnDefs, function (a) {
3041
+ if (!a) {
3042
+ grid.refreshDomSizes();
3043
+ grid.buildColumns();
3044
+ return;
3045
+ }
3046
+ // we have to set this to false in case we want to autogenerate columns with no initial data.
3047
+ grid.lateBoundColumns = false;
3048
+ $scope.columns = [];
3049
+ grid.config.columnDefs = a;
3050
+ grid.buildColumns();
3051
+ grid.eventProvider.assignEvents();
3052
+ domUtilityService.RebuildGrid($scope, grid);
3053
+ }, true);
3054
+ }
3055
+ else {
3056
+ grid.buildColumns();
3057
+ }
3058
+
3059
+ // Watch totalServerItems if it's a string
3060
+ if (typeof options.totalServerItems === "string") {
3061
+ $scope.$parent.$watch(options.totalServerItems, function (newTotal, oldTotal) {
3062
+ // If the newTotal is not defined (like during init, set the value to 0)
3063
+ if (!angular.isDefined(newTotal)) {
3064
+ $scope.totalServerItems = 0;
3065
+ }
3066
+ // Otherwise set the value to the new total
3067
+ else {
3068
+ $scope.totalServerItems = newTotal;
3069
+ }
3070
+ });
3071
+ }
3072
+ // If it's NOT a string, then just set totalServerItems to 0 since they should only be setting this if using a string
3073
+ else {
3074
+ $scope.totalServerItems = 0;
3075
+ }
3076
+
3077
+ // if it is a string we can watch for data changes. otherwise you won't be able to update the grid data
3078
+ if (typeof options.data === "string") {
3079
+ var dataWatcher = function (a) {
3080
+ // make a temporary copy of the data
3081
+ grid.data = $.extend([], a);
3082
+ grid.rowFactory.fixRowCache();
3083
+ angular.forEach(grid.data, function (item, j) {
3084
+ var indx = grid.rowMap[j] || j;
3085
+ if (grid.rowCache[indx]) {
3086
+ grid.rowCache[indx].ensureEntity(item);
3087
+ }
3088
+ grid.rowMap[indx] = j;
3089
+ });
3090
+ grid.searchProvider.evalFilter();
3091
+ grid.configureColumnWidths();
3092
+ grid.refreshDomSizes();
3093
+ if (grid.config.sortInfo.fields.length > 0) {
3094
+ grid.getColsFromFields();
3095
+ grid.sortActual();
3096
+ grid.searchProvider.evalFilter();
3097
+ $scope.$emit('ngGridEventSorted', grid.config.sortInfo);
3098
+ }
3099
+ $scope.$emit("ngGridEventData", grid.gridId);
3100
+ };
3101
+ $scope.$parent.$watch(options.data, dataWatcher);
3102
+ $scope.$parent.$watch(options.data + '.length', function() {
3103
+ dataWatcher($scope.$eval(options.data));
3104
+ });
3105
+ }
3106
+
3107
+ grid.footerController = new ngFooter($scope, grid);
3108
+ //set the right styling on the container
3109
+ iElement.addClass("ngGrid").addClass(grid.gridId.toString());
3110
+ if (!options.enableHighlighting) {
3111
+ iElement.addClass("unselectable");
3112
+ }
3113
+ if (options.jqueryUITheme) {
3114
+ iElement.addClass('ui-widget');
3115
+ }
3116
+ iElement.append($compile($templateCache.get('gridTemplate.html'))($scope)); // make sure that if any of these change, we re-fire the calc logic
3117
+ //walk the element's graph and the correct properties on the grid
3118
+ domUtilityService.AssignGridContainers($scope, iElement, grid);
3119
+ //now use the manager to assign the event handlers
3120
+ grid.eventProvider = new ngEventProvider(grid, $scope, domUtilityService, $timeout);
3121
+
3122
+ // method for user to select a specific row programatically
3123
+ options.selectRow = function (rowIndex, state) {
3124
+ if (grid.rowCache[rowIndex]) {
3125
+ if (grid.rowCache[rowIndex].clone) {
3126
+ grid.rowCache[rowIndex].clone.setSelection(state ? true : false);
3127
+ }
3128
+ grid.rowCache[rowIndex].setSelection(state ? true : false);
3129
+ }
3130
+ };
3131
+ // method for user to select the row by data item programatically
3132
+ options.selectItem = function (itemIndex, state) {
3133
+ options.selectRow(grid.rowMap[itemIndex], state);
3134
+ };
3135
+ // method for user to set the select all state.
3136
+ options.selectAll = function (state) {
3137
+ $scope.toggleSelectAll(state);
3138
+ };
3139
+ // method for user to set the select all state on visible items.
3140
+ options.selectVisible = function (state) {
3141
+ $scope.toggleSelectAll(state, true);
3142
+ };
3143
+ // method for user to set the groups programatically
3144
+ options.groupBy = function (field) {
3145
+ if (field) {
3146
+ $scope.groupBy($scope.columns.filter(function(c) {
3147
+ return c.field === field;
3148
+ })[0]);
3149
+ } else {
3150
+ var arr = $.extend(true, [], $scope.configGroups);
3151
+ angular.forEach(arr, $scope.groupBy);
3152
+ }
3153
+ };
3154
+ // method for user to set the sort field programatically
3155
+ options.sortBy = function (field) {
3156
+ var col = $scope.columns.filter(function (c) {
3157
+ return c.field === field;
3158
+ })[0];
3159
+ if (col) {
3160
+ col.sort();
3161
+ }
3162
+ };
3163
+ // the grid Id, entity, scope for convenience
3164
+ options.gridId = grid.gridId;
3165
+ options.ngGrid = grid;
3166
+ options.$gridScope = $scope;
3167
+ options.$gridServices = { SortService: sortService, DomUtilityService: domUtilityService, UtilityService: $utils };
3168
+ $scope.$on('ngGridEventDigestGrid', function(){
3169
+ domUtilityService.digest($scope.$parent);
3170
+ });
3171
+
3172
+ $scope.$on('ngGridEventDigestGridParent', function(){
3173
+ domUtilityService.digest($scope.$parent);
3174
+ });
3175
+ // set up the columns
3176
+ $scope.$evalAsync(function() {
3177
+ $scope.adjustScrollLeft(0);
3178
+ });
3179
+ //initialize plugins.
3180
+ angular.forEach(options.plugins, function (p) {
3181
+ if (typeof p === "function") {
3182
+ p = new p(); //If p is a function, then we assume it is a class.
3183
+ }
3184
+ p.init($scope.$new(), grid, options.$gridServices);
3185
+ options.plugins[$utils.getInstanceType(p)] = p;
3186
+ });
3187
+ //send initi finalize notification.
3188
+ if (typeof options.init === "function") {
3189
+ options.init(grid, $scope);
3190
+ }
3191
+ return null;
3192
+ });
3193
+ }
3194
+ };
3195
+ }
3196
+ };
3197
+ return ngGridDirective;
3198
+ }]);
3199
+
3200
+ ngGridDirectives.directive('ngHeaderCell', ['$compile', function($compile) {
3201
+ var ngHeaderCell = {
3202
+ scope: false,
3203
+ compile: function() {
3204
+ return {
3205
+ pre: function($scope, iElement) {
3206
+ iElement.append($compile($scope.col.headerCellTemplate)($scope));
3207
+ }
3208
+ };
3209
+ }
3210
+ };
3211
+ return ngHeaderCell;
3212
+ }]);
3213
+ ngGridDirectives.directive('ngInput', [function() {
3214
+ return {
3215
+ require: 'ngModel',
3216
+ link: function (scope, elm, attrs, ngModel) {
3217
+ // Store the initial cell value so we can reset to it if need be
3218
+ var oldCellValue;
3219
+ var dereg = scope.$watch('ngModel', function() {
3220
+ oldCellValue = ngModel.$modelValue;
3221
+ dereg(); // only run this watch once, we don't want to overwrite our stored value when the input changes
3222
+ });
3223
+
3224
+ elm.bind('keydown', function(evt) {
3225
+ switch (evt.keyCode) {
3226
+ case 37: // Left arrow
3227
+ case 38: // Up arrow
3228
+ case 39: // Right arrow
3229
+ case 40: // Down arrow
3230
+ evt.stopPropagation();
3231
+ break;
3232
+ case 27: // Esc (reset to old value)
3233
+ if (!scope.$$phase) {
3234
+ scope.$apply(function() {
3235
+ ngModel.$setViewValue(oldCellValue);
3236
+ elm.blur();
3237
+ });
3238
+ }
3239
+ break;
3240
+ case 13: // Enter (Leave Field)
3241
+ if(scope.enableCellEditOnFocus && scope.totalFilteredItemsLength() - 1 > scope.row.rowIndex && scope.row.rowIndex > 0 || scope.enableCellEdit) {
3242
+ elm.blur();
3243
+ }
3244
+ break;
3245
+ }
3246
+
3247
+ return true;
3248
+ });
3249
+
3250
+ elm.bind('click', function(evt) {
3251
+ evt.stopPropagation();
3252
+ });
3253
+
3254
+ elm.bind('mousedown', function(evt) {
3255
+ evt.stopPropagation();
3256
+ });
3257
+
3258
+ scope.$on('ngGridEventStartCellEdit', function () {
3259
+ elm.focus();
3260
+ elm.select();
3261
+ });
3262
+
3263
+ angular.element(elm).bind('blur', function () {
3264
+ scope.$emit('ngGridEventEndCellEdit');
3265
+ });
3266
+ }
3267
+ };
3268
+ }]);
3269
+ ngGridDirectives.directive('ngRow', ['$compile', '$domUtilityService', '$templateCache', function ($compile, domUtilityService, $templateCache) {
3270
+ var ngRow = {
3271
+ scope: false,
3272
+ compile: function() {
3273
+ return {
3274
+ pre: function($scope, iElement) {
3275
+ $scope.row.elm = iElement;
3276
+ if ($scope.row.clone) {
3277
+ $scope.row.clone.elm = iElement;
3278
+ }
3279
+ if ($scope.row.isAggRow) {
3280
+ var html = $templateCache.get($scope.gridId + 'aggregateTemplate.html');
3281
+ if ($scope.row.aggLabelFilter) {
3282
+ html = html.replace(CUSTOM_FILTERS, '| ' + $scope.row.aggLabelFilter);
3283
+ } else {
3284
+ html = html.replace(CUSTOM_FILTERS, "");
3285
+ }
3286
+ iElement.append($compile(html)($scope));
3287
+ } else {
3288
+ iElement.append($compile($templateCache.get($scope.gridId + 'rowTemplate.html'))($scope));
3289
+ }
3290
+ $scope.$on('ngGridEventDigestRow', function(){
3291
+ domUtilityService.digest($scope);
3292
+ });
3293
+ }
3294
+ };
3295
+ }
3296
+ };
3297
+ return ngRow;
3298
+ }]);
3299
+ ngGridDirectives.directive('ngViewport', [function() {
3300
+ return function($scope, elm) {
3301
+ var isMouseWheelActive;
3302
+ var prevScollLeft;
3303
+ var prevScollTop = 0;
3304
+ elm.bind('scroll', function(evt) {
3305
+ var scrollLeft = evt.target.scrollLeft,
3306
+ scrollTop = evt.target.scrollTop;
3307
+ if ($scope.$headerContainer) {
3308
+ $scope.$headerContainer.scrollLeft(scrollLeft);
3309
+ }
3310
+ $scope.adjustScrollLeft(scrollLeft);
3311
+ $scope.adjustScrollTop(scrollTop);
3312
+ if (!$scope.$root.$$phase) {
3313
+ $scope.$digest();
3314
+ }
3315
+ prevScollLeft = scrollLeft;
3316
+ prevScollTop = scrollTop;
3317
+ isMouseWheelActive = false;
3318
+ return true;
3319
+ });
3320
+ elm.bind("mousewheel DOMMouseScroll", function() {
3321
+ isMouseWheelActive = true;
3322
+ if (elm.focus) { elm.focus(); }
3323
+ return true;
3324
+ });
3325
+ if (!$scope.enableCellSelection) {
3326
+ $scope.domAccessProvider.selectionHandlers($scope, elm);
3327
+ }
3328
+ };
3329
+ }]);
3330
+ window.ngGrid.i18n['da'] = {
3331
+ ngAggregateLabel: 'artikler',
3332
+ ngGroupPanelDescription: 'Grupér rækker udfra en kolonne ved at trække dens overskift hertil.',
3333
+ ngSearchPlaceHolder: 'Søg...',
3334
+ ngMenuText: 'Vælg kolonner:',
3335
+ ngShowingItemsLabel: 'Viste rækker:',
3336
+ ngTotalItemsLabel: 'Rækker totalt:',
3337
+ ngSelectedItemsLabel: 'Valgte rækker:',
3338
+ ngPageSizeLabel: 'Side størrelse:',
3339
+ ngPagerFirstTitle: 'Første side',
3340
+ ngPagerNextTitle: 'Næste side',
3341
+ ngPagerPrevTitle: 'Forrige side',
3342
+ ngPagerLastTitle: 'Sidste side'
3343
+ };
3344
+ window.ngGrid.i18n['de'] = {
3345
+ ngAggregateLabel: 'artikel',
3346
+ ngGroupPanelDescription: 'Ziehen Sie eine Spaltenüberschrift hier und legen Sie es der Gruppe nach dieser Spalte.',
3347
+ ngSearchPlaceHolder: 'Suche...',
3348
+ ngMenuText: 'Spalten auswählen:',
3349
+ ngShowingItemsLabel: 'Zeige Artikel:',
3350
+ ngTotalItemsLabel: 'Meiste Artikel:',
3351
+ ngSelectedItemsLabel: 'Ausgewählte Artikel:',
3352
+ ngPageSizeLabel: 'Größe Seite:',
3353
+ ngPagerFirstTitle: 'Erste Page',
3354
+ ngPagerNextTitle: 'Nächste Page',
3355
+ ngPagerPrevTitle: 'Vorherige Page',
3356
+ ngPagerLastTitle: 'Letzte Page'
3357
+ };
3358
+ window.ngGrid.i18n['en'] = {
3359
+ ngAggregateLabel: 'items',
3360
+ ngGroupPanelDescription: 'Drag a column header here and drop it to group by that column.',
3361
+ ngSearchPlaceHolder: 'Search...',
3362
+ ngMenuText: 'Choose Columns:',
3363
+ ngShowingItemsLabel: 'Showing Items:',
3364
+ ngTotalItemsLabel: 'Total Items:',
3365
+ ngSelectedItemsLabel: 'Selected Items:',
3366
+ ngPageSizeLabel: 'Page Size:',
3367
+ ngPagerFirstTitle: 'First Page',
3368
+ ngPagerNextTitle: 'Next Page',
3369
+ ngPagerPrevTitle: 'Previous Page',
3370
+ ngPagerLastTitle: 'Last Page'
3371
+ };
3372
+ window.ngGrid.i18n['es'] = {
3373
+ ngAggregateLabel: 'Artículos',
3374
+ ngGroupPanelDescription: 'Arrastre un encabezado de columna aquí y soltarlo para agrupar por esa columna.',
3375
+ ngSearchPlaceHolder: 'Buscar...',
3376
+ ngMenuText: 'Elegir columnas:',
3377
+ ngShowingItemsLabel: 'Artículos Mostrando:',
3378
+ ngTotalItemsLabel: 'Artículos Totales:',
3379
+ ngSelectedItemsLabel: 'Artículos Seleccionados:',
3380
+ ngPageSizeLabel: 'Tamaño de Página:',
3381
+ ngPagerFirstTitle: 'Primera Página',
3382
+ ngPagerNextTitle: 'Página Siguiente',
3383
+ ngPagerPrevTitle: 'Página Anterior',
3384
+ ngPagerLastTitle: 'Última Página'
3385
+ };
3386
+ window.ngGrid.i18n['fr'] = {
3387
+ ngAggregateLabel: 'articles',
3388
+ ngGroupPanelDescription: 'Faites glisser un en-tête de colonne ici et déposez-le vers un groupe par cette colonne.',
3389
+ ngSearchPlaceHolder: 'Recherche...',
3390
+ ngMenuText: 'Choisir des colonnes:',
3391
+ ngShowingItemsLabel: 'Articles Affichage des:',
3392
+ ngTotalItemsLabel: 'Nombre total d\'articles:',
3393
+ ngSelectedItemsLabel: 'Éléments Articles:',
3394
+ ngPageSizeLabel: 'Taille de page:',
3395
+ ngPagerFirstTitle: 'Première page',
3396
+ ngPagerNextTitle: 'Page Suivante',
3397
+ ngPagerPrevTitle: 'Page précédente',
3398
+ ngPagerLastTitle: 'Dernière page'
3399
+ };
3400
+ window.ngGrid.i18n['pt-br'] = {
3401
+ ngAggregateLabel: 'items',
3402
+ ngGroupPanelDescription: 'Arraste e solte uma coluna aqui para agrupar por essa coluna',
3403
+ ngSearchPlaceHolder: 'Procurar...',
3404
+ ngMenuText: 'Selecione as colunas:',
3405
+ ngShowingItemsLabel: 'Mostrando os Items:',
3406
+ ngTotalItemsLabel: 'Total de Items:',
3407
+ ngSelectedItemsLabel: 'Items Selecionados:',
3408
+ ngPageSizeLabel: 'Tamanho da Página:',
3409
+ ngPagerFirstTitle: 'Primeira Página',
3410
+ ngPagerNextTitle: 'Próxima Página',
3411
+ ngPagerPrevTitle: 'Página Anterior',
3412
+ ngPagerLastTitle: 'Última Página'
3413
+ };
3414
+ window.ngGrid.i18n['zh-cn'] = {
3415
+ ngAggregateLabel: '条目',
3416
+ ngGroupPanelDescription: '拖曳表头到此处以进行分组',
3417
+ ngSearchPlaceHolder: '搜索...',
3418
+ ngMenuText: '数据分组与选择列:',
3419
+ ngShowingItemsLabel: '当前显示条目:',
3420
+ ngTotalItemsLabel: '条目总数:',
3421
+ ngSelectedItemsLabel: '选中条目:',
3422
+ ngPageSizeLabel: '每页显示数:',
3423
+ ngPagerFirstTitle: '回到首页',
3424
+ ngPagerNextTitle: '下一页',
3425
+ ngPagerPrevTitle: '上一页',
3426
+ ngPagerLastTitle: '前往尾页'
3427
+ };
3428
+
3429
+ window.ngGrid.i18n['zh-tw'] = {
3430
+ ngAggregateLabel: '筆',
3431
+ ngGroupPanelDescription: '拖拉表頭到此處以進行分組',
3432
+ ngSearchPlaceHolder: '搜尋...',
3433
+ ngMenuText: '選擇欄位:',
3434
+ ngShowingItemsLabel: '目前顯示筆數:',
3435
+ ngTotalItemsLabel: '總筆數:',
3436
+ ngSelectedItemsLabel: '選取筆數:',
3437
+ ngPageSizeLabel: '每頁顯示:',
3438
+ ngPagerFirstTitle: '第一頁',
3439
+ ngPagerNextTitle: '下一頁',
3440
+ ngPagerPrevTitle: '上一頁',
3441
+ ngPagerLastTitle: '最後頁'
3442
+ };
3443
+
3444
+ angular.module("ngGrid").run(["$templateCache", function($templateCache) {
3445
+
3446
+ $templateCache.put("aggregateTemplate.html",
3447
+ "<div ng-click=\"row.toggleExpand()\" ng-style=\"rowStyle(row)\" class=\"ngAggregate\">" +
3448
+ " <span class=\"ngAggregateText\">{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span>" +
3449
+ " <div class=\"{{row.aggClass()}}\"></div>" +
3450
+ "</div>" +
3451
+ ""
3452
+ );
3453
+
3454
+ $templateCache.put("cellEditTemplate.html",
3455
+ "<div ng-cell-has-focus ng-dblclick=\"editCell()\">" +
3456
+ " <div ng-edit-cell-if=\"!isFocused\"> " +
3457
+ " DISPLAY_CELL_TEMPLATE" +
3458
+ " </div>" +
3459
+ " <div ng-edit-cell-if=\"isFocused\">" +
3460
+ " EDITABLE_CELL_TEMPLATE" +
3461
+ " </div>" +
3462
+ "</div>"
3463
+ );
3464
+
3465
+ $templateCache.put("cellTemplate.html",
3466
+ "<div class=\"ngCellText\" ng-class=\"col.colIndex()\"><span ng-cell-text>{{COL_FIELD CUSTOM_FILTERS}}</span></div>"
3467
+ );
3468
+
3469
+ $templateCache.put("checkboxCellTemplate.html",
3470
+ "<div class=\"ngSelectionCell\"><input tabindex=\"-1\" class=\"ngSelectionCheckbox\" type=\"checkbox\" ng-checked=\"row.selected\" /></div>"
3471
+ );
3472
+
3473
+ $templateCache.put("checkboxHeaderTemplate.html",
3474
+ "<input class=\"ngSelectionHeader\" type=\"checkbox\" ng-show=\"multiSelect\" ng-model=\"allSelected\" ng-change=\"toggleSelectAll(allSelected, true)\"/>"
3475
+ );
3476
+
3477
+ $templateCache.put("editableCellTemplate.html",
3478
+ "<input ng-class=\"'colt' + col.index\" ng-input=\"COL_FIELD\" ng-model=\"COL_FIELD\" />"
3479
+ );
3480
+
3481
+ $templateCache.put("footerTemplate.html",
3482
+ "<div ng-show=\"showFooter\" class=\"ngFooterPanel\" ng-class=\"{'ui-widget-content': jqueryUITheme, 'ui-corner-bottom': jqueryUITheme}\" ng-style=\"footerStyle()\">" +
3483
+ " <div class=\"ngTotalSelectContainer\" >" +
3484
+ " <div class=\"ngFooterTotalItems\" ng-class=\"{'ngNoMultiSelect': !multiSelect}\" >" +
3485
+ " <span class=\"ngLabel\">{{i18n.ngTotalItemsLabel}} {{maxRows()}}</span><span ng-show=\"filterText.length > 0\" class=\"ngLabel\">({{i18n.ngShowingItemsLabel}} {{totalFilteredItemsLength()}})</span>" +
3486
+ " </div>" +
3487
+ " <div class=\"ngFooterSelectedItems\" ng-show=\"multiSelect\">" +
3488
+ " <span class=\"ngLabel\">{{i18n.ngSelectedItemsLabel}} {{selectedItems.length}}</span>" +
3489
+ " </div>" +
3490
+ " </div>" +
3491
+ " <div class=\"ngPagerContainer\" style=\"float: right; margin-top: 10px;\" ng-show=\"enablePaging\" ng-class=\"{'ngNoMultiSelect': !multiSelect}\">" +
3492
+ " <div style=\"float:left; margin-right: 10px;\" class=\"ngRowCountPicker\">" +
3493
+ " <span style=\"float: left; margin-top: 3px;\" class=\"ngLabel\">{{i18n.ngPageSizeLabel}}</span>" +
3494
+ " <select style=\"float: left;height: 27px; width: 100px\" ng-model=\"pagingOptions.pageSize\" >" +
3495
+ " <option ng-repeat=\"size in pagingOptions.pageSizes\">{{size}}</option>" +
3496
+ " </select>" +
3497
+ " </div>" +
3498
+ " <div style=\"float:left; margin-right: 10px; line-height:25px;\" class=\"ngPagerControl\" style=\"float: left; min-width: 135px;\">" +
3499
+ " <button class=\"ngPagerButton\" ng-click=\"pageToFirst()\" ng-disabled=\"cantPageBackward()\" title=\"{{i18n.ngPagerFirstTitle}}\"><div class=\"ngPagerFirstTriangle\"><div class=\"ngPagerFirstBar\"></div></div></button>" +
3500
+ " <button class=\"ngPagerButton\" ng-click=\"pageBackward()\" ng-disabled=\"cantPageBackward()\" title=\"{{i18n.ngPagerPrevTitle}}\"><div class=\"ngPagerFirstTriangle ngPagerPrevTriangle\"></div></button>" +
3501
+ " <input class=\"ngPagerCurrent\" min=\"1\" max=\"{{maxPages()}}\" type=\"number\" style=\"width:50px; height: 24px; margin-top: 1px; padding: 0 4px;\" ng-model=\"pagingOptions.currentPage\"/>" +
3502
+ " <button class=\"ngPagerButton\" ng-click=\"pageForward()\" ng-disabled=\"cantPageForward()\" title=\"{{i18n.ngPagerNextTitle}}\"><div class=\"ngPagerLastTriangle ngPagerNextTriangle\"></div></button>" +
3503
+ " <button class=\"ngPagerButton\" ng-click=\"pageToLast()\" ng-disabled=\"cantPageToLast()\" title=\"{{i18n.ngPagerLastTitle}}\"><div class=\"ngPagerLastTriangle\"><div class=\"ngPagerLastBar\"></div></div></button>" +
3504
+ " </div>" +
3505
+ " </div>" +
3506
+ "</div>"
3507
+ );
3508
+
3509
+ $templateCache.put("gridTemplate.html",
3510
+ "<div class=\"ngTopPanel\" ng-class=\"{'ui-widget-header':jqueryUITheme, 'ui-corner-top': jqueryUITheme}\" ng-style=\"topPanelStyle()\">" +
3511
+ " <div class=\"ngGroupPanel\" ng-show=\"showGroupPanel()\" ng-style=\"groupPanelStyle()\">" +
3512
+ " <div class=\"ngGroupPanelDescription\" ng-show=\"configGroups.length == 0\">{{i18n.ngGroupPanelDescription}}</div>" +
3513
+ " <ul ng-show=\"configGroups.length > 0\" class=\"ngGroupList\">" +
3514
+ " <li class=\"ngGroupItem\" ng-repeat=\"group in configGroups\">" +
3515
+ " <span class=\"ngGroupElement\">" +
3516
+ " <span class=\"ngGroupName\">{{group.displayName}}" +
3517
+ " <span ng-click=\"removeGroup($index)\" class=\"ngRemoveGroup\">x</span>" +
3518
+ " </span>" +
3519
+ " <span ng-hide=\"$last\" class=\"ngGroupArrow\"></span>" +
3520
+ " </span>" +
3521
+ " </li>" +
3522
+ " </ul>" +
3523
+ " </div>" +
3524
+ " <div class=\"ngHeaderContainer\" ng-style=\"headerStyle()\">" +
3525
+ " <div class=\"ngHeaderScroller\" ng-style=\"headerScrollerStyle()\" ng-include=\"gridId + 'headerRowTemplate.html'\"></div>" +
3526
+ " </div>" +
3527
+ " <div ng-grid-menu></div>" +
3528
+ "</div>" +
3529
+ "<div class=\"ngViewport\" unselectable=\"on\" ng-viewport ng-class=\"{'ui-widget-content': jqueryUITheme}\" ng-style=\"viewportStyle()\">" +
3530
+ " <div class=\"ngCanvas\" ng-style=\"canvasStyle()\">" +
3531
+ " <div ng-style=\"rowStyle(row)\" ng-repeat=\"row in renderedRows\" ng-click=\"row.toggleSelected($event)\" ng-class=\"row.alternatingRowClass()\" ng-row></div>" +
3532
+ " </div>" +
3533
+ "</div>" +
3534
+ "<div ng-grid-footer></div>" +
3535
+ ""
3536
+ );
3537
+
3538
+ $templateCache.put("headerCellTemplate.html",
3539
+ "<div class=\"ngHeaderSortColumn {{col.headerClass}}\" ng-style=\"{'cursor': col.cursor}\" ng-class=\"{ 'ngSorted': !noSortVisible }\">" +
3540
+ " <div ng-click=\"col.sort($event)\" ng-class=\"'colt' + col.index\" class=\"ngHeaderText\">{{col.displayName}}</div>" +
3541
+ " <div class=\"ngSortButtonDown\" ng-show=\"col.showSortButtonDown()\"></div>" +
3542
+ " <div class=\"ngSortButtonUp\" ng-show=\"col.showSortButtonUp()\"></div>" +
3543
+ " <div class=\"ngSortPriority\">{{col.sortPriority}}</div>" +
3544
+ " <div ng-class=\"{ ngPinnedIcon: col.pinned, ngUnPinnedIcon: !col.pinned }\" ng-click=\"togglePin(col)\" ng-show=\"col.pinnable\"></div>" +
3545
+ "</div>" +
3546
+ "<div ng-show=\"col.resizable\" class=\"ngHeaderGrip\" ng-click=\"col.gripClick($event)\" ng-mousedown=\"col.gripOnMouseDown($event)\"></div>"
3547
+ );
3548
+
3549
+ $templateCache.put("headerRowTemplate.html",
3550
+ "<div ng-style=\"{ height: col.headerRowHeight }\" ng-repeat=\"col in renderedColumns\" ng-class=\"col.colIndex()\" class=\"ngHeaderCell\">" +
3551
+ " <div class=\"ngVerticalBar\" ng-style=\"{height: col.headerRowHeight}\" ng-class=\"{ ngVerticalBarVisible: !$last }\">&nbsp;</div>" +
3552
+ " <div ng-header-cell></div>" +
3553
+ "</div>"
3554
+ );
3555
+
3556
+ $templateCache.put("menuTemplate.html",
3557
+ "<div ng-show=\"showColumnMenu || showFilter\" class=\"ngHeaderButton\" ng-click=\"toggleShowMenu()\">" +
3558
+ " <div class=\"ngHeaderButtonArrow\"></div>" +
3559
+ "</div>" +
3560
+ "<div ng-show=\"showMenu\" class=\"ngColMenu\">" +
3561
+ " <div ng-show=\"showFilter\">" +
3562
+ " <input placeholder=\"{{i18n.ngSearchPlaceHolder}}\" type=\"text\" ng-model=\"filterText\"/>" +
3563
+ " </div>" +
3564
+ " <div ng-show=\"showColumnMenu\">" +
3565
+ " <span class=\"ngMenuText\">{{i18n.ngMenuText}}</span>" +
3566
+ " <ul class=\"ngColList\">" +
3567
+ " <li class=\"ngColListItem\" ng-repeat=\"col in columns | ngColumns\">" +
3568
+ " <label><input ng-disabled=\"col.pinned\" type=\"checkbox\" class=\"ngColListCheckbox\" ng-model=\"col.visible\"/>{{col.displayName}}</label>" +
3569
+ " <a title=\"Group By\" ng-class=\"col.groupedByClass()\" ng-show=\"col.groupable && col.visible\" ng-click=\"groupBy(col)\"></a>" +
3570
+ " <span class=\"ngGroupingNumber\" ng-show=\"col.groupIndex > 0\">{{col.groupIndex}}</span> " +
3571
+ " </li>" +
3572
+ " </ul>" +
3573
+ " </div>" +
3574
+ "</div>"
3575
+ );
3576
+
3577
+ $templateCache.put("rowTemplate.html",
3578
+ "<div ng-style=\"{ 'cursor': row.cursor }\" ng-repeat=\"col in renderedColumns\" ng-class=\"col.colIndex()\" class=\"ngCell {{col.cellClass}}\">" +
3579
+ " <div class=\"ngVerticalBar\" ng-style=\"{height: rowHeight}\" ng-class=\"{ ngVerticalBarVisible: !$last }\">&nbsp;</div>" +
3580
+ " <div ng-cell></div>" +
3581
+ "</div>"
3582
+ );
3583
+
3584
+ }]);
3585
+
3586
+ }(window, jQuery));