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