PageSourceSearch

https://cloudexchange-th.com/bower_components/angular-bootstrap/ui-bootstrap-tpls.js

js cloudexchange-th.com collected 2026-10-07 21:53:47 UTC 260,877 bytes, 7,347 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.