PageSourceSearch

https://portaleducacional.unibrasil.com.br/FrameHTML/web/js/libs/angular-bootstrap/ui-bootstrap-tpls.js

js unibrasil.com.br collected 2026-09-24 09:18:28 UTC 197,657 bytes, 5,836 lines download raw bytes

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