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