PageSourceSearch

https://www.dpdparcelwizard.ie/DesktopModules/Inventise.DPD/Scripts/ui-bootstrap-tpls-0.11.0.js?cdv=177

js dpdparcelwizard.ie collected 2026-10-02 15:37:22 UTC 139,270 bytes, 4,116 lines download raw bytes

1/*
2 * angular-ui-bootstrap
3 * http://angular-ui.github.io/bootstrap/
4
5 * Version: 0.11.0 - 2014-05-01
6 * License: MIT
7 */
8angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","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.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-unsafe-popup.html","template/tooltip/tooltip-popup.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.transition', [])
11
12/**
13 * $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
14 * @param  {DOMElement} element  The DOMElement that will be animated.
15 * @param  {string|object|function} trigger  The thing that will cause the transition to start:
16 *   - As a string, it represents the css class to be added to the element.
17 *   - As an object, it represents a hash of style attributes to be applied to the element.
18 *   - As a function, it represents a function to be called that will cause the transition to occur.
19 * @return {Promise}  A promise that is resolved when the transition finishes.
20 */
21.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
22
23  var $transition = function(element, trigger, options) {
24    options = options || {};
25    var deferred = $q.defer();
26    var endEventName = $transition[options.animation ? 'animationEndEventName' : 'transitionEndEventName'];
27
28    var transitionEndHandler = function(event) {
29      $rootScope.$apply(function() {
30        element.unbind(endEventName, transitionEndHandler);
31        deferred.resolve(element);
32      });
33    };
34
35    if (endEventName) {
36      element.bind(endEventName, transitionEndHandler);
37    }
38
39    // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
40    $timeout(function() {
41      if ( angular.isString(trigger) ) {
42        element.addClass(trigger);
43      } else if ( angular.isFunction(trigger) ) {
44        trigger(element);
45      } else if ( angular.isObject(trigger) ) {
46        element.css(trigger);
47      }
48      //If browser does not support transitions, instantly resolve
49      if ( !endEventName ) {
50        deferred.resolve(element);
51      }
52    });
53
54    // Add our custom cancel function to the promise that is returned
55    // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
56    // i.e. it will therefore never raise a transitionEnd event for that transition
57    deferred.promise.cancel = function() {
58      if ( endEventName ) {
59        element.unbind(endEventName, transitionEndHandler);
60      }
61      deferred.reject('Transition cancelled');
62    };
63
64    return deferred.promise;
65  };
66
67  // Work out the name of the transitionEnd event
68  var transElement = document.createElement('trans');
69  var transitionEndEventNames = {
70    'WebkitTransition': 'webkitTransitionEnd',
71    'MozTransition': 'transitionend',
72    'OTransition': 'oTransitionEnd',
73    'transition': 'transitionend'
74  };
75  var animationEndEventNames = {
76    'WebkitTransition': 'webkitAnimationEnd',
77    'MozTransition': 'animationend',
78    'OTransition': 'oAnimationEnd',
79    'transition': 'animationend'
80  };
81  function findEndEventName(endEventNames) {
82    for (var name in endEventNames){
83      if (transElement.style[name] !== undefined) {
84        return endEventNames[name];
85      }
86    }
87  }
88  $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
89  $transition.animationEndEventName = findEndEventName(animationEndEventNames);
90  return $transition;
91}]);
92
93angular.module('ui.bootstrap.collapse', ['ui.bootstrap.transition'])
94
95  .directive('collapse', ['$transition', function ($transition) {
96
97    return {
98      link: function (scope, element, attrs) {
99
100        var initialAnimSkip = true;
101        var currentTransition;
102
103        function doTransition(change) {
104          var newTransition = $transition(element, change);
105          if (currentTransition) {
106            currentTransition.cancel();
107          }
108          currentTransition = newTransition;
109          newTransition.then(newTransitionDone, newTransitionDone);
110          return newTransition;
111
112          function newTransitionDone() {
113            // Make sure it's this transition, otherwise, leave it alone.
114            if (currentTransition === newTransition) {
115              currentTransition = undefined;
116            }
117          }
118        }
119
120        function expand() {
121          if (initialAnimSkip) {
122            initialAnimSkip = false;
123            expandDone();
124          } else {
125            element.removeClass('collapse').addClass('collapsing');
126            doTransition({ height: element[0].scrollHeight + 'px' }).then(expandDone);
127          }
128        }
129
130        function expandDone() {
131          element.removeClass('collapsing');
132          element.addClass('collapse in');
133          element.css({height: 'auto'});
134        }
135
136        function collapse() {
137          if (initialAnimSkip) {
138            initialAnimSkip = false;
139            collapseDone();
140            element.css({height: 0});
141          } else {
142            // CSS transitions don't work with height: auto, so we have to manually change the height to a specific value
143            element.css({ height: element[0].scrollHeight + 'px' });
144            //trigger reflow so a browser realizes that height was updated from auto to a specific value
145            var x = element[0].offsetWidth;
146
147            element.removeClass('collapse in').addClass('collapsing');
148
149            doTransition({ height: 0 }).then(collapseDone);
150          }
151        }
152
153        function collapseDone() {
154          element.removeClass('collapsing');
155          element.addClass('collapse');
156        }
157
158        scope.$watch(attrs.collapse, function (shouldCollapse) {
159          if (shouldCollapse) {
160            collapse();
161          } else {
162            expand();
163          }
164        });
165      }
166    };
167  }]);
168
169angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
170
171.constant('accordionConfig', {
172  closeOthers: true
173})
174
175.controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
176
177  // This array keeps track of the accordion groups
178  this.groups = [];
179
180  // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
181  this.closeOthers = function(openGroup) {
182    var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
183    if ( closeOthers ) {
184      angular.forEach(this.groups, function (group) {
185        if ( group !== openGroup ) {
186          group.isOpen = false;
187        }
188      });
189    }
190  };
191
192  // This is called from the accordion-group directive to add itself to the accordion
193  this.addGroup = function(groupScope) {
194    var that = this;
195    this.groups.push(groupScope);
196
197    groupScope.$on('$destroy', function (event) {
198      that.removeGroup(groupScope);
199    });
200  };
201
202  // This is called from the accordion-group directive when to remove itself
203  this.removeGroup = function(group) {
204    var index = this.groups.indexOf(group);
205    if ( index !== -1 ) {
206      this.groups.splice(index, 1);
207    }
208  };
209
210}])
211
212// The accordion directive simply sets up the directive controller
213// and adds an accordion CSS class to itself element.
214.directive('accordion', function () {
215  return {
216    restrict:'EA',
217    controller:'AccordionController',
218    transclude: true,
219    replace: false,
220    templateUrl: 'template/accordion/accordion.html'
221  };
222})
223
224// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
225.directive('accordionGroup', function() {
226  return {
227    require:'^accordion',         // We need this directive to be inside an accordion
228    restrict:'EA',
229    transclude:true,              // It transcludes the contents of the directive into the template
230    replace: true,                // The element containing the directive will be replaced with the template
231    templateUrl:'template/accordion/accordion-group.html',
232    scope: {
233      heading: '@',               // Interpolate the heading attribute onto this scope
234      isOpen: '=?',
235      isDisabled: '=?'
236    },
237    controller: function() {
238      this.setHeading = function(element) {
239        this.heading = element;
240      };
241    },
242    link: function(scope, element, attrs, accordionCtrl) {
243      accordionCtrl.addGroup(scope);
244
245      scope.$watch('isOpen', function(value) {
246        if ( value ) {
247          accordionCtrl.closeOthers(scope);
248        }
249      });
250
251      scope.toggleOpen = function() {
252        if ( !scope.isDisabled ) {
253          scope.isOpen = !scope.isOpen;
254        }
255      };
256    }
257  };
258})
259
260// Use accordion-heading below an accordion-group to provide a heading containing HTML
261// <accordion-group>
262//   <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
263// </accordion-group>
264.directive('accordionHeading', function() {
265  return {
266    restrict: 'EA',
267    transclude: true,   // Grab the contents to be used as the heading
268    template: '',       // In effect remove this element!
269    replace: true,
270    require: '^accordionGroup',
271    link: function(scope, element, attr, accordionGroupCtrl, transclude) {
272      // Pass the heading to the accordion-group controller
273      // so that it can be transcluded into the right place in the template
274      // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
275      accordionGroupCtrl.setHeading(transclude(scope, function() {}));
276    }
277  };
278})
279
280// Use in the accordion-group template to indicate where you want the heading to be transcluded
281// You must provide the property on the accordion-group controller that will hold the transcluded element
282// <div class="accordion-group">
283//   <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
284//   ...
285// </div>
286.directive('accordionTransclude', function() {
287  return {
288    require: '^accordionGroup',
289    link: function(scope, element, attr, controller) {
290      scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
291        if ( heading ) {
292          element.html('');
293          element.append(heading);
294        }
295      });
296    }
297  };
298});
299
300angular.module('ui.bootstrap.alert', [])
301
302.controller('AlertController', ['$scope', '$attrs', function ($scope, $attrs) {
303  $scope.closeable = 'close' in $attrs;
304}])
305
306.directive('alert', function () {
307  return {
308    restrict:'EA',
309    controller:'AlertController',
310    templateUrl:'template/alert/alert.html',
311    transclude:true,
312    replace:true,
313    scope: {
314      type: '@',
315      close: '&'
316    }
317  };
318});
319
320angular.module('ui.bootstrap.bindHtml', [])
321
322  .directive('bindHtmlUnsafe', function () {
323    return function (scope, element, attr) {
324      element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
325      scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
326        element.html(value || '');
327      });
328    };
329  });
330angular.module('ui.bootstrap.buttons', [])
331
332.constant('buttonConfig', {
333  activeClass: 'active',
334  toggleEvent: 'click'
335})
336
337.controller('ButtonsController', ['buttonConfig', function(buttonConfig) {
338  this.activeClass = buttonConfig.activeClass || 'active';
339  this.toggleEvent = buttonConfig.toggleEvent || 'click';
340}])
341
342.directive('btnRadio', function () {
343  return {
344    require: ['btnRadio', 'ngModel'],
345    controller: 'ButtonsController',
346    link: function (scope, element, attrs, ctrls) {
347      var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
348
349      //model -> UI
350      ngModelCtrl.$render = function () {
351        element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
352      };
353
354      //ui->model
355      element.bind(buttonsCtrl.toggleEvent, function () {
356        var isActive = element.hasClass(buttonsCtrl.activeClass);
357
358        if (!isActive || angular.isDefined(attrs.uncheckable)) {
359          scope.$apply(function () {
360            ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.btnRadio));
361            ngModelCtrl.$render();
362          });
363        }
364      });
365    }
366  };
367})
368
369.directive('btnCheckbox', function () {
370  return {
371    require: ['btnCheckbox', 'ngModel'],
372    controller: 'ButtonsController',
373    link: function (scope, element, attrs, ctrls) {
374      var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
375
376      function getTrueValue() {
377        return getCheckboxValue(attrs.btnCheckboxTrue, true);
378      }
379
380      function getFalseValue() {
381        return getCheckboxValue(attrs.btnCheckboxFalse, false);
382      }
383
384      function getCheckboxValue(attributeValue, defaultValue) {
385        var val = scope.$eval(attributeValue);
386        return angular.isDefined(val) ? val : defaultValue;
387      }
388
389      //model -> UI
390      ngModelCtrl.$render = function () {
391        element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
392      };
393
394      //ui->model
395      element.bind(buttonsCtrl.toggleEvent, function () {
396        scope.$apply(function () {
397          ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
398          ngModelCtrl.$render();
399        });
400      });
401    }
402  };
403});
404
405/**
406* @ngdoc overview
407* @name ui.bootstrap.carousel
408*
409* @description
410* AngularJS version of an image carousel.
411*
412*/
413angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
414.controller('CarouselController', ['$scope', '$timeout', '$transition', function ($scope, $timeout, $transition) {
415  var self = this,
416    slides = self.slides = $scope.slides = [],
417    currentIndex = -1,
418    currentTimeout, isPlaying;
419  self.currentSlide = null;
420
421  var destroyed = false;
422  /* direction: "prev" or "next" */
423  self.select = $scope.select = function(nextSlide, direction) {
424    var nextIndex = slides.indexOf(nextSlide);
425    //Decide direction if it's not given
426    if (direction === undefined) {
427      direction = nextIndex > currentIndex ? 'next' : 'prev';
428    }
429    if (nextSlide && nextSlide !== self.currentSlide) {
430      if ($scope.$currentTransition) {
431        $scope.$currentTransition.cancel();
432        //Timeout so ng-class in template has time to fix classes for finished slide
433        $timeout(goNext);
434      } else {
435        goNext();
436      }
437    }
438    function goNext() {
439      // Scope has been destroyed, stop here.
440      if (destroyed) { return; }
441      //If we have a slide to transition from and we have a transition type and we're allowed, go
442      if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
443        //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
444        nextSlide.$element.addClass(direction);
445        var reflow = nextSlide.$element[0].offsetWidth; //force reflow
446
447        //Set all other slides to stop doing their stuff for the new transition
448        angular.forEach(slides, function(slide) {
449          angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});
450        });
451        angular.extend(nextSlide, {direction: direction, active: true, entering: true});
452        angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});
453
454        $scope.$currentTransition = $transition(nextSlide.$element, {});
455        //We have to create new pointers inside a closure since next & current will change
456        (function(next,current) {
457          $scope.$currentTransition.then(
458            function(){ transitionDone(next, current); },
459            function(){ transitionDone(next, current); }
460          );
461        }(nextSlide, self.currentSlide));
462      } else {
463        transitionDone(nextSlide, self.currentSlide);
464      }
465      self.currentSlide = nextSlide;
466      currentIndex = nextIndex;
467      //every time you change slides, reset the timer
468      restartTimer();
469    }
470    function transitionDone(next, current) {
471      angular.extend(next, {direction: '', active: true, leaving: false, entering: false});
472      angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});
473      $scope.$currentTransition = null;
474    }
475  };
476  $scope.$on('$destroy', function () {
477    destroyed = true;
478  });
479
480  /* Allow outside people to call indexOf on slides array */
481  self.indexOfSlide = function(slide) {
482    return slides.indexOf(slide);
483  };
484
485  $scope.next = function() {
486    var newIndex = (currentIndex + 1) % slides.length;
487
488    //Prevent this user-triggered transition from occurring if there is already one in progress
489    if (!$scope.$currentTransition) {
490      return self.select(slides[newIndex], 'next');
491    }
492  };
493
494  $scope.prev = function() {
495    var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
496
497    //Prevent this user-triggered transition from occurring if there is already one in progress
498    if (!$scope.$currentTransition) {
499      return self.select(slides[newIndex], 'prev');
500    }
501  };
502
503  $scope.isActive = function(slide) {
504     return self.currentSlide === slide;
505  };
506
507  $scope.$watch('interval', restartTimer);
508  $scope.$on('$destroy', resetTimer);
509
510  function restartTimer() {
511    resetTimer();
512    var interval = +$scope.interval;
513    if (!isNaN(interval) && interval>=0) {
514      currentTimeout = $timeout(timerFn, interval);
515    }
516  }
517
518  function resetTimer() {
519    if (currentTimeout) {
520      $timeout.cancel(currentTimeout);
521      currentTimeout = null;
522    }
523  }
524
525  function timerFn() {
526    if (isPlaying) {
527      $scope.next();
528      restartTimer();
529    } else {
530      $scope.pause();
531    }
532  }
533
534  $scope.play = function() {
535    if (!isPlaying) {
536      isPlaying = true;
537      restartTimer();
538    }
539  };
540  $scope.pause = function() {
541    if (!$scope.noPause) {
542      isPlaying = false;
543      resetTimer();
544    }
545  };
546
547  self.addSlide = function(slide, element) {
548    slide.$element = element;
549    slides.push(slide);
550    //if this is the first slide or the slide is set to active, select it
551    if(slides.length === 1 || slide.active) {
552      self.select(slides[slides.length-1]);
553      if (slides.length == 1) {
554        $scope.play();
555      }
556    } else {
557      slide.active = false;
558    }
559  };
560
561  self.removeSlide = function(slide) {
562    //get the index of the slide inside the carousel
563    var index = slides.indexOf(slide);
564    slides.splice(index, 1);
565    if (slides.length > 0 && slide.active) {
566      if (index >= slides.length) {
567        self.select(slides[index-1]);
568      } else {
569        self.select(slides[index]);
570      }
571    } else if (currentIndex > index) {
572      currentIndex--;
573    }
574  };
575
576}])
577
578/**
579 * @ngdoc directive
580 * @name ui.bootstrap.carousel.directive:carousel
581 * @restrict EA
582 *
583 * @description
584 * Carousel is the outer container for a set of image 'slides' to showcase.
585 *
586 * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide.
587 * @param {boolean=} noTransition Whether to disable transitions on the carousel.
588 * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover).
589 *
590 * @example
591<example module="ui.bootstrap">
592  <file name="index.html">
593    <carousel>
594      <slide>
595        <img src="http://placekitten.com/150/150" style="margin:auto;">
596        <div class="carousel-caption">
597          <p>Beautiful!</p>
598        </div>
599      </slide>
600      <slide>
601        <img src="http://placekitten.com/100/150" style="margin:auto;">
602        <div class="carousel-caption">
603          <p>D'aww!</p>
604        </div>
605      </slide>
606    </carousel>
607  </file>
608  <file name="demo.css">
609    .carousel-indicators {
610      top: auto;
611      bottom: 15px;
612    }
613  </file>
614</example>
615 */
616.directive('carousel', [function() {
617  return {
618    restrict: 'EA',
619    transclude: true,
620    replace: true,
621    controller: 'CarouselController',
622    require: 'carousel',
623    templateUrl: 'template/carousel/carousel.html',
624    scope: {
625      interval: '=',
626      noTransition: '=',
627      noPause: '='
628    }
629  };
630}])
631
632/**
633 * @ngdoc directive
634 * @name ui.bootstrap.carousel.directive:slide
635 * @restrict EA
636 *
637 * @description
638 * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}.  Must be placed as a child of a carousel element.
639 *
640 * @param {boolean=} active Model binding, whether or not this slide is currently active.
641 *
642 * @example
643<example module="ui.bootstrap">
644  <file name="index.html">
645<div ng-controller="CarouselDemoCtrl">
646  <carousel>
647    <slide ng-repeat="slide in slides" active="slide.active">
648      <img ng-src="{{slide.image}}" style="margin:auto;">
649      <div class="carousel-caption">
650        <h4>Slide {{$index}}</h4>
651        <p>{{slide.text}}</p>
652      </div>
653    </slide>
654  </carousel>
655  Interval, in milliseconds: <input type="number" ng-model="myInterval">
656  <br />Enter a negative number to stop the interval.
657</div>
658  </file>
659  <file name="script.js">
660function CarouselDemoCtrl($scope) {
661  $scope.myInterval = 5000;
662}
663  </file>
664  <file name="demo.css">
665    .carousel-indicators {
666      top: auto;
667      bottom: 15px;
668    }
669  </file>
670</example>
671*/
672
673.directive('slide', function() {
674  return {
675    require: '^carousel',
676    restrict: 'EA',
677    transclude: true,
678    replace: true,
679    templateUrl: 'template/carousel/slide.html',
680    scope: {
681      active: '=?'
682    },
683    link: function (scope, element, attrs, carouselCtrl) {
684      carouselCtrl.addSlide(scope, element);
685      //when the scope is destroyed then remove the slide from the current slides array
686      scope.$on('$destroy', function() {
687        carouselCtrl.removeSlide(scope);
688      });
689
690      scope.$watch('active', function(active) {
691        if (active) {
692          carouselCtrl.select(scope);
693        }
694      });
695    }
696  };
697});
698
699angular.module('ui.bootstrap.dateparser', [])
700
701.service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {
702
703  this.parsers = {};
704
705  var formatCodeToRegex = {
706    'yyyy': {
707      regex: '\\d{4}',
708      apply: function(value) { this.year = +value; }
709    },
710    'yy': {
711      regex: '\\d{2}',
712      apply: function(value) { this.year = +value + 2000; }
713    },
714    'y': {
715      regex: '\\d{1,4}',
716      apply: function(value) { this.year = +value; }
717    },
718    'MMMM': {
719      regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
720      apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
721    },
722    'MMM': {
723      regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
724      apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
725    },
726    'MM': {
727      regex: '0[1-9]|1[0-2]',
728      apply: function(value) { this.month = value - 1; }
729    },
730    'M': {
731      regex: '[1-9]|1[0-2]',
732      apply: function(value) { this.month = value - 1; }
733    },
734    'dd': {
735      regex: '[0-2][0-9]{1}|3[0-1]{1}',
736      apply: function(value) { this.date = +value; }
737    },
738    'd': {
739      regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
740      apply: function(value) { this.date = +value; }
741    },
742    'EEEE': {
743      regex: $locale.DATETIME_FORMATS.DAY.join('|')
744    },
745    'EEE': {
746      regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
747    }
748  };
749
750  this.createParser = function(format) {
751    var map = [], regex = format.split('');
752
753    angular.forEach(formatCodeToRegex, function(data, code) {
754      var index = format.indexOf(code);
755
756      if (index > -1) {
757        format = format.split('');
758
759        regex[index] = '(' + data.regex + ')';
760        format[index] = '$'; // Custom symbol to define consumed part of format
761        for (var i = index + 1, n = index + code.length; i < n; i++) {
762          regex[i] = '';
763          format[i] = '$';
764        }
765        format = format.join('');
766
767        map.push({ index: index, apply: data.apply });
768      }
769    });
770
771    return {
772      regex: new RegExp('^' + regex.join('') + '$'),
773      map: orderByFilter(map, 'index')
774    };
775  };
776
777  this.parse = function(input, format) {
778    if ( !angular.isString(input) ) {
779      return input;
780    }
781
782    format = $locale.DATETIME_FORMATS[format] || format;
783
784    if ( !this.parsers[format] ) {
785      this.parsers[format] = this.createParser(format);
786    }
787
788    var parser = this.parsers[format],
789        regex = parser.regex,
790        map = parser.map,
791        results = input.match(regex);
792
793    if ( results && results.length ) {
794      var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt;
795
796      for( var i = 1, n = results.length; i < n; i++ ) {
797        var mapper = map[i-1];
798        if ( mapper.apply ) {
799          mapper.apply.call(fields, results[i]);
800        }
801      }
802
803      if ( isValid(fields.year, fields.month, fields.date) ) {
804        dt = new Date( fields.year, fields.month, fields.date, fields.hours);
805      }
806
807      return dt;
808    }
809  };
810
811  // Check if date is valid for specific month (and year for February).
812  // Month: 0 = Jan, 1 = Feb, etc
813  function isValid(year, month, date) {
814    if ( month === 1 && date > 28) {
815        return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
816    }
817
818    if ( month === 3 || month === 5 || month === 8 || month === 10) {
819        return date < 31;
820    }
821
822    return true;
823  }
824}]);
825
826angular.module('ui.bootstrap.position', [])
827
828/**
829 * A set of utility methods that can be use to retrieve position of DOM elements.
830 * It is meant to be used where we need to absolute-position DOM elements in
831 * relation to other, existing elements (this is the case for tooltips, popovers,
832 * typeahead suggestions etc.).
833 */
834  .factory('$position', ['$document', '$window', function ($document, $window) {
835
836    function getStyle(el, cssprop) {
837      if (el.currentStyle) { //IE
838        return el.currentStyle[cssprop];
839      } else if ($window.getComputedStyle) {
840        return $window.getComputedStyle(el)[cssprop];
841      }
842      // finally try and get inline style
843      return el.style[cssprop];
844    }
845
846    /**
847     * Checks if a given element is statically positioned
848     * @param element - raw DOM element
849     */
850    function isStaticPositioned(element) {
851      return (getStyle(element, 'position') || 'static' ) === 'static';
852    }
853
854    /**
855     * returns the closest, non-statically positioned parentOffset of a given element
856     * @param element
857     */
858    var parentOffsetEl = function (element) {
859      var docDomEl = $document[0];
860      var offsetParent = element.offsetParent || docDomEl;
861      while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
862        offsetParent = offsetParent.offsetParent;
863      }
864      return offsetParent || docDomEl;
865    };
866
867    return {
868      /**
869       * Provides read-only equivalent of jQuery's position function:
870       * http://api.jquery.com/position/
871       */
872      position: function (element) {
873        var elBCR = this.offset(element);
874        var offsetParentBCR = { top: 0, left: 0 };
875        var offsetParentEl = parentOffsetEl(element[0]);
876        if (offsetParentEl != $document[0]) {
877          offsetParentBCR = this.offset(angular.element(offsetParentEl));
878          offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
879          offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
880        }
881
882        var boundingClientRect = element[0].getBoundingClientRect();
883        return {
884          width: boundingClientRect.width || element.prop('offsetWidth'),
885          height: boundingClientRect.height || element.prop('offsetHeight'),
886          top: elBCR.top - offsetParentBCR.top,
887          left: elBCR.left - offsetParentBCR.left
888        };
889      },
890
891      /**
892       * Provides read-only equivalent of jQuery's offset function:
893       * http://api.jquery.com/offset/
894       */
895      offset: function (element) {
896        var boundingClientRect = element[0].getBoundingClientRect();
897        return {
898          width: boundingClientRect.width || element.prop('offsetWidth'),
899          height: boundingClientRect.height || element.prop('offsetHeight'),
900          top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
901          left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
902        };
903      },
904
905      /**
906       * Provides coordinates for the targetEl in relation to hostEl
907       */
908      positionElements: function (hostEl, targetEl, positionStr, appendToBody) {
909
910        var positionStrParts = positionStr.split('-');
911        var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
912
913        var hostElPos,
914          targetElWidth,
915          targetElHeight,
916          targetElPos;
917
918        hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
919
920        targetElWidth = targetEl.prop('offsetWidth');
921        targetElHeight = targetEl.prop('offsetHeight');
922
923        var shiftWidth = {
924          center: function () {
925            return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
926          },
927          left: function () {
928            return hostElPos.left;
929          },
930          right: function () {
931            return hostElPos.left + hostElPos.width;
932          }
933        };
934
935        var shiftHeight = {
936          center: function () {
937            return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
938          },
939          top: function () {
940            return hostElPos.top;
941          },
942          bottom: function () {
943            return hostElPos.top + hostElPos.height;
944          }
945        };
946
947        switch (pos0) {
948          case 'right':
949            targetElPos = {
950              top: shiftHeight[pos1](),
951              left: shiftWidth[pos0]()
952            };
953            break;
954          case 'left':
955            targetElPos = {
956              top: shiftHeight[pos1](),
957              left: hostElPos.left - targetElWidth
958            };
959            break;
960          case 'bottom':
961            targetElPos = {
962              top: shiftHeight[pos0](),
963              left: shiftWidth[pos1]()
964            };
965            break;
966          default:
967            targetElPos = {
968              top: hostElPos.top - targetElHeight,
969              left: shiftWidth[pos1]()
970            };
971            break;
972        }
973
974        return targetElPos;
975      }
976    };
977  }]);
978
979angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
980
981.constant('datepickerConfig', {
982  formatDay: 'dd',
983  formatMonth: 'MMMM',
984  formatYear: 'yyyy',
985  formatDayHeader: 'EEE',
986  formatDayTitle: 'MMMM yyyy',
987  formatMonthTitle: 'yyyy',
988  datepickerMode: 'day',
989  minMode: 'day',
990  maxMode: 'year',
991  showWeeks: true,
992  startingDay: 0,
993  yearRange: 20,
994  minDate: null,
995  maxDate: null
996})
997
998.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
999  var self = this,
1000      ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
1001
1002  // Modes chain
1003  this.modes = ['day', 'month', 'year'];
1004
1005  // Configuration attributes
1006  angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
1007                   'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {
1008    self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
1009  });
1010
1011  // Watchable attributes
1012  angular.forEach(['minDate', 'maxDate'], function( key ) {
1013    if ( $attrs[key] ) {
1014      $scope.$parent.$watch($parse($attrs[key]), function(value) {
1015        self[key] = value ? new Date(value) : null;
1016        self.refreshView();
1017      });
1018    } else {
1019      self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;
1020    }
1021  });
1022
1023  $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
1024  $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
1025  this.activeDate = angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date();
1026
1027  $scope.isActive = function(dateObject) {
1028    if (self.compare(dateObject.date, self.activeDate) === 0) {
1029      $scope.activeDateId = dateObject.uid;
1030      return true;
1031    }
1032    return false;
1033  };
1034
1035  this.init = function( ngModelCtrl_ ) {
1036    ngModelCtrl = ngModelCtrl_;
1037
1038    ngModelCtrl.$render = function() {
1039      self.render();
1040    };
1041  };
1042
1043  this.render = function() {
1044    if ( ngModelCtrl.$modelValue ) {
1045      var date = new Date( ngModelCtrl.$modelValue ),
1046          isValid = !isNaN(date);
1047
1048      if ( isValid ) {
1049        this.activeDate = date;
1050      } else {
1051        $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.');
1052      }
1053      ngModelCtrl.$setValidity('date', isValid);
1054    }
1055    this.refreshView();
1056  };
1057
1058  this.refreshView = function() {
1059    if ( this.element ) {
1060      this._refreshView();
1061
1062      var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
1063      ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
1064    }
1065  };
1066
1067  this.createDateObject = function(date, format) {
1068    var model = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
1069    return {
1070      date: date,
1071      label: dateFilter(date, format),
1072      selected: model && this.compare(date, model) === 0,
1073      disabled: this.isDisabled(date),
1074      current: this.compare(date, new Date()) === 0
1075    };
1076  };
1077
1078  this.isDisabled = function( date ) {
1079    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})));
1080  };
1081
1082  // Split array into smaller arrays
1083  this.split = function(arr, size) {
1084    var arrays = [];
1085    while (arr.length > 0) {
1086      arrays.push(arr.splice(0, size));
1087    }
1088    return arrays;
1089  };
1090
1091  $scope.select = function( date ) {
1092    if ( $scope.datepickerMode === self.minMode ) {
1093      var dt = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
1094      dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
1095      ngModelCtrl.$setViewValue( dt );
1096      ngModelCtrl.$render();
1097    } else {
1098      self.activeDate = date;
1099      $scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) - 1 ];
1100    }
1101  };
1102
1103  $scope.move = function( direction ) {
1104    var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
1105        month = self.activeDate.getMonth() + direction * (self.step.months || 0);
1106    self.activeDate.setFullYear(year, month, 1);
1107    self.refreshView();
1108  };
1109
1110  $scope.toggleMode = function( direction ) {
1111    direction = direction || 1;
1112
1113    if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
1114      return;
1115    }
1116
1117    $scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) + direction ];
1118  };
1119
1120  // Key event mapper
1121  $scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };
1122
1123  var focusElement = function() {
1124    $timeout(function() {
1125      self.element[0].focus();
1126    }, 0 , false);
1127  };
1128
1129  // Listen for focus requests from popup directive
1130  $scope.$on('datepicker.focus', focusElement);
1131
1132  $scope.keydown = function( evt ) {
1133    var key = $scope.keys[evt.which];
1134
1135    if ( !key || evt.shiftKey || evt.altKey ) {
1136      return;
1137    }
1138
1139    evt.preventDefault();
1140    evt.stopPropagation();
1141
1142    if (key === 'enter' || key === 'space') {
1143      if ( self.isDisabled(self.activeDate)) {
1144        return; // do nothing
1145      }
1146      $scope.select(self.activeDate);
1147      focusElement();
1148    } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
1149      $scope.toggleMode(key === 'up' ? 1 : -1);
1150      focusElement();
1151    } else {
1152      self.handleKeyDown(key, evt);
1153      self.refreshView();
1154    }
1155  };
1156}])
1157
1158.directive( 'datepicker', function () {
1159  return {
1160    restrict: 'EA',
1161    replace: true,
1162    templateUrl: 'template/datepicker/datepicker.html',
1163    scope: {
1164      datepickerMode: '=?',
1165      dateDisabled: '&'
1166    },
1167    require: ['datepicker', '?^ngModel'],
1168    controller: 'DatepickerController',
1169    link: function(scope, element, attrs, ctrls) {
1170      var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
1171
1172      if ( ngModelCtrl ) {
1173        datepickerCtrl.init( ngModelCtrl );
1174      }
1175    }
1176  };
1177})
1178
1179.directive('daypicker', ['dateFilter', function (dateFilter) {
1180  return {
1181    restrict: 'EA',
1182    replace: true,
1183    templateUrl: 'template/datepicker/day.html',
1184    require: '^datepicker',
1185    link: function(scope, element, attrs, ctrl) {
1186      scope.showWeeks = ctrl.showWeeks;
1187
1188      ctrl.step = { months: 1 };
1189      ctrl.element = element;
1190
1191      var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1192      function getDaysInMonth( year, month ) {
1193        return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
1194      }
1195
1196      function getDates(startDate, n) {
1197        var dates = new Array(n), current = new Date(startDate), i = 0;
1198        current.setHours(12); // Prevent repeated dates because of timezone bug
1199        while ( i < n ) {
1200          dates[i++] = new Date(current);
1201          current.setDate( current.getDate() + 1 );
1202        }
1203        return dates;
1204      }
1205
1206      ctrl._refreshView = function() {
1207        var year = ctrl.activeDate.getFullYear(),
1208          month = ctrl.activeDate.getMonth(),
1209          firstDayOfMonth = new Date(year, month, 1),
1210          difference = ctrl.startingDay - firstDayOfMonth.getDay(),
1211          numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
1212          firstDate = new Date(firstDayOfMonth);
1213
1214        if ( numDisplayedFromPreviousMonth > 0 ) {
1215          firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
1216        }
1217
1218        // 42 is the number of days on a six-month calendar
1219        var days = getDates(firstDate, 42);
1220        for (var i = 0; i < 42; i ++) {
1221          days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
1222            secondary: days[i].getMonth() !== month,
1223            uid: scope.uniqueId + '-' + i
1224          });
1225        }
1226
1227        scope.labels = new Array(7);
1228        for (var j = 0; j < 7; j++) {
1229          scope.labels[j] = {
1230            abbr: dateFilter(days[j].date, ctrl.formatDayHeader),
1231            full: dateFilter(days[j].date, 'EEEE')
1232          };
1233        }
1234
1235        scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
1236        scope.rows = ctrl.split(days, 7);
1237
1238        if ( scope.showWeeks ) {
1239          scope.weekNumbers = [];
1240          var weekNumber = getISO8601WeekNumber( scope.rows[0][0].date ),
1241              numWeeks = scope.rows.length;
1242          while( scope.weekNumbers.push(weekNumber++) < numWeeks ) {}
1243        }
1244      };
1245
1246      ctrl.compare = function(date1, date2) {
1247        return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
1248      };
1249
1250      function getISO8601WeekNumber(date) {
1251        var checkDate = new Date(date);
1252        checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
1253        var time = checkDate.getTime();
1254        checkDate.setMonth(0); // Compare with Jan 1
1255        checkDate.setDate(1);
1256        return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
1257      }
1258
1259      ctrl.handleKeyDown = function( key, evt ) {
1260        var date = ctrl.activeDate.getDate();
1261
1262        if (key === 'left') {
1263          date = date - 1;   // up
1264        } else if (key === 'up') {
1265          date = date - 7;   // down
1266        } else if (key === 'right') {
1267          date = date + 1;   // down
1268        } else if (key === 'down') {
1269          date = date + 7;
1270        }
1270 else if (key === 'pageup' || key === 'pagedown') {
1271          var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
1272          ctrl.activeDate.setMonth(month, 1);
1273          date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
1274        } else if (key === 'home') {
1275          date = 1;
1276        } else if (key === 'end') {
1277          date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
1278        }
1279        ctrl.activeDate.setDate(date);
1280      };
1281
1282      ctrl.refreshView();
1283    }
1284  };
1285}])
1286
1287.directive('monthpicker', ['dateFilter', function (dateFilter) {
1288  return {
1289    restrict: 'EA',
1290    replace: true,
1291    templateUrl: 'template/datepicker/month.html',
1292    require: '^datepicker',
1293    link: function(scope, element, attrs, ctrl) {
1294      ctrl.step = { years: 1 };
1295      ctrl.element = element;
1296
1297      ctrl._refreshView = function() {
1298        var months = new Array(12),
1299            year = ctrl.activeDate.getFullYear();
1300
1301        for ( var i = 0; i < 12; i++ ) {
1302          months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {
1303            uid: scope.uniqueId + '-' + i
1304          });
1305        }
1306
1307        scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
1308        scope.rows = ctrl.split(months, 3);
1309      };
1310
1311      ctrl.compare = function(date1, date2) {
1312        return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
1313      };
1314
1315      ctrl.handleKeyDown = function( key, evt ) {
1316        var date = ctrl.activeDate.getMonth();
1317
1318        if (key === 'left') {
1319          date = date - 1;   // up
1320        } else if (key === 'up') {
1321          date = date - 3;   // down
1322        } else if (key === 'right') {
1323          date = date + 1;   // down
1324        } else if (key === 'down') {
1325          date = date + 3;
1326        } else if (key === 'pageup' || key === 'pagedown') {
1327          var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
1328          ctrl.activeDate.setFullYear(year);
1329        } else if (key === 'home') {
1330          date = 0;
1331        } else if (key === 'end') {
1332          date = 11;
1333        }
1334        ctrl.activeDate.setMonth(date);
1335      };
1336
1337      ctrl.refreshView();
1338    }
1339  };
1340}])
1341
1342.directive('yearpicker', ['dateFilter', function (dateFilter) {
1343  return {
1344    restrict: 'EA',
1345    replace: true,
1346    templateUrl: 'template/datepicker/year.html',
1347    require: '^datepicker',
1348    link: function(scope, element, attrs, ctrl) {
1349      var range = ctrl.yearRange;
1350
1351      ctrl.step = { years: range };
1352      ctrl.element = element;
1353
1354      function getStartingYear( year ) {
1355        return parseInt((year - 1) / range, 10) * range + 1;
1356      }
1357
1358      ctrl._refreshView = function() {
1359        var years = new Array(range);
1360
1361        for ( var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++ ) {
1362          years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {
1363            uid: scope.uniqueId + '-' + i
1364          });
1365        }
1366
1367        scope.title = [years[0].label, years[range - 1].label].join(' - ');
1368        scope.rows = ctrl.split(years, 5);
1369      };
1370
1371      ctrl.compare = function(date1, date2) {
1372        return date1.getFullYear() - date2.getFullYear();
1373      };
1374
1375      ctrl.handleKeyDown = function( key, evt ) {
1376        var date = ctrl.activeDate.getFullYear();
1377
1378        if (key === 'left') {
1379          date = date - 1;   // up
1380        } else if (key === 'up') {
1381          date = date - 5;   // down
1382        } else if (key === 'right') {
1383          date = date + 1;   // down
1384        } else if (key === 'down') {
1385          date = date + 5;
1386        } else if (key === 'pageup' || key === 'pagedown') {
1387          date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
1388        } else if (key === 'home') {
1389          date = getStartingYear( ctrl.activeDate.getFullYear() );
1390        } else if (key === 'end') {
1391          date = getStartingYear( ctrl.activeDate.getFullYear() ) + range - 1;
1392        }
1393        ctrl.activeDate.setFullYear(date);
1394      };
1395
1396      ctrl.refreshView();
1397    }
1398  };
1399}])
1400
1401.constant('datepickerPopupConfig', {
1402  datepickerPopup: 'yyyy-MM-dd',
1403  currentText: 'Today',
1404  clearText: 'Clear',
1405  closeText: 'Done',
1406  closeOnDateSelection: true,
1407  appendToBody: false,
1408  showButtonBar: true
1409})
1410
1411.directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig',
1412function ($compile, $parse, $document, $position, dateFilter, dateParser, datepickerPopupConfig) {
1413  return {
1414    restrict: 'EA',
1415    require: 'ngModel',
1416    scope: {
1417      isOpen: '=?',
1418      currentText: '@',
1419      clearText: '@',
1420      closeText: '@',
1421      dateDisabled: '&'
1422    },
1423    link: function(scope, element, attrs, ngModel) {
1424      var dateFormat,
1425          closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
1426          appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
1427
1428      scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
1429
1430      scope.getText = function( key ) {
1431        return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
1432      };
1433
1434      attrs.$observe('datepickerPopup', function(value) {
1435          dateFormat = value || datepickerPopupConfig.datepickerPopup;
1436          ngModel.$render();
1437      });
1438
1439      // popup element used to display calendar
1440      var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
1441      popupEl.attr({
1442        'ng-model': 'date',
1443        'ng-change': 'dateSelection()'
1444      });
1445
1446      function cameltoDash( string ){
1447        return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
1448      }
1449
1450      // datepicker element
1451      var datepickerEl = angular.element(popupEl.children()[0]);
1452      if ( attrs.datepickerOptions ) {
1453        angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function( value, option ) {
1454          datepickerEl.attr( cameltoDash(option), value );
1455        });
1456      }
1457
1458      angular.forEach(['minDate', 'maxDate'], function( key ) {
1459        if ( attrs[key] ) {
1460          scope.$parent.$watch($parse(attrs[key]), function(value){
1461            scope[key] = value;
1462          });
1463          datepickerEl.attr(cameltoDash(key), key);
1464        }
1465      });
1466      if (attrs.dateDisabled) {
1467        datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
1468      }
1469
1470      function parseDate(viewValue) {
1471        if (!viewValue) {
1472          ngModel.$setValidity('date', true);
1473          return null;
1474        } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
1475          ngModel.$setValidity('date', true);
1476          return viewValue;
1477        } else if (angular.isString(viewValue)) {
1478          var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);
1479          if (isNaN(date)) {
1480            ngModel.$setValidity('date', false);
1481            return undefined;
1482          } else {
1483            ngModel.$setValidity('date', true);
1484            return date;
1485          }
1486        } else {
1487          ngModel.$setValidity('date', false);
1488          return undefined;
1489        }
1490      }
1491      ngModel.$parsers.unshift(parseDate);
1492
1493      // Inner change
1494      scope.dateSelection = function(dt) {
1495        if (angular.isDefined(dt)) {
1496          scope.date = dt;
1497        }
1498        ngModel.$setViewValue(scope.date);
1499        ngModel.$render();
1500
1501        if ( closeOnDateSelection ) {
1502          scope.isOpen = false;
1503          element[0].focus();
1504        }
1505      };
1506
1507      element.bind('input change keyup', function() {
1508        scope.$apply(function() {
1509          scope.date = ngModel.$modelValue;
1510        });
1511      });
1512
1513      // Outter change
1514      ngModel.$render = function() {
1515        var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
1516        element.val(date);
1517        scope.date = parseDate( ngModel.$modelValue );
1518      };
1519
1520      var documentClickBind = function(event) {
1521        if (scope.isOpen && event.target !== element[0]) {
1522          scope.$apply(function() {
1523            scope.isOpen = false;
1524          });
1525        }
1526      };
1527
1528      var keydown = function(evt, noApply) {
1529        scope.keydown(evt);
1530      };
1531      element.bind('keydown', keydown);
1532
1533      scope.keydown = function(evt) {
1534        if (evt.which === 27) {
1535          evt.preventDefault();
1536          evt.stopPropagation();
1537          scope.close();
1538        } else if (evt.which === 40 && !scope.isOpen) {
1539          scope.isOpen = true;
1540        }
1541      };
1542
1543      scope.$watch('isOpen', function(value) {
1544        if (value) {
1545          scope.$broadcast('datepicker.focus');
1546          scope.position = appendToBody ? $position.offset(element) : $position.position(element);
1547          scope.position.top = scope.position.top + element.prop('offsetHeight');
1548
1549          $document.bind('click', documentClickBind);
1550        } else {
1551          $document.unbind('click', documentClickBind);
1552        }
1553      });
1554
1555      scope.select = function( date ) {
1556        if (date === 'today') {
1557          var today = new Date();
1558          if (angular.isDate(ngModel.$modelValue)) {
1559            date = new Date(ngModel.$modelValue);
1560            date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
1561          } else {
1562            date = new Date(today.setHours(0, 0, 0, 0));
1563          }
1564        }
1565        scope.dateSelection( date );
1566      };
1567
1568      scope.close = function() {
1569        scope.isOpen = false;
1570        element[0].focus();
1571      };
1572
1573      var $popup = $compile(popupEl)(scope);
1574      if ( appendToBody ) {
1575        $document.find('body').append($popup);
1576      } else {
1577        element.after($popup);
1578      }
1579
1580      scope.$on('$destroy', function() {
1581        $popup.remove();
1582        element.unbind('keydown', keydown);
1583        $document.unbind('click', documentClickBind);
1584      });
1585    }
1586  };
1587}])
1588
1589.directive('datepickerPopupWrap', function() {
1590  return {
1591    restrict:'EA',
1592    replace: true,
1593    transclude: true,
1594    templateUrl: 'template/datepicker/popup.html',
1595    link:function (scope, element, attrs) {
1596      element.bind('click', function(event) {
1597        event.preventDefault();
1598        event.stopPropagation();
1599      });
1600    }
1601  };
1602});
1603
1604angular.module('ui.bootstrap.dropdown', [])
1605
1606.constant('dropdownConfig', {
1607  openClass: 'open'
1608})
1609
1610.service('dropdownService', ['$document', function($document) {
1611  var openScope = null;
1612
1613  this.open = function( dropdownScope ) {
1614    if ( !openScope ) {
1615      $document.bind('click', closeDropdown);
1616      $document.bind('keydown', escapeKeyBind);
1617    }
1618
1619    if ( openScope && openScope !== dropdownScope ) {
1620        openScope.isOpen = false;
1621    }
1622
1623    openScope = dropdownScope;
1624  };
1625
1626  this.close = function( dropdownScope ) {
1627    if ( openScope === dropdownScope ) {
1628      openScope = null;
1629      $document.unbind('click', closeDropdown);
1630      $document.unbind('keydown', escapeKeyBind);
1631    }
1632  };
1633
1634  var closeDropdown = function( evt ) {
1635    if (evt && evt.isDefaultPrevented()) {
1636        return;
1637    }
1638
1639    openScope.$apply(function() {
1640      openScope.isOpen = false;
1641    });
1642  };
1643
1644  var escapeKeyBind = function( evt ) {
1645    if ( evt.which === 27 ) {
1646      openScope.focusToggleElement();
1647      closeDropdown();
1648    }
1649  };
1650}])
1651
1652.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) {
1653  var self = this,
1654      scope = $scope.$new(), // create a child scope so we are not polluting original one
1655      openClass = dropdownConfig.openClass,
1656      getIsOpen,
1657      setIsOpen = angular.noop,
1658      toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop;
1659
1660  this.init = function( element ) {
1661    self.$element = element;
1662
1663    if ( $attrs.isOpen ) {
1664      getIsOpen = $parse($attrs.isOpen);
1665      setIsOpen = getIsOpen.assign;
1666
1667      $scope.$watch(getIsOpen, function(value) {
1668        scope.isOpen = !!value;
1669      });
1670    }
1671  };
1672
1673  this.toggle = function( open ) {
1674    return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
1675  };
1676
1677  // Allow other directives to watch status
1678  this.isOpen = function() {
1679    return scope.isOpen;
1680  };
1681
1682  scope.focusToggleElement = function() {
1683    if ( self.toggleElement ) {
1684      self.toggleElement[0].focus();
1685    }
1686  };
1687
1688  scope.$watch('isOpen', function( isOpen, wasOpen ) {
1689    $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);
1690
1691    if ( isOpen ) {
1692      scope.focusToggleElement();
1693      dropdownService.open( scope );
1694    } else {
1695      dropdownService.close( scope );
1696    }
1697
1698    setIsOpen($scope, isOpen);
1699    if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
1700      toggleInvoker($scope, { open: !!isOpen });
1701    }
1702  });
1703
1704  $scope.$on('$locationChangeSuccess', function() {
1705    scope.isOpen = false;
1706  });
1707
1708  $scope.$on('$destroy', function() {
1709    scope.$destroy();
1710  });
1711}])
1712
1713.directive('dropdown', function() {
1714  return {
1715    restrict: 'CA',
1716    controller: 'DropdownController',
1717    link: function(scope, element, attrs, dropdownCtrl) {
1718      dropdownCtrl.init( element );
1719    }
1720  };
1721})
1722
1723.directive('dropdownToggle', function() {
1724  return {
1725    restrict: 'CA',
1726    require: '?^dropdown',
1727    link: function(scope, element, attrs, dropdownCtrl) {
1728      if ( !dropdownCtrl ) {
1729        return;
1730      }
1731
1732      dropdownCtrl.toggleElement = element;
1733
1734      var toggleDropdown = function(event) {
1735        event.preventDefault();
1736
1737        if ( !element.hasClass('disabled') && !attrs.disabled ) {
1738          scope.$apply(function() {
1739            dropdownCtrl.toggle();
1740          });
1741        }
1742      };
1743
1744      element.bind('click', toggleDropdown);
1745
1746      // WAI-ARIA
1747      element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
1748      scope.$watch(dropdownCtrl.isOpen, function( isOpen ) {
1749        element.attr('aria-expanded', !!isOpen);
1750      });
1751
1752      scope.$on('$destroy', function() {
1753        element.unbind('click', toggleDropdown);
1754      });
1755    }
1756  };
1757});
1758
1759angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1760
1761/**
1762 * A helper, internal data structure that acts as a map but also allows getting / removing
1763 * elements in the LIFO order
1764 */
1765  .factory('$$stackedMap', function () {
1766    return {
1767      createNew: function () {
1768        var stack = [];
1769
1770        return {
1771          add: function (key, value) {
1772            stack.push({
1773              key: key,
1774              value: value
1775            });
1776          },
1777          get: function (key) {
1778            for (var i = 0; i < stack.length; i++) {
1779              if (key == stack[i].key) {
1780                return stack[i];
1781              }
1782            }
1783          },
1784          keys: function() {
1785            var keys = [];
1786            for (var i = 0; i < stack.length; i++) {
1787              keys.push(stack[i].key);
1788            }
1789            return keys;
1790          },
1791          top: function () {
1792            return stack[stack.length - 1];
1793          },
1794          remove: function (key) {
1795            var idx = -1;
1796            for (var i = 0; i < stack.length; i++) {
1797              if (key == stack[i].key) {
1798                idx = i;
1799                break;
1800              }
1801            }
1802            return stack.splice(idx, 1)[0];
1803          },
1804          removeTop: function () {
1805            return stack.splice(stack.length - 1, 1)[0];
1806          },
1807          length: function () {
1808            return stack.length;
1809          }
1810        };
1811      }
1812    };
1813  })
1814
1815/**
1816 * A helper directive for the $modal service. It creates a backdrop element.
1817 */
1818  .directive('modalBackdrop', ['$timeout', function ($timeout) {
1819    return {
1820      restrict: 'EA',
1821      replace: true,
1822      templateUrl: 'template/modal/backdrop.html',
1823      link: function (scope) {
1824
1825        scope.animate = false;
1826
1827        //trigger CSS transitions
1828        $timeout(function () {
1829          scope.animate = true;
1830        });
1831      }
1832    };
1833  }])
1834
1835  .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
1836    return {
1837      restrict: 'EA',
1838      scope: {
1839        index: '@',
1840        animate: '='
1841      },
1842      replace: true,
1843      transclude: true,
1844      templateUrl: function(tElement, tAttrs) {
1845        return tAttrs.templateUrl || 'template/modal/window.html';
1846      },
1847      link: function (scope, element, attrs) {
1848        element.addClass(attrs.windowClass || '');
1849        scope.size = attrs.size;
1850
1851        $timeout(function () {
1852          // trigger CSS transitions
1853          scope.animate = true;
1854          // focus a freshly-opened modal
1855          element[0].focus();
1856        });
1857
1858        scope.close = function (evt) {
1859          var modal = $modalStack.getTop();
1860          if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
1861            evt.preventDefault();
1862            evt.stopPropagation();
1863            $modalStack.dismiss(modal.key, 'backdrop click');
1864          }
1865        };
1866      }
1867    };
1868  }])
1869
1870  .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
1871    function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {
1872
1873      var OPENED_MODAL_CLASS = 'modal-open';
1874
1875      var backdropDomEl, backdropScope;
1876      var openedWindows = $$stackedMap.createNew();
1877      var $modalStack = {};
1878
1879      function backdropIndex() {
1880        var topBackdropIndex = -1;
1881        var opened = openedWindows.keys();
1882        for (var i = 0; i < opened.length; i++) {
1883          if (openedWindows.get(opened[i]).value.backdrop) {
1884            topBackdropIndex = i;
1885          }
1886        }
1887        return topBackdropIndex;
1888      }
1889
1890      $rootScope.$watch(backdropIndex, function(newBackdropIndex){
1891        if (backdropScope) {
1892          backdropScope.index = newBackdropIndex;
1893        }
1894      });
1895
1896      function removeModalWindow(modalInstance) {
1897
1898        var body = $document.find('body').eq(0);
1899        var modalWindow = openedWindows.get(modalInstance).value;
1900
1901        //clean up the stack
1902        openedWindows.remove(modalInstance);
1903
1904        //remove window DOM element
1905        removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, function() {
1906          modalWindow.modalScope.$destroy();
1907          body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
1908          checkRemoveBackdrop();
1909        });
1910      }
1911
1912      function checkRemoveBackdrop() {
1913          //remove backdrop if no longer needed
1914          if (backdropDomEl && backdropIndex() == -1) {
1915            var backdropScopeRef = backdropScope;
1916            removeAfterAnimate(backdropDomEl, backdropScope, 150, function () {
1917              backdropScopeRef.$destroy();
1918              backdropScopeRef = null;
1919            });
1920            backdropDomEl = undefined;
1921            backdropScope = undefined;
1922          }
1923      }
1924
1925      function removeAfterAnimate(domEl, scope, emulateTime, done) {
1926        // Closing animation
1927        scope.animate = false;
1928
1929        var transitionEndEventName = $transition.transitionEndEventName;
1930        if (transitionEndEventName) {
1931          // transition out
1932          var timeout = $timeout(afterAnimating, emulateTime);
1933
1934          domEl.bind(transitionEndEventName, function () {
1935            $timeout.cancel(timeout);
1936            afterAnimating();
1937            scope.$apply();
1938          });
1939        } else {
1940          // Ensure this call is async
1941          $timeout(afterAnimating, 0);
1942        }
1943
1944        function afterAnimating() {
1945          if (afterAnimating.done) {
1946            return;
1947          }
1948          afterAnimating.done = true;
1949
1950          domEl.remove();
1951          if (done) {
1952            done();
1953          }
1954        }
1955      }
1956
1957      $document.bind('keydown', function (evt) {
1958        var modal;
1959
1960        if (evt.which === 27) {
1961          modal = openedWindows.top();
1962          if (modal && modal.value.keyboard) {
1963            evt.preventDefault();
1964            $rootScope.$apply(function () {
1965              $modalStack.dismiss(modal.key, 'escape key press');
1966            });
1967          }
1968        }
1969      });
1970
1971      $modalStack.open = function (modalInstance, modal) {
1972
1973        openedWindows.add(modalInstance, {
1974          deferred: modal.deferred,
1975          modalScope: modal.scope,
1976          backdrop: modal.backdrop,
1977          keyboard: modal.keyboard
1978        });
1979
1980        var body = $document.find('body').eq(0),
1981            currBackdropIndex = backdropIndex();
1982
1983        if (currBackdropIndex >= 0 && !backdropDomEl) {
1984          backdropScope = $rootScope.$new(true);
1985          backdropScope.index = currBackdropIndex;
1986          backdropDomEl = $compile('<div modal-backdrop></div>')(backdropScope);
1987          body.append(backdropDomEl);
1988        }
1989
1990        var angularDomEl = angular.element('<div modal-window></div>');
1991        angularDomEl.attr({
1992          'template-url': modal.windowTemplateUrl,
1993          'window-class': modal.windowClass,
1994          'size': modal.size,
1995          'index': openedWindows.length() - 1,
1996          'animate': 'animate'
1997        }).html(modal.content);
1998
1999        var modalDomEl = $compile(angularDomEl)(modal.scope);
2000        openedWindows.top().value.modalDomEl = modalDomEl;
2001        body.append(modalDomEl);
2002        body.addClass(OPENED_MODAL_CLASS);
2003      };
2004
2005      $modalStack.close = function (modalInstance, result) {
2006        var modalWindow = openedWindows.get(modalInstance).value;
2007        if (modalWindow) {
2008          modalWindow.deferred.resolve(result);
2009          removeModalWindow(modalInstance);
2010        }
2011      };
2012
2013      $modalStack.dismiss = function (modalInstance, reason) {
2014        var modalWindow = openedWindows.get(modalInstance).value;
2015        if (modalWindow) {
2016          modalWindow.deferred.reject(reason);
2017          removeModalWindow(modalInstance);
2018        }
2019      };
2020
2021      $modalStack.dismissAll = function (reason) {
2022        var topModal = this.getTop();
2023        while (topModal) {
2024          this.dismiss(topModal.key, reason);
2025          topModal = this.getTop();
2026        }
2027      };
2028
2029      $modalStack.getTop = function () {
2030        return openedWindows.top();
2031      };
2032
2033      return $modalStack;
2034    }])
2035
2036  .provider('$modal', function () {
2037
2038    var $modalProvider = {
2039      options: {
2040        backdrop: true, //can be also false or 'static'
2041        keyboard: true
2042      },
2043      $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
2044        function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
2045
2046          var $modal = {};
2047
2048          function getTemplatePromise(options) {
2049            return options.template ? $q.when(options.template) :
2050              $http.get(options.templateUrl, {cache: $templateCache}).then(function (result) {
2051                return result.data;
2052              });
2053          }
2054
2055          function getResolvePromises(resolves) {
2056            var promisesArr = [];
2057            angular.forEach(resolves, function (value, key) {
2058              if (angular.isFunction(value) || angular.isArray(value)) {
2059                promisesArr.push($q.when($injector.invoke(value)));
2060              }
2061            });
2062            return promisesArr;
2063          }
2064
2065          $modal.open = function (modalOptions) {
2066
2067            var modalResultDeferred = $q.defer();
2068            var modalOpenedDeferred = $q.defer();
2069
2070            //prepare an instance of a modal to be injected into controllers and returned to a caller
2071            var modalInstance = {
2072              result: modalResultDeferred.promise,
2073              opened: modalOpenedDeferred.promise,
2074              close: function (result) {
2075                $modalStack.close(modalInstance, result);
2076              },
2077              dismiss: function (reason) {
2078                $modalStack.dismiss(modalInstance, reason);
2079              }
2080            };
2081
2082            //merge and clean up options
2083            modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
2084            modalOptions.resolve = modalOptions.resolve || {};
2085
2086            //verify options
2087            if (!modalOptions.template && !modalOptions.templateUrl) {
2088              throw new Error('One of template or templateUrl options is required.');
2089            }
2090
2091            var templateAndResolvePromise =
2092              $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
2093
2094
2095            templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
2096
2097              var modalScope = (modalOptions.scope || $rootScope).$new();
2098              modalScope.$close = modalInstance.close;
2099              modalScope.$dismiss = modalInstance.dismiss;
2100
2101              var ctrlInstance, ctrlLocals = {};
2102              var resolveIter = 1;
2103
2104              //controllers
2105              if (modalOptions.controller) {
2106                ctrlLocals.$scope = modalScope;
2107                ctrlLocals.$modalInstance = modalInstance;
2108                angular.forEach(modalOptions.resolve, function (value, key) {
2109                  ctrlLocals[key] = tplAndVars[resolveIter++];
2110                });
2111
2112                ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
2113              }
2114
2115              $modalStack.open(modalInstance, {
2116                scope: modalScope,
2117                deferred: modalResultDeferred,
2118                content: tplAndVars[0],
2119                backdrop: modalOptions.backdrop,
2120                keyboard: modalOptions.keyboard,
2121                windowClass: modalOptions.windowClass,
2122                windowTemplateUrl: modalOptions.windowTemplateUrl,
2123                size: modalOptions.size
2124              });
2125
2126            }, function resolveError(reason) {
2127              modalResultDeferred.reject(reason);
2128            });
2129
2130            templateAndResolvePromise.then(function () {
2131              modalOpenedDeferred.resolve(true);
2132            }, function () {
2133              modalOpenedDeferred.reject(false);
2134            });
2135
2136            return modalInstance;
2137          };
2138
2139          return $modal;
2140        }]
2141    };
2142
2143    return $modalProvider;
2144  });
2145
2146angular.module('ui.bootstrap.pagination', [])
2147
2148.controller('PaginationController', ['$scope', '$attrs', '$parse', function ($scope, $attrs, $parse) {
2149  var self = this,
2150      ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
2151      setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
2152
2153  this.init = function(ngModelCtrl_, config) {
2154    ngModelCtrl = ngModelCtrl_;
2155    this.config = config;
2156
2157    ngModelCtrl.$render = function() {
2158      self.render();
2159    };
2160
2161    if ($attrs.itemsPerPage) {
2162      $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
2163        self.itemsPerPage = parseInt(value, 10);
2164        $scope.totalPages = self.calculateTotalPages();
2165      });
2166    } else {
2167      this.itemsPerPage = config.itemsPerPage;
2168    }
2169  };
2170
2171  this.calculateTotalPages = function() {
2172    var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
2173    return Math.max(totalPages || 0, 1);
2174  };
2175
2176  this.render = function() {
2177    $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1;
2178  };
2179
2180  $scope.selectPage = function(page) {
2181    if ( $scope.page !== page && page > 0 && page <= $scope.totalPages) {
2182      ngModelCtrl.$setViewValue(page);
2183      ngModelCtrl.$render();
2184    }
2185  };
2186
2187  $scope.getText = function( key ) {
2188    return $scope[key + 'Text'] || self.config[key + 'Text'];
2189  };
2190  $scope.noPrevious = function() {
2191    return $scope.page === 1;
2192  };
2193  $scope.noNext = function() {
2194    return $scope.page === $scope.totalPages;
2195  };
2196
2197  $scope.$watch('totalItems', function() {
2198    $scope.totalPages = self.calculateTotalPages();
2199  });
2200
2201  $scope.$watch('totalPages', function(value) {
2202    setNumPages($scope.$parent, value); // Readonly variable
2203
2204    if ( $scope.page > value ) {
2205      $scope.selectPage(value);
2206    } else {
2207      ngModelCtrl.$render();
2208    }
2209  });
2210}])
2211
2212.constant('paginationConfig', {
2213  itemsPerPage: 10,
2214  boundaryLinks: false,
2215  directionLinks: true,
2216  firstText: 'First',
2217  previousText: 'Previous',
2218  nextText: 'Next',
2219  lastText: 'Last',
2220  rotate: true
2221})
2222
2223.directive('pagination', ['$parse', 'paginationConfig', function($parse, paginationConfig) {
2224  return {
2225    restrict: 'EA',
2226    scope: {
2227      totalItems: '=',
2228      firstText: '@',
2229      previousText: '@',
2230      nextText: '@',
2231      lastText: '@'
2232    },
2233    require: ['pagination', '?ngModel'],
2234    controller: 'PaginationController',
2235    templateUrl: 'template/pagination/pagination.html',
2236    replace: true,
2237    link: function(scope, element, attrs, ctrls) {
2238      var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
2239
2240      if (!ngModelCtrl) {
2241         return; // do nothing if no ng-model
2242      }
2243
2244      // Setup configuration parameters
2245      var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize,
2246          rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate;
2247      scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boun
2247daryLinks;
2248      scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
2249
2250      paginationCtrl.init(ngModelCtrl, paginationConfig);
2251
2252      if (attrs.maxSize) {
2253        scope.$parent.$watch($parse(attrs.maxSize), function(value) {
2254          maxSize = parseInt(value, 10);
2255          paginationCtrl.render();
2256        });
2257      }
2258
2259      // Create page object used in template
2260      function makePage(number, text, isActive) {
2261        return {
2262          number: number,
2263          text: text,
2264          active: isActive
2265        };
2266      }
2267
2268      function getPages(currentPage, totalPages) {
2269        var pages = [];
2270
2271        // Default page limits
2272        var startPage = 1, endPage = totalPages;
2273        var isMaxSized = ( angular.isDefined(maxSize) && maxSize < totalPages );
2274
2275        // recompute if maxSize
2276        if ( isMaxSized ) {
2277          if ( rotate ) {
2278            // Current page is displayed in the middle of the visible ones
2279            startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);
2280            endPage   = startPage + maxSize - 1;
2281
2282            // Adjust if limit is exceeded
2283            if (endPage > totalPages) {
2284              endPage   = totalPages;
2285              startPage = endPage - maxSize + 1;
2286            }
2287          } else {
2288            // Visible pages are paginated with maxSize
2289            startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;
2290
2291            // Adjust last page if limit is exceeded
2292            endPage = Math.min(startPage + maxSize - 1, totalPages);
2293          }
2294        }
2295
2296        // Add page number links
2297        for (var number = startPage; number <= endPage; number++) {
2298          var page = makePage(number, number, number === currentPage);
2299          pages.push(page);
2300        }
2301
2302        // Add links to move between page sets
2303        if ( isMaxSized && ! rotate ) {
2304          if ( startPage > 1 ) {
2305            var previousPageSet = makePage(startPage - 1, '...', false);
2306            pages.unshift(previousPageSet);
2307          }
2308
2309          if ( endPage < totalPages ) {
2310            var nextPageSet = makePage(endPage + 1, '...', false);
2311            pages.push(nextPageSet);
2312          }
2313        }
2314
2315        return pages;
2316      }
2317
2318      var originalRender = paginationCtrl.render;
2319      paginationCtrl.render = function() {
2320        originalRender();
2321        if (scope.page > 0 && scope.page <= scope.totalPages) {
2322          scope.pages = getPages(scope.page, scope.totalPages);
2323        }
2324      };
2325    }
2326  };
2327}])
2328
2329.constant('pagerConfig', {
2330  itemsPerPage: 10,
2331  previousText: '« Previous',
2332  nextText: 'Next »',
2333  align: true
2334})
2335
2336.directive('pager', ['pagerConfig', function(pagerConfig) {
2337  return {
2338    restrict: 'EA',
2339    scope: {
2340      totalItems: '=',
2341      previousText: '@',
2342      nextText: '@'
2343    },
2344    require: ['pager', '?ngModel'],
2345    controller: 'PaginationController',
2346    templateUrl: 'template/pagination/pager.html',
2347    replace: true,
2348    link: function(scope, element, attrs, ctrls) {
2349      var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
2350
2351      if (!ngModelCtrl) {
2352         return; // do nothing if no ng-model
2353      }
2354
2355      scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align;
2356      paginationCtrl.init(ngModelCtrl, pagerConfig);
2357    }
2358  };
2359}]);
2360
2361/**
2362 * The following features are still outstanding: animation as a
2363 * function, placement as a function, inside, support for more triggers than
2364 * just mouse enter/leave, html tooltips, and selector delegation.
2365 */
2366angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap.bindHtml' ] )
2367
2368/**
2369 * The $tooltip service creates tooltip- and popover-like directives as well as
2370 * houses global options for them.
2371 */
2372.provider( '$tooltip', function () {
2373  // The default options tooltip and popover.
2374  var defaultOptions = {
2375    placement: 'top',
2376    animation: true,
2377    popupDelay: 0
2378  };
2379
2380  // Default hide triggers for each show trigger
2381  var triggerMap = {
2382    'mouseenter': 'mouseleave',
2383    'click': 'click',
2384    'focus': 'blur'
2385  };
2386
2387  // The options specified to the provider globally.
2388  var globalOptions = {};
2389
2390  /**
2391   * `options({})` allows global configuration of all tooltips in the
2392   * application.
2393   *
2394   *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
2395   *     // place tooltips left instead of top by default
2396   *     $tooltipProvider.options( { placement: 'left' } );
2397   *   });
2398   */
2399	this.options = function( value ) {
2400		angular.extend( globalOptions, value );
2401	};
2402
2403  /**
2404   * This allows you to extend the set of trigger mappings available. E.g.:
2405   *
2406   *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
2407   */
2408  this.setTriggers = function setTriggers ( triggers ) {
2409    angular.extend( triggerMap, triggers );
2410  };
2411
2412  /**
2413   * This is a helper function for translating camel-case to snake-case.
2414   */
2415  function snake_case(name){
2416    var regexp = /[A-Z]/g;
2417    var separator = '-';
2418    return name.replace(regexp, function(letter, pos) {
2419      return (pos ? separator : '') + letter.toLowerCase();
2420    });
2421  }
2422
2423  /**
2424   * Returns the actual instance of the $tooltip service.
2425   * TODO support multiple triggers
2426   */
2427  this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $parse, $document, $position, $interpolate ) {
2428    return function $tooltip ( type, prefix, defaultTriggerShow ) {
2429      var options = angular.extend( {}, defaultOptions, globalOptions );
2430
2431      /**
2432       * Returns an object of show and hide triggers.
2433       *
2434       * If a trigger is supplied,
2435       * it is used to show the tooltip; otherwise, it will use the `trigger`
2436       * option passed to the `$tooltipProvider.options` method; else it will
2437       * default to the trigger supplied to this directive factory.
2438       *
2439       * The hide trigger is based on the show trigger. If the `trigger` option
2440       * was passed to the `$tooltipProvider.options` method, it will use the
2441       * mapped trigger from `triggerMap` or the passed trigger if the map is
2442       * undefined; otherwise, it uses the `triggerMap` value of the show
2443       * trigger; else it will just use the show trigger.
2444       */
2445      function getTriggers ( trigger ) {
2446        var show = trigger || options.trigger || defaultTriggerShow;
2447        var hide = triggerMap[show] || show;
2448        return {
2449          show: show,
2450          hide: hide
2451        };
2452      }
2453
2454      var directiveName = snake_case( type );
2455
2456      var startSym = $interpolate.startSymbol();
2457      var endSym = $interpolate.endSymbol();
2458      var template =
2459        '<div '+ directiveName +'-popup '+
2460          'title="'+startSym+'tt_title'+endSym+'" '+
2461          'content="'+startSym+'tt_content'+endSym+'" '+
2462          'placement="'+startSym+'tt_placement'+endSym+'" '+
2463          'animation="tt_animation" '+
2464          'is-open="tt_isOpen"'+
2465          '>'+
2466        '</div>';
2467
2468      return {
2469        restrict: 'EA',
2470        scope: true,
2471        compile: function (tElem, tAttrs) {
2472          var tooltipLinker = $compile( template );
2473
2474          return function link ( scope, element, attrs ) {
2475            var tooltip;
2476            var transitionTimeout;
2477            var popupTimeout;
2478            var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
2479            var triggers = getTriggers( undefined );
2480            var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
2481
2482            var positionTooltip = function () {
2483
2484              var ttPosition = $position.positionElements(element, tooltip, scope.tt_placement, appendToBody);
2485              ttPosition.top += 'px';
2486              ttPosition.left += 'px';
2487
2488              // Now set the calculated positioning.
2489              tooltip.css( ttPosition );
2490            };
2491
2492            // By default, the tooltip is not open.
2493            // TODO add ability to start tooltip opened
2494            scope.tt_isOpen = false;
2495
2496            function toggleTooltipBind () {
2497              if ( ! scope.tt_isOpen ) {
2498                showTooltipBind();
2499              } else {
2500                hideTooltipBind();
2501              }
2502            }
2503
2504            // Show the tooltip with delay if specified, otherwise show it immediately
2505            function showTooltipBind() {
2506              if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {
2507                return;
2508              }
2509              if ( scope.tt_popupDelay ) {
2510                // Do nothing if the tooltip was already scheduled to pop-up.
2511                // This happens if show is triggered multiple times before any hide is triggered.
2512                if (!popupTimeout) {
2513                  popupTimeout = $timeout( show, scope.tt_popupDelay, false );
2514                  popupTimeout.then(function(reposition){reposition();});
2515                }
2516              } else {
2517                show()();
2518              }
2519            }
2520
2521            function hideTooltipBind () {
2522              scope.$apply(function () {
2523                hide();
2524              });
2525            }
2526
2527            // Show the tooltip popup element.
2528            function show() {
2529
2530              popupTimeout = null;
2531
2532              // If there is a pending remove transition, we must cancel it, lest the
2533              // tooltip be mysteriously removed.
2534              if ( transitionTimeout ) {
2535                $timeout.cancel( transitionTimeout );
2536                transitionTimeout = null;
2537              }
2538
2539              // Don't show empty tooltips.
2540              if ( ! scope.tt_content ) {
2541                return angular.noop;
2542              }
2543
2544              createTooltip();
2545
2546              // Set the initial positioning.
2547              tooltip.css({ top: 0, left: 0, display: 'block' });
2548
2549              // Now we add it to the DOM because need some info about it. But it's not 
2550              // visible yet anyway.
2551              if ( appendToBody ) {
2552                  $document.find( 'body' ).append( tooltip );
2553              } else {
2554                element.after( tooltip );
2555              }
2556
2557              positionTooltip();
2558
2559              // And show the tooltip.
2560              scope.tt_isOpen = true;
2561              scope.$digest(); // digest required as $apply is not called
2562
2563              // Return positioning function as promise callback for correct
2564              // positioning after draw.
2565              return positionTooltip;
2566            }
2567
2568            // Hide the tooltip popup element.
2569            function hide() {
2570              // First things first: we don't show it anymore.
2571              scope.tt_isOpen = false;
2572
2573              //if tooltip is going to be shown after delay, we must cancel this
2574              $timeout.cancel( popupTimeout );
2575              popupTimeout = null;
2576
2577              // And now we remove it from the DOM. However, if we have animation, we 
2578              // need to wait for it to expire beforehand.
2579              // FIXME: this is a placeholder for a port of the transitions library.
2580              if ( scope.tt_animation ) {
2581                if (!transitionTimeout) {
2582                  transitionTimeout = $timeout(removeTooltip, 500);
2583                }
2584              } else {
2585                removeTooltip();
2586              }
2587            }
2588
2589            function createTooltip() {
2590              // There can only be one tooltip element per directive shown at once.
2591              if (tooltip) {
2592                removeTooltip();
2593              }
2594              tooltip = tooltipLinker(scope, function () {});
2595
2596              // Get contents rendered into the tooltip
2597              scope.$digest();
2598            }
2599
2600            function removeTooltip() {
2601              transitionTimeout = null;
2602              if (tooltip) {
2603                tooltip.remove();
2604                tooltip = null;
2605              }
2606            }
2607
2608            /**
2609             * Observe the relevant attributes.
2610             */
2611            attrs.$observe( type, function ( val ) {
2612              scope.tt_content = val;
2613
2614              if (!val && scope.tt_isOpen ) {
2615                hide();
2616              }
2617            });
2618
2619            attrs.$observe( prefix+'Title', function ( val ) {
2620              scope.tt_title = val;
2621            });
2622
2623            attrs.$observe( prefix+'Placement', function ( val ) {
2624              scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
2625            });
2626
2627            attrs.$observe( prefix+'PopupDelay', function ( val ) {
2628              var delay = parseInt( val, 10 );
2629              scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
2630            });
2631
2632            var unregisterTriggers = function () {
2633              element.unbind(triggers.show, showTooltipBind);
2634              element.unbind(triggers.hide, hideTooltipBind);
2635            };
2636
2637            attrs.$observe( prefix+'Trigger', function ( val ) {
2638              unregisterTriggers();
2639
2640              triggers = getTriggers( val );
2641
2642              if ( triggers.show === triggers.hide ) {
2643                element.bind( triggers.show, toggleTooltipBind );
2644              } else {
2645                element.bind( triggers.show, showTooltipBind );
2646                element.bind( triggers.hide, hideTooltipBind );
2647              }
2648            });
2649
2650            var animation = scope.$eval(attrs[prefix + 'Animation']);
2651            scope.tt_animation = angular.isDefined(animation) ? !!animation : options.animation;
2652
2653            attrs.$observe( prefix+'AppendToBody', function ( val ) {
2654              appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody;
2655            });
2656
2657            // if a tooltip is attached to <body> we need to remove it on
2658            // location change as its parent scope will probably not be destroyed
2659            // by the change.
2660            if ( appendToBody ) {
2661              scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {
2662              if ( scope.tt_isOpen ) {
2663                hide();
2664              }
2665            });
2666            }
2667
2668            // Make sure tooltip is destroyed and removed.
2669            scope.$on('$destroy', function onDestroyTooltip() {
2670              $timeout.cancel( transitionTimeout );
2671              $timeout.cancel( popupTimeout );
2672              unregisterTriggers();
2673              removeTooltip();
2674            });
2675          };
2676        }
2677      };
2678    };
2679  }];
2680})
2681
2682.directive( 'tooltipPopup', function () {
2683  return {
2684    restrict: 'EA',
2685    replace: true,
2686    scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
2687    templateUrl: 'template/tooltip/tooltip-popup.html'
2688  };
2689})
2690
2691.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
2692  return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
2693}])
2694
2695.directive( 'tooltipHtmlUnsafePopup', function () {
2696  return {
2697    restrict: 'EA',
2698    replace: true,
2699    scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
2700    templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
2701  };
2702})
2703
2704.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
2705  return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
2706}]);
2707
2708/**
2709 * The following features are still outstanding: popup delay, animation as a
2710 * function, placement as a function, inside, support for more triggers than
2711 * just mouse enter/leave, html popovers, and selector delegatation.
2712 */
2713angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
2714
2715.directive( 'popoverPopup', function () {
2716  return {
2717    restrict: 'EA',
2718    replace: true,
2719    scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
2720    templateUrl: 'template/popover/popover.html'
2721  };
2722})
2723
2724.directive( 'popover', [ '$tooltip', function ( $tooltip ) {
2725  return $tooltip( 'popover', 'popover', 'click' );
2726}]);
2727
2728angular.module('ui.bootstrap.progressbar', [])
2729
2730.constant('progressConfig', {
2731  animate: true,
2732  max: 100
2733})
2734
2735.controller('ProgressController', ['$scope', '$attrs', 'progressConfig', function($scope, $attrs, progressConfig) {
2736    var self = this,
2737        animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
2738
2739    this.bars = [];
2740    $scope.max = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : progressConfig.max;
2741
2742    this.addBar = function(bar, element) {
2743        if ( !animate ) {
2744            element.css({'transition': 'none'});
2745        }
2746
2747        this.bars.push(bar);
2748
2749        bar.$watch('value', function( value ) {
2750            bar.percent = +(100 * value / $scope.max).toFixed(2);
2751        });
2752
2753        bar.$on('$destroy', function() {
2754            element = null;
2755            self.removeBar(bar);
2756        });
2757    };
2758
2759    this.removeBar = function(bar) {
2760        this.bars.splice(this.bars.indexOf(bar), 1);
2761    };
2762}])
2763
2764.directive('progress', function() {
2765    return {
2766        restrict: 'EA',
2767        replace: true,
2768        transclude: true,
2769        controller: 'ProgressController',
2770        require: 'progress',
2771        scope: {},
2772        templateUrl: 'template/progressbar/progress.html'
2773    };
2774})
2775
2776.directive('bar', function() {
2777    return {
2778        restrict: 'EA',
2779        replace: true,
2780        transclude: true,
2781        require: '^progress',
2782        scope: {
2783            value: '=',
2784            type: '@'
2785        },
2786        templateUrl: 'template/progressbar/bar.html',
2787        link: function(scope, element, attrs, progressCtrl) {
2788            progressCtrl.addBar(scope, element);
2789        }
2790    };
2791})
2792
2793.directive('progressbar', function() {
2794    return {
2795        restrict: 'EA',
2796        replace: true,
2797        transclude: true,
2798        controller: 'ProgressController',
2799        scope: {
2800            value: '=',
2801            type: '@'
2802        },
2803        templateUrl: 'template/progressbar/progressbar.html',
2804        link: function(scope, element, attrs, progressCtrl) {
2805            progressCtrl.addBar(scope, angular.element(element.children()[0]));
2806        }
2807    };
2808});
2809angular.module('ui.bootstrap.rating', [])
2810
2811.constant('ratingConfig', {
2812  max: 5,
2813  stateOn: null,
2814  stateOff: null
2815})
2816
2817.controller('RatingController', ['$scope', '$attrs', 'ratingConfig', function($scope, $attrs, ratingConfig) {
2818  var ngModelCtrl  = { $setViewValue: angular.noop };
2819
2820  this.init = function(ngModelCtrl_) {
2821    ngModelCtrl = ngModelCtrl_;
2822    ngModelCtrl.$render = this.render;
2823
2824    this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
2825    this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
2826
2827    var ratingStates = angular.isDefined($attrs.ratingStates) ? $scope.$parent.$eval($attrs.ratingStates) :
2828                        new Array( angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max );
2829    $scope.range = this.buildTemplateObjects(ratingStates);
2830  };
2831
2832  this.buildTemplateObjects = function(states) {
2833    for (var i = 0, n = states.length; i < n; i++) {
2834      states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff }, states[i]);
2835    }
2836    return states;
2837  };
2838
2839  $scope.rate = function(value) {
2840    if ( !$scope.readonly && value >= 0 && value <= $scope.range.length ) {
2841      ngModelCtrl.$setViewValue(value);
2842      ngModelCtrl.$render();
2843    }
2844  };
2845
2846  $scope.enter = function(value) {
2847    if ( !$scope.readonly ) {
2848      $scope.value = value;
2849    }
2850    $scope.onHover({value: value});
2851  };
2852
2853  $scope.reset = function() {
2854    $scope.value = ngModelCtrl.$viewValue;
2855    $scope.onLeave();
2856  };
2857
2858  $scope.onKeydown = function(evt) {
2859    if (/(37|38|39|40)/.test(evt.which)) {
2860      evt.preventDefault();
2861      evt.stopPropagation();
2862      $scope.rate( $scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1) );
2863    }
2864  };
2865
2866  this.render = function() {
2867    $scope.value = ngModelCtrl.$viewValue;
2868  };
2869}])
2870
2871.directive('rating', function() {
2872  return {
2873    restrict: 'EA',
2874    require: ['rating', 'ngModel'],
2875    scope: {
2876      readonly: '=?',
2877      onHover: '&',
2878      onLeave: '&'
2879    },
2880    controller: 'RatingController',
2881    templateUrl: 'template/rating/rating.html',
2882    replace: true,
2883    link: function(scope, element, attrs, ctrls) {
2884      var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
2885
2886      if ( ngModelCtrl ) {
2887        ratingCtrl.init( ngModelCtrl );
2888      }
2889    }
2890  };
2891});
2892
2893/**
2894 * @ngdoc overview
2895 * @name ui.bootstrap.tabs
2896 *
2897 * @description
2898 * AngularJS version of the tabs directive.
2899 */
2900
2901angular.module('ui.bootstrap.tabs', [])
2902
2903.controller('TabsetController', ['$scope', function TabsetCtrl($scope) {
2904  var ctrl = this,
2905      tabs = ctrl.tabs = $scope.tabs = [];
2906
2907  ctrl.select = function(selectedTab) {
2908    angular.forEach(tabs, function(tab) {
2909      if (tab.active && tab !== selectedTab) {
2910        tab.active = false;
2911        tab.onDeselect();
2912      }
2913    });
2914    selectedTab.active = true;
2915    selectedTab.onSelect();
2916  };
2917
2918  ctrl.addTab = function addTab(tab) {
2919    tabs.push(tab);
2920    // we can't run the select function on the first tab
2921    // since that would select it twice
2922    if (tabs.length === 1) {
2923      tab.active = true;
2924    } else if (tab.active) {
2925      ctrl.select(tab);
2926    }
2927  };
2928
2929  ctrl.removeTab = function removeTab(tab) {
2930    var index = tabs.indexOf(tab);
2931    //Select a new tab if the tab to be removed is selected
2932    if (tab.active && tabs.length > 1) {
2933      //If this is the last tab, select the previous tab. else, the next tab.
2934      var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
2935      ctrl.select(tabs[newActiveIndex]);
2936    }
2937    tabs.splice(index, 1);
2938  };
2939}])
2940
2941/**
2942 * @ngdoc directive
2943 * @name ui.bootstrap.tabs.directive:tabset
2944 * @restrict EA
2945 *
2946 * @description
2947 * Tabset is the outer container for the tabs directive
2948 *
2949 * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
2950 * @param {boolean=} justified Whether or not to use justified styling for the tabs.
2951 *
2952 * @example
2953<example module="ui.bootstrap">
2954  <file name="index.html">
2955    <tabset>
2956      <tab heading="Tab 1"><b>First</b> Content!</tab>
2957      <tab heading="Tab 2"><i>Second</i> Content!</tab>
2958    </tabset>
2959    <hr />
2960    <tabset vertical="true">
2961      <tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>
2962      <tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>
2963    </tabset>
2964    <tabset justified="true">
2965      <tab heading="Justified Tab 1"><b>First</b> Justified Content!</tab>
2966      <tab heading="Justified Tab 2"><i>Second</i> Justified Content!</tab>
2967    </tabset>
2968  </file>
2969</example>
2970 */
2971.directive('tabset', function() {
2972  return {
2973    restrict: 'EA',
2974    transclude: true,
2975    replace: true,
2976    scope: {
2977      type: '@'
2978    },
2979    controller: 'TabsetController',
2980    templateUrl: 'template/tabs/tabset.html',
2981    link: function(scope, element, attrs) {
2982      scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
2983      scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
2984    }
2985  };
2986})
2987
2988/**
2989 * @ngdoc directive
2990 * @name ui.bootstrap.tabs.directive:tab
2991 * @restrict EA
2992 *
2993 * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
2994 * @param {string=} select An expression to evaluate when the tab is selected.
2995 * @param {boolean=} active A binding, telling whether or not this tab is selected.
2996 * @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
2997 *
2998 * @description
2999 * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
3000 *
3001 * @example
3002<example module="ui.bootstrap">
3003  <file name="index.html">
3004    <div ng-controller="TabsDemoCtrl">
3005      <button class="btn btn-small" ng-click="items[0].active = true">
3006        Select item 1, using active binding
3007      </button>
3008      <button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled">
3009        Enable/disable item 2, using disabled binding
3010      </button>
3011      <br />
3012      <tabset>
3013        <tab heading="Tab 1">First Tab</tab>
3014        <tab select="alertMe()">
3015          <tab-heading><i class="icon-bell"></i> Alert me!</tab-heading>
3016          Second Tab, with alert callback and html heading!
3017        </tab>
3018        <tab ng-repeat="item in items"
3019          heading="{{item.title}}"
3020          disabled="item.disabled"
3021          active="item.active">
3022          {{item.content}}
3023        </tab>
3024      </tabset>
3025    </div>
3026  </file>
3027  <file name="script.js">
3028    function TabsDemoCtrl($scope) {
3029      $scope.items = [
3030        { title:"Dynamic Title 1", content:"Dynamic Item 0" },
3031        { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
3032      ];
3033
3034      $scope.alertMe = function() {
3035        setTimeout(function() {
3036          alert("You've selected the alert tab!");
3037        });
3038      };
3039    };
3040  </file>
3041</example>
3042 */
3043
3044/**
3045 * @ngdoc directive
3046 * @name ui.bootstrap.tabs.directive:tabHeading
3047 * @restrict EA
3048 *
3049 * @description
3050 * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
3051 *
3052 * @example
3053<example module="ui.bootstrap">
3054  <file name="index.html">
3055    <tabset>
3056      <tab>
3057        <tab-heading><b>HTML</b> in my titles?!</tab-heading>
3058        And some content, too!
3059      </tab>
3060      <tab>
3061        <tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading>
3062        That's right.
3063      </tab>
3064    </tabset>
3065  </file>
3066</example>
3067 */
3068.directive('tab', ['$parse', function($parse) {
3069  return {
3070    require: '^tabset',
3071    restrict: 'EA',
3072    replace: true,
3073    templateUrl: 'template/tabs/tab.html',
3074    transclude: true,
3075    scope: {
3076      active: '=?',
3077      heading: '@',
3078      onSelect: '&select', //This callback is called in contentHeadingTransclude
3079                          //once it inserts the tab's content into the dom
3080      onDeselect: '&deselect'
3081    },
3082    controller: function() {
3083      //Empty controller so other directives can require being 'under' a tab
3084    },
3085    compile: function(elm, attrs, transclude) {
3086      return function postLink(scope, elm, attrs, tabsetCtrl) {
3087        scope.$watch('active', function(active) {
3088          if (active) {
3089            tabsetCtrl.select(scope);
3090          }
3091        });
3092
3093        scope.disabled = false;
3094        if ( attrs.disabled ) {
3095          scope.$parent.$watch($parse(attrs.disabled), function(value) {
3096            scope.disabled = !! value;
3097          });
3098        }
3099
3100        scope.select = function() {
3101          if ( !scope.disabled ) {
3102            scope.active = true;
3103          }
3104        };
3105
3106        tabsetCtrl.addTab(scope);
3107        scope.$on('$destroy', function() {
3108          tabsetCtrl.removeTab(scope);
3109        });
3110
3111        //We need to transclude later, once the content container is ready.
3112        //when this link happens, we're inside a tab heading.
3113        scope.$transcludeFn = transclude;
3114      };
3115    }
3116  };
3117}])
3118
3119.directive('tabHeadingTransclude', [function() {
3120  return {
3121    restrict: 'A',
3122    require: '^tab',
3123    link: function(scope, elm, attrs, tabCtrl) {
3124      scope.$watch('headingElement', function updateHeadingElement(heading) {
3125        if (heading) {
3126          elm.html('');
3127          elm.append(heading);
3128        }
3129      });
3130    }
3131  };
3132}])
3133
3134.directive('tabContentTransclude', function() {
3135  return {
3136    restrict: 'A',
3137    require: '^tabset',
3138    link: function(scope, elm, attrs) {
3139      var tab = scope.$eval(attrs.tabContentTransclude);
3140
3141      //Now our tab is ready to be transcluded: both the tab heading area
3142      //and the tab content area are loaded.  Transclude 'em both.
3143      tab.$transcludeFn(tab.$parent, function(contents) {
3144        angular.forEach(contents, function(node) {
3145          if (isTabHeading(node)) {
3146            //Let tabHeadingTransclude know.
3147            tab.headingElement = node;
3148          } else {
3149            elm.append(node);
3150          }
3151        });
3152      });
3153    }
3154  };
3155  function isTabHeading(node) {
3156    return node.tagName &&  (
3157      node.hasAttribute('tab-heading') ||
3158      node.hasAttribute('data-tab-heading') ||
3159      node.tagName.toLowerCase() === 'tab-heading' ||
3160      node.tagName.toLowerCase() === 'data-tab-heading'
3161    );
3162  }
3163})
3164
3165;
3166
3167angular.module('ui.bootstrap.timepicker', [])
3168
3169.constant('timepickerConfig', {
3170  hourStep: 1,
3171  minuteStep: 1,
3172  showMeridian: true,
3173  meridians: null,
3174  readonlyInput: false,
3175  mousewheel: true
3176})
3177
3178.controller('TimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'timepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {
3179  var selected = new Date(),
3180      ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
3181      meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
3182
3183  this.init = function( ngModelCtrl_, inputs ) {
3184    ngModelCtrl = ngModelCtrl_;
3185    ngModelCtrl.$render = this.render;
3186
3187    var hoursInputEl = inputs.eq(0),
3188        minutesInputEl = inputs.eq(1);
3189
3190    var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
3191    if ( mousewheel ) {
3192      this.setupMousewheelEvents( hoursInputEl, minutesInputEl );
3193    }
3194
3195    $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
3196    this.setupInputEvents( hoursInputEl, minutesInputEl );
3197  };
3198
3199  var hourStep = timepickerConfig.hourStep;
3200  if ($attrs.hourStep) {
3201    $scope.$parent.$watch($parse($attrs.hourStep), function(value) {
3202      hourStep = parseInt(value, 10);
3203    });
3204  }
3205
3206  var minuteStep = timepickerConfig.minuteStep;
3207  if ($attrs.minuteStep) {
3208    $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
3209      minuteStep = parseInt(value, 10);
3210    });
3211  }
3212
3213  // 12H / 24H mode
3214  $scope.showMeridian = timepickerConfig.showMeridian;
3215  if ($attrs.showMeridian) {
3216    $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
3217      $scope.showMeridian = !!value;
3218
3219      if ( ngModelCtrl.$error.time ) {
3220        // Evaluate from template
3221        var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
3222        if (angular.isDefined( hours ) && angular.isDefined( minutes )) {
3223          selected.setHours( hours );
3224          refresh();
3225        }
3226      } else {
3227        updateTemplate();
3228      }
3229    });
3230  }
3231
3232  // Get $scope.hours in 24H mode if valid
3233  function getHoursFromTemplate ( ) {
3234    var hours = parseInt( $scope.hours, 10 );
3235    var valid = ( $scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
3236    if ( !valid ) {
3237      return undefined;
3238    }
3239
3240    if ( $scope.showMeridian ) {
3241      if ( hours === 12 ) {
3242        hours = 0;
3243      }
3244      if ( $scope.meridian === meridians[1] ) {
3245        hours = hours + 12;
3246      }
3247    }
3248    return hours;
3249  }
3250
3251  function getMinutesFromTemplate() {
3252    var minutes = parseInt($scope.minutes, 10);
3253    return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;
3254  }
3255
3256  function pad( value ) {
3257    return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
3258  }
3259
3260  // Respond on mousewheel spin
3261  this.setupMousewheelEvents = function( hoursInputEl, minutesInputEl ) {
3262    var isScrollingUp = function(e) {
3263      if (e.originalEvent) {
3264        e = e.originalEvent;
3265      }
3266      //pick correct delta variable depending on event
3267      var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
3268      return (e.detail || delta > 0);
3269    };
3270
3271    hoursInputEl.bind('mousewheel wheel', function(e) {
3272      $scope.$apply( (isScrollingUp(e)) ? $scope.incrementHours() : $scope.decrementHours() );
3273      e.preventDefault();
3274    });
3275
3276    minutesInputEl.bind('mousewheel wheel', function(e) {
3277      $scope.$apply( (isScrollingUp(e)) ? $scope.incrementMinutes() : $scope.decrementMinutes() );
3278      e.preventDefault();
3279    });
3280
3281  };
3282
3283  this.setupInputEvents = function( hoursInputEl, minutesInputEl ) {
3284    if ( $scope.readonlyInput ) {
3285      $scope.updateHours = angular.noop;
3286      $scope.updateMinutes = angular.noop;
3287      return;
3288    }
3289
3290    var invalidate = function(invalidHours, invalidMinutes) {
3291      ngModelCtrl.$setViewValue( null );
3292      ngModelCtrl.$setValidity('time', false);
3293      if (angular.isDefined(invalidHours)) {
3294        $scope.invalidHours = invalidHours;
3295      }
3296      if (angular.isDefined(invalidMinutes)) {
3297        $scope.invalidMinutes = invalidMinutes;
3298      }
3299    };
3300
3301    $scope.updateHours = function() {
3302      var hours = getHoursFromTemplate();
3303
3304      if ( angular.isDefined(hours) ) {
3305        selected.setHours( hours );
3306        refresh( 'h' );
3307      } else {
3308        invalidate(true);
3309      }
3310    };
3311
3312    hoursInputEl.bind('blur', function(e) {
3313      if ( !$scope.invalidHours && $scope.hours < 10) {
3314        $scope.$apply( function() {
3315          $scope.hours = pad( $scope.hours );
3316        });
3317      }
3318    });
3319
3320    $scope.updateMinutes = function() {
3321      var minutes = getMinutesFromTemplate();
3322
3323      if ( angular.isDefined(minutes) ) {
3324        selected.setMinutes( minutes );
3325        refresh( 'm' );
3326      } else {
3327        invalidate(undefined, true);
3328      }
3329    };
3330
3331    minutesInputEl.bind('blur', function(e) {
3332      if ( !$scope.invalidMinutes && $scope.minutes < 10 ) {
3333        $scope.$apply( function() {
3334          $scope.minutes = pad( $scope.minutes );
3335        });
3336      }
3337    });
3338
3339  };
3340
3341  this.render = function() {
3342    var date = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : null;
3343
3344    if ( isNaN(date) ) {
3345      ngModelCtrl.$setValidity('time', false);
3346      $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.');
3347    } else {
3348      if ( date ) {
3349        selected = date;
3350      }
3351      makeValid();
3352      updateTemplate();
3353    }
3354  };
3355
3356  // Call internally when we know that model is valid.
3357  function refresh( keyboardChange ) {
3358    makeValid();
3359    ngModelCtrl.$setViewValue( new Date(selected) );
3360    updateTemplate( keyboardChange );
3361  }
3362
3363  function makeValid() {
3364    ngModelCtrl.$setValidity('time', true);
3365    $scope.invalidHours = false;
3366    $scope.invalidMinutes = false;
3367  }
3368
3369  function updateTemplate( keyboardChange ) {
3370    var hours = selected.getHours(), minutes = selected.getMinutes();
3371
3372    if ( $scope.showMeridian ) {
3373      hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system
3374    }
3375
3376    $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
3377    $scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);
3378    $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
3379  }
3380
3381  function addMinutes( minutes ) {
3382    var dt = new Date( selected.getTime() + minutes * 60000 );
3383    selected.setHours( dt.getHours(), dt.getMinutes() );
3384    refresh();
3385  }
3386
3387  $scope.incrementHours = function() {
3388    addMinutes( hourStep * 60 );
3389  };
3390  $scope.decrementHours = function() {
3391    addMinutes( - hourStep * 60 );
3392  };
3393  $scope.incrementMinutes = function() {
3394    addMinutes( minuteStep );
3395  };
3396  $scope.decrementMinutes = function() {
3397    addMinutes( - minuteStep );
3398  };
3399  $scope.toggleMeridian = function() {
3400    addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
3401  };
3402}])
3403
3404.directive('timepicker', function () {
3405  return {
3406    restrict: 'EA',
3407    require: ['timepicker', '?^ngModel'],
3408    controller:'TimepickerController',
3409    replace: true,
3410    scope: {},
3411    templateUrl: 'template/timepicker/timepicker.html',
3412    link: function(scope, element, attrs, ctrls) {
3413      var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
3414
3415      if ( ngModelCtrl ) {
3416        timepickerCtrl.init( ngModelCtrl, element.find('input') );
3417      }
3418    }
3419  };
3420});
3421
3422angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
3423
3424/**
3425 * A helper service that can parse typeahead's syntax (string provided by users)
3426 * Extracted to a separate service for ease of unit testing
3427 */
3428  .factory('typeaheadParser', ['$parse', function ($parse) {
3429
3430  //                      00000111000000000000022200000000000000003333333333333330000000000044000
3431  var TYPEAHEAD_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;
3432
3433  return {
3434    parse:function (input) {
3435
3436      var match = input.match(TYPEAHEAD_REGEXP);
3437      if (!match) {
3438        throw new Error(
3439          'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
3440            ' but got "' + input + '".');
3441      }
3442
3443      return {
3444        itemName:match[3],
3445        source:$parse(match[4]),
3446        viewMapper:$parse(match[2] || match[1]),
3447        modelMapper:$parse(match[1])
3448      };
3449    }
3450  };
3451}])
3452
3453  .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',
3454    function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
3455
3456  var HOT_KEYS = [9, 13, 27, 38, 40];
3457
3458  return {
3459    require:'ngModel',
3460    link:function (originalScope, element, attrs, modelCtrl) {
3461
3462      //SUPPORTED ATTRIBUTES (OPTIONS)
3463
3464      //minimal no of characters that needs to be entered before typeahead kicks-in
3465      var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
3466
3467      //minimal wait time after last character typed before typehead kicks-in
3468      var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
3469
3470      //should it restrict model values to the ones selected from the popup only?
3471      var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
3472
3473      //binding to a variable that indicates if matches are being retrieved asynchronously
3474      var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
3475
3476      //a callback executed when a match is selected
3477      var onSelectCallback = $parse(attrs.typeaheadOnSelect);
3478
3479      var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
3480
3481      var appendToBody =  attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
3482
3483      //INTERNAL VARIABLES
3484
3485      //model setter executed upon match selection
3486      var $setModelValue = $parse(attrs.ngModel).assign;
3487
3488      //expressions used by typeahead
3489      var parserResult = typeaheadParser.parse(attrs.typeahead);
3490
3491      var hasFocus;
3492
3493      //create a child scope for the typeahead directive so we are not polluting original scope
3494      //with typeahead-specific data (matches, query etc.)
3495      var scope = originalScope.$new();
3496      originalScope.$on('$destroy', function(){
3497        scope.$destroy();
3498      });
3499
3500      // WAI-ARIA
3501      var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
3502      element.attr({
3503        'aria-autocomplete': 'list',
3504        'aria-expanded': false,
3505        'aria-owns': popupId
3506      });
3507
3508      //pop-up element used to display matches
3509      var popUpEl = angular.element('<div typeahead-popup></div>');
3510      popUpEl.attr({
3511        id: popupId,
3512        matches: 'matches',
3513        active: 'activeIdx',
3514        select: 'select(activeIdx)',
3515        query: 'query',
3516        position: 'position'
3517      });
3518      //custom item template
3519      if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
3520        popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
3521      }
3522
3523      var resetMatches = function() {
3524        scope.matches = [];
3525        scope.activeIdx = -1;
3526        element.attr('aria-expanded', false);
3527      };
3528
3529      var getMatchId = function(index) {
3530        return popupId + '-option-' + index;
3531      };
3532
3533      // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
3534      // This attribute is added or removed automatically when the `activeIdx` changes.
3535      scope.$watch('activeIdx', function(index) {
3536        if (index < 0) {
3537          element.removeAttr('aria-activedescendant');
3538        } else {
3539          element.attr('aria-activedescendant', getMatchId(index));
3540        }
3541      });
3542
3543      var getMatchesAsync = function(inputValue) {
3544
3545        var locals = {$viewValue: inputValue};
3546        isLoadingSetter(originalScope, true);
3547        $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
3548
3549          //it might happen that several async queries were in progress if a user were typing fast
3550          //but we are interested only in responses that correspond to the current view value
3551          var onCurrentRequest = (inputValue === modelCtrl.$viewValue);
3552          if (onCurrentRequest && hasFocus) {
3553            if (matches.length > 0) {
3554
3555              scope.activeIdx = 0;
3556              scope.matches.length = 0;
3557
3558              //transform labels
3559              for(var i=0; i<matches.length; i++) {
3560                locals[parserResult.itemName] = matches[i];
3561                scope.matches.push({
3562                  id: getMatchId(i),
3563                  label: parserResult.viewMapper(scope, locals),
3564                  model: matches[i]
3565                });
3566              }
3567
3568              scope.query = inputValue;
3569              //position pop-up with matches - we need to re-calculate its position each time we are opening a window
3570              //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
3571              //due to other elements being rendered
3572              scope.position = appendToBody ? $position.offset(element) : $position.position(element);
3573              scope.position.top = scope.position.top + element.prop('offsetHeight');
3574
3575              element.attr('aria-expanded', true);
3576            } else {
3577              resetMatches();
3578            }
3579          }
3580          if (onCurrentRequest) {
3581            isLoadingSetter(originalScope, false);
3582          }
3583        }, function(){
3584          resetMatches();
3585          isLoadingSetter(originalScope, false);
3586        });
3587      };
3588
3589      resetMatches();
3590
3591      //we need to propagate user's query so we can higlight matches
3592      scope.query = undefined;
3593
3594      //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later 
3595      var timeoutPromise;
3596
3597      //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
3598      //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
3599      modelCtrl.$parsers.unshift(function (inputValue) {
3600
3601        hasFocus = true;
3602
3603        if (inputValue && inputValue.length >= minSearch) {
3604          if (waitTime > 0) {
3605            if (timeoutPromise) {
3606              $timeout.cancel(timeoutPromise);//cancel previous timeout
3607            }
3608            timeoutPromise = $timeout(function () {
3609              getMatchesAsync(inputValue);
3610            }, waitTime);
3611          } else {
3612            getMatchesAsync(inputValue);
3613          }
3614        } else {
3615          isLoadingSetter(originalScope, false);
3616          resetMatches();
3617        }
3618
3619        if (isEditable) {
3620          return inputValue;
3621        } else {
3622          if (!inputValue) {
3623            // Reset in case user had typed something previously.
3624            modelCtrl.$setValidity('editable', true);
3625            return inputValue;
3626          } else {
3627            modelCtrl.$setValidity('editable', false);
3628            return undefined;
3629          }
3630        }
3631      });
3632
3633      modelCtrl.$formatters.push(function (modelValue) {
3634
3635        var candidateViewValue, emptyViewValue;
3636        var locals = {};
3637
3638        if (inputFormatter) {
3639
3640          locals['$model'] = modelValue;
3641          return inputFormatter(originalScope, locals);
3642
3643        } else {
3644
3645          //it might happen that we don't have enough info to properly render input value
3646          //we need to check for this situation and simply return model value if we can't apply custom formatting
3647          locals[parserResult.itemName] = modelValue;
3648          candidateViewValue = parserResult.viewMapper(originalScope, locals);
3649          locals[parserResult.itemName] = undefined;
3650          emptyViewValue = parserResult.viewMapper(originalScope, locals);
3651
3652          return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
3653        }
3654      });
3655
3656      scope.select = function (activeIdx) {
3657        //called from within the $digest() cycle
3658        var locals = {};
3659        var model, item;
3660
3661        locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
3662        model = parserResult.modelMapper(originalScope, locals);
3663        $setModelValue(originalScope, model);
3664        modelCtrl.$setValidity('editable', true);
3665
3666        onSelectCallback(originalScope, {
3667          $item: item,
3668          $model: model,
3669          $label: parserResult.viewMapper(originalScope, locals)
3670        });
3671
3672        resetMatches();
3673
3674        //return focus to the input element if a match was selected via a mouse click event
3675        // use timeout to avoid $rootScope:inprog error
3676        $timeout(function() { element[0].focus(); }, 0, false);
3677      };
3678
3679      //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
3680      element.bind('keydown', function (evt) {
3681
3682        //typeahead is open and an "interesting" key was pressed
3683        if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
3684          return;
3685        }
3686
3687        evt.preventDefault();
3688
3689        if (evt.which === 40) {
3690          scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
3691          scope.$digest();
3692
3693        } else if (evt.which === 38) {
3694          scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
3695          scope.$digest();
3696
3697        } else if (evt.which === 13 || evt.which === 9) {
3698          scope.$apply(function () {
3699            scope.select(scope.activeIdx);
3700          });
3701
3702        } else if (evt.which === 27) {
3703          evt.stopPropagation();
3704
3705          resetMatches();
3706          scope.$digest();
3707        }
3708      });
3709
3710      element.bind('blur', function (evt) {
3711        hasFocus = false;
3712      });
3713
3714      // Keep reference to click handler to unbind it.
3715      var dismissClickHandler = function (evt) {
3716        if (element[0] !== evt.target) {
3717          resetMatches();
3718          scope.$digest();
3719        }
3720      };
3721
3722      $document.bind('click', dismissClickHandler);
3723
3724      originalScope.$on('$destroy', function(){
3725        $document.unbind('click', dismissClickHandler);
3726      });
3727
3728      var $popup = $compile(popUpEl)(scope);
3729      if ( appendToBody ) {
3730        $document.find('body').append($popup);
3731      } else {
3732        element.after($popup);
3733      }
3734    }
3735  };
3736
3737}])
3738
3739  .directive('typeaheadPopup', function () {
3740    return {
3741      restrict:'EA',
3742      scope:{
3743        matches:'=',
3744        query:'=',
3745        active:'=',
3746        position:'=',
3747        select:'&'
3748      },
3749      replace:true,
3750      templateUrl:'template/typeahead/typeahead-popup.html',
3751      link:function (scope, element, attrs) {
3752
3753        scope.templateUrl = attrs.templateUrl;
3754
3755        scope.isOpen = function () {
3756          return scope.matches.length > 0;
3757        };
3758
3759        scope.isActive = function (matchIdx) {
3760          return scope.active == matchIdx;
3761        };
3762
3763        scope.selectActive = function (matchIdx) {
3764          scope.active = matchIdx;
3765        };
3766
3767        scope.selectMatch = function (activeIdx) {
3768          scope.select({activeIdx:activeIdx});
3769        };
3770      }
3771    };
3772  })
3773
3774  .directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {
3775    return {
3776      restrict:'EA',
3777      scope:{
3778        index:'=',
3779        match:'=',
3780        query:'='
3781      },
3782      link:function (scope, element, attrs) {
3783        var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';
3784        $http.get(tplUrl, {cache: $templateCache}).success(function(tplContent){
3785           element.replaceWith($compile(tplContent.trim())(scope));
3786        });
3787      }
3788    };
3789  }])
3790
3791  .filter('typeaheadHighlight', function() {
3792
3793    function escapeRegexp(queryToEscape) {
3794      return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
3795    }
3796
3797    return function(matchItem, query) {
3798      return query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;
3799    };
3800  });
3801
3802angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
3803  $templateCache.put("template/accordion/accordion-group.html",
3804    "<div class=\"panel panel-default\">\n" +
3805    "  <div class=\"panel-heading\">\n" +
3806    "    <h4 class=\"panel-title\">\n" +
3807    "      <a class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
3808    "    </h4>\n" +
3809    "  </div>\n" +
3810    "  <div class=\"panel-collapse\" collapse=\"!isOpen\">\n" +
3811    "	  <div class=\"panel-body\" ng-transclude></div>\n" +
3812    "  </div>\n" +
3813    "</div>");
3814}]);
3815
3816angular.module("template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
3817  $templateCache.put("template/accordion/accordion.html",
3818    "<div class=\"panel-group\" ng-transclude></div>");
3819}]);
3820
3821angular.module("template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
3822  $templateCache.put("template/alert/alert.html",
3823    "<div class=\"alert\" ng-class=\"{'alert-{{type || 'warning'}}': true, 'alert-dismissable': closeable}\" role=\"alert\">\n" +
3824    "    <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close()\">\n" +
3825    "        <span aria-hidden=\"true\">&times;</span>\n" +
3826    "        <span class=\"sr-only\">Close</span>\n" +
3827    "    </button>\n" +
3828    "    <div ng-transclude></div>\n" +
3829    "</div>\n" +
3830    "");
3831}]);
3832
3833angular.module("template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
3834  $templateCache.put("template/carousel/carousel.html",
3835    "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
3836    "    <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
3837    "        <li ng-repeat=\"slide in slides track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\n" +
3838    "    </ol>\n" +
3839    "    <div class=\"carousel-inner\" ng-transclude></div>\n" +
3840    "    <a class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-left\"></span></a>\n" +
3841    "    <a class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-right\"></span></a>\n" +
3842    "</div>\n" +
3843    "");
3844}]);
3845
3846angular.module("template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
3847  $templateCache.put("template/carousel/slide.html",
3848    "<div ng-class=\"{\n" +
3849    "    'active': leaving || (active && !entering),\n" +
3850    "    'prev': (next || active) && direction=='prev',\n" +
3851    "    'next': (next || active) && direction=='next',\n" +
3852    "    'right': direction=='prev',\n" +
3853    "    'left': direction=='next'\n" +
3854    "  }\" class=\"item text-center\" ng-transclude></div>\n" +
3855    "");
3856}]);
3857
3858angular.module("template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
3859  $templateCache.put("template/datepicker/datepicker.html",
3860    "<div ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
3861    "  <daypicker ng-switch-when=\"day\" tabindex=\"0\"></daypicker>\n" +
3862    "  <monthpicker ng-switch-when=\"month\" tabindex=\"0\"></monthpicker>\n" +
3863    "  <yearpicker ng-switch-when=\"year\" tabindex=\"0\"></yearpicker>\n" +
3864    "</div>");
3865}]);
3866
3867angular.module("template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
3868  $templateCache.put("template/datepicker/day.html",
3869    "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
3870    "  <thead>\n" +
3871    "    <tr>\n" +
3872    "      <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" +
3873    "      <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()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
3874    "      <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" +
3875    "    </tr>\n" +
3876    "    <tr>\n" +
3877    "      <th ng-show=\"showWeeks\" class=\"text-center\"></th>\n" +
3878    "      <th ng-repeat=\"label in labels track by $index\" class=\"text-center\"><small aria-label=\"{{label.full}}\">{{label.abbr}}</small></th>\n" +
3879    "    </tr>\n" +
3880    "  </thead>\n" +
3881    "  <tbody>\n" +
3882    "    <tr ng-repeat=\"row in rows track by $index\">\n" +
3883    "      <td ng-show=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
3884    "      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
3885    "        <button type=\"button\" style=\"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" +
3886    "      </td>\n" +
3887    "    </tr>\n" +
3888    "  </tbody>\n" +
3889    "</table>\n" +
3890    "");
3891}]);
3892
3893angular.module("template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
3894  $templateCache.put("template/datepicker/month.html",
3895    "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
3896    "  <thead>\n" +
3897    "    <tr>\n" +
3898    "      <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" +
3899    "      <th><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
3900    "      <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" +
3901    "    </tr>\n" +
3902    "  </thead>\n" +
3903    "  <tbody>\n" +
3904    "    <tr ng-repeat=\"row in rows track by $index\">\n" +
3905    "      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
3906    "        <button type=\"button\" style=\"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" +
3907    "      </td>\n" +
3908    "    </tr>\n" +
3909    "  </tbody>\n" +
3910    "</table>\n" +
3911    "");
3912}]);
3913
3914angular.module("template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
3915  $templateCache.put("template/datepicker/popup.html",
3916    "<ul class=\"dropdown-menu\" ng-style=\"{display: (isOpen && 'block') || 'none', top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\">\n" +
3917    "	<li ng-transclude></li>\n" +
3918    "	<li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\n" +
3919    "		<span class=\"btn-group\">\n" +
3920    "			<button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"select('today')\">{{ getText('current') }}</button>\n" +
3921    "			<button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
3922    "		</span>\n" +
3923    "		<button type=\"button\" class=\"btn btn-sm btn-success pull-right\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
3924    "	</li>\n" +
3925    "</ul>\n" +
3926    "");
3927}]);
3928
3929angular.module("template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
3930  $templateCache.put("template/datepicker/year.html",
3931    "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
3932    "  <thead>\n" +
3933    "    <tr>\n" +
3934    "      <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" +
3935    "      <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()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
3936    "      <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" +
3937    "    </tr>\n" +
3938    "  </thead>\n" +
3939    "  <tbody>\n" +
3940    "    <tr ng-repeat=\"row in rows track by $index\">\n" +
3941    "      <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
3942    "        <button type=\"button\" style=\"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" +
3943    "      </td>\n" +
3944    "    </tr>\n" +
3945    "  </tbody>\n" +
3946    "</table>\n" +
3947    "");
3948}]);
3949
3950angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
3951  $templateCache.put("template/modal/backdrop.html",
3952    "<div class=\"modal-backdrop fade\"\n" +
3953    "     ng-class=\"{in: animate}\"\n" +
3954    "     ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
3955    "></div>\n" +
3956    "");
3957}]);
3958
3959angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
3960  $templateCache.put("template/modal/window.html",
3961    "<div tabindex=\"-1\" role=\"dialog\" class=\"modal fade\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
3962    "    <div class=\"modal-dialog\" ng-class=\"{'modal-sm': size == 'sm', 'modal-lg': size == 'lg'}\"><div class=\"modal-content\" ng-transclude></div></div>\n" +
3963    "</div>");
3964}]);
3965
3966angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
3967  $templateCache.put("template/pagination/pager.html",
3968    "<ul class=\"pager\">\n" +
3969    "  <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1)\">{{getText('previous')}}</a></li>\n" +
3970    "  <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1)\">{{getText('next')}}</a></li>\n" +
3971    "</ul>");
3972}]);
3973
3974angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
3975  $templateCache.put("template/pagination/pagination.html",
3976    "<ul class=\"pagination\">\n" +
3977    "  <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(1)\">{{getText('first')}}</a></li>\n" +
3978    "  <li ng-if=\"directionLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(page - 1)\">{{getText('previous')}}</a></li>\n" +
3979    "  <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active}\"><a href ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\n" +
3980    "  <li ng-if=\"directionLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(page + 1)\">{{getText('next')}}</a></li>\n" +
3981    "  <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(totalPages)\">{{getText('last')}}</a></li>\n" +
3982    "</ul>");
3983}]);
3984
3985angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) {
3986  $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html",
3987    "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
3988    "  <div class=\"tooltip-arrow\"></div>\n" +
3989    "  <div class=\"tooltip-inner\" bind-html-unsafe=\"content\"></div>\n" +
3990    "</div>\n" +
3991    "");
3992}]);
3993
3994angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
3995  $templateCache.put("template/tooltip/tooltip-popup.html",
3996    "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
3997    "  <div class=\"tooltip-arrow\"></div>\n" +
3998    "  <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
3999    "</div>\n" +
4000    "");
4001}]);
4002
4003angular.module("template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
4004  $templateCache.put("template/popover/popover.html",
4005    "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
4006    "  <div class=\"arrow\"></div>\n" +
4007    "\n" +
4008    "  <div class=\"popover-inner\">\n" +
4009    "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
4010    "      <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
4011    "  </div>\n" +
4012    "</div>\n" +
4013    "");
4014}]);
4015
4016angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
4017  $templateCache.put("template/progressbar/bar.html",
4018    "<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 + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>");
4019}]);
4020
4021angular.module("template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
4022  $templateCache.put("template/progressbar/progress.html",
4023    "<div class=\"progress\" ng-transclude></div>");
4024}]);
4025
4026angular.module("template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
4027  $templateCache.put("template/progressbar/progressbar.html",
4028    "<div class=\"progress\">\n" +
4029    "  <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 + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\n" +
4030    "</div>");
4031}]);
4032
4033angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
4034  $templateCache.put("template/rating/rating.html",
4035    "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
4036    "    <i ng-repeat=\"r in range track by $index\" 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')\">\n" +
4037    "        <span class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
4038    "    </i>\n" +
4039    "</span>");
4040}]);
4041
4042angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
4043  $templateCache.put("template/tabs/tab.html",
4044    "<li ng-class=\"{active: active, disabled: disabled}\">\n" +
4045    "  <a ng-click=\"select()\" tab-heading-transclude>{{heading}}</a>\n" +
4046    "</li>\n" +
4047    "");
4048}]);
4049
4050angular.module("template/tabs/tabset-titles.html", []).run(["$templateCache", function($templateCache) {
4051  $templateCache.put("template/tabs/tabset-titles.html",
4052    "<ul class=\"nav {{type && 'nav-' + type}}\" ng-class=\"{'nav-stacked': vertical}\">\n" +
4053    "</ul>\n" +
4054    "");
4055}]);
4056
4057angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
4058  $templateCache.put("template/tabs/tabset.html",
4059    "\n" +
4060    "<div>\n" +
4061    "  <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
4062    "  <div class=\"tab-content\">\n" +
4063    "    <div class=\"tab-pane\" \n" +
4064    "         ng-repeat=\"tab in tabs\" \n" +
4065    "         ng-class=\"{active: tab.active}\"\n" +
4066    "         tab-content-transclude=\"tab\">\n" +
4067    "    </div>\n" +
4068    "  </div>\n" +
4069    "</div>\n" +
4070    "");
4071}]);
4072
4073angular.module("template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
4074  $templateCache.put("template/timepicker/timepicker.html",
4075    "<table>\n" +
4076    "	<tbody>\n" +
4077    "		<tr class=\"text-center\">\n" +
4078    "			<td><a ng-click=\"incrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
4079    "			<td>&nbsp;</td>\n" +
4080    "			<td><a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
4081    "			<td ng-show=\"showMeridian\"></td>\n" +
4082    "		</tr>\n" +
4083    "		<tr>\n" +
4084    "			<td style=\"width:50px;\" class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" +
4085    "				<input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
4086    "			</td>\n" +
4087    "			<td>:</td>\n" +
4088    "			<td style=\"width:50px;\" class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
4089    "				<input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
4090    "			</td>\n" +
4091    "			<td ng-show=\"showMeridian\"><button type=\"button\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\n" +
4092    "		</tr>\n" +
4093    "		<tr class=\"text-center\">\n" +
4094    "			<td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
4095    "			<td>&nbsp;</td>\n" +
4096    "			<td><a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
4097    "			<td ng-show=\"showMeridian\"></td>\n" +
4098    "		</tr>\n" +
4099    "	</tbody>\n" +
4100    "</table>\n" +
4101    "");
4102}]);
4103
4104angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
4105  $templateCache.put("template/typeahead/typeahead-match.html",
4106    "<a tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>");
4107}]);
4108
4109angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
4110  $templateCache.put("template/typeahead/typeahead-popup.html",
4111    "<ul class=\"dropdown-menu\" ng-if=\"isOpen()\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
4112    "    <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" +
4113    "        <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
4114    "    </li>\n" +
4115    "</ul>");
4116}]);

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.