Add config folder to be able to change config
[clamp.git] / src / main / resources / META-INF / resources / designer / lib / ui-grid-stable.js
1 /*! ui-grid - v3.0.0-rc.3 - 2014-09-25
2 * Copyright (c) 2014 ; License: MIT */
3 (function () {
4   'use strict';
5   angular.module('ui.grid.i18n', []);
6   angular.module('ui.grid', ['ui.grid.i18n']);
7 })();
8 (function () {
9   'use strict';
10   angular.module('ui.grid').constant('uiGridConstants', {
11     CUSTOM_FILTERS: /CUSTOM_FILTERS/g,
12     COL_FIELD: /COL_FIELD/g,
13     DISPLAY_CELL_TEMPLATE: /DISPLAY_CELL_TEMPLATE/g,
14     TEMPLATE_REGEXP: /<.+>/,
15     FUNC_REGEXP: /(\([^)]*\))?$/,
16     DOT_REGEXP: /\./g,
17     APOS_REGEXP: /'/g,
18     BRACKET_REGEXP: /^(.*)((?:\s*\[\s*\d+\s*\]\s*)|(?:\s*\[\s*"(?:[^"\\]|\\.)*"\s*\]\s*)|(?:\s*\[\s*'(?:[^'\\]|\\.)*'\s*\]\s*))(.*)$/,
19     COL_CLASS_PREFIX: 'ui-grid-col',
20     events: {
21       GRID_SCROLL: 'uiGridScroll',
22       COLUMN_MENU_SHOWN: 'uiGridColMenuShown',
23       ITEM_DRAGGING: 'uiGridItemDragStart' // For any item being dragged
24     },
25     // copied from http://www.lsauer.com/2011/08/javascript-keymap-keycodes-in-json.html
26     keymap: {
27       TAB: 9,
28       STRG: 17,
29       CTRL: 17,
30       CTRLRIGHT: 18,
31       CTRLR: 18,
32       SHIFT: 16,
33       RETURN: 13,
34       ENTER: 13,
35       BACKSPACE: 8,
36       BCKSP: 8,
37       ALT: 18,
38       ALTR: 17,
39       ALTRIGHT: 17,
40       SPACE: 32,
41       WIN: 91,
42       MAC: 91,
43       FN: null,
44       UP: 38,
45       DOWN: 40,
46       LEFT: 37,
47       RIGHT: 39,
48       ESC: 27,
49       DEL: 46,
50       F1: 112,
51       F2: 113,
52       F3: 114,
53       F4: 115,
54       F5: 116,
55       F6: 117,
56       F7: 118,
57       F8: 119,
58       F9: 120,
59       F10: 121,
60       F11: 122,
61       F12: 123
62     },
63     ASC: 'asc',
64     DESC: 'desc',
65     filter: {
66       STARTS_WITH: 2,
67       ENDS_WITH: 4,
68       EXACT: 8,
69       CONTAINS: 16,
70       GREATER_THAN: 32,
71       GREATER_THAN_OR_EQUAL: 64,
72       LESS_THAN: 128,
73       LESS_THAN_OR_EQUAL: 256,
74       NOT_EQUAL: 512
75     },
76
77     aggregationTypes: {
78       sum: 2,
79       count: 4,
80       avg: 8,
81       min: 16,
82       max: 32
83     },
84
85     // TODO(c0bra): Create full list of these somehow. NOTE: do any allow a space before or after them?
86     CURRENCY_SYMBOLS: ['ƒ', '$', '£', '$', '¤', '¥', '៛', '₩', '₱', '฿', '₫']
87   });
88
89 })();
90 angular.module('ui.grid').directive('uiGridCell', ['$compile', '$log', '$parse', 'gridUtil', 'uiGridConstants', function ($compile, $log, $parse, gridUtil, uiGridConstants) {
91   var uiGridCell = {
92     priority: 0,
93     scope: false,
94     require: '?^uiGrid',
95     compile: function() {
96       return {
97         pre: function($scope, $elm, $attrs, uiGridCtrl) {
98           function compileTemplate() {
99             var compiledElementFn = $scope.col.compiledElementFn;
100
101             compiledElementFn($scope, function(clonedElement, scope) {
102               $elm.append(clonedElement);
103             });
104           }
105
106           // If the grid controller is present, use it to get the compiled cell template function
107           if (uiGridCtrl) {
108              compileTemplate();
109           }
110           // No controller, compile the element manually (for unit tests)
111           else {
112             var html = $scope.col.cellTemplate
113               .replace(uiGridConstants.COL_FIELD, 'grid.getCellValue(row, col)');
114             var cellElement = $compile(html)($scope);
115             $elm.append(cellElement);
116           }
117         },
118         post: function($scope, $elm, $attrs, uiGridCtrl) {
119           $elm.addClass($scope.col.getColClass(false));
120           if ($scope.col.cellClass) {
121             //var contents = angular.element($elm[0].getElementsByClassName('ui-grid-cell-contents'));
122             var contents = $elm;
123             if (angular.isFunction($scope.col.cellClass)) {
124               contents.addClass($scope.col.cellClass($scope.grid, $scope.row, $scope.col, $scope.rowRenderIndex, $scope.colRenderIndex));
125             }
126             else {
127               contents.addClass($scope.col.cellClass);
128             }
129           }
130         }
131       };
132     }
133   };
134
135   return uiGridCell;
136 }]);
137
138
139 (function(){
140
141 angular.module('ui.grid').directive('uiGridColumnMenu', ['$log', '$timeout', '$window', '$document', '$injector', 'gridUtil', 'uiGridConstants', 'i18nService', function ($log, $timeout, $window, $document, $injector, gridUtil, uiGridConstants, i18nService) {
142
143   var uiGridColumnMenu = {
144     priority: 0,
145     scope: true,
146     require: '?^uiGrid',
147     templateUrl: 'ui-grid/uiGridColumnMenu',
148     replace: true,
149     link: function ($scope, $elm, $attrs, uiGridCtrl) {
150       gridUtil.enableAnimations($elm);
151
152       $scope.grid = uiGridCtrl.grid;
153
154       var self = this;
155
156       // Store a reference to this link/controller in the main uiGrid controller
157       // to allow showMenu later
158       uiGridCtrl.columnMenuScope = $scope;
159
160       // Save whether we're shown or not so the columns can check
161       self.shown = $scope.menuShown = false;
162
163       // Put asc and desc sort directions in scope
164       $scope.asc = uiGridConstants.ASC;
165       $scope.desc = uiGridConstants.DESC;
166
167       // $scope.i18n = i18nService;
168
169       // Get the grid menu element. We'll use it to calculate positioning
170       $scope.menu = $elm[0].querySelectorAll('.ui-grid-menu');
171
172       // Get the inner menu part. It's what slides up/down
173       $scope.inner = $elm[0].querySelectorAll('.ui-grid-menu-inner');
174
175       /**
176        * @ngdoc boolean
177        * @name enableSorting
178        * @propertyOf ui.grid.class:GridOptions.columnDef
179        * @description (optional) True by default. When enabled, this setting adds sort
180        * widgets to the column header, allowing sorting of the data in the individual column.
181        */
182       $scope.sortable = function() {
183         if (uiGridCtrl.grid.options.enableSorting && typeof($scope.col) !== 'undefined' && $scope.col && $scope.col.enableSorting) {
184           return true;
185         }
186         else {
187           return false;
188         }
189       };
190
191       /**
192        * @ngdoc boolean
193        * @name enableFiltering
194        * @propertyOf ui.grid.class:GridOptions.columnDef
195        * @description (optional) True by default. When enabled, this setting adds filter
196        * widgets to the column header, allowing filtering of the data in the individual column.
197        */
198       $scope.filterable = function() {
199         if (uiGridCtrl.grid.options.enableFiltering && typeof($scope.col) !== 'undefined' && $scope.col && $scope.col.enableFiltering) {
200           return true;
201         }
202         else {
203           return false;
204         }
205       };
206       
207       var defaultMenuItems = [
208         // NOTE: disabling this in favor of a little filter text box
209         // Column filter input
210         // {
211         //   templateUrl: 'ui-grid/uiGridColumnFilter',
212         //   action: function($event) {
213         //     $event.stopPropagation();
214         //     $scope.filterColumn($event);
215         //   },
216         //   cancel: function ($event) {
217         //     $event.stopPropagation();
218
219         //     $scope.col.filter = {};
220         //   },
221         //   shown: function () {
222         //     return filterable();
223         //   }
224         // },
225         {
226           title: i18nService.getSafeText('sort.ascending'),
227           icon: 'ui-grid-icon-sort-alt-up',
228           action: function($event) {
229             $event.stopPropagation();
230             $scope.sortColumn($event, uiGridConstants.ASC);
231           },
232           shown: function () {
233             return $scope.sortable();
234           },
235           active: function() {
236             return (typeof($scope.col) !== 'undefined' && typeof($scope.col.sort) !== 'undefined' && typeof($scope.col.sort.direction) !== 'undefined' && $scope.col.sort.direction === uiGridConstants.ASC);
237           }
238         },
239         {
240           title: i18nService.getSafeText('sort.descending'),
241           icon: 'ui-grid-icon-sort-alt-down',
242           action: function($event) {
243             $event.stopPropagation();
244             $scope.sortColumn($event, uiGridConstants.DESC);
245           },
246           shown: function() {
247             return $scope.sortable();
248           },
249           active: function() {
250             return (typeof($scope.col) !== 'undefined' && typeof($scope.col.sort) !== 'undefined' && typeof($scope.col.sort.direction) !== 'undefined' && $scope.col.sort.direction === uiGridConstants.DESC);
251           }
252         },
253         {
254           title: i18nService.getSafeText('sort.remove'),
255           icon: 'ui-grid-icon-cancel',
256           action: function ($event) {
257             $event.stopPropagation();
258             $scope.unsortColumn();
259           },
260           shown: function() {
261             return ($scope.sortable() && typeof($scope.col) !== 'undefined' && (typeof($scope.col.sort) !== 'undefined' && typeof($scope.col.sort.direction) !== 'undefined') && $scope.col.sort.direction !== null);
262           }
263         },
264         {
265           title: i18nService.getSafeText('column.hide'),
266           icon: 'ui-grid-icon-cancel',
267           action: function ($event) {
268             $event.stopPropagation();
269             $scope.hideColumn();
270           }
271         }
272       ];
273
274       // Set the menu items for use with the column menu. Let's the user specify extra menu items per column if they want.
275       $scope.menuItems = defaultMenuItems;
276       $scope.$watch('col.menuItems', function (n, o) {
277         if (typeof(n) !== 'undefined' && n && angular.isArray(n)) {
278           n.forEach(function (item) {
279             if (typeof(item.context) === 'undefined' || !item.context) {
280               item.context = {};
281             }
282             item.context.col = $scope.col;
283           });
284
285           $scope.menuItems = defaultMenuItems.concat(n);
286         }
287         else {
288           $scope.menuItems = defaultMenuItems;
289         }
290       });
291
292       var $animate;
293       try {
294         $animate = $injector.get('$animate');
295       }
296       catch (e) {
297         $log.info('$animate service not found (ngAnimate not add as a dependency?), menu animations will not occur');
298       }
299
300       // Show the menu
301       $scope.showMenu = function(column, $columnElement) {
302         // Swap to this column
303         //   note - store a reference to this column in 'self' so the columns can check whether they're the shown column or not
304         self.col = $scope.col = column;
305
306         // Remove an existing document click handler
307         $document.off('click', documentClick);
308
309         /* Reposition the menu below this column's element */
310         var left = $columnElement[0].offsetLeft;
311         var top = $columnElement[0].offsetTop;
312
313         // Get the grid scrollLeft
314         var offset = 0;
315         if (column.grid.options.offsetLeft) {
316           offset = column.grid.options.offsetLeft;
317         }
318
319         var height = gridUtil.elementHeight($columnElement, true);
320         var width = gridUtil.elementWidth($columnElement, true);
321
322         // Flag for whether we're hidden for showing via $animate
323         var hidden = false;
324
325         // Re-position the menu AFTER it's been shown, so we can calculate the width correctly.
326         function reposition() {
327           $timeout(function() {
328             if (hidden && $animate) {
329               $animate.removeClass($scope.inner, 'ng-hide');
330               self.shown = $scope.menuShown = true;
331               $scope.$broadcast('show-menu');
332             }
333             else if (angular.element($scope.inner).hasClass('ng-hide')) {
334               angular.element($scope.inner).removeClass('ng-hide');
335             }
336
337             // var containerScrollLeft = $columnelement
338             var containerId = column.renderContainer ? column.renderContainer : 'body';
339             var renderContainer = column.grid.renderContainers[containerId];
340             // var containerScrolLeft = renderContainer.prevScrollLeft;
341
342             // It's possible that the render container of the column we're attaching to is offset from the grid (i.e. pinned containers), we
343             //   need to get the different in the offsetLeft between the render container and the grid
344             var renderContainerElm = gridUtil.closestElm($columnElement, '.ui-grid-render-container');
345             var renderContainerOffset = renderContainerElm.offsetLeft - $scope.grid.element[0].offsetLeft;
346
347             var containerScrolLeft = renderContainerElm.querySelectorAll('.ui-grid-viewport')[0].scrollLeft;
348
349             var myWidth = gridUtil.elementWidth($scope.menu, true);
350
351             // TODO(c0bra): use padding-left/padding-right based on document direction (ltr/rtl), place menu on proper side
352             // Get the column menu right padding
353             var paddingRight = parseInt(angular.element($scope.menu).css('padding-right'), 10);
354
355             // $log.debug('position', left + ' + ' + width + ' - ' + myWidth + ' + ' + paddingRight);
356
357             $elm.css('left', (left + renderContainerOffset - containerScrolLeft + width - myWidth + paddingRight) + 'px');
358             $elm.css('top', (top + height) + 'px');
359
360             // Hide the menu on a click on the document
361             $document.on('click', documentClick);
362           });
363         }
364
365         if ($scope.menuShown && $animate) {
366           // Animate closing the menu on the current column, then animate it opening on the other column
367           $animate.addClass($scope.inner, 'ng-hide', reposition);
368           hidden = true;
369         }
370         else {
371           self.shown = $scope.menuShown = true;
372           $scope.$broadcast('show-menu');
373           reposition();
374         }
375       };
376
377       // Hide the menu
378       $scope.hideMenu = function() {
379         delete self.col;
380         delete $scope.col;
381         self.shown = $scope.menuShown = false;
382         $scope.$broadcast('hide-menu');
383       };
384
385       // Prevent clicks on the menu from bubbling up to the document and making it hide prematurely
386       // $elm.on('click', function (event) {
387       //   event.stopPropagation();
388       // });
389
390       function documentClick() {
391         $scope.$apply($scope.hideMenu);
392         $document.off('click', documentClick);
393       }
394       
395       function resizeHandler() {
396         $scope.$apply($scope.hideMenu);
397       }
398       angular.element($window).bind('resize', resizeHandler);
399
400       $scope.$on('$destroy', $scope.$on(uiGridConstants.events.GRID_SCROLL, function(evt, args) {
401         $scope.hideMenu();
402         // if (!$scope.$$phase) { $scope.$apply(); }
403       }));
404
405       $scope.$on('$destroy', $scope.$on(uiGridConstants.events.ITEM_DRAGGING, function(evt, args) {
406         $scope.hideMenu();
407         // if (!$scope.$$phase) { $scope.$apply(); }
408       }));
409
410       $scope.$on('$destroy', function() {
411         angular.element($window).off('resize', resizeHandler);
412         $document.off('click', documentClick);
413       });
414
415       /* Column methods */
416       $scope.sortColumn = function (event, dir) {
417         event.stopPropagation();
418
419         uiGridCtrl.grid.sortColumn($scope.col, dir, true)
420           .then(function () {
421             uiGridCtrl.grid.refresh();
422             $scope.hideMenu();
423           });
424       };
425
426       $scope.unsortColumn = function () {
427         $scope.col.unsort();
428
429         uiGridCtrl.grid.refresh();
430         $scope.hideMenu();
431       };
432
433       $scope.hideColumn = function () {
434         $scope.col.colDef.visible = false;
435
436         uiGridCtrl.grid.refresh();
437         $scope.hideMenu();
438       };
439     },
440     controller: ['$scope', function ($scope) {
441       var self = this;
442       
443       $scope.$watch('menuItems', function (n, o) {
444         self.menuItems = n;
445       });
446     }]
447   };
448
449   return uiGridColumnMenu;
450
451 }]);
452
453 })();
454 (function () {
455   'use strict';
456
457   angular.module('ui.grid').directive('uiGridFooterCell', ['$log', '$timeout', 'gridUtil', '$compile', function ($log, $timeout, gridUtil, $compile) {
458     var uiGridFooterCell = {
459       priority: 0,
460       scope: {
461         col: '=',
462         row: '=',
463         renderIndex: '='
464       },
465       replace: true,
466       require: '^uiGrid',
467       compile: function compile(tElement, tAttrs, transclude) {
468         return {
469           pre: function ($scope, $elm, $attrs, uiGridCtrl) {
470             function compileTemplate(template) {
471               gridUtil.getTemplate(template).then(function (contents) {
472                 var linkFunction = $compile(contents);
473                 var html = linkFunction($scope);
474                 $elm.append(html);
475               });
476             }
477
478             //compile the footer template
479             if ($scope.col.footerCellTemplate) {
480               //compile the custom template
481               compileTemplate($scope.col.footerCellTemplate);
482             }
483             else {
484               //use default template
485               compileTemplate('ui-grid/uiGridFooterCell');
486             }
487           },
488           post: function ($scope, $elm, $attrs, uiGridCtrl) {
489             //$elm.addClass($scope.col.getColClass(false));
490             $scope.grid = uiGridCtrl.grid;
491
492             $elm.addClass($scope.col.getColClass(false));
493           }
494         };
495       }
496     };
497
498     return uiGridFooterCell;
499   }]);
500
501 })();
502
503 (function () {
504   'use strict';
505
506   angular.module('ui.grid').directive('uiGridFooter', ['$log', '$templateCache', '$compile', 'uiGridConstants', 'gridUtil', '$timeout', function ($log, $templateCache, $compile, uiGridConstants, gridUtil, $timeout) {
507     var defaultTemplate = 'ui-grid/ui-grid-footer';
508
509     return {
510       restrict: 'EA',
511       replace: true,
512       // priority: 1000,
513       require: ['^uiGrid', '^uiGridRenderContainer'],
514       scope: true,
515       compile: function ($elm, $attrs) {
516         return {
517           pre: function ($scope, $elm, $attrs, controllers) {
518             var uiGridCtrl = controllers[0];
519             var containerCtrl = controllers[1];
520
521             $scope.grid = uiGridCtrl.grid;
522             $scope.colContainer = containerCtrl.colContainer;
523
524             containerCtrl.footer = $elm;
525
526             var footerTemplate = ($scope.grid.options.footerTemplate) ? $scope.grid.options.footerTemplate : defaultTemplate;
527             gridUtil.getTemplate(footerTemplate)
528               .then(function (contents) {
529                 var template = angular.element(contents);
530
531                 var newElm = $compile(template)($scope);
532                 $elm.append(newElm);
533
534                 if (containerCtrl) {
535                   // Inject a reference to the footer viewport (if it exists) into the grid controller for use in the horizontal scroll handler below
536                   var footerViewport = $elm[0].getElementsByClassName('ui-grid-footer-viewport')[0];
537
538                   if (footerViewport) {
539                     containerCtrl.footerViewport = footerViewport;
540                   }
541                 }
542               });
543           },
544
545           post: function ($scope, $elm, $attrs, controllers) {
546             var uiGridCtrl = controllers[0];
547             var containerCtrl = controllers[1];
548
549             $log.debug('ui-grid-footer link');
550
551             var grid = uiGridCtrl.grid;
552
553             // Don't animate footer cells
554             gridUtil.disableAnimations($elm);
555
556             containerCtrl.footer = $elm;
557
558             var footerViewport = $elm[0].getElementsByClassName('ui-grid-footer-viewport')[0];
559             if (footerViewport) {
560               containerCtrl.footerViewport = footerViewport;
561             }
562           }
563         };
564       }
565     };
566   }]);
567
568 })();
569 (function(){
570   'use strict';
571
572   angular.module('ui.grid').directive('uiGridGroupPanel', ["$compile", "uiGridConstants", "gridUtil", function($compile, uiGridConstants, gridUtil) {
573     var defaultTemplate = 'ui-grid/ui-grid-group-panel';
574
575     return {
576       restrict: 'EA',
577       replace: true,
578       require: '?^uiGrid',
579       scope: false,
580       compile: function($elm, $attrs) {
581         return {
582           pre: function ($scope, $elm, $attrs, uiGridCtrl) {
583             var groupPanelTemplate = $scope.grid.options.groupPanelTemplate  || defaultTemplate;
584
585              gridUtil.getTemplate(groupPanelTemplate)
586               .then(function (contents) {
587                 var template = angular.element(contents);
588                 
589                 var newElm = $compile(template)($scope);
590                 $elm.append(newElm);
591               });
592           },
593
594           post: function ($scope, $elm, $attrs, uiGridCtrl) {
595             $elm.bind('$destroy', function() {
596               // scrollUnbinder();
597             });
598           }
599         };
600       }
601     };
602   }]);
603
604 })();
605 (function(){
606   'use strict';
607
608   angular.module('ui.grid').directive('uiGridHeaderCell', ['$log', '$compile', '$timeout', '$window', '$document', 'gridUtil', 'uiGridConstants', 
609   function ($log, $compile, $timeout, $window, $document, gridUtil, uiGridConstants) {
610     // Do stuff after mouse has been down this many ms on the header cell
611     var mousedownTimeout = 500;
612
613     var uiGridHeaderCell = {
614       priority: 0,
615       scope: {
616         col: '=',
617         row: '=',
618         renderIndex: '='
619       },
620       require: '?^uiGrid',
621       replace: true,
622       compile: function() {
623         return {
624           pre: function ($scope, $elm, $attrs, uiGridCtrl) {
625             var cellHeader = $compile($scope.col.headerCellTemplate)($scope);
626             $elm.append(cellHeader);
627           },
628           
629           post: function ($scope, $elm, $attrs, uiGridCtrl) {
630             $scope.grid = uiGridCtrl.grid;
631             
632             /**
633              * @ngdoc event
634              * @name filterChanged
635              * @eventOf  ui.grid.core.api:PublicApi
636              * @description  is raised after the filter is changed.  The nature
637              * of the watch expression doesn't allow notification of what changed,
638              * so the receiver of this event will need to re-extract the filter 
639              * conditions from the columns.
640              * 
641              */
642             if (!$scope.grid.api.core.raise.filterChanged){
643               $scope.grid.api.registerEvent( 'core', 'filterChanged' );
644             }
645                         
646     
647             $elm.addClass($scope.col.getColClass(false));
648     // shane - No need for watch now that we trackby col name
649     //        $scope.$watch('col.index', function (newValue, oldValue) {
650     //          if (newValue === oldValue) { return; }
651     //          var className = $elm.attr('class');
652     //          className = className.replace(uiGridConstants.COL_CLASS_PREFIX + oldValue, uiGridConstants.COL_CLASS_PREFIX + newValue);
653     //          $elm.attr('class', className);
654     //        });
655     
656             // Hide the menu by default
657             $scope.menuShown = false;
658     
659             // Put asc and desc sort directions in scope
660             $scope.asc = uiGridConstants.ASC;
661             $scope.desc = uiGridConstants.DESC;
662     
663             // Store a reference to menu element
664             var $colMenu = angular.element( $elm[0].querySelectorAll('.ui-grid-header-cell-menu') );
665     
666             var $contentsElm = angular.element( $elm[0].querySelectorAll('.ui-grid-cell-contents') );
667     
668             // Figure out whether this column is sortable or not
669             if (uiGridCtrl.grid.options.enableSorting && $scope.col.enableSorting) {
670               $scope.sortable = true;
671             }
672             else {
673               $scope.sortable = false;
674             }
675     
676             if (uiGridCtrl.grid.options.enableFiltering && $scope.col.enableFiltering) {
677               $scope.filterable = true;
678             }
679             else {
680               $scope.filterable = false;
681             }
682     
683             function handleClick(evt) {
684               // If the shift key is being held down, add this column to the sort
685               var add = false;
686               if (evt.shiftKey) {
687                 add = true;
688               }
689     
690               // Sort this column then rebuild the grid's rows
691               uiGridCtrl.grid.sortColumn($scope.col, add)
692                 .then(function () {
693                   if (uiGridCtrl.columnMenuScope) { uiGridCtrl.columnMenuScope.hideMenu(); }
694                   uiGridCtrl.grid.refresh();
695                 });
696             }
697     
698             // Long-click (for mobile)
699             var cancelMousedownTimeout;
700             var mousedownStartTime = 0;
701             $contentsElm.on('mousedown', function(event) {
702               if (typeof(event.originalEvent) !== 'undefined' && event.originalEvent !== undefined) {
703                 event = event.originalEvent;
704               }
705     
706               // Don't show the menu if it's not the left button
707               if (event.button && event.button !== 0) {
708                 return;
709               }
710     
711               mousedownStartTime = (new Date()).getTime();
712     
713               cancelMousedownTimeout = $timeout(function() { }, mousedownTimeout);
714     
715               cancelMousedownTimeout.then(function () {
716                 uiGridCtrl.columnMenuScope.showMenu($scope.col, $elm);
717               });
718             });
719     
720             $contentsElm.on('mouseup', function () {
721               $timeout.cancel(cancelMousedownTimeout);
722             });
723     
724             $scope.toggleMenu = function($event) {
725               $event.stopPropagation();
726     
727               // If the menu is already showing...
728               if (uiGridCtrl.columnMenuScope.menuShown) {
729                 // ... and we're the column the menu is on...
730                 if (uiGridCtrl.columnMenuScope.col === $scope.col) {
731                   // ... hide it
732                   uiGridCtrl.columnMenuScope.hideMenu();
733                 }
734                 // ... and we're NOT the column the menu is on
735                 else {
736                   // ... move the menu to our column
737                   uiGridCtrl.columnMenuScope.showMenu($scope.col, $elm);
738                 }
739               }
740               // If the menu is NOT showing
741               else {
742                 // ... show it on our column
743                 uiGridCtrl.columnMenuScope.showMenu($scope.col, $elm);
744               }
745             };
746     
747             // If this column is sortable, add a click event handler
748             if ($scope.sortable) {
749               $contentsElm.on('click', function(evt) {
750                 evt.stopPropagation();
751     
752                 $timeout.cancel(cancelMousedownTimeout);
753     
754                 var mousedownEndTime = (new Date()).getTime();
755                 var mousedownTime = mousedownEndTime - mousedownStartTime;
756     
757                 if (mousedownTime > mousedownTimeout) {
758                   // long click, handled above with mousedown
759                 }
760                 else {
761                   // short click
762                   handleClick(evt);
763                 }
764               });
765     
766               $scope.$on('$destroy', function () {
767                 // Cancel any pending long-click timeout
768                 $timeout.cancel(cancelMousedownTimeout);
769               });
770             }
771     
772             if ($scope.filterable) {
773               var filterDeregisters = [];
774               angular.forEach($scope.col.filters, function(filter, i) {
775                 filterDeregisters.push($scope.$watch('col.filters[' + i + '].term', function(n, o) {
776                   uiGridCtrl.grid.api.core.raise.filterChanged();
777                   uiGridCtrl.grid.refresh()
778                     .then(function () {
779                       if (uiGridCtrl.prevScrollArgs && uiGridCtrl.prevScrollArgs.y && uiGridCtrl.prevScrollArgs.y.percentage) {
780                          uiGridCtrl.fireScrollingEvent({ y: { percentage: uiGridCtrl.prevScrollArgs.y.percentage } });
781                       }
782                       // uiGridCtrl.fireEvent('force-vertical-scroll');
783                     });
784                 }));  
785               });
786               $scope.$on('$destroy', function() {
787                 angular.forEach(filterDeregisters, function(filterDeregister) {
788                   filterDeregister();
789                 });
790               });
791             }
792           }
793         };
794       }
795     };
796
797     return uiGridHeaderCell;
798   }]);
799
800 })();
801
802 (function(){
803   'use strict';
804
805   angular.module('ui.grid').directive('uiGridHeader', ['$log', '$templateCache', '$compile', 'uiGridConstants', 'gridUtil', '$timeout', function($log, $templateCache, $compile, uiGridConstants, gridUtil, $timeout) {
806     var defaultTemplate = 'ui-grid/ui-grid-header';
807     var emptyTemplate = 'ui-grid/ui-grid-no-header';
808
809     return {
810       restrict: 'EA',
811       // templateUrl: 'ui-grid/ui-grid-header',
812       replace: true,
813       // priority: 1000,
814       require: ['^uiGrid', '^uiGridRenderContainer'],
815       scope: true,
816       compile: function($elm, $attrs) {
817         return {
818           pre: function ($scope, $elm, $attrs, controllers) {
819             var uiGridCtrl = controllers[0];
820             var containerCtrl = controllers[1];
821
822             $scope.grid = uiGridCtrl.grid;
823             $scope.colContainer = containerCtrl.colContainer;
824
825             containerCtrl.header = $elm;
826             containerCtrl.colContainer.header = $elm;
827
828             /**
829              * @ngdoc property
830              * @name hideHeader
831              * @propertyOf ui.grid.class:GridOptions
832              * @description Null by default. When set to true, this setting will replace the
833              * standard header template with '<div></div>', resulting in no header being shown.
834              */
835             
836             var headerTemplate;
837             if ($scope.grid.options.hideHeader){
838               headerTemplate = emptyTemplate;
839             } else {
840               headerTemplate = ($scope.grid.options.headerTemplate) ? $scope.grid.options.headerTemplate : defaultTemplate;            
841             }
842
843              gridUtil.getTemplate(headerTemplate)
844               .then(function (contents) {
845                 var template = angular.element(contents);
846                 
847                 var newElm = $compile(template)($scope);
848                 $elm.append(newElm);
849
850                 if (containerCtrl) {
851                   // Inject a reference to the header viewport (if it exists) into the grid controller for use in the horizontal scroll handler below
852                   var headerViewport = $elm[0].getElementsByClassName('ui-grid-header-viewport')[0];
853
854                   if (headerViewport) {
855                     containerCtrl.headerViewport = headerViewport;
856                   }
857                 }
858               });
859           },
860
861           post: function ($scope, $elm, $attrs, controllers) {
862             var uiGridCtrl = controllers[0];
863             var containerCtrl = controllers[1];
864
865             $log.debug('ui-grid-header link');
866
867             var grid = uiGridCtrl.grid;
868
869             // Don't animate header cells
870             gridUtil.disableAnimations($elm);
871
872             function updateColumnWidths() {
873               var asterisksArray = [],
874                   percentArray = [],
875                   manualArray = [],
876                   asteriskNum = 0,
877                   totalWidth = 0;
878
879               // Get the width of the viewport
880               var availableWidth = containerCtrl.colContainer.getViewportWidth();
881
882               if (typeof(uiGridCtrl.grid.verticalScrollbarWidth) !== 'undefined' && uiGridCtrl.grid.verticalScrollbarWidth !== undefined && uiGridCtrl.grid.verticalScrollbarWidth > 0) {
883                 availableWidth = availableWidth + uiGridCtrl.grid.verticalScrollbarWidth;
884               }
885
886               // The total number of columns
887               // var equalWidthColumnCount = columnCount = uiGridCtrl.grid.options.columnDefs.length;
888               // var equalWidth = availableWidth / equalWidthColumnCount;
889
890               // The last column we processed
891               var lastColumn;
892
893               var manualWidthSum = 0;
894
895               var canvasWidth = 0;
896
897               var ret = '';
898
899
900               // uiGridCtrl.grid.columns.forEach(function(column, i) {
901
902               var columnCache = containerCtrl.colContainer.visibleColumnCache;
903
904               columnCache.forEach(function(column, i) {
905                 // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + i + ' { width: ' + equalWidth + 'px; left: ' + left + 'px; }';
906                 //var colWidth = (typeof(c.width) !== 'undefined' && c.width !== undefined) ? c.width : equalWidth;
907
908                 // Skip hidden columns
909                 if (!column.visible) { return; }
910
911                 var colWidth,
912                     isPercent = false;
913
914                 if (!angular.isNumber(column.width)) {
915                   isPercent = isNaN(column.width) ? gridUtil.endsWith(column.width, "%") : false;
916                 }
917
918                 if (angular.isString(column.width) && column.width.indexOf('*') !== -1) { //  we need to save it until the end to do the calulations on the remaining width.
919                   asteriskNum = parseInt(asteriskNum + column.width.length, 10);
920                   
921                   asterisksArray.push(column);
922                 }
923                 else if (isPercent) { // If the width is a percentage, save it until the very last.
924                   percentArray.push(column);
925                 }
926                 else if (angular.isNumber(column.width)) {
927                   manualWidthSum = parseInt(manualWidthSum + column.width, 10);
928                   
929                   canvasWidth = parseInt(canvasWidth, 10) + parseInt(column.width, 10);
930
931                   column.drawnWidth = column.width;
932
933                   // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + column.width + 'px; }';
934                 }
935               });
936
937               // Get the remaining width (available width subtracted by the manual widths sum)
938               var remainingWidth = availableWidth - manualWidthSum;
939
940               var i, column, colWidth;
941
942               if (percentArray.length > 0) {
943                 // Pre-process to make sure they're all within any min/max values
944                 for (i = 0; i < percentArray.length; i++) {
945                   column = percentArray[i];
946
947                   var percent = parseInt(column.width.replace(/%/g, ''), 10) / 100;
948
949                   colWidth = parseInt(percent * remainingWidth, 10);
950
951                   if (column.colDef.minWidth && colWidth < column.colDef.minWidth) {
952                     colWidth = column.colDef.minWidth;
953
954                     remainingWidth = remainingWidth - colWidth;
955
956                     canvasWidth += colWidth;
957                     column.drawnWidth = colWidth;
958
959                     // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
960
961                     // Remove this element from the percent array so it's not processed below
962                     percentArray.splice(i, 1);
963                   }
964                   else if (column.colDef.maxWidth && colWidth > column.colDef.maxWidth) {
965                     colWidth = column.colDef.maxWidth;
966
967                     remainingWidth = remainingWidth - colWidth;
968
969                     canvasWidth += colWidth;
970                     column.drawnWidth = colWidth;
971
972                     // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
973
974                     // Remove this element from the percent array so it's not processed below
975                     percentArray.splice(i, 1);
976                   }
977                 }
978
979                 percentArray.forEach(function(column) {
980                   var percent = parseInt(column.width.replace(/%/g, ''), 10) / 100;
981                   var colWidth = parseInt(percent * remainingWidth, 10);
982
983                   canvasWidth += colWidth;
984
985                   column.drawnWidth = colWidth;
986
987                   // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
988                 });
989               }
990
991               if (asterisksArray.length > 0) {
992                 var asteriskVal = parseInt(remainingWidth / asteriskNum, 10);
993
994                  // Pre-process to make sure they're all within any min/max values
995                 for (i = 0; i < asterisksArray.length; i++) {
996                   column = asterisksArray[i];
997
998                   colWidth = parseInt(asteriskVal * column.width.length, 10);
999
1000                   if (column.colDef.minWidth && colWidth < column.colDef.minWidth) {
1001                     colWidth = column.colDef.minWidth;
1002
1003                     remainingWidth = remainingWidth - colWidth;
1004                     asteriskNum--;
1005
1006                     canvasWidth += colWidth;
1007                     column.drawnWidth = colWidth;
1008
1009                     // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
1010
1011                     lastColumn = column;
1012
1013                     // Remove this element from the percent array so it's not processed below
1014                     asterisksArray.splice(i, 1);
1015                   }
1016                   else if (column.colDef.maxWidth && colWidth > column.colDef.maxWidth) {
1017                     colWidth = column.colDef.maxWidth;
1018
1019                     remainingWidth = remainingWidth - colWidth;
1020                     asteriskNum--;
1021
1022                     canvasWidth += colWidth;
1023                     column.drawnWidth = colWidth;
1024
1025                     // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
1026
1027                     // Remove this element from the percent array so it's not processed below
1028                     asterisksArray.splice(i, 1);
1029                   }
1030                 }
1031
1032                 // Redo the asterisk value, as we may have removed columns due to width constraints
1033                 asteriskVal = parseInt(remainingWidth / asteriskNum, 10);
1034
1035                 asterisksArray.forEach(function(column) {
1036                   var colWidth = parseInt(asteriskVal * column.width.length, 10);
1037
1038                   canvasWidth += colWidth;
1039
1040                   column.drawnWidth = colWidth;
1041
1042                   // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
1043                 });
1044               }
1045
1046               // If the grid width didn't divide evenly into the column widths and we have pixels left over, dole them out to the columns one by one to make everything fit
1047               var leftoverWidth = availableWidth - parseInt(canvasWidth, 10);
1048
1049               if (leftoverWidth > 0 && canvasWidth > 0 && canvasWidth < availableWidth) {
1050                 var variableColumn = false;
1051                 // uiGridCtrl.grid.columns.forEach(function(col) {
1052                 columnCache.forEach(function(col) {
1053                   if (col.width && !angular.isNumber(col.width)) {
1054                     variableColumn = true;
1055                   }
1056                 });
1057
1058                 if (variableColumn) {
1059                   var remFn = function (column) {
1060                     if (leftoverWidth > 0) {
1061                       column.drawnWidth = column.drawnWidth + 1;
1062                       canvasWidth = canvasWidth + 1;
1063                       leftoverWidth--;
1064                     }
1065                   };
1066                   while (leftoverWidth > 0) {
1067                     columnCache.forEach(remFn);
1068                   }
1069                 }
1070               }
1071
1072               if (canvasWidth < availableWidth) {
1073                 canvasWidth = availableWidth;
1074               }
1075
1076               // Build the CSS
1077               // uiGridCtrl.grid.columns.forEach(function (column) {
1078               columnCache.forEach(function (column) {
1079                 ret = ret + column.getColClassDefinition();
1080               });
1081
1082               // Add the vertical scrollbar width back in to the canvas width, it's taken out in getCanvasWidth
1083               if (grid.verticalScrollbarWidth) {
1084                 canvasWidth = canvasWidth + grid.verticalScrollbarWidth;
1085               }
1086               // canvasWidth = canvasWidth + 1;
1087
1088               containerCtrl.colContainer.canvasWidth = parseInt(canvasWidth, 10);
1089
1090               // Return the styles back to buildStyles which pops them into the `customStyles` scope variable
1091               return ret;
1092             }
1093             
1094             containerCtrl.header = $elm;
1095             
1096             var headerViewport = $elm[0].getElementsByClassName('ui-grid-header-viewport')[0];
1097             if (headerViewport) {
1098               containerCtrl.headerViewport = headerViewport;
1099             }
1100
1101             //todo: remove this if by injecting gridCtrl into unit tests
1102             if (uiGridCtrl) {
1103               uiGridCtrl.grid.registerStyleComputation({
1104                 priority: 5,
1105                 func: updateColumnWidths
1106               });
1107             }
1108           }
1109         };
1110       }
1111     };
1112   }]);
1113
1114 })();
1115 (function(){
1116
1117 /**
1118  * @ngdoc directive
1119  * @name ui.grid.directive:uiGridColumnMenu
1120  * @element style
1121  * @restrict A
1122  *
1123  * @description
1124  * Allows us to interpolate expressions in `<style>` elements. Angular doesn't do this by default as it can/will/might? break in IE8.
1125  *
1126  * @example
1127  <doc:example module="app">
1128  <doc:source>
1129  <script>
1130  var app = angular.module('app', ['ui.grid']);
1131
1132  app.controller('MainCtrl', ['$scope', function ($scope) {
1133    
1134  }]);
1135  </script>
1136
1137  <div ng-controller="MainCtrl">
1138    <div ui-grid-menu shown="true"  ></div>
1139  </div>
1140  </doc:source>
1141  <doc:scenario>
1142  </doc:scenario>
1143  </doc:example>
1144  */
1145 angular.module('ui.grid')
1146
1147 .directive('uiGridMenu', ['$log', '$compile', '$timeout', '$window', '$document', 'gridUtil', function ($log, $compile, $timeout, $window, $document, gridUtil) {
1148   var uiGridMenu = {
1149     priority: 0,
1150     scope: {
1151       // shown: '&',
1152       menuItems: '=',
1153       autoHide: '=?'
1154     },
1155     require: '?^uiGrid',
1156     templateUrl: 'ui-grid/uiGridMenu',
1157     replace: false,
1158     link: function ($scope, $elm, $attrs, uiGridCtrl) {
1159       gridUtil.enableAnimations($elm);
1160
1161       if (typeof($scope.autoHide) === 'undefined' || $scope.autoHide === undefined) {
1162         $scope.autoHide = true;
1163       }
1164
1165       if ($scope.autoHide) {
1166         angular.element($window).on('resize', $scope.hideMenu);
1167       }
1168
1169       $scope.$on('hide-menu', function () {
1170         $scope.shown = false;
1171       });
1172
1173       $scope.$on('show-menu', function () {
1174         $scope.shown = true;
1175       });
1176
1177       $scope.$on('$destroy', function() {
1178         angular.element($window).off('resize', $scope.hideMenu);
1179       });
1180     },
1181     controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
1182       var self = this;
1183
1184       self.hideMenu = $scope.hideMenu = function() {
1185         $scope.shown = false;
1186       };
1187
1188       function documentClick() {
1189         $scope.$apply(function () {
1190           self.hideMenu();
1191           angular.element(document).off('click', documentClick);
1192         });
1193       }
1194
1195       self.showMenu = $scope.showMenu = function() {
1196         $scope.shown = true;
1197
1198         // Turn off an existing dpcument click handler
1199         angular.element(document).off('click', documentClick);
1200
1201         // Turn on the document click handler, but in a timeout so it doesn't apply to THIS click if there is one
1202         $timeout(function() {
1203           angular.element(document).on('click', documentClick);
1204         });
1205       };
1206
1207       $scope.$on('$destroy', function () {
1208         angular.element(document).off('click', documentClick);
1209       });
1210     }]
1211   };
1212
1213   return uiGridMenu;
1214 }])
1215
1216 .directive('uiGridMenuItem', ['$log', 'gridUtil', '$compile', 'i18nService', function ($log, gridUtil, $compile, i18nService) {
1217   var uiGridMenuItem = {
1218     priority: 0,
1219     scope: {
1220       title: '=',
1221       active: '=',
1222       action: '=',
1223       icon: '=',
1224       shown: '=',
1225       context: '=',
1226       templateUrl: '='
1227     },
1228     require: ['?^uiGrid', '^uiGridMenu'],
1229     templateUrl: 'ui-grid/uiGridMenuItem',
1230     replace: true,
1231     compile: function($elm, $attrs) {
1232       return {
1233         pre: function ($scope, $elm, $attrs, controllers) {
1234           var uiGridCtrl = controllers[0],
1235               uiGridMenuCtrl = controllers[1];
1236           
1237           if ($scope.templateUrl) {
1238             gridUtil.getTemplate($scope.templateUrl)
1239                 .then(function (contents) {
1240                   var template = angular.element(contents);
1241                     
1242                   var newElm = $compile(template)($scope);
1243                   $elm.replaceWith(newElm);
1244                 });
1245           }
1246         },
1247         post: function ($scope, $elm, $attrs, controllers) {
1248           var uiGridCtrl = controllers[0],
1249               uiGridMenuCtrl = controllers[1];
1250
1251           // TODO(c0bra): validate that shown and active are functions if they're defined. An exception is already thrown above this though
1252           // if (typeof($scope.shown) !== 'undefined' && $scope.shown && typeof($scope.shown) !== 'function') {
1253           //   throw new TypeError("$scope.shown is defined but not a function");
1254           // }
1255           if (typeof($scope.shown) === 'undefined' || $scope.shown === null) {
1256             $scope.shown = function() { return true; };
1257           }
1258
1259           $scope.itemShown = function () {
1260             var context = {};
1261             if ($scope.context) {
1262               context.context = $scope.context;
1263             }
1264
1265             if (typeof(uiGridCtrl) !== 'undefined' && uiGridCtrl) {
1266               context.grid = uiGridCtrl.grid;
1267             }
1268
1269             return $scope.shown.call(context);
1270           };
1271
1272           $scope.itemAction = function($event,title) {
1273             $log.debug('itemAction');
1274             $event.stopPropagation();
1275
1276             if (typeof($scope.action) === 'function') {
1277               var context = {};
1278
1279               if ($scope.context) {
1280                 context.context = $scope.context;
1281               }
1282
1283               // Add the grid to the function call context if the uiGrid controller is present
1284               if (typeof(uiGridCtrl) !== 'undefined' && uiGridCtrl) {
1285                 context.grid = uiGridCtrl.grid;
1286               }
1287
1288               $scope.action.call(context, $event, title);
1289
1290               uiGridMenuCtrl.hideMenu();
1291             }
1292           };
1293
1294           $scope.i18n = i18nService.get();
1295         }
1296       };
1297     }
1298   };
1299
1300   return uiGridMenuItem;
1301 }]);
1302
1303 })();
1304 (function () {
1305 // 'use strict';
1306
1307   angular.module('ui.grid').directive('uiGridNativeScrollbar', ['$log', '$timeout', '$document', 'uiGridConstants', 'gridUtil',
1308     function ($log, $timeout, $document, uiGridConstants, gridUtil) {
1309     var scrollBarWidth = gridUtil.getScrollbarWidth();
1310     scrollBarWidth = scrollBarWidth > 0 ? scrollBarWidth : 17;
1311
1312     // If the browser is IE, add 1px to the scrollbar container, otherwise scroll events won't work right (in IE11 at least)
1313     var browser = gridUtil.detectBrowser();
1314     if (browser === 'ie') {
1315       scrollBarWidth = scrollBarWidth + 1;
1316     }
1317
1318     return {
1319       scope: {
1320         type: '@'
1321       },
1322       require: ['^uiGrid', '^uiGridRenderContainer'],
1323       link: function ($scope, $elm, $attrs, controllers) {
1324         var uiGridCtrl = controllers[0];
1325         var containerCtrl = controllers[1];
1326         var rowContainer = containerCtrl.rowContainer;
1327         var colContainer = containerCtrl.colContainer;
1328         var grid = uiGridCtrl.grid;
1329
1330         var contents = angular.element('<div class="contents">&nbsp;</div>');
1331
1332         $elm.addClass('ui-grid-native-scrollbar');
1333
1334         var previousScrollPosition;
1335
1336         var elmMaxScroll = 0;
1337
1338         if ($scope.type === 'vertical') {
1339           // Update the width based on native scrollbar width
1340           $elm.css('width', scrollBarWidth + 'px');
1341
1342           $elm.addClass('vertical');
1343
1344           grid.verticalScrollbarWidth = scrollBarWidth;
1345           colContainer.verticalScrollbarWidth = scrollBarWidth;
1346
1347           // Save the initial scroll position for use in scroll events
1348           previousScrollPosition = $elm[0].scrollTop;
1349         }
1350         else if ($scope.type === 'horizontal') {
1351           // Update the height based on native scrollbar height
1352           $elm.css('height', scrollBarWidth + 'px');
1353
1354           $elm.addClass('horizontal');
1355
1356           // Save this scrollbar's dimension in the grid properties
1357           grid.horizontalScrollbarHeight = scrollBarWidth;
1358           rowContainer.horizontalScrollbarHeight = scrollBarWidth;
1359
1360           // Save the initial scroll position for use in scroll events
1361           previousScrollPosition = gridUtil.normalizeScrollLeft($elm);
1362         }
1363
1364         // Save the contents elm inside the scrollbar elm so it sizes correctly
1365         $elm.append(contents);
1366
1367         // Get the relevant element dimension now that the contents are in it
1368         if ($scope.type === 'vertical') {
1369           elmMaxScroll = gridUtil.elementHeight($elm);
1370         }
1371         else if ($scope.type === 'horizontal') {
1372           elmMaxScroll = gridUtil.elementWidth($elm);
1373         }
1374
1375         function updateNativeVerticalScrollbar() {
1376           // Get the height that the scrollbar should have
1377           var height = rowContainer.getViewportHeight();
1378
1379           // Update the vertical scrollbar's content height so it's the same as the canvas
1380           var contentHeight = rowContainer.getCanvasHeight();
1381
1382           // TODO(c0bra): set scrollbar `top` by height of header row
1383           // var headerHeight = gridUtil.outerElementHeight(containerCtrl.header);
1384           var headerHeight = colContainer.headerHeight ? colContainer.headerHeight : grid.headerHeight;
1385
1386           // $log.debug('headerHeight in scrollbar', headerHeight);
1387
1388           // var ret = '.grid' + uiGridCtrl.grid.id + ' .ui-grid-native-scrollbar.vertical .contents { height: ' + h + 'px; }';
1389           var ret = '.grid' + grid.id + ' .ui-grid-render-container-' + containerCtrl.containerId + ' .ui-grid-native-scrollbar.vertical .contents { height: ' + contentHeight + 'px; }';
1390           ret += '\n .grid' + grid.id + ' .ui-grid-render-container-' + containerCtrl.containerId + ' .ui-grid-native-scrollbar.vertical { height: ' + height + 'px; top: ' + headerHeight + 'px}';
1391
1392           elmMaxScroll = contentHeight;
1393
1394           return ret;
1395         }
1396
1397         // Get the grid's bottom border height (TODO(c0bra): need to account for footer here!)
1398         var gridElm = gridUtil.closestElm($elm, '.ui-grid');
1399         var gridBottomBorder = gridUtil.getBorderSize(gridElm, 'bottom');
1400
1401         function updateNativeHorizontalScrollbar() {
1402           var w = colContainer.getCanvasWidth();
1403
1404           // Scrollbar needs to be negatively positioned beyond the bottom of the relatively-positioned render container
1405           var bottom = (scrollBarWidth * -1) + gridBottomBorder;
1406           if (grid.options.showFooter) {
1407             bottom -= 1;
1408           }
1409           var ret = '.grid' + grid.id + ' .ui-grid-render-container-' + containerCtrl.containerId + ' .ui-grid-native-scrollbar.horizontal { bottom: ' + bottom + 'px; }';
1410           ret += '.grid' + grid.id + ' .ui-grid-render-container-' + containerCtrl.containerId + ' .ui-grid-native-scrollbar.horizontal .contents { width: ' + w + 'px; }';
1411
1412           elmMaxScroll = w;
1413
1414           return ret;
1415         }
1416
1417         // NOTE: priority 6 so they run after the column widths update, which in turn update the canvas width
1418         if ($scope.type === 'vertical') {
1419           grid.registerStyleComputation({
1420             priority: 6,
1421             func: updateNativeVerticalScrollbar
1422           });
1423         }
1424         else if ($scope.type === 'horizontal') {
1425           grid.registerStyleComputation({
1426             priority: 6,
1427             func: updateNativeHorizontalScrollbar
1428           });
1429         }
1430
1431
1432         $scope.scrollSource = null;
1433
1434         function scrollEvent(evt) {
1435           if ($scope.type === 'vertical') {
1436             grid.flagScrollingVertically();
1437             var newScrollTop = $elm[0].scrollTop;
1438
1439             var yDiff = previousScrollPosition - newScrollTop;
1440
1441             var vertScrollLength = (rowContainer.getCanvasHeight() - rowContainer.getViewportHeight());
1442
1443             // Subtract the h. scrollbar height from the vertical length if it's present
1444             if (grid.horizontalScrollbarHeight && grid.horizontalScrollbarHeight > 0) {
1445               vertScrollLength = vertScrollLength - uiGridCtrl.grid.horizontalScrollbarHeight;
1446             }
1447
1448             var vertScrollPercentage = newScrollTop / vertScrollLength;
1449
1450             if (vertScrollPercentage > 1) {
1451               vertScrollPercentage = 1;
1452             }
1453             if (vertScrollPercentage < 0) {
1454               vertScrollPercentage = 0;
1455             }
1456
1457             var yArgs = {
1458               target: $elm,
1459               y: {
1460                 percentage: vertScrollPercentage
1461               }
1462             };
1463
1464             // If the source of this scroll is defined (i.e., not us, then don't fire the scroll event because we'll be re-triggering)
1465             if (!$scope.scrollSource) {
1466               uiGridCtrl.fireScrollingEvent(yArgs);
1467             }
1468             else {
1469               // Reset the scroll source for the next scroll event
1470               $scope.scrollSource = null;
1471             }
1472
1473             previousScrollPosition = newScrollTop;
1474           }
1475           else if ($scope.type === 'horizontal') {
1476             grid.flagScrollingHorizontally();
1477             // var newScrollLeft = $elm[0].scrollLeft;
1478             var newScrollLeft = gridUtil.normalizeScrollLeft($elm);
1479
1480             var xDiff = previousScrollPosition - newScrollLeft;
1481
1482             var horizScrollLength = (colContainer.getCanvasWidth() - colContainer.getViewportWidth());
1483             var horizScrollPercentage = newScrollLeft / horizScrollLength;
1484
1485             var xArgs = {
1486               target: $elm,
1487               x: {
1488                 percentage: horizScrollPercentage
1489               }
1490             };
1491
1492             // If the source of this scroll is defined (i.e., not us, then don't fire the scroll event because we'll be re-triggering)
1493             if (!$scope.scrollSource) {
1494               uiGridCtrl.fireScrollingEvent(xArgs);
1495             }
1496             else {
1497               // Reset the scroll source for the next scroll event
1498               $scope.scrollSource = null;
1499             }
1500
1501             previousScrollPosition = newScrollLeft;
1502           }
1503         }
1504
1505         $elm.on('scroll', scrollEvent);
1506
1507         $elm.on('$destroy', function () {
1508           $elm.off('scroll');
1509         });
1510
1511         function gridScroll(evt, args) {
1512           // Don't listen to our own scroll event!
1513           if (args.target && (args.target === $elm || angular.element(args.target).hasClass('ui-grid-native-scrollbar'))) {
1514             return;
1515           }
1516
1517           // Set the source of the scroll event in our scope so it's available in our 'scroll' event handler
1518           $scope.scrollSource = args.target;
1519
1520           if ($scope.type === 'vertical') {
1521             if (args.y && typeof(args.y.percentage) !== 'undefined' && args.y.percentage !== undefined) {
1522               grid.flagScrollingVertically();
1523               var vertScrollLength = (rowContainer.getCanvasHeight() - rowContainer.getViewportHeight());
1524
1525               var newScrollTop = Math.max(0, args.y.percentage * vertScrollLength);
1526
1527               $elm[0].scrollTop = newScrollTop;
1528
1529
1530             }
1531           }
1532           else if ($scope.type === 'horizontal') {
1533             if (args.x && typeof(args.x.percentage) !== 'undefined' && args.x.percentage !== undefined) {
1534               grid.flagScrollingHorizontally();
1535               var horizScrollLength = (colContainer.getCanvasWidth() - colContainer.getViewportWidth());
1536
1537               var newScrollLeft = Math.max(0, args.x.percentage * horizScrollLength);
1538
1539               // $elm[0].scrollLeft = newScrollLeft;
1540               $elm[0].scrollLeft = gridUtil.denormalizeScrollLeft($elm, newScrollLeft);
1541             }
1542           }
1543         }
1544
1545         var gridScrollDereg = $scope.$on(uiGridConstants.events.GRID_SCROLL, gridScroll);
1546         $scope.$on('$destroy', gridScrollDereg);
1547
1548
1549
1550       }
1551     };
1552   }]);
1553 })();
1554 (function () {
1555   'use strict';
1556
1557   var module = angular.module('ui.grid');
1558   
1559   module.directive('uiGridRenderContainer', ['$log', '$timeout', '$document', 'uiGridConstants', 'gridUtil',
1560     function($log, $timeout, $document, uiGridConstants, GridUtil) {
1561     return {
1562       replace: true,
1563       transclude: true,
1564       templateUrl: 'ui-grid/uiGridRenderContainer',
1565       require: ['^uiGrid', 'uiGridRenderContainer'],
1566       scope: {
1567         containerId: '=',
1568         rowContainerName: '=',
1569         colContainerName: '=',
1570         bindScrollHorizontal: '=',
1571         bindScrollVertical: '=',
1572         enableScrollbars: '='
1573       },
1574       controller: 'uiGridRenderContainer as RenderContainer',
1575       compile: function () {
1576         return {
1577           pre: function prelink($scope, $elm, $attrs, controllers) {
1578             $log.debug('render container ' + $scope.containerId + ' pre-link');
1579
1580             var uiGridCtrl = controllers[0];
1581             var containerCtrl = controllers[1];
1582
1583             var grid = $scope.grid = uiGridCtrl.grid;
1584
1585             // Verify that the render container for this element exists
1586             if (!$scope.rowContainerName) {
1587               throw "No row render container name specified";
1588             }
1589             if (!$scope.colContainerName) {
1590               throw "No column render container name specified";
1591             }
1592
1593             if (!grid.renderContainers[$scope.rowContainerName]) {
1594               throw "Row render container '" + $scope.rowContainerName + "' is not registered.";
1595             }
1596             if (!grid.renderContainers[$scope.colContainerName]) {
1597               throw "Column render container '" + $scope.colContainerName + "' is not registered.";
1598             }
1599
1600             var rowContainer = $scope.rowContainer = grid.renderContainers[$scope.rowContainerName];
1601             var colContainer = $scope.colContainer = grid.renderContainers[$scope.colContainerName];
1602             
1603             containerCtrl.containerId = $scope.containerId;
1604             containerCtrl.rowContainer = rowContainer;
1605             containerCtrl.colContainer = colContainer;
1606           },
1607           post: function postlink($scope, $elm, $attrs, controllers) {
1608             $log.debug('render container ' + $scope.containerId + ' post-link');
1609
1610             var uiGridCtrl = controllers[0];
1611             var containerCtrl = controllers[1];
1612
1613             var grid = uiGridCtrl.grid;
1614             var rowContainer = containerCtrl.rowContainer;
1615             var colContainer = containerCtrl.colContainer;
1616
1617             // Put the container name on this element as a class
1618             $elm.addClass('ui-grid-render-container-' + $scope.containerId);
1619
1620             // Bind to left/right-scroll events
1621             var scrollUnbinder;
1622             if ($scope.bindScrollHorizontal || $scope.bindScrollVertical) {
1623               scrollUnbinder = $scope.$on(uiGridConstants.events.GRID_SCROLL, scrollHandler);
1624             }
1625
1626             function scrollHandler (evt, args) {
1627               // Vertical scroll
1628               if (args.y && $scope.bindScrollVertical) {
1629                 containerCtrl.prevScrollArgs = args;
1630
1631                 var scrollLength = (rowContainer.getCanvasHeight() - rowContainer.getViewportHeight());
1632
1633                 // Add the height of the native horizontal scrollbar, if it's there. Otherwise it will mask over the final row
1634                 if (grid.horizontalScrollbarHeight && grid.horizontalScrollbarHeight > 0) {
1635                   scrollLength = scrollLength + grid.horizontalScrollbarHeight;
1636                 }
1637
1638                 var oldScrollTop = containerCtrl.viewport[0].scrollTop;
1639                 
1640                 var scrollYPercentage;
1641                 if (typeof(args.y.percentage) !== 'undefined' && args.y.percentage !== undefined) {
1642                   scrollYPercentage = args.y.percentage;
1643                 }
1644                 else if (typeof(args.y.pixels) !== 'undefined' && args.y.pixels !== undefined) {
1645                   scrollYPercentage = args.y.percentage = (oldScrollTop + args.y.pixels) / scrollLength;
1646                   // $log.debug('y.percentage', args.y.percentage);
1647                 }
1648                 else {
1649                   throw new Error("No percentage or pixel value provided for scroll event Y axis");
1650                 }
1651
1652                 var newScrollTop = Math.max(0, scrollYPercentage * scrollLength);
1653
1654                 containerCtrl.viewport[0].scrollTop = newScrollTop;
1655                 
1656                 // TOOD(c0bra): what's this for?
1657                 // grid.options.offsetTop = newScrollTop;
1658
1659                 containerCtrl.prevScrollArgs.y.pixels = newScrollTop - oldScrollTop;
1660               }
1661
1662               // Horizontal scroll
1663               if (args.x && $scope.bindScrollHorizontal) {
1664                 containerCtrl.prevScrollArgs = args;
1665
1666                 var scrollWidth = (colContainer.getCanvasWidth() - colContainer.getViewportWidth());
1667
1668                 // var oldScrollLeft = containerCtrl.viewport[0].scrollLeft;
1669                 var oldScrollLeft = GridUtil.normalizeScrollLeft(containerCtrl.viewport);
1670
1671                 var scrollXPercentage;
1672                 if (typeof(args.x.percentage) !== 'undefined' && args.x.percentage !== undefined) {
1673                   scrollXPercentage = args.x.percentage;
1674                 }
1675                 else if (typeof(args.x.pixels) !== 'undefined' && args.x.pixels !== undefined) {
1676                   scrollXPercentage = args.x.percentage = (oldScrollLeft + args.x.pixels) / scrollWidth;
1677                 }
1678                 else {
1679                   throw new Error("No percentage or pixel value provided for scroll event X axis");
1680                 }
1681
1682                 var newScrollLeft = Math.max(0, scrollXPercentage * scrollWidth);
1683                 
1684                 // uiGridCtrl.adjustScrollHorizontal(newScrollLeft, scrollXPercentage);
1685
1686                 // containerCtrl.viewport[0].scrollLeft = newScrollLeft;
1687                 containerCtrl.viewport[0].scrollLeft = GridUtil.denormalizeScrollLeft(containerCtrl.viewport, newScrollLeft);
1688
1689                 containerCtrl.prevScrollLeft = newScrollLeft;
1690
1691                 if (containerCtrl.headerViewport) {
1692                   // containerCtrl.headerViewport.scrollLeft = newScrollLeft;
1693                   containerCtrl.headerViewport.scrollLeft = GridUtil.denormalizeScrollLeft(containerCtrl.headerViewport, newScrollLeft);
1694                 }
1695
1696                 if (containerCtrl.footerViewport) {
1697                   // containerCtrl.footerViewport.scrollLeft = newScrollLeft;
1698                   containerCtrl.footerViewport.scrollLeft = GridUtil.denormalizeScrollLeft(containerCtrl.footerViewport, newScrollLeft);
1699                 }
1700
1701                 // uiGridCtrl.grid.options.offsetLeft = newScrollLeft;
1702
1703                 containerCtrl.prevScrollArgs.x.pixels = newScrollLeft - oldScrollLeft;
1704               }
1705             }
1706
1707             // Scroll the render container viewport when the mousewheel is used
1708             $elm.bind('wheel mousewheel DomMouseScroll MozMousePixelScroll', function(evt) {
1709               // use wheelDeltaY
1710               evt.preventDefault();
1711
1712               var newEvent = GridUtil.normalizeWheelEvent(evt);
1713
1714               var args = { target: $elm };
1715               if (newEvent.deltaY !== 0) {
1716                 var scrollYAmount = newEvent.deltaY * -120;
1717
1718                 // Get the scroll percentage
1719                 var scrollYPercentage = (containerCtrl.viewport[0].scrollTop + scrollYAmount) / (rowContainer.getCanvasHeight() - rowContainer.getViewportHeight());
1720
1721                 // Keep scrollPercentage within the range 0-1.
1722                 if (scrollYPercentage < 0) { scrollYPercentage = 0; }
1723                 else if (scrollYPercentage > 1) { scrollYPercentage = 1; }
1724
1725                 args.y = { percentage: scrollYPercentage, pixels: scrollYAmount };
1726               }
1727               if (newEvent.deltaX !== 0) {
1728                 var scrollXAmount = newEvent.deltaX * -120;
1729
1730                 // Get the scroll percentage
1731                 var scrollLeft = GridUtil.normalizeScrollLeft(containerCtrl.viewport);
1732                 var scrollXPercentage = (scrollLeft + scrollXAmount) / (colContainer.getCanvasWidth() - colContainer.getViewportWidth());
1733
1734                 // Keep scrollPercentage within the range 0-1.
1735                 if (scrollXPercentage < 0) { scrollXPercentage = 0; }
1736                 else if (scrollXPercentage > 1) { scrollXPercentage = 1; }
1737
1738                 args.x = { percentage: scrollXPercentage, pixels: scrollXAmount };
1739               }
1740               
1741               uiGridCtrl.fireScrollingEvent(args);
1742             });
1743             
1744
1745             var startY = 0,
1746             startX = 0,
1747             scrollTopStart = 0,
1748             scrollLeftStart = 0,
1749             directionY = 1,
1750             directionX = 1,
1751             moveStart;
1752
1753             function touchmove(event) {
1754               if (event.originalEvent) {
1755                 event = event.originalEvent;
1756               }
1757
1758               event.preventDefault();
1759
1760               var deltaX, deltaY, newX, newY;
1761               newX = event.targetTouches[0].screenX;
1762               newY = event.targetTouches[0].screenY;
1763               deltaX = -(newX - startX);
1764               deltaY = -(newY - startY);
1765
1766               directionY = (deltaY < 1) ? -1 : 1;
1767               directionX = (deltaX < 1) ? -1 : 1;
1768
1769               deltaY *= 2;
1770               deltaX *= 2;
1771
1772               var args = { target: event.target };
1773
1774               if (deltaY !== 0) {
1775                 var scrollYPercentage = (scrollTopStart + deltaY) / (rowContainer.getCanvasHeight() - rowContainer.getViewportHeight());
1776
1777                 if (scrollYPercentage > 1) { scrollYPercentage = 1; }
1778                 else if (scrollYPercentage < 0) { scrollYPercentage = 0; }
1779
1780                 args.y = { percentage: scrollYPercentage, pixels: deltaY };
1781               }
1782               if (deltaX !== 0) {
1783                 var scrollXPercentage = (scrollLeftStart + deltaX) / (colContainer.getCanvasWidth() - colContainer.getViewportWidth());
1784
1785                 if (scrollXPercentage > 1) { scrollXPercentage = 1; }
1786                 else if (scrollXPercentage < 0) { scrollXPercentage = 0; }
1787
1788                 args.x = { percentage: scrollXPercentage, pixels: deltaX };
1789               }
1790
1791               uiGridCtrl.fireScrollingEvent(args);
1792             }
1793             
1794             function touchend(event) {
1795               if (event.originalEvent) {
1796                 event = event.originalEvent;
1797               }
1798
1799               event.preventDefault();
1800
1801               $document.unbind('touchmove', touchmove);
1802               $document.unbind('touchend', touchend);
1803               $document.unbind('touchcancel', touchend);
1804
1805               // Get the distance we moved on the Y axis
1806               var scrollTopEnd = containerCtrl.viewport[0].scrollTop;
1807               var scrollLeftEnd = containerCtrl.viewport[0].scrollTop;
1808               var deltaY = Math.abs(scrollTopEnd - scrollTopStart);
1809               var deltaX = Math.abs(scrollLeftEnd - scrollLeftStart);
1810
1811               // Get the duration it took to move this far
1812               var moveDuration = (new Date()) - moveStart;
1813
1814               // Scale the amount moved by the time it took to move it (i.e. quicker, longer moves == more scrolling after the move is over)
1815               var moveYScale = deltaY / moveDuration;
1816               var moveXScale = deltaX / moveDuration;
1817
1818               var decelerateInterval = 63; // 1/16th second
1819               var decelerateCount = 8; // == 1/2 second
1820               var scrollYLength = 120 * directionY * moveYScale;
1821               var scrollXLength = 120 * directionX * moveXScale;
1822
1823               function decelerate() {
1824                 $timeout(function() {
1825                   var args = { target: event.target };
1826
1827                   if (scrollYLength !== 0) {
1828                     var scrollYPercentage = (containerCtrl.viewport[0].scrollTop + scrollYLength) / (rowContainer.getCanvasHeight() - rowContainer.getViewportHeight());
1829
1830                     args.y = { percentage: scrollYPercentage, pixels: scrollYLength };
1831                   }
1832
1833                   if (scrollXLength !== 0) {
1834                     var scrollXPercentage = (containerCtrl.viewport[0].scrollLeft + scrollXLength) / (colContainer.getCanvasWidth() - colContainer.getViewportWidth());
1835                     args.x = { percentage: scrollXPercentage, pixels: scrollXLength };
1836                   }
1837
1838                   uiGridCtrl.fireScrollingEvent(args);
1839
1840                   decelerateCount = decelerateCount -1;
1841                   scrollYLength = scrollYLength / 2;
1842                   scrollXLength = scrollXLength / 2;
1843
1844                   if (decelerateCount > 0) {
1845                     decelerate();
1846                   }
1847                   else {
1848                     uiGridCtrl.scrollbars.forEach(function (sbar) {
1849                       sbar.removeClass('ui-grid-scrollbar-visible');
1850                       sbar.removeClass('ui-grid-scrolling');
1851                     });
1852                   }
1853                 }, decelerateInterval);
1854               }
1855
1856               // decelerate();
1857             }
1858
1859             if (GridUtil.isTouchEnabled()) {
1860               $elm.bind('touchstart', function (event) {
1861                 if (event.originalEvent) {
1862                   event = event.originalEvent;
1863                 }
1864
1865                 event.preventDefault();
1866
1867                 uiGridCtrl.scrollbars.forEach(function (sbar) {
1868                   sbar.addClass('ui-grid-scrollbar-visible');
1869                   sbar.addClass('ui-grid-scrolling');
1870                 });
1871
1872                 moveStart = new Date();
1873                 startY = event.targetTouches[0].screenY;
1874                 startX = event.targetTouches[0].screenX;
1875                 scrollTopStart = containerCtrl.viewport[0].scrollTop;
1876                 scrollLeftStart = containerCtrl.viewport[0].scrollLeft;
1877                 
1878                 $document.on('touchmove', touchmove);
1879                 $document.on('touchend touchcancel', touchend);
1880               });
1881             }
1882
1883             $elm.bind('$destroy', function() {
1884               scrollUnbinder();
1885               $elm.unbind('keydown');
1886
1887               ['touchstart', 'touchmove', 'touchend','keydown', 'wheel', 'mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'].forEach(function (eventName) {
1888                 $elm.unbind(eventName);
1889               });
1890             });
1891             
1892             // TODO(c0bra): Handle resizing the inner canvas based on the number of elements
1893             function update() {
1894               var ret = '';
1895
1896               var canvasWidth = colContainer.getCanvasWidth();
1897               var viewportWidth = colContainer.getViewportWidth();
1898
1899               var canvasHeight = rowContainer.getCanvasHeight();
1900               var viewportHeight = rowContainer.getViewportHeight();
1901
1902               var headerViewportWidth = colContainer.getHeaderViewportWidth();
1903               var footerViewportWidth = colContainer.getHeaderViewportWidth();
1904               
1905               // Set canvas dimensions
1906               ret += '\n .grid' + uiGridCtrl.grid.id + ' .ui-grid-render-container-' + $scope.containerId + ' .ui-grid-canvas { width: ' + canvasWidth + 'px; height: ' + canvasHeight + 'px; }';
1907               ret += '\n .grid' + uiGridCtrl.grid.id + ' .ui-grid-render-container-' + $scope.containerId + ' .ui-grid-header-canvas { width: ' + canvasWidth + 'px; }';
1908               
1909               ret += '\n .grid' + uiGridCtrl.grid.id + ' .ui-grid-render-container-' + $scope.containerId + ' .ui-grid-viewport { width: ' + viewportWidth + 'px; height: ' + viewportHeight + 'px; }';
1910               ret += '\n .grid' + uiGridCtrl.grid.id + ' .ui-grid-render-container-' + $scope.containerId + ' .ui-grid-header-viewport { width: ' + headerViewportWidth + 'px; }';
1911
1912               ret += '\n .grid' + uiGridCtrl.grid.id + ' .ui-grid-render-container-' + $scope.containerId + ' .ui-grid-footer-canvas { width: ' + canvasWidth + 'px; }';
1913               ret += '\n .grid' + uiGridCtrl.grid.id + ' .ui-grid-render-container-' + $scope.containerId + ' .ui-grid-footer-viewport { width: ' + footerViewportWidth + 'px; }';
1914               // Update
1915
1916               return ret;
1917             }
1918             
1919             uiGridCtrl.grid.registerStyleComputation({
1920               priority: 6,
1921               func: update
1922             });
1923           }
1924         };
1925       }
1926     };
1927
1928   }]);
1929
1930   module.controller('uiGridRenderContainer', ['$scope', '$log', function ($scope, $log) {
1931     var self = this;
1932
1933     self.rowStyle = function (index) {
1934       var renderContainer = $scope.grid.renderContainers[$scope.containerId];
1935
1936       var styles = {};
1937       
1938       if (!renderContainer.disableRowOffset) {
1939         if (index === 0 && self.currentTopRow !== 0) {
1940           // The row offset-top is just the height of the rows above the current top-most row, which are no longer rendered
1941           var hiddenRowWidth = ($scope.rowContainer.currentTopRow) *
1942             $scope.rowContainer.visibleRowCache[$scope.rowContainer.currentTopRow].height;
1943
1944           // return { 'margin-top': hiddenRowWidth + 'px' };
1945           styles['margin-top'] = hiddenRowWidth + 'px';
1946         }
1947       }
1948       
1949       if (!renderContainer.disableColumnOffset && $scope.colContainer.currentFirstColumn !== 0) {
1950         if ($scope.grid.isRTL()) {
1951           styles['margin-right'] = $scope.colContainer.columnOffset + 'px';
1952         }
1953         else {
1954           styles['margin-left'] = $scope.colContainer.columnOffset + 'px';
1955         }
1956       }
1957
1958       return styles;
1959     };
1960
1961     self.columnStyle = function (index) {
1962       var renderContainer = $scope.grid.renderContainers[$scope.containerId];
1963
1964       var self = this;
1965
1966       if (!renderContainer.disableColumnOffset) {
1967         if (index === 0 && $scope.colContainer.currentFirstColumn !== 0) {
1968           var offset = $scope.colContainer.columnOffset;
1969
1970           if ($scope.grid.isRTL()) {
1971             return { 'margin-right': offset + 'px' };
1972           }
1973           else {
1974             return { 'margin-left': offset + 'px' }; 
1975           }
1976         }
1977       }
1978
1979       return null;
1980     };
1981   }]);
1982
1983 })();
1984 (function(){
1985   'use strict';
1986
1987   angular.module('ui.grid').directive('uiGridRow', ['$log', function($log) {
1988     return {
1989       replace: true,
1990       // priority: 2001,
1991       // templateUrl: 'ui-grid/ui-grid-row',
1992       require: ['^uiGrid', '^uiGridRenderContainer'],
1993       scope: {
1994          row: '=uiGridRow',
1995          //rowRenderIndex is added to scope to give the true visual index of the row to any directives that need it
1996          rowRenderIndex: '='
1997       },
1998       compile: function() {
1999         return {
2000           pre: function($scope, $elm, $attrs, controllers) {
2001             var uiGridCtrl = controllers[0];
2002             var containerCtrl = controllers[1];
2003
2004             var grid = uiGridCtrl.grid;
2005
2006             $scope.grid = uiGridCtrl.grid;
2007             $scope.colContainer = containerCtrl.colContainer;
2008
2009             grid.getRowTemplateFn.then(function (templateFn) {
2010               templateFn($scope, function(clonedElement, scope) {
2011                 $elm.replaceWith(clonedElement);
2012               });
2013             });
2014           },
2015           post: function($scope, $elm, $attrs, controllers) {
2016             var uiGridCtrl = controllers[0];
2017             var containerCtrl = controllers[1];
2018
2019             //add optional reference to externalScopes function to scope
2020             //so it can be retrieved in lower elements
2021             $scope.getExternalScopes = uiGridCtrl.getExternalScopes;
2022           }
2023         };
2024       }
2025     };
2026   }]);
2027
2028 })();
2029 (function(){
2030 // 'use strict';
2031
2032   /**
2033    * @ngdoc directive
2034    * @name ui.grid.directive:uiGridStyle
2035    * @element style
2036    * @restrict A
2037    *
2038    * @description
2039    * Allows us to interpolate expressions in `<style>` elements. Angular doesn't do this by default as it can/will/might? break in IE8.
2040    *
2041    * @example
2042    <doc:example module="app">
2043    <doc:source>
2044    <script>
2045    var app = angular.module('app', ['ui.grid']);
2046
2047    app.controller('MainCtrl', ['$scope', function ($scope) {
2048           $scope.myStyle = '.blah { border: 1px solid }';
2049         }]);
2050    </script>
2051
2052    <div ng-controller="MainCtrl">
2053    <style ui-grid-style>{{ myStyle }}</style>
2054    <span class="blah">I am in a box.</span>
2055    </div>
2056    </doc:source>
2057    <doc:scenario>
2058    it('should apply the right class to the element', function () {
2059         element(by.css('.blah')).getCssValue('border')
2060           .then(function(c) {
2061             expect(c).toContain('1px solid');
2062           });
2063       });
2064    </doc:scenario>
2065    </doc:example>
2066    */
2067
2068
2069   angular.module('ui.grid').directive('uiGridStyle', ['$log', '$interpolate', function($log, $interpolate) {
2070     return {
2071       // restrict: 'A',
2072       // priority: 1000,
2073       // require: '?^uiGrid',
2074       link: function($scope, $elm, $attrs, uiGridCtrl) {
2075         $log.debug('ui-grid-style link');
2076         // if (uiGridCtrl === undefined) {
2077         //    $log.warn('[ui-grid-style link] uiGridCtrl is undefined!');
2078         // }
2079
2080         var interpolateFn = $interpolate($elm.text(), true);
2081
2082         if (interpolateFn) {
2083           $scope.$watch(interpolateFn, function(value) {
2084             $elm.text(value);
2085           });
2086         }
2087
2088           // uiGridCtrl.recalcRowStyles = function() {
2089           //   var offset = (scope.options.offsetTop || 0) - (scope.options.excessRows * scope.options.rowHeight);
2090           //   var rowHeight = scope.options.rowHeight;
2091
2092           //   var ret = '';
2093           //   var rowStyleCount = uiGridCtrl.minRowsToRender() + (scope.options.excessRows * 2);
2094           //   for (var i = 1; i <= rowStyleCount; i++) {
2095           //     ret = ret + ' .grid' + scope.gridId + ' .ui-grid-row:nth-child(' + i + ') { top: ' + offset + 'px; }';
2096           //     offset = offset + rowHeight;
2097           //   }
2098
2099           //   scope.rowStyles = ret;
2100           // };
2101
2102           // uiGridCtrl.styleComputions.push(uiGridCtrl.recalcRowStyles);
2103
2104       }
2105     };
2106   }]);
2107
2108 })();
2109 (function(){
2110   'use strict';
2111
2112   angular.module('ui.grid').directive('uiGridViewport', ['$log', 'gridUtil',
2113     function($log, gridUtil) {
2114       return {
2115         replace: true,
2116         scope: {},
2117         templateUrl: 'ui-grid/uiGridViewport',
2118         require: ['^uiGrid', '^uiGridRenderContainer'],
2119         link: function($scope, $elm, $attrs, controllers) {
2120           $log.debug('viewport post-link');
2121
2122           var uiGridCtrl = controllers[0];
2123           var containerCtrl = controllers[1];
2124
2125           $scope.containerCtrl = containerCtrl;
2126
2127           var rowContainer = containerCtrl.rowContainer;
2128           var colContainer = containerCtrl.colContainer;
2129
2130           var grid = uiGridCtrl.grid;
2131
2132           $scope.grid = uiGridCtrl.grid;
2133
2134           // Put the containers in scope so we can get rows and columns from them
2135           $scope.rowContainer = containerCtrl.rowContainer;
2136           $scope.colContainer = containerCtrl.colContainer;
2137
2138           // Register this viewport with its container 
2139           containerCtrl.viewport = $elm;
2140
2141           $elm.on('scroll', function (evt) {
2142             var newScrollTop = $elm[0].scrollTop;
2143             // var newScrollLeft = $elm[0].scrollLeft;
2144             var newScrollLeft = gridUtil.normalizeScrollLeft($elm);
2145
2146             // Handle RTL here
2147
2148             if (newScrollLeft !== colContainer.prevScrollLeft) {
2149               var xDiff = newScrollLeft - colContainer.prevScrollLeft;
2150
2151               var horizScrollLength = (colContainer.getCanvasWidth() - colContainer.getViewportWidth());
2152               var horizScrollPercentage = newScrollLeft / horizScrollLength;
2153
2154               colContainer.adjustScrollHorizontal(newScrollLeft, horizScrollPercentage);
2155             }
2156
2157             if (newScrollTop !== rowContainer.prevScrollTop) {
2158               var yDiff = newScrollTop - rowContainer.prevScrollTop;
2159
2160               // uiGridCtrl.fireScrollingEvent({ y: { pixels: diff } });
2161               var vertScrollLength = (rowContainer.getCanvasHeight() - rowContainer.getViewportHeight());
2162               // var vertScrollPercentage = (uiGridCtrl.prevScrollTop + yDiff) / vertScrollLength;
2163               var vertScrollPercentage = newScrollTop / vertScrollLength;
2164
2165               if (vertScrollPercentage > 1) { vertScrollPercentage = 1; }
2166               if (vertScrollPercentage < 0) { vertScrollPercentage = 0; }
2167               
2168               rowContainer.adjustScrollVertical(newScrollTop, vertScrollPercentage);
2169             }
2170           });
2171         }
2172       };
2173     }
2174   ]);
2175
2176 })();
2177 (function() {
2178
2179 angular.module('ui.grid')
2180 .directive('uiGridVisible', function uiGridVisibleAction() {
2181   return function ($scope, $elm, $attr) {
2182     $scope.$watch($attr.uiGridVisible, function (visible) {
2183         // $elm.css('visibility', visible ? 'visible' : 'hidden');
2184         $elm[visible ? 'removeClass' : 'addClass']('ui-grid-invisible');
2185     });
2186   };
2187 });
2188
2189 })();
2190 (function () {
2191   'use strict';
2192
2193   angular.module('ui.grid').controller('uiGridController', ['$scope', '$element', '$attrs', '$log', 'gridUtil', '$q', 'uiGridConstants',
2194                     '$templateCache', 'gridClassFactory', '$timeout', '$parse', '$compile',
2195     function ($scope, $elm, $attrs, $log, gridUtil, $q, uiGridConstants,
2196               $templateCache, gridClassFactory, $timeout, $parse, $compile) {
2197       $log.debug('ui-grid controller');
2198
2199       var self = this;
2200
2201       // Extend options with ui-grid attribute reference
2202       self.grid = gridClassFactory.createGrid($scope.uiGrid);
2203       $elm.addClass('grid' + self.grid.id);
2204       self.grid.rtl = $elm.css('direction') === 'rtl';
2205
2206
2207       //add optional reference to externalScopes function to controller
2208       //so it can be retrieved in lower elements that have isolate scope
2209       self.getExternalScopes = $scope.getExternalScopes;
2210
2211       // angular.extend(self.grid.options, );
2212
2213       //all properties of grid are available on scope
2214       $scope.grid = self.grid;
2215
2216       if ($attrs.uiGridColumns) {
2217         $attrs.$observe('uiGridColumns', function(value) {
2218           self.grid.options.columnDefs = value;
2219           self.grid.buildColumns()
2220             .then(function(){
2221               self.grid.preCompileCellTemplates();
2222
2223               self.grid.refreshCanvas(true);
2224             });
2225         });
2226       }
2227
2228
2229       var dataWatchCollectionDereg;
2230       if (angular.isString($scope.uiGrid.data)) {
2231         dataWatchCollectionDereg = $scope.$parent.$watchCollection($scope.uiGrid.data, dataWatchFunction);
2232       }
2233       else {
2234         dataWatchCollectionDereg = $scope.$parent.$watchCollection(function() { return $scope.uiGrid.data; }, dataWatchFunction);
2235       }
2236
2237       var columnDefWatchCollectionDereg = $scope.$parent.$watchCollection(function() { return $scope.uiGrid.columnDefs; }, columnDefsWatchFunction);
2238
2239       function columnDefsWatchFunction(n, o) {
2240         if (n && n !== o) {
2241           self.grid.options.columnDefs = n;
2242           self.grid.buildColumns()
2243             .then(function(){
2244
2245               self.grid.preCompileCellTemplates();
2246
2247               self.grid.refreshCanvas(true);
2248             });
2249         }
2250       }
2251
2252       function dataWatchFunction(n) {
2253         // $log.debug('dataWatch fired');
2254         var promises = [];
2255
2256         if (n) {
2257           if (self.grid.columns.length === 0) {
2258             $log.debug('loading cols in dataWatchFunction');
2259             if (!$attrs.uiGridColumns && self.grid.options.columnDefs.length === 0) {
2260               self.grid.buildColumnDefsFromData(n);
2261             }
2262             promises.push(self.grid.buildColumns()
2263               .then(function() {
2264                 self.grid.preCompileCellTemplates();}
2265             ));
2266           }
2267           $q.all(promises).then(function() {
2268             self.grid.modifyRows(n)
2269               .then(function () {
2270                 // if (self.viewport) {
2271                   self.grid.redrawInPlace();
2272                 // }
2273
2274                 $scope.$evalAsync(function() {
2275                   self.grid.refreshCanvas(true);
2276                 });
2277               });
2278           });
2279         }
2280       }
2281
2282
2283       $scope.$on('$destroy', function() {
2284         dataWatchCollectionDereg();
2285         columnDefWatchCollectionDereg();
2286       });
2287
2288       $scope.$watch(function () { return self.grid.styleComputations; }, function() {
2289         self.grid.refreshCanvas(true);
2290       });
2291
2292
2293       /* Event Methods */
2294
2295       //todo: throttle this event?
2296       self.fireScrollingEvent = function(args) {
2297         $scope.$broadcast(uiGridConstants.events.GRID_SCROLL, args);
2298       };
2299
2300       self.fireEvent = function(eventName, args) {
2301         // Add the grid to the event arguments if it's not there
2302         if (typeof(args) === 'undefined' || args === undefined) {
2303           args = {};
2304         }
2305
2306         if (typeof(args.grid) === 'undefined' || args.grid === undefined) {
2307           args.grid = self.grid;
2308         }
2309
2310         $scope.$broadcast(eventName, args);
2311       };
2312
2313       self.innerCompile = function innerCompile(elm) {
2314         $compile(elm)($scope);
2315       };
2316
2317     }]);
2318
2319 /**
2320  *  @ngdoc directive
2321  *  @name ui.grid.directive:uiGrid
2322  *  @element div
2323  *  @restrict EA
2324  *  @param {Object} uiGrid Options for the grid to use
2325  *  @param {Object=} external-scopes Add external-scopes='someScopeObjectYouNeed' attribute so you can access
2326  *            your scopes from within any custom templatedirective.  You access by $scope.getExternalScopes() function
2327  *
2328  *  @description Create a very basic grid.
2329  *
2330  *  @example
2331     <example module="app">
2332       <file name="app.js">
2333         var app = angular.module('app', ['ui.grid']);
2334
2335         app.controller('MainCtrl', ['$scope', function ($scope) {
2336           $scope.data = [
2337             { name: 'Bob', title: 'CEO' },
2338             { name: 'Frank', title: 'Lowly Developer' }
2339           ];
2340         }]);
2341       </file>
2342       <file name="index.html">
2343         <div ng-controller="MainCtrl">
2344           <div ui-grid="{ data: data }"></div>
2345         </div>
2346       </file>
2347     </example>
2348  */
2349 angular.module('ui.grid').directive('uiGrid',
2350   [
2351     '$log',
2352     '$compile',
2353     '$templateCache',
2354     'gridUtil',
2355     '$window',
2356     function(
2357       $log,
2358       $compile,
2359       $templateCache,
2360       gridUtil,
2361       $window
2362       ) {
2363       return {
2364         templateUrl: 'ui-grid/ui-grid',
2365         scope: {
2366           uiGrid: '=',
2367           getExternalScopes: '&?externalScopes' //optional functionwrapper around any needed external scope instances
2368         },
2369         replace: true,
2370         transclude: true,
2371         controller: 'uiGridController',
2372         compile: function () {
2373           return {
2374             post: function ($scope, $elm, $attrs, uiGridCtrl) {
2375               $log.debug('ui-grid postlink');
2376
2377               var grid = uiGridCtrl.grid;
2378
2379               // Initialize scrollbars (TODO: move to controller??)
2380               uiGridCtrl.scrollbars = [];
2381
2382               //todo: assume it is ok to communicate that rendering is complete??
2383               grid.renderingComplete();
2384
2385               grid.element = $elm;
2386
2387               grid.gridWidth = $scope.gridWidth = gridUtil.elementWidth($elm);
2388
2389               // Default canvasWidth to the grid width, in case we don't get any column definitions to calculate it from
2390               grid.canvasWidth = uiGridCtrl.grid.gridWidth;
2391
2392               grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);
2393
2394               // If the grid isn't tall enough to fit a single row, it's kind of useless. Resize it to fit a minimum number of rows
2395               if (grid.gridHeight < grid.options.rowHeight) {
2396                 // Figure out the new height
2397                 var newHeight = grid.options.minRowsToShow * grid.options.rowHeight;
2398
2399                 $elm.css('height', newHeight + 'px');
2400
2401                 grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);
2402               }
2403
2404               // Run initial canvas refresh
2405               grid.refreshCanvas();
2406
2407               //add pinned containers for row headers support
2408               //moved from pinning feature
2409               var left = angular.element('<div ng-if="grid.hasLeftContainer()" style="width: 0" ui-grid-pinned-container="\'left\'"></div>');
2410               $elm.prepend(left);
2411               uiGridCtrl.innerCompile(left);
2412
2413               var right = angular.element('<div  ng-if="grid.hasRightContainer()" style="width: 0" ui-grid-pinned-container="\'right\'"></div>');
2414               $elm.append(right);
2415               uiGridCtrl.innerCompile(right);
2416
2417
2418               //if we add a left container after render, we need to watch and react
2419               $scope.$watch(function () { return grid.hasLeftContainer();}, function (newValue, oldValue) {
2420                 if (newValue === oldValue) {
2421                   return;
2422                 }
2423
2424                 //todo: remove this code.  it was commented out after moving from pinning because body is already float:left
2425 //                var bodyContainer = angular.element($elm[0].querySelectorAll('[container-id="body"]'));
2426 //                if (newValue){
2427 //                  bodyContainer.attr('style', 'float: left; position: inherit');
2428 //                }
2429 //                else {
2430 //                  bodyContainer.attr('style', 'float: left; position: relative');
2431 //                }
2432
2433                 grid.refreshCanvas(true);
2434               });
2435
2436               //if we add a right container after render, we need to watch and react
2437               $scope.$watch(function () { return grid.hasRightContainer();}, function (newValue, oldValue) {
2438                 if (newValue === oldValue) {
2439                   return;
2440                 }
2441                 grid.refreshCanvas(true);
2442               });
2443
2444
2445               // Resize the grid on window resize events
2446               function gridResize($event) {
2447                 grid.gridWidth = $scope.gridWidth = gridUtil.elementWidth($elm);
2448                 grid.gridHeight = $scope.gridHeight = gridUtil.elementHeight($elm);
2449
2450                 grid.queueRefresh();
2451               }
2452
2453               angular.element($window).on('resize', gridResize);
2454
2455               // Unbind from window resize events when the grid is destroyed
2456               $elm.on('$destroy', function () {
2457                 angular.element($window).off('resize', gridResize);
2458               });
2459             }
2460           };
2461         }
2462       };
2463     }
2464   ]);
2465
2466 })();
2467
2468 (function(){
2469   'use strict';
2470
2471   angular.module('ui.grid').directive('uiGridPinnedContainer', ['$log', function ($log) {
2472     return {
2473       restrict: 'EA',
2474       replace: true,
2475       template: '<div class="ui-grid-pinned-container"><div ui-grid-render-container container-id="side" row-container-name="\'body\'" col-container-name="side" bind-scroll-vertical="true" class="{{ side }} ui-grid-render-container-{{ side }}"></div></div>',
2476       scope: {
2477         side: '=uiGridPinnedContainer'
2478       },
2479       require: '^uiGrid',
2480       compile: function compile() {
2481         return {
2482           post: function ($scope, $elm, $attrs, uiGridCtrl) {
2483             $log.debug('ui-grid-pinned-container ' + $scope.side + ' link');
2484
2485             var grid = uiGridCtrl.grid;
2486
2487             var myWidth = 0;
2488
2489             $elm.addClass('ui-grid-pinned-container-' + $scope.side);
2490
2491             function updateContainerDimensions() {
2492               // $log.debug('update ' + $scope.side + ' dimensions');
2493
2494               var ret = '';
2495
2496               // Column containers
2497               if ($scope.side === 'left' || $scope.side === 'right') {
2498                 var cols = grid.renderContainers[$scope.side].visibleColumnCache;
2499                 var width = 0;
2500                 for (var i = 0; i < cols.length; i++) {
2501                   var col = cols[i];
2502                   width += col.drawnWidth;
2503                 }
2504
2505                 myWidth = width;
2506
2507                 // $log.debug('myWidth', myWidth);
2508
2509                 // TODO(c0bra): Subtract sum of col widths from grid viewport width and update it
2510                 $elm.attr('style', null);
2511
2512                 var myHeight = grid.renderContainers.body.getViewportHeight(); // + grid.horizontalScrollbarHeight;
2513
2514                 ret += '.grid' + grid.id + ' .ui-grid-pinned-container-' + $scope.side + ', .grid' + grid.id + ' .ui-grid-pinned-container-' + $scope.side + ' .ui-grid-render-container-' + $scope.side + ' .ui-grid-viewport { width: ' + myWidth + 'px; height: ' + myHeight + 'px; } ';
2515               }
2516
2517               return ret;
2518             }
2519
2520             grid.renderContainers.body.registerViewportAdjuster(function (adjustment) {
2521               // Subtract our own width
2522               adjustment.width -= myWidth;
2523
2524               return adjustment;
2525             });
2526
2527             // Register style computation to adjust for columns in `side`'s render container
2528             grid.registerStyleComputation({
2529               priority: 15,
2530               func: updateContainerDimensions
2531             });
2532           }
2533         };
2534       }
2535     };
2536   }]);
2537 })();
2538 (function(){
2539
2540 angular.module('ui.grid')
2541 .factory('Grid', ['$log', '$q', '$compile', '$parse', 'gridUtil', 'uiGridConstants', 'GridOptions', 'GridColumn', 'GridRow', 'GridApi', 'rowSorter', 'rowSearcher', 'GridRenderContainer', '$timeout',
2542     function($log, $q, $compile, $parse, gridUtil, uiGridConstants, GridOptions, GridColumn, GridRow, GridApi, rowSorter, rowSearcher, GridRenderContainer, $timeout) {
2543
2544 /**
2545  * @ngdoc object
2546  * @name ui.grid.core.api:PublicApi
2547  * @description Public Api for the core grid features
2548  *
2549  */
2550
2551
2552 /**
2553    * @ngdoc function
2554    * @name ui.grid.class:Grid
2555    * @description Grid is the main viewModel.  Any properties or methods needed to maintain state are defined in
2556  * * this prototype.  One instance of Grid is created per Grid directive instance.
2557    * @param {object} options Object map of options to pass into the grid. An 'id' property is expected.
2558    */
2559   var Grid = function Grid(options) {
2560     var self = this;
2561   // Get the id out of the options, then remove it
2562   if (options !== undefined && typeof(options.id) !== 'undefined' && options.id) {
2563     if (!/^[_a-zA-Z0-9-]+$/.test(options.id)) {
2564       throw new Error("Grid id '" + options.id + '" is invalid. It must follow CSS selector syntax rules.');
2565     }
2566   }
2567   else {
2568     throw new Error('No ID provided. An ID must be given when creating a grid.');
2569   }
2570
2571   self.id = options.id;
2572   delete options.id;
2573
2574   // Get default options
2575   self.options = new GridOptions();
2576
2577   // Extend the default options with what we were passed in
2578   angular.extend(self.options, options);
2579
2580   self.headerHeight = self.options.headerRowHeight;
2581   self.footerHeight = self.options.showFooter === true ? self.options.footerRowHeight : 0;
2582
2583   self.rtl = false;
2584   self.gridHeight = 0;
2585   self.gridWidth = 0;
2586   self.columnBuilders = [];
2587   self.rowBuilders = [];
2588   self.rowsProcessors = [];
2589   self.columnsProcessors = [];
2590   self.styleComputations = [];
2591   self.viewportAdjusters = [];
2592   self.rowHeaderColumns = [];
2593
2594   // self.visibleRowCache = [];
2595
2596   // Set of 'render' containers for self grid, which can render sets of rows
2597   self.renderContainers = {};
2598
2599   // Create a
2600   self.renderContainers.body = new GridRenderContainer('body', self);
2601
2602   self.cellValueGetterCache = {};
2603
2604   // Cached function to use with custom row templates
2605   self.getRowTemplateFn = null;
2606
2607
2608   //representation of the rows on the grid.
2609   //these are wrapped references to the actual data rows (options.data)
2610   self.rows = [];
2611
2612   //represents the columns on the grid
2613   self.columns = [];
2614
2615   /**
2616    * @ngdoc boolean
2617    * @name isScrollingVertically
2618    * @propertyOf ui.grid.class:Grid
2619    * @description set to true when Grid is scrolling vertically. Set to false via debounced method
2620    */
2621   self.isScrollingVertically = false;
2622
2623   /**
2624    * @ngdoc boolean
2625    * @name isScrollingHorizontally
2626    * @propertyOf ui.grid.class:Grid
2627    * @description set to true when Grid is scrolling horizontally. Set to false via debounced method
2628    */
2629   self.isScrollingHorizontally = false;
2630
2631   var debouncedVertical = gridUtil.debounce(function () {
2632     self.isScrollingVertically = false;
2633   }, 300);
2634
2635   var debouncedHorizontal = gridUtil.debounce(function () {
2636     self.isScrollingHorizontally = false;
2637   }, 300);
2638
2639
2640   /**
2641    * @ngdoc function
2642    * @name flagScrollingVertically
2643    * @methodOf ui.grid.class:Grid
2644    * @description sets isScrollingVertically to true and sets it to false in a debounced function
2645    */
2646   self.flagScrollingVertically = function() {
2647     self.isScrollingVertically = true;
2648     debouncedVertical();
2649   };
2650
2651   /**
2652    * @ngdoc function
2653    * @name flagScrollingHorizontally
2654    * @methodOf ui.grid.class:Grid
2655    * @description sets isScrollingHorizontally to true and sets it to false in a debounced function
2656    */
2657   self.flagScrollingHorizontally = function() {
2658     self.isScrollingHorizontally = true;
2659     debouncedHorizontal();
2660   };
2661
2662
2663
2664   self.api = new GridApi(self);
2665
2666   /**
2667    * @ngdoc function
2668    * @name refresh
2669    * @methodOf ui.grid.core.api:PublicApi
2670    * @description Refresh the rendered grid on screen.
2671    * 
2672    */
2673   self.api.registerMethod( 'core', 'refresh', this.refresh );
2674
2675   /**
2676    * @ngdoc function
2677    * @name refreshRows
2678    * @methodOf ui.grid.core.api:PublicApi
2679    * @description Refresh the rendered grid on screen?  Note: not functional at present
2680    * @returns {promise} promise that is resolved when render completes?
2681    * 
2682    */
2683   self.api.registerMethod( 'core', 'refreshRows', this.refreshRows );
2684
2685
2686   /**
2687    * @ngdoc function
2688    * @name sortChanged
2689    * @methodOf  ui.grid.core.api:PublicApi
2690    * @description The sort criteria on one or more columns has
2691    * changed.  Provides as parameters the grid and the output of
2692    * getColumnSorting, which is an array of gridColumns
2693    * that have sorting on them, sorted in priority order. 
2694    * 
2695    * @param {Grid} grid the grid
2696    * @param {array} sortColumns an array of columns with 
2697    * sorts on them, in priority order
2698    * 
2699    * @example
2700    * <pre>
2701    *      gridApi.core.on.sortChanged( grid, sortColumns );
2702    * </pre>
2703    */
2704   self.api.registerEvent( 'core', 'sortChanged' );
2705 };
2706
2707     /**
2708      * @ngdoc function
2709      * @name isRTL
2710      * @methodOf ui.grid.class:Grid
2711      * @description Returns true if grid is RightToLeft
2712      */
2713     Grid.prototype.isRTL = function () {
2714       return this.rtl;
2715     };
2716
2717
2718       /**
2719    * @ngdoc function
2720    * @name registerColumnBuilder
2721    * @methodOf ui.grid.class:Grid
2722    * @description When the build creates columns from column definitions, the columnbuilders will be called to add
2723    * additional properties to the column.
2724    * @param {function(colDef, col, gridOptions)} columnsProcessor function to be called
2725    */
2726   Grid.prototype.registerColumnBuilder = function registerColumnBuilder(columnBuilder) {
2727     this.columnBuilders.push(columnBuilder);
2728   };
2729
2730   /**
2731    * @ngdoc function
2732    * @name buildColumnDefsFromData
2733    * @methodOf ui.grid.class:Grid
2734    * @description Populates columnDefs from the provided data
2735    * @param {function(colDef, col, gridOptions)} rowBuilder function to be called
2736    */
2737   Grid.prototype.buildColumnDefsFromData = function (dataRows){
2738     this.options.columnDefs =  gridUtil.getColumnsFromData(dataRows,  this.options.excludeProperties);
2739   };
2740
2741   /**
2742    * @ngdoc function
2743    * @name registerRowBuilder
2744    * @methodOf ui.grid.class:Grid
2745    * @description When the build creates rows from gridOptions.data, the rowBuilders will be called to add
2746    * additional properties to the row.
2747    * @param {function(colDef, col, gridOptions)} rowBuilder function to be called
2748    */
2749   Grid.prototype.registerRowBuilder = function registerRowBuilder(rowBuilder) {
2750     this.rowBuilders.push(rowBuilder);
2751   };
2752
2753   /**
2754    * @ngdoc function
2755    * @name getColumn
2756    * @methodOf ui.grid.class:Grid
2757    * @description returns a grid column for the column name
2758    * @param {string} name column name
2759    */
2760   Grid.prototype.getColumn = function getColumn(name) {
2761     var columns = this.columns.filter(function (column) {
2762       return column.colDef.name === name;
2763     });
2764     return columns.length > 0 ? columns[0] : null;
2765   };
2766
2767   /**
2768    * @ngdoc function
2769    * @name getColDef
2770    * @methodOf ui.grid.class:Grid
2771    * @description returns a grid colDef for the column name
2772    * @param {string} name column.field
2773    */
2774   Grid.prototype.getColDef = function getColDef(name) {
2775     var colDefs = this.options.columnDefs.filter(function (colDef) {
2776       return colDef.name === name;
2777     });
2778     return colDefs.length > 0 ? colDefs[0] : null;
2779   };
2780
2781   /**
2782    * @ngdoc function
2783    * @name assignTypes
2784    * @methodOf ui.grid.class:Grid
2785    * @description uses the first row of data to assign colDef.type for any types not defined.
2786    */
2787   /**
2788    * @ngdoc property
2789    * @name type
2790    * @propertyOf ui.grid.class:GridOptions.columnDef
2791    * @description the type of the column, used in sorting.  If not provided then the 
2792    * grid will guess the type.  Add this only if the grid guessing is not to your
2793    * satisfaction.  Refer to {@link ui.grid.service:GridUtil.guessType gridUtil.guessType} for
2794    * a list of values the grid knows about.
2795    *
2796    */
2797   Grid.prototype.assignTypes = function(){
2798     var self = this;
2799     self.options.columnDefs.forEach(function (colDef, index) {
2800
2801       //Assign colDef type if not specified
2802       if (!colDef.type) {
2803         var col = new GridColumn(colDef, index, self);
2804         var firstRow = self.rows.length > 0 ? self.rows[0] : null;
2805         if (firstRow) {
2806           colDef.type = gridUtil.guessType(self.getCellValue(firstRow, col));
2807         }
2808         else {
2809           $log.log('Unable to assign type from data, so defaulting to string');
2810           colDef.type = 'string';
2811         }
2812       }
2813     });
2814   };
2815
2816   /**
2817   * @ngdoc function
2818   * @name addRowHeaderColumn
2819   * @methodOf ui.grid.class:Grid
2820   * @description adds a row header column to the grid
2821   * @param {object} column def
2822   */
2823   Grid.prototype.addRowHeaderColumn = function addRowHeaderColumn(colDef) {
2824     var self = this;
2825     //self.createLeftContainer();
2826     var rowHeaderCol = new GridColumn(colDef, self.rowHeaderColumns.length + 1, self);
2827     rowHeaderCol.isRowHeader = true;
2828     if (self.isRTL()) {
2829       self.createRightContainer();
2830       rowHeaderCol.renderContainer = 'right';
2831     }
2832     else {
2833       self.createLeftContainer();
2834       rowHeaderCol.renderContainer = 'left';
2835     }
2836
2837     self.columnBuilders[0](colDef,rowHeaderCol,self.gridOptions)
2838       .then(function(){
2839         rowHeaderCol.enableFiltering = false;
2840         rowHeaderCol.enableSorting = false;
2841         self.rowHeaderColumns.push(rowHeaderCol);
2842       });
2843   };
2844
2845   /**
2846    * @ngdoc function
2847    * @name buildColumns
2848    * @methodOf ui.grid.class:Grid
2849    * @description creates GridColumn objects from the columnDefinition.  Calls each registered
2850    * columnBuilder to further process the column
2851    * @returns {Promise} a promise to load any needed column resources
2852    */
2853   Grid.prototype.buildColumns = function buildColumns() {
2854     $log.debug('buildColumns');
2855     var self = this;
2856     var builderPromises = [];
2857     var offset = self.rowHeaderColumns.length;
2858
2859     //add row header columns to the grid columns array
2860     angular.forEach(self.rowHeaderColumns, function (rowHeaderColumn) {
2861       offset++;
2862       self.columns.push(rowHeaderColumn);
2863     });
2864
2865     // Synchronize self.columns with self.options.columnDefs so that columns can also be removed.
2866     if (self.columns.length > self.options.columnDefs.length) {
2867       self.columns.forEach(function (column, index) {
2868         if (!self.getColDef(column.name)) {
2869           self.columns.splice(index, 1);
2870         }
2871       });
2872     }
2873
2874     self.options.columnDefs.forEach(function (colDef, index) {
2875       self.preprocessColDef(colDef);
2876       var col = self.getColumn(colDef.name);
2877
2878       if (!col) {
2879         col = new GridColumn(colDef, index + offset, self);
2880         self.columns.push(col);
2881       }
2882       else {
2883         col.updateColumnDef(colDef, col.index);
2884       }
2885
2886       self.columnBuilders.forEach(function (builder) {
2887         builderPromises.push(builder.call(self, colDef, col, self.options));
2888       });
2889     });
2890
2891     return $q.all(builderPromises);
2892   };
2893
2894 /**
2895  * @ngdoc function
2896  * @name preCompileCellTemplates
2897  * @methodOf ui.grid.class:Grid
2898  * @description precompiles all cell templates
2899  */
2900   Grid.prototype.preCompileCellTemplates = function() {
2901         this.columns.forEach(function (col) {
2902           var html = col.cellTemplate.replace(uiGridConstants.COL_FIELD, 'grid.getCellValue(row, col)');
2903
2904           var compiledElementFn = $compile(html);
2905           col.compiledElementFn = compiledElementFn;
2906         });
2907   };
2908
2909   /**
2910    * @ngdoc function
2911    * @name createLeftContainer
2912    * @methodOf ui.grid.class:Grid
2913    * @description creates the left render container if it doesn't already exist
2914    */
2915   Grid.prototype.createLeftContainer = function() {
2916     if (!this.hasLeftContainer()) {
2917       this.renderContainers.left = new GridRenderContainer('left', this, { disableColumnOffset: true });
2918     }
2919   };
2920
2921   /**
2922    * @ngdoc function
2923    * @name createRightContainer
2924    * @methodOf ui.grid.class:Grid
2925    * @description creates the right render container if it doesn't already exist
2926    */
2927   Grid.prototype.createRightContainer = function() {
2928     if (!this.hasRightContainer()) {
2929       this.renderContainers.right = new GridRenderContainer('right', this, { disableColumnOffset: true });
2930     }
2931   };
2932
2933   /**
2934    * @ngdoc function
2935    * @name hasLeftContainer
2936    * @methodOf ui.grid.class:Grid
2937    * @description returns true if leftContainer exists
2938    */
2939   Grid.prototype.hasLeftContainer = function() {
2940     return this.renderContainers.left !== undefined;
2941   };
2942
2943   /**
2944    * @ngdoc function
2945    * @name hasLeftContainer
2946    * @methodOf ui.grid.class:Grid
2947    * @description returns true if rightContainer exists
2948    */
2949   Grid.prototype.hasRightContainer = function() {
2950     return this.renderContainers.right !== undefined;
2951   };
2952
2953
2954       /**
2955    * undocumented function
2956    * @name preprocessColDef
2957    * @methodOf ui.grid.class:Grid
2958    * @description defaults the name property from field to maintain backwards compatibility with 2.x
2959    * validates that name or field is present
2960    */
2961   Grid.prototype.preprocessColDef = function preprocessColDef(colDef) {
2962     if (!colDef.field && !colDef.name) {
2963       throw new Error('colDef.name or colDef.field property is required');
2964     }
2965
2966     //maintain backwards compatibility with 2.x
2967     //field was required in 2.x.  now name is required
2968     if (colDef.name === undefined && colDef.field !== undefined) {
2969       colDef.name = colDef.field;
2970     }
2971
2972   };
2973
2974   // Return a list of items that exist in the `n` array but not the `o` array. Uses optional property accessors passed as third & fourth parameters
2975   Grid.prototype.newInN = function newInN(o, n, oAccessor, nAccessor) {
2976     var self = this;
2977
2978     var t = [];
2979     for (var i=0; i<n.length; i++) {
2980       var nV = nAccessor ? n[i][nAccessor] : n[i];
2981       
2982       var found = false;
2983       for (var j=0; j<o.length; j++) {
2984         var oV = oAccessor ? o[j][oAccessor] : o[j];
2985         if (self.options.rowEquality(nV, oV)) {
2986           found = true;
2987           break;
2988         }
2989       }
2990       if (!found) {
2991         t.push(nV);
2992       }
2993     }
2994     
2995     return t;
2996   };
2997
2998     /**
2999      * @ngdoc function
3000      * @name getRow
3001      * @methodOf ui.grid.class:Grid
3002      * @description returns the GridRow that contains the rowEntity
3003      * @param {object} rowEntity the gridOptions.data array element instance
3004      */
3005     Grid.prototype.getRow = function getRow(rowEntity) {
3006       var rows = this.rows.filter(function (row) {
3007         return row.entity === rowEntity;
3008       });
3009       return rows.length > 0 ? rows[0] : null;
3010     };
3011
3012
3013       /**
3014    * @ngdoc function
3015    * @name modifyRows
3016    * @methodOf ui.grid.class:Grid
3017    * @description creates or removes GridRow objects from the newRawData array.  Calls each registered
3018    * rowBuilder to further process the row
3019    *
3020    * Rows are identified using the gridOptions.rowEquality function
3021    */
3022   Grid.prototype.modifyRows = function modifyRows(newRawData) {
3023     var self = this,
3024         i,
3025         newRow;
3026
3027     if (self.rows.length === 0 && newRawData.length > 0) {
3028       if (self.options.enableRowHashing) {
3029         if (!self.rowHashMap) {
3030           self.createRowHashMap();
3031         }
3032
3033         for (i=0; i<newRawData.length; i++) {
3034           newRow = newRawData[i];
3035
3036           self.rowHashMap.put(newRow, {
3037             i: i,
3038             entity: newRow
3039           });
3040         }
3041       }
3042
3043       self.addRows(newRawData);
3044       //now that we have data, it is save to assign types to colDefs
3045       self.assignTypes();
3046     }
3047     else if (newRawData.length > 0) {
3048       var unfoundNewRows, unfoundOldRows, unfoundNewRowsToFind;
3049
3050       // If row hashing is turned on
3051       if (self.options.enableRowHashing) {
3052         // Array of new rows that haven't been found in the old rowset
3053         unfoundNewRows = [];
3054         // Array of new rows that we explicitly HAVE to search for manually in the old row set. They cannot be looked up by their identity (because it doesn't exist).
3055         unfoundNewRowsToFind = [];
3056         // Map of rows that have been found in the new rowset
3057         var foundOldRows = {};
3058         // Array of old rows that have NOT been found in the new rowset
3059         unfoundOldRows = [];
3060
3061         // Create the row HashMap if it doesn't exist already
3062         if (!self.rowHashMap) {
3063           self.createRowHashMap();
3064         }
3065         var rowhash = self.rowHashMap;
3066         
3067         // Make sure every new row has a hash
3068         for (i = 0; i < newRawData.length; i++) {
3069           newRow = newRawData[i];
3070
3071           // Flag this row as needing to be manually found if it didn't come in with a $$hashKey
3072           var mustFind = false;
3073           if (!self.options.getRowIdentity(newRow)) {
3074             mustFind = true;
3075           }
3076
3077           // See if the new row is already in the rowhash
3078           var found = rowhash.get(newRow);
3079           // If so...
3080           if (found) {
3081             // See if it's already being used by as GridRow
3082             if (found.row) {
3083               // If so, mark this new row as being found
3084               foundOldRows[self.options.rowIdentity(newRow)] = true;
3085             }
3086           }
3087           else {
3088             // Put the row in the hashmap with the index it corresponds to
3089             rowhash.put(newRow, {
3090               i: i,
3091               entity: newRow
3092             });
3093             
3094             // This row has to be searched for manually in the old row set
3095             if (mustFind) {
3096               unfoundNewRowsToFind.push(newRow);
3097             }
3098             else {
3099               unfoundNewRows.push(newRow);
3100             }
3101           }
3102         }
3103
3104         // Build the list of unfound old rows
3105         for (i = 0; i < self.rows.length; i++) {
3106           var row = self.rows[i];
3107           var hash = self.options.rowIdentity(row.entity);
3108           if (!foundOldRows[hash]) {
3109             unfoundOldRows.push(row);
3110           }
3111         }
3112       }
3113
3114       // Look for new rows
3115       var newRows = unfoundNewRows || [];
3116
3117       // The unfound new rows is either `unfoundNewRowsToFind`, if row hashing is turned on, or straight `newRawData` if it isn't
3118       var unfoundNew = (unfoundNewRowsToFind || newRawData);
3119
3120       // Search for real new rows in `unfoundNew` and concat them onto `newRows`
3121       newRows = newRows.concat(self.newInN(self.rows, unfoundNew, 'entity'));
3122       
3123       self.addRows(newRows); 
3124       
3125       var deletedRows = self.getDeletedRows((unfoundOldRows || self.rows), newRawData);
3126
3127       for (i = 0; i < deletedRows.length; i++) {
3128         if (self.options.enableRowHashing) {
3129           self.rowHashMap.remove(deletedRows[i].entity);
3130         }
3131
3132         self.rows.splice( self.rows.indexOf(deletedRows[i]), 1 );
3133       }
3134     }
3135     // Empty data set
3136     else {
3137       // Reset the row HashMap
3138       self.createRowHashMap();
3139
3140       // Reset the rows length!
3141       self.rows.length = 0;
3142     }
3143     
3144     var p1 = $q.when(self.processRowsProcessors(self.rows))
3145       .then(function (renderableRows) {
3146         return self.setVisibleRows(renderableRows);
3147       });
3148
3149     var p2 = $q.when(self.processColumnsProcessors(self.columns))
3150       .then(function (renderableColumns) {
3151         return self.setVisibleColumns(renderableColumns);
3152       });
3153
3154     return $q.all([p1, p2]);
3155   };
3156
3157   Grid.prototype.getDeletedRows = function(oldRows, newRows) {
3158     var self = this;
3159
3160     var olds = oldRows.filter(function (oldRow) {
3161       return !newRows.some(function (newItem) {
3162         return self.options.rowEquality(newItem, oldRow.entity);
3163       });
3164     });
3165     // var olds = self.newInN(newRows, oldRows, null, 'entity');
3166     // dump('olds', olds);
3167     return olds;
3168   };
3169
3170   /**
3171    * Private Undocumented Method
3172    * @name addRows
3173    * @methodOf ui.grid.class:Grid
3174    * @description adds the newRawData array of rows to the grid and calls all registered
3175    * rowBuilders. this keyword will reference the grid
3176    */
3177   Grid.prototype.addRows = function addRows(newRawData) {
3178     var self = this;
3179
3180     var existingRowCount = self.rows.length;
3181     for (var i=0; i < newRawData.length; i++) {
3182       var newRow = self.processRowBuilders(new GridRow(newRawData[i], i + existingRowCount, self));
3183
3184       if (self.options.enableRowHashing) {
3185         var found = self.rowHashMap.get(newRow.entity);
3186         if (found) {
3187           found.row = newRow;
3188         }
3189       }
3190
3191       self.rows.push(newRow);
3192     }
3193   };
3194
3195   /**
3196    * @ngdoc function
3197    * @name processRowBuilders
3198    * @methodOf ui.grid.class:Grid
3199    * @description processes all RowBuilders for the gridRow
3200    * @param {GridRow} gridRow reference to gridRow
3201    * @returns {GridRow} the gridRow with all additional behavior added
3202    */
3203   Grid.prototype.processRowBuilders = function processRowBuilders(gridRow) {
3204     var self = this;
3205
3206     self.rowBuilders.forEach(function (builder) {
3207       builder.call(self, gridRow, self.gridOptions);
3208     });
3209
3210     return gridRow;
3211   };
3212
3213   /**
3214    * @ngdoc function
3215    * @name registerStyleComputation
3216    * @methodOf ui.grid.class:Grid
3217    * @description registered a styleComputation function
3218    * 
3219    * If the function returns a value it will be appended into the grid's `<style>` block
3220    * @param {function($scope)} styleComputation function
3221    */
3222   Grid.prototype.registerStyleComputation = function registerStyleComputation(styleComputationInfo) {
3223     this.styleComputations.push(styleComputationInfo);
3224   };
3225
3226
3227   // NOTE (c0bra): We already have rowBuilders. I think these do exactly the same thing...
3228   // Grid.prototype.registerRowFilter = function(filter) {
3229   //   // TODO(c0bra): validate filter?
3230
3231   //   this.rowFilters.push(filter);
3232   // };
3233
3234   // Grid.prototype.removeRowFilter = function(filter) {
3235   //   var idx = this.rowFilters.indexOf(filter);
3236
3237   //   if (typeof(idx) !== 'undefined' && idx !== undefined) {
3238   //     this.rowFilters.slice(idx, 1);
3239   //   }
3240   // };
3241   
3242   // Grid.prototype.processRowFilters = function(rows) {
3243   //   var self = this;
3244   //   self.rowFilters.forEach(function (filter) {
3245   //     filter.call(self, rows);
3246   //   });
3247   // };
3248
3249
3250   /**
3251    * @ngdoc function
3252    * @name registerRowsProcessor
3253    * @methodOf ui.grid.class:Grid
3254    * @param {function(renderableRows)} rows processor function
3255    * @returns {Array[GridRow]} Updated renderable rows
3256    * @description
3257
3258      Register a "rows processor" function. When the rows are updated,
3259      the grid calls each registered "rows processor", which has a chance
3260      to alter the set of rows (sorting, etc) as long as the count is not
3261      modified.
3262    */
3263   Grid.prototype.registerRowsProcessor = function registerRowsProcessor(processor) {
3264     if (!angular.isFunction(processor)) {
3265       throw 'Attempt to register non-function rows processor: ' + processor;
3266     }
3267
3268     this.rowsProcessors.push(processor);
3269   };
3270
3271   /**
3272    * @ngdoc function
3273    * @name removeRowsProcessor
3274    * @methodOf ui.grid.class:Grid
3275    * @param {function(renderableRows)} rows processor function
3276    * @description Remove a registered rows processor
3277    */
3278   Grid.prototype.removeRowsProcessor = function removeRowsProcessor(processor) {
3279     var idx = this.rowsProcessors.indexOf(processor);
3280
3281     if (typeof(idx) !== 'undefined' && idx !== undefined) {
3282       this.rowsProcessors.splice(idx, 1);
3283     }
3284   };
3285   
3286   /**
3287    * Private Undocumented Method
3288    * @name processRowsProcessors
3289    * @methodOf ui.grid.class:Grid
3290    * @param {Array[GridRow]} The array of "renderable" rows
3291    * @param {Array[GridColumn]} The array of columns
3292    * @description Run all the registered rows processors on the array of renderable rows
3293    */
3294   Grid.prototype.processRowsProcessors = function processRowsProcessors(renderableRows) {
3295     var self = this;
3296
3297     // Create a shallow copy of the rows so that we can safely sort them without altering the original grid.rows sort order
3298     var myRenderableRows = renderableRows.slice(0);
3299     
3300     // self.rowsProcessors.forEach(function (processor) {
3301     //   myRenderableRows = processor.call(self, myRenderableRows, self.columns);
3302
3303     //   if (!renderableRows) {
3304     //     throw "Processor at index " + i + " did not return a set of renderable rows";
3305     //   }
3306
3307     //   if (!angular.isArray(renderableRows)) {
3308     //     throw "Processor at index " + i + " did not return an array";
3309     //   }
3310
3311     //   i++;
3312     // });
3313
3314     // Return myRenderableRows with no processing if we have no rows processors 
3315     if (self.rowsProcessors.length === 0) {
3316       return $q.when(myRenderableRows);
3317     }
3318   
3319     // Counter for iterating through rows processors
3320     var i = 0;
3321     
3322     // Promise for when we're done with all the processors
3323     var finished = $q.defer();
3324
3325     // This function will call the processor in self.rowsProcessors at index 'i', and then
3326     //   when done will call the next processor in the list, using the output from the processor
3327     //   at i as the argument for 'renderedRowsToProcess' on the next iteration.
3328     //  
3329     //   If we're at the end of the list of processors, we resolve our 'finished' callback with
3330     //   the result.
3331     function startProcessor(i, renderedRowsToProcess) {
3332       // Get the processor at 'i'
3333       var processor = self.rowsProcessors[i];
3334
3335       // Call the processor, passing in the rows to process and the current columns
3336       //   (note: it's wrapped in $q.when() in case the processor does not return a promise)
3337       return $q.when( processor.call(self, renderedRowsToProcess, self.columns) )
3338         .then(function handleProcessedRows(processedRows) {
3339           // Check for errors
3340           if (!processedRows) {
3341             throw "Processor at index " + i + " did not return a set of renderable rows";
3342           }
3343
3344           if (!angular.isArray(processedRows)) {
3345             throw "Processor at index " + i + " did not return an array";
3346           }
3347
3348           // Processor is done, increment the counter
3349           i++;
3350
3351           // If we're not done with the processors, call the next one
3352           if (i <= self.rowsProcessors.length - 1) {
3353             return startProcessor(i, processedRows);
3354           }
3355           // We're done! Resolve the 'finished' promise
3356           else {
3357             finished.resolve(processedRows);
3358           }
3359         });
3360     }
3361
3362     // Start on the first processor
3363     startProcessor(0, myRenderableRows);
3364     
3365     return finished.promise;
3366   };
3367
3368   Grid.prototype.setVisibleRows = function setVisibleRows(rows) {
3369     // $log.debug('setVisibleRows');
3370
3371     var self = this;
3372
3373     //var newVisibleRowCache = [];
3374
3375     // Reset all the render container row caches
3376     for (var i in self.renderContainers) {
3377       var container = self.renderContainers[i];
3378
3379       container.visibleRowCache.length = 0;
3380     }
3381     
3382     // rows.forEach(function (row) {
3383     for (var ri = 0; ri < rows.length; ri++) {
3384       var row = rows[ri];
3385
3386       // If the row is visible
3387       if (row.visible) {
3388         // newVisibleRowCache.push(row);
3389
3390         // If the row has a container specified
3391         if (typeof(row.renderContainer) !== 'undefined' && row.renderContainer) {
3392           self.renderContainers[row.renderContainer].visibleRowCache.push(row);
3393         }
3394         // If not, put it into the body container
3395         else {
3396           self.renderContainers.body.visibleRowCache.push(row);
3397         }
3398       }
3399     }
3400   };
3401
3402   /**
3403    * @ngdoc function
3404    * @name registerColumnsProcessor
3405    * @methodOf ui.grid.class:Grid
3406    * @param {function(renderableColumns)} rows processor function
3407    * @returns {Array[GridColumn]} Updated renderable columns
3408    * @description
3409
3410      Register a "columns processor" function. When the columns are updated,
3411      the grid calls each registered "columns processor", which has a chance
3412      to alter the set of columns, as long as the count is not modified.
3413    */
3414   Grid.prototype.registerColumnsProcessor = function registerColumnsProcessor(processor) {
3415     if (!angular.isFunction(processor)) {
3416       throw 'Attempt to register non-function rows processor: ' + processor;
3417     }
3418
3419     this.columnsProcessors.push(processor);
3420   };
3421
3422   Grid.prototype.removeColumnsProcessor = function removeColumnsProcessor(processor) {
3423     var idx = this.columnsProcessors.indexOf(processor);
3424
3425     if (typeof(idx) !== 'undefined' && idx !== undefined) {
3426       this.columnsProcessors.splice(idx, 1);
3427     }
3428   };
3429
3430   Grid.prototype.processColumnsProcessors = function processColumnsProcessors(renderableColumns) {
3431     var self = this;
3432
3433     // Create a shallow copy of the rows so that we can safely sort them without altering the original grid.rows sort order
3434     var myRenderableColumns = renderableColumns.slice(0);
3435
3436     // Return myRenderableRows with no processing if we have no rows processors 
3437     if (self.columnsProcessors.length === 0) {
3438       return $q.when(myRenderableColumns);
3439     }
3440   
3441     // Counter for iterating through rows processors
3442     var i = 0;
3443     
3444     // Promise for when we're done with all the processors
3445     var finished = $q.defer();
3446
3447     // This function will call the processor in self.rowsProcessors at index 'i', and then
3448     //   when done will call the next processor in the list, using the output from the processor
3449     //   at i as the argument for 'renderedRowsToProcess' on the next iteration.
3450     //  
3451     //   If we're at the end of the list of processors, we resolve our 'finished' callback with
3452     //   the result.
3453     function startProcessor(i, renderedColumnsToProcess) {
3454       // Get the processor at 'i'
3455       var processor = self.columnsProcessors[i];
3456
3457       // Call the processor, passing in the rows to process and the current columns
3458       //   (note: it's wrapped in $q.when() in case the processor does not return a promise)
3459       return $q.when( processor.call(self, renderedColumnsToProcess, self.rows) )
3460         .then(function handleProcessedRows(processedColumns) {
3461           // Check for errors
3462           if (!processedColumns) {
3463             throw "Processor at index " + i + " did not return a set of renderable rows";
3464           }
3465
3466           if (!angular.isArray(processedColumns)) {
3467             throw "Processor at index " + i + " did not return an array";
3468           }
3469
3470           // Processor is done, increment the counter
3471           i++;
3472
3473           // If we're not done with the processors, call the next one
3474           if (i <= self.columnsProcessors.length - 1) {
3475             return startProcessor(i, myRenderableColumns);
3476           }
3477           // We're done! Resolve the 'finished' promise
3478           else {
3479             finished.resolve(myRenderableColumns);
3480           }
3481         });
3482     }
3483
3484     // Start on the first processor
3485     startProcessor(0, myRenderableColumns);
3486     
3487     return finished.promise;
3488   };
3489
3490   Grid.prototype.setVisibleColumns = function setVisibleColumns(columns) {
3491     // $log.debug('setVisibleColumns');
3492
3493     var self = this;
3494
3495     // Reset all the render container row caches
3496     for (var i in self.renderContainers) {
3497       var container = self.renderContainers[i];
3498
3499       container.visibleColumnCache.length = 0;
3500     }
3501
3502     for (var ci = 0; ci < columns.length; ci++) {
3503       var column = columns[ci];
3504
3505       // If the column is visible
3506       if (column.visible) {
3507         // If the column has a container specified
3508         if (typeof(column.renderContainer) !== 'undefined' && column.renderContainer) {
3509           self.renderContainers[column.renderContainer].visibleColumnCache.push(column);
3510         }
3511         // If not, put it into the body container
3512         else {
3513           self.renderContainers.body.visibleColumnCache.push(column);
3514         }
3515       }
3516     }
3517   };
3518
3519   /**
3520    * @ngdoc function
3521    * @name handleWindowResize
3522    * @methodOf ui.grid.class:Grid
3523    * @description Triggered when the browser window resizes; automatically resizes the grid
3524    */
3525   Grid.prototype.handleWindowResize = function handleWindowResize($event) {
3526     var self = this;
3527
3528     self.gridWidth = gridUtil.elementWidth(self.element);
3529     self.gridHeight = gridUtil.elementHeight(self.element);
3530
3531     self.queueRefresh();
3532   };
3533
3534   /**
3535    * @ngdoc function
3536    * @name queueRefresh
3537    * @methodOf ui.grid.class:Grid
3538    * @description todo: @c0bra can you document this method?
3539    */
3540   Grid.prototype.queueRefresh = function queueRefresh() {
3541     var self = this;
3542     if (self.refreshCanceller) {
3543       $timeout.cancel(self.refreshCanceller);
3544     }
3545
3546     self.refreshCanceller = $timeout(function () {
3547       self.refreshCanvas(true);
3548     });
3549
3550     self.refreshCanceller.then(function () {
3551       self.refreshCanceller = null;
3552     });
3553
3554     return self.refreshCanceller;
3555   };
3556
3557   /**
3558    * @ngdoc function
3559    * @name buildStyles
3560    * @methodOf ui.grid.class:Grid
3561    * @description calls each styleComputation function
3562    */
3563   // TODO: this used to take $scope, but couldn't see that it was used
3564   Grid.prototype.buildStyles = function buildStyles() {
3565     // $log.debug('buildStyles');
3566
3567     var self = this;
3568     
3569     self.customStyles = '';
3570
3571     self.styleComputations
3572       .sort(function(a, b) {
3573         if (a.priority === null) { return 1; }
3574         if (b.priority === null) { return -1; }
3575         if (a.priority === null && b.priority === null) { return 0; }
3576         return a.priority - b.priority;
3577       })
3578       .forEach(function (compInfo) {
3579         // this used to provide $scope as a second parameter, but I couldn't find any 
3580         // style builders that used it, so removed it as part of moving to grid from controller
3581         var ret = compInfo.func.call(self);
3582
3583         if (angular.isString(ret)) {
3584           self.customStyles += '\n' + ret;
3585         }
3586       });
3587   };
3588
3589
3590   Grid.prototype.minColumnsToRender = function minColumnsToRender() {
3591     var self = this;
3592     var viewport = this.getViewportWidth();
3593
3594     var min = 0;
3595     var totalWidth = 0;
3596     self.columns.forEach(function(col, i) {
3597       if (totalWidth < viewport) {
3598         totalWidth += col.drawnWidth;
3599         min++;
3600       }
3601       else {
3602         var currWidth = 0;
3603         for (var j = i; j >= i - min; j--) {
3604           currWidth += self.columns[j].drawnWidth;
3605         }
3606         if (currWidth < viewport) {
3607           min++;
3608         }
3609       }
3610     });
3611
3612     return min;
3613   };
3614
3615   Grid.prototype.getBodyHeight = function getBodyHeight() {
3616     // Start with the viewportHeight
3617     var bodyHeight = this.getViewportHeight();
3618
3619     // Add the horizontal scrollbar height if there is one
3620     if (typeof(this.horizontalScrollbarHeight) !== 'undefined' && this.horizontalScrollbarHeight !== undefined && this.horizontalScrollbarHeight > 0) {
3621       bodyHeight = bodyHeight + this.horizontalScrollbarHeight;
3622     }
3623
3624     return bodyHeight;
3625   };
3626
3627   // NOTE: viewport drawable height is the height of the grid minus the header row height (including any border)
3628   // TODO(c0bra): account for footer height
3629   Grid.prototype.getViewportHeight = function getViewportHeight() {
3630     var self = this;
3631
3632     var viewPortHeight = this.gridHeight - this.headerHeight - this.footerHeight;
3633
3634     // Account for native horizontal scrollbar, if present
3635     if (typeof(this.horizontalScrollbarHeight) !== 'undefined' && this.horizontalScrollbarHeight !== undefined && this.horizontalScrollbarHeight > 0) {
3636       viewPortHeight = viewPortHeight - this.horizontalScrollbarHeight;
3637     }
3638
3639     var adjustment = self.getViewportAdjustment();
3640     
3641     viewPortHeight = viewPortHeight + adjustment.height;
3642
3643     // $log.debug('viewPortHeight', viewPortHeight);
3644
3645     return viewPortHeight;
3646   };
3647
3648   Grid.prototype.getViewportWidth = function getViewportWidth() {
3649     var self = this;
3650
3651     var viewPortWidth = this.gridWidth;
3652
3653     if (typeof(this.verticalScrollbarWidth) !== 'undefined' && this.verticalScrollbarWidth !== undefined && this.verticalScrollbarWidth > 0) {
3654       viewPortWidth = viewPortWidth - this.verticalScrollbarWidth;
3655     }
3656
3657     var adjustment = self.getViewportAdjustment();
3658     
3659     viewPortWidth = viewPortWidth + adjustment.width;
3660
3661     // $log.debug('getviewPortWidth', viewPortWidth);
3662
3663     return viewPortWidth;
3664   };
3665
3666   Grid.prototype.getHeaderViewportWidth = function getHeaderViewportWidth() {
3667     var viewPortWidth = this.getViewportWidth();
3668
3669     if (typeof(this.verticalScrollbarWidth) !== 'undefined' && this.verticalScrollbarWidth !== undefined && this.verticalScrollbarWidth > 0) {
3670       viewPortWidth = viewPortWidth + this.verticalScrollbarWidth;
3671     }
3672
3673     return viewPortWidth;
3674   };
3675
3676   Grid.prototype.registerViewportAdjuster = function registerViewportAdjuster(func) {
3677     this.viewportAdjusters.push(func);
3678   };
3679
3680   Grid.prototype.removeViewportAdjuster = function registerViewportAdjuster(func) {
3681     var idx = this.viewportAdjusters.indexOf(func);
3682
3683     if (typeof(idx) !== 'undefined' && idx !== undefined) {
3684       this.viewportAdjusters.splice(idx, 1);
3685     }
3686   };
3687
3688   Grid.prototype.getViewportAdjustment = function getViewportAdjustment() {
3689     var self = this;
3690
3691     var adjustment = { height: 0, width: 0 };
3692
3693     self.viewportAdjusters.forEach(function (func) {
3694       adjustment = func.call(this, adjustment);
3695     });
3696
3697     return adjustment;
3698   };
3699
3700   Grid.prototype.getVisibleRowCount = function getVisibleRowCount() {
3701     // var count = 0;
3702
3703     // this.rows.forEach(function (row) {
3704     //   if (row.visible) {
3705     //     count++;
3706     //   }
3707     // });
3708
3709     // return this.visibleRowCache.length;
3710     return this.renderContainers.body.visibleRowCache.length;
3711   };
3712
3713    Grid.prototype.getVisibleRows = function getVisibleRows() {
3714     return this.renderContainers.body.visibleRowCache;
3715    };
3716
3717   Grid.prototype.getVisibleColumnCount = function getVisibleColumnCount() {
3718     // var count = 0;
3719
3720     // this.rows.forEach(function (row) {
3721     //   if (row.visible) {
3722     //     count++;
3723     //   }
3724     // });
3725
3726     // return this.visibleRowCache.length;
3727     return this.renderContainers.body.visibleColumnCache.length;
3728   };
3729
3730
3731   Grid.prototype.searchRows = function searchRows(renderableRows) {
3732     return rowSearcher.search(this, renderableRows, this.columns);
3733   };
3734
3735   Grid.prototype.sortByColumn = function sortByColumn(renderableRows) {
3736     return rowSorter.sort(this, renderableRows, this.columns);
3737   };
3738
3739   Grid.prototype.getCellValue = function getCellValue(row, col){
3740     var self = this;
3741
3742     if (!self.cellValueGetterCache[col.colDef.name]) {
3743       self.cellValueGetterCache[col.colDef.name] = $parse(row.getEntityQualifiedColField(col));
3744     }
3745
3746     return self.cellValueGetterCache[col.colDef.name](row);
3747   };
3748
3749   
3750   Grid.prototype.getNextColumnSortPriority = function getNextColumnSortPriority() {
3751     var self = this,
3752         p = 0;
3753
3754     self.columns.forEach(function (col) {
3755       if (col.sort && col.sort.priority && col.sort.priority > p) {
3756         p = col.sort.priority;
3757       }
3758     });
3759
3760     return p + 1;
3761   };
3762
3763   /**
3764    * @ngdoc function
3765    * @name resetColumnSorting
3766    * @methodOf ui.grid.class:Grid
3767    * @description Return the columns that the grid is currently being sorted by
3768    * @param {GridColumn} [excludedColumn] Optional GridColumn to exclude from having its sorting reset
3769    */
3770   Grid.prototype.resetColumnSorting = function resetColumnSorting(excludeCol) {
3771     var self = this;
3772
3773     self.columns.forEach(function (col) {
3774       if (col !== excludeCol) {
3775         col.sort = {};
3776       }
3777     });
3778   };
3779
3780   /**
3781    * @ngdoc function
3782    * @name getColumnSorting
3783    * @methodOf ui.grid.class:Grid
3784    * @description Return the columns that the grid is currently being sorted by
3785    * @returns {Array[GridColumn]} An array of GridColumn objects
3786    */
3787   Grid.prototype.getColumnSorting = function getColumnSorting() {
3788     var self = this;
3789
3790     var sortedCols = [], myCols;
3791
3792     // Iterate through all the columns, sorted by priority
3793     // Make local copy of column list, because sorting is in-place and we do not want to
3794     // change the original sequence of columns
3795     myCols = self.columns.slice(0);
3796     myCols.sort(rowSorter.prioritySort).forEach(function (col) {
3797       if (col.sort && typeof(col.sort.direction) !== 'undefined' && col.sort.direction && (col.sort.direction === uiGridConstants.ASC || col.sort.direction === uiGridConstants.DESC)) {
3798         sortedCols.push(col);
3799       }
3800     });
3801
3802     return sortedCols;
3803   };
3804
3805   /**
3806    * @ngdoc function
3807    * @name sortColumn
3808    * @methodOf ui.grid.class:Grid
3809    * @description Set the sorting on a given column, optionally resetting any existing sorting on the Grid.
3810    * Emits the sortChanged event whenever the sort criteria are changed.
3811    * @param {GridColumn} column Column to set the sorting on
3812    * @param {uiGridConstants.ASC|uiGridConstants.DESC} [direction] Direction to sort by, either descending or ascending.
3813    *   If not provided, the column will iterate through the sort directions: ascending, descending, unsorted.
3814    * @param {boolean} [add] Add this column to the sorting. If not provided or set to `false`, the Grid will reset any existing sorting and sort
3815    *   by this column only
3816    * @returns {Promise} A resolved promise that supplies the column.
3817    */
3818   
3819   Grid.prototype.sortColumn = function sortColumn(column, directionOrAdd, add) {
3820     var self = this,
3821         direction = null;
3822
3823     if (typeof(column) === 'undefined' || !column) {
3824       throw new Error('No column parameter provided');
3825     }
3826
3827     // Second argument can either be a direction or whether to add this column to the existing sort.
3828     //   If it's a boolean, it's an add, otherwise, it's a direction
3829     if (typeof(directionOrAdd) === 'boolean') {
3830       add = directionOrAdd;
3831     }
3832     else {
3833       direction = directionOrAdd;
3834     }
3835     
3836     if (!add) {
3837       self.resetColumnSorting(column);
3838       column.sort.priority = 0;
3839     }
3840     else {
3841       column.sort.priority = self.getNextColumnSortPriority();
3842     }
3843
3844     if (!direction) {
3845       // Figure out the sort direction
3846       if (column.sort.direction && column.sort.direction === uiGridConstants.ASC) {
3847         column.sort.direction = uiGridConstants.DESC;
3848       }
3849       else if (column.sort.direction && column.sort.direction === uiGridConstants.DESC) {
3850         column.sort.direction = null;
3851       }
3852       else {
3853         column.sort.direction = uiGridConstants.ASC;
3854       }
3855     }
3856     else {
3857       column.sort.direction = direction;
3858     }
3859     
3860     self.api.core.raise.sortChanged( self, self.getColumnSorting() );
3861
3862     return $q.when(column);
3863   };
3864   
3865   /**
3866    * communicate to outside world that we are done with initial rendering
3867    */
3868   Grid.prototype.renderingComplete = function(){
3869     if (angular.isFunction(this.options.onRegisterApi)) {
3870       this.options.onRegisterApi(this.api);
3871     }
3872     this.api.core.raise.renderingComplete( this.api );
3873   };
3874
3875   Grid.prototype.createRowHashMap = function createRowHashMap() {
3876     var self = this;
3877
3878     var hashMap = new RowHashMap();
3879     hashMap.grid = self;
3880
3881     self.rowHashMap = hashMap;
3882   };
3883   
3884   
3885   /**
3886    * @ngdoc function
3887    * @name refresh
3888    * @methodOf ui.grid.class:Grid
3889    * @description Refresh the rendered grid on screen.
3890    * 
3891    */
3892   Grid.prototype.refresh = function refresh() {
3893     $log.debug('grid refresh');
3894     
3895     var self = this;
3896     
3897     var p1 = self.processRowsProcessors(self.rows).then(function (renderableRows) {
3898       self.setVisibleRows(renderableRows);
3899     });
3900
3901     var p2 = self.processColumnsProcessors(self.columns).then(function (renderableColumns) {
3902       self.setVisibleColumns(renderableColumns);
3903     });
3904
3905     return $q.all([p1, p2]).then(function () {
3906       self.redrawInPlace();
3907
3908       self.refreshCanvas(true);
3909     });
3910   };  
3911   
3912   /**
3913    * @ngdoc function
3914    * @name refreshRows
3915    * @methodOf ui.grid.class:Grid
3916    * @description Refresh the rendered rows on screen?  Note: not functional at present 
3917    * @returns {promise} promise that is resolved when render completes?
3918    * 
3919    */
3920   Grid.prototype.refreshRows = function refreshRows() {
3921     var self = this;
3922     
3923     return self.processRowsProcessors(self.rows)
3924       .then(function (renderableRows) {
3925         self.setVisibleRows(renderableRows);
3926
3927         // TODO: this method doesn't exist, so clearly refreshRows doesn't work.
3928         self.redrawRows();
3929
3930         self.refreshCanvas();
3931       });
3932   };
3933
3934   /**
3935    * @ngdoc function
3936    * @name redrawCanvas
3937    * @methodOf ui.grid.class:Grid
3938    * @description TBD
3939    * @params {object} buildStyles optional parameter.  Use TBD
3940    * @returns {promise} promise that is resolved when the canvas
3941    * has been refreshed
3942    * 
3943    */
3944   Grid.prototype.refreshCanvas = function(buildStyles) {
3945     var self = this;
3946     
3947     if (buildStyles) {
3948       self.buildStyles();
3949     }
3950
3951     var p = $q.defer();
3952
3953     // Get all the header heights
3954     var containerHeadersToRecalc = [];
3955     for (var containerId in self.renderContainers) {
3956       if (self.renderContainers.hasOwnProperty(containerId)) {
3957         var container = self.renderContainers[containerId];
3958
3959         if (container.header) {
3960           containerHeadersToRecalc.push(container);
3961         }
3962       }
3963     }
3964
3965     if (containerHeadersToRecalc.length > 0) {
3966       // Putting in a timeout as it's not calculating after the grid element is rendered and filled out
3967       $timeout(function() {
3968         // var oldHeaderHeight = self.grid.headerHeight;
3969         // self.grid.headerHeight = gridUtil.outerElementHeight(self.header);
3970
3971         var rebuildStyles = false;
3972
3973         // Get all the header heights
3974         for (var i = 0; i < containerHeadersToRecalc.length; i++) {
3975           var container = containerHeadersToRecalc[i];
3976
3977           if (container.header) {
3978             var oldHeaderHeight = container.headerHeight;
3979             var headerHeight = gridUtil.outerElementHeight(container.header);
3980             container.headerHeight = headerHeight;
3981
3982             if (oldHeaderHeight !== headerHeight) {
3983               rebuildStyles = true;
3984             }
3985           }
3986         }
3987
3988         // Rebuild styles if the header height has changed
3989         //   The header height is used in body/viewport calculations and those are then used in other styles so we need it to be available
3990         if (buildStyles && rebuildStyles) {
3991           self.buildStyles();
3992         }
3993
3994         p.resolve();
3995       });
3996     }
3997     else {
3998       // Timeout still needs to be here to trigger digest after styles have been rebuilt
3999       $timeout(function() {
4000         p.resolve();
4001       });
4002     }
4003
4004     return p.promise;
4005   };
4006
4007
4008   /**
4009    * @ngdoc function
4010    * @name redrawCanvas
4011    * @methodOf ui.grid.class:Grid
4012    * @description Redraw the rows and columns based on our current scroll position
4013    * 
4014    */
4015   Grid.prototype.redrawInPlace = function redrawInPlace() {
4016     // $log.debug('redrawInPlace');
4017     
4018     var self = this;
4019
4020     for (var i in self.renderContainers) {
4021       var container = self.renderContainers[i];
4022
4023       // $log.debug('redrawing container', i);
4024
4025       container.adjustRows(container.prevScrollTop, null);
4026       container.adjustColumns(container.prevScrollLeft, null);
4027     }
4028   };
4029
4030
4031   // Blatantly stolen from Angular as it isn't exposed (yet? 2.0?)
4032   function RowHashMap() {}
4033
4034   RowHashMap.prototype = {
4035     /**
4036      * Store key value pair
4037      * @param key key to store can be any type
4038      * @param value value to store can be any type
4039      */
4040     put: function(key, value) {
4041       this[this.grid.options.rowIdentity(key)] = value;
4042     },
4043
4044     /**
4045      * @param key
4046      * @returns {Object} the value for the key
4047      */
4048     get: function(key) {
4049       return this[this.grid.options.rowIdentity(key)];
4050     },
4051
4052     /**
4053      * Remove the key/value pair
4054      * @param key
4055      */
4056     remove: function(key) {
4057       var value = this[key = this.grid.options.rowIdentity(key)];
4058       delete this[key];
4059       return value;
4060     }
4061   };
4062
4063
4064
4065   return Grid;
4066
4067 }]);
4068
4069 })();
4070
4071 (function () {
4072
4073   angular.module('ui.grid')
4074     .factory('GridApi', ['$log', '$q', '$rootScope', 'gridUtil', 'uiGridConstants',
4075       function ($log, $q, $rootScope, gridUtil, uiGridConstants) {
4076         /**
4077          * @ngdoc function
4078          * @name ui.grid.class:GridApi
4079          * @description GridApi provides the ability to register public methods events inside the grid and allow
4080          * for other components to use the api via featureName.methodName and featureName.on.eventName(function(args){}
4081          * @param {object} grid grid that owns api
4082          */
4083         var GridApi = function GridApi(grid) {
4084           this.grid = grid;
4085           this.listeners = [];
4086           
4087           /**
4088            * @ngdoc function
4089            * @name renderingComplete
4090            * @methodOf  ui.grid.core.api:PublicApi
4091            * @description Rendering is complete, called at the same
4092            * time as `onRegisterApi`, but provides a way to obtain
4093            * that same event within features without stopping end
4094            * users from getting at the onRegisterApi method.
4095            * 
4096            * Included in gridApi so that it's always there - otherwise
4097            * there is still a timing problem with when a feature can
4098            * call this. 
4099            * 
4100            * @param {GridApi} gridApi the grid api, as normally 
4101            * returned in the onRegisterApi method
4102            * 
4103            * @example
4104            * <pre>
4105            *      gridApi.core.on.renderingComplete( grid );
4106            * </pre>
4107            */
4108           this.registerEvent( 'core', 'renderingComplete' );
4109         };
4110
4111         /**
4112          * @ngdoc function
4113          * @name ui.grid.class:suppressEvents
4114          * @methodOf ui.grid.class:GridApi
4115          * @description Used to execute a function while disabling the specified event listeners.
4116          * Disables the listenerFunctions, executes the callbackFn, and then enables
4117          * the listenerFunctions again
4118          * @param {object} listenerFuncs listenerFunc or array of listenerFuncs to suppress. These must be the same
4119          * functions that were used in the .on.eventName method
4120          * @param {object} callBackFn function to execute
4121          * @example
4122          * <pre>
4123          *    var navigate = function (newRowCol, oldRowCol){
4124          *       //do something on navigate
4125          *    }
4126          *
4127          *    gridApi.cellNav.on.navigate(scope,navigate);
4128          *
4129          *
4130          *    //call the scrollTo event and suppress our navigate listener
4131          *    //scrollTo will still raise the event for other listeners
4132          *    gridApi.suppressEvents(navigate, function(){
4133          *       gridApi.cellNav.scrollTo(aRow, aCol);
4134          *    });
4135          *
4136          * </pre>
4137          */
4138         GridApi.prototype.suppressEvents = function (listenerFuncs, callBackFn) {
4139           var self = this;
4140           var listeners = angular.isArray(listenerFuncs) ? listenerFuncs : [listenerFuncs];
4141
4142           //find all registered listeners
4143           var foundListeners = [];
4144           listeners.forEach(function (l) {
4145             foundListeners = self.listeners.filter(function (lstnr) {
4146               return l === lstnr.handler;
4147             });
4148           });
4149
4150           //deregister all the listeners
4151           foundListeners.forEach(function(l){
4152             l.dereg();
4153           });
4154
4155           callBackFn();
4156
4157           //reregister all the listeners
4158           foundListeners.forEach(function(l){
4159               l.dereg = registerEventWithAngular(l.scope, l.eventId, l.handler, self.grid);
4160           });
4161
4162         };
4163
4164         /**
4165          * @ngdoc function
4166          * @name registerEvent
4167          * @methodOf ui.grid.class:GridApi
4168          * @description Registers a new event for the given feature
4169          * @param {string} featureName name of the feature that raises the event
4170          * @param {string} eventName  name of the event
4171          */
4172         GridApi.prototype.registerEvent = function (featureName, eventName) {
4173           var self = this;
4174           if (!self[featureName]) {
4175             self[featureName] = {};
4176           }
4177
4178           var feature = self[featureName];
4179           if (!feature.on) {
4180             feature.on = {};
4181             feature.raise = {};
4182           }
4183
4184           var eventId = self.grid.id + featureName + eventName;
4185
4186           $log.log('Creating raise event method ' + featureName + '.raise.' + eventName);
4187           feature.raise[eventName] = function () {
4188             $rootScope.$broadcast.apply($rootScope, [eventId].concat(Array.prototype.slice.call(arguments)));
4189           };
4190
4191           $log.log('Creating on event method ' + featureName + '.on.' + eventName);
4192           feature.on[eventName] = function (scope, handler) {
4193             var dereg = registerEventWithAngular(scope, eventId, handler, self.grid);
4194
4195             //track our listener so we can turn off and on
4196             var listener = {handler: handler, dereg: dereg, eventId: eventId, scope: scope};
4197             self.listeners.push(listener);
4198
4199             //destroy tracking when scope is destroyed
4200             //wanted to remove the listener from the array but angular does
4201             //strange things in scope.$destroy so I could not access the listener array
4202             scope.$on('$destroy', function() {
4203               listener.dereg = null;
4204               listener.handler = null;
4205               listener.eventId = null;
4206               listener.scope = null;
4207             });
4208           };
4209         };
4210
4211         function registerEventWithAngular(scope, eventId, handler, grid) {
4212           return scope.$on(eventId, function (event) {
4213             var args = Array.prototype.slice.call(arguments);
4214             args.splice(0, 1); //remove evt argument
4215             handler.apply(grid.api, args);
4216           });
4217         }
4218
4219         /**
4220          * @ngdoc function
4221          * @name registerEventsFromObject
4222          * @methodOf ui.grid.class:GridApi
4223          * @description Registers features and events from a simple objectMap.
4224          * eventObjectMap must be in this format (multiple features allowed)
4225          * <pre>
4226          * {featureName:
4227          *        {
4228          *          eventNameOne:function(args){},
4229          *          eventNameTwo:function(args){}
4230          *        }
4231          *  }
4232          * </pre>
4233          * @param {object} eventObjectMap map of feature/event names
4234          */
4235         GridApi.prototype.registerEventsFromObject = function (eventObjectMap) {
4236           var self = this;
4237           var features = [];
4238           angular.forEach(eventObjectMap, function (featProp, featPropName) {
4239             var feature = {name: featPropName, events: []};
4240             angular.forEach(featProp, function (prop, propName) {
4241               feature.events.push(propName);
4242             });
4243             features.push(feature);
4244           });
4245
4246           features.forEach(function (feature) {
4247             feature.events.forEach(function (event) {
4248               self.registerEvent(feature.name, event);
4249             });
4250           });
4251
4252         };
4253
4254         /**
4255          * @ngdoc function
4256          * @name registerMethod
4257          * @methodOf ui.grid.class:GridApi
4258          * @description Registers a new event for the given feature
4259          * @param {string} featureName name of the feature
4260          * @param {string} methodName  name of the method
4261          * @param {object} callBackFn function to execute
4262          * @param {object} thisArg binds callBackFn 'this' to thisArg.  Defaults to gridApi.grid
4263          */
4264         GridApi.prototype.registerMethod = function (featureName, methodName, callBackFn, thisArg) {
4265           if (!this[featureName]) {
4266             this[featureName] = {};
4267           }
4268
4269           var feature = this[featureName];
4270
4271           feature[methodName] = gridUtil.createBoundedWrapper(thisArg || this.grid, callBackFn);
4272         };
4273
4274         /**
4275          * @ngdoc function
4276          * @name registerMethodsFromObject
4277          * @methodOf ui.grid.class:GridApi
4278          * @description Registers features and methods from a simple objectMap.
4279          * eventObjectMap must be in this format (multiple features allowed)
4280          * <br>
4281          * {featureName:
4282          *        {
4283          *          methodNameOne:function(args){},
4284          *          methodNameTwo:function(args){}
4285          *        }
4286          * @param {object} eventObjectMap map of feature/event names
4287          * @param {object} thisArg binds this to thisArg for all functions.  Defaults to gridApi.grid
4288          */
4289         GridApi.prototype.registerMethodsFromObject = function (methodMap, thisArg) {
4290           var self = this;
4291           var features = [];
4292           angular.forEach(methodMap, function (featProp, featPropName) {
4293             var feature = {name: featPropName, methods: []};
4294             angular.forEach(featProp, function (prop, propName) {
4295               feature.methods.push({name: propName, fn: prop});
4296             });
4297             features.push(feature);
4298           });
4299
4300           features.forEach(function (feature) {
4301             feature.methods.forEach(function (method) {
4302               self.registerMethod(feature.name, method.name, method.fn, thisArg);
4303             });
4304           });
4305
4306         };
4307         
4308         return GridApi;
4309
4310       }]);
4311
4312 })();
4313
4314 (function(){
4315
4316 angular.module('ui.grid')
4317 .factory('GridColumn', ['gridUtil', 'uiGridConstants', function(gridUtil, uiGridConstants) {
4318
4319   /**
4320    * @ngdoc function
4321    * @name ui.grid.class:GridColumn
4322    * @description Represents the viewModel for each column.  Any state or methods needed for a Grid Column
4323    * are defined on this prototype
4324    * @param {ColDef} colDef Column definition.
4325    * @param {number} index the current position of the column in the array
4326    * @param {Grid} grid reference to the grid
4327    */
4328    
4329    /**
4330     * ******************************************************************************************
4331     * PaulL1: Ugly hack here in documentation.  These properties are clearly properties of GridColumn, 
4332     * and need to be noted as such for those extending and building ui-grid itself.
4333     * However, from an end-developer perspective, they interact with all these through columnDefs,
4334     * and they really need to be documented there.  I feel like they're relatively static, and
4335     * I can't find an elegant way for ngDoc to reference to both....so I've duplicated each
4336     * comment block.  Ugh.
4337     * 
4338     */
4339
4340    /** 
4341     * @ngdoc property
4342     * @name name
4343     * @propertyOf ui.grid.class:GridColumn
4344     * @description (mandatory) each column should have a name, although for backward
4345     * compatibility with 2.x name can be omitted if field is present
4346     *
4347     */
4348
4349    /** 
4350     * @ngdoc property
4351     * @name name
4352     * @propertyOf ui.grid.class:GridOptions.columnDef
4353     * @description (mandatory) each column should have a name, although for backward
4354     * compatibility with 2.x name can be omitted if field is present
4355     *
4356     */
4357     
4358     /** 
4359     * @ngdoc property
4360     * @name displayName
4361     * @propertyOf ui.grid.class:GridColumn
4362     * @description Column name that will be shown in the header.  If displayName is not
4363     * provided then one is generated using the name.
4364     *
4365     */
4366
4367     /** 
4368     * @ngdoc property
4369     * @name displayName
4370     * @propertyOf ui.grid.class:GridOptions.columnDef
4371     * @description Column name that will be shown in the header.  If displayName is not
4372     * provided then one is generated using the name.
4373     *
4374     */
4375        
4376     /** 
4377     * @ngdoc property
4378     * @name field
4379     * @propertyOf ui.grid.class:GridColumn
4380     * @description field must be provided if you wish to bind to a 
4381     * property in the data source.  Should be an angular expression that evaluates against grid.options.data 
4382     * array element.  Can be a complex expression: <code>employee.address.city</code>, or can be a function: <code>employee.getFullAddress()</code>.
4383     * See the angular docs on binding expressions.
4384     *
4385     */
4386     
4387     /** 
4388     * @ngdoc property
4389     * @name field
4390     * @propertyOf ui.grid.class:GridOptions.columnDef
4391     * @description field must be provided if you wish to bind to a 
4392     * property in the data source.  Should be an angular expression that evaluates against grid.options.data 
4393     * array element.  Can be a complex expression: <code>employee.address.city</code>, or can be a function: <code>employee.getFullAddress()</code>.
4394     * See the angular docs on binding expressions.
4395     *
4396     */
4397     
4398     /** 
4399     * @ngdoc property
4400     * @name filter
4401     * @propertyOf ui.grid.class:GridColumn
4402     * @description Filter on this column.  
4403     * @example
4404     * <pre>{ term: 'text', condition: uiGridConstants.filter.STARTS_WITH, placeholder: 'type to filter...' }</pre>
4405     *
4406     */
4407
4408     /** 
4409     * @ngdoc property
4410     * @name filter
4411     * @propertyOf ui.grid.class:GridOptions.columnDef
4412     * @description Specify a single filter field on this column.
4413     * @example
4414     * <pre>$scope.gridOptions.columnDefs = [ 
4415     *   {
4416     *     field: 'field1',
4417     *     filter: {
4418     *       condition: uiGridConstants.filter.STARTS_WITH,
4419     *       placeholder: 'starts with...'
4420     *     }
4421     *   }
4422     * ]; </pre>
4423     *
4424     */
4425     
4426    
4427   function GridColumn(colDef, index, grid) {
4428     var self = this;
4429
4430     self.grid = grid;
4431     colDef.index = index;
4432
4433     self.updateColumnDef(colDef);
4434   }
4435
4436   GridColumn.prototype.setPropertyOrDefault = function (colDef, propName, defaultValue) {
4437     var self = this;
4438
4439     // Use the column definition filter if we were passed it
4440     if (typeof(colDef[propName]) !== 'undefined' && colDef[propName]) {
4441       self[propName] = colDef[propName];
4442     }
4443     // Otherwise use our own if it's set
4444     else if (typeof(self[propName]) !== 'undefined') {
4445       self[propName] = self[propName];
4446     }
4447     // Default to empty object for the filter
4448     else {
4449       self[propName] = defaultValue ? defaultValue : {};
4450     }
4451   };
4452
4453   
4454   
4455    /** 
4456     * @ngdoc property
4457     * @name width
4458     * @propertyOf ui.grid.class:GridOptions.columnDef
4459     * @description sets the column width.  Can be either 
4460     * a number or a percentage, or an * for auto.
4461     * @example
4462     * <pre>  $scope.gridOptions.columnDefs = [ { field: 'field1', width: 100},
4463     *                                          { field: 'field2', width: '20%'},
4464     *                                          { field: 'field3', width: '*' }]; </pre>
4465     *
4466     */
4467
4468    /** 
4469     * @ngdoc property
4470     * @name minWidth
4471     * @propertyOf ui.grid.class:GridOptions.columnDef
4472     * @description sets the minimum column width.  Should be a number.
4473     * @example
4474     * <pre>  $scope.gridOptions.columnDefs = [ { field: 'field1', minWidth: 100}]; </pre>
4475     *
4476     */
4477
4478    /** 
4479     * @ngdoc property
4480     * @name maxWidth
4481     * @propertyOf ui.grid.class:GridOptions.columnDef
4482     * @description sets the maximum column width.  Should be a number.
4483     * @example
4484     * <pre>  $scope.gridOptions.columnDefs = [ { field: 'field1', maxWidth: 100}]; </pre>
4485     *
4486     */
4487
4488    /** 
4489     * @ngdoc property
4490     * @name visible
4491     * @propertyOf ui.grid.class:GridOptions.columnDef
4492     * @description sets whether or not the column is visible
4493     * </br>Default is true
4494     * @example
4495     * <pre>  $scope.gridOptions.columnDefs = [ 
4496     *     { field: 'field1', visible: true},
4497     *     { field: 'field2', visible: false }
4498     *   ]; </pre>
4499     *
4500     */
4501    
4502   /**
4503    * @ngdoc property
4504    * @name sort
4505    * @propertyOf ui.grid.class:GridOptions.columnDef
4506    * @description Can be used to set the sort direction for the column, values are
4507    * uiGridConstants.ASC or uiGridConstants.DESC
4508    * @example
4509    * <pre>  $scope.gridOptions.columnDefs = [ { field: 'field1', sort: { direction: uiGridConstants.ASC }}] </pre>
4510    */
4511   
4512
4513     /** 
4514     * @ngdoc property
4515     * @name sortingAlgorithm
4516     * @propertyOf ui.grid.class:GridColumn
4517     * @description Algorithm to use for sorting this column. Takes 'a' and 'b' parameters 
4518     * like any normal sorting function.
4519     *
4520     */
4521
4522     /** 
4523     * @ngdoc property
4524     * @name sortingAlgorithm
4525     * @propertyOf ui.grid.class:GridOptions.columnDef
4526     * @description Algorithm to use for sorting this column. Takes 'a' and 'b' parameters 
4527     * like any normal sorting function.
4528     *
4529     */
4530       
4531    /** 
4532     * @ngdoc array
4533     * @name filters
4534     * @propertyOf ui.grid.class:GridOptions.columnDef
4535     * @description Specify multiple filter fields.
4536     * @example
4537     * <pre>$scope.gridOptions.columnDefs = [ 
4538     *   {
4539     *     field: 'field1', filters: [
4540     *       {
4541     *         condition: uiGridConstants.filter.STARTS_WITH,
4542     *         placeholder: 'starts with...'
4543     *       },
4544     *       {
4545     *         condition: uiGridConstants.filter.ENDS_WITH,
4546     *         placeholder: 'ends with...'
4547     *       }
4548     *     ]
4549     *   }
4550     * ]; </pre>
4551     *
4552     * 
4553     */ 
4554    
4555    /** 
4556     * @ngdoc array
4557     * @name filters
4558     * @propertyOf ui.grid.class:GridColumn
4559     * @description Filters for this column. Includes 'term' property bound to filter input elements.
4560     * @example
4561     * <pre>[
4562     *   {
4563     *     term: 'foo', // ngModel for <input>
4564     *     condition: uiGridConstants.filter.STARTS_WITH,
4565     *     placeholder: 'starts with...'
4566     *   },
4567     *   {
4568     *     term: 'baz',
4569     *     condition: uiGridConstants.filter.ENDS_WITH,
4570     *     placeholder: 'ends with...'
4571     *   }
4572     * ] </pre>
4573     *
4574     * 
4575     */   
4576
4577    /** 
4578     * @ngdoc array
4579     * @name menuItems
4580     * @propertyOf ui.grid.class:GridOptions.columnDef
4581     * @description used to add menu items to a column.  Refer to the tutorial on this 
4582     * functionality.
4583     * @example
4584     * <pre>  $scope.gridOptions.columnDefs = [ 
4585     *   { field: 'field1', menuItems: [
4586     *     {
4587     *       title: 'Outer Scope Alert',
4588     *       icon: 'ui-grid-icon-info-circled',
4589     *       action: function($event) {
4590     *         this.context.blargh(); // $scope.blargh() would work too, this is just an example
4591     *       },
4592     *       context: $scope
4593     *     },
4594     *     {
4595     *       title: 'Grid ID',
4596     *       action: function() {
4597     *         alert('Grid ID: ' + this.grid.id);
4598     *       }
4599     *     }
4600     *   ] }]; </pre>
4601     *
4602     */   
4603   GridColumn.prototype.updateColumnDef = function(colDef, index) {
4604     var self = this;
4605
4606     self.colDef = colDef;
4607
4608     //position of column
4609     self.index = (typeof(index) === 'undefined') ? colDef.index : index;
4610
4611     if (colDef.name === undefined) {
4612       throw new Error('colDef.name is required for column at index ' + self.index);
4613     }
4614
4615     var parseErrorMsg = "Cannot parse column width '" + colDef.width + "' for column named '" + colDef.name + "'";
4616
4617     // If width is not defined, set it to a single star
4618     if (gridUtil.isNullOrUndefined(colDef.width)) {
4619       self.width = '*';
4620     }
4621     else {
4622       // If the width is not a number
4623       if (!angular.isNumber(colDef.width)) {
4624         // See if it ends with a percent
4625         if (gridUtil.endsWith(colDef.width, '%')) {
4626           // If so we should be able to parse the non-percent-sign part to a number
4627           var percentStr = colDef.width.replace(/%/g, '');
4628           var percent = parseInt(percentStr, 10);
4629           if (isNaN(percent)) {
4630             throw new Error(parseErrorMsg);
4631           }
4632           self.width = colDef.width;
4633         }
4634         // And see if it's a number string
4635         else if (colDef.width.match(/^(\d+)$/)) {
4636           self.width = parseInt(colDef.width.match(/^(\d+)$/)[1], 10);
4637         }
4638         // Otherwise it should be a string of asterisks
4639         else if (!colDef.width.match(/^\*+$/)) {
4640           throw new Error(parseErrorMsg);
4641         }
4642       }
4643       // Is a number, use it as the width
4644       else {
4645         self.width = colDef.width;
4646       }
4647     }
4648
4649     // Remove this column from the grid sorting
4650     GridColumn.prototype.unsort = function () {
4651       this.sort = {};
4652     };
4653
4654     self.minWidth = !colDef.minWidth ? 50 : colDef.minWidth;
4655     self.maxWidth = !colDef.maxWidth ? 9000 : colDef.maxWidth;
4656
4657     //use field if it is defined; name if it is not
4658     self.field = (colDef.field === undefined) ? colDef.name : colDef.field;
4659
4660     // Use colDef.displayName as long as it's not undefined, otherwise default to the field name
4661     self.displayName = (colDef.displayName === undefined) ? gridUtil.readableColumnName(colDef.name) : colDef.displayName;
4662
4663     //self.originalIndex = index;
4664
4665     self.aggregationType = angular.isDefined(colDef.aggregationType) ? colDef.aggregationType : null;
4666     self.footerCellTemplate = angular.isDefined(colDef.footerCellTemplate) ? colDef.footerCellTemplate : null;
4667
4668     /**
4669      * @ngdoc property
4670      * @name cellClass
4671      * @propertyOf ui.grid.class:GridOptions.columnDef
4672      * @description cellClass can be a string specifying the class to append to a cell
4673      * or it can be a function(row,rowRenderIndex, col, colRenderIndex) that returns a class name
4674      *
4675      */
4676     self.cellClass = colDef.cellClass;
4677
4678
4679     /**
4680      * @ngdoc property
4681      * @name cellFilter
4682      * @propertyOf ui.grid.class:GridOptions.columnDef
4683      * @description cellFilter is a filter to apply to the content of each cell
4684      * @example
4685      * <pre>
4686      *   gridOptions.columnDefs[0].cellFilter = 'date'
4687      *
4688      */
4689     self.cellFilter = colDef.cellFilter ? colDef.cellFilter : "";
4690
4691     /**
4692      * @ngdoc property
4693      * @name headerCellFilter
4694      * @propertyOf ui.grid.class:GridOptions.columnDef
4695      * @description headerCellFilter is a filter to apply to the content of the column header
4696      * @example
4697      * <pre>
4698      *   gridOptions.columnDefs[0].headerCellFilter = 'translate'
4699      *
4700      */
4701     self.headerCellFilter = colDef.headerCellFilter ? colDef.headerCellFilter : "";
4702
4703     self.visible = gridUtil.isNullOrUndefined(colDef.visible) || colDef.visible;
4704
4705     self.headerClass = colDef.headerClass;
4706     //self.cursor = self.sortable ? 'pointer' : 'default';
4707
4708     self.visible = true;
4709
4710     // Turn on sorting by default
4711     self.enableSorting = typeof(colDef.enableSorting) !== 'undefined' ? colDef.enableSorting : true;
4712     self.sortingAlgorithm = colDef.sortingAlgorithm;
4713
4714     // Turn on filtering by default (it's disabled by default at the Grid level)
4715     self.enableFiltering = typeof(colDef.enableFiltering) !== 'undefined' ? colDef.enableFiltering : true;
4716
4717     // self.menuItems = colDef.menuItems;
4718     self.setPropertyOrDefault(colDef, 'menuItems', []);
4719
4720     // Use the column definition sort if we were passed it
4721     self.setPropertyOrDefault(colDef, 'sort');
4722
4723     // Set up default filters array for when one is not provided.
4724     //   In other words, this (in column def):
4725     //   
4726     //       filter: { term: 'something', flags: {}, condition: [CONDITION] }
4727     //       
4728     //   is just shorthand for this:
4729     //   
4730     //       filters: [{ term: 'something', flags: {}, condition: [CONDITION] }]
4731     //       
4732     var defaultFilters = [];
4733     if (colDef.filter) {
4734       defaultFilters.push(colDef.filter);
4735     }
4736     else if (self.enableFiltering && self.grid.options.enableFiltering) {
4737       // Add an empty filter definition object, which will
4738       // translate to a guessed condition and no pre-populated
4739       // value for the filter <input>.
4740       defaultFilters.push({});
4741     }
4742
4743     /**
4744      * @ngdoc object
4745      * @name ui.grid.class:GridOptions.columnDef.filter
4746      * @propertyOf ui.grid.class:GridOptions.columnDef
4747      * @description An object defining filtering for a column.
4748      */    
4749
4750     /**
4751      * @ngdoc property
4752      * @name condition
4753      * @propertyOf ui.grid.class:GridOptions.columnDef.filter
4754      * @description Defines how rows are chosen as matching the filter term. This can be set to
4755      * one of the constants in uiGridConstants.filter, or you can supply a custom filter function
4756      * that gets passed the following arguments: [searchTerm, cellValue, row, column].
4757      */
4758     
4759     /**
4760      * @ngdoc property
4761      * @name term
4762      * @propertyOf ui.grid.class:GridOptions.columnDef.filter
4763      * @description If set, the filter field will be pre-populated
4764      * with this value.
4765      */
4766
4767     /**
4768      * @ngdoc property
4769      * @name placeholder
4770      * @propertyOf ui.grid.class:GridOptions.columnDef.filter
4771      * @description String that will be set to the <input>.placeholder attribute.
4772      */
4773
4774     /*
4775
4776       self.filters = [
4777         {
4778           term: 'search term'
4779           condition: uiGridConstants.filter.CONTAINS,
4780           placeholder: 'my placeholder',
4781           flags: {
4782             caseSensitive: true
4783           }
4784         }
4785       ]
4786
4787     */
4788
4789     self.setPropertyOrDefault(colDef, 'filter');
4790     self.setPropertyOrDefault(colDef, 'filters', defaultFilters);
4791   };
4792
4793
4794
4795
4796     /**
4797      * @ngdoc function
4798      * @name getColClass
4799      * @methodOf ui.grid.class:GridColumn
4800      * @description Returns the class name for the column
4801      * @param {bool} prefixDot  if true, will return .className instead of className
4802      */
4803     GridColumn.prototype.getColClass = function (prefixDot) {
4804       var cls = uiGridConstants.COL_CLASS_PREFIX + this.index;
4805
4806       return prefixDot ? '.' + cls : cls;
4807     };
4808
4809     /**
4810      * @ngdoc function
4811      * @name getColClassDefinition
4812      * @methodOf ui.grid.class:GridColumn
4813      * @description Returns the class definition for th column
4814      */
4815     GridColumn.prototype.getColClassDefinition = function () {
4816       return ' .grid' + this.grid.id + ' ' + this.getColClass(true) + ' { width: ' + this.drawnWidth + 'px; }';
4817     };
4818
4819     /**
4820      * @ngdoc function
4821      * @name getRenderContainer
4822      * @methodOf ui.grid.class:GridColumn
4823      * @description Returns the render container object that this column belongs to.
4824      *
4825      * Columns will be default be in the `body` render container if they aren't allocated to one specifically.
4826      */
4827     GridColumn.prototype.getRenderContainer = function getRenderContainer() {
4828       var self = this;
4829
4830       var containerId = self.renderContainer;
4831
4832       if (containerId === null || containerId === '' || containerId === undefined) {
4833         containerId = 'body';
4834       }
4835
4836       return self.grid.renderContainers[containerId];
4837     };
4838
4839     /**
4840      * @ngdoc function
4841      * @name showColumn
4842      * @methodOf ui.grid.class:GridColumn
4843      * @description Makes the column visible by setting colDef.visible = true
4844      */
4845     GridColumn.prototype.showColumn = function() {
4846         this.colDef.visible = true;
4847     };
4848
4849     /**
4850      * @ngdoc function
4851      * @name hideColumn
4852      * @methodOf ui.grid.class:GridColumn
4853      * @description Hides the column by setting colDef.visible = false
4854      */
4855     GridColumn.prototype.hideColumn = function() {
4856         this.colDef.visible = false;
4857     };
4858
4859     /**
4860      * @ngdoc function
4861      * @name getAggregationValue
4862      * @methodOf ui.grid.class:GridColumn
4863      * @description gets the aggregation value based on the aggregation type for this column
4864      */
4865     GridColumn.prototype.getAggregationValue = function () {
4866       var self = this;
4867       var result = 0;
4868       var visibleRows = self.grid.getVisibleRows();
4869       var cellValues = [];
4870       angular.forEach(visibleRows, function (row) {
4871         var cellValue = self.grid.getCellValue(row, self);
4872         if (angular.isNumber(cellValue)) {
4873           cellValues.push(cellValue);
4874         }
4875       });
4876       if (angular.isFunction(self.aggregationType)) {
4877         return self.aggregationType(visibleRows, self);
4878       }
4879       else if (self.aggregationType === uiGridConstants.aggregationTypes.count) {
4880         //TODO: change to i18n
4881         return 'total rows: ' + self.grid.getVisibleRowCount();
4882       }
4883       else if (self.aggregationType === uiGridConstants.aggregationTypes.sum) {
4884         angular.forEach(cellValues, function (value) {
4885           result += value;
4886         });
4887         //TODO: change to i18n
4888         return 'total: ' + result;
4889       }
4890       else if (self.aggregationType === uiGridConstants.aggregationTypes.avg) {
4891         angular.forEach(cellValues, function (value) {
4892           result += value;
4893         });
4894         result = result / cellValues.length;
4895         //TODO: change to i18n
4896         return 'avg: ' + result;
4897       }
4898       else if (self.aggregationType === uiGridConstants.aggregationTypes.min) {
4899         return 'min: ' + Math.min.apply(null, cellValues);
4900       }
4901       else if (self.aggregationType === uiGridConstants.aggregationTypes.max) {
4902         return 'max: ' + Math.max.apply(null, cellValues);
4903       }
4904       else {
4905         return null;
4906       }
4907     };
4908
4909     return GridColumn;
4910   }]);
4911
4912 })();
4913
4914   (function(){
4915
4916 angular.module('ui.grid')
4917 .factory('GridOptions', ['gridUtil', function(gridUtil) {
4918
4919   /**
4920    * @ngdoc function
4921    * @name ui.grid.class:GridOptions
4922    * @description Default GridOptions class.  GridOptions are defined by the application developer and overlaid
4923    * over this object.  Setting gridOptions within your controller is the most common method for an application 
4924    * developer to configure the behaviour of their ui-grid
4925    * 
4926    * @example To define your gridOptions within your controller:
4927    * <pre>$scope.gridOptions = {
4928    *   data: $scope.myData,
4929    *   columnDefs: [ 
4930    *     { name: 'field1', displayName: 'pretty display name' },
4931    *     { name: 'field2', visible: false }
4932    *  ]
4933    * };</pre>
4934    * 
4935    * You can then use this within your html template, when you define your grid:
4936    * <pre>&lt;div ui-grid="gridOptions"&gt;&lt;/div&gt;</pre>
4937    *
4938    * To provide default options for all of the grids within your application, use an angular
4939    * decorator to modify the GridOptions factory.
4940    * <pre>app.config(function($provide){
4941    *    $provide.decorator('GridOptions',function($delegate){
4942    *      return function(){
4943    *        var defaultOptions = new $delegate();
4944    *        defaultOptions.excludeProperties = ['id' ,'$$hashKey'];
4945    *        return defaultOptions;
4946    *      };
4947    *    })
4948    *  })</pre>
4949    */
4950   function GridOptions() {
4951
4952     this.onRegisterApi = angular.noop();
4953
4954     /**
4955      * @ngdoc object
4956      * @name data
4957      * @propertyOf ui.grid.class:GridOptions
4958      * @description (mandatory) Array of data to be rendered into the grid, providing the data source or data binding for 
4959      * the grid.  The most common case is an array of objects, where each object has a number of attributes.
4960      * Each attribute automatically becomes a column in your grid.  This array could, for example, be sourced from
4961      * an angularJS $resource query request.  The array can also contain complex objects.
4962      * 
4963      */
4964     this.data = [];
4965
4966     /**
4967      * @ngdoc array
4968      * @name columnDefs
4969      * @propertyOf  ui.grid.class:GridOptions
4970      * @description Array of columnDef objects.  Only required property is name.
4971      * The individual options available in columnDefs are documented in the
4972      * {@link ui.grid.class:GridOptions.columnDef columnDef} section
4973      * </br>_field property can be used in place of name for backwards compatibility with 2.x_
4974      *  @example
4975      *
4976      * <pre>var columnDefs = [{name:'field1'}, {name:'field2'}];</pre>
4977      *
4978      */
4979     this.columnDefs = [];
4980
4981     /**
4982      * @ngdoc object
4983      * @name ui.grid.class:GridOptions.columnDef
4984      * @description Definition / configuration of an individual column, which would typically be
4985      * one of many column definitions within the gridOptions.columnDefs array
4986      * @example
4987      * <pre>{name:'field1', field: 'field1', filter: { term: 'xxx' }}</pre>
4988      *
4989      */
4990
4991         
4992     /**
4993      * @ngdoc array
4994      * @name excludeProperties
4995      * @propertyOf  ui.grid.class:GridOptions
4996      * @description Array of property names in data to ignore when auto-generating column names.  Provides the
4997      * inverse of columnDefs - columnDefs is a list of columns to include, excludeProperties is a list of columns
4998      * to exclude. 
4999      * 
5000      * If columnDefs is defined, this will be ignored.
5001      * 
5002      * Defaults to ['$$hashKey']
5003      */
5004     
5005     this.excludeProperties = ['$$hashKey'];
5006
5007     /**
5008      * @ngdoc boolean
5009      * @name enableRowHashing
5010      * @propertyOf ui.grid.class:GridOptions
5011      * @description True by default. When enabled, this setting allows uiGrid to add
5012      * `$$hashKey`-type properties (similar to Angular) to elements in the `data` array. This allows
5013      * the grid to maintain state while vastly speeding up the process of altering `data` by adding/moving/removing rows.
5014      * 
5015      * Note that this DOES add properties to your data that you may not want, but they are stripped out when using `angular.toJson()`. IF
5016      * you do not want this at all you can disable this setting but you will take a performance hit if you are using large numbers of rows
5017      * and are altering the data set often.
5018      */
5019     this.enableRowHashing = true;
5020
5021     /**
5022      * @ngdoc function
5023      * @name rowIdentity
5024      * @methodOf ui.grid.class:GridOptions
5025      * @description This function is used to get and, if necessary, set the value uniquely identifying this row.
5026      * 
5027      * By default it returns the `$$hashKey` property if it exists. If it doesn't it uses gridUtil.nextUid() to generate one
5028      */
5029     this.rowIdentity = function rowIdentity(row) {
5030         return gridUtil.hashKey(row);
5031     };
5032
5033     /**
5034      * @ngdoc function
5035      * @name getRowIdentity
5036      * @methodOf ui.grid.class:GridOptions
5037      * @description This function returns the identity value uniquely identifying this row.
5038      * 
5039      * By default it returns the `$$hashKey` property but can be overridden to use any property or set of properties you want.
5040      */
5041     this.getRowIdentity = function getRowIdentity(row) {
5042         return row.$$hashKey;
5043     };
5044
5045     this.headerRowHeight = 30;
5046     this.rowHeight = 30;
5047     this.maxVisibleRowCount = 200;
5048
5049     /**
5050      * @ngdoc integer
5051      * @name minRowsToShow
5052      * @propertyOf ui.grid.class:GridOptions
5053      * @description Minimum number of rows to show when the grid doesn't have a defined height. Defaults to "10".
5054      */
5055     this.minRowsToShow = 10;
5056
5057     this.showFooter = false;
5058     this.footerRowHeight = 30;
5059
5060     this.columnWidth = 50;
5061     this.maxVisibleColumnCount = 200;
5062
5063     // Turn virtualization on when number of data elements goes over this number
5064     this.virtualizationThreshold = 20;
5065
5066     this.columnVirtualizationThreshold = 10;
5067
5068     // Extra rows to to render outside of the viewport
5069     this.excessRows = 4;
5070     this.scrollThreshold = 4;
5071
5072     // Extra columns to to render outside of the viewport
5073     this.excessColumns = 4;
5074     this.horizontalScrollThreshold = 2;
5075
5076     /**
5077      * @ngdoc boolean
5078      * @name enableSorting
5079      * @propertyOf ui.grid.class:GridOptions
5080      * @description True by default. When enabled, this setting adds sort
5081      * widgets to the column headers, allowing sorting of the data for the entire grid.
5082      * Sorting can then be disabled on individual columns using the columnDefs.
5083      */
5084     this.enableSorting = true;
5085
5086     /**
5087      * @ngdoc boolean
5088      * @name enableFiltering
5089      * @propertyOf ui.grid.class:GridOptions
5090      * @description False by default. When enabled, this setting adds filter 
5091      * boxes to each column header, allowing filtering within the column for the entire grid.
5092      * Filtering can then be disabled on individual columns using the columnDefs. 
5093      */
5094     this.enableFiltering = false;
5095
5096     /**
5097      * @ngdoc boolean
5098      * @name enableColumnMenu
5099      * @propertyOf ui.grid.class:GridOptions
5100      * @description True by default. When enabled, this setting displays a column
5101      * menu within each column.
5102      */
5103     this.enableColumnMenu = true;
5104
5105     /**
5106      * @ngdoc boolean
5107      * @name enableScrollbars
5108      * @propertyOf ui.grid.class:GridOptions
5109      * @description True by default. When enabled, this settings enable vertical
5110      * and horizontal scrollbar for grid.
5111      */
5112     this.enableScrollbars = true;
5113
5114     // Columns can't be smaller than 10 pixels
5115     this.minimumColumnSize = 10;
5116
5117     /**
5118      * @ngdoc function
5119      * @name rowEquality
5120      * @methodOf ui.grid.class:GridOptions
5121      * @description By default, rows are compared using object equality.  This option can be overridden
5122      * to compare on any data item property or function
5123      * @param {object} entityA First Data Item to compare
5124      * @param {object} entityB Second Data Item to compare
5125      */
5126     this.rowEquality = function(entityA, entityB) {
5127       return entityA === entityB;
5128     };
5129
5130     /**
5131      * @ngdoc string
5132      * @name headerTemplate
5133      * @propertyOf ui.grid.class:GridOptions
5134      * @description Null by default. When provided, this setting uses a custom header
5135      * template, rather than the default template. Can be set to either the name of a template file:
5136      * <pre>  $scope.gridOptions.headerTemplate = 'header_template.html';</pre>
5137      * inline html 
5138      * <pre>  $scope.gridOptions.headerTemplate = '<div class="ui-grid-top-panel" style="text-align: center">I am a Custom Grid Header</div>'</pre>
5139      * or the id of a precompiled template (TBD how to use this).  
5140      * </br>Refer to the custom header tutorial for more information.
5141      * If you want no header at all, you can set to an empty div:
5142      * <pre>  $scope.gridOptions.headerTemplate = '<div></div>';</pre>
5143      * 
5144      * If you want to only have a static header, then you can set to static content.  If
5145      * you want to tailor the existing column headers, then you should look at the
5146      * current 'ui-grid-header.html' template in github as your starting point.
5147      * 
5148      */
5149     this.headerTemplate = null;
5150
5151     /**
5152      * @ngdoc string
5153      * @name footerTemplate
5154      * @propertyOf ui.grid.class:GridOptions
5155      * @description (optional) Null by default. When provided, this setting uses a custom footer
5156      * template. Can be set to either the name of a template file 'footer_template.html', inline html
5157      * <pre>'<div class="ui-grid-bottom-panel" style="text-align: center">I am a Custom Grid Footer</div>'</pre>, or the id
5158      * of a precompiled template (TBD how to use this).  Refer to the custom footer tutorial for more information.
5159      */
5160     this.footerTemplate = null;
5161
5162     /**
5163      * @ngdoc string
5164      * @name rowTemplate
5165      * @propertyOf ui.grid.class:GridOptions
5166      * @description 'ui-grid/ui-grid-row' by default. When provided, this setting uses a 
5167      * custom row template.  Can be set to either the name of a template file:
5168      * <pre> $scope.gridOptions.rowTemplate = 'row_template.html';</pre>
5169      * inline html 
5170      * <pre>  $scope.gridOptions.rowTemplate = '<div style="background-color: aquamarine" ng-click="getExternalScopes().fnOne(row)" ng-repeat="col in colContainer.renderedColumns track by col.colDef.name" class="ui-grid-cell" ui-grid-cell></div>';</pre>
5171      * or the id of a precompiled template (TBD how to use this) can be provided.  
5172      * </br>Refer to the custom row template tutorial for more information.
5173      */
5174     this.rowTemplate = 'ui-grid/ui-grid-row';
5175   }
5176
5177   return GridOptions;
5178
5179 }]);
5180
5181 })();
5182
5183 (function(){
5184
5185 angular.module('ui.grid')
5186 .factory('GridRenderContainer', ['$log', 'gridUtil', function($log, gridUtil) {
5187   function GridRenderContainer(name, grid, options) {
5188     var self = this;
5189
5190     // if (gridUtil.type(grid) !== 'Grid') {
5191     //   throw new Error('Grid argument is not a Grid object');
5192     // }
5193
5194     self.name = name;
5195
5196     self.grid = grid;
5197     
5198     // self.rowCache = [];
5199     // self.columnCache = [];
5200
5201     self.visibleRowCache = [];
5202     self.visibleColumnCache = [];
5203
5204     self.renderedRows = [];
5205     self.renderedColumns = [];
5206
5207     self.prevScrollTop = 0;
5208     self.prevScrolltopPercentage = 0;
5209     self.prevRowScrollIndex = 0;
5210
5211     self.prevScrollLeft = 0;
5212     self.prevScrollleftPercentage = 0;
5213     self.prevColumnScrollIndex = 0;
5214
5215     self.columnStyles = "";
5216
5217     self.viewportAdjusters = [];
5218
5219     if (options && angular.isObject(options)) {
5220       angular.extend(self, options);
5221     }
5222
5223     grid.registerStyleComputation({
5224       priority: 5,
5225       func: function () {
5226         return self.columnStyles;
5227       }
5228     });
5229   }
5230
5231   // GridRenderContainer.prototype.addRenderable = function addRenderable(renderable) {
5232   //   this.renderables.push(renderable);
5233   // };
5234
5235   GridRenderContainer.prototype.reset = function reset() {
5236     // this.rowCache.length = 0;
5237     // this.columnCache.length = 0;
5238
5239     this.visibleColumnCache.length = 0;
5240     this.visibleRowCache.length = 0;
5241
5242     this.renderedRows.length = 0;
5243     this.renderedColumns.length = 0;
5244   };
5245
5246   // TODO(c0bra): calculate size?? Should this be in a stackable directive?
5247
5248   GridRenderContainer.prototype.minRowsToRender = function minRowsToRender() {
5249     var self = this;
5250     var minRows = 0;
5251     var rowAddedHeight = 0;
5252     var viewPortHeight = self.getViewportHeight();
5253     for (var i = self.visibleRowCache.length - 1; rowAddedHeight < viewPortHeight && i >= 0; i--) {
5254       rowAddedHeight += self.visibleRowCache[i].height;
5255       minRows++;
5256     }
5257     return minRows;
5258   };
5259
5260   GridRenderContainer.prototype.minColumnsToRender = function minColumnsToRender() {
5261     var self = this;
5262     var viewportWidth = this.getViewportWidth();
5263
5264     var min = 0;
5265     var totalWidth = 0;
5266     // self.columns.forEach(function(col, i) {
5267     for (var i = 0; i < self.visibleColumnCache.length; i++) {
5268       var col = self.visibleColumnCache[i];
5269
5270       if (totalWidth < viewportWidth) {
5271         totalWidth += col.drawnWidth;
5272         min++;
5273       }
5274       else {
5275         var currWidth = 0;
5276         for (var j = i; j >= i - min; j--) {
5277           currWidth += self.visibleColumnCache[j].drawnWidth;
5278         }
5279         if (currWidth < viewportWidth) {
5280           min++;
5281         }
5282       }
5283     }
5284
5285     return min;
5286   };
5287
5288   GridRenderContainer.prototype.getVisibleRowCount = function getVisibleRowCount() {
5289     return this.visibleRowCache.length;
5290   };
5291
5292   GridRenderContainer.prototype.registerViewportAdjuster = function registerViewportAdjuster(func) {
5293     this.viewportAdjusters.push(func);
5294   };
5295
5296   GridRenderContainer.prototype.removeViewportAdjuster = function registerViewportAdjuster(func) {
5297     var idx = this.viewportAdjusters.indexOf(func);
5298
5299     if (typeof(idx) !== 'undefined' && idx !== undefined) {
5300       this.viewportAdjusters.splice(idx, 1);
5301     }
5302   };
5303
5304   GridRenderContainer.prototype.getViewportAdjustment = function getViewportAdjustment() {
5305     var self = this;
5306
5307     var adjustment = { height: 0, width: 0 };
5308
5309     self.viewportAdjusters.forEach(function (func) {
5310       adjustment = func.call(this, adjustment);
5311     });
5312
5313     return adjustment;
5314   };
5315
5316   GridRenderContainer.prototype.getViewportHeight = function getViewportHeight() {
5317     var self = this;
5318
5319     var headerHeight = (self.headerHeight) ? self.headerHeight : self.grid.headerHeight;
5320
5321     var viewPortHeight = self.grid.gridHeight - headerHeight - self.grid.footerHeight;
5322
5323     // Account for native horizontal scrollbar, if present
5324     if (typeof(self.horizontalScrollbarHeight) !== 'undefined' && self.horizontalScrollbarHeight !== undefined && self.horizontalScrollbarHeight > 0) {
5325       viewPortHeight = viewPortHeight - self.horizontalScrollbarHeight;
5326     }
5327
5328     var adjustment = self.getViewportAdjustment();
5329     
5330     viewPortHeight = viewPortHeight + adjustment.height;
5331
5332     return viewPortHeight;
5333   };
5334
5335   GridRenderContainer.prototype.getViewportWidth = function getViewportWidth() {
5336     var self = this;
5337
5338     var viewPortWidth = self.grid.gridWidth;
5339
5340     if (typeof(self.grid.verticalScrollbarWidth) !== 'undefined' && self.grid.verticalScrollbarWidth !== undefined && self.grid.verticalScrollbarWidth > 0) {
5341       viewPortWidth = viewPortWidth - self.grid.verticalScrollbarWidth;
5342     }
5343
5344     var adjustment = self.getViewportAdjustment();
5345     
5346     viewPortWidth = viewPortWidth + adjustment.width;
5347
5348     return viewPortWidth;
5349   };
5350
5351   GridRenderContainer.prototype.getHeaderViewportWidth = function getHeaderViewportWidth() {
5352     var self = this;
5353
5354     var viewPortWidth = this.getViewportWidth();
5355
5356     if (typeof(self.grid.verticalScrollbarWidth) !== 'undefined' && self.grid.verticalScrollbarWidth !== undefined && self.grid.verticalScrollbarWidth > 0) {
5357       viewPortWidth = viewPortWidth + self.grid.verticalScrollbarWidth;
5358     }
5359
5360     // var adjustment = self.getViewportAdjustment();
5361     // viewPortWidth = viewPortWidth + adjustment.width;
5362
5363     return viewPortWidth;
5364   };
5365
5366   GridRenderContainer.prototype.getCanvasHeight = function getCanvasHeight() {
5367     var self = this;
5368
5369     var ret =  0;
5370
5371     self.visibleRowCache.forEach(function(row){
5372       ret += row.height;
5373     });
5374
5375     if (typeof(self.grid.horizontalScrollbarHeight) !== 'undefined' && self.grid.horizontalScrollbarHeight !== undefined && self.grid.horizontalScrollbarHeight > 0) {
5376       ret = ret - self.grid.horizontalScrollbarHeight;
5377     }
5378
5379     return ret;
5380   };
5381
5382   GridRenderContainer.prototype.getCanvasWidth = function getCanvasWidth() {
5383     var self = this;
5384
5385     var ret = self.canvasWidth;
5386
5387     if (typeof(self.verticalScrollbarWidth) !== 'undefined' && self.verticalScrollbarWidth !== undefined && self.verticalScrollbarWidth > 0) {
5388       ret = ret - self.verticalScrollbarWidth;
5389     }
5390
5391     return ret;
5392   };
5393
5394   GridRenderContainer.prototype.setRenderedRows = function setRenderedRows(newRows) {
5395     this.renderedRows.length = newRows.length;
5396     for (var i = 0; i < newRows.length; i++) {
5397       this.renderedRows[i] = newRows[i];
5398     }
5399   };
5400
5401   GridRenderContainer.prototype.setRenderedColumns = function setRenderedColumns(newColumns) {
5402     var self = this;
5403
5404     // OLD:
5405     this.renderedColumns.length = newColumns.length;
5406     for (var i = 0; i < newColumns.length; i++) {
5407       this.renderedColumns[i] = newColumns[i];
5408     }
5409     
5410     this.updateColumnOffset();
5411   };
5412
5413   GridRenderContainer.prototype.updateColumnOffset = function updateColumnOffset() {
5414     // Calculate the width of the columns on the left side that are no longer rendered.
5415     //  That will be the offset for the columns as we scroll horizontally.
5416     var hiddenColumnsWidth = 0;
5417     for (var i = 0; i < this.currentFirstColumn; i++) {
5418       hiddenColumnsWidth += this.visibleColumnCache[i].drawnWidth;
5419     }
5420
5421     this.columnOffset = hiddenColumnsWidth;
5422   };
5423
5424   GridRenderContainer.prototype.adjustScrollVertical = function adjustScrollVertical(scrollTop, scrollPercentage, force) {
5425     if (this.prevScrollTop === scrollTop && !force) {
5426       return;
5427     }
5428
5429     scrollTop = this.getCanvasHeight() * scrollPercentage;
5430
5431     this.adjustRows(scrollTop, scrollPercentage);
5432
5433     this.prevScrollTop = scrollTop;
5434     this.prevScrolltopPercentage = scrollPercentage;
5435
5436     this.grid.queueRefresh();
5437   };
5438
5439   GridRenderContainer.prototype.adjustScrollHorizontal = function adjustScrollHorizontal(scrollLeft, scrollPercentage, force) {
5440     if (this.prevScrollLeft === scrollLeft && !force) {
5441       return;
5442     }
5443
5444     // scrollLeft = uiGridCtrl.canvas[0].scrollWidth * scrollPercentage;
5445     scrollLeft = this.getCanvasWidth() * scrollPercentage;
5446
5447     //$log.debug('scrollPercentage', scrollPercentage);
5448
5449     this.adjustColumns(scrollLeft, scrollPercentage);
5450
5451     this.prevScrollLeft = scrollLeft;
5452     this.prevScrollleftPercentage = scrollPercentage;
5453
5454     this.grid.queueRefresh();
5455   };
5456
5457   GridRenderContainer.prototype.adjustRows = function adjustRows(scrollTop, scrollPercentage) {
5458     var self = this;
5459
5460     var minRows = self.minRowsToRender();
5461
5462     var rowCache = self.visibleRowCache;
5463
5464     var maxRowIndex = rowCache.length - minRows;
5465     self.maxRowIndex = maxRowIndex;
5466
5467     var curRowIndex = self.prevRowScrollIndex;
5468
5469     // Calculate the scroll percentage according to the scrollTop location, if no percentage was provided
5470     if ((typeof(scrollPercentage) === 'undefined' || scrollPercentage === null) && scrollTop) {
5471       scrollPercentage = scrollTop / self.getCanvasHeight();
5472     }
5473     
5474     var rowIndex = Math.ceil(Math.min(maxRowIndex, maxRowIndex * scrollPercentage));
5475
5476     // Define a max row index that we can't scroll past
5477     if (rowIndex > maxRowIndex) {
5478       rowIndex = maxRowIndex;
5479     }
5480     
5481     var newRange = [];
5482     if (rowCache.length > self.grid.options.virtualizationThreshold) {
5483       // Have we hit the threshold going down?
5484       if (self.prevScrollTop < scrollTop && rowIndex < self.prevRowScrollIndex + self.grid.options.scrollThreshold && rowIndex < maxRowIndex) {
5485         return;
5486       }
5487       //Have we hit the threshold going up?
5488       if (self.prevScrollTop > scrollTop && rowIndex > self.prevRowScrollIndex - self.grid.options.scrollThreshold && rowIndex < maxRowIndex) {
5489         return;
5490       }
5491
5492       var rangeStart = Math.max(0, rowIndex - self.grid.options.excessRows);
5493       var rangeEnd = Math.min(rowCache.length, rowIndex + minRows + self.grid.options.excessRows);
5494
5495       newRange = [rangeStart, rangeEnd];
5496     }
5497     else {
5498       var maxLen = self.visibleRowCache.length;
5499       newRange = [0, Math.max(maxLen, minRows + self.grid.options.excessRows)];
5500     }
5501
5502     self.updateViewableRowRange(newRange);
5503
5504     self.prevRowScrollIndex = rowIndex;
5505   };
5506
5507   GridRenderContainer.prototype.adjustColumns = function adjustColumns(scrollLeft, scrollPercentage) {
5508     var self = this;
5509
5510     var minCols = self.minColumnsToRender();
5511
5512     var columnCache = self.visibleColumnCache;
5513     var maxColumnIndex = columnCache.length - minCols;
5514
5515     // Calculate the scroll percentage according to the scrollTop location, if no percentage was provided
5516     if ((typeof(scrollPercentage) === 'undefined' || scrollPercentage === null) && scrollLeft) {
5517       scrollPercentage = scrollLeft / self.getCanvasWidth();
5518     }
5519
5520     var colIndex = Math.ceil(Math.min(maxColumnIndex, maxColumnIndex * scrollPercentage));
5521
5522     // Define a max row index that we can't scroll past
5523     if (colIndex > maxColumnIndex) {
5524       colIndex = maxColumnIndex;
5525     }
5526     
5527     var newRange = [];
5528     if (columnCache.length > self.grid.options.columnVirtualizationThreshold && self.getCanvasWidth() > self.getViewportWidth()) {
5529       // Have we hit the threshold going down?
5530       if (self.prevScrollLeft < scrollLeft && colIndex < self.prevColumnScrollIndex + self.grid.options.horizontalScrollThreshold && colIndex < maxColumnIndex) {
5531         return;
5532       }
5533       //Have we hit the threshold going up?
5534       if (self.prevScrollLeft > scrollLeft && colIndex > self.prevColumnScrollIndex - self.grid.options.horizontalScrollThreshold && colIndex < maxColumnIndex) {
5535         return;
5536       }
5537
5538       var rangeStart = Math.max(0, colIndex - self.grid.options.excessColumns);
5539       var rangeEnd = Math.min(columnCache.length, colIndex + minCols + self.grid.options.excessColumns);
5540
5541       newRange = [rangeStart, rangeEnd];
5542     }
5543     else {
5544       var maxLen = self.visibleColumnCache.length;
5545
5546       newRange = [0, Math.max(maxLen, minCols + self.grid.options.excessColumns)];
5547     }
5548     
5549     self.updateViewableColumnRange(newRange);
5550
5551     self.prevColumnScrollIndex = colIndex;
5552   };
5553
5554   // Method for updating the visible rows
5555   GridRenderContainer.prototype.updateViewableRowRange = function updateViewableRowRange(renderedRange) {
5556     // Slice out the range of rows from the data
5557     // var rowArr = uiGridCtrl.grid.rows.slice(renderedRange[0], renderedRange[1]);
5558     var rowArr = this.visibleRowCache.slice(renderedRange[0], renderedRange[1]);
5559
5560     // Define the top-most rendered row
5561     this.currentTopRow = renderedRange[0];
5562
5563     // TODO(c0bra): make this method!
5564     this.setRenderedRows(rowArr);
5565   };
5566
5567   // Method for updating the visible columns
5568   GridRenderContainer.prototype.updateViewableColumnRange = function updateViewableColumnRange(renderedRange) {
5569     // Slice out the range of rows from the data
5570     // var columnArr = uiGridCtrl.grid.columns.slice(renderedRange[0], renderedRange[1]);
5571     var columnArr = this.visibleColumnCache.slice(renderedRange[0], renderedRange[1]);
5572
5573     // Define the left-most rendered columns
5574     this.currentFirstColumn = renderedRange[0];
5575
5576     this.setRenderedColumns(columnArr);
5577   };
5578
5579   GridRenderContainer.prototype.rowStyle = function (index) {
5580     var self = this;
5581
5582     var styles = {};
5583     
5584     if (index === 0 && self.currentTopRow !== 0) {
5585       // The row offset-top is just the height of the rows above the current top-most row, which are no longer rendered
5586       var hiddenRowWidth = (self.currentTopRow) * self.grid.options.rowHeight;
5587
5588       // return { 'margin-top': hiddenRowWidth + 'px' };
5589       styles['margin-top'] = hiddenRowWidth + 'px';
5590     }
5591
5592     if (self.currentFirstColumn !== 0) {
5593       if (self.grid.isRTL()) {
5594         styles['margin-right'] = self.columnOffset + 'px';
5595       }
5596       else {
5597         styles['margin-left'] = self.columnOffset + 'px';
5598       }
5599     }
5600
5601     return styles;
5602   };
5603
5604   GridRenderContainer.prototype.columnStyle = function (index) {
5605     var self = this;
5606     
5607     if (index === 0 && self.currentFirstColumn !== 0) {
5608       var offset = self.columnOffset;
5609
5610       if (self.grid.isRTL()) {
5611         return { 'margin-right': offset + 'px' };
5612       }
5613       else {
5614         return { 'margin-left': offset + 'px' };
5615       }
5616     }
5617
5618     return null;
5619   };
5620
5621   GridRenderContainer.prototype.updateColumnWidths = function () {
5622     var self = this;
5623
5624     var asterisksArray = [],
5625         percentArray = [],
5626         manualArray = [],
5627         asteriskNum = 0,
5628         totalWidth = 0;
5629
5630     // Get the width of the viewport
5631     var availableWidth = self.getViewportWidth();
5632
5633     if (typeof(self.grid.verticalScrollbarWidth) !== 'undefined' && self.grid.verticalScrollbarWidth !== undefined && self.grid.verticalScrollbarWidth > 0) {
5634       availableWidth = availableWidth + self.grid.verticalScrollbarWidth;
5635     }
5636
5637     // The total number of columns
5638     // var equalWidthColumnCount = columnCount = uiGridCtrl.grid.options.columnDefs.length;
5639     // var equalWidth = availableWidth / equalWidthColumnCount;
5640
5641     // The last column we processed
5642     var lastColumn;
5643
5644     var manualWidthSum = 0;
5645
5646     var canvasWidth = 0;
5647
5648     var ret = '';
5649
5650
5651     // uiGridCtrl.grid.columns.forEach(function(column, i) {
5652
5653     var columnCache = self.visibleColumnCache;
5654
5655     columnCache.forEach(function(column, i) {
5656       // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + i + ' { width: ' + equalWidth + 'px; left: ' + left + 'px; }';
5657       //var colWidth = (typeof(c.width) !== 'undefined' && c.width !== undefined) ? c.width : equalWidth;
5658
5659       // Skip hidden columns
5660       if (!column.visible) { return; }
5661
5662       var colWidth,
5663           isPercent = false;
5664
5665       if (!angular.isNumber(column.width)) {
5666         isPercent = isNaN(column.width) ? gridUtil.endsWith(column.width, "%") : false;
5667       }
5668
5669       if (angular.isString(column.width) && column.width.indexOf('*') !== -1) { //  we need to save it until the end to do the calulations on the remaining width.
5670         asteriskNum = parseInt(asteriskNum + column.width.length, 10);
5671         
5672         asterisksArray.push(column);
5673       }
5674       else if (isPercent) { // If the width is a percentage, save it until the very last.
5675         percentArray.push(column);
5676       }
5677       else if (angular.isNumber(column.width)) {
5678         manualWidthSum = parseInt(manualWidthSum + column.width, 10);
5679         
5680         canvasWidth = parseInt(canvasWidth, 10) + parseInt(column.width, 10);
5681
5682         column.drawnWidth = column.width;
5683
5684         // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + column.width + 'px; }';
5685       }
5686     });
5687
5688     // Get the remaining width (available width subtracted by the manual widths sum)
5689     var remainingWidth = availableWidth - manualWidthSum;
5690
5691     var i, column, colWidth;
5692
5693     if (percentArray.length > 0) {
5694       // Pre-process to make sure they're all within any min/max values
5695       for (i = 0; i < percentArray.length; i++) {
5696         column = percentArray[i];
5697
5698         var percent = parseInt(column.width.replace(/%/g, ''), 10) / 100;
5699
5700         colWidth = parseInt(percent * remainingWidth, 10);
5701
5702         if (column.colDef.minWidth && colWidth < column.colDef.minWidth) {
5703           colWidth = column.colDef.minWidth;
5704
5705           remainingWidth = remainingWidth - colWidth;
5706
5707           canvasWidth += colWidth;
5708           column.drawnWidth = colWidth;
5709
5710           // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
5711
5712           // Remove this element from the percent array so it's not processed below
5713           percentArray.splice(i, 1);
5714         }
5715         else if (column.colDef.maxWidth && colWidth > column.colDef.maxWidth) {
5716           colWidth = column.colDef.maxWidth;
5717
5718           remainingWidth = remainingWidth - colWidth;
5719
5720           canvasWidth += colWidth;
5721           column.drawnWidth = colWidth;
5722
5723           // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
5724
5725           // Remove this element from the percent array so it's not processed below
5726           percentArray.splice(i, 1);
5727         }
5728       }
5729
5730       percentArray.forEach(function(column) {
5731         var percent = parseInt(column.width.replace(/%/g, ''), 10) / 100;
5732         var colWidth = parseInt(percent * remainingWidth, 10);
5733
5734         canvasWidth += colWidth;
5735
5736         column.drawnWidth = colWidth;
5737
5738         // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
5739       });
5740     }
5741
5742     if (asterisksArray.length > 0) {
5743       var asteriskVal = parseInt(remainingWidth / asteriskNum, 10);
5744
5745        // Pre-process to make sure they're all within any min/max values
5746       for (i = 0; i < asterisksArray.length; i++) {
5747         column = asterisksArray[i];
5748
5749         colWidth = parseInt(asteriskVal * column.width.length, 10);
5750
5751         if (column.colDef.minWidth && colWidth < column.colDef.minWidth) {
5752           colWidth = column.colDef.minWidth;
5753
5754           remainingWidth = remainingWidth - colWidth;
5755           asteriskNum--;
5756
5757           canvasWidth += colWidth;
5758           column.drawnWidth = colWidth;
5759
5760           // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
5761
5762           lastColumn = column;
5763
5764           // Remove this element from the percent array so it's not processed below
5765           asterisksArray.splice(i, 1);
5766         }
5767         else if (column.colDef.maxWidth && colWidth > column.colDef.maxWidth) {
5768           colWidth = column.colDef.maxWidth;
5769
5770           remainingWidth = remainingWidth - colWidth;
5771           asteriskNum--;
5772
5773           canvasWidth += colWidth;
5774           column.drawnWidth = colWidth;
5775
5776           // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
5777
5778           // Remove this element from the percent array so it's not processed below
5779           asterisksArray.splice(i, 1);
5780         }
5781       }
5782
5783       // Redo the asterisk value, as we may have removed columns due to width constraints
5784       asteriskVal = parseInt(remainingWidth / asteriskNum, 10);
5785
5786       asterisksArray.forEach(function(column) {
5787         var colWidth = parseInt(asteriskVal * column.width.length, 10);
5788
5789         canvasWidth += colWidth;
5790
5791         column.drawnWidth = colWidth;
5792
5793         // ret = ret + ' .grid' + uiGridCtrl.grid.id + ' .col' + column.index + ' { width: ' + colWidth + 'px; }';
5794       });
5795     }
5796
5797     // If the grid width didn't divide evenly into the column widths and we have pixels left over, dole them out to the columns one by one to make everything fit
5798     var leftoverWidth = availableWidth - parseInt(canvasWidth, 10);
5799
5800     if (leftoverWidth > 0 && canvasWidth > 0 && canvasWidth < availableWidth) {
5801       var variableColumn = false;
5802       // uiGridCtrl.grid.columns.forEach(function(col) {
5803       columnCache.forEach(function(col) {
5804         if (col.width && !angular.isNumber(col.width)) {
5805           variableColumn = true;
5806         }
5807       });
5808
5809       if (variableColumn) {
5810         var remFn = function (column) {
5811           if (leftoverWidth > 0) {
5812             column.drawnWidth = column.drawnWidth + 1;
5813             canvasWidth = canvasWidth + 1;
5814             leftoverWidth--;
5815           }
5816         };
5817         while (leftoverWidth > 0) {
5818           columnCache.forEach(remFn);
5819         }
5820       }
5821     }
5822
5823     if (canvasWidth < availableWidth) {
5824       canvasWidth = availableWidth;
5825     }
5826
5827     // Build the CSS
5828     columnCache.forEach(function (column) {
5829       ret = ret + column.getColClassDefinition();
5830     });
5831
5832     // Add the vertical scrollbar width back in to the canvas width, it's taken out in getCanvasWidth
5833     if (self.grid.verticalScrollbarWidth) {
5834       canvasWidth = canvasWidth + self.grid.verticalScrollbarWidth;
5835     }
5836     // canvasWidth = canvasWidth + 1;
5837
5838     self.canvasWidth = parseInt(canvasWidth, 10);
5839
5840     // Return the styles back to buildStyles which pops them into the `customStyles` scope variable
5841     // return ret;
5842
5843     // Set this render container's column styles so they can be used in style computation
5844     this.columnStyles = ret;
5845   };
5846
5847   return GridRenderContainer;
5848 }]);
5849
5850 })();
5851 (function(){
5852
5853 angular.module('ui.grid')
5854 .factory('GridRow', ['gridUtil', function(gridUtil) {
5855
5856    /**
5857    * @ngdoc function
5858    * @name ui.grid.class:GridRow
5859    * @description GridRow is the viewModel for one logical row on the grid.  A grid Row is not necessarily a one-to-one
5860    * relation to gridOptions.data.
5861    * @param {object} entity the array item from GridOptions.data
5862    * @param {number} index the current position of the row in the array
5863    * @param {Grid} reference to the parent grid
5864    */
5865   function GridRow(entity, index, grid) {
5866
5867      /**
5868       *  @ngdoc object
5869       *  @name grid
5870       *  @propertyOf  ui.grid.class:GridRow
5871       *  @description A reference back to the grid
5872       */
5873      this.grid = grid;
5874
5875      /**
5876       *  @ngdoc object
5877       *  @name entity
5878       *  @propertyOf  ui.grid.class:GridRow
5879       *  @description A reference to an item in gridOptions.data[]
5880       */
5881     this.entity = entity;
5882
5883      /**
5884       *  @ngdoc object
5885       *  @name index
5886       *  @propertyOf  ui.grid.class:GridRow
5887       *  @description the index of the GridRow. It should always be unique and immutable
5888       */
5889     this.index = index;
5890
5891
5892      /**
5893       *  @ngdoc object
5894       *  @name uid
5895       *  @propertyOf  ui.grid.class:GridRow
5896       *  @description  UniqueId of row
5897       */
5898      this.uid = gridUtil.nextUid();
5899
5900      /**
5901       *  @ngdoc object
5902       *  @name visible
5903       *  @propertyOf  ui.grid.class:GridRow
5904       *  @description If true, the row will be rendered
5905       */
5906     // Default to true
5907     this.visible = true;
5908
5909   /**
5910     *  @ngdoc object
5911     *  @name height
5912     *  @propertyOf  ui.grid.class:GridRow
5913     *  @description height of each individual row
5914     */
5915     this.height = grid.options.rowHeight;
5916
5917     /**
5918      * @ngdoc function
5919      * @name setRowInvisible
5920      * @methodOf  ui.grid.core.api:PublicApi
5921      * @description Sets an override on the row to make it always invisible,
5922      * which will override any filtering or other visibility calculations.  
5923      * If the row is currently visible then sets it to invisible and calls
5924      * both grid refresh and emits the rowsVisibleChanged event
5925      * @param {object} rowEntity gridOptions.data[] array instance
5926      */
5927     if (!this.grid.api.core.setRowInvisible){
5928       this.grid.api.registerMethod( 'core', 'setRowInvisible', this.setRowInvisible );
5929     }
5930
5931     /**
5932      * @ngdoc function
5933      * @name clearRowInvisible
5934      * @methodOf  ui.grid.core.api:PublicApi
5935      * @description Clears any override on visibility for the row so that it returns to 
5936      * using normal filtering and other visibility calculations.  
5937      * If the row is currently invisible then sets it to visible and calls
5938      * both grid refresh and emits the rowsVisibleChanged event
5939      * TODO: if a filter is active then we can't just set it to visible?
5940      * @param {object} rowEntity gridOptions.data[] array instance
5941      */
5942     if (!this.grid.api.core.clearRowInvisible){
5943       this.grid.api.registerMethod( 'core', 'clearRowInvisible', this.clearRowInvisible );
5944     }
5945
5946     /**
5947      * @ngdoc function
5948      * @name getVisibleRows
5949      * @methodOf  ui.grid.core.api:PublicApi
5950      * @description Returns all visible rows
5951      * @param {Grid} grid the grid you want to get visible rows from
5952      * @returns {array} an array of gridRow 
5953      */
5954     if (!this.grid.api.core.getVisibleRows){
5955       this.grid.api.registerMethod( 'core', 'getVisibleRows', this.getVisibleRows );
5956     }
5957     
5958     /**
5959      * @ngdoc event
5960      * @name rowsVisibleChanged
5961      * @eventOf  ui.grid.core.api:PublicApi
5962      * @description  is raised after the rows that are visible
5963      * change.  The filtering is zero-based, so it isn't possible
5964      * to say which rows changed (unlike in the selection feature).
5965      * We can plausibly know which row was changed when setRowInvisible
5966      * is called, but in that situation the user already knows which row
5967      * they changed.  When a filter runs we don't know what changed, 
5968      * and that is the one that would have been useful.
5969      * 
5970      */
5971     if (!this.grid.api.core.raise.rowsVisibleChanged){
5972       this.grid.api.registerEvent( 'core', 'rowsVisibleChanged' );
5973     }
5974     
5975   }
5976
5977   /**
5978    * @ngdoc function
5979    * @name getQualifiedColField
5980    * @methodOf ui.grid.class:GridRow
5981    * @description returns the qualified field name as it exists on scope
5982    * ie: row.entity.fieldA
5983    * @param {GridCol} col column instance
5984    * @returns {string} resulting name that can be evaluated on scope
5985    */
5986   GridRow.prototype.getQualifiedColField = function(col) {
5987     return 'row.' + this.getEntityQualifiedColField(col);
5988   };
5989
5990     /**
5991      * @ngdoc function
5992      * @name getEntityQualifiedColField
5993      * @methodOf ui.grid.class:GridRow
5994      * @description returns the qualified field name minus the row path
5995      * ie: entity.fieldA
5996      * @param {GridCol} col column instance
5997      * @returns {string} resulting name that can be evaluated against a row
5998      */
5999   GridRow.prototype.getEntityQualifiedColField = function(col) {
6000     return gridUtil.preEval('entity.' + col.field);
6001   };
6002   
6003   
6004   /**
6005    * @ngdoc function
6006    * @name setRowInvisible
6007    * @methodOf  ui.grid.class:GridRow
6008    * @description Sets an override on the row that forces it to always
6009    * be invisible, and if the row is currently visible then marks it
6010    * as invisible and refreshes the grid.  Emits the rowsVisibleChanged
6011    * event if it changed the row visibility
6012    * @param {GridRow} row row to force invisible, needs to be a GridRow,
6013    * which can be found from your data entity using grid.findRow
6014    */
6015   GridRow.prototype.setRowInvisible = function (row) {
6016     if (row !== null) {
6017       row.forceInvisible = true;
6018       
6019       if ( row.visible ){
6020         row.visible = false;
6021         row.grid.refresh();
6022         row.grid.api.core.raise.rowsVisibleChanged();
6023       }
6024     }        
6025   };
6026
6027   /**
6028    * @ngdoc function
6029    * @name clearRowInvisible
6030    * @methodOf ui.grid.class:GridRow
6031    * @description Clears any override on the row visibility, returning it 
6032    * to normal visibility calculations.  If the row is currently invisible
6033    * then sets it to visible and calls refresh and emits the rowsVisibleChanged
6034    * event
6035    * TODO: if filter in action, then is this right?
6036    * @param {GridRow} row row clear force invisible, needs to be a GridRow,
6037    * which can be found from your data entity using grid.findRow
6038    */
6039   GridRow.prototype.clearRowInvisible = function (row) {
6040     if (row !== null) {
6041       row.forceInvisible = false;
6042       
6043       if ( !row.visible ){
6044         row.visible = true;
6045         row.grid.refresh();
6046         row.grid.api.core.raise.rowsVisibleChanged();
6047       }
6048     }        
6049   };
6050
6051   /**
6052    * @ngdoc function
6053    * @name getVisibleRows
6054    * @methodOf ui.grid.class:GridRow
6055    * @description Returns all the visible rows
6056    * @param {Grid} grid the grid to return rows from
6057    * @returns {array} rows that are currently visible, returns the
6058    * GridRows rather than gridRow.entity
6059    * TODO: should this come from visible row cache instead?
6060    */
6061   GridRow.prototype.getVisibleRows = function ( grid ) {
6062     return grid.rows.filter(function (row) {
6063       return row.visible;
6064     });
6065   };  
6066
6067   return GridRow;
6068 }]);
6069
6070 })();
6071 (function () {
6072   'use strict';
6073   /**
6074    *  @ngdoc object
6075    *  @name ui.grid.service:gridClassFactory
6076    *
6077    *  @description factory to return dom specific instances of a grid
6078    *
6079    */
6080   angular.module('ui.grid').service('gridClassFactory', ['gridUtil', '$q', '$compile', '$templateCache', 'uiGridConstants', '$log', 'Grid', 'GridColumn', 'GridRow',
6081     function (gridUtil, $q, $compile, $templateCache, uiGridConstants, $log, Grid, GridColumn, GridRow) {
6082
6083       var service = {
6084         /**
6085          * @ngdoc method
6086          * @name createGrid
6087          * @methodOf ui.grid.service:gridClassFactory
6088          * @description Creates a new grid instance. Each instance will have a unique id
6089          * @param {object} options An object map of options to pass into the created grid instance.
6090          * @returns {Grid} grid
6091          */
6092         createGrid : function(options) {
6093           options = (typeof(options) !== 'undefined') ? options: {};
6094           options.id = gridUtil.newId();
6095           var grid = new Grid(options);
6096
6097           // NOTE/TODO: rowTemplate should always be defined...
6098           if (grid.options.rowTemplate) {
6099             var rowTemplateFnPromise = $q.defer();
6100             grid.getRowTemplateFn = rowTemplateFnPromise.promise;
6101             
6102             gridUtil.getTemplate(grid.options.rowTemplate)
6103               .then(
6104                 function (template) {
6105                   var rowTemplateFn = $compile(template);
6106                   rowTemplateFnPromise.resolve(rowTemplateFn);
6107                 },
6108                 function (res) {
6109                   // Todo handle response error here?
6110                   throw new Error("Couldn't fetch/use row template '" + grid.options.rowTemplate + "'");
6111                 });
6112           }
6113
6114           grid.registerColumnBuilder(service.defaultColumnBuilder);
6115
6116           // Reset all rows to visible initially
6117           grid.registerRowsProcessor(function allRowsVisible(rows) {
6118             rows.forEach(function (row) {
6119               row.visible = !row.forceInvisible;
6120             });
6121
6122             return rows;
6123           });
6124
6125           grid.registerColumnsProcessor(function allColumnsVisible(columns) {
6126             columns.forEach(function (column) {
6127               column.visible = true;
6128             });
6129
6130             return columns;
6131           });
6132
6133           grid.registerColumnsProcessor(function(renderableColumns) {
6134               renderableColumns.forEach(function (column) {
6135                   if (column.colDef.visible === false) {
6136                       column.visible = false;
6137                   }
6138               });
6139
6140               return renderableColumns;
6141           });
6142
6143
6144
6145           if (grid.options.enableFiltering) {
6146             grid.registerRowsProcessor(grid.searchRows);
6147           }
6148
6149           // Register the default row processor, it sorts rows by selected columns
6150           if (grid.options.externalSort && angular.isFunction(grid.options.externalSort)) {
6151             grid.registerRowsProcessor(grid.options.externalSort);
6152           }
6153           else {
6154             grid.registerRowsProcessor(grid.sortByColumn);
6155           }
6156
6157           return grid;
6158         },
6159
6160         /**
6161          * @ngdoc function
6162          * @name defaultColumnBuilder
6163          * @methodOf ui.grid.service:gridClassFactory
6164          * @description Processes designTime column definitions and applies them to col for the
6165          *              core grid features
6166          * @param {object} colDef reference to column definition
6167          * @param {GridColumn} col reference to gridCol
6168          * @param {object} gridOptions reference to grid options
6169          */
6170         defaultColumnBuilder: function (colDef, col, gridOptions) {
6171
6172           var templateGetPromises = [];
6173
6174           /**
6175            * @ngdoc property
6176            * @name headerCellTemplate
6177            * @propertyOf ui.grid.class:GridOptions.columnDef
6178            * @description a custom template for the header for this column.  The default
6179            * is ui-grid/uiGridHeaderCell
6180            *
6181            */
6182           if (!colDef.headerCellTemplate) {
6183             colDef.headerCellTemplate = 'ui-grid/uiGridHeaderCell';
6184           }
6185
6186           /**
6187            * @ngdoc property
6188            * @name cellTemplate
6189            * @propertyOf ui.grid.class:GridOptions.columnDef
6190            * @description a custom template for each cell in this column.  The default
6191            * is ui-grid/uiGridCell
6192            *
6193            */
6194           if (!colDef.cellTemplate) {
6195             colDef.cellTemplate = 'ui-grid/uiGridCell';
6196           }
6197
6198           templateGetPromises.push(gridUtil.getTemplate(colDef.cellTemplate)
6199             .then(
6200               function (template) {
6201                 col.cellTemplate = template.replace(uiGridConstants.CUSTOM_FILTERS, col.cellFilter ? "|" + col.cellFilter : "");
6202               },
6203               function (res) {
6204                 throw new Error("Couldn't fetch/use colDef.cellTemplate '" + colDef.cellTemplate + "'");
6205               })
6206           );
6207
6208
6209           templateGetPromises.push(gridUtil.getTemplate(colDef.headerCellTemplate)
6210               .then(
6211               function (template) {
6212                 col.headerCellTemplate = template.replace(uiGridConstants.CUSTOM_FILTERS, col.headerCellFilter ? "|" + col.headerCellFilter : "");
6213               },
6214               function (res) {
6215                 throw new Error("Couldn't fetch/use colDef.headerCellTemplate '" + colDef.headerCellTemplate + "'");
6216               })
6217           );
6218
6219           return $q.all(templateGetPromises);
6220         }
6221
6222       };
6223
6224       //class definitions (moved to separate factories)
6225
6226       return service;
6227     }]);
6228
6229 })();
6230 (function() {
6231
6232 var module = angular.module('ui.grid');
6233
6234 function escapeRegExp(str) {
6235   return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
6236 }
6237
6238 function QuickCache() {
6239   var c = function(get, set) {
6240     // Return the cached value of 'get' if it's stored
6241     if (get && c.cache[get]) {
6242       return c.cache[get];
6243     }
6244     // Otherwise set it and return it
6245     else if (get && set) {
6246       c.cache[get] = set;
6247       return c.cache[get];
6248     }
6249     else {
6250       return undefined;
6251     }
6252   };
6253
6254   c.cache = {};
6255
6256   c.clear = function () {
6257     c.cache = {};
6258   };
6259
6260   return c;
6261 }
6262
6263 /**
6264  *  @ngdoc service
6265  *  @name ui.grid.service:rowSearcher
6266  *
6267  *  @description Service for searching/filtering rows based on column value conditions.
6268  */
6269 module.service('rowSearcher', ['$log', 'uiGridConstants', function ($log, uiGridConstants) {
6270   var defaultCondition = uiGridConstants.filter.STARTS_WITH;
6271
6272   var rowSearcher = {};
6273
6274   // rowSearcher.searchColumn = function searchColumn(condition, item) {
6275   //   var result;
6276
6277   //   var col = self.fieldMap[condition.columnDisplay];
6278
6279   //   if (!col) {
6280   //       return false;
6281   //   }
6282   //   var sp = col.cellFilter.split(':');
6283   //   var filter = col.cellFilter ? $filter(sp[0]) : null;
6284   //   var value = item[condition.column] || item[col.field.split('.')[0]];
6285   //   if (value === null || value === undefined) {
6286   //       return false;
6287   //   }
6288   //   if (typeof filter === "function") {
6289   //       var filterResults = filter(typeof value === "object" ? evalObject(value, col.field) : value, sp[1]).toString();
6290   //       result = condition.regex.test(filterResults);
6291   //   }
6292   //   else {
6293   //       result = condition.regex.test(typeof value === "object" ? evalObject(value, col.field).toString() : value.toString());
6294   //   }
6295   //   if (result) {
6296   //       return true;
6297   //   }
6298   //   return false;
6299   // };
6300
6301   /**
6302    * @ngdoc function
6303    * @name getTerm
6304    * @methodOf ui.grid.service:rowSearcher
6305    * @description Get the term from a filter
6306    * Trims leading and trailing whitespace
6307    * @param {object} filter object to use
6308    * @returns {object} Parsed term
6309    */
6310   rowSearcher.getTerm = function getTerm(filter) {
6311     if (typeof(filter.term) === 'undefined') { return filter.term; }
6312     
6313     var term = filter.term;
6314
6315     // Strip leading and trailing whitespace if the term is a string
6316     if (typeof(term) === 'string') {
6317       term = term.trim();
6318     }
6319
6320     return term;
6321   };
6322
6323   /**
6324    * @ngdoc function
6325    * @name stripTerm
6326    * @methodOf ui.grid.service:rowSearcher
6327    * @description Remove leading and trailing asterisk (*) from the filter's term
6328    * @param {object} filter object to use
6329    * @returns {uiGridConstants.filter<int>} Value representing the condition constant value
6330    */
6331   rowSearcher.stripTerm = function stripTerm(filter) {
6332     var term = rowSearcher.getTerm(filter);
6333
6334     if (typeof(term) === 'string') {
6335       return escapeRegExp(term.replace(/(^\*|\*$)/g, ''));
6336     }
6337     else {
6338       return term;
6339     }
6340   };
6341
6342   /**
6343    * @ngdoc function
6344    * @name guessCondition
6345    * @methodOf ui.grid.service:rowSearcher
6346    * @description Guess the condition for a filter based on its term
6347    * <br>
6348    * Defaults to STARTS_WITH. Uses CONTAINS for strings beginning and ending with *s (*bob*).
6349    * Uses STARTS_WITH for strings ending with * (bo*). Uses ENDS_WITH for strings starting with * (*ob).
6350    * @param {object} filter object to use
6351    * @returns {uiGridConstants.filter<int>} Value representing the condition constant value
6352    */
6353   rowSearcher.guessCondition = function guessCondition(filter) {
6354     if (typeof(filter.term) === 'undefined' || !filter.term) {
6355       return defaultCondition;
6356     }
6357
6358     var term = rowSearcher.getTerm(filter);
6359     
6360     // Term starts with and ends with a *, use 'contains' condition
6361     // if (/^\*[\s\S]+?\*$/.test(term)) {
6362     //   return uiGridConstants.filter.CONTAINS;
6363     // }
6364     // // Term starts with a *, use 'ends with' condition
6365     // else if (/^\*/.test(term)) {
6366     //   return uiGridConstants.filter.ENDS_WITH;
6367     // }
6368     // // Term ends with a *, use 'starts with' condition
6369     // else if (/\*$/.test(term)) {
6370     //   return uiGridConstants.filter.STARTS_WITH;
6371     // }
6372     // // Default to default condition
6373     // else {
6374     //   return defaultCondition;
6375     // }
6376
6377     // If the term has *s then turn it into a regex
6378     if (/\*/.test(term)) {
6379       var regexpFlags = '';
6380       if (!filter.flags || !filter.flags.caseSensitive) {
6381         regexpFlags += 'i';
6382       }
6383
6384       var reText = term.replace(/(\\)?\*/g, function ($0, $1) { return $1 ? $0 : '[\\s\\S]*?'; });
6385       return new RegExp('^' + reText + '$', regexpFlags);
6386     }
6387     // Otherwise default to default condition
6388     else {
6389       return defaultCondition;
6390     }
6391   };
6392
6393   rowSearcher.runColumnFilter = function runColumnFilter(grid, row, column, termCache, i, filter) {
6394     // Cache typeof condition
6395     var conditionType = typeof(filter.condition);
6396
6397     // Default to CONTAINS condition
6398     if (conditionType === 'undefined' || !filter.condition) {
6399       filter.condition = uiGridConstants.filter.CONTAINS;
6400     }
6401
6402     // Term to search for.
6403     var term = rowSearcher.stripTerm(filter);
6404
6405     if (term === null || term === undefined || term === '') {
6406       return true;
6407     }
6408
6409     // Get the column value for this row
6410     var value = grid.getCellValue(row, column);
6411
6412     var regexpFlags = '';
6413     if (!filter.flags || !filter.flags.caseSensitive) {
6414       regexpFlags += 'i';
6415     }
6416
6417     var cacheId = column.field + i;
6418
6419     // If the filter's condition is a RegExp, then use it
6420     if (filter.condition instanceof RegExp) {
6421       if (!filter.condition.test(value)) {
6422         return false;
6423       }
6424     }
6425     // If the filter's condition is a function, run it
6426     else if (conditionType === 'function') {
6427       return filter.condition(term, value, row, column);
6428     }
6429     else if (filter.condition === uiGridConstants.filter.STARTS_WITH) {
6430       var startswithRE = termCache(cacheId) ? termCache(cacheId) : termCache(cacheId, new RegExp('^' + term, regexpFlags));
6431
6432       if (!startswithRE.test(value)) {
6433         return false;
6434       }
6435     }
6436     else if (filter.condition === uiGridConstants.filter.ENDS_WITH) {
6437       var endswithRE = termCache(cacheId) ? termCache(cacheId) : termCache(cacheId, new RegExp(term + '$', regexpFlags));
6438
6439       if (!endswithRE.test(value)) {
6440         return false;
6441       }
6442     }
6443     else if (filter.condition === uiGridConstants.filter.CONTAINS) {
6444       var containsRE = termCache(cacheId) ? termCache(cacheId) : termCache(cacheId, new RegExp(term, regexpFlags));
6445
6446       if (!containsRE.test(value)) {
6447         return false;
6448       }
6449     }
6450     else if (filter.condition === uiGridConstants.filter.EXACT) {
6451       var exactRE = termCache(cacheId) ? termCache(cacheId) : termCache(cacheId,  new RegExp('^' + term + '$', regexpFlags));
6452
6453       if (!exactRE.test(value)) {
6454         return false;
6455       }
6456     }
6457     else if (filter.condition === uiGridConstants.filter.GREATER_THAN) {
6458       if (value <= term) {
6459         return false;
6460       }
6461     }
6462     else if (filter.condition === uiGridConstants.filter.GREATER_THAN_OR_EQUAL) {
6463       if (value < term) {
6464         return false;
6465       }
6466     }
6467     else if (filter.condition === uiGridConstants.filter.LESS_THAN) {
6468       if (value >= term) {
6469         return false;
6470       }
6471     }
6472     else if (filter.condition === uiGridConstants.filter.LESS_THAN_OR_EQUAL) {
6473       if (value > term) {
6474         return false;
6475       }
6476     }
6477     else if (filter.condition === uiGridConstants.filter.NOT_EQUAL) {
6478       if (!angular.equals(value, term)) {
6479         return false;
6480       }
6481     }
6482
6483     return true;
6484   };
6485
6486   /**
6487    * @ngdoc boolean
6488    * @name useExternalFiltering
6489    * @propertyOf ui.grid.class:GridOptions
6490    * @description False by default. When enabled, this setting suppresses the internal filtering.
6491    * All UI logic will still operate, allowing filter conditions to be set and modified.
6492    * 
6493    * The external filter logic can listen for the `filterChange` event, which fires whenever
6494    * a filter has been adjusted.
6495    */
6496   /**
6497    * @ngdoc function
6498    * @name searchColumn
6499    * @methodOf ui.grid.service:rowSearcher
6500    * @description Process filters on a given column against a given row. If the row meets the conditions on all the filters, return true.
6501    * @param {Grid} grid Grid to search in
6502    * @param {GridRow} row Row to search on
6503    * @param {GridCol} column Column with the filters to use
6504    * @returns {boolean} Whether the column matches or not.
6505    */
6506   rowSearcher.searchColumn = function searchColumn(grid, row, column, termCache) {
6507     var filters = [];
6508
6509     if (grid.options.useExternalFiltering) {
6510       return true;
6511     }
6512     
6513     if (typeof(column.filters) !== 'undefined' && column.filters && column.filters.length > 0) {
6514       filters = column.filters;
6515     } else {
6516       // If filters array is not there, assume no filters for this column. 
6517       // This array should have been built in GridColumn::updateColumnDef.
6518       return true;
6519     }
6520     
6521     for (var i in filters) {
6522       var filter = filters[i];
6523
6524       /*
6525         filter: {
6526           term: 'blah', // Search term to search for, could be a string, integer, etc.
6527           condition: uiGridConstants.filter.CONTAINS // Type of match to do. Defaults to CONTAINS (i.e. looking in a string), but could be EXACT, GREATER_THAN, etc.
6528           flags: { // Flags for the conditions
6529             caseSensitive: false // Case-sensitivity defaults to false
6530           }
6531         }
6532       */
6533      
6534       // Check for when no condition is supplied. In this case, guess the condition
6535       // to use based on the filter's term. Cache this result.
6536       if (!filter.condition) {
6537         // Cache custom conditions, building the RegExp takes time
6538         var conditionCacheId = 'cond-' + column.field + '-' + filter.term;
6539         var condition = termCache(conditionCacheId) ? termCache(conditionCacheId) : termCache(conditionCacheId, rowSearcher.guessCondition(filter));
6540
6541         // Create a surrogate filter so as not to change
6542         // the actual columnDef.filters.
6543         filter = {
6544           // Copy over the search term
6545           term: filter.term,
6546           // Use the guessed condition
6547           condition: condition,
6548           // Set flags, using passed flags if present
6549           flags: angular.extend({
6550             caseSensitive: false
6551           }, filter.flags)
6552         };
6553       }
6554
6555       var ret = rowSearcher.runColumnFilter(grid, row, column, termCache, i, filter);
6556       if (!ret) {
6557         return false;
6558       }
6559     }
6560
6561     return true;
6562     // }
6563     // else {
6564     //   // No filter conditions, default to true
6565     //   return true;
6566     // }
6567   };
6568
6569   /**
6570    * @ngdoc function
6571    * @name search
6572    * @methodOf ui.grid.service:rowSearcher
6573    * @description Run a search across
6574    * @param {Grid} grid Grid instance to search inside
6575    * @param {Array[GridRow]} rows GridRows to filter
6576    * @param {Array[GridColumn]} columns GridColumns with filters to process
6577    */
6578   rowSearcher.search = function search(grid, rows, columns) {
6579     // Don't do anything if we weren't passed any rows
6580     if (!rows) {
6581       return;
6582     }
6583
6584     // Create a term cache
6585     var termCache = new QuickCache();
6586
6587     // Build filtered column list
6588     var filterCols = [];
6589     columns.forEach(function (col) {
6590       if (typeof(col.filters) !== 'undefined' && col.filters.length > 0) {
6591         filterCols.push(col);
6592       }
6593       else if (typeof(col.filter) !== 'undefined' && col.filter && typeof(col.filter.term) !== 'undefined' && col.filter.term) {
6594         filterCols.push(col);
6595       }
6596     });
6597     
6598     if (filterCols.length > 0) {
6599       filterCols.forEach(function foreachFilterCol(col) {
6600         rows.forEach(function foreachRow(row) {
6601           if (row.forceInvisible || !rowSearcher.searchColumn(grid, row, col, termCache)) {
6602             row.visible = false;
6603           }
6604         });
6605       });
6606       
6607       grid.api.core.raise.rowsVisibleChanged();
6608
6609       // rows.forEach(function (row) {
6610       //   var matchesAllColumns = true;
6611
6612       //   for (var i in filterCols) {
6613       //     var col = filterCols[i];
6614
6615       //     if (!rowSearcher.searchColumn(grid, row, col, termCache)) {
6616       //       matchesAllColumns = false;
6617
6618       //       // Stop processing other terms
6619       //       break;
6620       //     }
6621       //   }
6622
6623       //   // Row doesn't match all the terms, don't display it
6624       //   if (!matchesAllColumns) {
6625       //     row.visible = false;
6626       //   }
6627       //   else {
6628       //     row.visible = true;
6629       //   }
6630       // });
6631     }
6632
6633     // Reset the term cache
6634     termCache.clear();
6635
6636     return rows;
6637   };
6638
6639   return rowSearcher;
6640 }]);
6641
6642 })();
6643 (function() {
6644
6645 var module = angular.module('ui.grid');
6646
6647 /**
6648  * @ngdoc object
6649  * @name ui.grid.class:RowSorter
6650  * @description RowSorter provides the default sorting mechanisms, 
6651  * including guessing column types and applying appropriate sort 
6652  * algorithms
6653  * 
6654  */ 
6655
6656 module.service('rowSorter', ['$parse', 'uiGridConstants', function ($parse, uiGridConstants) {
6657   var currencyRegexStr = 
6658     '(' +
6659     uiGridConstants.CURRENCY_SYMBOLS
6660       .map(function (a) { return '\\' + a; }) // Escape all the currency symbols ($ at least will jack up this regex)
6661       .join('|') + // Join all the symbols together with |s
6662     ')?';
6663
6664   // /^[-+]?[£$¤¥]?[\d,.]+%?$/
6665   var numberStrRegex = new RegExp('^[-+]?' + currencyRegexStr + '[\\d,.]+' + currencyRegexStr + '%?$');
6666
6667   var rowSorter = {
6668     // Cache of sorting functions. Once we create them, we don't want to keep re-doing it
6669     //   this takes a piece of data from the cell and tries to determine its type and what sorting
6670     //   function to use for it
6671     colSortFnCache: []
6672   };
6673
6674   // Guess which sort function to use on this item
6675   rowSorter.guessSortFn = function guessSortFn(itemType) {
6676
6677     // Check for numbers and booleans
6678     switch (itemType) {
6679       case "number":
6680         return rowSorter.sortNumber;
6681       case "boolean":
6682         return rowSorter.sortBool;
6683       case "string":
6684         return rowSorter.sortAlpha;
6685       case "date":
6686         return rowSorter.sortDate;
6687       case "object":
6688         return rowSorter.basicSort;
6689       default:
6690         throw new Error('No sorting function found for type:' + itemType);
6691     }
6692   };
6693
6694   // Basic sorting function
6695   rowSorter.basicSort = function basicSort(a, b) {
6696       if (a === b) {
6697           return 0;
6698       }
6699       if (a < b) {
6700           return -1;
6701       }
6702       return 1;
6703   };
6704
6705   // Number sorting function
6706   rowSorter.sortNumber = function sortNumber(a, b) {
6707       return a - b;
6708   };
6709
6710   rowSorter.sortNumberStr = function sortNumberStr(a, b) {
6711     var numA, // The parsed number form of 'a'
6712         numB, // The parsed number form of 'b'
6713         badA = false,
6714         badB = false;
6715
6716     // Try to parse 'a' to a float
6717     numA = parseFloat(a.replace(/[^0-9.-]/g, ''));
6718
6719     // If 'a' couldn't be parsed to float, flag it as bad
6720     if (isNaN(numA)) {
6721         badA = true;
6722     }
6723
6724     // Try to parse 'b' to a float
6725     numB = parseFloat(b.replace(/[^0-9.-]/g, ''));
6726
6727     // If 'b' couldn't be parsed to float, flag it as bad
6728     if (isNaN(numB)) {
6729         badB = true;
6730     }
6731
6732     // We want bad ones to get pushed to the bottom... which effectively is "greater than"
6733     if (badA && badB) {
6734         return 0;
6735     }
6736
6737     if (badA) {
6738         return 1;
6739     }
6740
6741     if (badB) {
6742         return -1;
6743     }
6744
6745     return numA - numB;
6746   };
6747
6748   // String sorting function
6749   rowSorter.sortAlpha = function sortAlpha(a, b) {
6750     var strA = a.toLowerCase(),
6751         strB = b.toLowerCase();
6752
6753     return strA === strB ? 0 : (strA < strB ? -1 : 1);
6754   };
6755
6756   // Date sorting function
6757   rowSorter.sortDate = function sortDate(a, b) {
6758     var timeA = a.getTime(),
6759         timeB = b.getTime();
6760
6761     return timeA === timeB ? 0 : (timeA < timeB ? -1 : 1);
6762   };
6763
6764   // Boolean sorting function
6765   rowSorter.sortBool = function sortBool(a, b) {
6766     if (a && b) {
6767       return 0;
6768     }
6769
6770     if (!a && !b) {
6771       return 0;
6772     }
6773     else {
6774       return a ? 1 : -1;
6775     }
6776   };
6777
6778   rowSorter.getSortFn = function getSortFn(grid, col, rows) {
6779     var sortFn, item;
6780
6781     // See if we already figured out what to use to sort the column and have it in the cache
6782     if (rowSorter.colSortFnCache[col.colDef.name]) {
6783       sortFn = rowSorter.colSortFnCache[col.colDef.name];
6784     }
6785     // If the column has its OWN sorting algorithm, use that
6786     else if (col.sortingAlgorithm !== undefined) {
6787       sortFn = col.sortingAlgorithm;
6788       rowSorter.colSortFnCache[col.colDef.name] = col.sortingAlgorithm;
6789     }
6790     // Try and guess what sort function to use
6791     else {
6792       // Guess the sort function
6793       sortFn = rowSorter.guessSortFn(col.colDef.type);
6794
6795       // If we found a sort function, cache it
6796       if (sortFn) {
6797         rowSorter.colSortFnCache[col.colDef.name] = sortFn;
6798       }
6799       else {
6800         // We assign the alpha sort because anything that is null/undefined will never get passed to
6801         // the actual sorting function. It will get caught in our null check and returned to be sorted
6802         // down to the bottom
6803         sortFn = rowSorter.sortAlpha;
6804       }
6805     }
6806
6807     return sortFn;
6808   };
6809
6810   rowSorter.prioritySort = function (a, b) {
6811     // Both columns have a sort priority
6812     if (a.sort.priority !== undefined && b.sort.priority !== undefined) {
6813       // A is higher priority
6814       if (a.sort.priority < b.sort.priority) {
6815         return -1;
6816       }
6817       // Equal
6818       else if (a.sort.priority === b.sort.priority) {
6819         return 0;
6820       }
6821       // B is higher
6822       else {
6823         return 1;
6824       }
6825     }
6826     // Only A has a priority
6827     else if (a.sort.priority) {
6828       return -1;
6829     }
6830     // Only B has a priority
6831     else if (b.sort.priority) {
6832       return 1;
6833     }
6834     // Neither has a priority
6835     else {
6836       return 0;
6837     }
6838   };
6839
6840   /**
6841    * @ngdoc object
6842    * @name useExternalSorting
6843    * @propertyOf ui.grid.class:GridOptions
6844    * @description Prevents the internal sorting from executing.  Events will
6845    * still be fired when the sort changes, and the sort information on
6846    * the columns will be updated, allowing an external sorter (for example,
6847    * server sorting) to be implemented.  Defaults to false. 
6848    * 
6849    */
6850   /**
6851    * @ngdoc method
6852    * @methodOf ui.grid.class:RowSorter
6853    * @name sort
6854    * @description sorts the grid 
6855    * @param {Object} grid the grid itself
6856    * @param {Object} rows the rows to be sorted
6857    * @param {Object} columns the columns in which to look
6858    * for sort criteria
6859    */
6860   rowSorter.sort = function rowSorterSort(grid, rows, columns) {
6861     // first make sure we are even supposed to do work
6862     if (!rows) {
6863       return;
6864     }
6865     
6866     if (grid.options.useExternalSorting){
6867       return rows;
6868     }
6869
6870     // Build the list of columns to sort by
6871     var sortCols = [];
6872     columns.forEach(function (col) {
6873       if (col.sort && col.sort.direction && (col.sort.direction === uiGridConstants.ASC || col.sort.direction === uiGridConstants.DESC)) {
6874         sortCols.push(col);
6875       }
6876     });
6877
6878     // Sort the "sort columns" by their sort priority
6879     sortCols = sortCols.sort(rowSorter.prioritySort);
6880
6881     // Now rows to sort by, maintain original order
6882     if (sortCols.length === 0) {
6883       return rows;
6884     }
6885     
6886     // Re-usable variables
6887     var col, direction;
6888
6889     // IE9-11 HACK.... the 'rows' variable would be empty where we call rowSorter.getSortFn(...) below. We have to use a separate reference
6890     // var d = data.slice(0);
6891     var r = rows.slice(0);
6892
6893     // Now actually sort the data
6894     return rows.sort(function rowSortFn(rowA, rowB) {
6895       var tem = 0,
6896           idx = 0,
6897           sortFn;
6898
6899       while (tem === 0 && idx < sortCols.length) {
6900         // grab the metadata for the rest of the logic
6901         col = sortCols[idx];
6902         direction = sortCols[idx].sort.direction;
6903
6904         sortFn = rowSorter.getSortFn(grid, col, r);
6905         
6906         var propA = grid.getCellValue(rowA, col);
6907         var propB = grid.getCellValue(rowB, col);
6908
6909         // We want to allow zero values to be evaluated in the sort function
6910         if ((!propA && propA !== 0) || (!propB && propB !== 0)) {
6911           // We want to force nulls and such to the bottom when we sort... which effectively is "greater than"
6912           if (!propB && !propA) {
6913             tem = 0;
6914           }
6915           else if (!propA) {
6916             tem = 1;
6917           }
6918           else if (!propB) {
6919             tem = -1;
6920           }
6921         }
6922         else {
6923           tem = sortFn(propA, propB);
6924         }
6925
6926         idx++;
6927       }
6928
6929       // Made it this far, we don't have to worry about null & undefined
6930       if (direction === uiGridConstants.ASC) {
6931         return tem;
6932       } else {
6933         return 0 - tem;
6934       }
6935     });
6936   };
6937
6938   return rowSorter;
6939 }]);
6940
6941 })();
6942 (function() {
6943
6944 var module = angular.module('ui.grid');
6945
6946
6947 function getStyles (elem) {
6948   return elem.ownerDocument.defaultView.getComputedStyle(elem, null);
6949 }
6950
6951 var rnumnonpx = new RegExp( "^(" + (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source + ")(?!px)[a-z%]+$", "i" ),
6952     // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
6953     // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
6954     rdisplayswap = /^(block|none|table(?!-c[ea]).+)/,
6955     cssShow = { position: "absolute", visibility: "hidden", display: "block" };
6956
6957 function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
6958   var i = extra === ( isBorderBox ? 'border' : 'content' ) ?
6959           // If we already have the right measurement, avoid augmentation
6960           4 :
6961           // Otherwise initialize for horizontal or vertical properties
6962           name === 'width' ? 1 : 0,
6963
6964           val = 0;
6965
6966   var sides = ['Top', 'Right', 'Bottom', 'Left'];
6967   
6968   for ( ; i < 4; i += 2 ) {
6969     var side = sides[i];
6970     // dump('side', side);
6971
6972     // both box models exclude margin, so add it if we want it
6973     if ( extra === 'margin' ) {
6974       var marg = parseFloat(styles[extra + side]);
6975       if (!isNaN(marg)) {
6976         val += marg;
6977       }
6978     }
6979     // dump('val1', val);
6980
6981     if ( isBorderBox ) {
6982       // border-box includes padding, so remove it if we want content
6983       if ( extra === 'content' ) {
6984         var padd = parseFloat(styles['padding' + side]);
6985         if (!isNaN(padd)) {
6986           val -= padd;
6987           // dump('val2', val);
6988         }
6989       }
6990
6991       // at this point, extra isn't border nor margin, so remove border
6992       if ( extra !== 'margin' ) {
6993         var bordermarg = parseFloat(styles['border' + side + 'Width']);
6994         if (!isNaN(bordermarg)) {
6995           val -= bordermarg;
6996           // dump('val3', val);
6997         }
6998       }
6999     }
7000     else {
7001       // at this point, extra isn't content, so add padding
7002       var nocontentPad = parseFloat(styles['padding' + side]);
7003       if (!isNaN(nocontentPad)) {
7004         val += nocontentPad;
7005         // dump('val4', val);
7006       }
7007
7008       // at this point, extra isn't content nor padding, so add border
7009       if ( extra !== 'padding') {
7010         var nocontentnopad = parseFloat(styles['border' + side + 'Width']);
7011         if (!isNaN(nocontentnopad)) {
7012           val += nocontentnopad;
7013           // dump('val5', val);
7014         }
7015       }
7016     }
7017   }
7018
7019   // dump('augVal', val);
7020
7021   return val;
7022 }
7023
7024 function getWidthOrHeight( elem, name, extra ) {
7025   // Start with offset property, which is equivalent to the border-box value
7026   var valueIsBorderBox = true,
7027           val = name === 'width' ? elem.offsetWidth : elem.offsetHeight,
7028           styles = getStyles(elem),
7029           isBorderBox = styles['boxSizing'] === 'border-box';
7030
7031   // some non-html elements return undefined for offsetWidth, so check for null/undefined
7032   // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
7033   // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
7034   if ( val <= 0 || val == null ) {
7035     // Fall back to computed then uncomputed css if necessary
7036     val = styles[name];
7037     if ( val < 0 || val == null ) {
7038       val = elem.style[ name ];
7039     }
7040
7041     // Computed unit is not pixels. Stop here and return.
7042     if ( rnumnonpx.test(val) ) {
7043       return val;
7044     }
7045
7046     // we need the check for style in case a browser which returns unreliable values
7047     // for getComputedStyle silently falls back to the reliable elem.style
7048     valueIsBorderBox = isBorderBox &&
7049             ( true || val === elem.style[ name ] ); // use 'true' instead of 'support.boxSizingReliable()'
7050
7051     // Normalize "", auto, and prepare for extra
7052     val = parseFloat( val ) || 0;
7053   }
7054
7055   // use the active box-sizing model to add/subtract irrelevant styles
7056   var ret = ( val +
7057     augmentWidthOrHeight(
7058       elem,
7059       name,
7060       extra || ( isBorderBox ? "border" : "content" ),
7061       valueIsBorderBox,
7062       styles
7063     )
7064   );
7065
7066   // dump('ret', ret, val);
7067   return ret;
7068 }
7069
7070 var uid = ['0', '0', '0'];
7071 var uidPrefix = 'uiGrid-';
7072
7073 /**
7074  *  @ngdoc service
7075  *  @name ui.grid.service:GridUtil
7076  *  
7077  *  @description Grid utility functions
7078  */
7079 module.service('gridUtil', ['$log', '$window', '$document', '$http', '$templateCache', '$timeout', '$injector', '$q', 'uiGridConstants',
7080   function ($log, $window, $document, $http, $templateCache, $timeout, $injector, $q, uiGridConstants) {
7081   var s = {
7082
7083     /**
7084      * @ngdoc method
7085      * @name createBoundedWrapper
7086      * @methodOf ui.grid.service:GridUtil
7087      *
7088      * @param {object} Object to bind 'this' to
7089      * @param {method} Method to bind
7090      * @returns {Function} The wrapper that performs the binding
7091      *
7092      * @description
7093      * Binds given method to given object.
7094      *
7095      * By means of a wrapper, ensures that ``method`` is always bound to
7096      * ``object`` regardless of its calling environment.
7097      * Iow, inside ``method``, ``this`` always points to ``object``.
7098      *
7099      * See http://alistapart.com/article/getoutbindingsituations
7100      *
7101      */
7102     createBoundedWrapper: function(object, method) {
7103         return function() {
7104             return method.apply(object, arguments);
7105         };
7106     },
7107
7108
7109     /**
7110      * @ngdoc method
7111      * @name readableColumnName
7112      * @methodOf ui.grid.service:GridUtil
7113      *
7114      * @param {string} columnName Column name as a string
7115      * @returns {string} Column name appropriately capitalized and split apart
7116      *
7117        @example
7118        <example module="app">
7119         <file name="app.js">
7120           var app = angular.module('app', ['ui.grid']);
7121
7122           app.controller('MainCtrl', ['$scope', 'gridUtil', function ($scope, gridUtil) {
7123             $scope.name = 'firstName';
7124             $scope.columnName = function(name) {
7125               return gridUtil.readableColumnName(name);
7126             };
7127           }]);
7128         </file>
7129         <file name="index.html">
7130           <div ng-controller="MainCtrl">
7131             <strong>Column name:</strong> <input ng-model="name" />
7132             <br>
7133             <strong>Output:</strong> <span ng-bind="columnName(name)"></span>
7134           </div>
7135         </file>
7136       </example>
7137      */
7138     readableColumnName: function (columnName) {
7139       // Convert underscores to spaces
7140       if (typeof(columnName) === 'undefined' || columnName === undefined || columnName === null) { return columnName; }
7141
7142       if (typeof(columnName) !== 'string') {
7143         columnName = String(columnName);
7144       }
7145
7146       return columnName.replace(/_+/g, ' ')
7147         // Replace a completely all-capsed word with a first-letter-capitalized version
7148         .replace(/^[A-Z]+$/, function (match) {
7149           return angular.lowercase(angular.uppercase(match.charAt(0)) + match.slice(1));
7150         })
7151         // Capitalize the first letter of words
7152         .replace(/(\w+)/g, function (match) {
7153           return angular.uppercase(match.charAt(0)) + match.slice(1);
7154         })
7155         // Put a space in between words that have partial capilizations (i.e. 'firstName' becomes 'First Name')
7156         // .replace(/([A-Z]|[A-Z]\w+)([A-Z])/g, "$1 $2");
7157         // .replace(/(\w+?|\w)([A-Z])/g, "$1 $2");
7158         .replace(/(\w+?(?=[A-Z]))/g, '$1 ');
7159     },
7160
7161     /**
7162      * @ngdoc method
7163      * @name getColumnsFromData
7164      * @methodOf ui.grid.service:GridUtil
7165      * @description Return a list of column names, given a data set
7166      *
7167      * @param {string} data Data array for grid
7168      * @returns {Object} Column definitions with field accessor and column name
7169      *
7170      * @example
7171        <pre>
7172          var data = [
7173            { firstName: 'Bob', lastName: 'Jones' },
7174            { firstName: 'Frank', lastName: 'Smith' }
7175          ];
7176
7177          var columnDefs = GridUtil.getColumnsFromData(data, excludeProperties);
7178
7179          columnDefs == [
7180           {
7181             field: 'firstName',
7182             name: 'First Name'
7183           },
7184           {
7185             field: 'lastName',
7186             name: 'Last Name'
7187           }
7188          ];
7189        </pre>
7190      */
7191     getColumnsFromData: function (data, excludeProperties) {
7192       var columnDefs = [];
7193
7194       if (!data || typeof(data[0]) === 'undefined' || data[0] === undefined) { return []; }
7195       if (angular.isUndefined(excludeProperties)) { excludeProperties = []; }
7196
7197       var item = data[0];
7198       
7199       angular.forEach(item,function (prop, propName) {
7200         if ( excludeProperties.indexOf(propName) === -1){
7201           columnDefs.push({
7202             name: propName
7203           });
7204         }
7205       });
7206
7207       return columnDefs;
7208     },
7209
7210     /**
7211      * @ngdoc method
7212      * @name newId
7213      * @methodOf ui.grid.service:GridUtil
7214      * @description Return a unique ID string
7215      *
7216      * @returns {string} Unique string
7217      *
7218      * @example
7219        <pre>
7220         var id = GridUtil.newId();
7221
7222         # 1387305700482;
7223        </pre>
7224      */
7225     newId: (function() {
7226       var seedId = new Date().getTime();
7227       return function() {
7228           return seedId += 1;
7229       };
7230     })(),
7231
7232
7233     /**
7234      * @ngdoc method
7235      * @name getTemplate
7236      * @methodOf ui.grid.service:GridUtil
7237      * @description Get's template from cache / element / url
7238      *
7239      * @param {string|element|promise} Either a string representing the template id, a string representing the template url,
7240      *   an jQuery/Angualr element, or a promise that returns the template contents to use.
7241      * @returns {object} a promise resolving to template contents
7242      *
7243      * @example
7244      <pre>
7245      GridUtil.getTemplate(url).then(function (contents) {
7246           alert(contents);
7247         })
7248      </pre>
7249      */
7250     getTemplate: function (template) {
7251       // Try to fetch the template out of the templateCache
7252       if ($templateCache.get(template)) {
7253         return $q.when($templateCache.get(template));
7254       }
7255
7256       // See if the template is itself a promise
7257       if (template.hasOwnProperty('then')) {
7258         return template;
7259       }
7260
7261       // If the template is an element, return the element
7262       try {
7263         if (angular.element(template).length > 0) {
7264           return $q.when(template);
7265         }
7266       }
7267       catch (err){
7268         //do nothing; not valid html
7269       }
7270
7271       $log.debug('Fetching url', template);
7272
7273       // Default to trying to fetch the template as a url with $http
7274       return $http({ method: 'GET', url: template})
7275         .then(
7276           function (result) {
7277             var templateHtml = result.data.trim();
7278             //put in templateCache for next call
7279             $templateCache.put(template, templateHtml);
7280             return templateHtml;
7281           },
7282           function (err) {
7283             throw new Error("Could not get template " + template + ": " + err);
7284           }
7285         );
7286     },
7287
7288     /**
7289      * @ngdoc method
7290      * @name guessType
7291      * @methodOf ui.grid.service:GridUtil
7292      * @description guesses the type of an argument
7293      *
7294      * @param {string/number/bool/object} item variable to examine
7295      * @returns {string} one of the following
7296      * 'string'
7297      * 'boolean'
7298      * 'number'
7299      * 'date'
7300      * 'object'
7301      */
7302     guessType : function (item) {
7303       var itemType = typeof(item);
7304
7305       // Check for numbers and booleans
7306       switch (itemType) {
7307         case "number":
7308         case "boolean":
7309         case "string":
7310           return itemType;
7311         default:
7312           if (angular.isDate(item)) {
7313             return "date";
7314           }
7315           return "object";
7316       }
7317     },
7318
7319
7320   /**
7321     * @ngdoc method
7322     * @name elementWidth
7323     * @methodOf ui.grid.service:GridUtil
7324     *
7325     * @param {element} element DOM element
7326     * @param {string} [extra] Optional modifier for calculation. Use 'margin' to account for margins on element
7327     *
7328     * @returns {number} Element width in pixels, accounting for any borders, etc.
7329     */
7330     elementWidth: function (elem) {
7331       
7332     },
7333
7334     /**
7335     * @ngdoc method
7336     * @name elementHeight
7337     * @methodOf ui.grid.service:GridUtil
7338     *
7339     * @param {element} element DOM element
7340     * @param {string} [extra] Optional modifier for calculation. Use 'margin' to account for margins on element
7341     *
7342     * @returns {number} Element height in pixels, accounting for any borders, etc.
7343     */
7344     elementHeight: function (elem) {
7345       
7346     },
7347
7348     // Thanks to http://stackoverflow.com/a/13382873/888165
7349     getScrollbarWidth: function() {
7350         var outer = document.createElement("div");
7351         outer.style.visibility = "hidden";
7352         outer.style.width = "100px";
7353         outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps
7354
7355         document.body.appendChild(outer);
7356
7357         var widthNoScroll = outer.offsetWidth;
7358         // force scrollbars
7359         outer.style.overflow = "scroll";
7360
7361         // add innerdiv
7362         var inner = document.createElement("div");
7363         inner.style.width = "100%";
7364         outer.appendChild(inner);        
7365
7366         var widthWithScroll = inner.offsetWidth;
7367
7368         // remove divs
7369         outer.parentNode.removeChild(outer);
7370
7371         return widthNoScroll - widthWithScroll;
7372     },
7373
7374     swap: function( elem, options, callback, args ) {
7375       var ret, name,
7376               old = {};
7377
7378       // Remember the old values, and insert the new ones
7379       for ( name in options ) {
7380         old[ name ] = elem.style[ name ];
7381         elem.style[ name ] = options[ name ];
7382       }
7383
7384       ret = callback.apply( elem, args || [] );
7385
7386       // Revert the old values
7387       for ( name in options ) {
7388         elem.style[ name ] = old[ name ];
7389       }
7390
7391       return ret;
7392     },
7393
7394     fakeElement: function( elem, options, callback, args ) {
7395       var ret, name,
7396           newElement = angular.element(elem).clone()[0];
7397
7398       for ( name in options ) {
7399         newElement.style[ name ] = options[ name ];
7400       }
7401
7402       angular.element(document.body).append(newElement);
7403
7404       ret = callback.call( newElement, newElement );
7405
7406       angular.element(newElement).remove();
7407
7408       return ret;
7409     },
7410
7411     /**
7412     * @ngdoc method
7413     * @name normalizeWheelEvent
7414     * @methodOf ui.grid.service:GridUtil
7415     *
7416     * @param {event} event A mouse wheel event
7417     *
7418     * @returns {event} A normalized event
7419     *
7420     * @description
7421     * Given an event from this list:
7422     *
7423     * `wheel, mousewheel, DomMouseScroll, MozMousePixelScroll`
7424     *
7425     * "normalize" it
7426     * so that it stays consistent no matter what browser it comes from (i.e. scale it correctly and make sure the direction is right.)
7427     */
7428     normalizeWheelEvent: function (event) {
7429       // var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'];
7430       // var toBind = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'];
7431       var lowestDelta, lowestDeltaXY;
7432       
7433       var orgEvent   = event || window.event,
7434           args       = [].slice.call(arguments, 1),
7435           delta      = 0,
7436           deltaX     = 0,
7437           deltaY     = 0,
7438           absDelta   = 0,
7439           absDeltaXY = 0,
7440           fn;
7441
7442       // event = $.event.fix(orgEvent);
7443       // event.type = 'mousewheel';
7444
7445       // NOTE: jQuery masks the event and stores it in the event as originalEvent
7446       if (orgEvent.originalEvent) {
7447         orgEvent = orgEvent.originalEvent;
7448       }
7449
7450       // Old school scrollwheel delta
7451       if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta; }
7452       if ( orgEvent.detail )     { delta = orgEvent.detail * -1; }
7453
7454       // At a minimum, setup the deltaY to be delta
7455       deltaY = delta;
7456
7457       // Firefox < 17 related to DOMMouseScroll event
7458       if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
7459           deltaY = 0;
7460           deltaX = delta * -1;
7461       }
7462
7463       // New school wheel delta (wheel event)
7464       if ( orgEvent.deltaY ) {
7465           deltaY = orgEvent.deltaY * -1;
7466           delta  = deltaY;
7467       }
7468       if ( orgEvent.deltaX ) {
7469           deltaX = orgEvent.deltaX;
7470           delta  = deltaX * -1;
7471       }
7472
7473       // Webkit
7474       if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY; }
7475       if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = orgEvent.wheelDeltaX; }
7476
7477       // Look for lowest delta to normalize the delta values
7478       absDelta = Math.abs(delta);
7479       if ( !lowestDelta || absDelta < lowestDelta ) { lowestDelta = absDelta; }
7480       absDeltaXY = Math.max(Math.abs(deltaY), Math.abs(deltaX));
7481       if ( !lowestDeltaXY || absDeltaXY < lowestDeltaXY ) { lowestDeltaXY = absDeltaXY; }
7482
7483       // Get a whole value for the deltas
7484       fn     = delta > 0 ? 'floor' : 'ceil';
7485       delta  = Math[fn](delta  / lowestDelta);
7486       deltaX = Math[fn](deltaX / lowestDeltaXY);
7487       deltaY = Math[fn](deltaY / lowestDeltaXY);
7488
7489       return {
7490         delta: delta,
7491         deltaX: deltaX,
7492         deltaY: deltaY
7493       };
7494     },
7495
7496     // Stolen from Modernizr
7497     // TODO: make this, and everythign that flows from it, robust
7498     //http://www.stucox.com/blog/you-cant-detect-a-touchscreen/
7499     isTouchEnabled: function() {
7500       var bool;
7501
7502       if (('ontouchstart' in $window) || $window.DocumentTouch && $document instanceof DocumentTouch) {
7503         bool = true;
7504       }
7505
7506       return bool;
7507     },
7508
7509     isNullOrUndefined: function(obj) {
7510       if (obj === undefined || obj === null) {
7511         return true;
7512       }
7513       return false;
7514     },
7515
7516     endsWith: function(str, suffix) {
7517       if (!str || !suffix || typeof str !== "string") {
7518         return false;
7519       }
7520       return str.indexOf(suffix, str.length - suffix.length) !== -1;
7521     },
7522
7523     // Shim requestAnimationFrame
7524     requestAnimationFrame: $window.requestAnimationFrame && $window.requestAnimationFrame.bind($window) ||
7525                            $window.webkitRequestAnimationFrame && $window.webkitRequestAnimationFrame.bind($window) ||
7526                            function(fn) {
7527                              return $timeout(fn, 10, false);
7528                            },
7529
7530     numericAndNullSort: function (a, b) {
7531       if (a === null) { return 1; }
7532       if (b === null) { return -1; }
7533       if (a === null && b === null) { return 0; }
7534       return a - b;
7535     },
7536
7537     // Disable ngAnimate animations on an element
7538     disableAnimations: function (element) {
7539       var $animate;
7540       try {
7541         $animate = $injector.get('$animate');
7542         $animate.enabled(false, element);
7543       }
7544       catch (e) {}
7545     },
7546
7547     enableAnimations: function (element) {
7548       var $animate;
7549       try {
7550         $animate = $injector.get('$animate');
7551         $animate.enabled(true, element);
7552       }
7553       catch (e) {}
7554     },
7555
7556     // Blatantly stolen from Angular as it isn't exposed (yet. 2.0 maybe?)
7557     nextUid: function nextUid() {
7558       var index = uid.length;
7559       var digit;
7560
7561       while (index) {
7562         index--;
7563         digit = uid[index].charCodeAt(0);
7564         if (digit === 57 /*'9'*/) {
7565           uid[index] = 'A';
7566           return uidPrefix + uid.join('');
7567         }
7568         if (digit === 90  /*'Z'*/) {
7569           uid[index] = '0';
7570         } else {
7571           uid[index] = String.fromCharCode(digit + 1);
7572           return uidPrefix + uid.join('');
7573         }
7574       }
7575       uid.unshift('0');
7576
7577       return uidPrefix + uid.join('');
7578     },
7579
7580     // Blatantly stolen from Angular as it isn't exposed (yet. 2.0 maybe?)
7581     hashKey: function hashKey(obj) {
7582       var objType = typeof obj,
7583           key;
7584
7585       if (objType === 'object' && obj !== null) {
7586         if (typeof (key = obj.$$hashKey) === 'function') {
7587           // must invoke on object to keep the right this
7588           key = obj.$$hashKey();
7589         }
7590         else if (typeof(obj.$$hashKey) !== 'undefined' && obj.$$hashKey) {
7591           key = obj.$$hashKey;
7592         }
7593         else if (key === undefined) {
7594           key = obj.$$hashKey = s.nextUid();
7595         }
7596       }
7597       else {
7598         key = obj;
7599       }
7600
7601       return objType + ':' + key;
7602     }
7603
7604     // setHashKey: function setHashKey(obj, h) {
7605     //   if (h) {
7606     //     obj.$$hashKey = h;
7607     //   }
7608     //   else {
7609     //     delete obj.$$hashKey;
7610     //   }
7611     // }
7612   };
7613
7614   ['width', 'height'].forEach(function (name) {
7615     var capsName = angular.uppercase(name.charAt(0)) + name.substr(1);
7616     s['element' + capsName] = function (elem, extra) {
7617       var e = elem;
7618       if (typeof(e.length) !== 'undefined' && e.length) {
7619         e = elem[0];
7620       }
7621
7622       if (e) {
7623         var styles = getStyles(e);
7624         return e.offsetWidth === 0 && rdisplayswap.test(styles.display) ?
7625                   s.fakeElement(e, cssShow, function(newElm) {
7626                     return getWidthOrHeight( newElm, name, extra );
7627                   }) :
7628                   getWidthOrHeight( e, name, extra );
7629       }
7630       else {
7631         return null;
7632       }
7633     };
7634
7635     s['outerElement' + capsName] = function (elem, margin) {
7636       return elem ? s['element' + capsName].call(this, elem, margin ? 'margin' : 'border') : null;
7637     };
7638   });
7639
7640   // http://stackoverflow.com/a/24107550/888165
7641   s.closestElm = function closestElm(el, selector) {
7642     if (typeof(el.length) !== 'undefined' && el.length) {
7643       el = el[0];
7644     }
7645
7646     var matchesFn;
7647
7648     // find vendor prefix
7649     ['matches','webkitMatchesSelector','mozMatchesSelector','msMatchesSelector','oMatchesSelector'].some(function(fn) {
7650         if (typeof document.body[fn] === 'function') {
7651             matchesFn = fn;
7652             return true;
7653         }
7654         return false;
7655     });
7656
7657     // traverse parents
7658     var parent;
7659     while (el !== null) {
7660       parent = el.parentElement;
7661       if (parent !== null && parent[matchesFn](selector)) {
7662           return parent;
7663       }
7664       el = parent;
7665     }
7666
7667     return null;
7668   };
7669
7670   s.type = function (obj) {
7671     var text = Function.prototype.toString.call(obj.constructor);
7672     return text.match(/function (.*?)\(/)[1];
7673   };
7674
7675   s.getBorderSize = function getBorderSize(elem, borderType) {
7676     if (typeof(elem.length) !== 'undefined' && elem.length) {
7677       elem = elem[0];
7678     }
7679
7680     var styles = getStyles(elem);
7681
7682     if (borderType) {
7683       borderType = 'border-' + borderType;
7684     }
7685     else {
7686       borderType = 'border';
7687     }
7688
7689     var val = parseInt(styles[borderType], 10);
7690
7691     if (isNaN(val)) {
7692       return 0;
7693     }
7694     else {
7695       return val;
7696     }
7697   };
7698
7699   // http://stackoverflow.com/a/22948274/888165
7700   // TODO: Opera? Mobile?
7701   s.detectBrowser = function detectBrowser() {
7702     var userAgent = $window.navigator.userAgent;
7703
7704     var browsers = {chrome: /chrome/i, safari: /safari/i, firefox: /firefox/i, ie: /internet explorer|trident\//i};
7705
7706     for (var key in browsers) {
7707       if (browsers[key].test(userAgent)) {
7708         return key;
7709       }
7710     }
7711
7712     return 'unknown';
7713   };
7714
7715   /**
7716     * @ngdoc method
7717     * @name normalizeScrollLeft
7718     * @methodOf ui.grid.service:GridUtil
7719     *
7720     * @param {element} element The element to get the `scrollLeft` from.
7721     *
7722     * @returns {int} A normalized scrollLeft value for the current browser.
7723     *
7724     * @description
7725     * Browsers currently handle RTL in different ways, resulting in inconsistent scrollLeft values. This method normalizes them
7726     */
7727   s.normalizeScrollLeft = function normalizeScrollLeft(element) {
7728     if (typeof(element.length) !== 'undefined' && element.length) {
7729       element = element[0];
7730     }
7731
7732     var browser = s.detectBrowser();
7733
7734     var scrollLeft = element.scrollLeft;
7735
7736     var dir = angular.element(element).css('direction');
7737
7738     // IE stays normal in RTL
7739     if (browser === 'ie') {
7740       return scrollLeft;
7741     }
7742     // Chrome doesn't alter the scrollLeft value. So with RTL on a 400px-wide grid, the right-most position will still be 400 and the left-most will still be 0;
7743     else if (browser === 'chrome') {
7744       if (dir === 'rtl') {
7745         // Get the max scroll for the element
7746         var maxScrollLeft = element.scrollWidth - element.clientWidth;
7747
7748         // Subtract the current scroll amount from the max scroll
7749         return maxScrollLeft - scrollLeft;
7750       }
7751       else {
7752         return scrollLeft;
7753       }
7754     }
7755     // Firefox goes negative!
7756     else if (browser === 'firefox') {
7757       return Math.abs(scrollLeft);
7758     }
7759     else {
7760       // TODO(c0bra): Handle other browsers? Android? iOS? Opera?
7761       return scrollLeft;
7762     }
7763   };
7764
7765   /**
7766   * @ngdoc method
7767   * @name normalizeScrollLeft
7768   * @methodOf ui.grid.service:GridUtil
7769   *
7770   * @param {element} element The element to normalize the `scrollLeft` value for
7771   * @param {int} scrollLeft The `scrollLeft` value to denormalize.
7772   *
7773   * @returns {int} A normalized scrollLeft value for the current browser.
7774   *
7775   * @description
7776   * Browsers currently handle RTL in different ways, resulting in inconsistent scrollLeft values. This method denormalizes a value for the current browser.
7777   */
7778   s.denormalizeScrollLeft = function denormalizeScrollLeft(element, scrollLeft) {
7779     if (typeof(element.length) !== 'undefined' && element.length) {
7780       element = element[0];
7781     }
7782
7783     var browser = s.detectBrowser();
7784
7785     var dir = angular.element(element).css('direction');
7786
7787     // IE stays normal in RTL
7788     if (browser === 'ie') {
7789       return scrollLeft;
7790     }
7791     // Chrome doesn't alter the scrollLeft value. So with RTL on a 400px-wide grid, the right-most position will still be 400 and the left-most will still be 0;
7792     else if (browser === 'chrome') {
7793       if (dir === 'rtl') {
7794         // Get the max scroll for the element
7795         var maxScrollLeft = element.scrollWidth - element.clientWidth;
7796
7797         // Subtract the current scroll amount from the max scroll
7798         return maxScrollLeft - scrollLeft;
7799       }
7800       else {
7801         return scrollLeft;
7802       }
7803     }
7804     // Firefox goes negative!
7805     else if (browser === 'firefox') {
7806       if (dir === 'rtl') {
7807         return scrollLeft * -1;
7808       }
7809       else {
7810         return scrollLeft;
7811       }
7812     }
7813     else {
7814       // TODO(c0bra): Handle other browsers? Android? iOS? Opera?
7815       return scrollLeft;
7816     }
7817   };
7818
7819     /**
7820      * @ngdoc method
7821      * @name preEval
7822      * @methodOf ui.grid.service:GridUtil
7823      *
7824      * @param {string} path Path to evaluate
7825      *
7826      * @returns {string} A path that is normalized.
7827      *
7828      * @description
7829      * Takes a field path and converts it to bracket notation to allow for special characters in path
7830      * @example
7831      * <pre>
7832      * gridUtil.preEval('property') == 'property'
7833      * gridUtil.preEval('nested.deep.prop-erty') = "nested['deep']['prop-erty']"
7834      * </pre>
7835      */
7836   s.preEval = function (path) {
7837     var m = uiGridConstants.BRACKET_REGEXP.exec(path);
7838     if (m) {
7839       return (m[1] ? s.preEval(m[1]) : m[1]) + m[2] + (m[3] ? s.preEval(m[3]) : m[3]);
7840     } else {
7841       path = path.replace(uiGridConstants.APOS_REGEXP, '\\\'');
7842       var parts = path.split(uiGridConstants.DOT_REGEXP);
7843       var preparsed = [parts.shift()];    // first item must be var notation, thus skip
7844       angular.forEach(parts, function (part) {
7845         preparsed.push(part.replace(uiGridConstants.FUNC_REGEXP, '\']$1'));
7846       });
7847       return preparsed.join('[\'');
7848     }
7849   };
7850
7851   /**
7852    * @ngdoc method
7853    * @name debounce
7854    * @methodOf ui.grid.service:GridUtil
7855    *
7856    * @param {function} func function to debounce
7857    * @param {number} wait milliseconds to delay
7858    * @param {bool} immediate execute before delay
7859    *
7860    * @returns {function} A function that can be executed as debounced function
7861    *
7862    * @description
7863    * Copied from https://github.com/shahata/angular-debounce
7864    * Takes a function, decorates it to execute only 1 time after multiple calls, and returns the decorated function
7865    * @example
7866    * <pre>
7867    * var debouncedFunc =  gridUtil.debounce(function(){alert('debounced');}, 500);
7868    * debouncedFunc();
7869    * debouncedFunc();
7870    * debouncedFunc();
7871    * </pre>
7872    */
7873   s.debounce =  function (func, wait, immediate) {
7874     var timeout, args, context, result;
7875     function debounce() {
7876       /* jshint validthis:true */
7877       context = this;
7878       args = arguments;
7879       var later = function () {
7880         timeout = null;
7881         if (!immediate) {
7882           result = func.apply(context, args);
7883         }
7884       };
7885       var callNow = immediate && !timeout;
7886       if (timeout) {
7887         $timeout.cancel(timeout);
7888       }
7889       timeout = $timeout(later, wait);
7890       if (callNow) {
7891         result = func.apply(context, args);
7892       }
7893       return result;
7894     }
7895     debounce.cancel = function () {
7896       $timeout.cancel(timeout);
7897       timeout = null;
7898     };
7899     return debounce;
7900   };
7901
7902   return s;
7903 }]);
7904
7905 // Add 'px' to the end of a number string if it doesn't have it already
7906 module.filter('px', function() {
7907   return function(str) {
7908     if (str.match(/^[\d\.]+$/)) {
7909       return str + 'px';
7910     }
7911     else {
7912       return str;
7913     }
7914   };
7915 });
7916
7917 })();
7918
7919 (function(){
7920   angular.module('ui.grid').config(['$provide', function($provide) {
7921     $provide.decorator('i18nService', ['$delegate', function($delegate) {
7922       $delegate.add('da', {
7923           aggregate:{
7924             label: 'artikler'
7925           },
7926           groupPanel:{
7927             description: 'Grupér rækker udfra en kolonne ved at trække dens overskift hertil.'
7928           },
7929           search:{
7930             placeholder: 'Søg...',
7931             showingItems: 'Viste rækker:',
7932             selectedItems: 'Valgte rækker:',
7933             totalItems: 'Rækker totalt:',
7934             size: 'Side størrelse:',
7935             first: 'Første side',
7936             next: 'Næste side',
7937             previous: 'Forrige side',
7938             last: 'Sidste side'
7939           },
7940           menu:{
7941             text: 'Vælg kolonner:',
7942           },
7943           column: {
7944             hide: 'Skjul kolonne'
7945           }
7946         });
7947       return $delegate;
7948     }]);
7949   }]);
7950 })();
7951 (function () {
7952   angular.module('ui.grid').config(['$provide', function($provide) {
7953     $provide.decorator('i18nService', ['$delegate', function($delegate) {
7954       $delegate.add('de', {
7955         aggregate: {
7956           label: 'eintrag'
7957         },
7958         groupPanel: {
7959           description: 'Ziehen Sie eine Spaltenüberschrift hierhin um nach dieser Spalte zu gruppieren.'
7960         },
7961         search: {
7962           placeholder: 'Suche...',
7963           showingItems: 'Zeige Einträge:',
7964           selectedItems: 'Ausgewählte Einträge:',
7965           totalItems: 'Einträge gesamt:',
7966           size: 'Einträge pro Seite:',
7967           first: 'Erste Seite',
7968           next: 'Nächste Seite',
7969           previous: 'Vorherige Seite',
7970           last: 'Letzte Seite'
7971         },
7972         menu: {
7973           text: 'Spalten auswählen:'
7974         },
7975         column: {
7976           hide: 'Spalte ausblenden'
7977         }
7978       });
7979       return $delegate;
7980     }]);
7981 }]);
7982 })();
7983 (function () {
7984   angular.module('ui.grid').config(['$provide', function($provide) {
7985     $provide.decorator('i18nService', ['$delegate', function($delegate) {
7986       $delegate.add('en', {
7987         aggregate: {
7988           label: 'items'
7989         },
7990         groupPanel: {
7991           description: 'Drag a column header here and drop it to group by that column.'
7992         },
7993         search: {
7994           placeholder: 'Search...',
7995           showingItems: 'Showing Items:',
7996           selectedItems: 'Selected Items:',
7997           totalItems: 'Total Items:',
7998           size: 'Page Size:',
7999           first: 'First Page',
8000           next: 'Next Page',
8001           previous: 'Previous Page',
8002           last: 'Last Page'
8003         },
8004         menu: {
8005           text: 'Choose Columns:'
8006         },
8007         sort: {
8008           ascending: 'Sort Ascending',
8009           descending: 'Sort Descending',
8010           remove: 'Remove Sort'
8011         },
8012         column: {
8013           hide: 'Hide Column'
8014         }
8015       });
8016       return $delegate;
8017     }]);
8018   }]);
8019 })();
8020 (function () {
8021   angular.module('ui.grid').config(['$provide', function($provide) {
8022     $provide.decorator('i18nService', ['$delegate', function($delegate) {
8023       $delegate.add('es', {
8024         aggregate: {
8025           label: 'Artículos'
8026         },
8027         groupPanel: {
8028           description: 'Arrastre un encabezado de columna aquí y soltarlo para agrupar por esa columna.'
8029         },
8030         search: {
8031           placeholder: 'Buscar...',
8032           showingItems: 'Artículos Mostrando:',
8033           selectedItems: 'Artículos Seleccionados:',
8034           totalItems: 'Artículos Totales:',
8035           size: 'Tamaño de Página:',
8036           first: 'Primera Página',
8037           next: 'Página Siguiente',
8038           previous: 'Página Anterior',
8039           last: 'Última Página'
8040         },
8041         menu: {
8042           text: 'Elegir columnas:'
8043         },
8044         column: {
8045           hide: 'Ocultar la columna'
8046         }
8047       });
8048       return $delegate;
8049     }]);
8050 }]);
8051 })();
8052 (function () {
8053   angular.module('ui.grid').config(['$provide', function($provide) {
8054     $provide.decorator('i18nService', ['$delegate', function($delegate) {
8055       $delegate.add('fa', {
8056         aggregate: {
8057           label: 'موردها'
8058         },
8059         groupPanel: {
8060           description: 'یک عنوان ستون اینجا را بردار و به گروهی از آن ستون بیانداز.'
8061         },
8062         search: {
8063           placeholder: 'جستجو...',
8064           showingItems: 'نمایش موردها:',
8065           selectedItems: 'موردهای انتخاب\u200cشده:',
8066           totalItems: 'همهٔ موردها:',
8067           size: 'اندازهٔ صفحه:',
8068           first: 'صفحهٔ اول',
8069           next: 'صفحهٔ بعد',
8070           previous: 'صفحهٔ قبل',
8071           last: 'آخرین صفحه'
8072         },
8073         menu: {
8074           text: 'انتخاب ستون\u200cها:'
8075         },
8076         column: {
8077           hide: 'ستون پنهان کن'
8078         }
8079       });
8080       return $delegate;
8081     }]);
8082 }]);
8083 })();
8084 (function () {
8085   angular.module('ui.grid').config(['$provide', function($provide) {
8086     $provide.decorator('i18nService', ['$delegate', function($delegate) {
8087       $delegate.add('fr', {
8088         aggregate: {
8089           label: 'articles'
8090         },
8091         groupPanel: {
8092           description: 'Faites glisser un en-tête de colonne ici et déposez-le vers un groupe par cette colonne.'
8093         },
8094         search: {
8095           placeholder: 'Recherche...',
8096           showingItems: 'Articles Affichage des:',
8097           selectedItems: 'Éléments Articles:',
8098           totalItems: 'Nombre total d\'articles:',
8099           size: 'Taille de page:',
8100           first: 'Première page',
8101           next: 'Page Suivante',
8102           previous: 'Page précédente',
8103           last: 'Dernière page'
8104         },
8105         menu: {
8106           text: 'Choisir des colonnes:'
8107         },
8108         column: {
8109           hide: 'Colonne de peau'
8110         }
8111       });
8112       return $delegate;
8113     }]);
8114 }]);
8115 })();
8116 (function () {
8117     angular.module('ui.grid').config(['$provide', function ($provide) {
8118         $provide.decorator('i18nService', ['$delegate', function ($delegate) {
8119             $delegate.add('he', {
8120                 aggregate: {
8121                     label: 'items'
8122                 },
8123                 groupPanel: {
8124                     description: 'גרור עמודה לכאן ושחרר בכדי לקבץ עמודה זו.'
8125                 },
8126                 search: {
8127                     placeholder: 'חפש...',
8128                     showingItems: 'מציג:',
8129                     selectedItems: 'סה"כ נבחרו:',
8130                     totalItems: 'סה"כ רשומות:',
8131                     size: 'תוצאות בדף:',
8132                     first: 'דף ראשון',
8133                     next: 'דף הבא',
8134                     previous: 'דף קודם',
8135                     last: 'דף אחרון'
8136                 },
8137                 menu: {
8138                     text: 'בחר עמודות:'
8139                 },
8140                 sort: {
8141                     ascending: 'סדר עולה',
8142                     descending: 'סדר יורד',
8143                     remove: 'בטל'
8144                 },
8145                 column: {
8146                   hide: 'טור הסתר'
8147                 }
8148             });
8149             return $delegate;
8150         }]);
8151     }]);
8152 })();
8153 (function () {
8154   angular.module('ui.grid').config(['$provide', function($provide) {
8155     $provide.decorator('i18nService', ['$delegate', function($delegate) {
8156       $delegate.add('nl', {
8157         aggregate: {
8158           label: 'items'
8159         },
8160         groupPanel: {
8161           description: 'Sleep hier een kolomnaam heen om op te groeperen.'
8162         },
8163         search: {
8164           placeholder: 'Zoeken...',
8165           showingItems: 'Getoonde items:',
8166           selectedItems: 'Geselecteerde items:',
8167           totalItems: 'Totaal aantal items:',
8168           size: 'Items per pagina:',
8169           first: 'Eerste pagina',
8170           next: 'Volgende pagina',
8171           previous: 'Vorige pagina',
8172           last: 'Laatste pagina'
8173         },
8174         menu: {
8175           text: 'Kies kolommen:'
8176         },
8177         sort: {
8178           ascending: 'Sorteer oplopend',
8179           descending: 'Sorteer aflopend',
8180           remove: 'Verwijder sortering'
8181         },
8182         column: {
8183           hide: 'Kolom te verbergen'
8184         }
8185       });
8186       return $delegate;
8187     }]);
8188   }]);
8189 })();
8190 (function () {
8191   angular.module('ui.grid').config(['$provide', function($provide) {
8192     $provide.decorator('i18nService', ['$delegate', function($delegate) {
8193       $delegate.add('pt-br', {
8194         aggregate: {
8195           label: 'itens'
8196         },
8197         groupPanel: {
8198           description: 'Arraste e solte uma coluna aqui para agrupar por essa coluna'
8199         },
8200         search: {
8201           placeholder: 'Procurar...',
8202           showingItems: 'Mostrando os Itens:',
8203           selectedItems: 'Items Selecionados:',
8204           totalItems: 'Total de Itens:',
8205           size: 'Tamanho da Página:',
8206           first: 'Primeira Página',
8207           next: 'Próxima Página',
8208           previous: 'Página Anterior',
8209           last: 'Última Página'
8210         },
8211         menu: {
8212           text: 'Selecione as colunas:'
8213         },
8214         column: {
8215           hide: 'Esconder coluna'
8216         }
8217       });
8218       return $delegate;
8219     }]);
8220 }]);
8221 })();
8222 (function () {
8223   angular.module('ui.grid').config(['$provide', function($provide) {
8224     $provide.decorator('i18nService', ['$delegate', function($delegate) {
8225       $delegate.add('ru', {
8226         aggregate: {
8227           label: 'элементы'
8228         },
8229         groupPanel: {
8230           description: 'Для группировки по столбцу перетащите сюда его название.'
8231         },
8232         search: {
8233           placeholder: 'Поиск...',
8234           showingItems: 'Показать элементы:',
8235           selectedItems: 'Выбранные элементы:',
8236           totalItems: 'Всего элементов:',
8237           size: 'Размер страницы:',
8238           first: 'Первая страница',
8239           next: 'Следующая страница',
8240           previous: 'Предыдущая страница',
8241           last: 'Последняя страница'
8242         },
8243         menu: {
8244           text: 'Выбрать столбцы:'
8245         },
8246         sort: {
8247           ascending: 'По возрастанию',
8248           descending: 'По убыванию',
8249           remove: 'Убрать сортировку'
8250         },
8251         column: {
8252           hide: 'спрятать столбец'
8253         }
8254       });
8255       return $delegate;
8256     }]);
8257   }]);
8258 })();
8259 (function () {
8260 angular.module('ui.grid').config(['$provide', function($provide) {
8261 $provide.decorator('i18nService', ['$delegate', function($delegate) {
8262 $delegate.add('sk', {
8263 aggregate: {
8264 label: 'items'
8265 },
8266 groupPanel: {
8267 description: 'Pretiahni sem názov stĺpca pre zoskupenie podľa toho stĺpca.'
8268 },
8269 search: {
8270 placeholder: 'Hľadaj...',
8271 showingItems: 'Zobrazujem položky:',
8272 selectedItems: 'Vybraté položky:',
8273 totalItems: 'Počet položiek:',
8274 size: 'Počet:',
8275 first: 'Prvá strana',
8276 next: 'Ďalšia strana',
8277 previous: 'Predchádzajúca strana',
8278 last: 'Posledná strana'
8279 },
8280 menu: {
8281 text: 'Vyberte stĺpce:'
8282 },
8283 sort: {
8284 ascending: 'Zotriediť vzostupne',
8285 descending: 'Zotriediť zostupne',
8286 remove: 'Vymazať triedenie'
8287 }
8288 });
8289 return $delegate;
8290 }]);
8291 }]);
8292 })();
8293
8294 /**
8295  * @ngdoc overview
8296  * @name ui.grid.i18n
8297  * @description
8298  *
8299  *  # ui.grid.i18n
8300  * This module provides i18n functions to ui.grid and any application that wants to use it
8301
8302  *
8303  * <div doc-module-components="ui.grid.i18n"></div>
8304  */
8305
8306 (function () {
8307   var DIRECTIVE_ALIASES = ['uiT', 'uiTranslate'];
8308   var FILTER_ALIASES = ['t', 'uiTranslate'];
8309
8310   var module = angular.module('ui.grid.i18n');
8311
8312
8313   /**
8314    *  @ngdoc object
8315    *  @name ui.grid.i18n.constant:i18nConstants
8316    *
8317    *  @description constants available in i18n module
8318    */
8319   module.constant('i18nConstants', {
8320     MISSING: '[MISSING]',
8321     UPDATE_EVENT: '$uiI18n',
8322
8323     LOCALE_DIRECTIVE_ALIAS: 'uiI18n',
8324     // default to english
8325     DEFAULT_LANG: 'en'
8326   });
8327
8328 //    module.config(['$provide', function($provide) {
8329 //        $provide.decorator('i18nService', ['$delegate', function($delegate) {}])}]);
8330
8331   /**
8332    *  @ngdoc service
8333    *  @name ui.grid.i18n.service:i18nService
8334    *
8335    *  @description Services for i18n
8336    */
8337   module.service('i18nService', ['$log', 'i18nConstants', '$rootScope',
8338     function ($log, i18nConstants, $rootScope) {
8339
8340       var langCache = {
8341         _langs: {},
8342         current: null,
8343         get: function (lang) {
8344           return this._langs[lang.toLowerCase()];
8345         },
8346         add: function (lang, strings) {
8347           var lower = lang.toLowerCase();
8348           if (!this._langs[lower]) {
8349             this._langs[lower] = {};
8350           }
8351           angular.extend(this._langs[lower], strings);
8352         },
8353         getAllLangs: function () {
8354           var langs = [];
8355           if (!this._langs) {
8356             return langs;
8357           }
8358
8359           for (var key in this._langs) {
8360             langs.push(key);
8361           }
8362
8363           return langs;
8364         },
8365         setCurrent: function (lang) {
8366           this.current = lang.toLowerCase();
8367         },
8368         getCurrentLang: function () {
8369           return this.current;
8370         }
8371       };
8372
8373       var service = {
8374
8375         /**
8376          * @ngdoc service
8377          * @name add
8378          * @methodOf ui.grid.i18n.service:i18nService
8379          * @description  Adds the languages and strings to the cache. Decorate this service to
8380          * add more translation strings
8381          * @param {string} lang language to add
8382          * @param {object} stringMaps of strings to add grouped by property names
8383          * @example
8384          * <pre>
8385          *      i18nService.add('en', {
8386          *         aggregate: {
8387          *                 label1: 'items',
8388          *                 label2: 'some more items'
8389          *                 }
8390          *         },
8391          *         groupPanel: {
8392          *              description: 'Drag a column header here and drop it to group by that column.'
8393          *           }
8394          *      }
8395          * </pre>
8396          */
8397         add: function (langs, stringMaps) {
8398           if (typeof(langs) === 'object') {
8399             angular.forEach(langs, function (lang) {
8400               if (lang) {
8401                 langCache.add(lang, stringMaps);
8402               }
8403             });
8404           } else {
8405             langCache.add(langs, stringMaps);
8406           }
8407         },
8408
8409         /**
8410          * @ngdoc service
8411          * @name getAllLangs
8412          * @methodOf ui.grid.i18n.service:i18nService
8413          * @description  return all currently loaded languages
8414          * @returns {array} string
8415          */
8416         getAllLangs: function () {
8417           return langCache.getAllLangs();
8418         },
8419
8420         /**
8421          * @ngdoc service
8422          * @name get
8423          * @methodOf ui.grid.i18n.service:i18nService
8424          * @description  return all currently loaded languages
8425          * @param {string} lang to return.  If not specified, returns current language
8426          * @returns {object} the translation string maps for the language
8427          */
8428         get: function (lang) {
8429           var language = lang ? lang : service.getCurrentLang();
8430           return langCache.get(language);
8431         },
8432
8433         /**
8434          * @ngdoc service
8435          * @name getSafeText
8436          * @methodOf ui.grid.i18n.service:i18nService
8437          * @description  returns the text specified in the path or a Missing text if text is not found
8438          * @param {string} path property path to use for retrieving text from string map
8439          * @param {string} lang to return.  If not specified, returns current language
8440          * @returns {object} the translation for the path
8441          * @example
8442          * <pre>
8443          * i18nService.getSafeText('sort.ascending')
8444          * </pre>
8445          */
8446         getSafeText: function (path, lang) {
8447           var language = lang ? lang : service.getCurrentLang();
8448           var trans = langCache.get(language);
8449
8450           if (!trans) {
8451             return i18nConstants.MISSING;
8452           }
8453
8454           var paths = path.split('.');
8455           var current = trans;
8456
8457           for (var i = 0; i < paths.length; ++i) {
8458             if (current[paths[i]] === undefined || current[paths[i]] === null) {
8459               return i18nConstants.MISSING;
8460             } else {
8461               current = current[paths[i]];
8462             }
8463           }
8464
8465           return current;
8466
8467         },
8468
8469         /**
8470          * @ngdoc service
8471          * @name setCurrentLang
8472          * @methodOf ui.grid.i18n.service:i18nService
8473          * @description sets the current language to use in the application
8474          * $broadcasts the Update_Event on the $rootScope
8475          * @param {string} lang to set
8476          * @example
8477          * <pre>
8478          * i18nService.setCurrentLang('fr');
8479          * </pre>
8480          */
8481
8482         setCurrentLang: function (lang) {
8483           if (lang) {
8484             langCache.setCurrent(lang);
8485             $rootScope.$broadcast(i18nConstants.UPDATE_EVENT);
8486           }
8487         },
8488
8489         /**
8490          * @ngdoc service
8491          * @name getCurrentLang
8492          * @methodOf ui.grid.i18n.service:i18nService
8493          * @description returns the current language used in the application
8494          */
8495         getCurrentLang: function () {
8496           var lang = langCache.getCurrentLang();
8497           if (!lang) {
8498             lang = i18nConstants.DEFAULT_LANG;
8499             langCache.setCurrent(lang);
8500           }
8501           return lang;
8502         }
8503
8504       };
8505
8506       return service;
8507
8508     }]);
8509
8510   var localeDirective = function (i18nService, i18nConstants) {
8511     return {
8512       compile: function () {
8513         return {
8514           pre: function ($scope, $elm, $attrs) {
8515             var alias = i18nConstants.LOCALE_DIRECTIVE_ALIAS;
8516             // check for watchable property
8517             var lang = $scope.$eval($attrs[alias]);
8518             if (lang) {
8519               $scope.$watch($attrs[alias], function () {
8520                 i18nService.setCurrentLang(lang);
8521               });
8522             } else if ($attrs.$$observers) {
8523               $attrs.$observe(alias, function () {
8524                 i18nService.setCurrentLang($attrs[alias] || i18nConstants.DEFAULT_LANG);
8525               });
8526             }
8527           }
8528         };
8529       }
8530     };
8531   };
8532
8533   module.directive('uiI18n', ['i18nService', 'i18nConstants', localeDirective]);
8534
8535   // directive syntax
8536   var uitDirective = function ($parse, i18nService, i18nConstants) {
8537     return {
8538       restrict: 'EA',
8539       compile: function () {
8540         return {
8541           pre: function ($scope, $elm, $attrs) {
8542             var alias1 = DIRECTIVE_ALIASES[0],
8543               alias2 = DIRECTIVE_ALIASES[1];
8544             var token = $attrs[alias1] || $attrs[alias2] || $elm.html();
8545             var missing = i18nConstants.MISSING + token;
8546             var observer;
8547             if ($attrs.$$observers) {
8548               var prop = $attrs[alias1] ? alias1 : alias2;
8549               observer = $attrs.$observe(prop, function (result) {
8550                 if (result) {
8551                   $elm.html($parse(result)(i18nService.getCurrentLang()) || missing);
8552                 }
8553               });
8554             }
8555             var getter = $parse(token);
8556             var listener = $scope.$on(i18nConstants.UPDATE_EVENT, function (evt) {
8557               if (observer) {
8558                 observer($attrs[alias1] || $attrs[alias2]);
8559               } else {
8560                 // set text based on i18n current language
8561                 $elm.html(getter(i18nService.get()) || missing);
8562               }
8563             });
8564             $scope.$on('$destroy', listener);
8565
8566             $elm.html(getter(i18nService.get()) || missing);
8567           }
8568         };
8569       }
8570     };
8571   };
8572
8573   DIRECTIVE_ALIASES.forEach(function (alias) {
8574     module.directive(alias, ['$parse', 'i18nService', 'i18nConstants', uitDirective]);
8575   });
8576
8577   // optional filter syntax
8578   var uitFilter = function ($parse, i18nService, i18nConstants) {
8579     return function (data) {
8580       var getter = $parse(data);
8581       // set text based on i18n current language
8582       return getter(i18nService.get()) || i18nConstants.MISSING + data;
8583     };
8584   };
8585
8586   FILTER_ALIASES.forEach(function (alias) {
8587     module.filter(alias, ['$parse', 'i18nService', 'i18nConstants', uitFilter]);
8588   });
8589
8590 })();
8591 (function () {
8592   angular.module('ui.grid').config(['$provide', function($provide) {
8593     $provide.decorator('i18nService', ['$delegate', function($delegate) {
8594       $delegate.add('zh-cn', {
8595         aggregate: {
8596           label: '条目'
8597         },
8598         groupPanel: {
8599           description: '拖曳表头到此处以进行分组'
8600         },
8601         search: {
8602           placeholder: '搜索...',
8603           showingItems: '当前显示条目:',
8604           selectedItems: '选中条目:',
8605           totalItems: '条目总数:',
8606           size: '每页显示数:',
8607           first: '回到首页',
8608           next: '下一页',
8609           previous: '上一页',
8610           last: '前往尾页'
8611         },
8612         menu: {
8613           text: '数据分组与选择列:'
8614         },
8615         column: {
8616           hide: '隐藏列'
8617         }
8618       });
8619       return $delegate;
8620     }]);
8621 }]);
8622 })();
8623
8624 (function () {
8625   angular.module('ui.grid').config(['$provide', function($provide) {
8626     $provide.decorator('i18nService', ['$delegate', function($delegate) {
8627       $delegate.add('zh-tw', {
8628         aggregate: {
8629           label: '筆'
8630         },
8631         groupPanel: {
8632           description: '拖拉表頭到此處以進行分組'
8633         },
8634         search: {
8635           placeholder: '搜尋...',
8636           showingItems: '目前顯示筆數:',
8637           selectedItems: '選取筆數:',
8638           totalItems: '總筆數:',
8639           size: '每頁顯示:',
8640           first: '第一頁',
8641           next: '下一頁',
8642           previous: '上一頁',
8643           last: '最後頁'
8644         },
8645         menu: {
8646           text: '選擇欄位:'
8647         },
8648         column: {
8649           hide: '隐藏列'
8650         }
8651       });
8652       return $delegate;
8653     }]);
8654 }]);
8655 })();
8656 (function() {
8657   'use strict';
8658   /**
8659    *  @ngdoc overview
8660    *  @name ui.grid.autoResize
8661    *
8662    *  @description 
8663    *
8664    *  #ui.grid.autoResize
8665    *  This module provides auto-resizing functionality to ui-grid
8666    *
8667    */
8668   var module = angular.module('ui.grid.autoResize', ['ui.grid']);
8669   
8670
8671   module.directive('uiGridAutoResize', ['$log', '$timeout', 'gridUtil', function ($log, $timeout, gridUtil) {
8672     return {
8673       require: 'uiGrid',
8674       scope: false,
8675       link: function ($scope, $elm, $attrs, uiGridCtrl) {
8676         var prevGridWidth, prevGridHeight;
8677
8678         function getDimensions() {
8679           prevGridHeight = gridUtil.elementHeight($elm);
8680           prevGridWidth = gridUtil.elementWidth($elm);
8681         }
8682
8683         // Initialize the dimensions
8684         getDimensions();
8685
8686         var canceler;
8687         function startTimeout() {
8688           $timeout.cancel(canceler);
8689
8690           canceler = $timeout(function () {
8691             var newGridHeight = gridUtil.elementHeight($elm);
8692             var newGridWidth = gridUtil.elementWidth($elm);
8693
8694             if (newGridHeight !== prevGridHeight || newGridWidth !== prevGridWidth) {
8695               uiGridCtrl.grid.gridHeight = newGridHeight;
8696               uiGridCtrl.grid.gridWidth = newGridWidth;
8697
8698               uiGridCtrl.grid.queueRefresh()
8699                 .then(function () {
8700                   getDimensions();
8701
8702                   startTimeout();
8703                 });
8704             }
8705             else {
8706               startTimeout();
8707             }
8708           }, 250);
8709         }
8710
8711         startTimeout();
8712
8713         $scope.$on('$destroy', function() {
8714           $timeout.cancel(canceler);
8715         });
8716       }
8717     };
8718   }]);
8719 })();
8720 (function () {
8721   'use strict';
8722   var module = angular.module('ui.grid.cellNav', ['ui.grid']);
8723
8724   function RowCol(row, col) {
8725     this.row = row;
8726     this.col = col;
8727   }
8728
8729   /**
8730    *  @ngdoc object
8731    *  @name ui.grid.cellNav.constant:uiGridCellNavConstants
8732    *
8733    *  @description constants available in cellNav
8734    */
8735   module.constant('uiGridCellNavConstants', {
8736     FEATURE_NAME : 'gridCellNav',
8737     CELL_NAV_EVENT: 'cellNav',
8738     direction: {LEFT: 0, RIGHT: 1, UP: 2, DOWN: 3}
8739   });
8740
8741   /**
8742    *  @ngdoc service
8743    *  @name ui.grid.cellNav.service:uiGridCellNavService
8744    *
8745    *  @description Services for cell navigation features. If you don't like the key maps we use,
8746    *  or the direction cells navigation, override with a service decorator (see angular docs)
8747    */
8748   module.service('uiGridCellNavService', ['$log', 'uiGridConstants', 'uiGridCellNavConstants', '$q',
8749     function ($log, uiGridConstants, uiGridCellNavConstants, $q) {
8750
8751       var service = {
8752
8753         initializeGrid: function (grid) {
8754           grid.registerColumnBuilder(service.cellNavColumnBuilder);
8755
8756           //create variables for state
8757           grid.cellNav = {};
8758           grid.cellNav.lastRowCol = null;
8759
8760           /**
8761            *  @ngdoc object
8762            *  @name ui.grid.cellNav.api:PublicApi
8763            *
8764            *  @description Public Api for cellNav feature
8765            */
8766           var publicApi = {
8767             events: {
8768               cellNav : {
8769                 /**
8770                  * @ngdoc event
8771                  * @name navigate
8772                  * @eventOf  ui.grid.cellNav.api:PublicApi
8773                  * @description raised when the active cell is changed
8774                  * <pre>
8775                  *      gridApi.cellNav.on.navigate(scope,function(newRowcol, oldRowCol){})
8776                  * </pre>
8777                  * @param {object} newRowCol new position
8778                  * @param {object} oldRowCol old position
8779                  */
8780                 navigate : function(newRowCol, oldRowCol){}
8781               }
8782             },
8783             methods: {
8784               cellNav: {
8785                 /**
8786                  * @ngdoc function
8787                  * @name scrollTo
8788                  * @methodOf  ui.grid.cellNav.api:PublicApi
8789                  * @description brings the specified row and column into view
8790                  * @param {Grid} grid the grid you'd like to act upon, usually available
8791                  * from gridApi.grid
8792                  * @param {object} $scope a scope we can broadcast events from
8793                  * @param {object} rowEntity gridOptions.data[] array instance to make visible
8794                  * @param {object} colDef to make visible
8795                  */
8796                 scrollTo: function (grid, $scope, rowEntity, colDef) {
8797                   service.scrollTo(grid, $scope, rowEntity, colDef);
8798                 },
8799                 /**
8800                  * @ngdoc function
8801                  * @name getFocusedCell
8802                  * @methodOf  ui.grid.cellNav.api:PublicApi
8803                  * @description returns the current (or last if Grid does not have focus) focused row and column
8804                  * <br> value is null if no selection has occurred
8805                  */
8806                 getFocusedCell: function () {
8807                   return grid.cellNav.lastRowCol;
8808                 }
8809               }
8810             }
8811           };
8812
8813           grid.api.registerEventsFromObject(publicApi.events);
8814
8815           grid.api.registerMethodsFromObject(publicApi.methods);
8816
8817         },
8818
8819
8820         /**
8821          * @ngdoc service
8822          * @name getDirection
8823          * @methodOf ui.grid.cellNav.service:uiGridCellNavService
8824          * @description  determines which direction to for a given keyDown event
8825          * @returns {uiGridCellNavConstants.direction} direction
8826          */
8827         getDirection: function (evt) {
8828           if (evt.keyCode === uiGridConstants.keymap.LEFT ||
8829             (evt.keyCode === uiGridConstants.keymap.TAB && evt.shiftKey)) {
8830             return uiGridCellNavConstants.direction.LEFT;
8831           }
8832           if (evt.keyCode === uiGridConstants.keymap.RIGHT ||
8833             evt.keyCode === uiGridConstants.keymap.TAB) {
8834             return uiGridCellNavConstants.direction.RIGHT;
8835           }
8836
8837           if (evt.keyCode === uiGridConstants.keymap.UP ||
8838             (evt.keyCode === uiGridConstants.keymap.ENTER && evt.shiftKey)) {
8839             return uiGridCellNavConstants.direction.UP;
8840           }
8841
8842           if (evt.keyCode === uiGridConstants.keymap.DOWN ||
8843             evt.keyCode === uiGridConstants.keymap.ENTER) {
8844             return uiGridCellNavConstants.direction.DOWN;
8845           }
8846
8847           return null;
8848         },
8849
8850         /**
8851          * @ngdoc service
8852          * @name getNextRowCol
8853          * @methodOf ui.grid.cellNav.service:uiGridCellNavService
8854          * @description  returns the next row and column for a given direction
8855          * columns that are not focusable are skipped
8856          * @param {object} direction navigation direction
8857          * @param {Grid} grid current grid
8858          * @param {GridRow} curRow Gridrow
8859          * @param {GridCol} curCol Gridcol
8860          * @returns {uiGridCellNavConstants.direction} rowCol object
8861          */
8862         getNextRowCol: function (direction, grid, curRow, curCol) {
8863           switch (direction) {
8864             case uiGridCellNavConstants.direction.LEFT:
8865               return service.getRowColLeft(grid.rows, grid.columns, curRow, curCol);
8866             case uiGridCellNavConstants.direction.RIGHT:
8867               return service.getRowColRight(grid.rows, grid.columns, curRow, curCol);
8868             case uiGridCellNavConstants.direction.UP:
8869               return service.getRowColUp(grid.rows, grid.columns, curRow, curCol);
8870             case uiGridCellNavConstants.direction.DOWN:
8871               return service.getRowColDown(grid.rows, grid.columns, curRow, curCol);
8872           }
8873         },
8874
8875         getRowColLeft: function (rows, cols, curRow, curCol) {
8876           var colIndex = service.getNextColIndexLeft(cols, curCol);
8877
8878           if (colIndex > curCol.index) {
8879             if (curRow.index === 0) {
8880               return new RowCol(curRow, cols[colIndex]); //return same row
8881             }
8882             else {
8883               //up one row and far right column
8884               return new RowCol(rows[curRow.index - 1], cols[colIndex]);
8885             }
8886           }
8887           else {
8888             return new RowCol(curRow, cols[colIndex]);
8889           }
8890         },
8891
8892         getRowColRight: function (rows, cols, curRow, curCol) {
8893           var colIndex = service.getNextColIndexRight(cols, curCol);
8894
8895           if (colIndex < curCol.index) {
8896             if (curRow.index === rows.length - 1) {
8897               return new RowCol(curRow, cols[colIndex]); //return same row
8898             }
8899             else {
8900               //down one row and far left column
8901               return new RowCol(rows[curRow.index + 1], cols[colIndex]);
8902             }
8903           }
8904           else {
8905             return new RowCol(curRow, cols[colIndex]);
8906           }
8907         },
8908
8909         getNextColIndexLeft: function (cols, curCol) {
8910           //start with next col to the left or the end of the array if curCol is the first col
8911           var i = curCol.index === 0 ? cols.length - 1 : curCol.index - 1;
8912
8913           //find first focusable column to the left
8914           //circle around to the end of the array if no col is found
8915           while (i !== curCol.index) {
8916             if (cols[i].colDef.allowCellFocus) {
8917               break;
8918             }
8919             i--;
8920             //go to end of array if at the beginning
8921             if (i === -1) {
8922               i = cols.length - 1;
8923             }
8924           }
8925
8926           return i;
8927         },
8928
8929         getNextColIndexRight: function (cols, curCol) {
8930           //start with next col to the right or the beginning of the array if curCol is the last col
8931           var i = curCol.index === cols.length - 1 ? 0 : curCol.index + 1;
8932
8933           //find first focusable column to the right
8934           //circle around to the beginning of the array if no col is found
8935           while (i !== curCol.index) {
8936             if (cols[i].colDef.allowCellFocus) {
8937               break;
8938             }
8939             i++;
8940             //go to end of array if at the beginning
8941             if (i > cols.length - 1) {
8942               i = 0;
8943             }
8944           }
8945
8946           return i;
8947         },
8948
8949         getRowColUp: function (rows, cols, curRow, curCol) {
8950           //if curCol is not focusable, then we need to find a focusable column to the right
8951           //this shouldn't ever happen in the grid, but we handle it anyway
8952           var colIndex = curCol.colDef.allowCellFocus ? curCol.index : service.getNextColIndexRight(cols, curCol);
8953
8954
8955           if (curRow.index === 0) {
8956             return new RowCol(curRow, cols[colIndex]); //return same row
8957           }
8958           else {
8959             //up one row
8960             return new RowCol(rows[curRow.index - 1], cols[colIndex]);
8961           }
8962         },
8963
8964         getRowColDown: function (rows, cols, curRow, curCol) {
8965           //if curCol is not focusable, then we need to find a focusable column to the right
8966           //this shouldn't ever happen in the grid, but we handle it anyway
8967           var colIndex = curCol.colDef.allowCellFocus ? curCol.index : service.getNextColIndexRight(cols, curCol);
8968
8969
8970           if (curRow.index === rows.length - 1) {
8971             return new RowCol(curRow, cols[colIndex]); //return same row
8972           }
8973           else {
8974             //down one row
8975             return new RowCol(rows[curRow.index + 1], cols[colIndex]);
8976           }
8977         },
8978
8979         /**
8980          * @ngdoc service
8981          * @name cellNavColumnBuilder
8982          * @methodOf ui.grid.cellNav.service:uiGridCellNavService
8983          * @description columnBuilder function that adds cell navigation properties to grid column
8984          * @returns {promise} promise that will load any needed templates when resolved
8985          */
8986         cellNavColumnBuilder: function (colDef, col, gridOptions) {
8987           var promises = [];
8988
8989           /**
8990            *  @ngdoc object
8991            *  @name ui.grid.cellNav.api:ColumnDef
8992            *
8993            *  @description Column Definitions for cellNav feature, these are available to be 
8994            *  set using the ui-grid {@link ui.grid.class:GridOptions.columnDef gridOptions.columnDefs}
8995            */
8996
8997           /**
8998            *  @ngdoc object
8999            *  @name allowCellFocus
9000            *  @propertyOf  ui.grid.cellNav.api:ColumnDef
9001            *  @description Enable focus on a cell.  
9002            *  <br/>Defaults to true
9003            */
9004           colDef.allowCellFocus = colDef.allowCellFocus === undefined ? true : colDef.allowCellFocus ;
9005
9006           return $q.all(promises);
9007         },
9008         
9009         /**
9010          * @ngdoc method
9011          * @methodOf ui.grid.cellNav.service:uiGridCellNavService
9012          * @name scrollVerticallyTo
9013          * @description Scroll the grid vertically such that the specified
9014          * row is in view
9015          * @param {Grid} grid the grid you'd like to act upon, usually available
9016          * from gridApi.grid
9017          * @param {object} $scope a scope we can broadcast events from
9018          * @param {object} rowEntity gridOptions.data[] array instance to make visible
9019          * @param {object} colDef to make visible
9020          */
9021         scrollTo: function (grid, $scope, rowEntity, colDef) {
9022           var args = {};
9023           
9024           if ( rowEntity !== null ){
9025             var row = grid.getRow(rowEntity);
9026             if ( row ) { 
9027               args.y = { percentage: row.index / grid.renderContainers.body.visibleRowCache.length }; 
9028             }
9029           }
9030           
9031           if ( colDef !== null ){
9032             var col = grid.getColumn(colDef.name ? colDef.name : colDef.field);
9033             if ( col ) {
9034               args.x = { percentage: this.getLeftWidth(grid, col.index) / this.getLeftWidth(grid, grid.renderContainers.body.visibleColumnCache.length - 1) };              
9035             }
9036           }
9037           
9038           if ( args.y || args.x ){
9039             $scope.$broadcast(uiGridConstants.events.GRID_SCROLL, args);
9040           }
9041         },
9042         
9043
9044         /**
9045          * @ngdoc method
9046          * @methodOf ui.grid.cellNav.service:uiGridCellNavService
9047          * @name getLeftWidth
9048          * @description Get the current drawn width of the columns in the 
9049          * grid up to and including the numbered column
9050          * @param {Grid} grid the grid you'd like to act upon, usually available
9051          * from gridApi.grid
9052          * @param {object} colIndex the column to total up to and including
9053          */
9054         getLeftWidth: function( grid, colIndex ){
9055           var width = 0;
9056           
9057           if ( !colIndex ){ return; }
9058           
9059           for ( var i=0; i <= colIndex; i++ ){
9060             if ( grid.renderContainers.body.visibleColumnCache[i].drawnWidth ){
9061               width += grid.renderContainers.body.visibleColumnCache[i].drawnWidth;
9062             } 
9063           }
9064           return width;
9065         }
9066
9067       };
9068
9069       return service;
9070     }]);
9071
9072   /**
9073    *  @ngdoc directive
9074    *  @name ui.grid.cellNav.directive:uiCellNav
9075    *  @element div
9076    *  @restrict EA
9077    *
9078    *  @description Adds cell navigation features to the grid columns
9079    *
9080    *  @example
9081    <example module="app">
9082    <file name="app.js">
9083    var app = angular.module('app', ['ui.grid', 'ui.grid.cellNav']);
9084
9085    app.controller('MainCtrl', ['$scope', function ($scope) {
9086       $scope.data = [
9087         { name: 'Bob', title: 'CEO' },
9088             { name: 'Frank', title: 'Lowly Developer' }
9089       ];
9090
9091       $scope.columnDefs = [
9092         {name: 'name'},
9093         {name: 'title'}
9094       ];
9095     }]);
9096    </file>
9097    <file name="index.html">
9098    <div ng-controller="MainCtrl">
9099    <div ui-grid="{ data: data, columnDefs: columnDefs }" ui-grid-cellnav></div>
9100    </div>
9101    </file>
9102    </example>
9103    */
9104   module.directive('uiGridCellnav', ['$log', 'uiGridCellNavService', 'uiGridCellNavConstants',
9105     function ($log, uiGridCellNavService, uiGridCellNavConstants) {
9106       return {
9107         replace: true,
9108         priority: -150,
9109         require: '^uiGrid',
9110         scope: false,
9111         compile: function () {
9112           return {
9113             pre: function ($scope, $elm, $attrs, uiGridCtrl) {
9114
9115               var grid = uiGridCtrl.grid;
9116               uiGridCellNavService.initializeGrid(grid);
9117
9118               uiGridCtrl.cellNav = {};
9119
9120               //  $log.debug('uiGridEdit preLink');
9121               uiGridCtrl.cellNav.broadcastCellNav = function (newRowCol) {
9122                 $scope.$broadcast(uiGridCellNavConstants.CELL_NAV_EVENT, newRowCol);
9123                 uiGridCtrl.cellNav.broadcastFocus(newRowCol.row, newRowCol.col);
9124               };
9125
9126               uiGridCtrl.cellNav.broadcastFocus = function (row, col) {
9127                 if (grid.cellNav.lastRowCol === null || (grid.cellNav.lastRowCol.row !== row || grid.cellNav.lastRowCol.col !== col)) {
9128                   var newRowCol = new RowCol(row, col);
9129                   grid.api.cellNav.raise.navigate(newRowCol, grid.cellNav.lastRowCol);
9130                   grid.cellNav.lastRowCol = newRowCol;
9131                 }
9132               };
9133
9134             },
9135             post: function ($scope, $elm, $attrs, uiGridCtrl) {
9136             }
9137           };
9138         }
9139       };
9140     }]);
9141
9142
9143   /**
9144    *  @ngdoc directive
9145    *  @name ui.grid.cellNav.directive:uiGridCell
9146    *  @element div
9147    *  @restrict A
9148    *  @description Stacks on top of ui.grid.uiGridCell to provide cell navigation
9149    */
9150   module.directive('uiGridCell', ['uiGridCellNavService', '$log', 'uiGridCellNavConstants',
9151     function (uiGridCellNavService, $log, uiGridCellNavConstants) {
9152       return {
9153         priority: -150, // run after default uiGridCell directive and ui.grid.edit uiGridCell
9154         restrict: 'A',
9155         require: '^uiGrid',
9156         scope: false,
9157         link: function ($scope, $elm, $attrs, uiGridCtrl) {
9158           if (!$scope.col.colDef.allowCellFocus) {
9159              return;
9160           }
9161
9162           setTabEnabled();
9163
9164           $elm.on('keydown', function (evt) {
9165             var direction = uiGridCellNavService.getDirection(evt);
9166             if (direction === null) {
9167               return true;
9168             }
9169
9170             var rowCol = uiGridCellNavService.getNextRowCol(direction, $scope.grid, $scope.row, $scope.col);
9171
9172             //$log.debug('next row ' + rowCol.row.index + ' next Col ' + rowCol.col.colDef.name);
9173             uiGridCtrl.cellNav.broadcastCellNav(rowCol);
9174             setTabEnabled();
9175
9176             return false;
9177           });
9178
9179           $elm.find('div').on('focus', function (evt) {
9180             uiGridCtrl.cellNav.broadcastFocus($scope.row, $scope.col);
9181           });
9182
9183           $scope.$on(uiGridCellNavConstants.CELL_NAV_EVENT, function(evt,rowCol){
9184              if (rowCol.row === $scope.row &&
9185                rowCol.col === $scope.col){
9186                // $log.debug('Setting focus on Row ' + rowCol.row.index + ' Col ' + rowCol.col.colDef.name);
9187                 setFocused();
9188              }
9189           });
9190
9191           function setTabEnabled(){
9192             $elm.find('div').attr("tabindex", -1);
9193           }
9194
9195           function setFocused(){
9196             var div = $elm.find('div');
9197             div[0].focus();
9198             div.attr("tabindex", 0);
9199           }
9200
9201         }
9202       };
9203     }]);
9204
9205 })();
9206 (function () {
9207   'use strict';
9208
9209   /**
9210    * @ngdoc overview
9211    * @name ui.grid.edit
9212    * @description
9213    *
9214    *  # ui.grid.edit
9215    * This module provides cell editing capability to ui.grid. The goal was to emulate keying data in a spreadsheet via
9216    * a keyboard.
9217    * <br/>
9218    * <br/>
9219    * To really get the full spreadsheet-like data entry, the ui.grid.cellNav module should be used. This will allow the
9220    * user to key data and then tab, arrow, or enter to the cells beside or below.
9221    *
9222    * <div doc-module-components="ui.grid.edit"></div>
9223    */
9224
9225   var module = angular.module('ui.grid.edit', ['ui.grid']);
9226
9227   /**
9228    *  @ngdoc object
9229    *  @name ui.grid.edit.constant:uiGridEditConstants
9230    *
9231    *  @description constants available in edit module
9232    */
9233   module.constant('uiGridEditConstants', {
9234     EDITABLE_CELL_TEMPLATE: /EDITABLE_CELL_TEMPLATE/g,
9235     //must be lowercase because template bulder converts to lower
9236     EDITABLE_CELL_DIRECTIVE: /editable_cell_directive/g,
9237     events: {
9238       BEGIN_CELL_EDIT: 'uiGridEventBeginCellEdit',
9239       END_CELL_EDIT: 'uiGridEventEndCellEdit',
9240       CANCEL_CELL_EDIT: 'uiGridEventCancelCellEdit'
9241     }
9242   });
9243
9244   /**
9245    *  @ngdoc service
9246    *  @name ui.grid.edit.service:uiGridEditService
9247    *
9248    *  @description Services for editing features
9249    */
9250   module.service('uiGridEditService', ['$log', '$q', '$templateCache', 'uiGridConstants', 'gridUtil',
9251     function ($log, $q, $templateCache, uiGridConstants, gridUtil) {
9252
9253       var service = {
9254
9255         initializeGrid: function (grid) {
9256
9257           service.defaultGridOptions(grid.options);
9258
9259           grid.registerColumnBuilder(service.editColumnBuilder);
9260
9261           /**
9262            *  @ngdoc object
9263            *  @name ui.grid.edit.api:PublicApi
9264            *
9265            *  @description Public Api for edit feature
9266            */
9267           var publicApi = {
9268             events: {
9269               edit: {
9270                 /**
9271                  * @ngdoc event
9272                  * @name afterCellEdit
9273                  * @eventOf  ui.grid.edit.api:PublicApi
9274                  * @description raised when cell editing is complete
9275                  * <pre>
9276                  *      gridApi.edit.on.afterCellEdit(scope,function(rowEntity, colDef){})
9277                  * </pre>
9278                  * @param {object} rowEntity the options.data element that was edited
9279                  * @param {object} colDef the column that was edited
9280                  * @param {object} newValue new value
9281                  * @param {object} oldValue old value
9282                  */
9283                 afterCellEdit: function (rowEntity, colDef, newValue, oldValue) {
9284                 },
9285                 /**
9286                  * @ngdoc event
9287                  * @name beginCellEdit
9288                  * @eventOf  ui.grid.edit.api:PublicApi
9289                  * @description raised when cell editing starts on a cell
9290                  * <pre>
9291                  *      gridApi.edit.on.beginCellEdit(scope,function(rowEntity, colDef){})
9292                  * </pre>
9293                  * @param {object} rowEntity the options.data element that was edited
9294                  * @param {object} colDef the column that was edited
9295                  */
9296                 beginCellEdit: function (rowEntity, colDef) {
9297                 },
9298                 /**
9299                  * @ngdoc event
9300                  * @name cancelCellEdit
9301                  * @eventOf  ui.grid.edit.api:PublicApi
9302                  * @description raised when cell editing is cancelled on a cell
9303                  * <pre>
9304                  *      gridApi.edit.on.cancelCellEdit(scope,function(rowEntity, colDef){})
9305                  * </pre>
9306                  * @param {object} rowEntity the options.data element that was edited
9307                  * @param {object} colDef the column that was edited
9308                  */
9309                 cancelCellEdit: function (rowEntity, colDef) {
9310                 }                
9311               }
9312             },
9313             methods: {
9314               edit: { }
9315             }
9316           };
9317
9318           grid.api.registerEventsFromObject(publicApi.events);
9319           //grid.api.registerMethodsFromObject(publicApi.methods);
9320
9321         },
9322
9323         defaultGridOptions: function (gridOptions) {
9324
9325           /**
9326            *  @ngdoc object
9327            *  @name ui.grid.edit.api:GridOptions
9328            *
9329            *  @description Options for configuring the edit feature, these are available to be  
9330            *  set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
9331            */
9332
9333           /**
9334            *  @ngdoc object
9335            *  @name enableCellEdit
9336            *  @propertyOf  ui.grid.edit.api:GridOptions
9337            *  @description If defined, sets the default value for the editable flag on each individual colDefs 
9338            *  if their individual enableCellEdit configuration is not defined. Defaults to undefined.  
9339            */
9340
9341           /**
9342            *  @ngdoc object
9343            *  @name cellEditableCondition
9344            *  @propertyOf  ui.grid.edit.api:GridOptions
9345            *  @description If specified, either a value or function to be used by all columns before editing.  
9346            *  If falsy, then editing of cell is not allowed.
9347            *  @example
9348            *  <pre>
9349            *  function($scope){
9350            *    //use $scope.row.entity and $scope.col.colDef to determine if editing is allowed
9351            *    return true;
9352            *  }
9353            *  </pre>
9354            */
9355           gridOptions.cellEditableCondition = gridOptions.cellEditableCondition === undefined ? true : gridOptions.cellEditableCondition;
9356
9357           /**
9358            *  @ngdoc object
9359            *  @name editableCellTemplate
9360            *  @propertyOf  ui.grid.edit.api:GridOptions
9361            *  @description If specified, cellTemplate to use as the editor for all columns.  
9362            *  <br/> defaults to 'ui-grid/cellTextEditor'
9363            */
9364
9365           /**
9366            *  @ngdoc object
9367            *  @name enableCellEditOnFocus
9368            *  @propertyOf  ui.grid.edit.api:GridOptions
9369            *  @description If true, then editor is invoked as soon as cell receives focus. Default false.
9370            *  <br/>_requires cellNav feature and the edit feature to be enabled_
9371            */
9372             //enableCellEditOnFocus can only be used if cellnav module is used
9373           gridOptions.enableCellEditOnFocus = gridOptions.enableCellEditOnFocus === undefined ?
9374             false: gridOptions.enableCellEditOnFocus;
9375         },
9376
9377         /**
9378          * @ngdoc service
9379          * @name editColumnBuilder
9380          * @methodOf ui.grid.edit.service:uiGridEditService
9381          * @description columnBuilder function that adds edit properties to grid column
9382          * @returns {promise} promise that will load any needed templates when resolved
9383          */
9384         editColumnBuilder: function (colDef, col, gridOptions) {
9385
9386           var promises = [];
9387
9388           /**
9389            *  @ngdoc object
9390            *  @name ui.grid.edit.api:ColumnDef
9391            *
9392            *  @description Column Definition for edit feature, these are available to be 
9393            *  set using the ui-grid {@link ui.grid.class:GridOptions.columnDef gridOptions.columnDefs}
9394            */
9395
9396           /**
9397            *  @ngdoc object
9398            *  @name enableCellEdit
9399            *  @propertyOf  ui.grid.edit.api:ColumnDef
9400            *  @description enable editing on column
9401            */
9402           colDef.enableCellEdit = colDef.enableCellEdit === undefined ? (gridOptions.enableCellEdit === undefined ?
9403             (colDef.type !== 'object'):gridOptions.enableCellEdit) : colDef.enableCellEdit;
9404
9405           /**
9406            *  @ngdoc object
9407            *  @name cellEditableCondition
9408            *  @propertyOf  ui.grid.edit.api:ColumnDef
9409            *  @description If specified, either a value or function evaluated before editing cell.  If falsy, then editing of cell is not allowed.
9410            *  @example 
9411            *  <pre>
9412            *  function($scope){
9413            *    //use $scope.row.entity and $scope.col.colDef to determine if editing is allowed
9414            *    return true;
9415            *  }
9416            *  </pre>
9417            */
9418           colDef.cellEditableCondition = colDef.cellEditableCondition === undefined ? gridOptions.cellEditableCondition :  colDef.cellEditableCondition;
9419
9420           /**
9421            *  @ngdoc object
9422            *  @name editableCellTemplate
9423            *  @propertyOf  ui.grid.edit.api:ColumnDef
9424            *  @description cell template to be used when editing this column. Can be Url or text template
9425            *  <br/>Defaults to gridOptions.editableCellTemplate
9426            */
9427           if (colDef.enableCellEdit) {
9428             colDef.editableCellTemplate = colDef.editableCellTemplate || gridOptions.editableCellTemplate || 'ui-grid/cellEditor';
9429
9430             promises.push(gridUtil.getTemplate(colDef.editableCellTemplate)
9431               .then(
9432               function (template) {
9433                 col.editableCellTemplate = template;
9434               },
9435               function (res) {
9436                 // Todo handle response error here?
9437                 throw new Error("Couldn't fetch/use colDef.editableCellTemplate '" + colDef.editableCellTemplate + "'");
9438               }));
9439           }
9440
9441           /**
9442            *  @ngdoc object
9443            *  @name enableCellEditOnFocus
9444            *  @propertyOf  ui.grid.edit.api:ColumnDef
9445            *  @requires ui.grid.cellNav
9446            *  @description If true, then editor is invoked as soon as cell receives focus. Default false.
9447            *  <br>_requires both the cellNav feature and the edit feature to be enabled_
9448            */
9449             //enableCellEditOnFocus can only be used if cellnav module is used
9450           colDef.enableCellEditOnFocus = colDef.enableCellEditOnFocus === undefined ? gridOptions.enableCellEditOnFocus : colDef.enableCellEditOnFocus;
9451
9452           return $q.all(promises);
9453         },
9454
9455         /**
9456          * @ngdoc service
9457          * @name isStartEditKey
9458          * @methodOf ui.grid.edit.service:uiGridEditService
9459          * @description  Determines if a keypress should start editing.  Decorate this service to override with your
9460          * own key events.  See service decorator in angular docs.
9461          * @param {Event} evt keydown event
9462          * @returns {boolean} true if an edit should start
9463          */
9464         isStartEditKey: function (evt) {
9465           if (evt.keyCode === uiGridConstants.keymap.LEFT ||
9466             (evt.keyCode === uiGridConstants.keymap.TAB && evt.shiftKey) ||
9467
9468             evt.keyCode === uiGridConstants.keymap.RIGHT ||
9469             evt.keyCode === uiGridConstants.keymap.TAB ||
9470
9471             evt.keyCode === uiGridConstants.keymap.UP ||
9472             (evt.keyCode === uiGridConstants.keymap.ENTER && evt.shiftKey) ||
9473
9474             evt.keyCode === uiGridConstants.keymap.DOWN ||
9475             evt.keyCode === uiGridConstants.keymap.ENTER) {
9476             return false;
9477
9478           }
9479           return true;
9480         }
9481
9482
9483       };
9484
9485       return service;
9486
9487     }]);
9488
9489   /**
9490    *  @ngdoc directive
9491    *  @name ui.grid.edit.directive:uiGridEdit
9492    *  @element div
9493    *  @restrict A
9494    *
9495    *  @description Adds editing features to the ui-grid directive.
9496    *
9497    *  @example
9498    <example module="app">
9499    <file name="app.js">
9500    var app = angular.module('app', ['ui.grid', 'ui.grid.edit']);
9501
9502    app.controller('MainCtrl', ['$scope', function ($scope) {
9503       $scope.data = [
9504         { name: 'Bob', title: 'CEO' },
9505             { name: 'Frank', title: 'Lowly Developer' }
9506       ];
9507
9508       $scope.columnDefs = [
9509         {name: 'name', enableCellEdit: true},
9510         {name: 'title', enableCellEdit: true}
9511       ];
9512     }]);
9513    </file>
9514    <file name="index.html">
9515    <div ng-controller="MainCtrl">
9516    <div ui-grid="{ data: data, columnDefs: columnDefs }" ui-grid-edit></div>
9517    </div>
9518    </file>
9519    </example>
9520    */
9521   module.directive('uiGridEdit', ['$log', 'uiGridEditService', function ($log, uiGridEditService) {
9522     return {
9523       replace: true,
9524       priority: 0,
9525       require: '^uiGrid',
9526       scope: false,
9527       compile: function () {
9528         return {
9529           pre: function ($scope, $elm, $attrs, uiGridCtrl) {
9530             uiGridEditService.initializeGrid(uiGridCtrl.grid);
9531           },
9532           post: function ($scope, $elm, $attrs, uiGridCtrl) {
9533           }
9534         };
9535       }
9536     };
9537   }]);
9538
9539   /**
9540    *  @ngdoc directive
9541    *  @name ui.grid.edit.directive:uiGridCell
9542    *  @element div
9543    *  @restrict A
9544    *
9545    *  @description Stacks on top of ui.grid.uiGridCell to provide in-line editing capabilities to the cell
9546    *  Editing Actions.
9547    *
9548    *  Binds edit start events to the uiGridCell element.  When the events fire, the gridCell element is appended
9549    *  with the columnDef.editableCellTemplate element ('cellEditor.html' by default).
9550    *
9551    *  The editableCellTemplate should respond to uiGridEditConstants.events.BEGIN\_CELL\_EDIT angular event
9552    *  and do the initial steps needed to edit the cell (setfocus on input element, etc).
9553    *
9554    *  When the editableCellTemplate recognizes that the editing is ended (blur event, Enter key, etc.)
9555    *  it should emit the uiGridEditConstants.events.END\_CELL\_EDIT event.
9556    *
9557    *  If editableCellTemplate recognizes that the editing has been cancelled (esc key)
9558    *  it should emit the uiGridEditConstants.events.CANCEL\_CELL\_EDIT event.  The original value
9559    *  will be set back on the model by the uiGridCell directive.
9560    *
9561    *  Events that invoke editing:
9562    *    - dblclick
9563    *    - F2 keydown (when using cell selection)
9564    *
9565    *  Events that end editing:
9566    *    - Dependent on the specific editableCellTemplate
9567    *    - Standards should be blur and enter keydown
9568    *
9569    *  Events that cancel editing:
9570    *    - Dependent on the specific editableCellTemplate
9571    *    - Standards should be Esc keydown
9572    *
9573    *  Grid Events that end editing:
9574    *    - uiGridConstants.events.GRID_SCROLL
9575    *
9576    */
9577   module.directive('uiGridCell',
9578     ['$compile', 'uiGridConstants', 'uiGridEditConstants', '$log', '$parse', 'uiGridEditService',
9579       function ($compile, uiGridConstants, uiGridEditConstants, $log, $parse, uiGridEditService) {
9580         return {
9581           priority: -100, // run after default uiGridCell directive
9582           restrict: 'A',
9583           scope: false,
9584           link: function ($scope, $elm, $attrs) {
9585             if (!$scope.col.colDef.enableCellEdit) {
9586               return;
9587             }
9588
9589             var html;
9590             var origCellValue;
9591             var inEdit = false;
9592             var isFocusedBeforeEdit = false;
9593             var cellModel;
9594
9595             registerBeginEditEvents();
9596
9597             function registerBeginEditEvents() {
9598               $elm.on('dblclick', beginEdit);
9599               $elm.on('keydown', beginEditKeyDown);
9600               if ($scope.col.colDef.enableCellEditOnFocus) {
9601                 $elm.find('div').on('focus', beginEditFocus);
9602               }
9603             }
9604
9605             function cancelBeginEditEvents() {
9606               $elm.off('dblclick', beginEdit);
9607               $elm.off('keydown', beginEditKeyDown);
9608               if ($scope.col.colDef.enableCellEditOnFocus) {
9609                 $elm.find('div').off('focus', beginEditFocus);
9610               }
9611             }
9612
9613             function beginEditFocus(evt) {
9614               evt.stopPropagation();
9615               beginEdit();
9616             }
9617
9618             function beginEditKeyDown(evt) {
9619               if (uiGridEditService.isStartEditKey(evt)) {
9620                 beginEdit();
9621               }
9622             }
9623
9624             function shouldEdit(col, row) {
9625               return !row.isSaving && 
9626                 ( angular.isFunction(col.colDef.cellEditableCondition) ?
9627                     col.colDef.cellEditableCondition($scope) :
9628                     col.colDef.cellEditableCondition );
9629             }
9630
9631
9632             /**
9633              *  @ngdoc property
9634              *  @name editDropdownOptionsArray
9635              *  @propertyOf ui.grid.edit.api:ColumnDef
9636              *  @description an array of values in the format
9637              *  [ {id: xxx, value: xxx} ], which is populated
9638              *  into the edit dropdown
9639              * 
9640              */
9641             /**
9642              *  @ngdoc property
9643              *  @name editDropdownIdLabel
9644              *  @propertyOf ui.grid.edit.api:ColumnDef
9645              *  @description the label for the "id" field
9646              *  in the editDropdownOptionsArray.  Defaults
9647              *  to 'id'
9648              *  @example
9649              *  <pre>
9650              *    $scope.gridOptions = { 
9651              *      columnDefs: [
9652              *        {name: 'status', editableCellTemplate: 'ui-grid/dropdownEditor', 
9653              *          editDropdownOptionsArray: [{code: 1, status: 'active'}, {code: 2, status: 'inactive'}],
9654              *          editDropdownIdLabel: 'code', editDropdownValueLabel: 'status' }
9655              *      ],
9656              *  </pre>
9657              * 
9658              */
9659             /**
9660              *  @ngdoc property
9661              *  @name editDropdownValueLabel
9662              *  @propertyOf ui.grid.edit.api:ColumnDef
9663              *  @description the label for the "value" field
9664              *  in the editDropdownOptionsArray.  Defaults
9665              *  to 'value'
9666              *  @example
9667              *  <pre>
9668              *    $scope.gridOptions = { 
9669              *      columnDefs: [
9670              *        {name: 'status', editableCellTemplate: 'ui-grid/dropdownEditor', 
9671              *          editDropdownOptionsArray: [{code: 1, status: 'active'}, {code: 2, status: 'inactive'}],
9672              *          editDropdownIdLabel: 'code', editDropdownValueLabel: 'status' }
9673              *      ],
9674              *  </pre>
9675              * 
9676              */
9677             /**
9678              *  @ngdoc property
9679              *  @name editDropdownFilter
9680              *  @propertyOf ui.grid.edit.api:ColumnDef
9681              *  @description A filter that you would like to apply to the values in the options list
9682              *  of the dropdown.  For example if you were using angular-translate you might set this
9683              *  to `'translate'`
9684              *  @example
9685              *  <pre>
9686              *    $scope.gridOptions = { 
9687              *      columnDefs: [
9688              *        {name: 'status', editableCellTemplate: 'ui-grid/dropdownEditor', 
9689              *          editDropdownOptionsArray: [{code: 1, status: 'active'}, {code: 2, status: 'inactive'}],
9690              *          editDropdownIdLabel: 'code', editDropdownValueLabel: 'status', editDropdownFilter: 'translate' }
9691              *      ],
9692              *  </pre>
9693              * 
9694              */
9695             function beginEdit() {
9696               if (!shouldEdit($scope.col, $scope.row)) {
9697                 return;
9698               }
9699
9700               cellModel = $parse($scope.row.getQualifiedColField($scope.col));
9701               //get original value from the cell
9702               origCellValue = cellModel($scope);
9703
9704               html = $scope.col.editableCellTemplate;
9705               html = html.replace(uiGridConstants.COL_FIELD, $scope.row.getQualifiedColField($scope.col));
9706               
9707               var optionFilter = $scope.col.colDef.editDropdownFilter ? '|' + $scope.col.colDef.editDropdownFilter : ''; 
9708               html = html.replace(uiGridConstants.CUSTOM_FILTERS, optionFilter);
9709
9710               $scope.inputType = 'text';
9711               switch ($scope.col.colDef.type){
9712                 case 'boolean':
9713                   $scope.inputType = 'checkbox';
9714                   break;
9715                 case 'number':
9716                   $scope.inputType = 'number';
9717                   break;
9718                 case 'date':
9719                   $scope.inputType = 'date';
9720                   break;
9721               }
9722               
9723               $scope.editDropdownOptionsArray = $scope.col.colDef.editDropdownOptionsArray;
9724               $scope.editDropdownIdLabel = $scope.col.colDef.editDropdownIdLabel ? $scope.col.colDef.editDropdownIdLabel : 'id';  
9725               $scope.editDropdownValueLabel = $scope.col.colDef.editDropdownValueLabel ? $scope.col.colDef.editDropdownValueLabel : 'value';  
9726
9727               var cellElement;
9728               $scope.$apply(function () {
9729                   inEdit = true;
9730                   cancelBeginEditEvents();
9731                   cellElement = $compile(html)($scope.$new());
9732                   var gridCellContentsEl = angular.element($elm.children()[0]);
9733                   isFocusedBeforeEdit = gridCellContentsEl.hasClass(':focus');
9734                   gridCellContentsEl.addClass('ui-grid-cell-contents-hidden');
9735                   $elm.append(cellElement);
9736                 }
9737               );
9738
9739               //stop editing when grid is scrolled
9740               var deregOnGridScroll = $scope.$on(uiGridConstants.events.GRID_SCROLL, function () {
9741                 endEdit(true);
9742                 $scope.grid.api.edit.raise.afterCellEdit($scope.row.entity, $scope.col.colDef, cellModel($scope), origCellValue);
9743                 deregOnGridScroll();
9744               });
9745
9746               //end editing
9747               var deregOnEndCellEdit = $scope.$on(uiGridEditConstants.events.END_CELL_EDIT, function (evt, retainFocus) {
9748                 endEdit(retainFocus);
9749                 $scope.grid.api.edit.raise.afterCellEdit($scope.row.entity, $scope.col.colDef, cellModel($scope), origCellValue);
9750                 deregOnEndCellEdit();
9751               });
9752
9753               //cancel editing
9754               var deregOnCancelCellEdit = $scope.$on(uiGridEditConstants.events.CANCEL_CELL_EDIT, function () {
9755                 cancelEdit();
9756                 deregOnCancelCellEdit();
9757               });
9758
9759               $scope.$broadcast(uiGridEditConstants.events.BEGIN_CELL_EDIT);
9760               $scope.grid.api.edit.raise.beginCellEdit($scope.row.entity, $scope.col.colDef);
9761             }
9762
9763             function endEdit(retainFocus) {
9764               if (!inEdit) {
9765                 return;
9766               }
9767               var gridCellContentsEl = angular.element($elm.children()[0]);
9768               //remove edit element
9769               angular.element($elm.children()[1]).remove();
9770               gridCellContentsEl.removeClass('ui-grid-cell-contents-hidden');
9771               if (retainFocus && isFocusedBeforeEdit) {
9772                 gridCellContentsEl.focus();
9773               }
9774               isFocusedBeforeEdit = false;
9775               inEdit = false;
9776               registerBeginEditEvents();
9777             }
9778
9779             function cancelEdit() {
9780               if (!inEdit) {
9781                 return;
9782               }
9783               cellModel.assign($scope, origCellValue);
9784               $scope.$apply();
9785
9786               $scope.grid.api.edit.raise.cancelCellEdit($scope.row.entity, $scope.col.colDef);
9787               endEdit(true);
9788             }
9789
9790           }
9791         };
9792       }]);
9793
9794   /**
9795    *  @ngdoc directive
9796    *  @name ui.grid.edit.directive:uiGridEditor
9797    *  @element div
9798    *  @restrict A
9799    *
9800    *  @description input editor directive for editable fields.
9801    *  Provides EndEdit and CancelEdit events
9802    *
9803    *  Events that end editing:
9804    *     blur and enter keydown
9805    *
9806    *  Events that cancel editing:
9807    *    - Esc keydown
9808    *
9809    */
9810   module.directive('uiGridEditor',
9811     ['uiGridConstants', 'uiGridEditConstants',
9812       function (uiGridConstants, uiGridEditConstants) {
9813         return {
9814           scope: true,
9815           compile: function () {
9816             return {
9817               pre: function ($scope, $elm, $attrs) {
9818
9819               },
9820               post: function ($scope, $elm, $attrs) {
9821
9822                 //set focus at start of edit
9823                 $scope.$on(uiGridEditConstants.events.BEGIN_CELL_EDIT, function () {
9824                   $elm[0].focus();
9825                   $elm[0].select();
9826                   $elm.on('blur', function (evt) {
9827                     $scope.stopEdit(evt);
9828                   });
9829                 });
9830
9831                
9832                $scope.deepEdit = false;
9833                
9834                $scope.stopEdit = function (evt) {
9835                   if ($scope.inputForm && !$scope.inputForm.$valid) {
9836                     evt.stopPropagation();
9837                     $scope.$emit(uiGridEditConstants.events.CANCEL_CELL_EDIT);
9838                   }
9839                   else {
9840                     $scope.$emit(uiGridEditConstants.events.END_CELL_EDIT);
9841                   }
9842                   $scope.deepEdit = false;
9843                 };
9844
9845                 $elm.on('click', function (evt) {
9846                   $scope.deepEdit = true;
9847                 });
9848
9849                 $elm.on('keydown', function (evt) {
9850                   switch (evt.keyCode) {
9851                     case uiGridConstants.keymap.ESC:
9852                       evt.stopPropagation();
9853                       $scope.$emit(uiGridEditConstants.events.CANCEL_CELL_EDIT);
9854                       break;
9855                     case uiGridConstants.keymap.ENTER: // Enter (Leave Field)
9856                       $scope.stopEdit(evt);
9857                       break;
9858                     case uiGridConstants.keymap.TAB:
9859                       $scope.stopEdit(evt);
9860                       break;
9861                   }
9862
9863                   if ($scope.deepEdit) {
9864                     switch (evt.keyCode) {
9865                       case uiGridConstants.keymap.LEFT:
9866                         evt.stopPropagation();
9867                         break;
9868                       case uiGridConstants.keymap.RIGHT:
9869                         evt.stopPropagation();
9870                         break;
9871                       case uiGridConstants.keymap.UP:
9872                         evt.stopPropagation();
9873                         break;
9874                       case uiGridConstants.keymap.DOWN:
9875                         evt.stopPropagation();
9876                         break;
9877                     }
9878                   }
9879
9880                   return true;
9881                 });
9882               }
9883             };
9884           }
9885         };
9886       }]);
9887
9888   /**
9889    *  @ngdoc directive
9890    *  @name ui.grid.edit.directive:input
9891    *  @element input
9892    *  @restrict E
9893    *
9894    *  @description directive to provide binding between input[date] value and ng-model for angular 1.2
9895    *  It is similar to input[date] directive of angular 1.3
9896    *
9897    *  Supported date format for input is 'yyyy-MM-dd'
9898    *  The directive will set the $valid property of input element and the enclosing form to false if
9899    *  model is invalid date or value of input is entered wrong.
9900    *
9901    */
9902     module.directive('input', ['$filter', function ($filter) {
9903       function parseDateString(dateString) {
9904         if ('undefined' === typeof dateString || '' === dateString) {
9905           return null;
9906         }
9907         var parts = dateString.split('-');
9908         if (3 !== parts.length) {
9909           return null;
9910         }
9911         var year = parseInt(parts[0], 10);
9912         var month = parseInt(parts[1], 10);
9913         var day = parseInt(parts[2], 10);
9914
9915         if (month < 1 || year < 1 || day < 1) {
9916           return null;
9917         }
9918         return new Date(year, (month - 1), day);
9919       }
9920       return {
9921         restrict: 'E',
9922         require: '?ngModel',
9923         link: function (scope, element, attrs, ngModel) {
9924
9925           if (angular.version.minor === 2 && attrs.type && 'date' === attrs.type && ngModel) {
9926
9927             ngModel.$formatters.push(function (modelValue) {
9928               ngModel.$setValidity(null,(!modelValue || !isNaN(modelValue.getTime())));
9929               return $filter('date')(modelValue, 'yyyy-MM-dd');
9930             });
9931
9932             ngModel.$parsers.push(function (viewValue) {
9933               if (viewValue && viewValue.length > 0) {
9934                 var dateValue = parseDateString(viewValue);
9935                 ngModel.$setValidity(null, (dateValue && !isNaN(dateValue.getTime())));
9936                 return dateValue;
9937               }
9938               else {
9939                 ngModel.$setValidity(null, true);
9940                 return null;
9941               }
9942             });
9943           }
9944         }
9945       };
9946     }]);
9947     
9948     
9949   /**
9950    *  @ngdoc directive
9951    *  @name ui.grid.edit.directive:uiGridEditDropdown
9952    *  @element div
9953    *  @restrict A
9954    *
9955    *  @description dropdown editor for editable fields.
9956    *  Provides EndEdit and CancelEdit events
9957    *
9958    *  Events that end editing:
9959    *     blur and enter keydown, and any left/right nav
9960    *
9961    *  Events that cancel editing:
9962    *    - Esc keydown
9963    *
9964    */
9965   module.directive('uiGridEditDropdown',
9966     ['uiGridConstants', 'uiGridEditConstants',
9967       function (uiGridConstants, uiGridEditConstants) {
9968         return {
9969           scope: true,
9970           compile: function () {
9971             return {
9972               pre: function ($scope, $elm, $attrs) {
9973
9974               },
9975               post: function ($scope, $elm, $attrs) {
9976
9977                 //set focus at start of edit
9978                 $scope.$on(uiGridEditConstants.events.BEGIN_CELL_EDIT, function () {
9979                   $elm[0].focus();
9980                   $elm[0].style.width = ($elm[0].parentElement.offsetWidth - 1) + 'px';
9981                   $elm.on('blur', function (evt) {
9982                     $scope.stopEdit(evt);
9983                   });
9984                 });
9985
9986                
9987                 $scope.stopEdit = function (evt) {
9988                   // no need to validate a dropdown - invalid values shouldn't be
9989                   // available in the list
9990                   $scope.$emit(uiGridEditConstants.events.END_CELL_EDIT);
9991                 };
9992
9993                 $elm.on('keydown', function (evt) {
9994                   switch (evt.keyCode) {
9995                     case uiGridConstants.keymap.ESC:
9996                       evt.stopPropagation();
9997                       $scope.$emit(uiGridEditConstants.events.CANCEL_CELL_EDIT);
9998                       break;
9999                     case uiGridConstants.keymap.ENTER: // Enter (Leave Field)
10000                       $scope.stopEdit(evt);
10001                       break;
10002                     case uiGridConstants.keymap.LEFT:
10003                       $scope.stopEdit(evt);
10004                       break;
10005                     case uiGridConstants.keymap.RIGHT:
10006                       $scope.stopEdit(evt);
10007                       break;
10008                     case uiGridConstants.keymap.UP:
10009                       evt.stopPropagation();
10010                       break;
10011                     case uiGridConstants.keymap.DOWN:
10012                       evt.stopPropagation();
10013                       break;
10014                     case uiGridConstants.keymap.TAB:
10015                       $scope.stopEdit(evt);
10016                       break;
10017                   }
10018                   return true;
10019                 });
10020               }
10021             };
10022           }
10023         };
10024       }]);    
10025
10026 })();
10027
10028 (function () {
10029   'use strict';
10030
10031   var module = angular.module('ui.grid.expandable', ['ui.grid']);
10032
10033   module.service('uiGridExpandableService', ['gridUtil', '$log', '$compile', function (gridUtil, $log, $compile) {
10034     var service = {
10035       initializeGrid: function (grid) {
10036         var publicApi = {
10037           events: {
10038             expandable: {
10039               rowExpandedStateChanged: function (scope, row) {
10040               }
10041             }
10042           },
10043           methods: {
10044             expandable: {
10045               toggleRowExpansion: function (rowEntity) {
10046                 var row = grid.getRow(rowEntity);
10047                 if (row !== null) {
10048                   service.toggleRowExpansion(grid, row);
10049                 }
10050               },
10051               expandAllRows: function() {
10052                 service.expandAllRows(grid);
10053               },
10054               collapseAllRows: function() {
10055                 service.collapseAllRows(grid);
10056               }
10057             }
10058           }
10059         };
10060         grid.api.registerEventsFromObject(publicApi.events);
10061         grid.api.registerMethodsFromObject(publicApi.methods);
10062       },
10063       toggleRowExpansion: function (grid, row) {
10064         row.isExpanded = !row.isExpanded;
10065
10066         if (row.isExpanded) {
10067           row.height = row.grid.options.rowHeight + grid.options.expandable.expandableRowHeight;
10068         }
10069         else {
10070           row.height = row.grid.options.rowHeight;
10071         }
10072
10073         grid.api.expandable.raise.rowExpandedStateChanged(row);
10074       },
10075       expandAllRows: function(grid, $scope) {
10076         angular.forEach(grid.renderContainers.body.visibleRowCache, function(row) {
10077           if (!row.isExpanded) {
10078             service.toggleRowExpansion(grid, row);
10079           }
10080         });
10081         grid.refresh();
10082       },
10083       collapseAllRows: function(grid) {
10084         angular.forEach(grid.renderContainers.body.visibleRowCache, function(row) {
10085           if (row.isExpanded) {
10086             service.toggleRowExpansion(grid, row);
10087           }
10088         });
10089         grid.refresh();
10090       }
10091     };
10092     return service;
10093   }]);
10094
10095   module.directive('uiGridExpandable', ['$log', 'uiGridExpandableService', '$templateCache',
10096     function ($log, uiGridExpandableService, $templateCache) {
10097       return {
10098         replace: true,
10099         priority: 0,
10100         require: '^uiGrid',
10101         scope: false,
10102         compile: function () {
10103           return {
10104             pre: function ($scope, $elm, $attrs, uiGridCtrl) {
10105               var expandableRowHeaderColDef = {name: 'expandableButtons', width: 40};
10106               expandableRowHeaderColDef.cellTemplate = $templateCache.get('ui-grid/expandableRowHeader');
10107               uiGridCtrl.grid.addRowHeaderColumn(expandableRowHeaderColDef);
10108               uiGridExpandableService.initializeGrid(uiGridCtrl.grid);
10109             },
10110             post: function ($scope, $elm, $attrs, uiGridCtrl) {
10111             }
10112           };
10113         }
10114       };
10115     }]);
10116
10117   module.directive('uiGridExpandableRow',
10118   ['uiGridExpandableService', '$timeout', '$log', '$compile', 'uiGridConstants','gridUtil','$interval',
10119     function (uiGridExpandableService, $timeout, $log, $compile, uiGridConstants, gridUtil, $interval) {
10120
10121       return {
10122         replace: false,
10123         priority: 0,
10124         scope: false,
10125
10126         compile: function () {
10127           return {
10128             pre: function ($scope, $elm, $attrs, uiGridCtrl) {
10129               gridUtil.getTemplate($scope.grid.options.expandable.rowExpandableTemplate).then(
10130                 function (template) {
10131                   var expandedRowElement = $compile(template)($scope);
10132                   $elm.append(expandedRowElement);
10133                   $scope.row.expandedRendered = true;
10134               });
10135             },
10136
10137             post: function ($scope, $elm, $attrs, uiGridCtrl) {
10138               $scope.$on('$destroy', function() {
10139                 $scope.row.expandedRendered = false;
10140               });
10141             }
10142           };
10143         }
10144       };
10145     }]);
10146
10147   module.directive('uiGridRow',
10148     ['$compile', '$log', '$templateCache',
10149       function ($compile, $log, $templateCache) {
10150         return {
10151           priority: -200,
10152           scope: false,
10153           compile: function ($elm, $attrs) {
10154             return {
10155               pre: function ($scope, $elm, $attrs, controllers) {
10156
10157                 $scope.expandableRow = {};
10158
10159                 $scope.expandableRow.shouldRenderExpand = function () {
10160                   var ret = $scope.colContainer.name === 'body' &&  $scope.row.isExpanded && (!$scope.grid.isScrollingVertically || $scope.row.expandedRendered);
10161                   return ret;
10162                 };
10163
10164                 $scope.expandableRow.shouldRenderFiller = function () {
10165                   var ret = $scope.row.isExpanded && ( $scope.colContainer.name !== 'body' || ($scope.grid.isScrollingVertically && !$scope.row.expandedRendered));
10166                   return ret;
10167                 };
10168
10169               },
10170               post: function ($scope, $elm, $attrs, controllers) {
10171               }
10172             };
10173           }
10174         };
10175       }]);
10176
10177   module.directive('uiGridViewport',
10178     ['$compile', '$log', '$templateCache',
10179       function ($compile, $log, $templateCache) {
10180         return {
10181           priority: -200,
10182           scope: false,
10183           compile: function ($elm, $attrs) {
10184             var rowRepeatDiv = angular.element($elm.children().children()[0]);
10185             var expandedRowFillerElement = $templateCache.get('ui-grid/expandableScrollFiller');
10186             var expandedRowElement = $templateCache.get('ui-grid/expandableRow');
10187             rowRepeatDiv.append(expandedRowElement);
10188             rowRepeatDiv.append(expandedRowFillerElement);
10189             return {
10190               pre: function ($scope, $elm, $attrs, controllers) {
10191               },
10192               post: function ($scope, $elm, $attrs, controllers) {
10193               }
10194             };
10195           }
10196         };
10197       }]);
10198
10199 })();
10200
10201 (function () {
10202   'use strict';
10203
10204   /**
10205    * @ngdoc overview
10206    * @name ui.grid.exporter
10207    * @description
10208    *
10209    *  # ui.grid.exporter
10210    * This module provides the ability to exporter data from the grid.  
10211    * 
10212    * Data can be exported in a range of formats, and all data, visible 
10213    * data, or selected rows can be exported, with all columns or visible
10214    * columns.
10215    * 
10216    * No UI is provided, the caller should provide their own UI/buttons 
10217    * as appropriate.
10218    * 
10219    * <br/>
10220    * <br/>
10221    *
10222    * <div doc-module-components="ui.grid.exporter"></div>
10223    */
10224
10225   var module = angular.module('ui.grid.exporter', ['ui.grid']);
10226
10227   /**
10228    *  @ngdoc object
10229    *  @name ui.grid.exporter.constant:uiGridExporterConstants
10230    *
10231    *  @description constants available in exporter module
10232    */
10233   /**
10234    * @ngdoc property
10235    * @propertyOf ui.grid.exporter.constant:uiGridExporterConstants
10236    * @name ALL
10237    * @description export all data, including data not visible.  Can
10238    * be set for either rowTypes or colTypes
10239    */
10240   /**
10241    * @ngdoc property
10242    * @propertyOf ui.grid.exporter.constant:uiGridExporterConstants
10243    * @name VISIBLE
10244    * @description export only visible data, including data not visible.  Can
10245    * be set for either rowTypes or colTypes
10246    */
10247   /**
10248    * @ngdoc property
10249    * @propertyOf ui.grid.exporter.constant:uiGridExporterConstants
10250    * @name SELECTED
10251    * @description export all data, including data not visible.  Can
10252    * be set only for rowTypes, selection of only some columns is 
10253    * not supported
10254    */
10255   module.constant('uiGridExporterConstants', {
10256     featureName: 'exporter',
10257     ALL: 'all',
10258     VISIBLE: 'visible',
10259     SELECTED: 'selected',
10260     CSV_CONTENT: 'CSV_CONTENT',
10261     LINK_LABEL: 'LINK_LABEL',
10262     BUTTON_LABEL: 'BUTTON_LABEL'
10263   });
10264
10265   /**
10266    *  @ngdoc service
10267    *  @name ui.grid.exporter.service:uiGridExporterService
10268    *
10269    *  @description Services for exporter feature
10270    */
10271   module.service('uiGridExporterService', ['$log', '$q', 'uiGridExporterConstants', 'gridUtil', '$compile',
10272     function ($log, $q, uiGridExporterConstants, gridUtil, $compile) {
10273
10274       var service = {
10275
10276         initializeGrid: function (grid) {
10277
10278           //add feature namespace and any properties to grid for needed state
10279           grid.exporter = {};
10280           this.defaultGridOptions(grid.options);
10281
10282           /**
10283            *  @ngdoc object
10284            *  @name ui.grid.exporter.api:PublicApi
10285            *
10286            *  @description Public Api for exporter feature
10287            */
10288           var publicApi = {
10289             events: {
10290               exporter: {
10291               }
10292             },
10293             methods: {
10294               exporter: {
10295                 /**
10296                  * @ngdoc function
10297                  * @name csvExport
10298                  * @methodOf  ui.grid.exporter.api:PublicApi
10299                  * @description Exports rows from the grid in csv format, 
10300                  * the data exported is selected based on the provided options
10301                  * @param {string} rowTypes which rows to export, valid values are
10302                  * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10303                  * uiGridExporterConstants.SELECTED
10304                  * @param {string} colTypes which columns to export, valid values are
10305                  * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE
10306                  * @param {element} $elm (Optional) A UI element into which the
10307                  * resulting download link will be placed. 
10308                  */
10309                 csvExport: function (rowTypes, colTypes, $elm) {
10310                   service.csvExport(grid, rowTypes, colTypes, $elm);
10311                 },
10312                 /**
10313                  * @ngdoc function
10314                  * @name pdfExport
10315                  * @methodOf  ui.grid.exporter.api:PublicApi
10316                  * @description Exports rows from the grid in pdf format, 
10317                  * the data exported is selected based on the provided options
10318                  * Note that this function has a dependency on pdfMake, all
10319                  * going well this has been installed for you.
10320                  * The resulting pdf opens in a new browser window.
10321                  * @param {string} rowTypes which rows to export, valid values are
10322                  * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10323                  * uiGridExporterConstants.SELECTED
10324                  * @param {string} colTypes which columns to export, valid values are
10325                  * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE
10326                  */
10327                 pdfExport: function (rowTypes, colTypes) {
10328                   service.pdfExport(grid, rowTypes, colTypes);
10329                 }
10330               }
10331             }
10332           };
10333
10334           grid.api.registerEventsFromObject(publicApi.events);
10335
10336           grid.api.registerMethodsFromObject(publicApi.methods);
10337
10338         },
10339
10340         defaultGridOptions: function (gridOptions) {
10341           //default option to true unless it was explicitly set to false
10342           /**
10343            * @ngdoc object
10344            * @name ui.grid.exporter.api:GridOptions
10345            *
10346            * @description GridOptions for selection feature, these are available to be  
10347            * set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
10348            */
10349
10350           /**
10351            * @ngdoc object
10352            * @name exporterSuppressButton
10353            * @propertyOf  ui.grid.exporter.api:GridOptions
10354            * @description Don't show the export menu button, implying the user
10355            * will roll their own UI for calling the exporter
10356            * <br/>Defaults to false
10357            */
10358           gridOptions.exporterSuppressButton = gridOptions.exporterSuppressButton === true;
10359           /**
10360            * @ngdoc object
10361            * @name exporterLinkTemplate
10362            * @propertyOf  ui.grid.exporter.api:GridOptions
10363            * @description A custom template to use for the resulting
10364            * link (for csv export)
10365            * <br/>Defaults to ui-grid/csvLink
10366            */
10367           gridOptions.exporterLinkTemplate = gridOptions.exporterLinkTemplate ? gridOptions.exporterLinkTemplate : 'ui-grid/csvLink';
10368           /**
10369            * @ngdoc object
10370            * @name exporterHeaderTemplate
10371            * @propertyOf  ui.grid.exporter.api:GridOptions
10372            * @description A custom template to use for the header
10373            * section, containing the button and csv download link.  Not
10374            * needed if you've set suppressButton and are providing a custom
10375            * $elm into which the download link will go.
10376            * <br/>Defaults to ui-grid/exporterHeader
10377            */
10378           gridOptions.exporterHeaderTemplate = gridOptions.exporterHeaderTemplate ? gridOptions.exporterHeaderTemplate : 'ui-grid/exporterHeader';
10379           /**
10380            * @ngdoc object
10381            * @name exporterLinkLabel
10382            * @propertyOf  ui.grid.exporter.api:GridOptions
10383            * @description The text to show on the CSV download
10384            * link
10385            * <br/>Defaults to 'Download CSV'
10386            */
10387           gridOptions.exporterLinkLabel = gridOptions.exporterLinkLabel ? gridOptions.exporterLinkLabel : 'Download CSV';
10388           /**
10389            * @ngdoc object
10390            * @name exporterButtonLabel
10391            * @propertyOf  ui.grid.exporter.api:GridOptions
10392            * @description The text to show on the exporter menu button
10393            * link
10394            * <br/>Defaults to 'Export'
10395            */
10396           gridOptions.exporterButtonLabel = gridOptions.exporterButtonLabel ? gridOptions.exporterButtonLabel : 'Export';
10397           /**
10398            * @ngdoc object
10399            * @name exporterPdfDefaultStyle
10400            * @propertyOf  ui.grid.exporter.api:GridOptions
10401            * @description The default style in pdfMake format
10402            * <br/>Defaults to:
10403            * <pre>
10404            *   {
10405            *     fontSize: 11
10406            *   }
10407            * </pre>
10408            */
10409           gridOptions.exporterPdfDefaultStyle = gridOptions.exporterPdfDefaultStyle ? gridOptions.exporterPdfDefaultStyle : { fontSize: 11 };
10410           /**
10411            * @ngdoc object
10412            * @name exporterPdfTableStyle
10413            * @propertyOf  ui.grid.exporter.api:GridOptions
10414            * @description The table style in pdfMake format
10415            * <br/>Defaults to:
10416            * <pre>
10417            *   {
10418            *     margin: [0, 5, 0, 15]
10419            *   }
10420            * </pre>
10421            */
10422           gridOptions.exporterPdfTableStyle = gridOptions.exporterPdfTableStyle ? gridOptions.exporterPdfTableStyle : { margin: [0, 5, 0, 15] };
10423           /**
10424            * @ngdoc object
10425            * @name exporterPdfTableHeaderStyle
10426            * @propertyOf  ui.grid.exporter.api:GridOptions
10427            * @description The tableHeader style in pdfMake format
10428            * <br/>Defaults to:
10429            * <pre>
10430            *   {
10431            *     bold: true,
10432            *     fontSize: 12,
10433            *     color: 'black'
10434            *   }
10435            * </pre>
10436            */
10437           gridOptions.exporterPdfTableHeaderStyle = gridOptions.exporterPdfTableHeaderStyle ? gridOptions.exporterPdfTableHeaderStyle : { bold: true, fontSize: 12, color: 'black' };
10438           /**
10439            * @ngdoc object
10440            * @name exporterPdfOrientation
10441            * @propertyOf  ui.grid.exporter.api:GridOptions
10442            * @description The orientation, should be a valid pdfMake value,
10443            * 'landscape' or 'portrait'
10444            * <br/>Defaults to landscape
10445            */
10446           gridOptions.exporterPdfOrientation = gridOptions.exporterPdfOrientation ? gridOptions.exporterPdfOrientation : 'landscape';
10447           /**
10448            * @ngdoc object
10449            * @name exporterPdfPageSize
10450            * @propertyOf  ui.grid.exporter.api:GridOptions
10451            * @description The orientation, should be a valid pdfMake
10452            * paper size, usually 'A4' or 'LETTER'
10453            * {@link https://github.com/bpampuch/pdfmake/blob/master/src/standardPageSizes.js pdfMake page sizes}
10454            * <br/>Defaults to A4
10455            */
10456           gridOptions.exporterPdfPageSize = gridOptions.exporterPdfPageSize ? gridOptions.exporterPdfPageSize : 'A4';
10457           /**
10458            * @ngdoc object
10459            * @name exporterPdfMaxGridWidth
10460            * @propertyOf  ui.grid.exporter.api:GridOptions
10461            * @description The maxium grid width - the current grid width 
10462            * will be scaled to match this, with any fixed width columns
10463            * being adjusted accordingly.
10464            * <br/>Defaults to 720 (for A4 landscape), use 670 for LETTER 
10465            */
10466           gridOptions.exporterPdfMaxGridWidth = gridOptions.exporterPdfMaxGridWidth ? gridOptions.exporterPdfMaxGridWidth : 720;
10467           /**
10468            * @ngdoc object
10469            * @name exporterPdfTableLayout
10470            * @propertyOf  ui.grid.exporter.api:GridOptions
10471            * @description A tableLayout in pdfMake format, 
10472            * controls gridlines and the like.  We use the default
10473            * layout usually.
10474            * <br/>Defaults to null, which means no layout 
10475            */
10476
10477         },
10478
10479
10480         /**
10481          * @ngdoc function
10482          * @name showMenu
10483          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10484          * @description Shows the grid menu with exporter content,
10485          * allowing the user to select export options 
10486          * @param {Grid} grid the grid from which data should be exported
10487          */
10488         showMenu: function ( grid ) {
10489           grid.exporter.$scope.menuItems = [
10490             {
10491               title: 'Export all data as csv',
10492               action: function ($event) {
10493                 this.grid.api.exporter.csvExport( uiGridExporterConstants.ALL, uiGridExporterConstants.ALL );
10494               }
10495             },
10496             {
10497               title: 'Export visible data as csv',
10498               action: function ($event) {
10499                 this.grid.api.exporter.csvExport( uiGridExporterConstants.VISIBLE, uiGridExporterConstants.VISIBLE );
10500               }
10501             },
10502             {
10503               title: 'Export selected data as csv',
10504               action: function ($event) {
10505                 this.grid.api.exporter.csvExport( uiGridExporterConstants.SELECTED, uiGridExporterConstants.VISIBLE );
10506               }
10507             },
10508             {
10509               title: 'Export all data as pdf',
10510               action: function ($event) {
10511                 this.grid.api.exporter.pdfExport( uiGridExporterConstants.ALL, uiGridExporterConstants.ALL );
10512               }
10513             },
10514             {
10515               title: 'Export visible data as pdf',
10516               action: function ($event) {
10517                 this.grid.api.exporter.pdfExport( uiGridExporterConstants.VISIBLE, uiGridExporterConstants.VISIBLE );
10518               }
10519             },
10520             {
10521               title: 'Export selected data as pdf',
10522               action: function ($event) {
10523                 this.grid.api.exporter.pdfExport( uiGridExporterConstants.SELECTED, uiGridExporterConstants.VISIBLE );
10524               }
10525             }
10526           ];
10527           
10528           grid.exporter.$scope.$broadcast('toggleExporterMenu');          
10529         },
10530         
10531
10532         /**
10533          * @ngdoc function
10534          * @name csvExport
10535          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10536          * @description Exports rows from the grid in csv format, 
10537          * the data exported is selected based on the provided options
10538          * @param {Grid} grid the grid from which data should be exported
10539          * @param {string} rowTypes which rows to export, valid values are
10540          * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10541          * uiGridExporterConstants.SELECTED
10542          * @param {string} colTypes which columns to export, valid values are
10543          * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10544          * uiGridExporterConstants.SELECTED
10545          * @param {element} $elm (Optional) A UI element into which the
10546          * resulting download link will be placed. 
10547          */
10548         csvExport: function (grid, rowTypes, colTypes, $elm) {
10549           var exportColumnHeaders = this.getColumnHeaders(grid, colTypes);
10550           var exportData = this.getData(grid, rowTypes, colTypes);
10551           var csvContent = this.formatAsCsv(exportColumnHeaders, exportData);
10552           this.renderCsvLink(grid, csvContent, $elm);
10553           
10554           // this.grid.exporter.$scope.$broadcast('clearExporterMenu');
10555         },
10556         
10557         
10558         /**
10559          * @ngdoc function
10560          * @name getColumnHeaders
10561          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10562          * @description Gets the column headers from the grid to use
10563          * as a title row for the exported file, all headers have 
10564          * headerCellFilters applied as appropriate.
10565          * 
10566          * TODO: filters
10567          * 
10568          * Column headers are an array of objects, each object has
10569          * name, displayName, width and align attributes.  Only name is
10570          * used for csv, all attributes are used for pdf.
10571          * 
10572          * @param {Grid} grid the grid from which data should be exported
10573          * @param {string} colTypes which columns to export, valid values are
10574          * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10575          * uiGridExporterConstants.SELECTED
10576          */
10577         getColumnHeaders: function (grid, colTypes) {
10578           var headers = [];
10579           angular.forEach(grid.columns, function( gridCol, index ) {
10580             if (gridCol.visible || colTypes === uiGridExporterConstants.ALL){
10581               headers.push({
10582                 name: gridCol.field,
10583                 displayName: gridCol.displayName,
10584                 // TODO: should we do something to normalise here if too wide?
10585                 width: gridCol.drawnWidth ? gridCol.drawnWidth : gridCol.width,
10586                 // TODO: if/when we have an alignment attribute, use it here
10587                 align: gridCol.colDef.type === 'number' ? 'right' : 'left'
10588               });
10589             }
10590           });
10591           
10592           return headers;
10593         },
10594         
10595         
10596         /**
10597          * @ngdoc function
10598          * @name getData
10599          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10600          * @description Gets data from the grid based on the provided options,
10601          * all cells have cellFilters applied as appropriate
10602          * @param {Grid} grid the grid from which data should be exported
10603          * @param {string} rowTypes which rows to export, valid values are
10604          * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10605          * uiGridExporterConstants.SELECTED
10606          * @param {string} colTypes which columns to export, valid values are
10607          * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10608          * uiGridExporterConstants.SELECTED
10609          */
10610         getData: function (grid, rowTypes, colTypes) {
10611           var data = [];
10612           
10613           var rows;
10614           
10615           switch ( rowTypes ) {
10616             case uiGridExporterConstants.ALL:
10617               rows = grid.rows; 
10618               break;
10619             case uiGridExporterConstants.VISIBLE:
10620               rows = grid.getVisibleRows();
10621               break;
10622             case uiGridExporterConstants.SELECTED:
10623               if ( grid.api.selection ){
10624                 rows = grid.api.selection.getSelectedGridRows();
10625               } else {
10626                 $log.error('selection feature must be enabled to allow selected rows to be exported');
10627               }
10628               break;
10629           }
10630           
10631           if ( uiGridExporterConstants.ALL ) {
10632             angular.forEach(rows, function( row, index ) {
10633
10634               var extractedRow = [];
10635               angular.forEach(grid.columns, function( gridCol, index ) {
10636                 if (gridCol.visible || colTypes === uiGridExporterConstants.ALL){
10637                   extractedRow.push(grid.getCellValue(row, gridCol));
10638                 }
10639               });
10640               
10641               data.push(extractedRow);
10642             });
10643             
10644             return data;
10645           }
10646         },
10647
10648
10649         /**
10650          * @ngdoc function
10651          * @name formatAsCSV
10652          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10653          * @description Formats the column headers and data as a CSV, 
10654          * and sends that data to the user
10655          * @param {array} exportColumnHeaders an array of column headers, 
10656          * where each header is an object with name, width and maybe alignment
10657          * @param {array} exportData an array of rows, where each row is
10658          * an array of column data
10659          * @returns {string} csv the formatted csv as a string
10660          */
10661         formatAsCsv: function (exportColumnHeaders, exportData) {
10662           var self = this;
10663           
10664           var bareHeaders = exportColumnHeaders.map(function(header){return header.displayName;});
10665           
10666           var csv = self.formatRowAsCsv(this)(bareHeaders) + '\n';
10667           
10668           csv += exportData.map(this.formatRowAsCsv(this)).join('\n');
10669           
10670           return csv;
10671         },
10672
10673         /**
10674          * @ngdoc function
10675          * @name formatRowAsCsv
10676          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10677          * @description Renders a single field as a csv field, including
10678          * quotes around the value
10679          * @param {exporterService} exporter pass in exporter 
10680          * @param {array} row the row to be turned into a csv string
10681          * @returns {string} a csv-ified version of the row
10682          */
10683         formatRowAsCsv: function ( exporter ) {
10684           return function( row ) {
10685             return row.map(exporter.formatFieldAsCsv).join(',');
10686           };
10687         },
10688         
10689         /**
10690          * @ngdoc function
10691          * @name formatFieldAsCsv
10692          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10693          * @description Renders a single field as a csv field, including
10694          * quotes around the value
10695          * @param {field} field the field to be turned into a csv string,
10696          * may be of any type
10697          * @returns {string} a csv-ified version of the field
10698          */
10699         formatFieldAsCsv: function (field) {
10700           if (field == null) { // we want to catch anything null-ish, hence just == not ===
10701             return '';
10702           }
10703           if (typeof(field) === 'number') {
10704             return field;
10705           }
10706           if (typeof(field) === 'boolean') {
10707             return (field ? 'TRUE' : 'FALSE') ;
10708           }
10709           if (typeof(field) === 'string') {
10710             return '"' + field.replace(/"/g,'""') + '"';
10711           }
10712
10713           return JSON.stringify(field);        
10714         },
10715
10716         /**
10717          * @ngdoc function
10718          * @name renderCsvLink
10719          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10720          * @description Creates a download link with the csv content, 
10721          * putting it into the default exporter element, or into the element
10722          * passed in if provided
10723          * @param {Grid} grid the grid from which data should be exported
10724          * @param {string} csvContent the csv content that we'd like to 
10725          * make available as a download link
10726          * @param {element} $elm (Optional) A UI element into which the
10727          * resulting download link will be placed.  If not provided, the
10728          * link is put into the default exporter element. 
10729          */
10730         renderCsvLink: function (grid, csvContent, $elm) {
10731           var targetElm = $elm ? $elm : angular.element( grid.exporter.gridElm[0].querySelectorAll('.ui-grid-exporter-csv-link') );
10732           if ( angular.element( targetElm[0].querySelectorAll('.ui-grid-exporter-csv-link-span')) ) {
10733             angular.element( targetElm[0].querySelectorAll('.ui-grid-exporter-csv-link-span')).remove();
10734           }
10735           
10736           var linkTemplate = gridUtil.getTemplate(grid.options.exporterLinkTemplate)
10737           .then(function (contents) {
10738             contents = contents.replace(uiGridExporterConstants.LINK_LABEL, grid.options.exporterLinkLabel);
10739             contents = contents.replace(uiGridExporterConstants.CSV_CONTENT, encodeURIComponent(csvContent));
10740           
10741             var template = angular.element(contents);
10742             
10743             var newElm = $compile(template)(grid.exporter.$scope);
10744             targetElm.append(newElm);
10745           });
10746           
10747         },
10748         
10749         /**
10750          * @ngdoc function
10751          * @name pdfExport
10752          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10753          * @description Exports rows from the grid in pdf format, 
10754          * the data exported is selected based on the provided options.
10755          * Note that this function has a dependency on jsPDF, which must
10756          * be either included as a script on your page, or downloaded and
10757          * served as part of your site.  The resulting pdf opens in a new
10758          * browser window.
10759          * @param {Grid} grid the grid from which data should be exported
10760          * @param {string} rowTypes which rows to export, valid values are
10761          * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10762          * uiGridExporterConstants.SELECTED
10763          * @param {string} colTypes which columns to export, valid values are
10764          * uiGridExporterConstants.ALL, uiGridExporterConstants.VISIBLE,
10765          * uiGridExporterConstants.SELECTED
10766          */
10767         pdfExport: function (grid, rowTypes, colTypes) {
10768           var exportColumnHeaders = this.getColumnHeaders(grid, colTypes);
10769           var exportData = this.getData(grid, rowTypes, colTypes);
10770           var docDefinition = this.prepareAsPdf(grid, exportColumnHeaders, exportData);
10771           
10772           pdfMake.createPdf(docDefinition).open();
10773         },
10774         
10775         
10776         /**
10777          * @ngdoc function
10778          * @name renderAsPdf
10779          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10780          * @description Renders the data into a pdf, and opens that pdf.
10781          * 
10782          * @param {Grid} grid the grid from which data should be exported
10783          * @param {array} exportColumnHeaders an array of column headers, 
10784          * where each header is an object with name, width and maybe alignment
10785          * @param {array} exportData an array of rows, where each row is
10786          * an array of column data
10787          * @returns {object} a pdfMake format document definition, ready 
10788          * for generation
10789          */        
10790         prepareAsPdf: function(grid, exportColumnHeaders, exportData) {
10791           var headerWidths = this.calculatePdfHeaderWidths( grid, exportColumnHeaders );
10792           
10793           var headerColumns = exportColumnHeaders.map( function( header ) {
10794             return { text: header.displayName, style: 'tableHeader' }; 
10795           });
10796           
10797           var stringData = exportData.map(this.formatRowAsPdf(this));
10798           
10799           var allData = [headerColumns].concat(stringData);
10800           
10801           var docDefinition = {
10802             pageOrientation: grid.options.exporterPdfOrientation,
10803             content: [{
10804               style: 'tableStyle',
10805               table: {
10806                 headerRows: 1,
10807                 widths: headerWidths,
10808                 body: allData 
10809               }
10810             }],
10811             styles: {
10812               tableStyle: grid.options.exporterPdfTableStyle,
10813               tableHeader: grid.options.exporterPdfTableHeaderStyle,
10814             },
10815             defaultStyle: grid.options.exporterPdfDefaultStyle
10816           };
10817           
10818           if ( grid.options.exporterPdfLayout ){
10819             docDefinition.layout = grid.options.exporterPdfLayout;
10820           }
10821           
10822           return docDefinition;
10823           
10824         },
10825         
10826                 
10827         /**
10828          * @ngdoc function
10829          * @name calculatePdfHeaderWidths
10830          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10831          * @description Determines the column widths base on the 
10832          * widths we got from the grid.  If the column is drawn
10833          * then we have a drawnWidth.  If the column is not visible
10834          * then we have '*', 'x%' or a width.  When columns are
10835          * not visible they don't contribute to the overall gridWidth,
10836          * so we need to adjust to allow for extra columns
10837          * 
10838          * Our basic heuristic is to take the current gridWidth, plus 
10839          * numeric columns and call this the base gridwidth.
10840          * 
10841          * To that we add 100 for any '*' column, and x% of the base gridWidth
10842          * for any column that is a %
10843          *  
10844          * @param {Grid} grid the grid from which data should be exported
10845          * @param {object} exportHeaders array of header information 
10846          * @returns {object} an array of header widths
10847          */
10848         calculatePdfHeaderWidths: function ( grid, exportHeaders ) {
10849           var baseGridWidth = 0;
10850           angular.forEach(exportHeaders, function(value){
10851             if (typeof(value.width) === 'number'){
10852               baseGridWidth += value.width;
10853             }
10854           });
10855           
10856           var extraColumns = 0;
10857           angular.forEach(exportHeaders, function(value){
10858             if (value.width === '*'){
10859               extraColumns += 100;
10860             }
10861             if (typeof(value.width) === 'string' && value.width.match(/(\d)*%/)) {
10862               var percent = parseInt(value.width.match(/(\d)*%/)[0]);
10863               
10864               value.width = baseGridWidth * percent / 100;
10865               extraColumns += value.width;
10866             }
10867           });
10868           
10869           var gridWidth = baseGridWidth + extraColumns;
10870           
10871           return exportHeaders.map(function( header ) {
10872             return header.width === '*' ? header.width : header.width * grid.options.exporterPdfMaxGridWidth / gridWidth;
10873           });
10874           
10875         },
10876         
10877         /**
10878          * @ngdoc function
10879          * @name formatRowAsPdf
10880          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10881          * @description Renders a row in a format consumable by PDF,
10882          * mainly meaning casting everything to a string
10883          * @param {exporterService} exporter pass in exporter 
10884          * @param {array} row the row to be turned into a csv string
10885          * @returns {string} a csv-ified version of the row
10886          */
10887         formatRowAsPdf: function ( exporter ) {
10888           return function( row ) {
10889             return row.map(exporter.formatFieldAsPdfString);
10890           };
10891         },
10892         
10893         
10894         /**
10895          * @ngdoc function
10896          * @name formatFieldAsCsv
10897          * @methodOf  ui.grid.exporter.service:uiGridExporterService
10898          * @description Renders a single field as a pdf-able field, which
10899          * is different from a csv field only in that strings don't have quotes
10900          * around them
10901          * @param {field} field the field to be turned into a pdf string,
10902          * may be of any type
10903          * @returns {string} a string-ified version of the field
10904          */
10905         formatFieldAsPdfString: function (field) {
10906           if (field == null) { // we want to catch anything null-ish, hence just == not ===
10907             return '';
10908           }
10909           if (typeof(field) === 'number') {
10910             return field.toString();
10911           }
10912           if (typeof(field) === 'boolean') {
10913             return (field ? 'TRUE' : 'FALSE') ;
10914           }
10915           if (typeof(field) === 'string') {
10916             return field.replace(/"/g,'""');
10917           }
10918
10919           return JSON.stringify(field).replace(/^"/,'').replace(/"$/,'');        
10920         }
10921       };
10922
10923       return service;
10924
10925     }
10926   ]);
10927
10928   /**
10929    *  @ngdoc directive
10930    *  @name ui.grid.exporter.directive:uiGridExporter
10931    *  @element div
10932    *  @restrict A
10933    *
10934    *  @description Adds exporter features to grid
10935    *
10936    *  @example
10937    <example module="app">
10938    <file name="app.js">
10939    var app = angular.module('app', ['ui.grid', 'ui.grid.edit']);
10940
10941    app.controller('MainCtrl', ['$scope', function ($scope) {
10942       $scope.data = [
10943         { name: 'Bob', title: 'CEO' },
10944             { name: 'Frank', title: 'Lowly Developer' }
10945       ];
10946
10947       $scope.gridOptions = {
10948         columnDefs: [
10949           {name: 'name', enableCellEdit: true},
10950           {name: 'title', enableCellEdit: true}
10951         ],
10952         data: $scope.data
10953       };
10954     }]);
10955    </file>
10956    <file name="index.html">
10957    <div ng-controller="MainCtrl">
10958    <div ui-grid="gridOptions" ui-grid-exporter></div>
10959    </div>
10960    </file>
10961    </example>
10962    */
10963   module.directive('uiGridExporter', ['$log', 'uiGridExporterConstants', 'uiGridExporterService', 'gridUtil', '$compile',
10964     function ($log, uiGridExporterConstants, uiGridExporterService, gridUtil, $compile) {
10965       return {
10966         replace: true,
10967         priority: 0,
10968         require: '^uiGrid',
10969         scope: false,
10970         compile: function () {
10971           return {
10972             pre: function ($scope, $elm, $attrs, uiGridCtrl) {
10973               uiGridExporterService.initializeGrid(uiGridCtrl.grid);
10974               uiGridCtrl.grid.exporter.$scope = $scope;
10975             },
10976             post: function ($scope, $elm, $attrs, uiGridCtrl) {
10977             }
10978           };
10979         }
10980       };
10981     }
10982   ]);
10983 })();
10984 (function() {
10985   'use strict';
10986   /**
10987    *  @ngdoc overview
10988    *  @name ui.grid.infiniteScroll
10989    *
10990    *  @description 
10991    *
10992    *   #ui.grid.infiniteScroll
10993    * This module provides infinite scroll functionality to ui-grid
10994    *
10995    */
10996   var module = angular.module('ui.grid.infiniteScroll', ['ui.grid']);
10997   /**
10998    *  @ngdoc service
10999    *  @name ui.grid.infiniteScroll.service:uiGridInfiniteScrollService
11000    *
11001    *  @description Service for infinite scroll features
11002    */
11003   module.service('uiGridInfiniteScrollService', ['gridUtil', '$log', '$compile', '$timeout', function (gridUtil, $log, $compile, $timeout) {
11004     
11005     var service = {
11006
11007       /**
11008        * @ngdoc function
11009        * @name initializeGrid
11010        * @methodOf ui.grid.infiniteScroll.service:uiGridInfiniteScrollService
11011        * @description This method register events and methods into grid public API
11012        */
11013
11014       initializeGrid: function(grid) {
11015         /**
11016          *  @ngdoc object
11017          *  @name ui.grid.infiniteScroll.api:PublicAPI
11018          *
11019          *  @description Public API for infinite scroll feature
11020          */
11021         var publicApi = {
11022           events: {
11023             infiniteScroll: {
11024
11025               /**
11026                * @ngdoc event
11027                * @name needLoadMoreData
11028                * @eventOf ui.grid.infiniteScroll.api:PublicAPI
11029                * @description This event fires when scroll reached bottom percentage of grid
11030                * and needs to load data
11031                */
11032
11033               needLoadMoreData: function ($scope, fn) {
11034               }
11035             }
11036           },
11037           methods: {
11038             infiniteScroll: {
11039
11040               /**
11041                * @ngdoc function
11042                * @name dataLoaded
11043                * @methodOf ui.grid.infiniteScroll.api:PublicAPI
11044                * @description This function is used as a promise when data finished loading.
11045                * See infinite_scroll ngdoc for example of usage
11046                */
11047
11048               dataLoaded: function() {
11049                 grid.options.loadTimout = false;
11050               }
11051             }
11052           }
11053         };
11054         grid.options.loadTimout = false;
11055         grid.api.registerEventsFromObject(publicApi.events);
11056         grid.api.registerMethodsFromObject(publicApi.methods);
11057       },
11058
11059       /**
11060        * @ngdoc function
11061        * @name loadData
11062        * @methodOf ui.grid.infiniteScroll.service:uiGridInfiniteScrollService
11063        * @description This function fires 'needLoadMoreData' event
11064        */
11065
11066       loadData: function (grid) {
11067           grid.api.infiniteScroll.raise.needLoadMoreData();
11068           grid.options.loadTimout = true;
11069       },
11070
11071       /**
11072        * @ngdoc function
11073        * @name checkScroll
11074        * @methodOf ui.grid.infiniteScroll.service:uiGridInfiniteScrollService
11075        * @description This function checks scroll position inside grid and 
11076        * calls 'loadData' function when scroll reaches 'infiniteScrollPercentage'
11077        */
11078
11079       checkScroll: function(grid, scrollTop) {
11080
11081         /* Take infiniteScrollPercentage value or use 20% as default */
11082         var infiniteScrollPercentage = grid.options.infiniteScrollPercentage ? grid.options.infiniteScrollPercentage : 20; 
11083
11084         if (!grid.options.loadTimout && scrollTop <= infiniteScrollPercentage) {
11085           this.loadData(grid);
11086           return true;
11087         }
11088         return false;
11089       }
11090       /**
11091       * @ngdoc property
11092       * @name infiniteScrollPercentage
11093       * @propertyOf ui.grid.class:GridOptions
11094       * @description This setting controls at what percentage of the scroll more data
11095       * is requested by the infinite scroll
11096       */
11097     };
11098     return service;
11099   }]);
11100   /**
11101    *  @ngdoc directive
11102    *  @name ui.grid.infiniteScroll.directive:uiGridInfiniteScroll
11103    *  @element div
11104    *  @restrict A
11105    *
11106    *  @description Adds infinite scroll features to grid
11107    *
11108    *  @example
11109    <example module="app">
11110    <file name="app.js">
11111    var app = angular.module('app', ['ui.grid', 'ui.grid.infiniteScroll']);
11112
11113    app.controller('MainCtrl', ['$scope', function ($scope) {
11114       $scope.data = [
11115         { name: 'Alex', car: 'Toyota' },
11116             { name: 'Sam', car: 'Lexus' }
11117       ];
11118
11119       $scope.columnDefs = [
11120         {name: 'name'},
11121         {name: 'car'}
11122       ];
11123     }]);
11124    </file>
11125    <file name="index.html">
11126    <div ng-controller="MainCtrl">
11127    <div ui-grid="{ data: data, columnDefs: columnDefs }" ui-grid-infinite-scroll="20"></div>
11128    </div>
11129    </file>
11130    </example>
11131    */
11132   
11133   module.directive('uiGridInfiniteScroll', ['$log', 'uiGridInfiniteScrollService',
11134     function ($log, uiGridInfiniteScrollService) {
11135       return {
11136         priority: -200,
11137         scope: false,
11138         require: '^uiGrid',
11139         compile: function($scope, $elm, $attr){
11140           return { 
11141             pre: function($scope, $elm, $attr, uiGridCtrl) {
11142               uiGridInfiniteScrollService.initializeGrid(uiGridCtrl.grid);
11143             },
11144             post: function($scope, $elm, $attr) {
11145             }
11146           };
11147         }
11148       };
11149     }]);
11150
11151   module.directive('uiGridViewport',
11152     ['$compile', '$log', 'uiGridInfiniteScrollService', 'uiGridConstants', 
11153       function ($compile, $log, uiGridInfiniteScrollService, uiGridConstants) {
11154         return {
11155           priority: -200,
11156           scope: false,
11157           link: function ($scope, $elm, $attr){
11158             $scope.$on(uiGridConstants.events.GRID_SCROLL, function(evt, args) {
11159
11160               var percentage = 100 - (args.y.percentage * 100);
11161               uiGridInfiniteScrollService.checkScroll($scope.grid, percentage);
11162             });
11163           }
11164         };
11165       }]);
11166 })();
11167 (function () {
11168   'use strict';
11169
11170   /**
11171    * @ngdoc overview
11172    * @name ui.grid.pinning
11173    * @description
11174    *
11175    *  # ui.grid.pinning
11176    * This module provides column pinning to the end user via menu options in the column header
11177    * <br/>
11178    * <br/>
11179    *
11180    * <div doc-module-components="ui.grid.pinning"></div>
11181    */
11182
11183   var module = angular.module('ui.grid.pinning', ['ui.grid']);
11184
11185   module.config(['$provide', function ($provide) {
11186     $provide.decorator('i18nService', ['$delegate', function ($delegate) {
11187       $delegate.add('en',
11188         { pinning: {
11189             pinLeft: 'Pin Left',
11190             pinRight: 'Pin Right',
11191             unpin: 'Unpin'
11192           }
11193         }
11194       );
11195
11196       return $delegate;
11197     }]);
11198   }]);
11199
11200   module.service('uiGridPinningService', ['$log', 'GridRenderContainer', 'i18nService', function ($log, GridRenderContainer, i18nService) {
11201     var service = {
11202
11203       initializeGrid: function (grid) {
11204         service.defaultGridOptions(grid.options);
11205
11206         // Register a column builder to add new menu items for pinning left and right
11207         grid.registerColumnBuilder(service.pinningColumnBuilder);
11208       },
11209
11210       defaultGridOptions: function (gridOptions) {
11211         //default option to true unless it was explicitly set to false
11212         /**
11213          *  @ngdoc object
11214          *  @name ui.grid.pinning.api:GridOptions
11215          *
11216          *  @description GridOptions for pinning feature, these are available to be  
11217            *  set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
11218          */
11219
11220         /**
11221          *  @ngdoc object
11222          *  @name enableRowSelection
11223          *  @propertyOf  ui.grid.pinning.api:GridOptions
11224          *  @description Enable pinning for the entire grid.  
11225          *  <br/>Defaults to true
11226          */
11227         gridOptions.enablePinning = gridOptions.enablePinning !== false;
11228
11229       },
11230
11231       pinningColumnBuilder: function (colDef, col, gridOptions) {
11232         //default to true unless gridOptions or colDef is explicitly false
11233
11234         /**
11235          *  @ngdoc object
11236          *  @name ui.grid.pinning.api:ColumnDef
11237          *
11238          *  @description ColumnDef for pinning feature, these are available to be 
11239          *  set using the ui-grid {@link ui.grid.class:GridOptions.columnDef gridOptions.columnDefs}
11240          */
11241
11242         /**
11243          *  @ngdoc object
11244          *  @name enablePinning
11245          *  @propertyOf  ui.grid.pinning.api:ColumnDef
11246          *  @description Enable pinning for the individual column.  
11247          *  <br/>Defaults to true
11248          */
11249         colDef.enablePinning = colDef.enablePinning === undefined ? gridOptions.enablePinning : colDef.enablePinning;
11250
11251
11252         /**
11253          *  @ngdoc object
11254          *  @name pinnedLeft
11255          *  @propertyOf  ui.grid.pinning.api:ColumnDef
11256          *  @description Column is pinned left when grid is rendered
11257          *  <br/>Defaults to false
11258          */
11259
11260         /**
11261          *  @ngdoc object
11262          *  @name pinnedRight
11263          *  @propertyOf  ui.grid.pinning.api:ColumnDef
11264          *  @description Column is pinned right when grid is rendered
11265          *  <br/>Defaults to false
11266          */
11267         if (colDef.pinnedLeft) {
11268           col.renderContainer = 'left';
11269           col.grid.createLeftContainer();
11270         }
11271         else if (colDef.pinnedRight) {
11272           col.renderContainer = 'right';
11273           col.grid.createRightContainer();
11274         }
11275
11276         if (!colDef.enablePinning) {
11277           return;
11278         }
11279
11280         var pinColumnLeftAction = {
11281           title: i18nService.get().pinning.pinLeft,
11282           icon: 'ui-grid-icon-left-open',
11283           shown: function () {
11284             return typeof(this.context.col.renderContainer) === 'undefined' || !this.context.col.renderContainer || this.context.col.renderContainer !== 'left';
11285           },
11286           action: function () {
11287             this.context.col.renderContainer = 'left';
11288             this.context.col.grid.createLeftContainer();
11289
11290             // Need to call refresh twice; once to move our column over to the new render container and then
11291             //   a second time to update the grid viewport dimensions with our adjustments
11292             col.grid.refresh()
11293               .then(function () {
11294                 col.grid.refresh();
11295               });
11296           }
11297         };
11298
11299         var pinColumnRightAction = {
11300           title: i18nService.get().pinning.pinRight,
11301           icon: 'ui-grid-icon-right-open',
11302           shown: function () {
11303             return typeof(this.context.col.renderContainer) === 'undefined' || !this.context.col.renderContainer || this.context.col.renderContainer !== 'right';
11304           },
11305           action: function () {
11306             this.context.col.renderContainer = 'right';
11307             this.context.col.grid.createRightContainer();
11308
11309
11310             // Need to call refresh twice; once to move our column over to the new render container and then
11311             //   a second time to update the grid viewport dimensions with our adjustments
11312             col.grid.refresh()
11313               .then(function () {
11314                 col.grid.refresh();
11315               });
11316           }
11317         };
11318
11319         var removePinAction = {
11320           title: i18nService.get().pinning.unpin,
11321           icon: 'ui-grid-icon-cancel',
11322           shown: function () {
11323             return typeof(this.context.col.renderContainer) !== 'undefined' && this.context.col.renderContainer !== null && this.context.col.renderContainer !== 'body';
11324           },
11325           action: function () {
11326             this.context.col.renderContainer = null;
11327
11328             // Need to call refresh twice; once to move our column over to the new render container and then
11329             //   a second time to update the grid viewport dimensions with our adjustments
11330             col.grid.refresh()
11331               .then(function () {
11332                 col.grid.refresh();
11333               });
11334           }
11335         };
11336
11337         col.menuItems.push(pinColumnLeftAction);
11338         col.menuItems.push(pinColumnRightAction);
11339         col.menuItems.push(removePinAction);
11340       }
11341     };
11342
11343     return service;
11344   }]);
11345
11346   module.directive('uiGridPinning', ['$log', 'uiGridPinningService',
11347     function ($log, uiGridPinningService) {
11348       return {
11349         require: 'uiGrid',
11350         scope: false,
11351         compile: function () {
11352           return {
11353             pre: function ($scope, $elm, $attrs, uiGridCtrl) {
11354               uiGridPinningService.initializeGrid(uiGridCtrl.grid);
11355             },
11356             post: function ($scope, $elm, $attrs, uiGridCtrl) {
11357             }
11358           };
11359         }
11360       };
11361     }]);
11362
11363
11364 })();
11365 (function(){
11366   'use strict';
11367
11368   var module = angular.module('ui.grid.resizeColumns', ['ui.grid']);
11369
11370   module.constant('columnBounds', {
11371     minWidth: 35
11372   });
11373
11374
11375   module.service('uiGridResizeColumnsService', ['$log','$q',
11376     function ($log,$q) {
11377
11378       var service = {
11379         defaultGridOptions: function(gridOptions){
11380           //default option to true unless it was explicitly set to false
11381           /**
11382            *  @ngdoc object
11383            *  @name ui.grid.resizeColumns.api:GridOptions
11384            *
11385            *  @description GridOptions for resizeColumns feature, these are available to be  
11386            *  set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
11387            */
11388
11389           /**
11390            *  @ngdoc object
11391            *  @name enableColumnResizing
11392            *  @propertyOf  ui.grid.resizeColumns.api:GridOptions
11393            *  @description Enable column resizing on the entire grid 
11394            *  <br/>Defaults to true
11395            */
11396           gridOptions.enableColumnResizing = gridOptions.enableColumnResizing !== false;
11397
11398           //legacy support
11399           //use old name if it is explicitly false
11400           if (gridOptions.enableColumnResize === false){
11401             gridOptions.enableColumnResizing = false;
11402           }
11403         },
11404
11405         colResizerColumnBuilder: function (colDef, col, gridOptions) {
11406
11407           var promises = [];
11408           /**
11409            *  @ngdoc object
11410            *  @name ui.grid.resizeColumns.api:ColumnDef
11411            *
11412            *  @description ColumnDef for resizeColumns feature, these are available to be 
11413            *  set using the ui-grid {@link ui.grid.class:GridOptions.columnDef gridOptions.columnDefs}
11414            */
11415
11416           /**
11417            *  @ngdoc object
11418            *  @name enableColumnResizing
11419            *  @propertyOf  ui.grid.resizeColumns.api:ColumnDef
11420            *  @description Enable column resizing on an individual column
11421            *  <br/>Defaults to GridOptions.enableColumnResizing
11422            */
11423           //default to true unless gridOptions or colDef is explicitly false
11424           colDef.enableColumnResizing = colDef.enableColumnResizing === undefined ? gridOptions.enableColumnResizing : colDef.enableColumnResizing;
11425
11426
11427           //legacy support of old option name
11428           if (colDef.enableColumnResize === false){
11429             colDef.enableColumnResizing = false;
11430           }
11431
11432           return $q.all(promises);
11433         }
11434       };
11435
11436       return service;
11437
11438     }]);
11439
11440
11441   /**
11442    * @ngdoc directive
11443    * @name ui.grid.resizeColumns.directive:uiGridResizeColumns
11444    * @element div
11445    * @restrict A
11446    * @description
11447    * Enables resizing for all columns on the grid. If, for some reason, you want to use the ui-grid-resize-columns directive, but not allow column resizing, you can explicitly set the
11448    * option to false. This prevents resizing for the entire grid, regardless of individual columnDef options.
11449    *
11450    * @example
11451    <doc:example module="app">
11452    <doc:source>
11453    <script>
11454    var app = angular.module('app', ['ui.grid', 'ui.grid.resizeColumns']);
11455
11456    app.controller('MainCtrl', ['$scope', function ($scope) {
11457           $scope.gridOpts = {
11458             data: [
11459               { "name": "Ethel Price", "gender": "female", "company": "Enersol" },
11460               { "name": "Claudine Neal", "gender": "female", "company": "Sealoud" },
11461               { "name": "Beryl Rice", "gender": "female", "company": "Velity" },
11462               { "name": "Wilder Gonzales", "gender": "male", "company": "Geekko" }
11463             ]
11464           };
11465         }]);
11466    </script>
11467
11468    <div ng-controller="MainCtrl">
11469    <div class="testGrid" ui-grid="gridOpts" ui-grid-resize-columns ></div>
11470    </div>
11471    </doc:source>
11472    <doc:scenario>
11473
11474    </doc:scenario>
11475    </doc:example>
11476    */
11477   module.directive('uiGridResizeColumns', ['$log', 'uiGridResizeColumnsService', function ($log, uiGridResizeColumnsService) {
11478     return {
11479       replace: true,
11480       priority: 0,
11481       require: '^uiGrid',
11482       scope: false,
11483       compile: function () {
11484         return {
11485           pre: function ($scope, $elm, $attrs, uiGridCtrl) {
11486
11487             uiGridResizeColumnsService.defaultGridOptions(uiGridCtrl.grid.options);
11488             uiGridCtrl.grid.registerColumnBuilder( uiGridResizeColumnsService.colResizerColumnBuilder);
11489
11490           },
11491           post: function ($scope, $elm, $attrs, uiGridCtrl) {
11492           }
11493         };
11494       }
11495     };
11496   }]);
11497
11498   // Extend the uiGridHeaderCell directive
11499   module.directive('uiGridHeaderCell', ['$log', '$templateCache', '$compile', '$q', function ($log, $templateCache, $compile, $q) {
11500     return {
11501       // Run after the original uiGridHeaderCell
11502       priority: -10,
11503       require: '^uiGrid',
11504       // scope: false,
11505       compile: function() {
11506         return {
11507           post: function ($scope, $elm, $attrs, uiGridCtrl) {
11508            if (uiGridCtrl.grid.options.enableColumnResizing) {
11509               var renderIndexDefer = $q.defer();
11510
11511               $attrs.$observe('renderIndex', function (n, o) {
11512                 $scope.renderIndex = $scope.$eval(n);
11513
11514                 renderIndexDefer.resolve();
11515               });
11516
11517               renderIndexDefer.promise.then(function() {
11518                 var columnResizerElm = $templateCache.get('ui-grid/columnResizer');
11519
11520                 var resizerLeft = angular.element(columnResizerElm).clone();
11521                 var resizerRight = angular.element(columnResizerElm).clone();
11522
11523                 resizerLeft.attr('position', 'left');
11524                 resizerRight.attr('position', 'right');
11525
11526                 var col = $scope.col;
11527                 var renderContainer = col.getRenderContainer();
11528
11529
11530                 // Get the column to the left of this one
11531                 var otherCol = renderContainer.renderedColumns[$scope.renderIndex - 1];
11532
11533                 // Don't append the left resizer if this is the first column or the column to the left of this one has resizing disabled
11534                 if (otherCol && $scope.col.index !== 0 && otherCol.colDef.enableColumnResizing !== false) {
11535                   $elm.prepend(resizerLeft);
11536                 }
11537                 
11538                 // Don't append the right resizer if this column has resizing disabled
11539                 //if ($scope.col.index !== $scope.grid.renderedColumns.length - 1 && $scope.col.colDef.enableColumnResizing !== false) {
11540                 if ($scope.col.colDef.enableColumnResizing !== false) {
11541                   $elm.append(resizerRight);
11542                 }
11543
11544                 $compile(resizerLeft)($scope);
11545                 $compile(resizerRight)($scope);
11546               });
11547             }
11548           }
11549         };
11550       }
11551     };
11552   }]);
11553
11554
11555   
11556   /**
11557    * @ngdoc directive
11558    * @name ui.grid.resizeColumns.directive:uiGridColumnResizer
11559    * @element div
11560    * @restrict A
11561    *
11562    * @description
11563    * Draggable handle that controls column resizing.
11564    * 
11565    * @example
11566    <doc:example module="app">
11567      <doc:source>
11568        <script>
11569         var app = angular.module('app', ['ui.grid', 'ui.grid.resizeColumns']);
11570
11571         app.controller('MainCtrl', ['$scope', function ($scope) {
11572           $scope.gridOpts = {
11573             enableColumnResizing: true,
11574             data: [
11575               { "name": "Ethel Price", "gender": "female", "company": "Enersol" },
11576               { "name": "Claudine Neal", "gender": "female", "company": "Sealoud" },
11577               { "name": "Beryl Rice", "gender": "female", "company": "Velity" },
11578               { "name": "Wilder Gonzales", "gender": "male", "company": "Geekko" }
11579             ]
11580           };
11581         }]);
11582        </script>
11583
11584        <div ng-controller="MainCtrl">
11585         <div class="testGrid" ui-grid="gridOpts"></div>
11586        </div>
11587      </doc:source>
11588      <doc:scenario>
11589       // TODO: e2e specs?
11590         // TODO: Obey minWidth and maxWIdth;
11591
11592       // TODO: post-resize a horizontal scroll event should be fired
11593      </doc:scenario>
11594    </doc:example>
11595    */  
11596   module.directive('uiGridColumnResizer', ['$log', '$document', 'gridUtil', 'uiGridConstants', 'columnBounds', function ($log, $document, gridUtil, uiGridConstants, columnBounds) {
11597     var resizeOverlay = angular.element('<div class="ui-grid-resize-overlay"></div>');
11598
11599     var resizer = {
11600       priority: 0,
11601       scope: {
11602         col: '=',
11603         position: '@',
11604         renderIndex: '='
11605       },
11606       require: '?^uiGrid',
11607       link: function ($scope, $elm, $attrs, uiGridCtrl) {
11608         var startX = 0,
11609             x = 0,
11610             gridLeft = 0;
11611
11612         if ($scope.position === 'left') {
11613           $elm.addClass('left');
11614         }
11615         else if ($scope.position === 'right') {
11616           $elm.addClass('right');
11617         }
11618
11619         // Resize all the other columns around col
11620         function resizeAroundColumn(col) {
11621           // Get this column's render container
11622           var renderContainer = col.getRenderContainer();
11623
11624           renderContainer.visibleColumnCache.forEach(function (column) {
11625             // Skip the column we just resized
11626             if (column.index === col.index) { return; }
11627             
11628             var colDef = column.colDef;
11629             if (!colDef.width || (angular.isString(colDef.width) && (colDef.width.indexOf('*') !== -1 || colDef.width.indexOf('%') !== -1))) {
11630               colDef.width = column.drawnWidth;
11631             }
11632           });
11633         }
11634
11635         // Build the columns then refresh the grid canvas
11636         //   takes an argument representing the diff along the X-axis that the resize had
11637         function buildColumnsAndRefresh(xDiff) {
11638           // Build the columns
11639           uiGridCtrl.grid.buildColumns()
11640             .then(function() {
11641               // Then refresh the grid canvas, rebuilding the styles so that the scrollbar updates its size
11642               uiGridCtrl.grid.refreshCanvas(true);
11643             });
11644         }
11645
11646         function mousemove(event, args) {
11647           if (event.originalEvent) { event = event.originalEvent; }
11648           event.preventDefault();
11649
11650           x = event.clientX - gridLeft;
11651
11652           if (x < 0) { x = 0; }
11653           else if (x > uiGridCtrl.grid.gridWidth) { x = uiGridCtrl.grid.gridWidth; }
11654
11655           // The other column to resize (the one next to this one)
11656           var col = $scope.col;
11657           var renderContainer = col.getRenderContainer();
11658           var otherCol;
11659           if ($scope.position === 'left') {
11660             // Get the column to the left of this one
11661             col = renderContainer.renderedColumns[$scope.renderIndex - 1];
11662             otherCol = $scope.col;
11663           }
11664           else if ($scope.position === 'right') {
11665             otherCol = renderContainer.renderedColumns[$scope.renderIndex + 1];
11666           }
11667
11668           // Don't resize if it's disabled on this column
11669           if (col.colDef.enableColumnResizing === false) {
11670             return;
11671           }
11672
11673           if (!uiGridCtrl.grid.element.hasClass('column-resizing')) {
11674             uiGridCtrl.grid.element.addClass('column-resizing');
11675           }
11676
11677           // Get the diff along the X axis
11678           var xDiff = x - startX;
11679
11680           // Get the width that this mouse would give the column
11681           var newWidth = col.drawnWidth + xDiff;
11682
11683           // If the new width would be less than the column's allowably minimum width, don't allow it
11684           if (col.colDef.minWidth && newWidth < col.colDef.minWidth) {
11685             x = x + (col.colDef.minWidth - newWidth);
11686           }
11687           else if (!col.colDef.minWidth && columnBounds.minWidth && newWidth < columnBounds.minWidth) {
11688             x = x + (col.colDef.minWidth - newWidth);
11689           }
11690           else if (col.colDef.maxWidth && newWidth > col.colDef.maxWidth) {
11691             x = x + (col.colDef.maxWidth - newWidth);
11692           }
11693           
11694           resizeOverlay.css({ left: x + 'px' });
11695
11696           uiGridCtrl.fireEvent(uiGridConstants.events.ITEM_DRAGGING);
11697         }
11698
11699         function mouseup(event, args) {
11700           if (event.originalEvent) { event = event.originalEvent; }
11701           event.preventDefault();
11702
11703           uiGridCtrl.grid.element.removeClass('column-resizing');
11704
11705           resizeOverlay.remove();
11706
11707           // Resize the column
11708           x = event.clientX - gridLeft;
11709           var xDiff = x - startX;
11710
11711           if (xDiff === 0) {
11712             $document.off('mouseup', mouseup);
11713             $document.off('mousemove', mousemove);
11714             return;
11715           }
11716
11717           // The other column to resize (the one next to this one)
11718           var col = $scope.col;
11719           var renderContainer = col.getRenderContainer();
11720
11721           var otherCol;
11722           if ($scope.position === 'left') {
11723             // Get the column to the left of this one
11724             col = renderContainer.renderedColumns[$scope.renderIndex - 1];
11725             otherCol = $scope.col;
11726           }
11727           else if ($scope.position === 'right') {
11728             otherCol = renderContainer.renderedColumns[$scope.renderIndex + 1];
11729           }
11730
11731           // Don't resize if it's disabled on this column
11732           if (col.colDef.enableColumnResizing === false) {
11733             return;
11734           }
11735
11736           // Get the new width
11737           var newWidth = col.drawnWidth + xDiff;
11738
11739           // If the new width is less than the minimum width, make it the minimum width
11740           if (col.colDef.minWidth && newWidth < col.colDef.minWidth) {
11741             newWidth = col.colDef.minWidth;
11742           }
11743           else if (!col.colDef.minWidth && columnBounds.minWidth && newWidth < columnBounds.minWidth) {
11744             newWidth = columnBounds.minWidth;
11745           }
11746           // 
11747           if (col.colDef.maxWidth && newWidth > col.colDef.maxWidth) {
11748             newWidth = col.colDef.maxWidth;
11749           }
11750           
11751           col.colDef.width = newWidth;
11752
11753           // All other columns because fixed to their drawn width, if they aren't already
11754           resizeAroundColumn(col);
11755
11756           buildColumnsAndRefresh(xDiff);
11757
11758           $document.off('mouseup', mouseup);
11759           $document.off('mousemove', mousemove);
11760         }
11761
11762         $elm.on('mousedown', function(event, args) {
11763           if (event.originalEvent) { event = event.originalEvent; }
11764           event.stopPropagation();
11765
11766           // Get the left offset of the grid
11767           // gridLeft = uiGridCtrl.grid.element[0].offsetLeft;
11768           gridLeft = uiGridCtrl.grid.element[0].getBoundingClientRect().left;
11769
11770           // Get the starting X position, which is the X coordinate of the click minus the grid's offset
11771           startX = event.clientX - gridLeft;
11772
11773           // Append the resizer overlay
11774           uiGridCtrl.grid.element.append(resizeOverlay);
11775
11776           // Place the resizer overlay at the start position
11777           resizeOverlay.css({ left: startX });
11778
11779           // Add handlers for mouse move and up events
11780           $document.on('mouseup', mouseup);
11781           $document.on('mousemove', mousemove);
11782         });
11783
11784         // On doubleclick, resize to fit all rendered cells
11785         $elm.on('dblclick', function(event, args) {
11786           event.stopPropagation();
11787
11788           var col = $scope.col;
11789           var renderContainer = col.getRenderContainer();
11790
11791           var otherCol, multiplier;
11792
11793           // If we're the left-positioned resizer then we need to resize the column to the left of our column, and not our column itself
11794           if ($scope.position === 'left') {
11795             col = renderContainer.renderedColumns[$scope.renderIndex - 1];
11796             otherCol = $scope.col;
11797             multiplier = 1;
11798           }
11799           else if ($scope.position === 'right') {
11800             otherCol = renderContainer.renderedColumns[$scope.renderIndex + 1];
11801             otherCol = renderContainer.renderedColumns[$scope.renderIndex + 1];
11802             multiplier = -1;
11803           }
11804
11805           // Go through the rendered rows and find out the max size for the data in this column
11806           var maxWidth = 0;
11807           var xDiff = 0;
11808
11809           // Get the parent render container element
11810           var renderContainerElm = gridUtil.closestElm($elm, '.ui-grid-render-container');
11811
11812           // Get the cell contents so we measure correctly. For the header cell we have to account for the sort icon and the menu buttons, if present
11813           var cells = renderContainerElm.querySelectorAll('.' + uiGridConstants.COL_CLASS_PREFIX + col.index + ' .ui-grid-cell-contents');
11814           Array.prototype.forEach.call(cells, function (cell) {
11815               // Get the cell width
11816               // $log.debug('width', gridUtil.elementWidth(cell));
11817
11818               // Account for the menu button if it exists
11819               var menuButton;
11820               if (angular.element(cell).parent().hasClass('ui-grid-header-cell')) {
11821                 menuButton = angular.element(cell).parent()[0].querySelectorAll('.ui-grid-column-menu-button');
11822               }
11823
11824               gridUtil.fakeElement(cell, {}, function(newElm) {
11825                 // Make the element float since it's a div and can expand to fill its container
11826                 var e = angular.element(newElm);
11827                 e.attr('style', 'float: left');
11828
11829                 var width = gridUtil.elementWidth(e);
11830
11831                 if (menuButton) {
11832                   var menuButtonWidth = gridUtil.elementWidth(menuButton);
11833                   width = width + menuButtonWidth;
11834                 }
11835
11836                 if (width > maxWidth) {
11837                   maxWidth = width;
11838                   xDiff = maxWidth - width;
11839                 }
11840               });
11841             });
11842
11843           // If the new width is less than the minimum width, make it the minimum width
11844           if (col.colDef.minWidth && maxWidth < col.colDef.minWidth) {
11845             maxWidth = col.colDef.minWidth;
11846           }
11847           else if (!col.colDef.minWidth && columnBounds.minWidth && maxWidth < columnBounds.minWidth) {
11848             maxWidth = columnBounds.minWidth;
11849           }
11850           // 
11851           if (col.colDef.maxWidth && maxWidth > col.colDef.maxWidth) {
11852             maxWidth = col.colDef.maxWidth;
11853           }
11854
11855           col.colDef.width = maxWidth;
11856           
11857           // All other columns because fixed to their drawn width, if they aren't already
11858           resizeAroundColumn(col);
11859
11860           buildColumnsAndRefresh(xDiff);
11861         });
11862
11863         $elm.on('$destroy', function() {
11864           $elm.off('mousedown');
11865           $elm.off('dblclick');
11866           $document.off('mousemove', mousemove);
11867           $document.off('mouseup', mouseup);
11868         });
11869       }
11870     };
11871
11872     return resizer;
11873   }]);
11874
11875 })();
11876 (function () {
11877   'use strict';
11878
11879   /**
11880    * @ngdoc overview
11881    * @name ui.grid.rowEdit
11882    * @description
11883    *
11884    *  # ui.grid.rowEdit
11885    * This module extends the edit feature to provide tracking and saving of rows
11886    * of data.  The tutorial provides more information on how this feature is best
11887    * used {@link tutorial/205_row_editable here}.
11888    * <br/>
11889    * This feature depends on usage of the ui-grid-edit feature, and also benefits
11890    * from use of ui-grid-cellNav to provide the full spreadsheet-like editing 
11891    * experience
11892    * 
11893    */
11894
11895   var module = angular.module('ui.grid.rowEdit', ['ui.grid', 'ui.grid.edit', 'ui.grid.cellNav']);
11896
11897   /**
11898    *  @ngdoc object
11899    *  @name ui.grid.rowEdit.constant:uiGridRowEditConstants
11900    *
11901    *  @description constants available in row edit module
11902    */
11903   module.constant('uiGridRowEditConstants', {
11904   });
11905
11906   /**
11907    *  @ngdoc service
11908    *  @name ui.grid.rowEdit.service:uiGridRowEditService
11909    *
11910    *  @description Services for row editing features
11911    */
11912   module.service('uiGridRowEditService', ['$interval', '$log', '$q', 'uiGridConstants', 'uiGridRowEditConstants', 'gridUtil', 
11913     function ($interval, $log, $q, uiGridConstants, uiGridRowEditConstants, gridUtil) {
11914
11915       var service = {
11916
11917         initializeGrid: function (scope, grid) {
11918           /**
11919            *  @ngdoc object
11920            *  @name ui.grid.rowEdit.api:PublicApi
11921            *
11922            *  @description Public Api for rowEdit feature
11923            */
11924           var publicApi = {
11925             events: {
11926               rowEdit: {
11927                 /**
11928                  * @ngdoc event
11929                  * @eventOf ui.grid.rowEdit.api:PublicApi
11930                  * @name saveRow
11931                  * @description raised when a row is ready for saving.  Once your
11932                  * row has saved you may need to use angular.extend to update the
11933                  * data entity with any changed data from your save (for example, 
11934                  * lock version information if you're using optimistic locking,
11935                  * or last update time/user information).
11936                  * 
11937                  * Your method should call setSavePromise somewhere in the body before
11938                  * returning control.  The feature will then wait, with the gridRow greyed out 
11939                  * whilst this promise is being resolved.
11940                  * 
11941                  * <pre>
11942                  *      gridApi.rowEdit.on.saveRow(scope,function(rowEntity){})
11943                  * </pre>
11944                  * and somewhere within the event handler:
11945                  * <pre>
11946                  *      gridApi.rowEdit.setSavePromise( grid, rowEntity, savePromise)
11947                  * </pre>
11948                  * @param {object} rowEntity the options.data element that was edited
11949                  * @returns {promise} Your saveRow method should return a promise, the
11950                  * promise should either be resolved (implying successful save), or 
11951                  * rejected (implying an error).
11952                  */
11953                 saveRow: function (rowEntity) {
11954                 }
11955               }
11956             },
11957             methods: {
11958               rowEdit: {
11959                 /**
11960                  * @ngdoc method
11961                  * @methodOf ui.grid.rowEdit.api:PublicApi
11962                  * @name setSavePromise
11963                  * @description Sets the promise associated with the row save, mandatory that
11964                  * the saveRow event handler calls this method somewhere before returning.
11965                  * <pre>
11966                  *      gridApi.rowEdit.setSavePromise(grid, rowEntity)
11967                  * </pre>
11968                  * @param {object} grid the grid for which dirty rows should be returned
11969                  * @param {object} rowEntity a data row from the grid for which a save has
11970                  * been initiated
11971                  * @param {promise} savePromise the promise that will be resolved when the
11972                  * save is successful, or rejected if the save fails
11973                  * 
11974                  */
11975                 setSavePromise: function (grid, rowEntity, savePromise) {
11976                   service.setSavePromise(grid, rowEntity, savePromise);
11977                 },
11978                 /**
11979                  * @ngdoc method
11980                  * @methodOf ui.grid.rowEdit.api:PublicApi
11981                  * @name getDirtyRows
11982                  * @description Returns all currently dirty rows
11983                  * <pre>
11984                  *      gridApi.rowEdit.getDirtyRows(grid)
11985                  * </pre>
11986                  * @param {object} grid the grid for which dirty rows should be returned
11987                  * @returns {array} An array of gridRows that are currently dirty
11988                  * 
11989                  */
11990                 getDirtyRows: function (grid) {
11991                   return grid.rowEditDirtyRows;
11992                 },
11993                 /**
11994                  * @ngdoc method
11995                  * @methodOf ui.grid.rowEdit.api:PublicApi
11996                  * @name getErrorRows
11997                  * @description Returns all currently errored rows
11998                  * <pre>
11999                  *      gridApi.rowEdit.getErrorRows(grid)
12000                  * </pre>
12001                  * @param {object} grid the grid for which errored rows should be returned
12002                  * @returns {array} An array of gridRows that are currently in error
12003                  * 
12004                  */
12005                 getErrorRows: function (grid) {
12006                   return grid.rowEditErrorRows;
12007                 },
12008                 /**
12009                  * @ngdoc method
12010                  * @methodOf ui.grid.rowEdit.api:PublicApi
12011                  * @name flushDirtyRows
12012                  * @description Triggers a save event for all currently dirty rows, could
12013                  * be used where user presses a save button or navigates away from the page
12014                  * <pre>
12015                  *      gridApi.rowEdit.flushDirtyRows(grid)
12016                  * </pre>
12017                  * @param {object} grid the grid for which dirty rows should be flushed
12018                  * @returns {promise} a promise that represents the aggregate of all
12019                  * of the individual save promises - i.e. it will be resolved when all
12020                  * the individual save promises have been resolved.
12021                  * 
12022                  */
12023                 flushDirtyRows: function (grid) {
12024                   return service.flushDirtyRows(grid);
12025                 }
12026               }
12027             }
12028           };
12029
12030           grid.api.registerEventsFromObject(publicApi.events);
12031           grid.api.registerMethodsFromObject(publicApi.methods);
12032           
12033           grid.api.core.on.renderingComplete( scope, function ( gridApi ) {
12034             grid.api.edit.on.afterCellEdit( scope, service.endEditCell );
12035             grid.api.edit.on.beginCellEdit( scope, service.beginEditCell );
12036             grid.api.edit.on.cancelCellEdit( scope, service.cancelEditCell );
12037             
12038             if ( grid.api.cellNav ) {
12039               grid.api.cellNav.on.navigate( scope, service.navigate );
12040             }              
12041           });
12042
12043         },
12044
12045         defaultGridOptions: function (gridOptions) {
12046
12047           /**
12048            *  @ngdoc object
12049            *  @name ui.grid.rowEdit.api:GridOptions
12050            *
12051            *  @description Options for configuring the rowEdit feature, these are available to be  
12052            *  set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
12053            */
12054
12055         },
12056
12057
12058         /**
12059          * @ngdoc method
12060          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12061          * @name saveRow
12062          * @description  Returns a function that saves the specified row from the grid,
12063          * and returns a promise
12064          * @param {object} grid the grid for which dirty rows should be flushed
12065          * @param {GridRow} gridRow the row that should be saved
12066          * @returns {function} the saveRow function returns a function.  That function
12067          * in turn, when called, returns a promise relating to the save callback
12068          */
12069         saveRow: function ( grid, gridRow ) {
12070           var self = this;
12071
12072           return function() {
12073             gridRow.isSaving = true;
12074
12075             var promise = grid.api.rowEdit.raise.saveRow( gridRow.entity );
12076             
12077             if ( gridRow.rowEditSavePromise ){
12078               gridRow.rowEditSavePromise.then( self.processSuccessPromise( grid, gridRow ), self.processErrorPromise( grid, gridRow ));
12079             } else {
12080               $log.log( 'A promise was not returned when saveRow event was raised, either nobody is listening to event, or event handler did not return a promise' );
12081             }
12082             return promise;
12083           };
12084         },
12085         
12086
12087         /**
12088          * @ngdoc method
12089          * @methodOf  ui.grid.rowEdit.service:uiGridRowEditService
12090          * @name setSavePromise
12091          * @description Sets the promise associated with the row save, mandatory that
12092          * the saveRow event handler calls this method somewhere before returning.
12093          * <pre>
12094          *      gridApi.rowEdit.setSavePromise(grid, rowEntity)
12095          * </pre>
12096          * @param {object} grid the grid for which dirty rows should be returned
12097          * @param {object} rowEntity a data row from the grid for which a save has
12098          * been initiated
12099          * @param {promise} savePromise the promise that will be resolved when the
12100          * save is successful, or rejected if the save fails
12101          * 
12102          */
12103         setSavePromise: function (grid, rowEntity, savePromise) {
12104           var gridRow = grid.getRow( rowEntity );
12105           gridRow.rowEditSavePromise = savePromise;
12106         },
12107
12108
12109         /**
12110          * @ngdoc method
12111          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12112          * @name processSuccessPromise
12113          * @description  Returns a function that processes the successful
12114          * resolution of a save promise  
12115          * @param {object} grid the grid for which the promise should be processed
12116          * @param {GridRow} gridRow the row that has been saved
12117          * @returns {function} the success handling function
12118          */
12119         processSuccessPromise: function ( grid, gridRow ) {
12120           var self = this;
12121           
12122           return function() {
12123             delete gridRow.isSaving;
12124             delete gridRow.isDirty;
12125             delete gridRow.isError;
12126             delete gridRow.rowEditSaveTimer;
12127             self.removeRow( grid.rowEditErrorRows, gridRow );
12128             self.removeRow( grid.rowEditDirtyRows, gridRow );
12129           };
12130         },
12131         
12132
12133         /**
12134          * @ngdoc method
12135          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12136          * @name processErrorPromise
12137          * @description  Returns a function that processes the failed
12138          * resolution of a save promise  
12139          * @param {object} grid the grid for which the promise should be processed
12140          * @param {GridRow} gridRow the row that is now in error
12141          * @returns {function} the error handling function
12142          */
12143         processErrorPromise: function ( grid, gridRow ) {
12144           return function() {
12145             delete gridRow.isSaving;
12146             delete gridRow.rowEditSaveTimer;
12147
12148             gridRow.isError = true;
12149             
12150             if (!grid.rowEditErrorRows){
12151               grid.rowEditErrorRows = [];
12152             }
12153             grid.rowEditErrorRows.push( gridRow );
12154           };
12155         },
12156         
12157         
12158         /**
12159          * @ngdoc method
12160          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12161          * @name removeRow
12162          * @description  Removes a row from a cache of rows - either
12163          * grid.rowEditErrorRows or grid.rowEditDirtyRows.  If the row
12164          * is not present silently does nothing. 
12165          * @param {array} rowArray the array from which to remove the row
12166          * @param {GridRow} gridRow the row that should be removed
12167          */
12168         removeRow: function( rowArray, removeGridRow ){
12169           angular.forEach( rowArray, function( gridRow, index ){
12170             if ( gridRow.uid === removeGridRow.uid ){
12171               rowArray.splice( index, 1);
12172             }
12173           });
12174         },
12175         
12176         
12177         /**
12178          * @ngdoc method
12179          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12180          * @name flushDirtyRows
12181          * @description Triggers a save event for all currently dirty rows, could
12182          * be used where user presses a save button or navigates away from the page
12183          * <pre>
12184          *      gridApi.rowEdit.flushDirtyRows(grid)
12185          * </pre>
12186          * @param {object} grid the grid for which dirty rows should be flushed
12187          * @returns {promise} a promise that represents the aggregate of all
12188          * of the individual save promises - i.e. it will be resolved when all
12189          * the individual save promises have been resolved.
12190          * 
12191          */
12192         flushDirtyRows: function(grid){
12193           var promises = [];
12194           angular.forEach(grid.rowEditDirtyRows, function( gridRow ){
12195             service.saveRow( grid, gridRow )();
12196             promises.push( gridRow.rowEditSavePromise );
12197           });
12198           
12199           return $q.all( promises );
12200         },
12201         
12202         
12203         /**
12204          * @ngdoc property
12205          * @propertyOf ui.grid.rowEdit.api:GridOptions
12206          * @name rowEditWaitInterval
12207          * @description How long the grid should wait for another change on this row
12208          * before triggering a save (in milliseconds)
12209          * 
12210          * @example
12211          * Setting the wait interval to 4 seconds
12212          * <pre>
12213          *   $scope.gridOptions = { rowEditWaitInterval: 4000 }
12214          * </pre>
12215          * 
12216          */
12217         /**
12218          * @ngdoc method
12219          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12220          * @name endEditCell
12221          * @description Receives an afterCellEdit event from the edit function,
12222          * and sets flags as appropriate.  Only the rowEntity parameter
12223          * is processed, although other params are available.  Grid
12224          * is automatically provided by the gridApi. 
12225          * @param {object} rowEntity the data entity for which the cell
12226          * was edited
12227          */        
12228         endEditCell: function( rowEntity, colDef, newValue, previousValue ){
12229           var grid = this.grid;
12230           var gridRow = grid.getRow( rowEntity );
12231           if ( !gridRow ){ $log.log( 'Unable to find rowEntity in grid data, dirty flag cannot be set' ); return; }
12232
12233           if ( newValue !== previousValue || gridRow.isDirty ){
12234             if ( !grid.rowEditDirtyRows ){
12235               grid.rowEditDirtyRows = [];
12236             }
12237             
12238             if ( !gridRow.isDirty ){
12239               gridRow.isDirty = true;
12240               grid.rowEditDirtyRows.push( gridRow );
12241             }
12242             
12243             delete gridRow.isError;
12244             
12245             service.considerSetTimer( grid, gridRow );
12246           }
12247         },
12248         
12249         
12250         /**
12251          * @ngdoc method
12252          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12253          * @name beginEditCell
12254          * @description Receives a beginCellEdit event from the edit function,
12255          * and cancels any rowEditSaveTimers if present, as the user is still editing
12256          * this row.  Only the rowEntity parameter
12257          * is processed, although other params are available.  Grid
12258          * is automatically provided by the gridApi. 
12259          * @param {object} rowEntity the data entity for which the cell
12260          * editing has commenced
12261          */
12262         beginEditCell: function( rowEntity, colDef ){
12263           var grid = this.grid;
12264           var gridRow = grid.getRow( rowEntity );
12265           if ( !gridRow ){ $log.log( 'Unable to find rowEntity in grid data, timer cannot be cancelled' ); return; }
12266           
12267           service.cancelTimer( grid, gridRow );
12268         },
12269
12270
12271         /**
12272          * @ngdoc method
12273          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12274          * @name cancelEditCell
12275          * @description Receives a cancelCellEdit event from the edit function,
12276          * and if the row was already dirty, restarts the save timer.  If the row
12277          * was not already dirty, then it's not dirty now either and does nothing.
12278          * 
12279          * Only the rowEntity parameter
12280          * is processed, although other params are available.  Grid
12281          * is automatically provided by the gridApi.
12282          *  
12283          * @param {object} rowEntity the data entity for which the cell
12284          * editing was cancelled
12285          */        
12286         cancelEditCell: function( rowEntity, colDef ){
12287           var grid = this.grid;
12288           var gridRow = grid.getRow( rowEntity );
12289           if ( !gridRow ){ $log.log( 'Unable to find rowEntity in grid data, timer cannot be set' ); return; }
12290           
12291           service.considerSetTimer( grid, gridRow );
12292         },
12293         
12294         
12295         /**
12296          * @ngdoc method
12297          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12298          * @name navigate
12299          * @description cellNav tells us that the selected cell has changed.  If
12300          * the new row had a timer running, then stop it similar to in a beginCellEdit
12301          * call.  If the old row is dirty and not the same as the new row, then 
12302          * start a timer on it.
12303          * @param {object} newRowCol the row and column that were selected
12304          * @param {object} oldRowCol the row and column that was left
12305          * 
12306          */
12307         navigate: function( newRowCol, oldRowCol ){
12308           var grid = this.grid;
12309           if ( newRowCol.row.rowEditSaveTimer ){
12310             service.cancelTimer( grid, newRowCol.row );
12311           }
12312
12313           if ( oldRowCol && oldRowCol.row && oldRowCol.row !== newRowCol.row ){
12314             service.considerSetTimer( grid, oldRowCol.row );
12315           }
12316         },
12317         
12318         
12319         /**
12320          * @ngdoc method
12321          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12322          * @name considerSetTimer
12323          * @description Consider setting a timer on this row (if it is dirty).  if there is a timer running 
12324          * on the row and the row isn't currently saving, cancel it, using cancelTimer, then if the row is 
12325          * dirty and not currently saving then set a new timer
12326          * @param {object} grid the grid for which we are processing
12327          * @param {GridRow} gridRow the row for which the timer should be adjusted
12328          * 
12329          */
12330         considerSetTimer: function( grid, gridRow ){
12331           service.cancelTimer( grid, gridRow );
12332           
12333           if ( gridRow.isDirty && !gridRow.isSaving ){
12334             var waitTime = grid.options.rowEditWaitInterval ? grid.options.rowEditWaitInterval : 2000;
12335             gridRow.rowEditSaveTimer = $interval( service.saveRow( grid, gridRow ), waitTime, 1);
12336           }
12337         },
12338         
12339
12340         /**
12341          * @ngdoc method
12342          * @methodOf ui.grid.rowEdit.service:uiGridRowEditService
12343          * @name cancelTimer
12344          * @description cancel the $interval for any timer running on this row
12345          * then delete the timer itself
12346          * @param {object} grid the grid for which we are processing
12347          * @param {GridRow} gridRow the row for which the timer should be adjusted
12348          * 
12349          */
12350         cancelTimer: function( grid, gridRow ){
12351           if ( gridRow.rowEditSaveTimer && !gridRow.isSaving ){
12352             $interval.cancel(gridRow.rowEditSaveTimer);
12353             delete gridRow.rowEditSaveTimer;
12354           }
12355         }
12356       };
12357
12358       return service;
12359
12360     }]);
12361
12362   /**
12363    *  @ngdoc directive
12364    *  @name ui.grid.rowEdit.directive:uiGridEdit
12365    *  @element div
12366    *  @restrict A
12367    *
12368    *  @description Adds row editing features to the ui-grid-edit directive.
12369    *
12370    */
12371   module.directive('uiGridRowEdit', ['$log', 'uiGridRowEditService', 'uiGridEditConstants', 
12372   function ($log, uiGridRowEditService, uiGridEditConstants) {
12373     return {
12374       replace: true,
12375       priority: 0,
12376       require: '^uiGrid',
12377       scope: false,
12378       compile: function () {
12379         return {
12380           pre: function ($scope, $elm, $attrs, uiGridCtrl) {
12381             uiGridRowEditService.initializeGrid($scope, uiGridCtrl.grid);
12382           },
12383           post: function ($scope, $elm, $attrs, uiGridCtrl) {            
12384           }
12385         };
12386       }
12387     };
12388   }]);
12389
12390
12391   /**
12392    *  @ngdoc directive
12393    *  @name ui.grid.rowEdit.directive:uiGridViewport
12394    *  @element div
12395    *
12396    *  @description Stacks on top of ui.grid.uiGridViewport to alter the attributes used
12397    *  for the grid row to allow coloring of saving and error rows
12398    */
12399   module.directive('uiGridViewport',
12400     ['$compile', 'uiGridConstants', '$log', '$parse',
12401       function ($compile, uiGridConstants, $log, $parse) {
12402         return {
12403           priority: -200, // run after default  directive
12404           scope: false,
12405           compile: function ($elm, $attrs) {
12406             var rowRepeatDiv = angular.element($elm.children().children()[0]);
12407             rowRepeatDiv.attr("ng-class", "{'ui-grid-row-saving': row.isSaving, 'ui-grid-row-error': row.isError}");
12408             return {
12409               pre: function ($scope, $elm, $attrs, controllers) {
12410
12411               },
12412               post: function ($scope, $elm, $attrs, controllers) {
12413               }
12414             };
12415           }
12416         };
12417       }]);
12418
12419 })();
12420
12421 (function () {
12422   'use strict';
12423
12424   /**
12425    * @ngdoc overview
12426    * @name ui.grid.selection
12427    * @description
12428    *
12429    *  # ui.grid.selection
12430    * This module provides row selection
12431    * <br/>
12432    * <br/>
12433    *
12434    * <div doc-module-components="ui.grid.selection"></div>
12435    */
12436
12437   var module = angular.module('ui.grid.selection', ['ui.grid']);
12438
12439   /**
12440    *  @ngdoc object
12441    *  @name ui.grid.selection.constant:uiGridSelectionConstants
12442    *
12443    *  @description constants available in selection module
12444    */
12445   module.constant('uiGridSelectionConstants', {
12446     featureName: "selection"
12447   });
12448
12449   /**
12450    *  @ngdoc service
12451    *  @name ui.grid.selection.service:uiGridSelectionService
12452    *
12453    *  @description Services for selection features
12454    */
12455   module.service('uiGridSelectionService', ['$log', '$q', '$templateCache', 'uiGridSelectionConstants', 'gridUtil',
12456     function ($log, $q, $templateCache, uiGridSelectionConstants, gridUtil) {
12457
12458       var service = {
12459
12460         initializeGrid: function (grid) {
12461
12462           //add feature namespace and any properties to grid for needed state
12463           grid.selection = {};
12464           grid.selection.lastSelectedRow = null;
12465
12466           service.defaultGridOptions(grid.options);
12467
12468           /**
12469            *  @ngdoc object
12470            *  @name ui.grid.selection.api:PublicApi
12471            *
12472            *  @description Public Api for selection feature
12473            */
12474           var publicApi = {
12475             events: {
12476               selection: {
12477                 /**
12478                  * @ngdoc event
12479                  * @name rowSelectionChanged
12480                  * @eventOf  ui.grid.selection.api:PublicApi
12481                  * @description  is raised after the row.isSelected state is changed
12482                  * @param {GridRow} row the row that was selected/deselected
12483                  */
12484                 rowSelectionChanged: function (scope, row) {
12485                 }
12486               }
12487             },
12488             methods: {
12489               selection: {
12490                 /**
12491                  * @ngdoc function
12492                  * @name toggleRowSelection
12493                  * @methodOf  ui.grid.selection.api:PublicApi
12494                  * @description Toggles data row as selected or unselected
12495                  * @param {object} rowEntity gridOptions.data[] array instance
12496                  */
12497                 toggleRowSelection: function (rowEntity) {
12498                   var row = grid.getRow(rowEntity);
12499                   if (row !== null) {
12500                     service.toggleRowSelection(grid, row, grid.options.multiSelect);
12501                   }
12502                 },
12503                 /**
12504                  * @ngdoc function
12505                  * @name selectRow
12506                  * @methodOf  ui.grid.selection.api:PublicApi
12507                  * @description Select the data row
12508                  * @param {object} rowEntity gridOptions.data[] array instance
12509                  */
12510                 selectRow: function (rowEntity) {
12511                   var row = grid.getRow(rowEntity);
12512                   if (row !== null && !row.isSelected) {
12513                     service.toggleRowSelection(grid, row, grid.options.multiSelect);
12514                   }
12515                 },
12516                 /**
12517                  * @ngdoc function
12518                  * @name unSelectRow
12519                  * @methodOf  ui.grid.selection.api:PublicApi
12520                  * @description UnSelect the data row
12521                  * @param {object} rowEntity gridOptions.data[] array instance
12522                  */
12523                 unSelectRow: function (rowEntity) {
12524                   var row = grid.getRow(rowEntity);
12525                   if (row !== null && row.isSelected) {
12526                     service.toggleRowSelection(grid, row, grid.options.multiSelect);
12527                   }
12528                 },
12529                 /**
12530                  * @ngdoc function
12531                  * @name selectAllRows
12532                  * @methodOf  ui.grid.selection.api:PublicApi
12533                  * @description Selects all rows.  Does nothing if multiSelect = false
12534                  */
12535                 selectAllRows: function () {
12536                   if (grid.options.multiSelect === false) {
12537                     return;
12538                   }
12539
12540                   grid.rows.forEach(function (row) {
12541                     row.isSelected = true;
12542                   });
12543                 },
12544                 /**
12545                  * @ngdoc function
12546                  * @name selectAllVisibleRows
12547                  * @methodOf  ui.grid.selection.api:PublicApi
12548                  * @description Selects all visible rows.  Does nothing if multiSelect = false
12549                  */
12550                 selectAllVisibleRows: function () {
12551                   if (grid.options.multiSelect === false) {
12552                     return;
12553                   }
12554
12555                   grid.rows.forEach(function (row) {
12556                     if (row.visible) {
12557                       row.isSelected = true;
12558                     } else {
12559                       row.isSelected = false;
12560                     }
12561                   });
12562                 },
12563                 /**
12564                  * @ngdoc function
12565                  * @name clearSelectedRows
12566                  * @methodOf  ui.grid.selection.api:PublicApi
12567                  * @description Unselects all rows
12568                  */
12569                 clearSelectedRows: function () {
12570                   service.clearSelectedRows(grid);
12571                 },
12572                 /**
12573                  * @ngdoc function
12574                  * @name getSelectedRows
12575                  * @methodOf  ui.grid.selection.api:PublicApi
12576                  * @description returns all selectedRow's entity references
12577                  */
12578                 getSelectedRows: function () {
12579                   return service.getSelectedRows(grid).map(function (gridRow) {
12580                     return gridRow.entity;
12581                   });
12582                 },
12583                 /**
12584                  * @ngdoc function
12585                  * @name getSelectedGridRows
12586                  * @methodOf  ui.grid.selection.api:PublicApi
12587                  * @description returns all selectedRow's as gridRows
12588                  */
12589                 getSelectedGridRows: function () {
12590                   return service.getSelectedRows(grid);
12591                 },
12592                 /**
12593                  * @ngdoc function
12594                  * @name setMultiSelect
12595                  * @methodOf  ui.grid.selection.api:PublicApi
12596                  * @description Sets the current gridOption.multiSelect to true or false
12597                  * @param {bool} multiSelect true to allow multiple rows
12598                  */
12599                 setMultiSelect: function (multiSelect) {
12600                   grid.options.multiSelect = multiSelect;
12601                 }
12602               }
12603             }
12604           };
12605
12606           grid.api.registerEventsFromObject(publicApi.events);
12607
12608           grid.api.registerMethodsFromObject(publicApi.methods);
12609
12610         },
12611
12612         defaultGridOptions: function (gridOptions) {
12613           //default option to true unless it was explicitly set to false
12614           /**
12615            *  @ngdoc object
12616            *  @name ui.grid.selection.api:GridOptions
12617            *
12618            *  @description GridOptions for selection feature, these are available to be  
12619            *  set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
12620            */
12621
12622           /**
12623            *  @ngdoc object
12624            *  @name enableRowSelection
12625            *  @propertyOf  ui.grid.selection.api:GridOptions
12626            *  @description Enable row selection for entire grid.
12627            *  <br/>Defaults to true
12628            */
12629           gridOptions.enableRowSelection = gridOptions.enableRowSelection !== false;
12630           /**
12631            *  @ngdoc object
12632            *  @name multiSelect
12633            *  @propertyOf  ui.grid.selection.api:GridOptions
12634            *  @description Enable multiple row selection for entire grid
12635            *  <br/>Defaults to true
12636            */
12637           gridOptions.multiSelect = gridOptions.multiSelect !== false;
12638           /**
12639            *  @ngdoc object
12640            *  @name enableRowHeaderSelection
12641            *  @propertyOf  ui.grid.selection.api:GridOptions
12642            *  @description Enable a row header to be used for selection
12643            *  <br/>Defaults to true
12644            */
12645           gridOptions.enableRowHeaderSelection = gridOptions.enableRowHeaderSelection !== false;
12646         },
12647
12648         /**
12649          * @ngdoc function
12650          * @name toggleRowSelection
12651          * @methodOf  ui.grid.selection.service:uiGridSelectionService
12652          * @description Toggles row as selected or unselected
12653          * @param {Grid} grid grid object
12654          * @param {GridRow} row row to select or deselect
12655          * @param {bool} multiSelect if false, only one row at time can be selected
12656          */
12657         toggleRowSelection: function (grid, row, multiSelect) {
12658           var selected = row.isSelected;
12659           if (!multiSelect && !selected) {
12660             service.clearSelectedRows(grid);
12661           }
12662           row.isSelected = !selected;
12663           if (row.isSelected === true) {
12664             grid.selection.lastSelectedRow = row;
12665           }
12666           grid.api.selection.raise.rowSelectionChanged(row);
12667         },
12668         /**
12669          * @ngdoc function
12670          * @name shiftSelect
12671          * @methodOf  ui.grid.selection.service:uiGridSelectionService
12672          * @description selects a group of rows from the last selected row using the shift key
12673          * @param {Grid} grid grid object
12674          * @param {GridRow} clicked row
12675          * @param {bool} multiSelect if false, does nothing this is for multiSelect only
12676          */
12677         shiftSelect: function (grid, row, multiSelect) {
12678           if (!multiSelect) {
12679             return;
12680           }
12681           var selectedRows = service.getSelectedRows(grid);
12682           var fromRow = selectedRows.length > 0 ? grid.renderContainers.body.visibleRowCache.indexOf(grid.selection.lastSelectedRow) : 0;
12683           var toRow = grid.renderContainers.body.visibleRowCache.indexOf(row);
12684           //reverse select direction
12685           if (fromRow > toRow) {
12686             var tmp = fromRow;
12687             fromRow = toRow;
12688             toRow = tmp;
12689           }
12690           for (var i = fromRow; i <= toRow; i++) {
12691             var rowToSelect = grid.renderContainers.body.visibleRowCache[i];
12692             if (rowToSelect) {
12693               rowToSelect.isSelected = true;
12694               grid.selection.lastSelectedRow = rowToSelect;
12695               grid.api.selection.raise.rowSelectionChanged(rowToSelect);
12696             }
12697           }
12698         },
12699         /**
12700          * @ngdoc function
12701          * @name getSelectedRows
12702          * @methodOf  ui.grid.selection.service:uiGridSelectionService
12703          * @description Returns all the selected rows
12704          * @param {Grid} grid grid object
12705          */
12706         getSelectedRows: function (grid) {
12707           return grid.rows.filter(function (row) {
12708             return row.isSelected;
12709           });
12710         },
12711
12712         /**
12713          * @ngdoc function
12714          * @name clearSelectedRows
12715          * @methodOf  ui.grid.selection.service:uiGridSelectionService
12716          * @description Clears all selected rows
12717          * @param {Grid} grid grid object
12718          */
12719         clearSelectedRows: function (grid) {
12720           service.getSelectedRows(grid).forEach(function (row) {
12721             row.isSelected = false;
12722             grid.api.selection.raise.rowSelectionChanged(row);
12723           });
12724         }
12725
12726
12727       };
12728
12729       return service;
12730
12731     }]);
12732
12733   /**
12734    *  @ngdoc directive
12735    *  @name ui.grid.selection.directive:uiGridSelection
12736    *  @element div
12737    *  @restrict A
12738    *
12739    *  @description Adds selection features to grid
12740    *
12741    *  @example
12742    <example module="app">
12743    <file name="app.js">
12744    var app = angular.module('app', ['ui.grid', 'ui.grid.edit']);
12745
12746    app.controller('MainCtrl', ['$scope', function ($scope) {
12747       $scope.data = [
12748         { name: 'Bob', title: 'CEO' },
12749             { name: 'Frank', title: 'Lowly Developer' }
12750       ];
12751
12752       $scope.columnDefs = [
12753         {name: 'name', enableCellEdit: true},
12754         {name: 'title', enableCellEdit: true}
12755       ];
12756     }]);
12757    </file>
12758    <file name="index.html">
12759    <div ng-controller="MainCtrl">
12760    <div ui-grid="{ data: data, columnDefs: columnDefs }" ui-grid-selection></div>
12761    </div>
12762    </file>
12763    </example>
12764    */
12765   module.directive('uiGridSelection', ['$log', 'uiGridSelectionConstants', 'uiGridSelectionService', '$templateCache',
12766     function ($log, uiGridSelectionConstants, uiGridSelectionService, $templateCache) {
12767       return {
12768         replace: true,
12769         priority: 0,
12770         require: '^uiGrid',
12771         scope: false,
12772         compile: function () {
12773           return {
12774             pre: function ($scope, $elm, $attrs, uiGridCtrl) {
12775               uiGridSelectionService.initializeGrid(uiGridCtrl.grid);
12776               if (uiGridCtrl.grid.options.enableRowHeaderSelection) {
12777                 var cellTemplate = 'ui-grid/selectionRowHeader';
12778                 var selectionRowHeaderDef = { name: 'selectionRowHeaderCol', displayName: '', width: 30, cellTemplate: cellTemplate};
12779                 uiGridCtrl.grid.addRowHeaderColumn(selectionRowHeaderDef);
12780               }
12781             },
12782             post: function ($scope, $elm, $attrs, uiGridCtrl) {
12783
12784             }
12785           };
12786         }
12787       };
12788     }]);
12789
12790   module.directive('uiGridSelectionRowHeaderButtons', ['$log', '$templateCache', 'uiGridSelectionService',
12791     function ($log, $templateCache, uiGridSelectionService) {
12792       return {
12793         replace: true,
12794         restrict: 'E',
12795         template: $templateCache.get('ui-grid/selectionRowHeaderButtons'),
12796         scope: true,
12797         require: '^uiGrid',
12798         link: function($scope, $elm, $attrs, uiGridCtrl) {
12799           var self = uiGridCtrl.grid;
12800           $scope.selectButtonClick = function(row, evt) {
12801             if (evt.shiftKey) {
12802               uiGridSelectionService.shiftSelect(self, row, self.options.multiSelect);
12803
12804             }
12805             else {
12806               uiGridSelectionService.toggleRowSelection(self, row, self.options.multiSelect);
12807             }
12808           };
12809         }
12810       };
12811     }]);
12812
12813   /**
12814    *  @ngdoc directive
12815    *  @name ui.grid.selection.directive:uiGridViewport
12816    *  @element div
12817    *
12818    *  @description Stacks on top of ui.grid.uiGridViewport to alter the attributes used
12819    *  for the grid row
12820    */
12821   module.directive('uiGridViewport',
12822     ['$compile', 'uiGridConstants', 'uiGridSelectionConstants', '$log', '$parse', 'uiGridSelectionService',
12823       function ($compile, uiGridConstants, uiGridSelectionConstants, $log, $parse, uiGridSelectionService) {
12824         return {
12825           priority: -200, // run after default  directive
12826           scope: false,
12827           compile: function ($elm, $attrs) {
12828             var rowRepeatDiv = angular.element($elm.children().children()[0]);
12829             rowRepeatDiv.attr("ng-class", "{'ui-grid-row-selected': row.isSelected}");
12830             return {
12831               pre: function ($scope, $elm, $attrs, controllers) {
12832
12833               },
12834               post: function ($scope, $elm, $attrs, controllers) {
12835               }
12836             };
12837           }
12838         };
12839       }]);
12840
12841   /**
12842    *  @ngdoc directive
12843    *  @name ui.grid.selection.directive:uiGridCell
12844    *  @element div
12845    *  @restrict A
12846    *
12847    *  @description Stacks on top of ui.grid.uiGridCell to provide selection feature
12848    */
12849   module.directive('uiGridCell',
12850     ['$compile', 'uiGridConstants', 'uiGridSelectionConstants', '$log', '$parse', 'uiGridSelectionService',
12851       function ($compile, uiGridConstants, uiGridSelectionConstants, $log, $parse, uiGridSelectionService) {
12852         return {
12853           priority: -200, // run after default uiGridCell directive
12854           restrict: 'A',
12855           scope: false,
12856           link: function ($scope, $elm, $attrs) {
12857
12858             if ($scope.grid.options.enableRowSelection && !$scope.grid.options.enableRowHeaderSelection) {
12859               $elm.addClass('ui-grid-disable-selection');
12860               registerRowSelectionEvents();
12861             }
12862
12863             function registerRowSelectionEvents() {
12864               $elm.on('click', function (evt) {
12865                 if (evt.shiftKey) {
12866                   uiGridSelectionService.shiftSelect($scope.grid, $scope.row, $scope.grid.options.multiSelect);
12867
12868                 }
12869                 else {
12870                   uiGridSelectionService.toggleRowSelection($scope.grid, $scope.row, $scope.grid.options.multiSelect);
12871                 }
12872                 $scope.$apply();
12873               });
12874             }
12875           }
12876         };
12877       }]);
12878
12879 })();
12880 angular.module('ui.grid').run(['$templateCache', function($templateCache) {
12881   'use strict';
12882
12883   $templateCache.put('ui-grid/ui-grid-footer',
12884     "<div class=\"ui-grid-footer-panel\"><div ui-grid-group-panel ng-show=\"grid.options.showGroupPanel\"></div><div class=\"ui-grid-footer ui-grid-footer-viewport\"><div class=\"ui-grid-footer-canvas\"><div ng-repeat=\"col in colContainer.renderedColumns track by col.colDef.name\" ui-grid-footer-cell col=\"col\" render-index=\"$index\" class=\"ui-grid-footer-cell clearfix\" ng-style=\"$index === 0 && colContainer.columnStyle($index)\"></div></div></div></div>"
12885   );
12886
12887
12888   $templateCache.put('ui-grid/ui-grid-group-panel',
12889     "<div class=\"ui-grid-group-panel\"><div ui-t=\"groupPanel.description\" class=\"description\" ng-show=\"groupings.length == 0\"></div><ul ng-show=\"groupings.length > 0\" class=\"ngGroupList\"><li class=\"ngGroupItem\" ng-repeat=\"group in configGroups\"><span class=\"ngGroupElement\"><span class=\"ngGroupName\">{{group.displayName}} <span ng-click=\"removeGroup($index)\" class=\"ngRemoveGroup\">x</span></span> <span ng-hide=\"$last\" class=\"ngGroupArrow\"></span></span></li></ul></div>"
12890   );
12891
12892
12893   $templateCache.put('ui-grid/ui-grid-header',
12894     "<div class=\"ui-grid-top-panel\"><div ui-grid-group-panel ng-show=\"grid.options.showGroupPanel\"></div><div class=\"ui-grid-header ui-grid-header-viewport\"><div class=\"ui-grid-header-canvas\"><div class=\"ui-grid-header-cell clearfix\" ng-repeat=\"col in colContainer.renderedColumns track by col.colDef.name\" ui-grid-header-cell col=\"col\" render-index=\"$index\" ng-style=\"$index === 0 && colContainer.columnStyle($index)\"></div></div></div><div ui-grid-menu></div></div>"
12895   );
12896
12897
12898   $templateCache.put('ui-grid/ui-grid-no-header',
12899     "<div class=\"ui-grid-top-panel\"></div>"
12900   );
12901
12902
12903   $templateCache.put('ui-grid/ui-grid-row',
12904     "<div ng-repeat=\"(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name\" class=\"ui-grid-cell\" ng-class=\"{ 'ui-grid-row-header-cell': col.isRowHeader }\" ui-grid-cell></div>"
12905   );
12906
12907
12908   $templateCache.put('ui-grid/ui-grid',
12909     "<div ui-i18n=\"en\" class=\"ui-grid\"><!-- TODO (c0bra): add \"scoped\" attr here, eventually? --><style ui-grid-style>.grid{{ grid.id }} {\n" +
12910     "      /* Styles for the grid */\n" +
12911     "    }\n" +
12912     "\n" +
12913     "    .grid{{ grid.id }} .ui-grid-row, .grid{{ grid.id }} .ui-grid-cell, .grid{{ grid.id }} .ui-grid-cell .ui-grid-vertical-bar {\n" +
12914     "      height: {{ grid.options.rowHeight }}px;\n" +
12915     "    }\n" +
12916     "\n" +
12917     "    .grid{{ grid.id }} .ui-grid-row:last-child .ui-grid-cell {\n" +
12918     "      border-bottom-width: {{ ((grid.getTotalRowHeight() < grid.getViewportHeight()) && '1') || '0' }}px;\n" +
12919     "    }\n" +
12920     "\n" +
12921     "    {{ grid.verticalScrollbarStyles }}\n" +
12922     "    {{ grid.horizontalScrollbarStyles }}\n" +
12923     "\n" +
12924     "    .ui-grid[dir=rtl] .ui-grid-viewport {\n" +
12925     "      padding-left: {{ grid.verticalScrollbarWidth }}px;\n" +
12926     "    }\n" +
12927     "\n" +
12928     "    {{ grid.customStyles }}</style><div ui-grid-render-container container-id=\"'body'\" col-container-name=\"'body'\" row-container-name=\"'body'\" bind-scroll-horizontal=\"true\" bind-scroll-vertical=\"true\" enable-scrollbars=\"grid.options.enableScrollbars\"></div><div ui-grid-column-menu ng-if=\"grid.options.enableColumnMenu\"></div><div ng-transclude></div></div>"
12929   );
12930
12931
12932   $templateCache.put('ui-grid/uiGridCell',
12933     "<div class=\"ui-grid-cell-contents\">{{COL_FIELD CUSTOM_FILTERS}}</div>"
12934   );
12935
12936
12937   $templateCache.put('ui-grid/uiGridColumnFilter',
12938     "<li class=\"ui-grid-menu-item ui-grid-clearfix ui-grid-column-filter\" ng-show=\"itemShown()\" ng-click=\"$event.stopPropagation();\"><div class=\"input-container\"><input class=\"column-filter-input\" type=\"text\" ng-model=\"item.model\" placeholder=\"{{ i18n.search.placeholder }}\"><div class=\"column-filter-cancel-icon-container\"><i class=\"ui-grid-filter-cancel ui-grid-icon-cancel column-filter-cancel-icon\">&nbsp;</i></div></div><div style=\"button-container\" ng-click=\"item.action($event)\"><div class=\"ui-grid-button\"><i class=\"ui-grid-icon-search\">&nbsp;</i></div></div></li>"
12939   );
12940
12941
12942   $templateCache.put('ui-grid/uiGridColumnMenu',
12943     "<div class=\"ui-grid-column-menu\"><div ui-grid-menu menu-items=\"menuItems\"><!-- <div class=\"ui-grid-column-menu\">\n" +
12944     "    <div class=\"inner\" ng-show=\"menuShown\">\n" +
12945     "      <ul>\n" +
12946     "        <div ng-show=\"grid.options.enableSorting\">\n" +
12947     "          <li ng-click=\"sortColumn($event, asc)\" ng-class=\"{ 'selected' : col.sort.direction == asc }\"><i class=\"ui-grid-icon-sort-alt-up\"></i> Sort Ascending</li>\n" +
12948     "          <li ng-click=\"sortColumn($event, desc)\" ng-class=\"{ 'selected' : col.sort.direction == desc }\"><i class=\"ui-grid-icon-sort-alt-down\"></i> Sort Descending</li>\n" +
12949     "          <li ng-show=\"col.sort.direction\" ng-click=\"unsortColumn()\"><i class=\"ui-grid-icon-cancel\"></i> Remove Sort</li>\n" +
12950     "        </div>\n" +
12951     "      </ul>\n" +
12952     "    </div>\n" +
12953     "  </div> --></div></div>"
12954   );
12955
12956
12957   $templateCache.put('ui-grid/uiGridFooterCell',
12958     "<div class=\"ui-grid-cell-contents\" col-index=\"renderIndex\"><div>{{ col.getAggregationValue() }}</div></div>"
12959   );
12960
12961
12962   $templateCache.put('ui-grid/uiGridHeaderCell',
12963     "<div ng-class=\"{ 'sortable': sortable }\"><div class=\"ui-grid-vertical-bar\">&nbsp;</div><div class=\"ui-grid-cell-contents\" col-index=\"renderIndex\">{{ col.displayName CUSTOM_FILTERS }} <span ui-grid-visible=\"col.sort.direction\" ng-class=\"{ 'ui-grid-icon-up-dir': col.sort.direction == asc, 'ui-grid-icon-down-dir': col.sort.direction == desc, 'ui-grid-icon-blank': !col.sort.direction }\">&nbsp;</span></div><div ng-if=\"grid.options.enableColumnMenu && !col.isRowHeader\" class=\"ui-grid-column-menu-button\" ng-click=\"toggleMenu($event)\"><i class=\"ui-grid-icon-angle-down\">&nbsp;<i></i></i></div><div ng-if=\"filterable\" class=\"ui-grid-filter-container\" ng-repeat=\"colFilter in col.filters\"><input type=\"text\" class=\"ui-grid-filter-input\" ng-model=\"colFilter.term\" ng-click=\"$event.stopPropagation()\" ng-attr-placeholder=\"{{colFilter.placeholder || ''}}\"><div class=\"ui-grid-filter-button\" ng-click=\"colFilter.term = null\"><i class=\"ui-grid-icon-cancel right\" ng-show=\"!!colFilter.term\">&nbsp;</i> <!-- use !! because angular interprets 'f' as false --></div></div></div>"
12964   );
12965
12966
12967   $templateCache.put('ui-grid/uiGridMenu',
12968     "<div class=\"ui-grid-menu\"><div class=\"ui-grid-menu-inner\" ng-show=\"shown\"><ul class=\"ui-grid-menu-items\"><li ng-repeat=\"item in menuItems\" ui-grid-menu-item action=\"item.action\" title=\"item.title\" active=\"item.active\" icon=\"item.icon\" shown=\"item.shown\" context=\"item.context\" template-url=\"item.templateUrl\"></li></ul></div></div>"
12969   );
12970
12971
12972   $templateCache.put('ui-grid/uiGridMenuItem',
12973     "<li class=\"ui-grid-menu-item\" ng-click=\"itemAction($event, title)\" ng-show=\"itemShown()\" ng-class=\"{ 'ui-grid-menu-item-active' : active() }\"><i ng-class=\"icon\"></i> {{ title }}</li>"
12974   );
12975
12976
12977   $templateCache.put('ui-grid/uiGridRenderContainer',
12978     "<div class=\"ui-grid-render-container\"><div ui-grid-header></div><div ui-grid-viewport></div><div ui-grid-footer ng-if=\"grid.options.showFooter\"></div><!-- native scrolling --><div ui-grid-native-scrollbar ng-if=\"enableScrollbars\" type=\"vertical\"></div><div ui-grid-native-scrollbar ng-if=\"enableScrollbars\" type=\"horizontal\"></div></div>"
12979   );
12980
12981
12982   $templateCache.put('ui-grid/uiGridViewport',
12983     "<div class=\"ui-grid-viewport\"><div class=\"ui-grid-canvas\"><div ng-repeat=\"(rowRenderIndex, row) in rowContainer.renderedRows track by row.uid\" class=\"ui-grid-row\" ng-style=\"containerCtrl.rowStyle(rowRenderIndex)\"><div ui-grid-row=\"row\" row-render-index=\"rowRenderIndex\"></div></div></div></div>"
12984   );
12985
12986
12987   $templateCache.put('ui-grid/cellEditor',
12988     "<div><form name=\"inputForm\"><input type=\"{{inputType}}\" ng-class=\"'colt' + col.index\" ui-grid-editor ng-model=\"COL_FIELD\"></form></div>"
12989   );
12990
12991
12992   $templateCache.put('ui-grid/dropdownEditor',
12993     "<div><form name=\"inputForm\"><select ng-class=\"'colt' + col.index\" ui-grid-edit-dropdown ng-model=\"COL_FIELD\" ng-options=\"field[editDropdownIdLabel] as field[editDropdownValueLabel] CUSTOM_FILTERS for field in editDropdownOptionsArray\"></select></form></div>"
12994   );
12995
12996
12997   $templateCache.put('ui-grid/expandableRow',
12998     "<div ui-grid-expandable-row ng-if=\"expandableRow.shouldRenderExpand()\" class=\"expandableRow\" style=\"float:left;margin-top: 1px;margin-bottom: 1px\" ng-style=\"{width: (grid.renderContainers.body.getCanvasWidth() - grid.verticalScrollbarWidth)\n" +
12999     "     ,height: grid.options.expandable.expandableRowHeight}\"></div>"
13000   );
13001
13002
13003   $templateCache.put('ui-grid/expandableRowHeader',
13004     "<div class=\"ui-grid-row-header-cell uiGridExpandableButtonsCell\"><div class=\"ui-grid-cell-contents\"><i ng-class=\"{'ui-grid-icon-plus-squared':!row.isExpanded, 'ui-grid-icon-minus-squared':row.isExpanded}\" ng-click=\"grid.api.expandable.toggleRowExpansion(row.entity)\"></i></div></div>"
13005   );
13006
13007
13008   $templateCache.put('ui-grid/expandableScrollFiller',
13009     "<div ng-if=\"expandableRow.shouldRenderFiller()\" style=\"float:left;margin-top: 2px;margin-bottom: 2px\" ng-style=\"{width: (grid.getViewportWidth())\n" +
13010     "     ,height: grid.options.expandable.expandableRowHeight, 'margin-left': grid.options.rowHeader.rowHeaderWidth}\"><i class=\"ui-grid-icon-spin5 ui-grid-animate-spin\" ng-style=\"{'margin-top': ( grid.options.expandable.expandableRowHeight/2 - 5),\n" +
13011     "            'margin-left':((grid.getViewportWidth() - grid.options.rowHeader.rowHeaderWidth)/2 - 5)}\"></i></div>"
13012   );
13013
13014
13015   $templateCache.put('ui-grid/csvLink',
13016     "<span class=\"ui-grid-exporter-csv-link-span\"><a href=\"data:text/csv;charset=UTF-8,CSV_CONTENT\">LINK_LABEL</a></span>"
13017   );
13018
13019
13020   $templateCache.put('ui-grid/columnResizer',
13021     "<div ui-grid-column-resizer ng-if=\"grid.options.enableColumnResizing\" class=\"ui-grid-column-resizer\" col=\"col\" position=\"right\" render-index=\"renderIndex\"></div>"
13022   );
13023
13024
13025   $templateCache.put('ui-grid/selectionRowHeader',
13026     "<div class=\"ui-grid-row-header-cell ui-grid-disable-selection\"><div class=\"ui-grid-cell-contents\"><ui-grid-selection-row-header-buttons></ui-grid-selection-row-header-buttons></div></div>"
13027   );
13028
13029
13030   $templateCache.put('ui-grid/selectionRowHeaderButtons',
13031     "<div class=\"ui-grid-selection-row-header-buttons ui-grid-icon-ok\" ng-class=\"{'ui-grid-row-selected': row.isSelected}\" ng-click=\"selectButtonClick(row, $event)\">&nbsp;</div>"
13032   );
13033
13034 }]);