PageSourceSearch

https://samarthinstitute.edu.in/web-assets/angular/cms-ui-bootstrap-tpls-1.0.3.js?version=1772287900

js samarthinstitute.edu.in collected 2026-10-09 23:59:00 UTC 238,423 bytes, 6,825 lines download raw bytes

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

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.