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