67838936729d5defd5712c6d58122149e7559d5e
[portal/sdk.git] /
1 /*
2  * angular-ui-bootstrap
3  * http://angular-ui.github.io/bootstrap/
4
5  * Version: 2.5.0 - 2017-01-28
6  * License: MIT
7  */angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.tabindex","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.multiMap","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
8 angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]);
9 angular.module('ui.bootstrap.collapse', [])
10
11   .directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) {
12     var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
13     return {
14       link: function(scope, element, attrs) {
15         var expandingExpr = $parse(attrs.expanding),
16           expandedExpr = $parse(attrs.expanded),
17           collapsingExpr = $parse(attrs.collapsing),
18           collapsedExpr = $parse(attrs.collapsed),
19           horizontal = false,
20           css = {},
21           cssTo = {};
22
23         init();
24
25         function init() {
26           horizontal = !!('horizontal' in attrs);
27           if (horizontal) {
28             css = {
29               width: ''
30             };
31             cssTo = {width: '0'};
32           } else {
33             css = {
34               height: ''
35             };
36             cssTo = {height: '0'};
37           }
38           if (!scope.$eval(attrs.uibCollapse)) {
39             element.addClass('in')
40               .addClass('collapse')
41               .attr('aria-expanded', true)
42               .attr('aria-hidden', false)
43               .css(css);
44           }
45         }
46
47         function getScrollFromElement(element) {
48           if (horizontal) {
49             return {width: element.scrollWidth + 'px'};
50           }
51           return {height: element.scrollHeight + 'px'};
52         }
53
54         function expand() {
55           if (element.hasClass('collapse') && element.hasClass('in')) {
56             return;
57           }
58
59           $q.resolve(expandingExpr(scope))
60             .then(function() {
61               element.removeClass('collapse')
62                 .addClass('collapsing')
63                 .attr('aria-expanded', true)
64                 .attr('aria-hidden', false);
65
66               if ($animateCss) {
67                 $animateCss(element, {
68                   addClass: 'in',
69                   easing: 'ease',
70                   css: {
71                     overflow: 'hidden'
72                   },
73                   to: getScrollFromElement(element[0])
74                 }).start()['finally'](expandDone);
75               } else {
76                 $animate.addClass(element, 'in', {
77                   css: {
78                     overflow: 'hidden'
79                   },
80                   to: getScrollFromElement(element[0])
81                 }).then(expandDone);
82               }
83             }, angular.noop);
84         }
85
86         function expandDone() {
87           element.removeClass('collapsing')
88             .addClass('collapse')
89             .css(css);
90           expandedExpr(scope);
91         }
92
93         function collapse() {
94           if (!element.hasClass('collapse') && !element.hasClass('in')) {
95             return collapseDone();
96           }
97
98           $q.resolve(collapsingExpr(scope))
99             .then(function() {
100               element
101               // IMPORTANT: The width must be set before adding "collapsing" class.
102               // Otherwise, the browser attempts to animate from width 0 (in
103               // collapsing class) to the given width here.
104                 .css(getScrollFromElement(element[0]))
105                 // initially all panel collapse have the collapse class, this removal
106                 // prevents the animation from jumping to collapsed state
107                 .removeClass('collapse')
108                 .addClass('collapsing')
109                 .attr('aria-expanded', false)
110                 .attr('aria-hidden', true);
111
112               if ($animateCss) {
113                 $animateCss(element, {
114                   removeClass: 'in',
115                   to: cssTo
116                 }).start()['finally'](collapseDone);
117               } else {
118                 $animate.removeClass(element, 'in', {
119                   to: cssTo
120                 }).then(collapseDone);
121               }
122             }, angular.noop);
123         }
124
125         function collapseDone() {
126           element.css(cssTo); // Required so that collapse works when animation is disabled
127           element.removeClass('collapsing')
128             .addClass('collapse');
129           collapsedExpr(scope);
130         }
131
132         scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
133           if (shouldCollapse) {
134             collapse();
135           } else {
136             expand();
137           }
138         });
139       }
140     };
141   }]);
142
143 angular.module('ui.bootstrap.tabindex', [])
144
145 .directive('uibTabindexToggle', function() {
146   return {
147     restrict: 'A',
148     link: function(scope, elem, attrs) {
149       attrs.$observe('disabled', function(disabled) {
150         attrs.$set('tabindex', disabled ? -1 : null);
151       });
152     }
153   };
154 });
155
156 angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse', 'ui.bootstrap.tabindex'])
157
158 .constant('uibAccordionConfig', {
159   closeOthers: true
160 })
161
162 .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
163   // This array keeps track of the accordion groups
164   this.groups = [];
165
166   // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
167   this.closeOthers = function(openGroup) {
168     var closeOthers = angular.isDefined($attrs.closeOthers) ?
169       $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
170     if (closeOthers) {
171       angular.forEach(this.groups, function(group) {
172         if (group !== openGroup) {
173           group.isOpen = false;
174         }
175       });
176     }
177   };
178
179   // This is called from the accordion-group directive to add itself to the accordion
180   this.addGroup = function(groupScope) {
181     var that = this;
182     this.groups.push(groupScope);
183
184     groupScope.$on('$destroy', function(event) {
185       that.removeGroup(groupScope);
186     });
187   };
188
189   // This is called from the accordion-group directive when to remove itself
190   this.removeGroup = function(group) {
191     var index = this.groups.indexOf(group);
192     if (index !== -1) {
193       this.groups.splice(index, 1);
194     }
195   };
196 }])
197
198 // The accordion directive simply sets up the directive controller
199 // and adds an accordion CSS class to itself element.
200 .directive('uibAccordion', function() {
201   return {
202     controller: 'UibAccordionController',
203     controllerAs: 'accordion',
204     transclude: true,
205     templateUrl: function(element, attrs) {
206       return attrs.templateUrl || 'uib/template/accordion/accordion.html';
207     }
208   };
209 })
210
211 // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
212 .directive('uibAccordionGroup', function() {
213   return {
214     require: '^uibAccordion',         // We need this directive to be inside an accordion
215     transclude: true,              // It transcludes the contents of the directive into the template
216     restrict: 'A',
217     templateUrl: function(element, attrs) {
218       return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
219     },
220     scope: {
221       heading: '@',               // Interpolate the heading attribute onto this scope
222       panelClass: '@?',           // Ditto with panelClass
223       isOpen: '=?',
224       isDisabled: '=?'
225     },
226     controller: function() {
227       this.setHeading = function(element) {
228         this.heading = element;
229       };
230     },
231     link: function(scope, element, attrs, accordionCtrl) {
232       element.addClass('panel');
233       accordionCtrl.addGroup(scope);
234
235       scope.openClass = attrs.openClass || 'panel-open';
236       scope.panelClass = attrs.panelClass || 'panel-default';
237       scope.$watch('isOpen', function(value) {
238         element.toggleClass(scope.openClass, !!value);
239         if (value) {
240           accordionCtrl.closeOthers(scope);
241         }
242       });
243
244       scope.toggleOpen = function($event) {
245         if (!scope.isDisabled) {
246           if (!$event || $event.which === 32) {
247             scope.isOpen = !scope.isOpen;
248           }
249         }
250       };
251
252       var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
253       scope.headingId = id + '-tab';
254       scope.panelId = id + '-panel';
255     }
256   };
257 })
258
259 // Use accordion-heading below an accordion-group to provide a heading containing HTML
260 .directive('uibAccordionHeading', function() {
261   return {
262     transclude: true,   // Grab the contents to be used as the heading
263     template: '',       // In effect remove this element!
264     replace: true,
265     require: '^uibAccordionGroup',
266     link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
267       // Pass the heading to the accordion-group controller
268       // so that it can be transcluded into the right place in the template
269       // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
270       accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
271     }
272   };
273 })
274
275 // Use in the accordion-group template to indicate where you want the heading to be transcluded
276 // You must provide the property on the accordion-group controller that will hold the transcluded element
277 .directive('uibAccordionTransclude', function() {
278   return {
279     require: '^uibAccordionGroup',
280     link: function(scope, element, attrs, controller) {
281       scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
282         if (heading) {
283           var elem = angular.element(element[0].querySelector(getHeaderSelectors()));
284           elem.html('');
285           elem.append(heading);
286         }
287       });
288     }
289   };
290
291   function getHeaderSelectors() {
292       return 'uib-accordion-header,' +
293           'data-uib-accordion-header,' +
294           'x-uib-accordion-header,' +
295           'uib\\:accordion-header,' +
296           '[uib-accordion-header],' +
297           '[data-uib-accordion-header],' +
298           '[x-uib-accordion-header]';
299   }
300 });
301
302 angular.module('ui.bootstrap.alert', [])
303
304 .controller('UibAlertController', ['$scope', '$element', '$attrs', '$interpolate', '$timeout', function($scope, $element, $attrs, $interpolate, $timeout) {
305   $scope.closeable = !!$attrs.close;
306   $element.addClass('alert');
307   $attrs.$set('role', 'alert');
308   if ($scope.closeable) {
309     $element.addClass('alert-dismissible');
310   }
311
312   var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
313     $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
314
315   if (dismissOnTimeout) {
316     $timeout(function() {
317       $scope.close();
318     }, parseInt(dismissOnTimeout, 10));
319   }
320 }])
321
322 .directive('uibAlert', function() {
323   return {
324     controller: 'UibAlertController',
325     controllerAs: 'alert',
326     restrict: 'A',
327     templateUrl: function(element, attrs) {
328       return attrs.templateUrl || 'uib/template/alert/alert.html';
329     },
330     transclude: true,
331     scope: {
332       close: '&'
333     }
334   };
335 });
336
337 angular.module('ui.bootstrap.buttons', [])
338
339 .constant('uibButtonConfig', {
340   activeClass: 'active',
341   toggleEvent: 'click'
342 })
343
344 .controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) {
345   this.activeClass = buttonConfig.activeClass || 'active';
346   this.toggleEvent = buttonConfig.toggleEvent || 'click';
347 }])
348
349 .directive('uibBtnRadio', ['$parse', function($parse) {
350   return {
351     require: ['uibBtnRadio', 'ngModel'],
352     controller: 'UibButtonsController',
353     controllerAs: 'buttons',
354     link: function(scope, element, attrs, ctrls) {
355       var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
356       var uncheckableExpr = $parse(attrs.uibUncheckable);
357
358       element.find('input').css({display: 'none'});
359
360       //model -> UI
361       ngModelCtrl.$render = function() {
362         element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
363       };
364
365       //ui->model
366       element.on(buttonsCtrl.toggleEvent, function() {
367         if (attrs.disabled) {
368           return;
369         }
370
371         var isActive = element.hasClass(buttonsCtrl.activeClass);
372
373         if (!isActive || angular.isDefined(attrs.uncheckable)) {
374           scope.$apply(function() {
375             ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
376             ngModelCtrl.$render();
377           });
378         }
379       });
380
381       if (attrs.uibUncheckable) {
382         scope.$watch(uncheckableExpr, function(uncheckable) {
383           attrs.$set('uncheckable', uncheckable ? '' : undefined);
384         });
385       }
386     }
387   };
388 }])
389
390 .directive('uibBtnCheckbox', function() {
391   return {
392     require: ['uibBtnCheckbox', 'ngModel'],
393     controller: 'UibButtonsController',
394     controllerAs: 'button',
395     link: function(scope, element, attrs, ctrls) {
396       var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
397
398       element.find('input').css({display: 'none'});
399
400       function getTrueValue() {
401         return getCheckboxValue(attrs.btnCheckboxTrue, true);
402       }
403
404       function getFalseValue() {
405         return getCheckboxValue(attrs.btnCheckboxFalse, false);
406       }
407
408       function getCheckboxValue(attribute, defaultValue) {
409         return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue;
410       }
411
412       //model -> UI
413       ngModelCtrl.$render = function() {
414         element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
415       };
416
417       //ui->model
418       element.on(buttonsCtrl.toggleEvent, function() {
419         if (attrs.disabled) {
420           return;
421         }
422
423         scope.$apply(function() {
424           ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
425           ngModelCtrl.$render();
426         });
427       });
428     }
429   };
430 });
431
432 angular.module('ui.bootstrap.carousel', [])
433
434 .controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
435   var self = this,
436     slides = self.slides = $scope.slides = [],
437     SLIDE_DIRECTION = 'uib-slideDirection',
438     currentIndex = $scope.active,
439     currentInterval, isPlaying;
440
441   var destroyed = false;
442   $element.addClass('carousel');
443
444   self.addSlide = function(slide, element) {
445     slides.push({
446       slide: slide,
447       element: element
448     });
449     slides.sort(function(a, b) {
450       return +a.slide.index - +b.slide.index;
451     });
452     //if this is the first slide or the slide is set to active, select it
453     if (slide.index === $scope.active || slides.length === 1 && !angular.isNumber($scope.active)) {
454       if ($scope.$currentTransition) {
455         $scope.$currentTransition = null;
456       }
457
458       currentIndex = slide.index;
459       $scope.active = slide.index;
460       setActive(currentIndex);
461       self.select(slides[findSlideIndex(slide)]);
462       if (slides.length === 1) {
463         $scope.play();
464       }
465     }
466   };
467
468   self.getCurrentIndex = function() {
469     for (var i = 0; i < slides.length; i++) {
470       if (slides[i].slide.index === currentIndex) {
471         return i;
472       }
473     }
474   };
475
476   self.next = $scope.next = function() {
477     var newIndex = (self.getCurrentIndex() + 1) % slides.length;
478
479     if (newIndex === 0 && $scope.noWrap()) {
480       $scope.pause();
481       return;
482     }
483
484     return self.select(slides[newIndex], 'next');
485   };
486
487   self.prev = $scope.prev = function() {
488     var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
489
490     if ($scope.noWrap() && newIndex === slides.length - 1) {
491       $scope.pause();
492       return;
493     }
494
495     return self.select(slides[newIndex], 'prev');
496   };
497
498   self.removeSlide = function(slide) {
499     var index = findSlideIndex(slide);
500
501     //get the index of the slide inside the carousel
502     slides.splice(index, 1);
503     if (slides.length > 0 && currentIndex === index) {
504       if (index >= slides.length) {
505         currentIndex = slides.length - 1;
506         $scope.active = currentIndex;
507         setActive(currentIndex);
508         self.select(slides[slides.length - 1]);
509       } else {
510         currentIndex = index;
511         $scope.active = currentIndex;
512         setActive(currentIndex);
513         self.select(slides[index]);
514       }
515     } else if (currentIndex > index) {
516       currentIndex--;
517       $scope.active = currentIndex;
518     }
519
520     //clean the active value when no more slide
521     if (slides.length === 0) {
522       currentIndex = null;
523       $scope.active = null;
524     }
525   };
526
527   /* direction: "prev" or "next" */
528   self.select = $scope.select = function(nextSlide, direction) {
529     var nextIndex = findSlideIndex(nextSlide.slide);
530     //Decide direction if it's not given
531     if (direction === undefined) {
532       direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
533     }
534     //Prevent this user-triggered transition from occurring if there is already one in progress
535     if (nextSlide.slide.index !== currentIndex &&
536       !$scope.$currentTransition) {
537       goNext(nextSlide.slide, nextIndex, direction);
538     }
539   };
540
541   /* Allow outside people to call indexOf on slides array */
542   $scope.indexOfSlide = function(slide) {
543     return +slide.slide.index;
544   };
545
546   $scope.isActive = function(slide) {
547     return $scope.active === slide.slide.index;
548   };
549
550   $scope.isPrevDisabled = function() {
551     return $scope.active === 0 && $scope.noWrap();
552   };
553
554   $scope.isNextDisabled = function() {
555     return $scope.active === slides.length - 1 && $scope.noWrap();
556   };
557
558   $scope.pause = function() {
559     if (!$scope.noPause) {
560       isPlaying = false;
561       resetTimer();
562     }
563   };
564
565   $scope.play = function() {
566     if (!isPlaying) {
567       isPlaying = true;
568       restartTimer();
569     }
570   };
571
572   $element.on('mouseenter', $scope.pause);
573   $element.on('mouseleave', $scope.play);
574
575   $scope.$on('$destroy', function() {
576     destroyed = true;
577     resetTimer();
578   });
579
580   $scope.$watch('noTransition', function(noTransition) {
581     $animate.enabled($element, !noTransition);
582   });
583
584   $scope.$watch('interval', restartTimer);
585
586   $scope.$watchCollection('slides', resetTransition);
587
588   $scope.$watch('active', function(index) {
589     if (angular.isNumber(index) && currentIndex !== index) {
590       for (var i = 0; i < slides.length; i++) {
591         if (slides[i].slide.index === index) {
592           index = i;
593           break;
594         }
595       }
596
597       var slide = slides[index];
598       if (slide) {
599         setActive(index);
600         self.select(slides[index]);
601         currentIndex = index;
602       }
603     }
604   });
605
606   function getSlideByIndex(index) {
607     for (var i = 0, l = slides.length; i < l; ++i) {
608       if (slides[i].index === index) {
609         return slides[i];
610       }
611     }
612   }
613
614   function setActive(index) {
615     for (var i = 0; i < slides.length; i++) {
616       slides[i].slide.active = i === index;
617     }
618   }
619
620   function goNext(slide, index, direction) {
621     if (destroyed) {
622       return;
623     }
624
625     angular.extend(slide, {direction: direction});
626     angular.extend(slides[currentIndex].slide || {}, {direction: direction});
627     if ($animate.enabled($element) && !$scope.$currentTransition &&
628       slides[index].element && self.slides.length > 1) {
629       slides[index].element.data(SLIDE_DIRECTION, slide.direction);
630       var currentIdx = self.getCurrentIndex();
631
632       if (angular.isNumber(currentIdx) && slides[currentIdx].element) {
633         slides[currentIdx].element.data(SLIDE_DIRECTION, slide.direction);
634       }
635
636       $scope.$currentTransition = true;
637       $animate.on('addClass', slides[index].element, function(element, phase) {
638         if (phase === 'close') {
639           $scope.$currentTransition = null;
640           $animate.off('addClass', element);
641         }
642       });
643     }
644
645     $scope.active = slide.index;
646     currentIndex = slide.index;
647     setActive(index);
648
649     //every time you change slides, reset the timer
650     restartTimer();
651   }
652
653   function findSlideIndex(slide) {
654     for (var i = 0; i < slides.length; i++) {
655       if (slides[i].slide === slide) {
656         return i;
657       }
658     }
659   }
660
661   function resetTimer() {
662     if (currentInterval) {
663       $interval.cancel(currentInterval);
664       currentInterval = null;
665     }
666   }
667
668   function resetTransition(slides) {
669     if (!slides.length) {
670       $scope.$currentTransition = null;
671     }
672   }
673
674   function restartTimer() {
675     resetTimer();
676     var interval = +$scope.interval;
677     if (!isNaN(interval) && interval > 0) {
678       currentInterval = $interval(timerFn, interval);
679     }
680   }
681
682   function timerFn() {
683     var interval = +$scope.interval;
684     if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
685       $scope.next();
686     } else {
687       $scope.pause();
688     }
689   }
690 }])
691
692 .directive('uibCarousel', function() {
693   return {
694     transclude: true,
695     controller: 'UibCarouselController',
696     controllerAs: 'carousel',
697     restrict: 'A',
698     templateUrl: function(element, attrs) {
699       return attrs.templateUrl || 'uib/template/carousel/carousel.html';
700     },
701     scope: {
702       active: '=',
703       interval: '=',
704       noTransition: '=',
705       noPause: '=',
706       noWrap: '&'
707     }
708   };
709 })
710
711 .directive('uibSlide', ['$animate', function($animate) {
712   return {
713     require: '^uibCarousel',
714     restrict: 'A',
715     transclude: true,
716     templateUrl: function(element, attrs) {
717       return attrs.templateUrl || 'uib/template/carousel/slide.html';
718     },
719     scope: {
720       actual: '=?',
721       index: '=?'
722     },
723     link: function (scope, element, attrs, carouselCtrl) {
724       element.addClass('item');
725       carouselCtrl.addSlide(scope, element);
726       //when the scope is destroyed then remove the slide from the current slides array
727       scope.$on('$destroy', function() {
728         carouselCtrl.removeSlide(scope);
729       });
730
731       scope.$watch('active', function(active) {
732         $animate[active ? 'addClass' : 'removeClass'](element, 'active');
733       });
734     }
735   };
736 }])
737
738 .animation('.item', ['$animateCss',
739 function($animateCss) {
740   var SLIDE_DIRECTION = 'uib-slideDirection';
741
742   function removeClass(element, className, callback) {
743     element.removeClass(className);
744     if (callback) {
745       callback();
746     }
747   }
748
749   return {
750     beforeAddClass: function(element, className, done) {
751       if (className === 'active') {
752         var stopped = false;
753         var direction = element.data(SLIDE_DIRECTION);
754         var directionClass = direction === 'next' ? 'left' : 'right';
755         var removeClassFn = removeClass.bind(this, element,
756           directionClass + ' ' + direction, done);
757         element.addClass(direction);
758
759         $animateCss(element, {addClass: directionClass})
760           .start()
761           .done(removeClassFn);
762
763         return function() {
764           stopped = true;
765         };
766       }
767       done();
768     },
769     beforeRemoveClass: function (element, className, done) {
770       if (className === 'active') {
771         var stopped = false;
772         var direction = element.data(SLIDE_DIRECTION);
773         var directionClass = direction === 'next' ? 'left' : 'right';
774         var removeClassFn = removeClass.bind(this, element, directionClass, done);
775
776         $animateCss(element, {addClass: directionClass})
777           .start()
778           .done(removeClassFn);
779
780         return function() {
781           stopped = true;
782         };
783       }
784       done();
785     }
786   };
787 }]);
788
789 angular.module('ui.bootstrap.dateparser', [])
790
791 .service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', 'filterFilter', function($log, $locale, dateFilter, orderByFilter, filterFilter) {
792   // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
793   var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
794
795   var localeId;
796   var formatCodeToRegex;
797
798   this.init = function() {
799     localeId = $locale.id;
800
801     this.parsers = {};
802     this.formatters = {};
803
804     formatCodeToRegex = [
805       {
806         key: 'yyyy',
807         regex: '\\d{4}',
808         apply: function(value) { this.year = +value; },
809         formatter: function(date) {
810           var _date = new Date();
811           _date.setFullYear(Math.abs(date.getFullYear()));
812           return dateFilter(_date, 'yyyy');
813         }
814       },
815       {
816         key: 'yy',
817         regex: '\\d{2}',
818         apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; },
819         formatter: function(date) {
820           var _date = new Date();
821           _date.setFullYear(Math.abs(date.getFullYear()));
822           return dateFilter(_date, 'yy');
823         }
824       },
825       {
826         key: 'y',
827         regex: '\\d{1,4}',
828         apply: function(value) { this.year = +value; },
829         formatter: function(date) {
830           var _date = new Date();
831           _date.setFullYear(Math.abs(date.getFullYear()));
832           return dateFilter(_date, 'y');
833         }
834       },
835       {
836         key: 'M!',
837         regex: '0?[1-9]|1[0-2]',
838         apply: function(value) { this.month = value - 1; },
839         formatter: function(date) {
840           var value = date.getMonth();
841           if (/^[0-9]$/.test(value)) {
842             return dateFilter(date, 'MM');
843           }
844
845           return dateFilter(date, 'M');
846         }
847       },
848       {
849         key: 'MMMM',
850         regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
851         apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); },
852         formatter: function(date) { return dateFilter(date, 'MMMM'); }
853       },
854       {
855         key: 'MMM',
856         regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
857         apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); },
858         formatter: function(date) { return dateFilter(date, 'MMM'); }
859       },
860       {
861         key: 'MM',
862         regex: '0[1-9]|1[0-2]',
863         apply: function(value) { this.month = value - 1; },
864         formatter: function(date) { return dateFilter(date, 'MM'); }
865       },
866       {
867         key: 'M',
868         regex: '[1-9]|1[0-2]',
869         apply: function(value) { this.month = value - 1; },
870         formatter: function(date) { return dateFilter(date, 'M'); }
871       },
872       {
873         key: 'd!',
874         regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
875         apply: function(value) { this.date = +value; },
876         formatter: function(date) {
877           var value = date.getDate();
878           if (/^[1-9]$/.test(value)) {
879             return dateFilter(date, 'dd');
880           }
881
882           return dateFilter(date, 'd');
883         }
884       },
885       {
886         key: 'dd',
887         regex: '[0-2][0-9]{1}|3[0-1]{1}',
888         apply: function(value) { this.date = +value; },
889         formatter: function(date) { return dateFilter(date, 'dd'); }
890       },
891       {
892         key: 'd',
893         regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
894         apply: function(value) { this.date = +value; },
895         formatter: function(date) { return dateFilter(date, 'd'); }
896       },
897       {
898         key: 'EEEE',
899         regex: $locale.DATETIME_FORMATS.DAY.join('|'),
900         formatter: function(date) { return dateFilter(date, 'EEEE'); }
901       },
902       {
903         key: 'EEE',
904         regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
905         formatter: function(date) { return dateFilter(date, 'EEE'); }
906       },
907       {
908         key: 'HH',
909         regex: '(?:0|1)[0-9]|2[0-3]',
910         apply: function(value) { this.hours = +value; },
911         formatter: function(date) { return dateFilter(date, 'HH'); }
912       },
913       {
914         key: 'hh',
915         regex: '0[0-9]|1[0-2]',
916         apply: function(value) { this.hours = +value; },
917         formatter: function(date) { return dateFilter(date, 'hh'); }
918       },
919       {
920         key: 'H',
921         regex: '1?[0-9]|2[0-3]',
922         apply: function(value) { this.hours = +value; },
923         formatter: function(date) { return dateFilter(date, 'H'); }
924       },
925       {
926         key: 'h',
927         regex: '[0-9]|1[0-2]',
928         apply: function(value) { this.hours = +value; },
929         formatter: function(date) { return dateFilter(date, 'h'); }
930       },
931       {
932         key: 'mm',
933         regex: '[0-5][0-9]',
934         apply: function(value) { this.minutes = +value; },
935         formatter: function(date) { return dateFilter(date, 'mm'); }
936       },
937       {
938         key: 'm',
939         regex: '[0-9]|[1-5][0-9]',
940         apply: function(value) { this.minutes = +value; },
941         formatter: function(date) { return dateFilter(date, 'm'); }
942       },
943       {
944         key: 'sss',
945         regex: '[0-9][0-9][0-9]',
946         apply: function(value) { this.milliseconds = +value; },
947         formatter: function(date) { return dateFilter(date, 'sss'); }
948       },
949       {
950         key: 'ss',
951         regex: '[0-5][0-9]',
952         apply: function(value) { this.seconds = +value; },
953         formatter: function(date) { return dateFilter(date, 'ss'); }
954       },
955       {
956         key: 's',
957         regex: '[0-9]|[1-5][0-9]',
958         apply: function(value) { this.seconds = +value; },
959         formatter: function(date) { return dateFilter(date, 's'); }
960       },
961       {
962         key: 'a',
963         regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
964         apply: function(value) {
965           if (this.hours === 12) {
966             this.hours = 0;
967           }
968
969           if (value === 'PM') {
970             this.hours += 12;
971           }
972         },
973         formatter: function(date) { return dateFilter(date, 'a'); }
974       },
975       {
976         key: 'Z',
977         regex: '[+-]\\d{4}',
978         apply: function(value) {
979           var matches = value.match(/([+-])(\d{2})(\d{2})/),
980             sign = matches[1],
981             hours = matches[2],
982             minutes = matches[3];
983           this.hours += toInt(sign + hours);
984           this.minutes += toInt(sign + minutes);
985         },
986         formatter: function(date) {
987           return dateFilter(date, 'Z');
988         }
989       },
990       {
991         key: 'ww',
992         regex: '[0-4][0-9]|5[0-3]',
993         formatter: function(date) { return dateFilter(date, 'ww'); }
994       },
995       {
996         key: 'w',
997         regex: '[0-9]|[1-4][0-9]|5[0-3]',
998         formatter: function(date) { return dateFilter(date, 'w'); }
999       },
1000       {
1001         key: 'GGGG',
1002         regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'),
1003         formatter: function(date) { return dateFilter(date, 'GGGG'); }
1004       },
1005       {
1006         key: 'GGG',
1007         regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
1008         formatter: function(date) { return dateFilter(date, 'GGG'); }
1009       },
1010       {
1011         key: 'GG',
1012         regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
1013         formatter: function(date) { return dateFilter(date, 'GG'); }
1014       },
1015       {
1016         key: 'G',
1017         regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
1018         formatter: function(date) { return dateFilter(date, 'G'); }
1019       }
1020     ];
1021
1022     if (angular.version.major >= 1 && angular.version.minor > 4) {
1023       formatCodeToRegex.push({
1024         key: 'LLLL',
1025         regex: $locale.DATETIME_FORMATS.STANDALONEMONTH.join('|'),
1026         apply: function(value) { this.month = $locale.DATETIME_FORMATS.STANDALONEMONTH.indexOf(value); },
1027         formatter: function(date) { return dateFilter(date, 'LLLL'); }
1028       });
1029     }
1030   };
1031
1032   this.init();
1033
1034   function getFormatCodeToRegex(key) {
1035     return filterFilter(formatCodeToRegex, {key: key}, true)[0];
1036   }
1037
1038   this.getParser = function (key) {
1039     var f = getFormatCodeToRegex(key);
1040     return f && f.apply || null;
1041   };
1042
1043   this.overrideParser = function (key, parser) {
1044     var f = getFormatCodeToRegex(key);
1045     if (f && angular.isFunction(parser)) {
1046       this.parsers = {};
1047       f.apply = parser;
1048     }
1049   }.bind(this);
1050
1051   function createParser(format) {
1052     var map = [], regex = format.split('');
1053
1054     // check for literal values
1055     var quoteIndex = format.indexOf('\'');
1056     if (quoteIndex > -1) {
1057       var inLiteral = false;
1058       format = format.split('');
1059       for (var i = quoteIndex; i < format.length; i++) {
1060         if (inLiteral) {
1061           if (format[i] === '\'') {
1062             if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
1063               format[i+1] = '$';
1064               regex[i+1] = '';
1065             } else { // end of literal
1066               regex[i] = '';
1067               inLiteral = false;
1068             }
1069           }
1070           format[i] = '$';
1071         } else {
1072           if (format[i] === '\'') { // start of literal
1073             format[i] = '$';
1074             regex[i] = '';
1075             inLiteral = true;
1076           }
1077         }
1078       }
1079
1080       format = format.join('');
1081     }
1082
1083     angular.forEach(formatCodeToRegex, function(data) {
1084       var index = format.indexOf(data.key);
1085
1086       if (index > -1) {
1087         format = format.split('');
1088
1089         regex[index] = '(' + data.regex + ')';
1090         format[index] = '$'; // Custom symbol to define consumed part of format
1091         for (var i = index + 1, n = index + data.key.length; i < n; i++) {
1092           regex[i] = '';
1093           format[i] = '$';
1094         }
1095         format = format.join('');
1096
1097         map.push({
1098           index: index,
1099           key: data.key,
1100           apply: data.apply,
1101           matcher: data.regex
1102         });
1103       }
1104     });
1105
1106     return {
1107       regex: new RegExp('^' + regex.join('') + '$'),
1108       map: orderByFilter(map, 'index')
1109     };
1110   }
1111
1112   function createFormatter(format) {
1113     var formatters = [];
1114     var i = 0;
1115     var formatter, literalIdx;
1116     while (i < format.length) {
1117       if (angular.isNumber(literalIdx)) {
1118         if (format.charAt(i) === '\'') {
1119           if (i + 1 >= format.length || format.charAt(i + 1) !== '\'') {
1120             formatters.push(constructLiteralFormatter(format, literalIdx, i));
1121             literalIdx = null;
1122           }
1123         } else if (i === format.length) {
1124           while (literalIdx < format.length) {
1125             formatter = constructFormatterFromIdx(format, literalIdx);
1126             formatters.push(formatter);
1127             literalIdx = formatter.endIdx;
1128           }
1129         }
1130
1131         i++;
1132         continue;
1133       }
1134
1135       if (format.charAt(i) === '\'') {
1136         literalIdx = i;
1137         i++;
1138         continue;
1139       }
1140
1141       formatter = constructFormatterFromIdx(format, i);
1142
1143       formatters.push(formatter.parser);
1144       i = formatter.endIdx;
1145     }
1146
1147     return formatters;
1148   }
1149
1150   function constructLiteralFormatter(format, literalIdx, endIdx) {
1151     return function() {
1152       return format.substr(literalIdx + 1, endIdx - literalIdx - 1);
1153     };
1154   }
1155
1156   function constructFormatterFromIdx(format, i) {
1157     var currentPosStr = format.substr(i);
1158     for (var j = 0; j < formatCodeToRegex.length; j++) {
1159       if (new RegExp('^' + formatCodeToRegex[j].key).test(currentPosStr)) {
1160         var data = formatCodeToRegex[j];
1161         return {
1162           endIdx: i + data.key.length,
1163           parser: data.formatter
1164         };
1165       }
1166     }
1167
1168     return {
1169       endIdx: i + 1,
1170       parser: function() {
1171         return currentPosStr.charAt(0);
1172       }
1173     };
1174   }
1175
1176   this.filter = function(date, format) {
1177     if (!angular.isDate(date) || isNaN(date) || !format) {
1178       return '';
1179     }
1180
1181     format = $locale.DATETIME_FORMATS[format] || format;
1182
1183     if ($locale.id !== localeId) {
1184       this.init();
1185     }
1186
1187     if (!this.formatters[format]) {
1188       this.formatters[format] = createFormatter(format);
1189     }
1190
1191     var formatters = this.formatters[format];
1192
1193     return formatters.reduce(function(str, formatter) {
1194       return str + formatter(date);
1195     }, '');
1196   };
1197
1198   this.parse = function(input, format, baseDate) {
1199     if (!angular.isString(input) || !format) {
1200       return input;
1201     }
1202
1203     format = $locale.DATETIME_FORMATS[format] || format;
1204     format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
1205
1206     if ($locale.id !== localeId) {
1207       this.init();
1208     }
1209
1210     if (!this.parsers[format]) {
1211       this.parsers[format] = createParser(format, 'apply');
1212     }
1213
1214     var parser = this.parsers[format],
1215         regex = parser.regex,
1216         map = parser.map,
1217         results = input.match(regex),
1218         tzOffset = false;
1219     if (results && results.length) {
1220       var fields, dt;
1221       if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
1222         fields = {
1223           year: baseDate.getFullYear(),
1224           month: baseDate.getMonth(),
1225           date: baseDate.getDate(),
1226           hours: baseDate.getHours(),
1227           minutes: baseDate.getMinutes(),
1228           seconds: baseDate.getSeconds(),
1229           milliseconds: baseDate.getMilliseconds()
1230         };
1231       } else {
1232         if (baseDate) {
1233           $log.warn('dateparser:', 'baseDate is not a valid date');
1234         }
1235         fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
1236       }
1237
1238       for (var i = 1, n = results.length; i < n; i++) {
1239         var mapper = map[i - 1];
1240         if (mapper.matcher === 'Z') {
1241           tzOffset = true;
1242         }
1243
1244         if (mapper.apply) {
1245           mapper.apply.call(fields, results[i]);
1246         }
1247       }
1248
1249       var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
1250         Date.prototype.setFullYear;
1251       var timesetter = tzOffset ? Date.prototype.setUTCHours :
1252         Date.prototype.setHours;
1253
1254       if (isValid(fields.year, fields.month, fields.date)) {
1255         if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
1256           dt = new Date(baseDate);
1257           datesetter.call(dt, fields.year, fields.month, fields.date);
1258           timesetter.call(dt, fields.hours, fields.minutes,
1259             fields.seconds, fields.milliseconds);
1260         } else {
1261           dt = new Date(0);
1262           datesetter.call(dt, fields.year, fields.month, fields.date);
1263           timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
1264             fields.seconds || 0, fields.milliseconds || 0);
1265         }
1266       }
1267
1268       return dt;
1269     }
1270   };
1271
1272   // Check if date is valid for specific month (and year for February).
1273   // Month: 0 = Jan, 1 = Feb, etc
1274   function isValid(year, month, date) {
1275     if (date < 1) {
1276       return false;
1277     }
1278
1279     if (month === 1 && date > 28) {
1280       return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
1281     }
1282
1283     if (month === 3 || month === 5 || month === 8 || month === 10) {
1284       return date < 31;
1285     }
1286
1287     return true;
1288   }
1289
1290   function toInt(str) {
1291     return parseInt(str, 10);
1292   }
1293
1294   this.toTimezone = toTimezone;
1295   this.fromTimezone = fromTimezone;
1296   this.timezoneToOffset = timezoneToOffset;
1297   this.addDateMinutes = addDateMinutes;
1298   this.convertTimezoneToLocal = convertTimezoneToLocal;
1299
1300   function toTimezone(date, timezone) {
1301     return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
1302   }
1303
1304   function fromTimezone(date, timezone) {
1305     return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
1306   }
1307
1308   //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207
1309   function timezoneToOffset(timezone, fallback) {
1310     timezone = timezone.replace(/:/g, '');
1311     var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1312     return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1313   }
1314
1315   function addDateMinutes(date, minutes) {
1316     date = new Date(date.getTime());
1317     date.setMinutes(date.getMinutes() + minutes);
1318     return date;
1319   }
1320
1321   function convertTimezoneToLocal(date, timezone, reverse) {
1322     reverse = reverse ? -1 : 1;
1323     var dateTimezoneOffset = date.getTimezoneOffset();
1324     var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1325     return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1326   }
1327 }]);
1328
1329 // Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
1330 // at most one element.
1331 angular.module('ui.bootstrap.isClass', [])
1332 .directive('uibIsClass', [
1333          '$animate',
1334 function ($animate) {
1335   //                    11111111          22222222
1336   var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/;
1337   //                    11111111           22222222
1338   var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
1339
1340   var dataPerTracked = {};
1341
1342   return {
1343     restrict: 'A',
1344     compile: function(tElement, tAttrs) {
1345       var linkedScopes = [];
1346       var instances = [];
1347       var expToData = {};
1348       var lastActivated = null;
1349       var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP);
1350       var onExp = onExpMatches[2];
1351       var expsStr = onExpMatches[1];
1352       var exps = expsStr.split(',');
1353
1354       return linkFn;
1355
1356       function linkFn(scope, element, attrs) {
1357         linkedScopes.push(scope);
1358         instances.push({
1359           scope: scope,
1360           element: element
1361         });
1362
1363         exps.forEach(function(exp, k) {
1364           addForExp(exp, scope);
1365         });
1366
1367         scope.$on('$destroy', removeScope);
1368       }
1369
1370       function addForExp(exp, scope) {
1371         var matches = exp.match(IS_REGEXP);
1372         var clazz = scope.$eval(matches[1]);
1373         var compareWithExp = matches[2];
1374         var data = expToData[exp];
1375         if (!data) {
1376           var watchFn = function(compareWithVal) {
1377             var newActivated = null;
1378             instances.some(function(instance) {
1379               var thisVal = instance.scope.$eval(onExp);
1380               if (thisVal === compareWithVal) {
1381                 newActivated = instance;
1382                 return true;
1383               }
1384             });
1385             if (data.lastActivated !== newActivated) {
1386               if (data.lastActivated) {
1387                 $animate.removeClass(data.lastActivated.element, clazz);
1388               }
1389               if (newActivated) {
1390                 $animate.addClass(newActivated.element, clazz);
1391               }
1392               data.lastActivated = newActivated;
1393             }
1394           };
1395           expToData[exp] = data = {
1396             lastActivated: null,
1397             scope: scope,
1398             watchFn: watchFn,
1399             compareWithExp: compareWithExp,
1400             watcher: scope.$watch(compareWithExp, watchFn)
1401           };
1402         }
1403         data.watchFn(scope.$eval(compareWithExp));
1404       }
1405
1406       function removeScope(e) {
1407         var removedScope = e.targetScope;
1408         var index = linkedScopes.indexOf(removedScope);
1409         linkedScopes.splice(index, 1);
1410         instances.splice(index, 1);
1411         if (linkedScopes.length) {
1412           var newWatchScope = linkedScopes[0];
1413           angular.forEach(expToData, function(data) {
1414             if (data.scope === removedScope) {
1415               data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn);
1416               data.scope = newWatchScope;
1417             }
1418           });
1419         } else {
1420           expToData = {};
1421         }
1422       }
1423     }
1424   };
1425 }]);
1426 angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass'])
1427
1428 .value('$datepickerSuppressError', false)
1429
1430 .value('$datepickerLiteralWarning', true)
1431
1432 .constant('uibDatepickerConfig', {
1433   datepickerMode: 'day',
1434   formatDay: 'dd',
1435   formatMonth: 'MMMM',
1436   formatYear: 'yyyy',
1437   formatDayHeader: 'EEE',
1438   formatDayTitle: 'MMMM yyyy',
1439   formatMonthTitle: 'yyyy',
1440   maxDate: null,
1441   maxMode: 'year',
1442   minDate: null,
1443   minMode: 'day',
1444   monthColumns: 3,
1445   ngModelOptions: {},
1446   shortcutPropagation: false,
1447   showWeeks: true,
1448   yearColumns: 5,
1449   yearRows: 4
1450 })
1451
1452 .controller('UibDatepickerController', ['$scope', '$element', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser',
1453   function($scope, $element, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) {
1454   var self = this,
1455       ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
1456       ngModelOptions = {},
1457       watchListeners = [];
1458
1459   $element.addClass('uib-datepicker');
1460   $attrs.$set('role', 'application');
1461
1462   if (!$scope.datepickerOptions) {
1463     $scope.datepickerOptions = {};
1464   }
1465
1466   // Modes chain
1467   this.modes = ['day', 'month', 'year'];
1468
1469   [
1470     'customClass',
1471     'dateDisabled',
1472     'datepickerMode',
1473     'formatDay',
1474     'formatDayHeader',
1475     'formatDayTitle',
1476     'formatMonth',
1477     'formatMonthTitle',
1478     'formatYear',
1479     'maxDate',
1480     'maxMode',
1481     'minDate',
1482     'minMode',
1483     'monthColumns',
1484     'showWeeks',
1485     'shortcutPropagation',
1486     'startingDay',
1487     'yearColumns',
1488     'yearRows'
1489   ].forEach(function(key) {
1490     switch (key) {
1491       case 'customClass':
1492       case 'dateDisabled':
1493         $scope[key] = $scope.datepickerOptions[key] || angular.noop;
1494         break;
1495       case 'datepickerMode':
1496         $scope.datepickerMode = angular.isDefined($scope.datepickerOptions.datepickerMode) ?
1497           $scope.datepickerOptions.datepickerMode : datepickerConfig.datepickerMode;
1498         break;
1499       case 'formatDay':
1500       case 'formatDayHeader':
1501       case 'formatDayTitle':
1502       case 'formatMonth':
1503       case 'formatMonthTitle':
1504       case 'formatYear':
1505         self[key] = angular.isDefined($scope.datepickerOptions[key]) ?
1506           $interpolate($scope.datepickerOptions[key])($scope.$parent) :
1507           datepickerConfig[key];
1508         break;
1509       case 'monthColumns':
1510       case 'showWeeks':
1511       case 'shortcutPropagation':
1512       case 'yearColumns':
1513       case 'yearRows':
1514         self[key] = angular.isDefined($scope.datepickerOptions[key]) ?
1515           $scope.datepickerOptions[key] : datepickerConfig[key];
1516         break;
1517       case 'startingDay':
1518         if (angular.isDefined($scope.datepickerOptions.startingDay)) {
1519           self.startingDay = $scope.datepickerOptions.startingDay;
1520         } else if (angular.isNumber(datepickerConfig.startingDay)) {
1521           self.startingDay = datepickerConfig.startingDay;
1522         } else {
1523           self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7;
1524         }
1525
1526         break;
1527       case 'maxDate':
1528       case 'minDate':
1529         $scope.$watch('datepickerOptions.' + key, function(value) {
1530           if (value) {
1531             if (angular.isDate(value)) {
1532               self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.getOption('timezone'));
1533             } else {
1534               if ($datepickerLiteralWarning) {
1535                 $log.warn('Literal date support has been deprecated, please switch to date object usage');
1536               }
1537
1538               self[key] = new Date(dateFilter(value, 'medium'));
1539             }
1540           } else {
1541             self[key] = datepickerConfig[key] ?
1542               dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.getOption('timezone')) :
1543               null;
1544           }
1545
1546           self.refreshView();
1547         });
1548
1549         break;
1550       case 'maxMode':
1551       case 'minMode':
1552         if ($scope.datepickerOptions[key]) {
1553           $scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) {
1554             self[key] = $scope[key] = angular.isDefined(value) ? value : $scope.datepickerOptions[key];
1555             if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) ||
1556               key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) {
1557               $scope.datepickerMode = self[key];
1558               $scope.datepickerOptions.datepickerMode = self[key];
1559             }
1560           });
1561         } else {
1562           self[key] = $scope[key] = datepickerConfig[key] || null;
1563         }
1564
1565         break;
1566     }
1567   });
1568
1569   $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
1570
1571   $scope.disabled = angular.isDefined($attrs.disabled) || false;
1572   if (angular.isDefined($attrs.ngDisabled)) {
1573     watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
1574       $scope.disabled = disabled;
1575       self.refreshView();
1576     }));
1577   }
1578
1579   $scope.isActive = function(dateObject) {
1580     if (self.compare(dateObject.date, self.activeDate) === 0) {
1581       $scope.activeDateId = dateObject.uid;
1582       return true;
1583     }
1584     return false;
1585   };
1586
1587   this.init = function(ngModelCtrl_) {
1588     ngModelCtrl = ngModelCtrl_;
1589     ngModelOptions = extractOptions(ngModelCtrl);
1590
1591     if ($scope.datepickerOptions.initDate) {
1592       self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.getOption('timezone')) || new Date();
1593       $scope.$watch('datepickerOptions.initDate', function(initDate) {
1594         if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
1595           self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.getOption('timezone'));
1596           self.refreshView();
1597         }
1598       });
1599     } else {
1600       self.activeDate = new Date();
1601     }
1602
1603     var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date();
1604     this.activeDate = !isNaN(date) ?
1605       dateParser.fromTimezone(date, ngModelOptions.getOption('timezone')) :
1606       dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone'));
1607
1608     ngModelCtrl.$render = function() {
1609       self.render();
1610     };
1611   };
1612
1613   this.render = function() {
1614     if (ngModelCtrl.$viewValue) {
1615       var date = new Date(ngModelCtrl.$viewValue),
1616           isValid = !isNaN(date);
1617
1618       if (isValid) {
1619         this.activeDate = dateParser.fromTimezone(date, ngModelOptions.getOption('timezone'));
1620       } else if (!$datepickerSuppressError) {
1621         $log.error('Datepicker directive: "ng-model" value must be a Date object');
1622       }
1623     }
1624     this.refreshView();
1625   };
1626
1627   this.refreshView = function() {
1628     if (this.element) {
1629       $scope.selectedDt = null;
1630       this._refreshView();
1631       if ($scope.activeDt) {
1632         $scope.activeDateId = $scope.activeDt.uid;
1633       }
1634
1635       var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
1636       date = dateParser.fromTimezone(date, ngModelOptions.getOption('timezone'));
1637       ngModelCtrl.$setValidity('dateDisabled', !date ||
1638         this.element && !this.isDisabled(date));
1639     }
1640   };
1641
1642   this.createDateObject = function(date, format) {
1643     var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
1644     model = dateParser.fromTimezone(model, ngModelOptions.getOption('timezone'));
1645     var today = new Date();
1646     today = dateParser.fromTimezone(today, ngModelOptions.getOption('timezone'));
1647     var time = this.compare(date, today);
1648     var dt = {
1649       date: date,
1650       label: dateParser.filter(date, format),
1651       selected: model && this.compare(date, model) === 0,
1652       disabled: this.isDisabled(date),
1653       past: time < 0,
1654       current: time === 0,
1655       future: time > 0,
1656       customClass: this.customClass(date) || null
1657     };
1658
1659     if (model && this.compare(date, model) === 0) {
1660       $scope.selectedDt = dt;
1661     }
1662
1663     if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
1664       $scope.activeDt = dt;
1665     }
1666
1667     return dt;
1668   };
1669
1670   this.isDisabled = function(date) {
1671     return $scope.disabled ||
1672       this.minDate && this.compare(date, this.minDate) < 0 ||
1673       this.maxDate && this.compare(date, this.maxDate) > 0 ||
1674       $scope.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
1675   };
1676
1677   this.customClass = function(date) {
1678     return $scope.customClass({date: date, mode: $scope.datepickerMode});
1679   };
1680
1681   // Split array into smaller arrays
1682   this.split = function(arr, size) {
1683     var arrays = [];
1684     while (arr.length > 0) {
1685       arrays.push(arr.splice(0, size));
1686     }
1687     return arrays;
1688   };
1689
1690   $scope.select = function(date) {
1691     if ($scope.datepickerMode === self.minMode) {
1692       var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.getOption('timezone')) : new Date(0, 0, 0, 0, 0, 0, 0);
1693       dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
1694       dt = dateParser.toTimezone(dt, ngModelOptions.getOption('timezone'));
1695       ngModelCtrl.$setViewValue(dt);
1696       ngModelCtrl.$render();
1697     } else {
1698       self.activeDate = date;
1699       setMode(self.modes[self.modes.indexOf($scope.datepickerMode) - 1]);
1700
1701       $scope.$emit('uib:datepicker.mode');
1702     }
1703
1704     $scope.$broadcast('uib:datepicker.focus');
1705   };
1706
1707   $scope.move = function(direction) {
1708     var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
1709         month = self.activeDate.getMonth() + direction * (self.step.months || 0);
1710     self.activeDate.setFullYear(year, month, 1);
1711     self.refreshView();
1712   };
1713
1714   $scope.toggleMode = function(direction) {
1715     direction = direction || 1;
1716
1717     if ($scope.datepickerMode === self.maxMode && direction === 1 ||
1718       $scope.datepickerMode === self.minMode && direction === -1) {
1719       return;
1720     }
1721
1722     setMode(self.modes[self.modes.indexOf($scope.datepickerMode) + direction]);
1723
1724     $scope.$emit('uib:datepicker.mode');
1725   };
1726
1727   // Key event mapper
1728   $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
1729
1730   var focusElement = function() {
1731     self.element[0].focus();
1732   };
1733
1734   // Listen for focus requests from popup directive
1735   $scope.$on('uib:datepicker.focus', focusElement);
1736
1737   $scope.keydown = function(evt) {
1738     var key = $scope.keys[evt.which];
1739
1740     if (!key || evt.shiftKey || evt.altKey || $scope.disabled) {
1741       return;
1742     }
1743
1744     evt.preventDefault();
1745     if (!self.shortcutPropagation) {
1746       evt.stopPropagation();
1747     }
1748
1749     if (key === 'enter' || key === 'space') {
1750       if (self.isDisabled(self.activeDate)) {
1751         return; // do nothing
1752       }
1753       $scope.select(self.activeDate);
1754     } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
1755       $scope.toggleMode(key === 'up' ? 1 : -1);
1756     } else {
1757       self.handleKeyDown(key, evt);
1758       self.refreshView();
1759     }
1760   };
1761
1762   $element.on('keydown', function(evt) {
1763     $scope.$apply(function() {
1764       $scope.keydown(evt);
1765     });
1766   });
1767
1768   $scope.$on('$destroy', function() {
1769     //Clear all watch listeners on destroy
1770     while (watchListeners.length) {
1771       watchListeners.shift()();
1772     }
1773   });
1774
1775   function setMode(mode) {
1776     $scope.datepickerMode = mode;
1777     $scope.datepickerOptions.datepickerMode = mode;
1778   }
1779
1780   function extractOptions(ngModelCtrl) {
1781     var ngModelOptions;
1782
1783     if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
1784       // guarantee a value
1785       ngModelOptions = ngModelCtrl.$options ||
1786         $scope.datepickerOptions.ngModelOptions ||
1787         datepickerConfig.ngModelOptions ||
1788         {};
1789
1790       // mimic 1.6+ api
1791       ngModelOptions.getOption = function (key) {
1792         return ngModelOptions[key];
1793       };
1794     } else { // in angular >=1.6 $options is always present
1795       // ng-model-options defaults timezone to null; don't let its precedence squash a non-null value
1796       var timezone = ngModelCtrl.$options.getOption('timezone') ||
1797         ($scope.datepickerOptions.ngModelOptions ? $scope.datepickerOptions.ngModelOptions.timezone : null) ||
1798         (datepickerConfig.ngModelOptions ? datepickerConfig.ngModelOptions.timezone : null);
1799
1800       // values passed to createChild override existing values
1801       ngModelOptions = ngModelCtrl.$options // start with a ModelOptions instance
1802         .createChild(datepickerConfig.ngModelOptions) // lowest precedence
1803         .createChild($scope.datepickerOptions.ngModelOptions)
1804         .createChild(ngModelCtrl.$options) // highest precedence
1805         .createChild({timezone: timezone}); // to keep from squashing a non-null value
1806     }
1807
1808     return ngModelOptions;
1809   }
1810 }])
1811
1812 .controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
1813   var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1814
1815   this.step = { months: 1 };
1816   this.element = $element;
1817   function getDaysInMonth(year, month) {
1818     return month === 1 && year % 4 === 0 &&
1819       (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
1820   }
1821
1822   this.init = function(ctrl) {
1823     angular.extend(ctrl, this);
1824     scope.showWeeks = ctrl.showWeeks;
1825     ctrl.refreshView();
1826   };
1827
1828   this.getDates = function(startDate, n) {
1829     var dates = new Array(n), current = new Date(startDate), i = 0, date;
1830     while (i < n) {
1831       date = new Date(current);
1832       dates[i++] = date;
1833       current.setDate(current.getDate() + 1);
1834     }
1835     return dates;
1836   };
1837
1838   this._refreshView = function() {
1839     var year = this.activeDate.getFullYear(),
1840       month = this.activeDate.getMonth(),
1841       firstDayOfMonth = new Date(this.activeDate);
1842
1843     firstDayOfMonth.setFullYear(year, month, 1);
1844
1845     var difference = this.startingDay - firstDayOfMonth.getDay(),
1846       numDisplayedFromPreviousMonth = difference > 0 ?
1847         7 - difference : - difference,
1848       firstDate = new Date(firstDayOfMonth);
1849
1850     if (numDisplayedFromPreviousMonth > 0) {
1851       firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
1852     }
1853
1854     // 42 is the number of days on a six-week calendar
1855     var days = this.getDates(firstDate, 42);
1856     for (var i = 0; i < 42; i ++) {
1857       days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), {
1858         secondary: days[i].getMonth() !== month,
1859         uid: scope.uniqueId + '-' + i
1860       });
1861     }
1862
1863     scope.labels = new Array(7);
1864     for (var j = 0; j < 7; j++) {
1865       scope.labels[j] = {
1866         abbr: dateFilter(days[j].date, this.formatDayHeader),
1867         full: dateFilter(days[j].date, 'EEEE')
1868       };
1869     }
1870
1871     scope.title = dateFilter(this.activeDate, this.formatDayTitle);
1872     scope.rows = this.split(days, 7);
1873
1874     if (scope.showWeeks) {
1875       scope.weekNumbers = [];
1876       var thursdayIndex = (4 + 7 - this.startingDay) % 7,
1877           numWeeks = scope.rows.length;
1878       for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
1879         scope.weekNumbers.push(
1880           getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
1881       }
1882     }
1883   };
1884
1885   this.compare = function(date1, date2) {
1886     var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
1887     var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
1888     _date1.setFullYear(date1.getFullYear());
1889     _date2.setFullYear(date2.getFullYear());
1890     return _date1 - _date2;
1891   };
1892
1893   function getISO8601WeekNumber(date) {
1894     var checkDate = new Date(date);
1895     checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
1896     var time = checkDate.getTime();
1897     checkDate.setMonth(0); // Compare with Jan 1
1898     checkDate.setDate(1);
1899     return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
1900   }
1901
1902   this.handleKeyDown = function(key, evt) {
1903     var date = this.activeDate.getDate();
1904
1905     if (key === 'left') {
1906       date = date - 1;
1907     } else if (key === 'up') {
1908       date = date - 7;
1909     } else if (key === 'right') {
1910       date = date + 1;
1911     } else if (key === 'down') {
1912       date = date + 7;
1913     } else if (key === 'pageup' || key === 'pagedown') {
1914       var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
1915       this.activeDate.setMonth(month, 1);
1916       date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date);
1917     } else if (key === 'home') {
1918       date = 1;
1919     } else if (key === 'end') {
1920       date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth());
1921     }
1922     this.activeDate.setDate(date);
1923   };
1924 }])
1925
1926 .controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
1927   this.step = { years: 1 };
1928   this.element = $element;
1929
1930   this.init = function(ctrl) {
1931     angular.extend(ctrl, this);
1932     ctrl.refreshView();
1933   };
1934
1935   this._refreshView = function() {
1936     var months = new Array(12),
1937         year = this.activeDate.getFullYear(),
1938         date;
1939
1940     for (var i = 0; i < 12; i++) {
1941       date = new Date(this.activeDate);
1942       date.setFullYear(year, i, 1);
1943       months[i] = angular.extend(this.createDateObject(date, this.formatMonth), {
1944         uid: scope.uniqueId + '-' + i
1945       });
1946     }
1947
1948     scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
1949     scope.rows = this.split(months, this.monthColumns);
1950     scope.yearHeaderColspan = this.monthColumns > 3 ? this.monthColumns - 2 : 1;
1951   };
1952
1953   this.compare = function(date1, date2) {
1954     var _date1 = new Date(date1.getFullYear(), date1.getMonth());
1955     var _date2 = new Date(date2.getFullYear(), date2.getMonth());
1956     _date1.setFullYear(date1.getFullYear());
1957     _date2.setFullYear(date2.getFullYear());
1958     return _date1 - _date2;
1959   };
1960
1961   this.handleKeyDown = function(key, evt) {
1962     var date = this.activeDate.getMonth();
1963
1964     if (key === 'left') {
1965       date = date - 1;
1966     } else if (key === 'up') {
1967       date = date - this.monthColumns;
1968     } else if (key === 'right') {
1969       date = date + 1;
1970     } else if (key === 'down') {
1971       date = date + this.monthColumns;
1972     } else if (key === 'pageup' || key === 'pagedown') {
1973       var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
1974       this.activeDate.setFullYear(year);
1975     } else if (key === 'home') {
1976       date = 0;
1977     } else if (key === 'end') {
1978       date = 11;
1979     }
1980     this.activeDate.setMonth(date);
1981   };
1982 }])
1983
1984 .controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
1985   var columns, range;
1986   this.element = $element;
1987
1988   function getStartingYear(year) {
1989     return parseInt((year - 1) / range, 10) * range + 1;
1990   }
1991
1992   this.yearpickerInit = function() {
1993     columns = this.yearColumns;
1994     range = this.yearRows * columns;
1995     this.step = { years: range };
1996   };
1997
1998   this._refreshView = function() {
1999     var years = new Array(range), date;
2000
2001     for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) {
2002       date = new Date(this.activeDate);
2003       date.setFullYear(start + i, 0, 1);
2004       years[i] = angular.extend(this.createDateObject(date, this.formatYear), {
2005         uid: scope.uniqueId + '-' + i
2006       });
2007     }
2008
2009     scope.title = [years[0].label, years[range - 1].label].join(' - ');
2010     scope.rows = this.split(years, columns);
2011     scope.columns = columns;
2012   };
2013
2014   this.compare = function(date1, date2) {
2015     return date1.getFullYear() - date2.getFullYear();
2016   };
2017
2018   this.handleKeyDown = function(key, evt) {
2019     var date = this.activeDate.getFullYear();
2020
2021     if (key === 'left') {
2022       date = date - 1;
2023     } else if (key === 'up') {
2024       date = date - columns;
2025     } else if (key === 'right') {
2026       date = date + 1;
2027     } else if (key === 'down') {
2028       date = date + columns;
2029     } else if (key === 'pageup' || key === 'pagedown') {
2030       date += (key === 'pageup' ? - 1 : 1) * range;
2031     } else if (key === 'home') {
2032       date = getStartingYear(this.activeDate.getFullYear());
2033     } else if (key === 'end') {
2034       date = getStartingYear(this.activeDate.getFullYear()) + range - 1;
2035     }
2036     this.activeDate.setFullYear(date);
2037   };
2038 }])
2039
2040 .directive('uibDatepicker', function() {
2041   return {
2042     templateUrl: function(element, attrs) {
2043       return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
2044     },
2045     scope: {
2046       datepickerOptions: '=?'
2047     },
2048     require: ['uibDatepicker', '^ngModel'],
2049     restrict: 'A',
2050     controller: 'UibDatepickerController',
2051     controllerAs: 'datepicker',
2052     link: function(scope, element, attrs, ctrls) {
2053       var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
2054
2055       datepickerCtrl.init(ngModelCtrl);
2056     }
2057   };
2058 })
2059
2060 .directive('uibDaypicker', function() {
2061   return {
2062     templateUrl: function(element, attrs) {
2063       return attrs.templateUrl || 'uib/template/datepicker/day.html';
2064     },
2065     require: ['^uibDatepicker', 'uibDaypicker'],
2066     restrict: 'A',
2067     controller: 'UibDaypickerController',
2068     link: function(scope, element, attrs, ctrls) {
2069       var datepickerCtrl = ctrls[0],
2070         daypickerCtrl = ctrls[1];
2071
2072       daypickerCtrl.init(datepickerCtrl);
2073     }
2074   };
2075 })
2076
2077 .directive('uibMonthpicker', function() {
2078   return {
2079     templateUrl: function(element, attrs) {
2080       return attrs.templateUrl || 'uib/template/datepicker/month.html';
2081     },
2082     require: ['^uibDatepicker', 'uibMonthpicker'],
2083     restrict: 'A',
2084     controller: 'UibMonthpickerController',
2085     link: function(scope, element, attrs, ctrls) {
2086       var datepickerCtrl = ctrls[0],
2087         monthpickerCtrl = ctrls[1];
2088
2089       monthpickerCtrl.init(datepickerCtrl);
2090     }
2091   };
2092 })
2093
2094 .directive('uibYearpicker', function() {
2095   return {
2096     templateUrl: function(element, attrs) {
2097       return attrs.templateUrl || 'uib/template/datepicker/year.html';
2098     },
2099     require: ['^uibDatepicker', 'uibYearpicker'],
2100     restrict: 'A',
2101     controller: 'UibYearpickerController',
2102     link: function(scope, element, attrs, ctrls) {
2103       var ctrl = ctrls[0];
2104       angular.extend(ctrl, ctrls[1]);
2105       ctrl.yearpickerInit();
2106
2107       ctrl.refreshView();
2108     }
2109   };
2110 });
2111
2112 angular.module('ui.bootstrap.position', [])
2113
2114 /**
2115  * A set of utility methods for working with the DOM.
2116  * It is meant to be used where we need to absolute-position elements in
2117  * relation to another element (this is the case for tooltips, popovers,
2118  * typeahead suggestions etc.).
2119  */
2120   .factory('$uibPosition', ['$document', '$window', function($document, $window) {
2121     /**
2122      * Used by scrollbarWidth() function to cache scrollbar's width.
2123      * Do not access this variable directly, use scrollbarWidth() instead.
2124      */
2125     var SCROLLBAR_WIDTH;
2126     /**
2127      * scrollbar on body and html element in IE and Edge overlay
2128      * content and should be considered 0 width.
2129      */
2130     var BODY_SCROLLBAR_WIDTH;
2131     var OVERFLOW_REGEX = {
2132       normal: /(auto|scroll)/,
2133       hidden: /(auto|scroll|hidden)/
2134     };
2135     var PLACEMENT_REGEX = {
2136       auto: /\s?auto?\s?/i,
2137       primary: /^(top|bottom|left|right)$/,
2138       secondary: /^(top|bottom|left|right|center)$/,
2139       vertical: /^(top|bottom)$/
2140     };
2141     var BODY_REGEX = /(HTML|BODY)/;
2142
2143     return {
2144
2145       /**
2146        * Provides a raw DOM element from a jQuery/jQLite element.
2147        *
2148        * @param {element} elem - The element to convert.
2149        *
2150        * @returns {element} A HTML element.
2151        */
2152       getRawNode: function(elem) {
2153         return elem.nodeName ? elem : elem[0] || elem;
2154       },
2155
2156       /**
2157        * Provides a parsed number for a style property.  Strips
2158        * units and casts invalid numbers to 0.
2159        *
2160        * @param {string} value - The style value to parse.
2161        *
2162        * @returns {number} A valid number.
2163        */
2164       parseStyle: function(value) {
2165         value = parseFloat(value);
2166         return isFinite(value) ? value : 0;
2167       },
2168
2169       /**
2170        * Provides the closest positioned ancestor.
2171        *
2172        * @param {element} element - The element to get the offest parent for.
2173        *
2174        * @returns {element} The closest positioned ancestor.
2175        */
2176       offsetParent: function(elem) {
2177         elem = this.getRawNode(elem);
2178
2179         var offsetParent = elem.offsetParent || $document[0].documentElement;
2180
2181         function isStaticPositioned(el) {
2182           return ($window.getComputedStyle(el).position || 'static') === 'static';
2183         }
2184
2185         while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
2186           offsetParent = offsetParent.offsetParent;
2187         }
2188
2189         return offsetParent || $document[0].documentElement;
2190       },
2191
2192       /**
2193        * Provides the scrollbar width, concept from TWBS measureScrollbar()
2194        * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
2195        * In IE and Edge, scollbar on body and html element overlay and should
2196        * return a width of 0.
2197        *
2198        * @returns {number} The width of the browser scollbar.
2199        */
2200       scrollbarWidth: function(isBody) {
2201         if (isBody) {
2202           if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) {
2203             var bodyElem = $document.find('body');
2204             bodyElem.addClass('uib-position-body-scrollbar-measure');
2205             BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth;
2206             BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0;
2207             bodyElem.removeClass('uib-position-body-scrollbar-measure');
2208           }
2209           return BODY_SCROLLBAR_WIDTH;
2210         }
2211
2212         if (angular.isUndefined(SCROLLBAR_WIDTH)) {
2213           var scrollElem = angular.element('<div class="uib-position-scrollbar-measure"></div>');
2214           $document.find('body').append(scrollElem);
2215           SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
2216           SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
2217           scrollElem.remove();
2218         }
2219
2220         return SCROLLBAR_WIDTH;
2221       },
2222
2223       /**
2224        * Provides the padding required on an element to replace the scrollbar.
2225        *
2226        * @returns {object} An object with the following properties:
2227        *   <ul>
2228        *     <li>**scrollbarWidth**: the width of the scrollbar</li>
2229        *     <li>**widthOverflow**: whether the the width is overflowing</li>
2230        *     <li>**right**: the amount of right padding on the element needed to replace the scrollbar</li>
2231        *     <li>**rightOriginal**: the amount of right padding currently on the element</li>
2232        *     <li>**heightOverflow**: whether the the height is overflowing</li>
2233        *     <li>**bottom**: the amount of bottom padding on the element needed to replace the scrollbar</li>
2234        *     <li>**bottomOriginal**: the amount of bottom padding currently on the element</li>
2235        *   </ul>
2236        */
2237       scrollbarPadding: function(elem) {
2238         elem = this.getRawNode(elem);
2239
2240         var elemStyle = $window.getComputedStyle(elem);
2241         var paddingRight = this.parseStyle(elemStyle.paddingRight);
2242         var paddingBottom = this.parseStyle(elemStyle.paddingBottom);
2243         var scrollParent = this.scrollParent(elem, false, true);
2244         var scrollbarWidth = this.scrollbarWidth(BODY_REGEX.test(scrollParent.tagName));
2245
2246         return {
2247           scrollbarWidth: scrollbarWidth,
2248           widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth,
2249           right: paddingRight + scrollbarWidth,
2250           originalRight: paddingRight,
2251           heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight,
2252           bottom: paddingBottom + scrollbarWidth,
2253           originalBottom: paddingBottom
2254          };
2255       },
2256
2257       /**
2258        * Checks to see if the element is scrollable.
2259        *
2260        * @param {element} elem - The element to check.
2261        * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
2262        *   default is false.
2263        *
2264        * @returns {boolean} Whether the element is scrollable.
2265        */
2266       isScrollable: function(elem, includeHidden) {
2267         elem = this.getRawNode(elem);
2268
2269         var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
2270         var elemStyle = $window.getComputedStyle(elem);
2271         return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX);
2272       },
2273
2274       /**
2275        * Provides the closest scrollable ancestor.
2276        * A port of the jQuery UI scrollParent method:
2277        * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
2278        *
2279        * @param {element} elem - The element to find the scroll parent of.
2280        * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
2281        *   default is false.
2282        * @param {boolean=} [includeSelf=false] - Should the element being passed be
2283        * included in the scrollable llokup.
2284        *
2285        * @returns {element} A HTML element.
2286        */
2287       scrollParent: function(elem, includeHidden, includeSelf) {
2288         elem = this.getRawNode(elem);
2289
2290         var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
2291         var documentEl = $document[0].documentElement;
2292         var elemStyle = $window.getComputedStyle(elem);
2293         if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) {
2294           return elem;
2295         }
2296         var excludeStatic = elemStyle.position === 'absolute';
2297         var scrollParent = elem.parentElement || documentEl;
2298
2299         if (scrollParent === documentEl || elemStyle.position === 'fixed') {
2300           return documentEl;
2301         }
2302
2303         while (scrollParent.parentElement && scrollParent !== documentEl) {
2304           var spStyle = $window.getComputedStyle(scrollParent);
2305           if (excludeStatic && spStyle.position !== 'static') {
2306             excludeStatic = false;
2307           }
2308
2309           if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
2310             break;
2311           }
2312           scrollParent = scrollParent.parentElement;
2313         }
2314
2315         return scrollParent;
2316       },
2317
2318       /**
2319        * Provides read-only equivalent of jQuery's position function:
2320        * http://api.jquery.com/position/ - distance to closest positioned
2321        * ancestor.  Does not account for margins by default like jQuery position.
2322        *
2323        * @param {element} elem - The element to caclulate the position on.
2324        * @param {boolean=} [includeMargins=false] - Should margins be accounted
2325        * for, default is false.
2326        *
2327        * @returns {object} An object with the following properties:
2328        *   <ul>
2329        *     <li>**width**: the width of the element</li>
2330        *     <li>**height**: the height of the element</li>
2331        *     <li>**top**: distance to top edge of offset parent</li>
2332        *     <li>**left**: distance to left edge of offset parent</li>
2333        *   </ul>
2334        */
2335       position: function(elem, includeMagins) {
2336         elem = this.getRawNode(elem);
2337
2338         var elemOffset = this.offset(elem);
2339         if (includeMagins) {
2340           var elemStyle = $window.getComputedStyle(elem);
2341           elemOffset.top -= this.parseStyle(elemStyle.marginTop);
2342           elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
2343         }
2344         var parent = this.offsetParent(elem);
2345         var parentOffset = {top: 0, left: 0};
2346
2347         if (parent !== $document[0].documentElement) {
2348           parentOffset = this.offset(parent);
2349           parentOffset.top += parent.clientTop - parent.scrollTop;
2350           parentOffset.left += parent.clientLeft - parent.scrollLeft;
2351         }
2352
2353         return {
2354           width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
2355           height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
2356           top: Math.round(elemOffset.top - parentOffset.top),
2357           left: Math.round(elemOffset.left - parentOffset.left)
2358         };
2359       },
2360
2361       /**
2362        * Provides read-only equivalent of jQuery's offset function:
2363        * http://api.jquery.com/offset/ - distance to viewport.  Does
2364        * not account for borders, margins, or padding on the body
2365        * element.
2366        *
2367        * @param {element} elem - The element to calculate the offset on.
2368        *
2369        * @returns {object} An object with the following properties:
2370        *   <ul>
2371        *     <li>**width**: the width of the element</li>
2372        *     <li>**height**: the height of the element</li>
2373        *     <li>**top**: distance to top edge of viewport</li>
2374        *     <li>**right**: distance to bottom edge of viewport</li>
2375        *   </ul>
2376        */
2377       offset: function(elem) {
2378         elem = this.getRawNode(elem);
2379
2380         var elemBCR = elem.getBoundingClientRect();
2381         return {
2382           width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
2383           height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
2384           top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
2385           left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
2386         };
2387       },
2388
2389       /**
2390        * Provides offset distance to the closest scrollable ancestor
2391        * or viewport.  Accounts for border and scrollbar width.
2392        *
2393        * Right and bottom dimensions represent the distance to the
2394        * respective edge of the viewport element.  If the element
2395        * edge extends beyond the viewport, a negative value will be
2396        * reported.
2397        *
2398        * @param {element} elem - The element to get the viewport offset for.
2399        * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
2400        * of the first scrollable element, default is false.
2401        * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
2402        * be accounted for, default is true.
2403        *
2404        * @returns {object} An object with the following properties:
2405        *   <ul>
2406        *     <li>**top**: distance to the top content edge of viewport element</li>
2407        *     <li>**bottom**: distance to the bottom content edge of viewport element</li>
2408        *     <li>**left**: distance to the left content edge of viewport element</li>
2409        *     <li>**right**: distance to the right content edge of viewport element</li>
2410        *   </ul>
2411        */
2412       viewportOffset: function(elem, useDocument, includePadding) {
2413         elem = this.getRawNode(elem);
2414         includePadding = includePadding !== false ? true : false;
2415
2416         var elemBCR = elem.getBoundingClientRect();
2417         var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
2418
2419         var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
2420         var offsetParentBCR = offsetParent.getBoundingClientRect();
2421
2422         offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
2423         offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
2424         if (offsetParent === $document[0].documentElement) {
2425           offsetBCR.top += $window.pageYOffset;
2426           offsetBCR.left += $window.pageXOffset;
2427         }
2428         offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
2429         offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
2430
2431         if (includePadding) {
2432           var offsetParentStyle = $window.getComputedStyle(offsetParent);
2433           offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
2434           offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
2435           offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
2436           offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
2437         }
2438
2439         return {
2440           top: Math.round(elemBCR.top - offsetBCR.top),
2441           bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
2442           left: Math.round(elemBCR.left - offsetBCR.left),
2443           right: Math.round(offsetBCR.right - elemBCR.right)
2444         };
2445       },
2446
2447       /**
2448        * Provides an array of placement values parsed from a placement string.
2449        * Along with the 'auto' indicator, supported placement strings are:
2450        *   <ul>
2451        *     <li>top: element on top, horizontally centered on host element.</li>
2452        *     <li>top-left: element on top, left edge aligned with host element left edge.</li>
2453        *     <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
2454        *     <li>bottom: element on bottom, horizontally centered on host element.</li>
2455        *     <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
2456        *     <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
2457        *     <li>left: element on left, vertically centered on host element.</li>
2458        *     <li>left-top: element on left, top edge aligned with host element top edge.</li>
2459        *     <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
2460        *     <li>right: element on right, vertically centered on host element.</li>
2461        *     <li>right-top: element on right, top edge aligned with host element top edge.</li>
2462        *     <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
2463        *   </ul>
2464        * A placement string with an 'auto' indicator is expected to be
2465        * space separated from the placement, i.e: 'auto bottom-left'  If
2466        * the primary and secondary placement values do not match 'top,
2467        * bottom, left, right' then 'top' will be the primary placement and
2468        * 'center' will be the secondary placement.  If 'auto' is passed, true
2469        * will be returned as the 3rd value of the array.
2470        *
2471        * @param {string} placement - The placement string to parse.
2472        *
2473        * @returns {array} An array with the following values
2474        * <ul>
2475        *   <li>**[0]**: The primary placement.</li>
2476        *   <li>**[1]**: The secondary placement.</li>
2477        *   <li>**[2]**: If auto is passed: true, else undefined.</li>
2478        * </ul>
2479        */
2480       parsePlacement: function(placement) {
2481         var autoPlace = PLACEMENT_REGEX.auto.test(placement);
2482         if (autoPlace) {
2483           placement = placement.replace(PLACEMENT_REGEX.auto, '');
2484         }
2485
2486         placement = placement.split('-');
2487
2488         placement[0] = placement[0] || 'top';
2489         if (!PLACEMENT_REGEX.primary.test(placement[0])) {
2490           placement[0] = 'top';
2491         }
2492
2493         placement[1] = placement[1] || 'center';
2494         if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
2495           placement[1] = 'center';
2496         }
2497
2498         if (autoPlace) {
2499           placement[2] = true;
2500         } else {
2501           placement[2] = false;
2502         }
2503
2504         return placement;
2505       },
2506
2507       /**
2508        * Provides coordinates for an element to be positioned relative to
2509        * another element.  Passing 'auto' as part of the placement parameter
2510        * will enable smart placement - where the element fits. i.e:
2511        * 'auto left-top' will check to see if there is enough space to the left
2512        * of the hostElem to fit the targetElem, if not place right (same for secondary
2513        * top placement).  Available space is calculated using the viewportOffset
2514        * function.
2515        *
2516        * @param {element} hostElem - The element to position against.
2517        * @param {element} targetElem - The element to position.
2518        * @param {string=} [placement=top] - The placement for the targetElem,
2519        *   default is 'top'. 'center' is assumed as secondary placement for
2520        *   'top', 'left', 'right', and 'bottom' placements.  Available placements are:
2521        *   <ul>
2522        *     <li>top</li>
2523        *     <li>top-right</li>
2524        *     <li>top-left</li>
2525        *     <li>bottom</li>
2526        *     <li>bottom-left</li>
2527        *     <li>bottom-right</li>
2528        *     <li>left</li>
2529        *     <li>left-top</li>
2530        *     <li>left-bottom</li>
2531        *     <li>right</li>
2532        *     <li>right-top</li>
2533        *     <li>right-bottom</li>
2534        *   </ul>
2535        * @param {boolean=} [appendToBody=false] - Should the top and left values returned
2536        *   be calculated from the body element, default is false.
2537        *
2538        * @returns {object} An object with the following properties:
2539        *   <ul>
2540        *     <li>**top**: Value for targetElem top.</li>
2541        *     <li>**left**: Value for targetElem left.</li>
2542        *     <li>**placement**: The resolved placement.</li>
2543        *   </ul>
2544        */
2545       positionElements: function(hostElem, targetElem, placement, appendToBody) {
2546         hostElem = this.getRawNode(hostElem);
2547         targetElem = this.getRawNode(targetElem);
2548
2549         // need to read from prop to support tests.
2550         var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
2551         var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
2552
2553         placement = this.parsePlacement(placement);
2554
2555         var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
2556         var targetElemPos = {top: 0, left: 0, placement: ''};
2557
2558         if (placement[2]) {
2559           var viewportOffset = this.viewportOffset(hostElem, appendToBody);
2560
2561           var targetElemStyle = $window.getComputedStyle(targetElem);
2562           var adjustedSize = {
2563             width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
2564             height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
2565           };
2566
2567           placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
2568                          placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
2569                          placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
2570                          placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
2571                          placement[0];
2572
2573           placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
2574                          placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
2575                          placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
2576                          placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
2577                          placement[1];
2578
2579           if (placement[1] === 'center') {
2580             if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2581               var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
2582               if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
2583                 placement[1] = 'left';
2584               } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
2585                 placement[1] = 'right';
2586               }
2587             } else {
2588               var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
2589               if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
2590                 placement[1] = 'top';
2591               } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
2592                 placement[1] = 'bottom';
2593               }
2594             }
2595           }
2596         }
2597
2598         switch (placement[0]) {
2599           case 'top':
2600             targetElemPos.top = hostElemPos.top - targetHeight;
2601             break;
2602           case 'bottom':
2603             targetElemPos.top = hostElemPos.top + hostElemPos.height;
2604             break;
2605           case 'left':
2606             targetElemPos.left = hostElemPos.left - targetWidth;
2607             break;
2608           case 'right':
2609             targetElemPos.left = hostElemPos.left + hostElemPos.width;
2610             break;
2611         }
2612
2613         switch (placement[1]) {
2614           case 'top':
2615             targetElemPos.top = hostElemPos.top;
2616             break;
2617           case 'bottom':
2618             targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
2619             break;
2620           case 'left':
2621             targetElemPos.left = hostElemPos.left;
2622             break;
2623           case 'right':
2624             targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
2625             break;
2626           case 'center':
2627             if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2628               targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
2629             } else {
2630               targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
2631             }
2632             break;
2633         }
2634
2635         targetElemPos.top = Math.round(targetElemPos.top);
2636         targetElemPos.left = Math.round(targetElemPos.left);
2637         targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
2638
2639         return targetElemPos;
2640       },
2641
2642       /**
2643        * Provides a way to adjust the top positioning after first
2644        * render to correctly align element to top after content
2645        * rendering causes resized element height
2646        *
2647        * @param {array} placementClasses - The array of strings of classes
2648        * element should have.
2649        * @param {object} containerPosition - The object with container
2650        * position information
2651        * @param {number} initialHeight - The initial height for the elem.
2652        * @param {number} currentHeight - The current height for the elem.
2653        */
2654       adjustTop: function(placementClasses, containerPosition, initialHeight, currentHeight) {
2655         if (placementClasses.indexOf('top') !== -1 && initialHeight !== currentHeight) {
2656           return {
2657             top: containerPosition.top - currentHeight + 'px'
2658           };
2659         }
2660       },
2661
2662       /**
2663        * Provides a way for positioning tooltip & dropdown
2664        * arrows when using placement options beyond the standard
2665        * left, right, top, or bottom.
2666        *
2667        * @param {element} elem - The tooltip/dropdown element.
2668        * @param {string} placement - The placement for the elem.
2669        */
2670       positionArrow: function(elem, placement) {
2671         elem = this.getRawNode(elem);
2672
2673         var innerElem = elem.querySelector('.tooltip-inner, .popover-inner');
2674         if (!innerElem) {
2675           return;
2676         }
2677
2678         var isTooltip = angular.element(innerElem).hasClass('tooltip-inner');
2679
2680         var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
2681         if (!arrowElem) {
2682           return;
2683         }
2684
2685         var arrowCss = {
2686           top: '',
2687           bottom: '',
2688           left: '',
2689           right: ''
2690         };
2691
2692         placement = this.parsePlacement(placement);
2693         if (placement[1] === 'center') {
2694           // no adjustment necessary - just reset styles
2695           angular.element(arrowElem).css(arrowCss);
2696           return;
2697         }
2698
2699         var borderProp = 'border-' + placement[0] + '-width';
2700         var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
2701
2702         var borderRadiusProp = 'border-';
2703         if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2704           borderRadiusProp += placement[0] + '-' + placement[1];
2705         } else {
2706           borderRadiusProp += placement[1] + '-' + placement[0];
2707         }
2708         borderRadiusProp += '-radius';
2709         var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
2710
2711         switch (placement[0]) {
2712           case 'top':
2713             arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
2714             break;
2715           case 'bottom':
2716             arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
2717             break;
2718           case 'left':
2719             arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
2720             break;
2721           case 'right':
2722             arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
2723             break;
2724         }
2725
2726         arrowCss[placement[1]] = borderRadius;
2727
2728         angular.element(arrowElem).css(arrowCss);
2729       }
2730     };
2731   }]);
2732
2733 angular.module('ui.bootstrap.datepickerPopup', ['ui.bootstrap.datepicker', 'ui.bootstrap.position'])
2734
2735 .value('$datepickerPopupLiteralWarning', true)
2736
2737 .constant('uibDatepickerPopupConfig', {
2738   altInputFormats: [],
2739   appendToBody: false,
2740   clearText: 'Clear',
2741   closeOnDateSelection: true,
2742   closeText: 'Done',
2743   currentText: 'Today',
2744   datepickerPopup: 'yyyy-MM-dd',
2745   datepickerPopupTemplateUrl: 'uib/template/datepickerPopup/popup.html',
2746   datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html',
2747   html5Types: {
2748     date: 'yyyy-MM-dd',
2749     'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
2750     'month': 'yyyy-MM'
2751   },
2752   onOpenFocus: true,
2753   showButtonBar: true,
2754   placement: 'auto bottom-left'
2755 })
2756
2757 .controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$log', '$parse', '$window', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', '$datepickerPopupLiteralWarning',
2758 function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig, $datepickerPopupLiteralWarning) {
2759   var cache = {},
2760     isHtml5DateInput = false;
2761   var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
2762     datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl,
2763     ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = [];
2764
2765   this.init = function(_ngModel_) {
2766     ngModel = _ngModel_;
2767     ngModelOptions = extractOptions(ngModel);
2768     closeOnDateSelection = angular.isDefined($attrs.closeOnDateSelection) ?
2769       $scope.$parent.$eval($attrs.closeOnDateSelection) :
2770       datepickerPopupConfig.closeOnDateSelection;
2771     appendToBody = angular.isDefined($attrs.datepickerAppendToBody) ?
2772       $scope.$parent.$eval($attrs.datepickerAppendToBody) :
2773       datepickerPopupConfig.appendToBody;
2774     onOpenFocus = angular.isDefined($attrs.onOpenFocus) ?
2775       $scope.$parent.$eval($attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
2776     datepickerPopupTemplateUrl = angular.isDefined($attrs.datepickerPopupTemplateUrl) ?
2777       $attrs.datepickerPopupTemplateUrl :
2778       datepickerPopupConfig.datepickerPopupTemplateUrl;
2779     datepickerTemplateUrl = angular.isDefined($attrs.datepickerTemplateUrl) ?
2780       $attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
2781     altInputFormats = angular.isDefined($attrs.altInputFormats) ?
2782       $scope.$parent.$eval($attrs.altInputFormats) :
2783       datepickerPopupConfig.altInputFormats;
2784
2785     $scope.showButtonBar = angular.isDefined($attrs.showButtonBar) ?
2786       $scope.$parent.$eval($attrs.showButtonBar) :
2787       datepickerPopupConfig.showButtonBar;
2788
2789     if (datepickerPopupConfig.html5Types[$attrs.type]) {
2790       dateFormat = datepickerPopupConfig.html5Types[$attrs.type];
2791       isHtml5DateInput = true;
2792     } else {
2793       dateFormat = $attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup;
2794       $attrs.$observe('uibDatepickerPopup', function(value, oldValue) {
2795         var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
2796         // Invalidate the $modelValue to ensure that formatters re-run
2797         // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
2798         if (newDateFormat !== dateFormat) {
2799           dateFormat = newDateFormat;
2800           ngModel.$modelValue = null;
2801
2802           if (!dateFormat) {
2803             throw new Error('uibDatepickerPopup must have a date format specified.');
2804           }
2805         }
2806       });
2807     }
2808
2809     if (!dateFormat) {
2810       throw new Error('uibDatepickerPopup must have a date format specified.');
2811     }
2812
2813     if (isHtml5DateInput && $attrs.uibDatepickerPopup) {
2814       throw new Error('HTML5 date input types do not support custom formats.');
2815     }
2816
2817     // popup element used to display calendar
2818     popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
2819
2820     popupEl.attr({
2821       'ng-model': 'date',
2822       'ng-change': 'dateSelection(date)',
2823       'template-url': datepickerPopupTemplateUrl
2824     });
2825
2826     // datepicker element
2827     datepickerEl = angular.element(popupEl.children()[0]);
2828     datepickerEl.attr('template-url', datepickerTemplateUrl);
2829
2830     if (!$scope.datepickerOptions) {
2831       $scope.datepickerOptions = {};
2832     }
2833
2834     if (isHtml5DateInput) {
2835       if ($attrs.type === 'month') {
2836         $scope.datepickerOptions.datepickerMode = 'month';
2837         $scope.datepickerOptions.minMode = 'month';
2838       }
2839     }
2840
2841     datepickerEl.attr('datepicker-options', 'datepickerOptions');
2842
2843     if (!isHtml5DateInput) {
2844       // Internal API to maintain the correct ng-invalid-[key] class
2845       ngModel.$$parserName = 'date';
2846       ngModel.$validators.date = validator;
2847       ngModel.$parsers.unshift(parseDate);
2848       ngModel.$formatters.push(function(value) {
2849         if (ngModel.$isEmpty(value)) {
2850           $scope.date = value;
2851           return value;
2852         }
2853
2854         if (angular.isNumber(value)) {
2855           value = new Date(value);
2856         }
2857
2858         $scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone'));
2859
2860         return dateParser.filter($scope.date, dateFormat);
2861       });
2862     } else {
2863       ngModel.$formatters.push(function(value) {
2864         $scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone'));
2865         return value;
2866       });
2867     }
2868
2869     // Detect changes in the view from the text box
2870     ngModel.$viewChangeListeners.push(function() {
2871       $scope.date = parseDateString(ngModel.$viewValue);
2872     });
2873
2874     $element.on('keydown', inputKeydownBind);
2875
2876     $popup = $compile(popupEl)($scope);
2877     // Prevent jQuery cache memory leak (template is now redundant after linking)
2878     popupEl.remove();
2879
2880     if (appendToBody) {
2881       $document.find('body').append($popup);
2882     } else {
2883       $element.after($popup);
2884     }
2885
2886     $scope.$on('$destroy', function() {
2887       if ($scope.isOpen === true) {
2888         if (!$rootScope.$$phase) {
2889           $scope.$apply(function() {
2890             $scope.isOpen = false;
2891           });
2892         }
2893       }
2894
2895       $popup.remove();
2896       $element.off('keydown', inputKeydownBind);
2897       $document.off('click', documentClickBind);
2898       if (scrollParentEl) {
2899         scrollParentEl.off('scroll', positionPopup);
2900       }
2901       angular.element($window).off('resize', positionPopup);
2902
2903       //Clear all watch listeners on destroy
2904       while (watchListeners.length) {
2905         watchListeners.shift()();
2906       }
2907     });
2908   };
2909
2910   $scope.getText = function(key) {
2911     return $scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
2912   };
2913
2914   $scope.isDisabled = function(date) {
2915     if (date === 'today') {
2916       date = dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone'));
2917     }
2918
2919     var dates = {};
2920     angular.forEach(['minDate', 'maxDate'], function(key) {
2921       if (!$scope.datepickerOptions[key]) {
2922         dates[key] = null;
2923       } else if (angular.isDate($scope.datepickerOptions[key])) {
2924         dates[key] = new Date($scope.datepickerOptions[key]);
2925       } else {
2926         if ($datepickerPopupLiteralWarning) {
2927           $log.warn('Literal date support has been deprecated, please switch to date object usage');
2928         }
2929
2930         dates[key] = new Date(dateFilter($scope.datepickerOptions[key], 'medium'));
2931       }
2932     });
2933
2934     return $scope.datepickerOptions &&
2935       dates.minDate && $scope.compare(date, dates.minDate) < 0 ||
2936       dates.maxDate && $scope.compare(date, dates.maxDate) > 0;
2937   };
2938
2939   $scope.compare = function(date1, date2) {
2940     return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
2941   };
2942
2943   // Inner change
2944   $scope.dateSelection = function(dt) {
2945     $scope.date = dt;
2946     var date = $scope.date ? dateParser.filter($scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
2947     $element.val(date);
2948     ngModel.$setViewValue(date);
2949
2950     if (closeOnDateSelection) {
2951       $scope.isOpen = false;
2952       $element[0].focus();
2953     }
2954   };
2955
2956   $scope.keydown = function(evt) {
2957     if (evt.which === 27) {
2958       evt.stopPropagation();
2959       $scope.isOpen = false;
2960       $element[0].focus();
2961     }
2962   };
2963
2964   $scope.select = function(date, evt) {
2965     evt.stopPropagation();
2966
2967     if (date === 'today') {
2968       var today = new Date();
2969       if (angular.isDate($scope.date)) {
2970         date = new Date($scope.date);
2971         date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
2972       } else {
2973         date = dateParser.fromTimezone(today, ngModelOptions.getOption('timezone'));
2974         date.setHours(0, 0, 0, 0);
2975       }
2976     }
2977     $scope.dateSelection(date);
2978   };
2979
2980   $scope.close = function(evt) {
2981     evt.stopPropagation();
2982
2983     $scope.isOpen = false;
2984     $element[0].focus();
2985   };
2986
2987   $scope.disabled = angular.isDefined($attrs.disabled) || false;
2988   if ($attrs.ngDisabled) {
2989     watchListeners.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(disabled) {
2990       $scope.disabled = disabled;
2991     }));
2992   }
2993
2994   $scope.$watch('isOpen', function(value) {
2995     if (value) {
2996       if (!$scope.disabled) {
2997         $timeout(function() {
2998           positionPopup();
2999
3000           if (onOpenFocus) {
3001             $scope.$broadcast('uib:datepicker.focus');
3002           }
3003
3004           $document.on('click', documentClickBind);
3005
3006           var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement;
3007           if (appendToBody || $position.parsePlacement(placement)[2]) {
3008             scrollParentEl = scrollParentEl || angular.element($position.scrollParent($element));
3009             if (scrollParentEl) {
3010               scrollParentEl.on('scroll', positionPopup);
3011             }
3012           } else {
3013             scrollParentEl = null;
3014           }
3015
3016           angular.element($window).on('resize', positionPopup);
3017         }, 0, false);
3018       } else {
3019         $scope.isOpen = false;
3020       }
3021     } else {
3022       $document.off('click', documentClickBind);
3023       if (scrollParentEl) {
3024         scrollParentEl.off('scroll', positionPopup);
3025       }
3026       angular.element($window).off('resize', positionPopup);
3027     }
3028   });
3029
3030   function cameltoDash(string) {
3031     return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
3032   }
3033
3034   function parseDateString(viewValue) {
3035     var date = dateParser.parse(viewValue, dateFormat, $scope.date);
3036     if (isNaN(date)) {
3037       for (var i = 0; i < altInputFormats.length; i++) {
3038         date = dateParser.parse(viewValue, altInputFormats[i], $scope.date);
3039         if (!isNaN(date)) {
3040           return date;
3041         }
3042       }
3043     }
3044     return date;
3045   }
3046
3047   function parseDate(viewValue) {
3048     if (angular.isNumber(viewValue)) {
3049       // presumably timestamp to date object
3050       viewValue = new Date(viewValue);
3051     }
3052
3053     if (!viewValue) {
3054       return null;
3055     }
3056
3057     if (angular.isDate(viewValue) && !isNaN(viewValue)) {
3058       return viewValue;
3059     }
3060
3061     if (angular.isString(viewValue)) {
3062       var date = parseDateString(viewValue);
3063       if (!isNaN(date)) {
3064         return dateParser.toTimezone(date, ngModelOptions.getOption('timezone'));
3065       }
3066     }
3067
3068     return ngModelOptions.getOption('allowInvalid') ? viewValue : undefined;
3069   }
3070
3071   function validator(modelValue, viewValue) {
3072     var value = modelValue || viewValue;
3073
3074     if (!$attrs.ngRequired && !value) {
3075       return true;
3076     }
3077
3078     if (angular.isNumber(value)) {
3079       value = new Date(value);
3080     }
3081
3082     if (!value) {
3083       return true;
3084     }
3085
3086     if (angular.isDate(value) && !isNaN(value)) {
3087       return true;
3088     }
3089
3090     if (angular.isString(value)) {
3091       return !isNaN(parseDateString(value));
3092     }
3093
3094     return false;
3095   }
3096
3097   function documentClickBind(event) {
3098     if (!$scope.isOpen && $scope.disabled) {
3099       return;
3100     }
3101
3102     var popup = $popup[0];
3103     var dpContainsTarget = $element[0].contains(event.target);
3104     // The popup node may not be an element node
3105     // In some browsers (IE) only element nodes have the 'contains' function
3106     var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target);
3107     if ($scope.isOpen && !(dpContainsTarget || popupContainsTarget)) {
3108       $scope.$apply(function() {
3109         $scope.isOpen = false;
3110       });
3111     }
3112   }
3113
3114   function inputKeydownBind(evt) {
3115     if (evt.which === 27 && $scope.isOpen) {
3116       evt.preventDefault();
3117       evt.stopPropagation();
3118       $scope.$apply(function() {
3119         $scope.isOpen = false;
3120       });
3121       $element[0].focus();
3122     } else if (evt.which === 40 && !$scope.isOpen) {
3123       evt.preventDefault();
3124       evt.stopPropagation();
3125       $scope.$apply(function() {
3126         $scope.isOpen = true;
3127       });
3128     }
3129   }
3130
3131   function positionPopup() {
3132     if ($scope.isOpen) {
3133       var dpElement = angular.element($popup[0].querySelector('.uib-datepicker-popup'));
3134       var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement;
3135       var position = $position.positionElements($element, dpElement, placement, appendToBody);
3136       dpElement.css({top: position.top + 'px', left: position.left + 'px'});
3137       if (dpElement.hasClass('uib-position-measure')) {
3138         dpElement.removeClass('uib-position-measure');
3139       }
3140     }
3141   }
3142
3143   function extractOptions(ngModelCtrl) {
3144     var ngModelOptions;
3145
3146     if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
3147       // guarantee a value
3148       ngModelOptions = angular.isObject(ngModelCtrl.$options) ?
3149         ngModelCtrl.$options :
3150         {
3151           timezone: null
3152         };
3153
3154       // mimic 1.6+ api
3155       ngModelOptions.getOption = function (key) {
3156         return ngModelOptions[key];
3157       };
3158     } else { // in angular >=1.6 $options is always present
3159       ngModelOptions = ngModelCtrl.$options;
3160     }
3161
3162     return ngModelOptions;
3163   }
3164
3165   $scope.$on('uib:datepicker.mode', function() {
3166     $timeout(positionPopup, 0, false);
3167   });
3168 }])
3169
3170 .directive('uibDatepickerPopup', function() {
3171   return {
3172     require: ['ngModel', 'uibDatepickerPopup'],
3173     controller: 'UibDatepickerPopupController',
3174     scope: {
3175       datepickerOptions: '=?',
3176       isOpen: '=?',
3177       currentText: '@',
3178       clearText: '@',
3179       closeText: '@'
3180     },
3181     link: function(scope, element, attrs, ctrls) {
3182       var ngModel = ctrls[0],
3183         ctrl = ctrls[1];
3184
3185       ctrl.init(ngModel);
3186     }
3187   };
3188 })
3189
3190 .directive('uibDatepickerPopupWrap', function() {
3191   return {
3192     restrict: 'A',
3193     transclude: true,
3194     templateUrl: function(element, attrs) {
3195       return attrs.templateUrl || 'uib/template/datepickerPopup/popup.html';
3196     }
3197   };
3198 });
3199
3200 angular.module('ui.bootstrap.debounce', [])
3201 /**
3202  * A helper, internal service that debounces a function
3203  */
3204   .factory('$$debounce', ['$timeout', function($timeout) {
3205     return function(callback, debounceTime) {
3206       var timeoutPromise;
3207
3208       return function() {
3209         var self = this;
3210         var args = Array.prototype.slice.call(arguments);
3211         if (timeoutPromise) {
3212           $timeout.cancel(timeoutPromise);
3213         }
3214
3215         timeoutPromise = $timeout(function() {
3216           callback.apply(self, args);
3217         }, debounceTime);
3218       };
3219     };
3220   }]);
3221
3222 angular.module('ui.bootstrap.multiMap', [])
3223 /**
3224  * A helper, internal data structure that stores all references attached to key
3225  */
3226   .factory('$$multiMap', function() {
3227     return {
3228       createNew: function() {
3229         var map = {};
3230
3231         return {
3232           entries: function() {
3233             return Object.keys(map).map(function(key) {
3234               return {
3235                 key: key,
3236                 value: map[key]
3237               };
3238             });
3239           },
3240           get: function(key) {
3241             return map[key];
3242           },
3243           hasKey: function(key) {
3244             return !!map[key];
3245           },
3246           keys: function() {
3247             return Object.keys(map);
3248           },
3249           put: function(key, value) {
3250             if (!map[key]) {
3251               map[key] = [];
3252             }
3253
3254             map[key].push(value);
3255           },
3256           remove: function(key, value) {
3257             var values = map[key];
3258
3259             if (!values) {
3260               return;
3261             }
3262
3263             var idx = values.indexOf(value);
3264
3265             if (idx !== -1) {
3266               values.splice(idx, 1);
3267             }
3268
3269             if (!values.length) {
3270               delete map[key];
3271             }
3272           }
3273         };
3274       }
3275     };
3276   });
3277
3278 angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.position'])
3279
3280 .constant('uibDropdownConfig', {
3281   appendToOpenClass: 'uib-dropdown-open',
3282   openClass: 'open'
3283 })
3284
3285 .service('uibDropdownService', ['$document', '$rootScope', '$$multiMap', function($document, $rootScope, $$multiMap) {
3286   var openScope = null;
3287   var openedContainers = $$multiMap.createNew();
3288
3289   this.isOnlyOpen = function(dropdownScope, appendTo) {
3290     var openedDropdowns = openedContainers.get(appendTo);
3291     if (openedDropdowns) {
3292       var openDropdown = openedDropdowns.reduce(function(toClose, dropdown) {
3293         if (dropdown.scope === dropdownScope) {
3294           return dropdown;
3295         }
3296
3297         return toClose;
3298       }, {});
3299       if (openDropdown) {
3300         return openedDropdowns.length === 1;
3301       }
3302     }
3303
3304     return false;
3305   };
3306
3307   this.open = function(dropdownScope, element, appendTo) {
3308     if (!openScope) {
3309       $document.on('click', closeDropdown);
3310     }
3311
3312     if (openScope && openScope !== dropdownScope) {
3313       openScope.isOpen = false;
3314     }
3315
3316     openScope = dropdownScope;
3317
3318     if (!appendTo) {
3319       return;
3320     }
3321
3322     var openedDropdowns = openedContainers.get(appendTo);
3323     if (openedDropdowns) {
3324       var openedScopes = openedDropdowns.map(function(dropdown) {
3325         return dropdown.scope;
3326       });
3327       if (openedScopes.indexOf(dropdownScope) === -1) {
3328         openedContainers.put(appendTo, {
3329           scope: dropdownScope
3330         });
3331       }
3332     } else {
3333       openedContainers.put(appendTo, {
3334         scope: dropdownScope
3335       });
3336     }
3337   };
3338
3339   this.close = function(dropdownScope, element, appendTo) {
3340     if (openScope === dropdownScope) {
3341       $document.off('click', closeDropdown);
3342       $document.off('keydown', this.keybindFilter);
3343       openScope = null;
3344     }
3345
3346     if (!appendTo) {
3347       return;
3348     }
3349
3350     var openedDropdowns = openedContainers.get(appendTo);
3351     if (openedDropdowns) {
3352       var dropdownToClose = openedDropdowns.reduce(function(toClose, dropdown) {
3353         if (dropdown.scope === dropdownScope) {
3354           return dropdown;
3355         }
3356
3357         return toClose;
3358       }, {});
3359       if (dropdownToClose) {
3360         openedContainers.remove(appendTo, dropdownToClose);
3361       }
3362     }
3363   };
3364
3365   var closeDropdown = function(evt) {
3366     // This method may still be called during the same mouse event that
3367     // unbound this event handler. So check openScope before proceeding.
3368     if (!openScope || !openScope.isOpen) { return; }
3369
3370     if (evt && openScope.getAutoClose() === 'disabled') { return; }
3371
3372     if (evt && evt.which === 3) { return; }
3373
3374     var toggleElement = openScope.getToggleElement();
3375     if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
3376       return;
3377     }
3378
3379     var dropdownElement = openScope.getDropdownElement();
3380     if (evt && openScope.getAutoClose() === 'outsideClick' &&
3381       dropdownElement && dropdownElement[0].contains(evt.target)) {
3382       return;
3383     }
3384
3385     openScope.focusToggleElement();
3386     openScope.isOpen = false;
3387
3388     if (!$rootScope.$$phase) {
3389       openScope.$apply();
3390     }
3391   };
3392
3393   this.keybindFilter = function(evt) {
3394     if (!openScope) {
3395       // see this.close as ESC could have been pressed which kills the scope so we can not proceed
3396       return;
3397     }
3398
3399     var dropdownElement = openScope.getDropdownElement();
3400     var toggleElement = openScope.getToggleElement();
3401     var dropdownElementTargeted = dropdownElement && dropdownElement[0].contains(evt.target);
3402     var toggleElementTargeted = toggleElement && toggleElement[0].contains(evt.target);
3403     if (evt.which === 27) {
3404       evt.stopPropagation();
3405       openScope.focusToggleElement();
3406       closeDropdown();
3407     } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen && (dropdownElementTargeted || toggleElementTargeted)) {
3408       evt.preventDefault();
3409       evt.stopPropagation();
3410       openScope.focusDropdownEntry(evt.which);
3411     }
3412   };
3413 }])
3414
3415 .controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) {
3416   var self = this,
3417     scope = $scope.$new(), // create a child scope so we are not polluting original one
3418     templateScope,
3419     appendToOpenClass = dropdownConfig.appendToOpenClass,
3420     openClass = dropdownConfig.openClass,
3421     getIsOpen,
3422     setIsOpen = angular.noop,
3423     toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
3424     keynavEnabled = false,
3425     selectedOption = null,
3426     body = $document.find('body');
3427
3428   $element.addClass('dropdown');
3429
3430   this.init = function() {
3431     if ($attrs.isOpen) {
3432       getIsOpen = $parse($attrs.isOpen);
3433       setIsOpen = getIsOpen.assign;
3434
3435       $scope.$watch(getIsOpen, function(value) {
3436         scope.isOpen = !!value;
3437       });
3438     }
3439
3440     keynavEnabled = angular.isDefined($attrs.keyboardNav);
3441   };
3442
3443   this.toggle = function(open) {
3444     scope.isOpen = arguments.length ? !!open : !scope.isOpen;
3445     if (angular.isFunction(setIsOpen)) {
3446       setIsOpen(scope, scope.isOpen);
3447     }
3448
3449     return scope.isOpen;
3450   };
3451
3452   // Allow other directives to watch status
3453   this.isOpen = function() {
3454     return scope.isOpen;
3455   };
3456
3457   scope.getToggleElement = function() {
3458     return self.toggleElement;
3459   };
3460
3461   scope.getAutoClose = function() {
3462     return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
3463   };
3464
3465   scope.getElement = function() {
3466     return $element;
3467   };
3468
3469   scope.isKeynavEnabled = function() {
3470     return keynavEnabled;
3471   };
3472
3473   scope.focusDropdownEntry = function(keyCode) {
3474     var elems = self.dropdownMenu ? //If append to body is used.
3475       angular.element(self.dropdownMenu).find('a') :
3476       $element.find('ul').eq(0).find('a');
3477
3478     switch (keyCode) {
3479       case 40: {
3480         if (!angular.isNumber(self.selectedOption)) {
3481           self.selectedOption = 0;
3482         } else {
3483           self.selectedOption = self.selectedOption === elems.length - 1 ?
3484             self.selectedOption :
3485             self.selectedOption + 1;
3486         }
3487         break;
3488       }
3489       case 38: {
3490         if (!angular.isNumber(self.selectedOption)) {
3491           self.selectedOption = elems.length - 1;
3492         } else {
3493           self.selectedOption = self.selectedOption === 0 ?
3494             0 : self.selectedOption - 1;
3495         }
3496         break;
3497       }
3498     }
3499     elems[self.selectedOption].focus();
3500   };
3501
3502   scope.getDropdownElement = function() {
3503     return self.dropdownMenu;
3504   };
3505
3506   scope.focusToggleElement = function() {
3507     if (self.toggleElement) {
3508       self.toggleElement[0].focus();
3509     }
3510   };
3511
3512   function removeDropdownMenu() {
3513     $element.append(self.dropdownMenu);
3514   }
3515
3516   scope.$watch('isOpen', function(isOpen, wasOpen) {
3517     var appendTo = null,
3518       appendToBody = false;
3519
3520     if (angular.isDefined($attrs.dropdownAppendTo)) {
3521       var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
3522       if (appendToEl) {
3523         appendTo = angular.element(appendToEl);
3524       }
3525     }
3526
3527     if (angular.isDefined($attrs.dropdownAppendToBody)) {
3528       var appendToBodyValue = $parse($attrs.dropdownAppendToBody)(scope);
3529       if (appendToBodyValue !== false) {
3530         appendToBody = true;
3531       }
3532     }
3533
3534     if (appendToBody && !appendTo) {
3535       appendTo = body;
3536     }
3537
3538     if (appendTo && self.dropdownMenu) {
3539       if (isOpen) {
3540         appendTo.append(self.dropdownMenu);
3541         $element.on('$destroy', removeDropdownMenu);
3542       } else {
3543         $element.off('$destroy', removeDropdownMenu);
3544         removeDropdownMenu();
3545       }
3546     }
3547
3548     if (appendTo && self.dropdownMenu) {
3549       var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
3550         css,
3551         rightalign,
3552         scrollbarPadding,
3553         scrollbarWidth = 0;
3554
3555       css = {
3556         top: pos.top + 'px',
3557         display: isOpen ? 'block' : 'none'
3558       };
3559
3560       rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
3561       if (!rightalign) {
3562         css.left = pos.left + 'px';
3563         css.right = 'auto';
3564       } else {
3565         css.left = 'auto';
3566         scrollbarPadding = $position.scrollbarPadding(appendTo);
3567
3568         if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
3569           scrollbarWidth = scrollbarPadding.scrollbarWidth;
3570         }
3571
3572         css.right = window.innerWidth - scrollbarWidth -
3573           (pos.left + $element.prop('offsetWidth')) + 'px';
3574       }
3575
3576       // Need to adjust our positioning to be relative to the appendTo container
3577       // if it's not the body element
3578       if (!appendToBody) {
3579         var appendOffset = $position.offset(appendTo);
3580
3581         css.top = pos.top - appendOffset.top + 'px';
3582
3583         if (!rightalign) {
3584           css.left = pos.left - appendOffset.left + 'px';
3585         } else {
3586           css.right = window.innerWidth -
3587             (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
3588         }
3589       }
3590
3591       self.dropdownMenu.css(css);
3592     }
3593
3594     var openContainer = appendTo ? appendTo : $element;
3595     var dropdownOpenClass = appendTo ? appendToOpenClass : openClass;
3596     var hasOpenClass = openContainer.hasClass(dropdownOpenClass);
3597     var isOnlyOpen = uibDropdownService.isOnlyOpen($scope, appendTo);
3598
3599     if (hasOpenClass === !isOpen) {
3600       var toggleClass;
3601       if (appendTo) {
3602         toggleClass = !isOnlyOpen ? 'addClass' : 'removeClass';
3603       } else {
3604         toggleClass = isOpen ? 'addClass' : 'removeClass';
3605       }
3606       $animate[toggleClass](openContainer, dropdownOpenClass).then(function() {
3607         if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
3608           toggleInvoker($scope, { open: !!isOpen });
3609         }
3610       });
3611     }
3612
3613     if (isOpen) {
3614       if (self.dropdownMenuTemplateUrl) {
3615         $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
3616           templateScope = scope.$new();
3617           $compile(tplContent.trim())(templateScope, function(dropdownElement) {
3618             var newEl = dropdownElement;
3619             self.dropdownMenu.replaceWith(newEl);
3620             self.dropdownMenu = newEl;
3621             $document.on('keydown', uibDropdownService.keybindFilter);
3622           });
3623         });
3624       } else {
3625         $document.on('keydown', uibDropdownService.keybindFilter);
3626       }
3627
3628       scope.focusToggleElement();
3629       uibDropdownService.open(scope, $element, appendTo);
3630     } else {
3631       uibDropdownService.close(scope, $element, appendTo);
3632       if (self.dropdownMenuTemplateUrl) {
3633         if (templateScope) {
3634           templateScope.$destroy();
3635         }
3636         var newEl = angular.element('<ul class="dropdown-menu"></ul>');
3637         self.dropdownMenu.replaceWith(newEl);
3638         self.dropdownMenu = newEl;
3639       }
3640
3641       self.selectedOption = null;
3642     }
3643
3644     if (angular.isFunction(setIsOpen)) {
3645       setIsOpen($scope, isOpen);
3646     }
3647   });
3648 }])
3649
3650 .directive('uibDropdown', function() {
3651   return {
3652     controller: 'UibDropdownController',
3653     link: function(scope, element, attrs, dropdownCtrl) {
3654       dropdownCtrl.init();
3655     }
3656   };
3657 })
3658
3659 .directive('uibDropdownMenu', function() {
3660   return {
3661     restrict: 'A',
3662     require: '?^uibDropdown',
3663     link: function(scope, element, attrs, dropdownCtrl) {
3664       if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
3665         return;
3666       }
3667
3668       element.addClass('dropdown-menu');
3669
3670       var tplUrl = attrs.templateUrl;
3671       if (tplUrl) {
3672         dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
3673       }
3674
3675       if (!dropdownCtrl.dropdownMenu) {
3676         dropdownCtrl.dropdownMenu = element;
3677       }
3678     }
3679   };
3680 })
3681
3682 .directive('uibDropdownToggle', function() {
3683   return {
3684     require: '?^uibDropdown',
3685     link: function(scope, element, attrs, dropdownCtrl) {
3686       if (!dropdownCtrl) {
3687         return;
3688       }
3689
3690       element.addClass('dropdown-toggle');
3691
3692       dropdownCtrl.toggleElement = element;
3693
3694       var toggleDropdown = function(event) {
3695         event.preventDefault();
3696
3697         if (!element.hasClass('disabled') && !attrs.disabled) {
3698           scope.$apply(function() {
3699             dropdownCtrl.toggle();
3700           });
3701         }
3702       };
3703
3704       element.on('click', toggleDropdown);
3705
3706       // WAI-ARIA
3707       element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
3708       scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
3709         element.attr('aria-expanded', !!isOpen);
3710       });
3711
3712       scope.$on('$destroy', function() {
3713         element.off('click', toggleDropdown);
3714       });
3715     }
3716   };
3717 });
3718
3719 angular.module('ui.bootstrap.stackedMap', [])
3720 /**
3721  * A helper, internal data structure that acts as a map but also allows getting / removing
3722  * elements in the LIFO order
3723  */
3724   .factory('$$stackedMap', function() {
3725     return {
3726       createNew: function() {
3727         var stack = [];
3728
3729         return {
3730           add: function(key, value) {
3731             stack.push({
3732               key: key,
3733               value: value
3734             });
3735           },
3736           get: function(key) {
3737             for (var i = 0; i < stack.length; i++) {
3738               if (key === stack[i].key) {
3739                 return stack[i];
3740               }
3741             }
3742           },
3743           keys: function() {
3744             var keys = [];
3745             for (var i = 0; i < stack.length; i++) {
3746               keys.push(stack[i].key);
3747             }
3748             return keys;
3749           },
3750           top: function() {
3751             return stack[stack.length - 1];
3752           },
3753           remove: function(key) {
3754             var idx = -1;
3755             for (var i = 0; i < stack.length; i++) {
3756               if (key === stack[i].key) {
3757                 idx = i;
3758                 break;
3759               }
3760             }
3761             return stack.splice(idx, 1)[0];
3762           },
3763           removeTop: function() {
3764             return stack.pop();
3765           },
3766           length: function() {
3767             return stack.length;
3768           }
3769         };
3770       }
3771     };
3772   });
3773 angular.module('ui.bootstrap.modal', ['ui.bootstrap.multiMap', 'ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
3774 /**
3775  * Pluggable resolve mechanism for the modal resolve resolution
3776  * Supports UI Router's $resolve service
3777  */
3778   .provider('$uibResolve', function() {
3779     var resolve = this;
3780     this.resolver = null;
3781
3782     this.setResolver = function(resolver) {
3783       this.resolver = resolver;
3784     };
3785
3786     this.$get = ['$injector', '$q', function($injector, $q) {
3787       var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null;
3788       return {
3789         resolve: function(invocables, locals, parent, self) {
3790           if (resolver) {
3791             return resolver.resolve(invocables, locals, parent, self);
3792           }
3793
3794           var promises = [];
3795
3796           angular.forEach(invocables, function(value) {
3797             if (angular.isFunction(value) || angular.isArray(value)) {
3798               promises.push($q.resolve($injector.invoke(value)));
3799             } else if (angular.isString(value)) {
3800               promises.push($q.resolve($injector.get(value)));
3801             } else {
3802               promises.push($q.resolve(value));
3803             }
3804           });
3805
3806           return $q.all(promises).then(function(resolves) {
3807             var resolveObj = {};
3808             var resolveIter = 0;
3809             angular.forEach(invocables, function(value, key) {
3810               resolveObj[key] = resolves[resolveIter++];
3811             });
3812
3813             return resolveObj;
3814           });
3815         }
3816       };
3817     }];
3818   })
3819
3820 /**
3821  * A helper directive for the $modal service. It creates a backdrop element.
3822  */
3823   .directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack',
3824   function($animate, $injector, $modalStack) {
3825     return {
3826       restrict: 'A',
3827       compile: function(tElement, tAttrs) {
3828         tElement.addClass(tAttrs.backdropClass);
3829         return linkFn;
3830       }
3831     };
3832
3833     function linkFn(scope, element, attrs) {
3834       if (attrs.modalInClass) {
3835         $animate.addClass(element, attrs.modalInClass);
3836
3837         scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
3838           var done = setIsAsync();
3839           if (scope.modalOptions.animation) {
3840             $animate.removeClass(element, attrs.modalInClass).then(done);
3841           } else {
3842             done();
3843           }
3844         });
3845       }
3846     }
3847   }])
3848
3849   .directive('uibModalWindow', ['$uibModalStack', '$q', '$animateCss', '$document',
3850   function($modalStack, $q, $animateCss, $document) {
3851     return {
3852       scope: {
3853         index: '@'
3854       },
3855       restrict: 'A',
3856       transclude: true,
3857       templateUrl: function(tElement, tAttrs) {
3858         return tAttrs.templateUrl || 'uib/template/modal/window.html';
3859       },
3860       link: function(scope, element, attrs) {
3861         element.addClass(attrs.windowTopClass || '');
3862         scope.size = attrs.size;
3863
3864         scope.close = function(evt) {
3865           var modal = $modalStack.getTop();
3866           if (modal && modal.value.backdrop &&
3867             modal.value.backdrop !== 'static' &&
3868             evt.target === evt.currentTarget) {
3869             evt.preventDefault();
3870             evt.stopPropagation();
3871             $modalStack.dismiss(modal.key, 'backdrop click');
3872           }
3873         };
3874
3875         // moved from template to fix issue #2280
3876         element.on('click', scope.close);
3877
3878         // This property is only added to the scope for the purpose of detecting when this directive is rendered.
3879         // We can detect that by using this property in the template associated with this directive and then use
3880         // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
3881         scope.$isRendered = true;
3882
3883         // Deferred object that will be resolved when this modal is rendered.
3884         var modalRenderDeferObj = $q.defer();
3885         // Resolve render promise post-digest
3886         scope.$$postDigest(function() {
3887           modalRenderDeferObj.resolve();
3888         });
3889
3890         modalRenderDeferObj.promise.then(function() {
3891           var animationPromise = null;
3892
3893           if (attrs.modalInClass) {
3894             animationPromise = $animateCss(element, {
3895               addClass: attrs.modalInClass
3896             }).start();
3897
3898             scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
3899               var done = setIsAsync();
3900               $animateCss(element, {
3901                 removeClass: attrs.modalInClass
3902               }).start().then(done);
3903             });
3904           }
3905
3906
3907           $q.when(animationPromise).then(function() {
3908             // Notify {@link $modalStack} that modal is rendered.
3909             var modal = $modalStack.getTop();
3910             if (modal) {
3911               $modalStack.modalRendered(modal.key);
3912             }
3913
3914             /**
3915              * If something within the freshly-opened modal already has focus (perhaps via a
3916              * directive that causes focus) then there's no need to try to focus anything.
3917              */
3918             if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
3919               var inputWithAutofocus = element[0].querySelector('[autofocus]');
3920               /**
3921                * Auto-focusing of a freshly-opened modal element causes any child elements
3922                * with the autofocus attribute to lose focus. This is an issue on touch
3923                * based devices which will show and then hide the onscreen keyboard.
3924                * Attempts to refocus the autofocus element via JavaScript will not reopen
3925                * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
3926                * the modal element if the modal does not contain an autofocus element.
3927                */
3928               if (inputWithAutofocus) {
3929                 inputWithAutofocus.focus();
3930               } else {
3931                 element[0].focus();
3932               }
3933             }
3934           });
3935         });
3936       }
3937     };
3938   }])
3939
3940   .directive('uibModalAnimationClass', function() {
3941     return {
3942       compile: function(tElement, tAttrs) {
3943         if (tAttrs.modalAnimation) {
3944           tElement.addClass(tAttrs.uibModalAnimationClass);
3945         }
3946       }
3947     };
3948   })
3949
3950   .directive('uibModalTransclude', ['$animate', function($animate) {
3951     return {
3952       link: function(scope, element, attrs, controller, transclude) {
3953         transclude(scope.$parent, function(clone) {
3954           element.empty();
3955           $animate.enter(clone, element);
3956         });
3957       }
3958     };
3959   }])
3960
3961   .factory('$uibModalStack', ['$animate', '$animateCss', '$document',
3962     '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition',
3963     function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap, $uibPosition) {
3964       var OPENED_MODAL_CLASS = 'modal-open';
3965
3966       var backdropDomEl, backdropScope;
3967       var openedWindows = $$stackedMap.createNew();
3968       var openedClasses = $$multiMap.createNew();
3969       var $modalStack = {
3970         NOW_CLOSING_EVENT: 'modal.stack.now-closing'
3971       };
3972       var topModalIndex = 0;
3973       var previousTopOpenedModal = null;
3974       var ARIA_HIDDEN_ATTRIBUTE_NAME = 'data-bootstrap-modal-aria-hidden-count';
3975
3976       //Modal focus behavior
3977       var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' +
3978         'button:not([disabled]):not([tabindex=\'-1\']),select:not([disabled]):not([tabindex=\'-1\']), textarea:not([disabled]):not([tabindex=\'-1\']), ' +
3979         'iframe, object, embed, *[tabindex]:not([tabindex=\'-1\']), *[contenteditable=true]';
3980       var scrollbarPadding;
3981       var SNAKE_CASE_REGEXP = /[A-Z]/g;
3982
3983       // TODO: extract into common dependency with tooltip
3984       function snake_case(name) {
3985         var separator = '-';
3986         return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
3987           return (pos ? separator : '') + letter.toLowerCase();
3988         });
3989       }
3990
3991       function isVisible(element) {
3992         return !!(element.offsetWidth ||
3993           element.offsetHeight ||
3994           element.getClientRects().length);
3995       }
3996
3997       function backdropIndex() {
3998         var topBackdropIndex = -1;
3999         var opened = openedWindows.keys();
4000         for (var i = 0; i < opened.length; i++) {
4001           if (openedWindows.get(opened[i]).value.backdrop) {
4002             topBackdropIndex = i;
4003           }
4004         }
4005
4006         // If any backdrop exist, ensure that it's index is always
4007         // right below the top modal
4008         if (topBackdropIndex > -1 && topBackdropIndex < topModalIndex) {
4009           topBackdropIndex = topModalIndex;
4010         }
4011         return topBackdropIndex;
4012       }
4013
4014       $rootScope.$watch(backdropIndex, function(newBackdropIndex) {
4015         if (backdropScope) {
4016           backdropScope.index = newBackdropIndex;
4017         }
4018       });
4019
4020       function removeModalWindow(modalInstance, elementToReceiveFocus) {
4021         var modalWindow = openedWindows.get(modalInstance).value;
4022         var appendToElement = modalWindow.appendTo;
4023
4024         //clean up the stack
4025         openedWindows.remove(modalInstance);
4026         previousTopOpenedModal = openedWindows.top();
4027         if (previousTopOpenedModal) {
4028           topModalIndex = parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10);
4029         }
4030
4031         removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
4032           var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
4033           openedClasses.remove(modalBodyClass, modalInstance);
4034           var areAnyOpen = openedClasses.hasKey(modalBodyClass);
4035           appendToElement.toggleClass(modalBodyClass, areAnyOpen);
4036           if (!areAnyOpen && scrollbarPadding && scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
4037             if (scrollbarPadding.originalRight) {
4038               appendToElement.css({paddingRight: scrollbarPadding.originalRight + 'px'});
4039             } else {
4040               appendToElement.css({paddingRight: ''});
4041             }
4042             scrollbarPadding = null;
4043           }
4044           toggleTopWindowClass(true);
4045         }, modalWindow.closedDeferred);
4046         checkRemoveBackdrop();
4047
4048         //move focus to specified element if available, or else to body
4049         if (elementToReceiveFocus && elementToReceiveFocus.focus) {
4050           elementToReceiveFocus.focus();
4051         } else if (appendToElement.focus) {
4052           appendToElement.focus();
4053         }
4054       }
4055
4056       // Add or remove "windowTopClass" from the top window in the stack
4057       function toggleTopWindowClass(toggleSwitch) {
4058         var modalWindow;
4059
4060         if (openedWindows.length() > 0) {
4061           modalWindow = openedWindows.top().value;
4062           modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch);
4063         }
4064       }
4065
4066       function checkRemoveBackdrop() {
4067         //remove backdrop if no longer needed
4068         if (backdropDomEl && backdropIndex() === -1) {
4069           var backdropScopeRef = backdropScope;
4070           removeAfterAnimate(backdropDomEl, backdropScope, function() {
4071             backdropScopeRef = null;
4072           });
4073           backdropDomEl = undefined;
4074           backdropScope = undefined;
4075         }
4076       }
4077
4078       function removeAfterAnimate(domEl, scope, done, closedDeferred) {
4079         var asyncDeferred;
4080         var asyncPromise = null;
4081         var setIsAsync = function() {
4082           if (!asyncDeferred) {
4083             asyncDeferred = $q.defer();
4084             asyncPromise = asyncDeferred.promise;
4085           }
4086
4087           return function asyncDone() {
4088             asyncDeferred.resolve();
4089           };
4090         };
4091         scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync);
4092
4093         // Note that it's intentional that asyncPromise might be null.
4094         // That's when setIsAsync has not been called during the
4095         // NOW_CLOSING_EVENT broadcast.
4096         return $q.when(asyncPromise).then(afterAnimating);
4097
4098         function afterAnimating() {
4099           if (afterAnimating.done) {
4100             return;
4101           }
4102           afterAnimating.done = true;
4103
4104           $animate.leave(domEl).then(function() {
4105             if (done) {
4106               done();
4107             }
4108
4109             domEl.remove();
4110             if (closedDeferred) {
4111               closedDeferred.resolve();
4112             }
4113           });
4114
4115           scope.$destroy();
4116         }
4117       }
4118
4119       $document.on('keydown', keydownListener);
4120
4121       $rootScope.$on('$destroy', function() {
4122         $document.off('keydown', keydownListener);
4123       });
4124
4125       function keydownListener(evt) {
4126         if (evt.isDefaultPrevented()) {
4127           return evt;
4128         }
4129
4130         var modal = openedWindows.top();
4131         if (modal) {
4132           switch (evt.which) {
4133             case 27: {
4134               if (modal.value.keyboard) {
4135                 evt.preventDefault();
4136                 $rootScope.$apply(function() {
4137                   $modalStack.dismiss(modal.key, 'escape key press');
4138                 });
4139               }
4140               break;
4141             }
4142             case 9: {
4143               var list = $modalStack.loadFocusElementList(modal);
4144               var focusChanged = false;
4145               if (evt.shiftKey) {
4146                 if ($modalStack.isFocusInFirstItem(evt, list) || $modalStack.isModalFocused(evt, modal)) {
4147                   focusChanged = $modalStack.focusLastFocusableElement(list);
4148                 }
4149               } else {
4150                 if ($modalStack.isFocusInLastItem(evt, list)) {
4151                   focusChanged = $modalStack.focusFirstFocusableElement(list);
4152                 }
4153               }
4154
4155               if (focusChanged) {
4156                 evt.preventDefault();
4157                 evt.stopPropagation();
4158               }
4159
4160               break;
4161             }
4162           }
4163         }
4164       }
4165
4166       $modalStack.open = function(modalInstance, modal) {
4167         var modalOpener = $document[0].activeElement,
4168           modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
4169
4170         toggleTopWindowClass(false);
4171
4172         // Store the current top first, to determine what index we ought to use
4173         // for the current top modal
4174         previousTopOpenedModal = openedWindows.top();
4175
4176         openedWindows.add(modalInstance, {
4177           deferred: modal.deferred,
4178           renderDeferred: modal.renderDeferred,
4179           closedDeferred: modal.closedDeferred,
4180           modalScope: modal.scope,
4181           backdrop: modal.backdrop,
4182           keyboard: modal.keyboard,
4183           openedClass: modal.openedClass,
4184           windowTopClass: modal.windowTopClass,
4185           animation: modal.animation,
4186           appendTo: modal.appendTo
4187         });
4188
4189         openedClasses.put(modalBodyClass, modalInstance);
4190
4191         var appendToElement = modal.appendTo,
4192             currBackdropIndex = backdropIndex();
4193
4194         if (currBackdropIndex >= 0 && !backdropDomEl) {
4195           backdropScope = $rootScope.$new(true);
4196           backdropScope.modalOptions = modal;
4197           backdropScope.index = currBackdropIndex;
4198           backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');
4199           backdropDomEl.attr({
4200             'class': 'modal-backdrop',
4201             'ng-style': '{\'z-index\': 1040 + (index && 1 || 0) + index*10}',
4202             'uib-modal-animation-class': 'fade',
4203             'modal-in-class': 'in'
4204           });
4205           if (modal.backdropClass) {
4206             backdropDomEl.addClass(modal.backdropClass);
4207           }
4208
4209           if (modal.animation) {
4210             backdropDomEl.attr('modal-animation', 'true');
4211           }
4212           $compile(backdropDomEl)(backdropScope);
4213           $animate.enter(backdropDomEl, appendToElement);
4214           if ($uibPosition.isScrollable(appendToElement)) {
4215             scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement);
4216             if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
4217               appendToElement.css({paddingRight: scrollbarPadding.right + 'px'});
4218             }
4219           }
4220         }
4221
4222         var content;
4223         if (modal.component) {
4224           content = document.createElement(snake_case(modal.component.name));
4225           content = angular.element(content);
4226           content.attr({
4227             resolve: '$resolve',
4228             'modal-instance': '$uibModalInstance',
4229             close: '$close($value)',
4230             dismiss: '$dismiss($value)'
4231           });
4232         } else {
4233           content = modal.content;
4234         }
4235
4236         // Set the top modal index based on the index of the previous top modal
4237         topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0;
4238         var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
4239         angularDomEl.attr({
4240           'class': 'modal',
4241           'template-url': modal.windowTemplateUrl,
4242           'window-top-class': modal.windowTopClass,
4243           'role': 'dialog',
4244           'aria-labelledby': modal.ariaLabelledBy,
4245           'aria-describedby': modal.ariaDescribedBy,
4246           'size': modal.size,
4247           'index': topModalIndex,
4248           'animate': 'animate',
4249           'ng-style': '{\'z-index\': 1050 + $$topModalIndex*10, display: \'block\'}',
4250           'tabindex': -1,
4251           'uib-modal-animation-class': 'fade',
4252           'modal-in-class': 'in'
4253         }).append(content);
4254         if (modal.windowClass) {
4255           angularDomEl.addClass(modal.windowClass);
4256         }
4257
4258         if (modal.animation) {
4259           angularDomEl.attr('modal-animation', 'true');
4260         }
4261
4262         appendToElement.addClass(modalBodyClass);
4263         if (modal.scope) {
4264           // we need to explicitly add the modal index to the modal scope
4265           // because it is needed by ngStyle to compute the zIndex property.
4266           modal.scope.$$topModalIndex = topModalIndex;
4267         }
4268         $animate.enter($compile(angularDomEl)(modal.scope), appendToElement);
4269
4270         openedWindows.top().value.modalDomEl = angularDomEl;
4271         openedWindows.top().value.modalOpener = modalOpener;
4272
4273         applyAriaHidden(angularDomEl);
4274
4275         function applyAriaHidden(el) {
4276           if (!el || el[0].tagName === 'BODY') {
4277             return;
4278           }
4279
4280           getSiblings(el).forEach(function(sibling) {
4281             var elemIsAlreadyHidden = sibling.getAttribute('aria-hidden') === 'true',
4282               ariaHiddenCount = parseInt(sibling.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10);
4283
4284             if (!ariaHiddenCount) {
4285               ariaHiddenCount = elemIsAlreadyHidden ? 1 : 0;
4286             }
4287
4288             sibling.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, ariaHiddenCount + 1);
4289             sibling.setAttribute('aria-hidden', 'true');
4290           });
4291
4292           return applyAriaHidden(el.parent());
4293
4294           function getSiblings(el) {
4295             var children = el.parent() ? el.parent().children() : [];
4296
4297             return Array.prototype.filter.call(children, function(child) {
4298               return child !== el[0];
4299             });
4300           }
4301         }
4302       };
4303
4304       function broadcastClosing(modalWindow, resultOrReason, closing) {
4305         return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
4306       }
4307
4308       function unhideBackgroundElements() {
4309         Array.prototype.forEach.call(
4310           document.querySelectorAll('[' + ARIA_HIDDEN_ATTRIBUTE_NAME + ']'),
4311           function(hiddenEl) {
4312             var ariaHiddenCount = parseInt(hiddenEl.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10),
4313               newHiddenCount = ariaHiddenCount - 1;
4314             hiddenEl.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, newHiddenCount);
4315
4316             if (!newHiddenCount) {
4317               hiddenEl.removeAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME);
4318               hiddenEl.removeAttribute('aria-hidden');
4319             }
4320           }
4321         );
4322       }
4323
4324       $modalStack.close = function(modalInstance, result) {
4325         var modalWindow = openedWindows.get(modalInstance);
4326         unhideBackgroundElements();
4327         if (modalWindow && broadcastClosing(modalWindow, result, true)) {
4328           modalWindow.value.modalScope.$$uibDestructionScheduled = true;
4329           modalWindow.value.deferred.resolve(result);
4330           removeModalWindow(modalInstance, modalWindow.value.modalOpener);
4331           return true;
4332         }
4333
4334         return !modalWindow;
4335       };
4336
4337       $modalStack.dismiss = function(modalInstance, reason) {
4338         var modalWindow = openedWindows.get(modalInstance);
4339         unhideBackgroundElements();
4340         if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
4341           modalWindow.value.modalScope.$$uibDestructionScheduled = true;
4342           modalWindow.value.deferred.reject(reason);
4343           removeModalWindow(modalInstance, modalWindow.value.modalOpener);
4344           return true;
4345         }
4346         return !modalWindow;
4347       };
4348
4349       $modalStack.dismissAll = function(reason) {
4350         var topModal = this.getTop();
4351         while (topModal && this.dismiss(topModal.key, reason)) {
4352           topModal = this.getTop();
4353         }
4354       };
4355
4356       $modalStack.getTop = function() {
4357         return openedWindows.top();
4358       };
4359
4360       $modalStack.modalRendered = function(modalInstance) {
4361         var modalWindow = openedWindows.get(modalInstance);
4362         if (modalWindow) {
4363           modalWindow.value.renderDeferred.resolve();
4364         }
4365       };
4366
4367       $modalStack.focusFirstFocusableElement = function(list) {
4368         if (list.length > 0) {
4369           list[0].focus();
4370           return true;
4371         }
4372         return false;
4373       };
4374
4375       $modalStack.focusLastFocusableElement = function(list) {
4376         if (list.length > 0) {
4377           list[list.length - 1].focus();
4378           return true;
4379         }
4380         return false;
4381       };
4382
4383       $modalStack.isModalFocused = function(evt, modalWindow) {
4384         if (evt && modalWindow) {
4385           var modalDomEl = modalWindow.value.modalDomEl;
4386           if (modalDomEl && modalDomEl.length) {
4387             return (evt.target || evt.srcElement) === modalDomEl[0];
4388           }
4389         }
4390         return false;
4391       };
4392
4393       $modalStack.isFocusInFirstItem = function(evt, list) {
4394         if (list.length > 0) {
4395           return (evt.target || evt.srcElement) === list[0];
4396         }
4397         return false;
4398       };
4399
4400       $modalStack.isFocusInLastItem = function(evt, list) {
4401         if (list.length > 0) {
4402           return (evt.target || evt.srcElement) === list[list.length - 1];
4403         }
4404         return false;
4405       };
4406
4407       $modalStack.loadFocusElementList = function(modalWindow) {
4408         if (modalWindow) {
4409           var modalDomE1 = modalWindow.value.modalDomEl;
4410           if (modalDomE1 && modalDomE1.length) {
4411             var elements = modalDomE1[0].querySelectorAll(tabbableSelector);
4412             return elements ?
4413               Array.prototype.filter.call(elements, function(element) {
4414                 return isVisible(element);
4415               }) : elements;
4416           }
4417         }
4418       };
4419
4420       return $modalStack;
4421     }])
4422
4423   .provider('$uibModal', function() {
4424     var $modalProvider = {
4425       options: {
4426         animation: true,
4427         backdrop: true, //can also be false or 'static'
4428         keyboard: true
4429       },
4430       $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack',
4431         function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) {
4432           var $modal = {};
4433
4434           function getTemplatePromise(options) {
4435             return options.template ? $q.when(options.template) :
4436               $templateRequest(angular.isFunction(options.templateUrl) ?
4437                 options.templateUrl() : options.templateUrl);
4438           }
4439
4440           var promiseChain = null;
4441           $modal.getPromiseChain = function() {
4442             return promiseChain;
4443           };
4444
4445           $modal.open = function(modalOptions) {
4446             var modalResultDeferred = $q.defer();
4447             var modalOpenedDeferred = $q.defer();
4448             var modalClosedDeferred = $q.defer();
4449             var modalRenderDeferred = $q.defer();
4450
4451             //prepare an instance of a modal to be injected into controllers and returned to a caller
4452             var modalInstance = {
4453               result: modalResultDeferred.promise,
4454               opened: modalOpenedDeferred.promise,
4455               closed: modalClosedDeferred.promise,
4456               rendered: modalRenderDeferred.promise,
4457               close: function (result) {
4458                 return $modalStack.close(modalInstance, result);
4459               },
4460               dismiss: function (reason) {
4461                 return $modalStack.dismiss(modalInstance, reason);
4462               }
4463             };
4464
4465             //merge and clean up options
4466             modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
4467             modalOptions.resolve = modalOptions.resolve || {};
4468             modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
4469
4470             if (!modalOptions.appendTo.length) {
4471               throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
4472             }
4473
4474             //verify options
4475             if (!modalOptions.component && !modalOptions.template && !modalOptions.templateUrl) {
4476               throw new Error('One of component or template or templateUrl options is required.');
4477             }
4478
4479             var templateAndResolvePromise;
4480             if (modalOptions.component) {
4481               templateAndResolvePromise = $q.when($uibResolve.resolve(modalOptions.resolve, {}, null, null));
4482             } else {
4483               templateAndResolvePromise =
4484                 $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
4485             }
4486
4487             function resolveWithTemplate() {
4488               return templateAndResolvePromise;
4489             }
4490
4491             // Wait for the resolution of the existing promise chain.
4492             // Then switch to our own combined promise dependency (regardless of how the previous modal fared).
4493             // Then add to $modalStack and resolve opened.
4494             // Finally clean up the chain variable if no subsequent modal has overwritten it.
4495             var samePromise;
4496             samePromise = promiseChain = $q.all([promiseChain])
4497               .then(resolveWithTemplate, resolveWithTemplate)
4498               .then(function resolveSuccess(tplAndVars) {
4499                 var providedScope = modalOptions.scope || $rootScope;
4500
4501                 var modalScope = providedScope.$new();
4502                 modalScope.$close = modalInstance.close;
4503                 modalScope.$dismiss = modalInstance.dismiss;
4504
4505                 modalScope.$on('$destroy', function() {
4506                   if (!modalScope.$$uibDestructionScheduled) {
4507                     modalScope.$dismiss('$uibUnscheduledDestruction');
4508                   }
4509                 });
4510
4511                 var modal = {
4512                   scope: modalScope,
4513                   deferred: modalResultDeferred,
4514                   renderDeferred: modalRenderDeferred,
4515                   closedDeferred: modalClosedDeferred,
4516                   animation: modalOptions.animation,
4517                   backdrop: modalOptions.backdrop,
4518                   keyboard: modalOptions.keyboard,
4519                   backdropClass: modalOptions.backdropClass,
4520                   windowTopClass: modalOptions.windowTopClass,
4521                   windowClass: modalOptions.windowClass,
4522                   windowTemplateUrl: modalOptions.windowTemplateUrl,
4523                   ariaLabelledBy: modalOptions.ariaLabelledBy,
4524                   ariaDescribedBy: modalOptions.ariaDescribedBy,
4525                   size: modalOptions.size,
4526                   openedClass: modalOptions.openedClass,
4527                   appendTo: modalOptions.appendTo
4528                 };
4529
4530                 var component = {};
4531                 var ctrlInstance, ctrlInstantiate, ctrlLocals = {};
4532
4533                 if (modalOptions.component) {
4534                   constructLocals(component, false, true, false);
4535                   component.name = modalOptions.component;
4536                   modal.component = component;
4537                 } else if (modalOptions.controller) {
4538                   constructLocals(ctrlLocals, true, false, true);
4539
4540                   // the third param will make the controller instantiate later,private api
4541                   // @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126
4542                   ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true, modalOptions.controllerAs);
4543                   if (modalOptions.controllerAs && modalOptions.bindToController) {
4544                     ctrlInstance = ctrlInstantiate.instance;
4545                     ctrlInstance.$close = modalScope.$close;
4546                     ctrlInstance.$dismiss = modalScope.$dismiss;
4547                     angular.extend(ctrlInstance, {
4548                       $resolve: ctrlLocals.$scope.$resolve
4549                     }, providedScope);
4550                   }
4551
4552                   ctrlInstance = ctrlInstantiate();
4553
4554                   if (angular.isFunction(ctrlInstance.$onInit)) {
4555                     ctrlInstance.$onInit();
4556                   }
4557                 }
4558
4559                 if (!modalOptions.component) {
4560                   modal.content = tplAndVars[0];
4561                 }
4562
4563                 $modalStack.open(modalInstance, modal);
4564                 modalOpenedDeferred.resolve(true);
4565
4566                 function constructLocals(obj, template, instanceOnScope, injectable) {
4567                   obj.$scope = modalScope;
4568                   obj.$scope.$resolve = {};
4569                   if (instanceOnScope) {
4570                     obj.$scope.$uibModalInstance = modalInstance;
4571                   } else {
4572                     obj.$uibModalInstance = modalInstance;
4573                   }
4574
4575                   var resolves = template ? tplAndVars[1] : tplAndVars;
4576                   angular.forEach(resolves, function(value, key) {
4577                     if (injectable) {
4578                       obj[key] = value;
4579                     }
4580
4581                     obj.$scope.$resolve[key] = value;
4582                   });
4583                 }
4584             }, function resolveError(reason) {
4585               modalOpenedDeferred.reject(reason);
4586               modalResultDeferred.reject(reason);
4587             })['finally'](function() {
4588               if (promiseChain === samePromise) {
4589                 promiseChain = null;
4590               }
4591             });
4592
4593             return modalInstance;
4594           };
4595
4596           return $modal;
4597         }
4598       ]
4599     };
4600
4601     return $modalProvider;
4602   });
4603
4604 angular.module('ui.bootstrap.paging', [])
4605 /**
4606  * Helper internal service for generating common controller code between the
4607  * pager and pagination components
4608  */
4609 .factory('uibPaging', ['$parse', function($parse) {
4610   return {
4611     create: function(ctrl, $scope, $attrs) {
4612       ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
4613       ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl
4614       ctrl._watchers = [];
4615
4616       ctrl.init = function(ngModelCtrl, config) {
4617         ctrl.ngModelCtrl = ngModelCtrl;
4618         ctrl.config = config;
4619
4620         ngModelCtrl.$render = function() {
4621           ctrl.render();
4622         };
4623
4624         if ($attrs.itemsPerPage) {
4625           ctrl._watchers.push($scope.$parent.$watch($attrs.itemsPerPage, function(value) {
4626             ctrl.itemsPerPage = parseInt(value, 10);
4627             $scope.totalPages = ctrl.calculateTotalPages();
4628             ctrl.updatePage();
4629           }));
4630         } else {
4631           ctrl.itemsPerPage = config.itemsPerPage;
4632         }
4633
4634         $scope.$watch('totalItems', function(newTotal, oldTotal) {
4635           if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
4636             $scope.totalPages = ctrl.calculateTotalPages();
4637             ctrl.updatePage();
4638           }
4639         });
4640       };
4641
4642       ctrl.calculateTotalPages = function() {
4643         var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
4644         return Math.max(totalPages || 0, 1);
4645       };
4646
4647       ctrl.render = function() {
4648         $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
4649       };
4650
4651       $scope.selectPage = function(page, evt) {
4652         if (evt) {
4653           evt.preventDefault();
4654         }
4655
4656         var clickAllowed = !$scope.ngDisabled || !evt;
4657         if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
4658           if (evt && evt.target) {
4659             evt.target.blur();
4660           }
4661           ctrl.ngModelCtrl.$setViewValue(page);
4662           ctrl.ngModelCtrl.$render();
4663         }
4664       };
4665
4666       $scope.getText = function(key) {
4667         return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
4668       };
4669
4670       $scope.noPrevious = function() {
4671         return $scope.page === 1;
4672       };
4673
4674       $scope.noNext = function() {
4675         return $scope.page === $scope.totalPages;
4676       };
4677
4678       ctrl.updatePage = function() {
4679         ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
4680
4681         if ($scope.page > $scope.totalPages) {
4682           $scope.selectPage($scope.totalPages);
4683         } else {
4684           ctrl.ngModelCtrl.$render();
4685         }
4686       };
4687
4688       $scope.$on('$destroy', function() {
4689         while (ctrl._watchers.length) {
4690           ctrl._watchers.shift()();
4691         }
4692       });
4693     }
4694   };
4695 }]);
4696
4697 angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex'])
4698
4699 .controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
4700   $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
4701
4702   uibPaging.create(this, $scope, $attrs);
4703 }])
4704
4705 .constant('uibPagerConfig', {
4706   itemsPerPage: 10,
4707   previousText: '« Previous',
4708   nextText: 'Next Â»',
4709   align: true
4710 })
4711
4712 .directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) {
4713   return {
4714     scope: {
4715       totalItems: '=',
4716       previousText: '@',
4717       nextText: '@',
4718       ngDisabled: '='
4719     },
4720     require: ['uibPager', '?ngModel'],
4721     restrict: 'A',
4722     controller: 'UibPagerController',
4723     controllerAs: 'pager',
4724     templateUrl: function(element, attrs) {
4725       return attrs.templateUrl || 'uib/template/pager/pager.html';
4726     },
4727     link: function(scope, element, attrs, ctrls) {
4728       element.addClass('pager');
4729       var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
4730
4731       if (!ngModelCtrl) {
4732         return; // do nothing if no ng-model
4733       }
4734
4735       paginationCtrl.init(ngModelCtrl, uibPagerConfig);
4736     }
4737   };
4738 }]);
4739
4740 angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex'])
4741 .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
4742   var ctrl = this;
4743   // Setup configuration parameters
4744   var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
4745     rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
4746     forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
4747     boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers,
4748     pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity;
4749   $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
4750   $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
4751   $attrs.$set('role', 'menu');
4752
4753   uibPaging.create(this, $scope, $attrs);
4754
4755   if ($attrs.maxSize) {
4756     ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) {
4757       maxSize = parseInt(value, 10);
4758       ctrl.render();
4759     }));
4760   }
4761
4762   // Create page object used in template
4763   function makePage(number, text, isActive) {
4764     return {
4765       number: number,
4766       text: text,
4767       active: isActive
4768     };
4769   }
4770
4771   function getPages(currentPage, totalPages) {
4772     var pages = [];
4773
4774     // Default page limits
4775     var startPage = 1, endPage = totalPages;
4776     var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
4777
4778     // recompute if maxSize
4779     if (isMaxSized) {
4780       if (rotate) {
4781         // Current page is displayed in the middle of the visible ones
4782         startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
4783         endPage = startPage + maxSize - 1;
4784
4785         // Adjust if limit is exceeded
4786         if (endPage > totalPages) {
4787           endPage = totalPages;
4788           startPage = endPage - maxSize + 1;
4789         }
4790       } else {
4791         // Visible pages are paginated with maxSize
4792         startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
4793
4794         // Adjust last page if limit is exceeded
4795         endPage = Math.min(startPage + maxSize - 1, totalPages);
4796       }
4797     }
4798
4799     // Add page number links
4800     for (var number = startPage; number <= endPage; number++) {
4801       var page = makePage(number, pageLabel(number), number === currentPage);
4802       pages.push(page);
4803     }
4804
4805     // Add links to move between page sets
4806     if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
4807       if (startPage > 1) {
4808         if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
4809         var previousPageSet = makePage(startPage - 1, '...', false);
4810         pages.unshift(previousPageSet);
4811       }
4812         if (boundaryLinkNumbers) {
4813           if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
4814             var secondPageLink = makePage(2, '2', false);
4815             pages.unshift(secondPageLink);
4816           }
4817           //add the first page
4818           var firstPageLink = makePage(1, '1', false);
4819           pages.unshift(firstPageLink);
4820         }
4821       }
4822
4823       if (endPage < totalPages) {
4824         if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
4825         var nextPageSet = makePage(endPage + 1, '...', false);
4826         pages.push(nextPageSet);
4827       }
4828         if (boundaryLinkNumbers) {
4829           if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
4830             var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
4831             pages.push(secondToLastPageLink);
4832           }
4833           //add the last page
4834           var lastPageLink = makePage(totalPages, totalPages, false);
4835           pages.push(lastPageLink);
4836         }
4837       }
4838     }
4839     return pages;
4840   }
4841
4842   var originalRender = this.render;
4843   this.render = function() {
4844     originalRender();
4845     if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
4846       $scope.pages = getPages($scope.page, $scope.totalPages);
4847     }
4848   };
4849 }])
4850
4851 .constant('uibPaginationConfig', {
4852   itemsPerPage: 10,
4853   boundaryLinks: false,
4854   boundaryLinkNumbers: false,
4855   directionLinks: true,
4856   firstText: 'First',
4857   previousText: 'Previous',
4858   nextText: 'Next',
4859   lastText: 'Last',
4860   rotate: true,
4861   forceEllipses: false
4862 })
4863
4864 .directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) {
4865   return {
4866     scope: {
4867       totalItems: '=',
4868       firstText: '@',
4869       previousText: '@',
4870       nextText: '@',
4871       lastText: '@',
4872       ngDisabled:'='
4873     },
4874     require: ['uibPagination', '?ngModel'],
4875     restrict: 'A',
4876     controller: 'UibPaginationController',
4877     controllerAs: 'pagination',
4878     templateUrl: function(element, attrs) {
4879       return attrs.templateUrl || 'uib/template/pagination/pagination.html';
4880     },
4881     link: function(scope, element, attrs, ctrls) {
4882       element.addClass('pagination');
4883       var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
4884
4885       if (!ngModelCtrl) {
4886          return; // do nothing if no ng-model
4887       }
4888
4889       paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
4890     }
4891   };
4892 }]);
4893
4894 /**
4895  * The following features are still outstanding: animation as a
4896  * function, placement as a function, inside, support for more triggers than
4897  * just mouse enter/leave, html tooltips, and selector delegation.
4898  */
4899 angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap'])
4900
4901 /**
4902  * The $tooltip service creates tooltip- and popover-like directives as well as
4903  * houses global options for them.
4904  */
4905 .provider('$uibTooltip', function() {
4906   // The default options tooltip and popover.
4907   var defaultOptions = {
4908     placement: 'top',
4909     placementClassPrefix: '',
4910     animation: true,
4911     popupDelay: 0,
4912     popupCloseDelay: 0,
4913     useContentExp: false
4914   };
4915
4916   // Default hide triggers for each show trigger
4917   var triggerMap = {
4918     'mouseenter': 'mouseleave',
4919     'click': 'click',
4920     'outsideClick': 'outsideClick',
4921     'focus': 'blur',
4922     'none': ''
4923   };
4924
4925   // The options specified to the provider globally.
4926   var globalOptions = {};
4927
4928   /**
4929    * `options({})` allows global configuration of all tooltips in the
4930    * application.
4931    *
4932    *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
4933    *     // place tooltips left instead of top by default
4934    *     $tooltipProvider.options( { placement: 'left' } );
4935    *   });
4936    */
4937         this.options = function(value) {
4938                 angular.extend(globalOptions, value);
4939         };
4940
4941   /**
4942    * This allows you to extend the set of trigger mappings available. E.g.:
4943    *
4944    *   $tooltipProvider.setTriggers( { 'openTrigger': 'closeTrigger' } );
4945    */
4946   this.setTriggers = function setTriggers(triggers) {
4947     angular.extend(triggerMap, triggers);
4948   };
4949
4950   /**
4951    * This is a helper function for translating camel-case to snake_case.
4952    */
4953   function snake_case(name) {
4954     var regexp = /[A-Z]/g;
4955     var separator = '-';
4956     return name.replace(regexp, function(letter, pos) {
4957       return (pos ? separator : '') + letter.toLowerCase();
4958     });
4959   }
4960
4961   /**
4962    * Returns the actual instance of the $tooltip service.
4963    * TODO support multiple triggers
4964    */
4965   this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
4966     var openedTooltips = $$stackedMap.createNew();
4967     $document.on('keyup', keypressListener);
4968
4969     $rootScope.$on('$destroy', function() {
4970       $document.off('keyup', keypressListener);
4971     });
4972
4973     function keypressListener(e) {
4974       if (e.which === 27) {
4975         var last = openedTooltips.top();
4976         if (last) {
4977           last.value.close();
4978           last = null;
4979         }
4980       }
4981     }
4982
4983     return function $tooltip(ttType, prefix, defaultTriggerShow, options) {
4984       options = angular.extend({}, defaultOptions, globalOptions, options);
4985
4986       /**
4987        * Returns an object of show and hide triggers.
4988        *
4989        * If a trigger is supplied,
4990        * it is used to show the tooltip; otherwise, it will use the `trigger`
4991        * option passed to the `$tooltipProvider.options` method; else it will
4992        * default to the trigger supplied to this directive factory.
4993        *
4994        * The hide trigger is based on the show trigger. If the `trigger` option
4995        * was passed to the `$tooltipProvider.options` method, it will use the
4996        * mapped trigger from `triggerMap` or the passed trigger if the map is
4997        * undefined; otherwise, it uses the `triggerMap` value of the show
4998        * trigger; else it will just use the show trigger.
4999        */
5000       function getTriggers(trigger) {
5001         var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
5002         var hide = show.map(function(trigger) {
5003           return triggerMap[trigger] || trigger;
5004         });
5005         return {
5006           show: show,
5007           hide: hide
5008         };
5009       }
5010
5011       var directiveName = snake_case(ttType);
5012
5013       var startSym = $interpolate.startSymbol();
5014       var endSym = $interpolate.endSymbol();
5015       var template =
5016         '<div '+ directiveName + '-popup ' +
5017           'uib-title="' + startSym + 'title' + endSym + '" ' +
5018           (options.useContentExp ?
5019             'content-exp="contentExp()" ' :
5020             'content="' + startSym + 'content' + endSym + '" ') +
5021           'origin-scope="origScope" ' +
5022           'class="uib-position-measure ' + prefix + '" ' +
5023           'tooltip-animation-class="fade"' +
5024           'uib-tooltip-classes ' +
5025           'ng-class="{ in: isOpen }" ' +
5026           '>' +
5027         '</div>';
5028
5029       return {
5030         compile: function(tElem, tAttrs) {
5031           var tooltipLinker = $compile(template);
5032
5033           return function link(scope, element, attrs, tooltipCtrl) {
5034             var tooltip;
5035             var tooltipLinkedScope;
5036             var transitionTimeout;
5037             var showTimeout;
5038             var hideTimeout;
5039             var positionTimeout;
5040             var adjustmentTimeout;
5041             var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
5042             var triggers = getTriggers(undefined);
5043             var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
5044             var ttScope = scope.$new(true);
5045             var repositionScheduled = false;
5046             var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
5047             var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false;
5048             var observers = [];
5049             var lastPlacement;
5050
5051             var positionTooltip = function() {
5052               // check if tooltip exists and is not empty
5053               if (!tooltip || !tooltip.html()) { return; }
5054
5055               if (!positionTimeout) {
5056                 positionTimeout = $timeout(function() {
5057                   var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
5058                   var initialHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight');
5059                   var elementPos = appendToBody ? $position.offset(element) : $position.position(element);
5060                   tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' });
5061                   var placementClasses = ttPosition.placement.split('-');
5062
5063                   if (!tooltip.hasClass(placementClasses[0])) {
5064                     tooltip.removeClass(lastPlacement.split('-')[0]);
5065                     tooltip.addClass(placementClasses[0]);
5066                   }
5067
5068                   if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) {
5069                     tooltip.removeClass(options.placementClassPrefix + lastPlacement);
5070                     tooltip.addClass(options.placementClassPrefix + ttPosition.placement);
5071                   }
5072
5073                   adjustmentTimeout = $timeout(function() {
5074                     var currentHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight');
5075                     var adjustment = $position.adjustTop(placementClasses, elementPos, initialHeight, currentHeight);
5076                     if (adjustment) {
5077                       tooltip.css(adjustment);
5078                     }
5079                     adjustmentTimeout = null;
5080                   }, 0, false);
5081
5082                   // first time through tt element will have the
5083                   // uib-position-measure class or if the placement
5084                   // has changed we need to position the arrow.
5085                   if (tooltip.hasClass('uib-position-measure')) {
5086                     $position.positionArrow(tooltip, ttPosition.placement);
5087                     tooltip.removeClass('uib-position-measure');
5088                   } else if (lastPlacement !== ttPosition.placement) {
5089                     $position.positionArrow(tooltip, ttPosition.placement);
5090                   }
5091                   lastPlacement = ttPosition.placement;
5092
5093                   positionTimeout = null;
5094                 }, 0, false);
5095               }
5096             };
5097
5098             // Set up the correct scope to allow transclusion later
5099             ttScope.origScope = scope;
5100
5101             // By default, the tooltip is not open.
5102             // TODO add ability to start tooltip opened
5103             ttScope.isOpen = false;
5104
5105             function toggleTooltipBind() {
5106               if (!ttScope.isOpen) {
5107                 showTooltipBind();
5108               } else {
5109                 hideTooltipBind();
5110               }
5111             }
5112
5113             // Show the tooltip with delay if specified, otherwise show it immediately
5114             function showTooltipBind() {
5115               if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
5116                 return;
5117               }
5118
5119               cancelHide();
5120               prepareTooltip();
5121
5122               if (ttScope.popupDelay) {
5123                 // Do nothing if the tooltip was already scheduled to pop-up.
5124                 // This happens if show is triggered multiple times before any hide is triggered.
5125                 if (!showTimeout) {
5126                   showTimeout = $timeout(show, ttScope.popupDelay, false);
5127                 }
5128               } else {
5129                 show();
5130               }
5131             }
5132
5133             function hideTooltipBind() {
5134               cancelShow();
5135
5136               if (ttScope.popupCloseDelay) {
5137                 if (!hideTimeout) {
5138                   hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false);
5139                 }
5140               } else {
5141                 hide();
5142               }
5143             }
5144
5145             // Show the tooltip popup element.
5146             function show() {
5147               cancelShow();
5148               cancelHide();
5149
5150               // Don't show empty tooltips.
5151               if (!ttScope.content) {
5152                 return angular.noop;
5153               }
5154
5155               createTooltip();
5156
5157               // And show the tooltip.
5158               ttScope.$evalAsync(function() {
5159                 ttScope.isOpen = true;
5160                 assignIsOpen(true);
5161                 positionTooltip();
5162               });
5163             }
5164
5165             function cancelShow() {
5166               if (showTimeout) {
5167                 $timeout.cancel(showTimeout);
5168                 showTimeout = null;
5169               }
5170
5171               if (positionTimeout) {
5172                 $timeout.cancel(positionTimeout);
5173                 positionTimeout = null;
5174               }
5175             }
5176
5177             // Hide the tooltip popup element.
5178             function hide() {
5179               if (!ttScope) {
5180                 return;
5181               }
5182
5183               // First things first: we don't show it anymore.
5184               ttScope.$evalAsync(function() {
5185                 if (ttScope) {
5186                   ttScope.isOpen = false;
5187                   assignIsOpen(false);
5188                   // And now we remove it from the DOM. However, if we have animation, we
5189                   // need to wait for it to expire beforehand.
5190                   // FIXME: this is a placeholder for a port of the transitions library.
5191                   // The fade transition in TWBS is 150ms.
5192                   if (ttScope.animation) {
5193                     if (!transitionTimeout) {
5194                       transitionTimeout = $timeout(removeTooltip, 150, false);
5195                     }
5196                   } else {
5197                     removeTooltip();
5198                   }
5199                 }
5200               });
5201             }
5202
5203             function cancelHide() {
5204               if (hideTimeout) {
5205                 $timeout.cancel(hideTimeout);
5206                 hideTimeout = null;
5207               }
5208
5209               if (transitionTimeout) {
5210                 $timeout.cancel(transitionTimeout);
5211                 transitionTimeout = null;
5212               }
5213             }
5214
5215             function createTooltip() {
5216               // There can only be one tooltip element per directive shown at once.
5217               if (tooltip) {
5218                 return;
5219               }
5220
5221               tooltipLinkedScope = ttScope.$new();
5222               tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
5223                 if (appendToBody) {
5224                   $document.find('body').append(tooltip);
5225                 } else {
5226                   element.after(tooltip);
5227                 }
5228               });
5229
5230               openedTooltips.add(ttScope, {
5231                 close: hide
5232               });
5233
5234               prepObservers();
5235             }
5236
5237             function removeTooltip() {
5238               cancelShow();
5239               cancelHide();
5240               unregisterObservers();
5241
5242               if (tooltip) {
5243                 tooltip.remove();
5244                 
5245                 tooltip = null;
5246                 if (adjustmentTimeout) {
5247                   $timeout.cancel(adjustmentTimeout);
5248                 }
5249               }
5250
5251               openedTooltips.remove(ttScope);
5252               
5253               if (tooltipLinkedScope) {
5254                 tooltipLinkedScope.$destroy();
5255                 tooltipLinkedScope = null;
5256               }
5257             }
5258
5259             /**
5260              * Set the initial scope values. Once
5261              * the tooltip is created, the observers
5262              * will be added to keep things in sync.
5263              */
5264             function prepareTooltip() {
5265               ttScope.title = attrs[prefix + 'Title'];
5266               if (contentParse) {
5267                 ttScope.content = contentParse(scope);
5268               } else {
5269                 ttScope.content = attrs[ttType];
5270               }
5271
5272               ttScope.popupClass = attrs[prefix + 'Class'];
5273               ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement;
5274               var placement = $position.parsePlacement(ttScope.placement);
5275               lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0];
5276
5277               var delay = parseInt(attrs[prefix + 'PopupDelay'], 10);
5278               var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10);
5279               ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
5280               ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay;
5281             }
5282
5283             function assignIsOpen(isOpen) {
5284               if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
5285                 isOpenParse.assign(scope, isOpen);
5286               }
5287             }
5288
5289             ttScope.contentExp = function() {
5290               return ttScope.content;
5291             };
5292
5293             /**
5294              * Observe the relevant attributes.
5295              */
5296             attrs.$observe('disabled', function(val) {
5297               if (val) {
5298                 cancelShow();
5299               }
5300
5301               if (val && ttScope.isOpen) {
5302                 hide();
5303               }
5304             });
5305
5306             if (isOpenParse) {
5307               scope.$watch(isOpenParse, function(val) {
5308                 if (ttScope && !val === ttScope.isOpen) {
5309                   toggleTooltipBind();
5310                 }
5311               });
5312             }
5313
5314             function prepObservers() {
5315               observers.length = 0;
5316
5317               if (contentParse) {
5318                 observers.push(
5319                   scope.$watch(contentParse, function(val) {
5320                     ttScope.content = val;
5321                     if (!val && ttScope.isOpen) {
5322                       hide();
5323                     }
5324                   })
5325                 );
5326
5327                 observers.push(
5328                   tooltipLinkedScope.$watch(function() {
5329                     if (!repositionScheduled) {
5330                       repositionScheduled = true;
5331                       tooltipLinkedScope.$$postDigest(function() {
5332                         repositionScheduled = false;
5333                         if (ttScope && ttScope.isOpen) {
5334                           positionTooltip();
5335                         }
5336                       });
5337                     }
5338                   })
5339                 );
5340               } else {
5341                 observers.push(
5342                   attrs.$observe(ttType, function(val) {
5343                     ttScope.content = val;
5344                     if (!val && ttScope.isOpen) {
5345                       hide();
5346                     } else {
5347                       positionTooltip();
5348                     }
5349                   })
5350                 );
5351               }
5352
5353               observers.push(
5354                 attrs.$observe(prefix + 'Title', function(val) {
5355                   ttScope.title = val;
5356                   if (ttScope.isOpen) {
5357                     positionTooltip();
5358                   }
5359                 })
5360               );
5361
5362               observers.push(
5363                 attrs.$observe(prefix + 'Placement', function(val) {
5364                   ttScope.placement = val ? val : options.placement;
5365                   if (ttScope.isOpen) {
5366                     positionTooltip();
5367                   }
5368                 })
5369               );
5370             }
5371
5372             function unregisterObservers() {
5373               if (observers.length) {
5374                 angular.forEach(observers, function(observer) {
5375                   observer();
5376                 });
5377                 observers.length = 0;
5378               }
5379             }
5380
5381             // hide tooltips/popovers for outsideClick trigger
5382             function bodyHideTooltipBind(e) {
5383               if (!ttScope || !ttScope.isOpen || !tooltip) {
5384                 return;
5385               }
5386               // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked
5387               if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) {
5388                 hideTooltipBind();
5389               }
5390             }
5391
5392             // KeyboardEvent handler to hide the tooltip on Escape key press
5393             function hideOnEscapeKey(e) {
5394               if (e.which === 27) {
5395                 hideTooltipBind();
5396               }
5397             }
5398
5399             var unregisterTriggers = function() {
5400               triggers.show.forEach(function(trigger) {
5401                 if (trigger === 'outsideClick') {
5402                   element.off('click', toggleTooltipBind);
5403                 } else {
5404                   element.off(trigger, showTooltipBind);
5405                   element.off(trigger, toggleTooltipBind);
5406                 }
5407                 element.off('keypress', hideOnEscapeKey);
5408               });
5409               triggers.hide.forEach(function(trigger) {
5410                 if (trigger === 'outsideClick') {
5411                   $document.off('click', bodyHideTooltipBind);
5412                 } else {
5413                   element.off(trigger, hideTooltipBind);
5414                 }
5415               });
5416             };
5417
5418             function prepTriggers() {
5419               var showTriggers = [], hideTriggers = [];
5420               var val = scope.$eval(attrs[prefix + 'Trigger']);
5421               unregisterTriggers();
5422
5423               if (angular.isObject(val)) {
5424                 Object.keys(val).forEach(function(key) {
5425                   showTriggers.push(key);
5426                   hideTriggers.push(val[key]);
5427                 });
5428                 triggers = {
5429                   show: showTriggers,
5430                   hide: hideTriggers
5431                 };
5432               } else {
5433                 triggers = getTriggers(val);
5434               }
5435
5436               if (triggers.show !== 'none') {
5437                 triggers.show.forEach(function(trigger, idx) {
5438                   if (trigger === 'outsideClick') {
5439                     element.on('click', toggleTooltipBind);
5440                     $document.on('click', bodyHideTooltipBind);
5441                   } else if (trigger === triggers.hide[idx]) {
5442                     element.on(trigger, toggleTooltipBind);
5443                   } else if (trigger) {
5444                     element.on(trigger, showTooltipBind);
5445                     element.on(triggers.hide[idx], hideTooltipBind);
5446                   }
5447                   element.on('keypress', hideOnEscapeKey);
5448                 });
5449               }
5450             }
5451
5452             prepTriggers();
5453
5454             var animation = scope.$eval(attrs[prefix + 'Animation']);
5455             ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;
5456
5457             var appendToBodyVal;
5458             var appendKey = prefix + 'AppendToBody';
5459             if (appendKey in attrs && attrs[appendKey] === undefined) {
5460               appendToBodyVal = true;
5461             } else {
5462               appendToBodyVal = scope.$eval(attrs[appendKey]);
5463             }
5464
5465             appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;
5466
5467             // Make sure tooltip is destroyed and removed.
5468             scope.$on('$destroy', function onDestroyTooltip() {
5469               unregisterTriggers();
5470               removeTooltip();
5471               ttScope = null;
5472             });
5473           };
5474         }
5475       };
5476     };
5477   }];
5478 })
5479
5480 // This is mostly ngInclude code but with a custom scope
5481 .directive('uibTooltipTemplateTransclude', [
5482          '$animate', '$sce', '$compile', '$templateRequest',
5483 function ($animate, $sce, $compile, $templateRequest) {
5484   return {
5485     link: function(scope, elem, attrs) {
5486       var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
5487
5488       var changeCounter = 0,
5489         currentScope,
5490         previousElement,
5491         currentElement;
5492
5493       var cleanupLastIncludeContent = function() {
5494         if (previousElement) {
5495           previousElement.remove();
5496           previousElement = null;
5497         }
5498
5499         if (currentScope) {
5500           currentScope.$destroy();
5501           currentScope = null;
5502         }
5503
5504         if (currentElement) {
5505           $animate.leave(currentElement).then(function() {
5506             previousElement = null;
5507           });
5508           previousElement = currentElement;
5509           currentElement = null;
5510         }
5511       };
5512
5513       scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) {
5514         var thisChangeId = ++changeCounter;
5515
5516         if (src) {
5517           //set the 2nd param to true to ignore the template request error so that the inner
5518           //contents and scope can be cleaned up.
5519           $templateRequest(src, true).then(function(response) {
5520             if (thisChangeId !== changeCounter) { return; }
5521             var newScope = origScope.$new();
5522             var template = response;
5523
5524             var clone = $compile(template)(newScope, function(clone) {
5525               cleanupLastIncludeContent();
5526               $animate.enter(clone, elem);
5527             });
5528
5529             currentScope = newScope;
5530             currentElement = clone;
5531
5532             currentScope.$emit('$includeContentLoaded', src);
5533           }, function() {
5534             if (thisChangeId === changeCounter) {
5535               cleanupLastIncludeContent();
5536               scope.$emit('$includeContentError', src);
5537             }
5538           });
5539           scope.$emit('$includeContentRequested', src);
5540         } else {
5541           cleanupLastIncludeContent();
5542         }
5543       });
5544
5545       scope.$on('$destroy', cleanupLastIncludeContent);
5546     }
5547   };
5548 }])
5549
5550 /**
5551  * Note that it's intentional that these classes are *not* applied through $animate.
5552  * They must not be animated as they're expected to be present on the tooltip on
5553  * initialization.
5554  */
5555 .directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) {
5556   return {
5557     restrict: 'A',
5558     link: function(scope, element, attrs) {
5559       // need to set the primary position so the
5560       // arrow has space during position measure.
5561       // tooltip.positionTooltip()
5562       if (scope.placement) {
5563         // // There are no top-left etc... classes
5564         // // in TWBS, so we need the primary position.
5565         var position = $uibPosition.parsePlacement(scope.placement);
5566         element.addClass(position[0]);
5567       }
5568
5569       if (scope.popupClass) {
5570         element.addClass(scope.popupClass);
5571       }
5572
5573       if (scope.animation) {
5574         element.addClass(attrs.tooltipAnimationClass);
5575       }
5576     }
5577   };
5578 }])
5579
5580 .directive('uibTooltipPopup', function() {
5581   return {
5582     restrict: 'A',
5583     scope: { content: '@' },
5584     templateUrl: 'uib/template/tooltip/tooltip-popup.html'
5585   };
5586 })
5587
5588 .directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) {
5589   return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter');
5590 }])
5591
5592 .directive('uibTooltipTemplatePopup', function() {
5593   return {
5594     restrict: 'A',
5595     scope: { contentExp: '&', originScope: '&' },
5596     templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
5597   };
5598 })
5599
5600 .directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) {
5601   return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', {
5602     useContentExp: true
5603   });
5604 }])
5605
5606 .directive('uibTooltipHtmlPopup', function() {
5607   return {
5608     restrict: 'A',
5609     scope: { contentExp: '&' },
5610     templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
5611   };
5612 })
5613
5614 .directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) {
5615   return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', {
5616     useContentExp: true
5617   });
5618 }]);
5619
5620 /**
5621  * The following features are still outstanding: popup delay, animation as a
5622  * function, placement as a function, inside, support for more triggers than
5623  * just mouse enter/leave, and selector delegatation.
5624  */
5625 angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
5626
5627 .directive('uibPopoverTemplatePopup', function() {
5628   return {
5629     restrict: 'A',
5630     scope: { uibTitle: '@', contentExp: '&', originScope: '&' },
5631     templateUrl: 'uib/template/popover/popover-template.html'
5632   };
5633 })
5634
5635 .directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) {
5636   return $uibTooltip('uibPopoverTemplate', 'popover', 'click', {
5637     useContentExp: true
5638   });
5639 }])
5640
5641 .directive('uibPopoverHtmlPopup', function() {
5642   return {
5643     restrict: 'A',
5644     scope: { contentExp: '&', uibTitle: '@' },
5645     templateUrl: 'uib/template/popover/popover-html.html'
5646   };
5647 })
5648
5649 .directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) {
5650   return $uibTooltip('uibPopoverHtml', 'popover', 'click', {
5651     useContentExp: true
5652   });
5653 }])
5654
5655 .directive('uibPopoverPopup', function() {
5656   return {
5657     restrict: 'A',
5658     scope: { uibTitle: '@', content: '@' },
5659     templateUrl: 'uib/template/popover/popover.html'
5660   };
5661 })
5662
5663 .directive('uibPopover', ['$uibTooltip', function($uibTooltip) {
5664   return $uibTooltip('uibPopover', 'popover', 'click');
5665 }]);
5666
5667 angular.module('ui.bootstrap.progressbar', [])
5668
5669 .constant('uibProgressConfig', {
5670   animate: true,
5671   max: 100
5672 })
5673
5674 .controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) {
5675   var self = this,
5676       animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
5677
5678   this.bars = [];
5679   $scope.max = getMaxOrDefault();
5680
5681   this.addBar = function(bar, element, attrs) {
5682     if (!animate) {
5683       element.css({'transition': 'none'});
5684     }
5685
5686     this.bars.push(bar);
5687
5688     bar.max = getMaxOrDefault();
5689     bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar';
5690
5691     bar.$watch('value', function(value) {
5692       bar.recalculatePercentage();
5693     });
5694
5695     bar.recalculatePercentage = function() {
5696       var totalPercentage = self.bars.reduce(function(total, bar) {
5697         bar.percent = +(100 * bar.value / bar.max).toFixed(2);
5698         return total + bar.percent;
5699       }, 0);
5700
5701       if (totalPercentage > 100) {
5702         bar.percent -= totalPercentage - 100;
5703       }
5704     };
5705
5706     bar.$on('$destroy', function() {
5707       element = null;
5708       self.removeBar(bar);
5709     });
5710   };
5711
5712   this.removeBar = function(bar) {
5713     this.bars.splice(this.bars.indexOf(bar), 1);
5714     this.bars.forEach(function (bar) {
5715       bar.recalculatePercentage();
5716     });
5717   };
5718
5719   //$attrs.$observe('maxParam', function(maxParam) {
5720   $scope.$watch('maxParam', function(maxParam) {
5721     self.bars.forEach(function(bar) {
5722       bar.max = getMaxOrDefault();
5723       bar.recalculatePercentage();
5724     });
5725   });
5726
5727   function getMaxOrDefault () {
5728     return angular.isDefined($scope.maxParam) ? $scope.maxParam : progressConfig.max;
5729   }
5730 }])
5731
5732 .directive('uibProgress', function() {
5733   return {
5734     replace: true,
5735     transclude: true,
5736     controller: 'UibProgressController',
5737     require: 'uibProgress',
5738     scope: {
5739       maxParam: '=?max'
5740     },
5741     templateUrl: 'uib/template/progressbar/progress.html'
5742   };
5743 })
5744
5745 .directive('uibBar', function() {
5746   return {
5747     replace: true,
5748     transclude: true,
5749     require: '^uibProgress',
5750     scope: {
5751       value: '=',
5752       type: '@'
5753     },
5754     templateUrl: 'uib/template/progressbar/bar.html',
5755     link: function(scope, element, attrs, progressCtrl) {
5756       progressCtrl.addBar(scope, element, attrs);
5757     }
5758   };
5759 })
5760
5761 .directive('uibProgressbar', function() {
5762   return {
5763     replace: true,
5764     transclude: true,
5765     controller: 'UibProgressController',
5766     scope: {
5767       value: '=',
5768       maxParam: '=?max',
5769       type: '@'
5770     },
5771     templateUrl: 'uib/template/progressbar/progressbar.html',
5772     link: function(scope, element, attrs, progressCtrl) {
5773       progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title});
5774     }
5775   };
5776 });
5777
5778 angular.module('ui.bootstrap.rating', [])
5779
5780 .constant('uibRatingConfig', {
5781   max: 5,
5782   stateOn: null,
5783   stateOff: null,
5784   enableReset: true,
5785   titles: ['one', 'two', 'three', 'four', 'five']
5786 })
5787
5788 .controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
5789   var ngModelCtrl = { $setViewValue: angular.noop },
5790     self = this;
5791
5792   this.init = function(ngModelCtrl_) {
5793     ngModelCtrl = ngModelCtrl_;
5794     ngModelCtrl.$render = this.render;
5795
5796     ngModelCtrl.$formatters.push(function(value) {
5797       if (angular.isNumber(value) && value << 0 !== value) {
5798         value = Math.round(value);
5799       }
5800
5801       return value;
5802     });
5803
5804     this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
5805     this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
5806     this.enableReset = angular.isDefined($attrs.enableReset) ?
5807       $scope.$parent.$eval($attrs.enableReset) : ratingConfig.enableReset;
5808     var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles;
5809     this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
5810       tmpTitles : ratingConfig.titles;
5811
5812     var ratingStates = angular.isDefined($attrs.ratingStates) ?
5813       $scope.$parent.$eval($attrs.ratingStates) :
5814       new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max);
5815     $scope.range = this.buildTemplateObjects(ratingStates);
5816   };
5817
5818   this.buildTemplateObjects = function(states) {
5819     for (var i = 0, n = states.length; i < n; i++) {
5820       states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]);
5821     }
5822     return states;
5823   };
5824
5825   this.getTitle = function(index) {
5826     if (index >= this.titles.length) {
5827       return index + 1;
5828     }
5829
5830     return this.titles[index];
5831   };
5832
5833   $scope.rate = function(value) {
5834     if (!$scope.readonly && value >= 0 && value <= $scope.range.length) {
5835       var newViewValue = self.enableReset && ngModelCtrl.$viewValue === value ? 0 : value;
5836       ngModelCtrl.$setViewValue(newViewValue);
5837       ngModelCtrl.$render();
5838     }
5839   };
5840
5841   $scope.enter = function(value) {
5842     if (!$scope.readonly) {
5843       $scope.value = value;
5844     }
5845     $scope.onHover({value: value});
5846   };
5847
5848   $scope.reset = function() {
5849     $scope.value = ngModelCtrl.$viewValue;
5850     $scope.onLeave();
5851   };
5852
5853   $scope.onKeydown = function(evt) {
5854     if (/(37|38|39|40)/.test(evt.which)) {
5855       evt.preventDefault();
5856       evt.stopPropagation();
5857       $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1));
5858     }
5859   };
5860
5861   this.render = function() {
5862     $scope.value = ngModelCtrl.$viewValue;
5863     $scope.title = self.getTitle($scope.value - 1);
5864   };
5865 }])
5866
5867 .directive('uibRating', function() {
5868   return {
5869     require: ['uibRating', 'ngModel'],
5870     restrict: 'A',
5871     scope: {
5872       readonly: '=?readOnly',
5873       onHover: '&',
5874       onLeave: '&'
5875     },
5876     controller: 'UibRatingController',
5877     templateUrl: 'uib/template/rating/rating.html',
5878     link: function(scope, element, attrs, ctrls) {
5879       var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
5880       ratingCtrl.init(ngModelCtrl);
5881     }
5882   };
5883 });
5884
5885 angular.module('ui.bootstrap.tabs', [])
5886
5887 .controller('UibTabsetController', ['$scope', function ($scope) {
5888   var ctrl = this,
5889     oldIndex;
5890   ctrl.tabs = [];
5891
5892   ctrl.select = function(index, evt) {
5893     if (!destroyed) {
5894       var previousIndex = findTabIndex(oldIndex);
5895       var previousSelected = ctrl.tabs[previousIndex];
5896       if (previousSelected) {
5897         previousSelected.tab.onDeselect({
5898           $event: evt,
5899           $selectedIndex: index
5900         });
5901         if (evt && evt.isDefaultPrevented()) {
5902           return;
5903         }
5904         previousSelected.tab.active = false;
5905       }
5906
5907       var selected = ctrl.tabs[index];
5908       if (selected) {
5909         selected.tab.onSelect({
5910           $event: evt
5911         });
5912         selected.tab.active = true;
5913         ctrl.active = selected.index;
5914         oldIndex = selected.index;
5915       } else if (!selected && angular.isDefined(oldIndex)) {
5916         ctrl.active = null;
5917         oldIndex = null;
5918       }
5919     }
5920   };
5921
5922   ctrl.addTab = function addTab(tab) {
5923     ctrl.tabs.push({
5924       tab: tab,
5925       index: tab.index
5926     });
5927     ctrl.tabs.sort(function(t1, t2) {
5928       if (t1.index > t2.index) {
5929         return 1;
5930       }
5931
5932       if (t1.index < t2.index) {
5933         return -1;
5934       }
5935
5936       return 0;
5937     });
5938
5939     if (tab.index === ctrl.active || !angular.isDefined(ctrl.active) && ctrl.tabs.length === 1) {
5940       var newActiveIndex = findTabIndex(tab.index);
5941       ctrl.select(newActiveIndex);
5942     }
5943   };
5944
5945   ctrl.removeTab = function removeTab(tab) {
5946     var index;
5947     for (var i = 0; i < ctrl.tabs.length; i++) {
5948       if (ctrl.tabs[i].tab === tab) {
5949         index = i;
5950         break;
5951       }
5952     }
5953
5954     if (ctrl.tabs[index].index === ctrl.active) {
5955       var newActiveTabIndex = index === ctrl.tabs.length - 1 ?
5956         index - 1 : index + 1 % ctrl.tabs.length;
5957       ctrl.select(newActiveTabIndex);
5958     }
5959
5960     ctrl.tabs.splice(index, 1);
5961   };
5962
5963   $scope.$watch('tabset.active', function(val) {
5964     if (angular.isDefined(val) && val !== oldIndex) {
5965       ctrl.select(findTabIndex(val));
5966     }
5967   });
5968
5969   var destroyed;
5970   $scope.$on('$destroy', function() {
5971     destroyed = true;
5972   });
5973
5974   function findTabIndex(index) {
5975     for (var i = 0; i < ctrl.tabs.length; i++) {
5976       if (ctrl.tabs[i].index === index) {
5977         return i;
5978       }
5979     }
5980   }
5981 }])
5982
5983 .directive('uibTabset', function() {
5984   return {
5985     transclude: true,
5986     replace: true,
5987     scope: {},
5988     bindToController: {
5989       active: '=?',
5990       type: '@'
5991     },
5992     controller: 'UibTabsetController',
5993     controllerAs: 'tabset',
5994     templateUrl: function(element, attrs) {
5995       return attrs.templateUrl || 'uib/template/tabs/tabset.html';
5996     },
5997     link: function(scope, element, attrs) {
5998       scope.vertical = angular.isDefined(attrs.vertical) ?
5999         scope.$parent.$eval(attrs.vertical) : false;
6000       scope.justified = angular.isDefined(attrs.justified) ?
6001         scope.$parent.$eval(attrs.justified) : false;
6002     }
6003   };
6004 })
6005
6006 .directive('uibTab', ['$parse', function($parse) {
6007   return {
6008     require: '^uibTabset',
6009     replace: true,
6010     templateUrl: function(element, attrs) {
6011       return attrs.templateUrl || 'uib/template/tabs/tab.html';
6012     },
6013     transclude: true,
6014     scope: {
6015       heading: '@',
6016       index: '=?',
6017       classes: '@?',
6018       onSelect: '&select', //This callback is called in contentHeadingTransclude
6019                           //once it inserts the tab's content into the dom
6020       onDeselect: '&deselect'
6021     },
6022     controller: function() {
6023       //Empty controller so other directives can require being 'under' a tab
6024     },
6025     controllerAs: 'tab',
6026     link: function(scope, elm, attrs, tabsetCtrl, transclude) {
6027       scope.disabled = false;
6028       if (attrs.disable) {
6029         scope.$parent.$watch($parse(attrs.disable), function(value) {
6030           scope.disabled = !! value;
6031         });
6032       }
6033
6034       if (angular.isUndefined(attrs.index)) {
6035         if (tabsetCtrl.tabs && tabsetCtrl.tabs.length) {
6036           scope.index = Math.max.apply(null, tabsetCtrl.tabs.map(function(t) { return t.index; })) + 1;
6037         } else {
6038           scope.index = 0;
6039         }
6040       }
6041
6042       if (angular.isUndefined(attrs.classes)) {
6043         scope.classes = '';
6044       }
6045
6046       scope.select = function(evt) {
6047         if (!scope.disabled) {
6048           var index;
6049           for (var i = 0; i < tabsetCtrl.tabs.length; i++) {
6050             if (tabsetCtrl.tabs[i].tab === scope) {
6051               index = i;
6052               break;
6053             }
6054           }
6055
6056           tabsetCtrl.select(index, evt);
6057         }
6058       };
6059
6060       tabsetCtrl.addTab(scope);
6061       scope.$on('$destroy', function() {
6062         tabsetCtrl.removeTab(scope);
6063       });
6064
6065       //We need to transclude later, once the content container is ready.
6066       //when this link happens, we're inside a tab heading.
6067       scope.$transcludeFn = transclude;
6068     }
6069   };
6070 }])
6071
6072 .directive('uibTabHeadingTransclude', function() {
6073   return {
6074     restrict: 'A',
6075     require: '^uibTab',
6076     link: function(scope, elm) {
6077       scope.$watch('headingElement', function updateHeadingElement(heading) {
6078         if (heading) {
6079           elm.html('');
6080           elm.append(heading);
6081         }
6082       });
6083     }
6084   };
6085 })
6086
6087 .directive('uibTabContentTransclude', function() {
6088   return {
6089     restrict: 'A',
6090     require: '^uibTabset',
6091     link: function(scope, elm, attrs) {
6092       var tab = scope.$eval(attrs.uibTabContentTransclude).tab;
6093
6094       //Now our tab is ready to be transcluded: both the tab heading area
6095       //and the tab content area are loaded.  Transclude 'em both.
6096       tab.$transcludeFn(tab.$parent, function(contents) {
6097         angular.forEach(contents, function(node) {
6098           if (isTabHeading(node)) {
6099             //Let tabHeadingTransclude know.
6100             tab.headingElement = node;
6101           } else {
6102             elm.append(node);
6103           }
6104         });
6105       });
6106     }
6107   };
6108
6109   function isTabHeading(node) {
6110     return node.tagName && (
6111       node.hasAttribute('uib-tab-heading') ||
6112       node.hasAttribute('data-uib-tab-heading') ||
6113       node.hasAttribute('x-uib-tab-heading') ||
6114       node.tagName.toLowerCase() === 'uib-tab-heading' ||
6115       node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
6116       node.tagName.toLowerCase() === 'x-uib-tab-heading' ||
6117       node.tagName.toLowerCase() === 'uib:tab-heading'
6118     );
6119   }
6120 });
6121
6122 angular.module('ui.bootstrap.timepicker', [])
6123
6124 .constant('uibTimepickerConfig', {
6125   hourStep: 1,
6126   minuteStep: 1,
6127   secondStep: 1,
6128   showMeridian: true,
6129   showSeconds: false,
6130   meridians: null,
6131   readonlyInput: false,
6132   mousewheel: true,
6133   arrowkeys: true,
6134   showSpinners: true,
6135   templateUrl: 'uib/template/timepicker/timepicker.html'
6136 })
6137
6138 .controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
6139   var hoursModelCtrl, minutesModelCtrl, secondsModelCtrl;
6140   var selected = new Date(),
6141     watchers = [],
6142     ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
6143     meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS,
6144     padHours = angular.isDefined($attrs.padHours) ? $scope.$parent.$eval($attrs.padHours) : true;
6145
6146   $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
6147   $element.removeAttr('tabindex');
6148
6149   this.init = function(ngModelCtrl_, inputs) {
6150     ngModelCtrl = ngModelCtrl_;
6151     ngModelCtrl.$render = this.render;
6152
6153     ngModelCtrl.$formatters.unshift(function(modelValue) {
6154       return modelValue ? new Date(modelValue) : null;
6155     });
6156
6157     var hoursInputEl = inputs.eq(0),
6158         minutesInputEl = inputs.eq(1),
6159         secondsInputEl = inputs.eq(2);
6160
6161     hoursModelCtrl = hoursInputEl.controller('ngModel');
6162     minutesModelCtrl = minutesInputEl.controller('ngModel');
6163     secondsModelCtrl = secondsInputEl.controller('ngModel');
6164
6165     var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
6166
6167     if (mousewheel) {
6168       this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl);
6169     }
6170
6171     var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
6172     if (arrowkeys) {
6173       this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl);
6174     }
6175
6176     $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
6177     this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl);
6178   };
6179
6180   var hourStep = timepickerConfig.hourStep;
6181   if ($attrs.hourStep) {
6182     watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) {
6183       hourStep = +value;
6184     }));
6185   }
6186
6187   var minuteStep = timepickerConfig.minuteStep;
6188   if ($attrs.minuteStep) {
6189     watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
6190       minuteStep = +value;
6191     }));
6192   }
6193
6194   var min;
6195   watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) {
6196     var dt = new Date(value);
6197     min = isNaN(dt) ? undefined : dt;
6198   }));
6199
6200   var max;
6201   watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) {
6202     var dt = new Date(value);
6203     max = isNaN(dt) ? undefined : dt;
6204   }));
6205
6206   var disabled = false;
6207   if ($attrs.ngDisabled) {
6208     watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
6209       disabled = value;
6210     }));
6211   }
6212
6213   $scope.noIncrementHours = function() {
6214     var incrementedSelected = addMinutes(selected, hourStep * 60);
6215     return disabled || incrementedSelected > max ||
6216       incrementedSelected < selected && incrementedSelected < min;
6217   };
6218
6219   $scope.noDecrementHours = function() {
6220     var decrementedSelected = addMinutes(selected, -hourStep * 60);
6221     return disabled || decrementedSelected < min ||
6222       decrementedSelected > selected && decrementedSelected > max;
6223   };
6224
6225   $scope.noIncrementMinutes = function() {
6226     var incrementedSelected = addMinutes(selected, minuteStep);
6227     return disabled || incrementedSelected > max ||
6228       incrementedSelected < selected && incrementedSelected < min;
6229   };
6230
6231   $scope.noDecrementMinutes = function() {
6232     var decrementedSelected = addMinutes(selected, -minuteStep);
6233     return disabled || decrementedSelected < min ||
6234       decrementedSelected > selected && decrementedSelected > max;
6235   };
6236
6237   $scope.noIncrementSeconds = function() {
6238     var incrementedSelected = addSeconds(selected, secondStep);
6239     return disabled || incrementedSelected > max ||
6240       incrementedSelected < selected && incrementedSelected < min;
6241   };
6242
6243   $scope.noDecrementSeconds = function() {
6244     var decrementedSelected = addSeconds(selected, -secondStep);
6245     return disabled || decrementedSelected < min ||
6246       decrementedSelected > selected && decrementedSelected > max;
6247   };
6248
6249   $scope.noToggleMeridian = function() {
6250     if (selected.getHours() < 12) {
6251       return disabled || addMinutes(selected, 12 * 60) > max;
6252     }
6253
6254     return disabled || addMinutes(selected, -12 * 60) < min;
6255   };
6256
6257   var secondStep = timepickerConfig.secondStep;
6258   if ($attrs.secondStep) {
6259     watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) {
6260       secondStep = +value;
6261     }));
6262   }
6263
6264   $scope.showSeconds = timepickerConfig.showSeconds;
6265   if ($attrs.showSeconds) {
6266     watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
6267       $scope.showSeconds = !!value;
6268     }));
6269   }
6270
6271   // 12H / 24H mode
6272   $scope.showMeridian = timepickerConfig.showMeridian;
6273   if ($attrs.showMeridian) {
6274     watchers.push($scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
6275       $scope.showMeridian = !!value;
6276
6277       if (ngModelCtrl.$error.time) {
6278         // Evaluate from template
6279         var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
6280         if (angular.isDefined(hours) && angular.isDefined(minutes)) {
6281           selected.setHours(hours);
6282           refresh();
6283         }
6284       } else {
6285         updateTemplate();
6286       }
6287     }));
6288   }
6289
6290   // Get $scope.hours in 24H mode if valid
6291   function getHoursFromTemplate() {
6292     var hours = +$scope.hours;
6293     var valid = $scope.showMeridian ? hours > 0 && hours < 13 :
6294       hours >= 0 && hours < 24;
6295     if (!valid || $scope.hours === '') {
6296       return undefined;
6297     }
6298
6299     if ($scope.showMeridian) {
6300       if (hours === 12) {
6301         hours = 0;
6302       }
6303       if ($scope.meridian === meridians[1]) {
6304         hours = hours + 12;
6305       }
6306     }
6307     return hours;
6308   }
6309
6310   function getMinutesFromTemplate() {
6311     var minutes = +$scope.minutes;
6312     var valid = minutes >= 0 && minutes < 60;
6313     if (!valid || $scope.minutes === '') {
6314       return undefined;
6315     }
6316     return minutes;
6317   }
6318
6319   function getSecondsFromTemplate() {
6320     var seconds = +$scope.seconds;
6321     return seconds >= 0 && seconds < 60 ? seconds : undefined;
6322   }
6323
6324   function pad(value, noPad) {
6325     if (value === null) {
6326       return '';
6327     }
6328
6329     return angular.isDefined(value) && value.toString().length < 2 && !noPad ?
6330       '0' + value : value.toString();
6331   }
6332
6333   // Respond on mousewheel spin
6334   this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
6335     var isScrollingUp = function(e) {
6336       if (e.originalEvent) {
6337         e = e.originalEvent;
6338       }
6339       //pick correct delta variable depending on event
6340       var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY;
6341       return e.detail || delta > 0;
6342     };
6343
6344     hoursInputEl.on('mousewheel wheel', function(e) {
6345       if (!disabled) {
6346         $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
6347       }
6348       e.preventDefault();
6349     });
6350
6351     minutesInputEl.on('mousewheel wheel', function(e) {
6352       if (!disabled) {
6353         $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
6354       }
6355       e.preventDefault();
6356     });
6357
6358      secondsInputEl.on('mousewheel wheel', function(e) {
6359       if (!disabled) {
6360         $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
6361       }
6362       e.preventDefault();
6363     });
6364   };
6365
6366   // Respond on up/down arrowkeys
6367   this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
6368     hoursInputEl.on('keydown', function(e) {
6369       if (!disabled) {
6370         if (e.which === 38) { // up
6371           e.preventDefault();
6372           $scope.incrementHours();
6373           $scope.$apply();
6374         } else if (e.which === 40) { // down
6375           e.preventDefault();
6376           $scope.decrementHours();
6377           $scope.$apply();
6378         }
6379       }
6380     });
6381
6382     minutesInputEl.on('keydown', function(e) {
6383       if (!disabled) {
6384         if (e.which === 38) { // up
6385           e.preventDefault();
6386           $scope.incrementMinutes();
6387           $scope.$apply();
6388         } else if (e.which === 40) { // down
6389           e.preventDefault();
6390           $scope.decrementMinutes();
6391           $scope.$apply();
6392         }
6393       }
6394     });
6395
6396     secondsInputEl.on('keydown', function(e) {
6397       if (!disabled) {
6398         if (e.which === 38) { // up
6399           e.preventDefault();
6400           $scope.incrementSeconds();
6401           $scope.$apply();
6402         } else if (e.which === 40) { // down
6403           e.preventDefault();
6404           $scope.decrementSeconds();
6405           $scope.$apply();
6406         }
6407       }
6408     });
6409   };
6410
6411   this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
6412     if ($scope.readonlyInput) {
6413       $scope.updateHours = angular.noop;
6414       $scope.updateMinutes = angular.noop;
6415       $scope.updateSeconds = angular.noop;
6416       return;
6417     }
6418
6419     var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) {
6420       ngModelCtrl.$setViewValue(null);
6421       ngModelCtrl.$setValidity('time', false);
6422       if (angular.isDefined(invalidHours)) {
6423         $scope.invalidHours = invalidHours;
6424         if (hoursModelCtrl) {
6425           hoursModelCtrl.$setValidity('hours', false);
6426         }
6427       }
6428
6429       if (angular.isDefined(invalidMinutes)) {
6430         $scope.invalidMinutes = invalidMinutes;
6431         if (minutesModelCtrl) {
6432           minutesModelCtrl.$setValidity('minutes', false);
6433         }
6434       }
6435
6436       if (angular.isDefined(invalidSeconds)) {
6437         $scope.invalidSeconds = invalidSeconds;
6438         if (secondsModelCtrl) {
6439           secondsModelCtrl.$setValidity('seconds', false);
6440         }
6441       }
6442     };
6443
6444     $scope.updateHours = function() {
6445       var hours = getHoursFromTemplate(),
6446         minutes = getMinutesFromTemplate();
6447
6448       ngModelCtrl.$setDirty();
6449
6450       if (angular.isDefined(hours) && angular.isDefined(minutes)) {
6451         selected.setHours(hours);
6452         selected.setMinutes(minutes);
6453         if (selected < min || selected > max) {
6454           invalidate(true);
6455         } else {
6456           refresh('h');
6457         }
6458       } else {
6459         invalidate(true);
6460       }
6461     };
6462
6463     hoursInputEl.on('blur', function(e) {
6464       ngModelCtrl.$setTouched();
6465       if (modelIsEmpty()) {
6466         makeValid();
6467       } else if ($scope.hours === null || $scope.hours === '') {
6468         invalidate(true);
6469       } else if (!$scope.invalidHours && $scope.hours < 10) {
6470         $scope.$apply(function() {
6471           $scope.hours = pad($scope.hours, !padHours);
6472         });
6473       }
6474     });
6475
6476     $scope.updateMinutes = function() {
6477       var minutes = getMinutesFromTemplate(),
6478         hours = getHoursFromTemplate();
6479
6480       ngModelCtrl.$setDirty();
6481
6482       if (angular.isDefined(minutes) && angular.isDefined(hours)) {
6483         selected.setHours(hours);
6484         selected.setMinutes(minutes);
6485         if (selected < min || selected > max) {
6486           invalidate(undefined, true);
6487         } else {
6488           refresh('m');
6489         }
6490       } else {
6491         invalidate(undefined, true);
6492       }
6493     };
6494
6495     minutesInputEl.on('blur', function(e) {
6496       ngModelCtrl.$setTouched();
6497       if (modelIsEmpty()) {
6498         makeValid();
6499       } else if ($scope.minutes === null) {
6500         invalidate(undefined, true);
6501       } else if (!$scope.invalidMinutes && $scope.minutes < 10) {
6502         $scope.$apply(function() {
6503           $scope.minutes = pad($scope.minutes);
6504         });
6505       }
6506     });
6507
6508     $scope.updateSeconds = function() {
6509       var seconds = getSecondsFromTemplate();
6510
6511       ngModelCtrl.$setDirty();
6512
6513       if (angular.isDefined(seconds)) {
6514         selected.setSeconds(seconds);
6515         refresh('s');
6516       } else {
6517         invalidate(undefined, undefined, true);
6518       }
6519     };
6520
6521     secondsInputEl.on('blur', function(e) {
6522       if (modelIsEmpty()) {
6523         makeValid();
6524       } else if (!$scope.invalidSeconds && $scope.seconds < 10) {
6525         $scope.$apply( function() {
6526           $scope.seconds = pad($scope.seconds);
6527         });
6528       }
6529     });
6530
6531   };
6532
6533   this.render = function() {
6534     var date = ngModelCtrl.$viewValue;
6535
6536     if (isNaN(date)) {
6537       ngModelCtrl.$setValidity('time', false);
6538       $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
6539     } else {
6540       if (date) {
6541         selected = date;
6542       }
6543
6544       if (selected < min || selected > max) {
6545         ngModelCtrl.$setValidity('time', false);
6546         $scope.invalidHours = true;
6547         $scope.invalidMinutes = true;
6548       } else {
6549         makeValid();
6550       }
6551       updateTemplate();
6552     }
6553   };
6554
6555   // Call internally when we know that model is valid.
6556   function refresh(keyboardChange) {
6557     makeValid();
6558     ngModelCtrl.$setViewValue(new Date(selected));
6559     updateTemplate(keyboardChange);
6560   }
6561
6562   function makeValid() {
6563     if (hoursModelCtrl) {
6564       hoursModelCtrl.$setValidity('hours', true);
6565     }
6566
6567     if (minutesModelCtrl) {
6568       minutesModelCtrl.$setValidity('minutes', true);
6569     }
6570
6571     if (secondsModelCtrl) {
6572       secondsModelCtrl.$setValidity('seconds', true);
6573     }
6574
6575     ngModelCtrl.$setValidity('time', true);
6576     $scope.invalidHours = false;
6577     $scope.invalidMinutes = false;
6578     $scope.invalidSeconds = false;
6579   }
6580
6581   function updateTemplate(keyboardChange) {
6582     if (!ngModelCtrl.$modelValue) {
6583       $scope.hours = null;
6584       $scope.minutes = null;
6585       $scope.seconds = null;
6586       $scope.meridian = meridians[0];
6587     } else {
6588       var hours = selected.getHours(),
6589         minutes = selected.getMinutes(),
6590         seconds = selected.getSeconds();
6591
6592       if ($scope.showMeridian) {
6593         hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system
6594       }
6595
6596       $scope.hours = keyboardChange === 'h' ? hours : pad(hours, !padHours);
6597       if (keyboardChange !== 'm') {
6598         $scope.minutes = pad(minutes);
6599       }
6600       $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
6601
6602       if (keyboardChange !== 's') {
6603         $scope.seconds = pad(seconds);
6604       }
6605       $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
6606     }
6607   }
6608
6609   function addSecondsToSelected(seconds) {
6610     selected = addSeconds(selected, seconds);
6611     refresh();
6612   }
6613
6614   function addMinutes(selected, minutes) {
6615     return addSeconds(selected, minutes*60);
6616   }
6617
6618   function addSeconds(date, seconds) {
6619     var dt = new Date(date.getTime() + seconds * 1000);
6620     var newDate = new Date(date);
6621     newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
6622     return newDate;
6623   }
6624
6625   function modelIsEmpty() {
6626     return ($scope.hours === null || $scope.hours === '') &&
6627       ($scope.minutes === null || $scope.minutes === '') &&
6628       (!$scope.showSeconds || $scope.showSeconds && ($scope.seconds === null || $scope.seconds === ''));
6629   }
6630
6631   $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
6632     $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
6633
6634   $scope.incrementHours = function() {
6635     if (!$scope.noIncrementHours()) {
6636       addSecondsToSelected(hourStep * 60 * 60);
6637     }
6638   };
6639
6640   $scope.decrementHours = function() {
6641     if (!$scope.noDecrementHours()) {
6642       addSecondsToSelected(-hourStep * 60 * 60);
6643     }
6644   };
6645
6646   $scope.incrementMinutes = function() {
6647     if (!$scope.noIncrementMinutes()) {
6648       addSecondsToSelected(minuteStep * 60);
6649     }
6650   };
6651
6652   $scope.decrementMinutes = function() {
6653     if (!$scope.noDecrementMinutes()) {
6654       addSecondsToSelected(-minuteStep * 60);
6655     }
6656   };
6657
6658   $scope.incrementSeconds = function() {
6659     if (!$scope.noIncrementSeconds()) {
6660       addSecondsToSelected(secondStep);
6661     }
6662   };
6663
6664   $scope.decrementSeconds = function() {
6665     if (!$scope.noDecrementSeconds()) {
6666       addSecondsToSelected(-secondStep);
6667     }
6668   };
6669
6670   $scope.toggleMeridian = function() {
6671     var minutes = getMinutesFromTemplate(),
6672         hours = getHoursFromTemplate();
6673
6674     if (!$scope.noToggleMeridian()) {
6675       if (angular.isDefined(minutes) && angular.isDefined(hours)) {
6676         addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60));
6677       } else {
6678         $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0];
6679       }
6680     }
6681   };
6682
6683   $scope.blur = function() {
6684     ngModelCtrl.$setTouched();
6685   };
6686
6687   $scope.$on('$destroy', function() {
6688     while (watchers.length) {
6689       watchers.shift()();
6690     }
6691   });
6692 }])
6693
6694 .directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) {
6695   return {
6696     require: ['uibTimepicker', '?^ngModel'],
6697     restrict: 'A',
6698     controller: 'UibTimepickerController',
6699     controllerAs: 'timepicker',
6700     scope: {},
6701     templateUrl: function(element, attrs) {
6702       return attrs.templateUrl || uibTimepickerConfig.templateUrl;
6703     },
6704     link: function(scope, element, attrs, ctrls) {
6705       var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
6706
6707       if (ngModelCtrl) {
6708         timepickerCtrl.init(ngModelCtrl, element.find('input'));
6709       }
6710     }
6711   };
6712 }]);
6713
6714 angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])
6715
6716 /**
6717  * A helper service that can parse typeahead's syntax (string provided by users)
6718  * Extracted to a separate service for ease of unit testing
6719  */
6720   .factory('uibTypeaheadParser', ['$parse', function($parse) {
6721     //                      000001111111100000000000002222222200000000000000003333333333333330000000000044444444000
6722     var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
6723     return {
6724       parse: function(input) {
6725         var match = input.match(TYPEAHEAD_REGEXP);
6726         if (!match) {
6727           throw new Error(
6728             'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
6729               ' but got "' + input + '".');
6730         }
6731
6732         return {
6733           itemName: match[3],
6734           source: $parse(match[4]),
6735           viewMapper: $parse(match[2] || match[1]),
6736           modelMapper: $parse(match[1])
6737         };
6738       }
6739     };
6740   }])
6741
6742   .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
6743     function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
6744     var HOT_KEYS = [9, 13, 27, 38, 40];
6745     var eventDebounceTime = 200;
6746     var modelCtrl, ngModelOptions;
6747     //SUPPORTED ATTRIBUTES (OPTIONS)
6748
6749     //minimal no of characters that needs to be entered before typeahead kicks-in
6750     var minLength = originalScope.$eval(attrs.typeaheadMinLength);
6751     if (!minLength && minLength !== 0) {
6752       minLength = 1;
6753     }
6754
6755     originalScope.$watch(attrs.typeaheadMinLength, function (newVal) {
6756         minLength = !newVal && newVal !== 0 ? 1 : newVal;
6757     });
6758
6759     //minimal wait time after last character typed before typeahead kicks-in
6760     var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
6761
6762     //should it restrict model values to the ones selected from the popup only?
6763     var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
6764     originalScope.$watch(attrs.typeaheadEditable, function (newVal) {
6765       isEditable = newVal !== false;
6766     });
6767
6768     //binding to a variable that indicates if matches are being retrieved asynchronously
6769     var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
6770
6771     //a function to determine if an event should cause selection
6772     var isSelectEvent = attrs.typeaheadShouldSelect ? $parse(attrs.typeaheadShouldSelect) : function(scope, vals) {
6773       var evt = vals.$event;
6774       return evt.which === 13 || evt.which === 9;
6775     };
6776
6777     //a callback executed when a match is selected
6778     var onSelectCallback = $parse(attrs.typeaheadOnSelect);
6779
6780     //should it select highlighted popup value when losing focus?
6781     var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
6782
6783     //binding to a variable that indicates if there were no results after the query is completed
6784     var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
6785
6786     var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
6787
6788     var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
6789
6790     var appendTo = attrs.typeaheadAppendTo ?
6791       originalScope.$eval(attrs.typeaheadAppendTo) : null;
6792
6793     var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
6794
6795     //If input matches an item of the list exactly, select it automatically
6796     var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
6797
6798     //binding to a variable that indicates if dropdown is open
6799     var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop;
6800
6801     var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false;
6802
6803     //INTERNAL VARIABLES
6804
6805     //model setter executed upon match selection
6806     var parsedModel = $parse(attrs.ngModel);
6807     var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
6808     var $setModelValue = function(scope, newValue) {
6809       if (angular.isFunction(parsedModel(originalScope)) &&
6810         ngModelOptions.getOption('getterSetter')) {
6811         return invokeModelSetter(scope, {$$$p: newValue});
6812       }
6813
6814       return parsedModel.assign(scope, newValue);
6815     };
6816
6817     //expressions used by typeahead
6818     var parserResult = typeaheadParser.parse(attrs.uibTypeahead);
6819
6820     var hasFocus;
6821
6822     //Used to avoid bug in iOS webview where iOS keyboard does not fire
6823     //mousedown & mouseup events
6824     //Issue #3699
6825     var selected;
6826
6827     //create a child scope for the typeahead directive so we are not polluting original scope
6828     //with typeahead-specific data (matches, query etc.)
6829     var scope = originalScope.$new();
6830     var offDestroy = originalScope.$on('$destroy', function() {
6831       scope.$destroy();
6832     });
6833     scope.$on('$destroy', offDestroy);
6834
6835     // WAI-ARIA
6836     var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
6837     element.attr({
6838       'aria-autocomplete': 'list',
6839       'aria-expanded': false,
6840       'aria-owns': popupId
6841     });
6842
6843     var inputsContainer, hintInputElem;
6844     //add read-only input to show hint
6845     if (showHint) {
6846       inputsContainer = angular.element('<div></div>');
6847       inputsContainer.css('position', 'relative');
6848       element.after(inputsContainer);
6849       hintInputElem = element.clone();
6850       hintInputElem.attr('placeholder', '');
6851       hintInputElem.attr('tabindex', '-1');
6852       hintInputElem.val('');
6853       hintInputElem.css({
6854         'position': 'absolute',
6855         'top': '0px',
6856         'left': '0px',
6857         'border-color': 'transparent',
6858         'box-shadow': 'none',
6859         'opacity': 1,
6860         'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)',
6861         'color': '#999'
6862       });
6863       element.css({
6864         'position': 'relative',
6865         'vertical-align': 'top',
6866         'background-color': 'transparent'
6867       });
6868
6869       if (hintInputElem.attr('id')) {
6870         hintInputElem.removeAttr('id'); // remove duplicate id if present.
6871       }
6872       inputsContainer.append(hintInputElem);
6873       hintInputElem.after(element);
6874     }
6875
6876     //pop-up element used to display matches
6877     var popUpEl = angular.element('<div uib-typeahead-popup></div>');
6878     popUpEl.attr({
6879       id: popupId,
6880       matches: 'matches',
6881       active: 'activeIdx',
6882       select: 'select(activeIdx, evt)',
6883       'move-in-progress': 'moveInProgress',
6884       query: 'query',
6885       position: 'position',
6886       'assign-is-open': 'assignIsOpen(isOpen)',
6887       debounce: 'debounceUpdate'
6888     });
6889     //custom item template
6890     if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
6891       popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
6892     }
6893
6894     if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
6895       popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
6896     }
6897
6898     var resetHint = function() {
6899       if (showHint) {
6900         hintInputElem.val('');
6901       }
6902     };
6903
6904     var resetMatches = function() {
6905       scope.matches = [];
6906       scope.activeIdx = -1;
6907       element.attr('aria-expanded', false);
6908       resetHint();
6909     };
6910
6911     var getMatchId = function(index) {
6912       return popupId + '-option-' + index;
6913     };
6914
6915     // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
6916     // This attribute is added or removed automatically when the `activeIdx` changes.
6917     scope.$watch('activeIdx', function(index) {
6918       if (index < 0) {
6919         element.removeAttr('aria-activedescendant');
6920       } else {
6921         element.attr('aria-activedescendant', getMatchId(index));
6922       }
6923     });
6924
6925     var inputIsExactMatch = function(inputValue, index) {
6926       if (scope.matches.length > index && inputValue) {
6927         return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
6928       }
6929
6930       return false;
6931     };
6932
6933     var getMatchesAsync = function(inputValue, evt) {
6934       var locals = {$viewValue: inputValue};
6935       isLoadingSetter(originalScope, true);
6936       isNoResultsSetter(originalScope, false);
6937       $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
6938         //it might happen that several async queries were in progress if a user were typing fast
6939         //but we are interested only in responses that correspond to the current view value
6940         var onCurrentRequest = inputValue === modelCtrl.$viewValue;
6941         if (onCurrentRequest && hasFocus) {
6942           if (matches && matches.length > 0) {
6943             scope.activeIdx = focusFirst ? 0 : -1;
6944             isNoResultsSetter(originalScope, false);
6945             scope.matches.length = 0;
6946
6947             //transform labels
6948             for (var i = 0; i < matches.length; i++) {
6949               locals[parserResult.itemName] = matches[i];
6950               scope.matches.push({
6951                 id: getMatchId(i),
6952                 label: parserResult.viewMapper(scope, locals),
6953                 model: matches[i]
6954               });
6955             }
6956
6957             scope.query = inputValue;
6958             //position pop-up with matches - we need to re-calculate its position each time we are opening a window
6959             //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
6960             //due to other elements being rendered
6961             recalculatePosition();
6962
6963             element.attr('aria-expanded', true);
6964
6965             //Select the single remaining option if user input matches
6966             if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
6967               if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
6968                 $$debounce(function() {
6969                   scope.select(0, evt);
6970                 }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
6971               } else {
6972                 scope.select(0, evt);
6973               }
6974             }
6975
6976             if (showHint) {
6977               var firstLabel = scope.matches[0].label;
6978               if (angular.isString(inputValue) &&
6979                 inputValue.length > 0 &&
6980                 firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
6981                 hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
6982               } else {
6983                 hintInputElem.val('');
6984               }
6985             }
6986           } else {
6987             resetMatches();
6988             isNoResultsSetter(originalScope, true);
6989           }
6990         }
6991         if (onCurrentRequest) {
6992           isLoadingSetter(originalScope, false);
6993         }
6994       }, function() {
6995         resetMatches();
6996         isLoadingSetter(originalScope, false);
6997         isNoResultsSetter(originalScope, true);
6998       });
6999     };
7000
7001     // bind events only if appendToBody params exist - performance feature
7002     if (appendToBody) {
7003       angular.element($window).on('resize', fireRecalculating);
7004       $document.find('body').on('scroll', fireRecalculating);
7005     }
7006
7007     // Declare the debounced function outside recalculating for
7008     // proper debouncing
7009     var debouncedRecalculate = $$debounce(function() {
7010       // if popup is visible
7011       if (scope.matches.length) {
7012         recalculatePosition();
7013       }
7014
7015       scope.moveInProgress = false;
7016     }, eventDebounceTime);
7017
7018     // Default progress type
7019     scope.moveInProgress = false;
7020
7021     function fireRecalculating() {
7022       if (!scope.moveInProgress) {
7023         scope.moveInProgress = true;
7024         scope.$digest();
7025       }
7026
7027       debouncedRecalculate();
7028     }
7029
7030     // recalculate actual position and set new values to scope
7031     // after digest loop is popup in right position
7032     function recalculatePosition() {
7033       scope.position = appendToBody ? $position.offset(element) : $position.position(element);
7034       scope.position.top += element.prop('offsetHeight');
7035     }
7036
7037     //we need to propagate user's query so we can higlight matches
7038     scope.query = undefined;
7039
7040     //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
7041     var timeoutPromise;
7042
7043     var scheduleSearchWithTimeout = function(inputValue) {
7044       timeoutPromise = $timeout(function() {
7045         getMatchesAsync(inputValue);
7046       }, waitTime);
7047     };
7048
7049     var cancelPreviousTimeout = function() {
7050       if (timeoutPromise) {
7051         $timeout.cancel(timeoutPromise);
7052       }
7053     };
7054
7055     resetMatches();
7056
7057     scope.assignIsOpen = function (isOpen) {
7058       isOpenSetter(originalScope, isOpen);
7059     };
7060
7061     scope.select = function(activeIdx, evt) {
7062       //called from within the $digest() cycle
7063       var locals = {};
7064       var model, item;
7065
7066       selected = true;
7067       locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
7068       model = parserResult.modelMapper(originalScope, locals);
7069       $setModelValue(originalScope, model);
7070       modelCtrl.$setValidity('editable', true);
7071       modelCtrl.$setValidity('parse', true);
7072
7073       onSelectCallback(originalScope, {
7074         $item: item,
7075         $model: model,
7076         $label: parserResult.viewMapper(originalScope, locals),
7077         $event: evt
7078       });
7079
7080       resetMatches();
7081
7082       //return focus to the input element if a match was selected via a mouse click event
7083       // use timeout to avoid $rootScope:inprog error
7084       if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
7085         $timeout(function() { element[0].focus(); }, 0, false);
7086       }
7087     };
7088
7089     //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
7090     element.on('keydown', function(evt) {
7091       //typeahead is open and an "interesting" key was pressed
7092       if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
7093         return;
7094       }
7095
7096       var shouldSelect = isSelectEvent(originalScope, {$event: evt});
7097
7098       /**
7099        * if there's nothing selected (i.e. focusFirst) and enter or tab is hit
7100        * or
7101        * shift + tab is pressed to bring focus to the previous element
7102        * then clear the results
7103        */
7104       if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) {
7105         resetMatches();
7106         scope.$digest();
7107         return;
7108       }
7109
7110       evt.preventDefault();
7111       var target;
7112       switch (evt.which) {
7113         case 27: // escape
7114           evt.stopPropagation();
7115
7116           resetMatches();
7117           originalScope.$digest();
7118           break;
7119         case 38: // up arrow
7120           scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
7121           scope.$digest();
7122           target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx];
7123           target.parentNode.scrollTop = target.offsetTop;
7124           break;
7125         case 40: // down arrow
7126           scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
7127           scope.$digest();
7128           target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx];
7129           target.parentNode.scrollTop = target.offsetTop;
7130           break;
7131         default:
7132           if (shouldSelect) {
7133             scope.$apply(function() {
7134               if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
7135                 $$debounce(function() {
7136                   scope.select(scope.activeIdx, evt);
7137                 }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
7138               } else {
7139                 scope.select(scope.activeIdx, evt);
7140               }
7141             });
7142           }
7143       }
7144     });
7145
7146     element.on('focus', function (evt) {
7147       hasFocus = true;
7148       if (minLength === 0 && !modelCtrl.$viewValue) {
7149         $timeout(function() {
7150           getMatchesAsync(modelCtrl.$viewValue, evt);
7151         }, 0);
7152       }
7153     });
7154
7155     element.on('blur', function(evt) {
7156       if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
7157         selected = true;
7158         scope.$apply(function() {
7159           if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
7160             $$debounce(function() {
7161               scope.select(scope.activeIdx, evt);
7162             }, scope.debounceUpdate.blur);
7163           } else {
7164             scope.select(scope.activeIdx, evt);
7165           }
7166         });
7167       }
7168       if (!isEditable && modelCtrl.$error.editable) {
7169         modelCtrl.$setViewValue();
7170         scope.$apply(function() {
7171           // Reset validity as we are clearing
7172           modelCtrl.$setValidity('editable', true);
7173           modelCtrl.$setValidity('parse', true);
7174         });
7175         element.val('');
7176       }
7177       hasFocus = false;
7178       selected = false;
7179     });
7180
7181     // Keep reference to click handler to unbind it.
7182     var dismissClickHandler = function(evt) {
7183       // Issue #3973
7184       // Firefox treats right click as a click on document
7185       if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
7186         resetMatches();
7187         if (!$rootScope.$$phase) {
7188           originalScope.$digest();
7189         }
7190       }
7191     };
7192
7193     $document.on('click', dismissClickHandler);
7194
7195     originalScope.$on('$destroy', function() {
7196       $document.off('click', dismissClickHandler);
7197       if (appendToBody || appendTo) {
7198         $popup.remove();
7199       }
7200
7201       if (appendToBody) {
7202         angular.element($window).off('resize', fireRecalculating);
7203         $document.find('body').off('scroll', fireRecalculating);
7204       }
7205       // Prevent jQuery cache memory leak
7206       popUpEl.remove();
7207
7208       if (showHint) {
7209           inputsContainer.remove();
7210       }
7211     });
7212
7213     var $popup = $compile(popUpEl)(scope);
7214
7215     if (appendToBody) {
7216       $document.find('body').append($popup);
7217     } else if (appendTo) {
7218       angular.element(appendTo).eq(0).append($popup);
7219     } else {
7220       element.after($popup);
7221     }
7222
7223     this.init = function(_modelCtrl) {
7224       modelCtrl = _modelCtrl;
7225       ngModelOptions = extractOptions(modelCtrl);
7226
7227       scope.debounceUpdate = $parse(ngModelOptions.getOption('debounce'))(originalScope);
7228
7229       //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
7230       //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
7231       modelCtrl.$parsers.unshift(function(inputValue) {
7232         hasFocus = true;
7233
7234         if (minLength === 0 || inputValue && inputValue.length >= minLength) {
7235           if (waitTime > 0) {
7236             cancelPreviousTimeout();
7237             scheduleSearchWithTimeout(inputValue);
7238           } else {
7239             getMatchesAsync(inputValue);
7240           }
7241         } else {
7242           isLoadingSetter(originalScope, false);
7243           cancelPreviousTimeout();
7244           resetMatches();
7245         }
7246
7247         if (isEditable) {
7248           return inputValue;
7249         }
7250
7251         if (!inputValue) {
7252           // Reset in case user had typed something previously.
7253           modelCtrl.$setValidity('editable', true);
7254           return null;
7255         }
7256
7257         modelCtrl.$setValidity('editable', false);
7258         return undefined;
7259       });
7260
7261       modelCtrl.$formatters.push(function(modelValue) {
7262         var candidateViewValue, emptyViewValue;
7263         var locals = {};
7264
7265         // The validity may be set to false via $parsers (see above) if
7266         // the model is restricted to selected values. If the model
7267         // is set manually it is considered to be valid.
7268         if (!isEditable) {
7269           modelCtrl.$setValidity('editable', true);
7270         }
7271
7272         if (inputFormatter) {
7273           locals.$model = modelValue;
7274           return inputFormatter(originalScope, locals);
7275         }
7276
7277         //it might happen that we don't have enough info to properly render input value
7278         //we need to check for this situation and simply return model value if we can't apply custom formatting
7279         locals[parserResult.itemName] = modelValue;
7280         candidateViewValue = parserResult.viewMapper(originalScope, locals);
7281         locals[parserResult.itemName] = undefined;
7282         emptyViewValue = parserResult.viewMapper(originalScope, locals);
7283
7284         return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
7285       });
7286     };
7287
7288     function extractOptions(ngModelCtrl) {
7289       var ngModelOptions;
7290
7291       if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
7292         // guarantee a value
7293         ngModelOptions = ngModelCtrl.$options || {};
7294
7295         // mimic 1.6+ api
7296         ngModelOptions.getOption = function (key) {
7297           return ngModelOptions[key];
7298         };
7299       } else { // in angular >=1.6 $options is always present
7300         ngModelOptions = ngModelCtrl.$options;
7301       }
7302
7303       return ngModelOptions;
7304     }
7305   }])
7306
7307   .directive('uibTypeahead', function() {
7308     return {
7309       controller: 'UibTypeaheadController',
7310       require: ['ngModel', 'uibTypeahead'],
7311       link: function(originalScope, element, attrs, ctrls) {
7312         ctrls[1].init(ctrls[0]);
7313       }
7314     };
7315   })
7316
7317   .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) {
7318     return {
7319       scope: {
7320         matches: '=',
7321         query: '=',
7322         active: '=',
7323         position: '&',
7324         moveInProgress: '=',
7325         select: '&',
7326         assignIsOpen: '&',
7327         debounce: '&'
7328       },
7329       replace: true,
7330       templateUrl: function(element, attrs) {
7331         return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
7332       },
7333       link: function(scope, element, attrs) {
7334         scope.templateUrl = attrs.templateUrl;
7335
7336         scope.isOpen = function() {
7337           var isDropdownOpen = scope.matches.length > 0;
7338           scope.assignIsOpen({ isOpen: isDropdownOpen });
7339           return isDropdownOpen;
7340         };
7341
7342         scope.isActive = function(matchIdx) {
7343           return scope.active === matchIdx;
7344         };
7345
7346         scope.selectActive = function(matchIdx) {
7347           scope.active = matchIdx;
7348         };
7349
7350         scope.selectMatch = function(activeIdx, evt) {
7351           var debounce = scope.debounce();
7352           if (angular.isNumber(debounce) || angular.isObject(debounce)) {
7353             $$debounce(function() {
7354               scope.select({activeIdx: activeIdx, evt: evt});
7355             }, angular.isNumber(debounce) ? debounce : debounce['default']);
7356           } else {
7357             scope.select({activeIdx: activeIdx, evt: evt});
7358           }
7359         };
7360       }
7361     };
7362   }])
7363
7364   .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
7365     return {
7366       scope: {
7367         index: '=',
7368         match: '=',
7369         query: '='
7370       },
7371       link: function(scope, element, attrs) {
7372         var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
7373         $templateRequest(tplUrl).then(function(tplContent) {
7374           var tplEl = angular.element(tplContent.trim());
7375           element.replaceWith(tplEl);
7376           $compile(tplEl)(scope);
7377         });
7378       }
7379     };
7380   }])
7381
7382   .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
7383     var isSanitizePresent;
7384     isSanitizePresent = $injector.has('$sanitize');
7385
7386     function escapeRegexp(queryToEscape) {
7387       // Regex: capture the whole query string and replace it with the string that will be used to match
7388       // the results, for example if the capture is "a" the result will be \a
7389       return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
7390     }
7391
7392     function containsHtml(matchItem) {
7393       return /<.*>/g.test(matchItem);
7394     }
7395
7396     return function(matchItem, query) {
7397       if (!isSanitizePresent && containsHtml(matchItem)) {
7398         $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
7399       }
7400       matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
7401       if (!isSanitizePresent) {
7402         matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
7403       }
7404       return matchItem;
7405     };
7406   }]);
7407
7408 angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
7409   $templateCache.put("uib/template/accordion/accordion-group.html",
7410     "<div role=\"tab\" id=\"{{::headingId}}\" aria-selected=\"{{isOpen}}\" class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" +
7411     "  <h4 class=\"panel-title\">\n" +
7412     "    <a role=\"button\" data-toggle=\"collapse\" href aria-expanded=\"{{isOpen}}\" aria-controls=\"{{::panelId}}\" tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\" ng-disabled=\"isDisabled\" uib-tabindex-toggle><span uib-accordion-header ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
7413     "  </h4>\n" +
7414     "</div>\n" +
7415     "<div id=\"{{::panelId}}\" aria-labelledby=\"{{::headingId}}\" aria-hidden=\"{{!isOpen}}\" role=\"tabpanel\" class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" +
7416     "  <div class=\"panel-body\" ng-transclude></div>\n" +
7417     "</div>\n" +
7418     "");
7419 }]);
7420
7421 angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
7422   $templateCache.put("uib/template/accordion/accordion.html",
7423     "<div role=\"tablist\" class=\"panel-group\" ng-transclude></div>");
7424 }]);
7425
7426 angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
7427   $templateCache.put("uib/template/alert/alert.html",
7428     "<button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" +
7429     "  <span aria-hidden=\"true\">&times;</span>\n" +
7430     "  <span class=\"sr-only\">Close</span>\n" +
7431     "</button>\n" +
7432     "<div ng-transclude></div>\n" +
7433     "");
7434 }]);
7435
7436 angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
7437   $templateCache.put("uib/template/carousel/carousel.html",
7438     "<div class=\"carousel-inner\" ng-transclude></div>\n" +
7439     "<a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-class=\"{ disabled: isPrevDisabled() }\" ng-show=\"slides.length > 1\">\n" +
7440     "  <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" +
7441     "  <span class=\"sr-only\">previous</span>\n" +
7442     "</a>\n" +
7443     "<a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-class=\"{ disabled: isNextDisabled() }\" ng-show=\"slides.length > 1\">\n" +
7444     "  <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" +
7445     "  <span class=\"sr-only\">next</span>\n" +
7446     "</a>\n" +
7447     "<ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
7448     "  <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" +
7449     "    <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" +
7450     "  </li>\n" +
7451     "</ol>\n" +
7452     "");
7453 }]);
7454
7455 angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
7456   $templateCache.put("uib/template/carousel/slide.html",
7457     "<div class=\"text-center\" ng-transclude></div>\n" +
7458     "");
7459 }]);
7460
7461 angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
7462   $templateCache.put("uib/template/datepicker/datepicker.html",
7463     "<div ng-switch=\"datepickerMode\">\n" +
7464     "  <div uib-daypicker ng-switch-when=\"day\" tabindex=\"0\" class=\"uib-daypicker\"></div>\n" +
7465     "  <div uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\" class=\"uib-monthpicker\"></div>\n" +
7466     "  <div uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\" class=\"uib-yearpicker\"></div>\n" +
7467     "</div>\n" +
7468     "");
7469 }]);
7470
7471 angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
7472   $templateCache.put("uib/template/datepicker/day.html",
7473     "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
7474     "  <thead>\n" +
7475     "    <tr>\n" +
7476     "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></i><span class=\"sr-only\">previous</span></button></th>\n" +
7477     "      <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" +
7478     "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></i><span class=\"sr-only\">next</span></button></th>\n" +
7479     "    </tr>\n" +
7480     "    <tr>\n" +
7481     "      <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
7482     "      <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
7483     "    </tr>\n" +
7484     "  </thead>\n" +
7485     "  <tbody>\n" +
7486     "    <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\" role=\"row\">\n" +
7487     "      <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
7488     "      <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" +
7489     "        id=\"{{::dt.uid}}\"\n" +
7490     "        ng-class=\"::dt.customClass\">\n" +
7491     "        <button type=\"button\" class=\"btn btn-default btn-sm\"\n" +
7492     "          uib-is-class=\"\n" +
7493     "            'btn-info' for selectedDt,\n" +
7494     "            'active' for activeDt\n" +
7495     "            on dt\"\n" +
7496     "          ng-click=\"select(dt.date)\"\n" +
7497     "          ng-disabled=\"::dt.disabled\"\n" +
7498     "          tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
7499     "      </td>\n" +
7500     "    </tr>\n" +
7501     "  </tbody>\n" +
7502     "</table>\n" +
7503     "");
7504 }]);
7505
7506 angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
7507   $templateCache.put("uib/template/datepicker/month.html",
7508     "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
7509     "  <thead>\n" +
7510     "    <tr>\n" +
7511     "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></i><span class=\"sr-only\">previous</span></button></th>\n" +
7512     "      <th colspan=\"{{::yearHeaderColspan}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" +
7513     "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></i><span class=\"sr-only\">next</span></i></button></th>\n" +
7514     "    </tr>\n" +
7515     "  </thead>\n" +
7516     "  <tbody>\n" +
7517     "    <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\" role=\"row\">\n" +
7518     "      <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" +
7519     "        id=\"{{::dt.uid}}\"\n" +
7520     "        ng-class=\"::dt.customClass\">\n" +
7521     "        <button type=\"button\" class=\"btn btn-default\"\n" +
7522     "          uib-is-class=\"\n" +
7523     "            'btn-info' for selectedDt,\n" +
7524     "            'active' for activeDt\n" +
7525     "            on dt\"\n" +
7526     "          ng-click=\"select(dt.date)\"\n" +
7527     "          ng-disabled=\"::dt.disabled\"\n" +
7528     "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
7529     "      </td>\n" +
7530     "    </tr>\n" +
7531     "  </tbody>\n" +
7532     "</table>\n" +
7533     "");
7534 }]);
7535
7536 angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
7537   $templateCache.put("uib/template/datepicker/year.html",
7538     "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
7539     "  <thead>\n" +
7540     "    <tr>\n" +
7541     "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></i><span class=\"sr-only\">previous</span></button></th>\n" +
7542     "      <th colspan=\"{{::columns - 2}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" +
7543     "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></i><span class=\"sr-only\">next</span></button></th>\n" +
7544     "    </tr>\n" +
7545     "  </thead>\n" +
7546     "  <tbody>\n" +
7547     "    <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\" role=\"row\">\n" +
7548     "      <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" +
7549     "        id=\"{{::dt.uid}}\"\n" +
7550     "        ng-class=\"::dt.customClass\">\n" +
7551     "        <button type=\"button\" class=\"btn btn-default\"\n" +
7552     "          uib-is-class=\"\n" +
7553     "            'btn-info' for selectedDt,\n" +
7554     "            'active' for activeDt\n" +
7555     "            on dt\"\n" +
7556     "          ng-click=\"select(dt.date)\"\n" +
7557     "          ng-disabled=\"::dt.disabled\"\n" +
7558     "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
7559     "      </td>\n" +
7560     "    </tr>\n" +
7561     "  </tbody>\n" +
7562     "</table>\n" +
7563     "");
7564 }]);
7565
7566 angular.module("uib/template/datepickerPopup/popup.html", []).run(["$templateCache", function($templateCache) {
7567   $templateCache.put("uib/template/datepickerPopup/popup.html",
7568     "<ul role=\"presentation\" class=\"uib-datepicker-popup dropdown-menu uib-position-measure\" dropdown-nested ng-if=\"isOpen\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" +
7569     "  <li ng-transclude></li>\n" +
7570     "  <li ng-if=\"showButtonBar\" class=\"uib-button-bar\">\n" +
7571     "    <span class=\"btn-group pull-left\">\n" +
7572     "      <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today', $event)\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
7573     "      <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null, $event)\">{{ getText('clear') }}</button>\n" +
7574     "    </span>\n" +
7575     "    <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close($event)\">{{ getText('close') }}</button>\n" +
7576     "  </li>\n" +
7577     "</ul>\n" +
7578     "");
7579 }]);
7580
7581 angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) {
7582   $templateCache.put("uib/template/modal/window.html",
7583     "<div class=\"modal-dialog {{size ? 'modal-' + size : ''}}\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" +
7584     "");
7585 }]);
7586
7587 angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) {
7588   $templateCache.put("uib/template/pager/pager.html",
7589     "<li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\" ng-disabled=\"noPrevious()||ngDisabled\" uib-tabindex-toggle>{{::getText('previous')}}</a></li>\n" +
7590     "<li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\" ng-disabled=\"noNext()||ngDisabled\" uib-tabindex-toggle>{{::getText('next')}}</a></li>\n" +
7591     "");
7592 }]);
7593
7594 angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
7595   $templateCache.put("uib/template/pagination/pagination.html",
7596     "<li role=\"menuitem\" ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\" ng-disabled=\"noPrevious()||ngDisabled\" uib-tabindex-toggle>{{::getText('first')}}</a></li>\n" +
7597     "<li role=\"menuitem\" ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\" ng-disabled=\"noPrevious()||ngDisabled\" uib-tabindex-toggle>{{::getText('previous')}}</a></li>\n" +
7598     "<li role=\"menuitem\" ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\" ng-disabled=\"ngDisabled&&!page.active\" uib-tabindex-toggle>{{page.text}}</a></li>\n" +
7599     "<li role=\"menuitem\" ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\" ng-disabled=\"noNext()||ngDisabled\" uib-tabindex-toggle>{{::getText('next')}}</a></li>\n" +
7600     "<li role=\"menuitem\" ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\" ng-disabled=\"noNext()||ngDisabled\" uib-tabindex-toggle>{{::getText('last')}}</a></li>\n" +
7601     "");
7602 }]);
7603
7604 angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
7605   $templateCache.put("uib/template/tooltip/tooltip-html-popup.html",
7606     "<div class=\"tooltip-arrow\"></div>\n" +
7607     "<div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
7608     "");
7609 }]);
7610
7611 angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
7612   $templateCache.put("uib/template/tooltip/tooltip-popup.html",
7613     "<div class=\"tooltip-arrow\"></div>\n" +
7614     "<div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
7615     "");
7616 }]);
7617
7618 angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) {
7619   $templateCache.put("uib/template/tooltip/tooltip-template-popup.html",
7620     "<div class=\"tooltip-arrow\"></div>\n" +
7621     "<div class=\"tooltip-inner\"\n" +
7622     "  uib-tooltip-template-transclude=\"contentExp()\"\n" +
7623     "  tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
7624     "");
7625 }]);
7626
7627 angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
7628   $templateCache.put("uib/template/popover/popover-html.html",
7629     "<div class=\"arrow\"></div>\n" +
7630     "\n" +
7631     "<div class=\"popover-inner\">\n" +
7632     "    <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" +
7633     "    <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" +
7634     "</div>\n" +
7635     "");
7636 }]);
7637
7638 angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
7639   $templateCache.put("uib/template/popover/popover-template.html",
7640     "<div class=\"arrow\"></div>\n" +
7641     "\n" +
7642     "<div class=\"popover-inner\">\n" +
7643     "    <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" +
7644     "    <div class=\"popover-content\"\n" +
7645     "      uib-tooltip-template-transclude=\"contentExp()\"\n" +
7646     "      tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
7647     "</div>\n" +
7648     "");
7649 }]);
7650
7651 angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
7652   $templateCache.put("uib/template/popover/popover.html",
7653     "<div class=\"arrow\"></div>\n" +
7654     "\n" +
7655     "<div class=\"popover-inner\">\n" +
7656     "    <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" +
7657     "    <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
7658     "</div>\n" +
7659     "");
7660 }]);
7661
7662 angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
7663   $templateCache.put("uib/template/progressbar/bar.html",
7664     "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
7665     "");
7666 }]);
7667
7668 angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
7669   $templateCache.put("uib/template/progressbar/progress.html",
7670     "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>");
7671 }]);
7672
7673 angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
7674   $templateCache.put("uib/template/progressbar/progressbar.html",
7675     "<div class=\"progress\">\n" +
7676     "  <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
7677     "</div>\n" +
7678     "");
7679 }]);
7680
7681 angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
7682   $templateCache.put("uib/template/rating/rating.html",
7683     "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\" aria-valuetext=\"{{title}}\">\n" +
7684     "    <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
7685     "    <i ng-repeat-end ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\" ng-attr-title=\"{{r.title}}\"></i>\n" +
7686     "</span>\n" +
7687     "");
7688 }]);
7689
7690 angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
7691   $templateCache.put("uib/template/tabs/tab.html",
7692     "<li ng-class=\"[{active: active, disabled: disabled}, classes]\" class=\"uib-tab nav-item\">\n" +
7693     "  <a href ng-click=\"select($event)\" class=\"nav-link\" uib-tab-heading-transclude>{{heading}}</a>\n" +
7694     "</li>\n" +
7695     "");
7696 }]);
7697
7698 angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
7699   $templateCache.put("uib/template/tabs/tabset.html",
7700     "<div>\n" +
7701     "  <ul class=\"nav nav-{{tabset.type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
7702     "  <div class=\"tab-content\">\n" +
7703     "    <div class=\"tab-pane\"\n" +
7704     "         ng-repeat=\"tab in tabset.tabs\"\n" +
7705     "         ng-class=\"{active: tabset.active === tab.index}\"\n" +
7706     "         uib-tab-content-transclude=\"tab\">\n" +
7707     "    </div>\n" +
7708     "  </div>\n" +
7709     "</div>\n" +
7710     "");
7711 }]);
7712
7713 angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
7714   $templateCache.put("uib/template/timepicker/timepicker.html",
7715     "<table class=\"uib-timepicker\">\n" +
7716     "  <tbody>\n" +
7717     "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
7718     "      <td class=\"uib-increment hours\"><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementHours()\" tabindex=\"-1\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
7719     "      <td>&nbsp;</td>\n" +
7720     "      <td class=\"uib-increment minutes\"><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementMinutes()\" tabindex=\"-1\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
7721     "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
7722     "      <td ng-show=\"showSeconds\" class=\"uib-increment seconds\"><a ng-click=\"incrementSeconds()\" ng-class=\"{disabled: noIncrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementSeconds()\" tabindex=\"-1\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
7723     "      <td ng-show=\"showMeridian\"></td>\n" +
7724     "    </tr>\n" +
7725     "    <tr>\n" +
7726     "      <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" +
7727     "        <input type=\"text\" placeholder=\"HH\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementHours()\" ng-blur=\"blur()\">\n" +
7728     "      </td>\n" +
7729     "      <td class=\"uib-separator\">:</td>\n" +
7730     "      <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
7731     "        <input type=\"text\" placeholder=\"MM\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementMinutes()\" ng-blur=\"blur()\">\n" +
7732     "      </td>\n" +
7733     "      <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" +
7734     "      <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" +
7735     "        <input type=\"text\" placeholder=\"SS\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementSeconds()\" ng-blur=\"blur()\">\n" +
7736     "      </td>\n" +
7737     "      <td ng-show=\"showMeridian\" class=\"uib-time am-pm\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" +
7738     "    </tr>\n" +
7739     "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
7740     "      <td class=\"uib-decrement hours\"><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementHours()\" tabindex=\"-1\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
7741     "      <td>&nbsp;</td>\n" +
7742     "      <td class=\"uib-decrement minutes\"><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementMinutes()\" tabindex=\"-1\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
7743     "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
7744     "      <td ng-show=\"showSeconds\" class=\"uib-decrement seconds\"><a ng-click=\"decrementSeconds()\" ng-class=\"{disabled: noDecrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementSeconds()\" tabindex=\"-1\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
7745     "      <td ng-show=\"showMeridian\"></td>\n" +
7746     "    </tr>\n" +
7747     "  </tbody>\n" +
7748     "</table>\n" +
7749     "");
7750 }]);
7751
7752 angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
7753   $templateCache.put("uib/template/typeahead/typeahead-match.html",
7754     "<a href\n" +
7755     "   tabindex=\"-1\"\n" +
7756     "   ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"\n" +
7757     "   ng-attr-title=\"{{match.label}}\"></a>\n" +
7758     "");
7759 }]);
7760
7761 angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
7762   $templateCache.put("uib/template/typeahead/typeahead-popup.html",
7763     "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
7764     "    <li class=\"uib-typeahead-match\" ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">\n" +
7765     "        <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
7766     "    </li>\n" +
7767     "</ul>\n" +
7768     "");
7769 }]);
7770 angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibCarouselCss && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>'); angular.$$uibCarouselCss = true; });
7771 angular.module('ui.bootstrap.datepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-datepicker .uib-title{width:100%;}.uib-day button,.uib-month button,.uib-year button{min-width:100%;}.uib-left,.uib-right{width:100%}</style>'); angular.$$uibDatepickerCss = true; });
7772 angular.module('ui.bootstrap.position').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibPositionCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-position-measure{display:block !important;visibility:hidden !important;position:absolute !important;top:-9999px !important;left:-9999px !important;}.uib-position-scrollbar-measure{position:absolute !important;top:-9999px !important;width:50px !important;height:50px !important;overflow:scroll !important;}.uib-position-body-scrollbar-measure{overflow:scroll !important;}</style>'); angular.$$uibPositionCss = true; });
7773 angular.module('ui.bootstrap.datepickerPopup').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerpopupCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-datepicker-popup.dropdown-menu{display:block;float:none;margin:0;}.uib-button-bar{padding:10px 9px 2px;}</style>'); angular.$$uibDatepickerpopupCss = true; });
7774 angular.module('ui.bootstrap.tooltip').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTooltipCss && angular.element(document).find('head').prepend('<style type="text/css">[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow,[uib-popover-popup].popover.top-left > .arrow,[uib-popover-popup].popover.top-right > .arrow,[uib-popover-popup].popover.bottom-left > .arrow,[uib-popover-popup].popover.bottom-right > .arrow,[uib-popover-popup].popover.left-top > .arrow,[uib-popover-popup].popover.left-bottom > .arrow,[uib-popover-popup].popover.right-top > .arrow,[uib-popover-popup].popover.right-bottom > .arrow,[uib-popover-html-popup].popover.top-left > .arrow,[uib-popover-html-popup].popover.top-right > .arrow,[uib-popover-html-popup].popover.bottom-left > .arrow,[uib-popover-html-popup].popover.bottom-right > .arrow,[uib-popover-html-popup].popover.left-top > .arrow,[uib-popover-html-popup].popover.left-bottom > .arrow,[uib-popover-html-popup].popover.right-top > .arrow,[uib-popover-html-popup].popover.right-bottom > .arrow,[uib-popover-template-popup].popover.top-left > .arrow,[uib-popover-template-popup].popover.top-right > .arrow,[uib-popover-template-popup].popover.bottom-left > .arrow,[uib-popover-template-popup].popover.bottom-right > .arrow,[uib-popover-template-popup].popover.left-top > .arrow,[uib-popover-template-popup].popover.left-bottom > .arrow,[uib-popover-template-popup].popover.right-top > .arrow,[uib-popover-template-popup].popover.right-bottom > .arrow{top:auto;bottom:auto;left:auto;right:auto;margin:0;}[uib-popover-popup].popover,[uib-popover-html-popup].popover,[uib-popover-template-popup].popover{display:block !important;}</style>'); angular.$$uibTooltipCss = true; });
7775 angular.module('ui.bootstrap.timepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTimepickerCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-time input{width:50px;}</style>'); angular.$$uibTimepickerCss = true; });
7776 angular.module('ui.bootstrap.typeahead').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTypeaheadCss && angular.element(document).find('head').prepend('<style type="text/css">[uib-typeahead-popup].dropdown-menu{display:block;}</style>'); angular.$$uibTypeaheadCss = true; });