angular-ui-bootstrap-rails 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b7cb71f6327665bf4f94b11f53fc7e7d748b950
4
- data.tar.gz: b88e861df141341be7a189fc57f021632e0411c5
3
+ metadata.gz: f1ee13223e10e01488c80f62c38379b74ba8356f
4
+ data.tar.gz: 739f975e2c2127906e92f7f7e0874fe8f860c152
5
5
  SHA512:
6
- metadata.gz: 2f26ca8e2a39eec83605370bdd58d139b3a4fac57bfe51ffffa45ed8c0180d80391e3b2c94823ad14ceb5e65af81b30d641bc7ff3dd1797395a2622ba20673aa
7
- data.tar.gz: 1e3d8de8fbe481a6ca76bc2eb5f109df033d36fa2afceb54757e6b83c16bd7d41eff3a5e622d9865ebfad4f56e254c1fc0281fa47fff6fb62b49f47ee325b4fb
6
+ metadata.gz: bf506d4e2f2a5a15d9734806559796fcfadcbf1e798f39b2e1924a8e36cc6648a880fe0832ee75cd48a4fe5db0f3518d4701b6c459314e1342a8e4ba4de7df61
7
+ data.tar.gz: 81bdc9fb3544c92f857ac6308ab304dcbcfa6ce7153f6392f0eceb3557e7ecb5b96f5e4e903d982c2e1e658235973d9a99e74a210caeef197bba325260e8f84f
@@ -1,7 +1,7 @@
1
1
  module AngularUI
2
2
  module Bootstrap
3
3
  module Rails
4
- VERSION = "0.12.1"
4
+ VERSION = "0.13.0"
5
5
  end
6
6
  end
7
7
  end
@@ -2,155 +2,47 @@
2
2
  * angular-ui-bootstrap
3
3
  * http://angular-ui.github.io/bootstrap/
4
4
 
5
- * Version: 0.12.1 - 2015-02-20
5
+ * Version: 0.13.0 - 2015-05-02
6
6
  * License: MIT
7
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.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
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/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]);
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
- });
8
+ angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.transition","ui.bootstrap.typeahead"]);
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/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-popup.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/tooltip/tooltip-template-popup.html","template/popover/popover-template.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]);
10
+ angular.module('ui.bootstrap.collapse', [])
53
11
 
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) {
12
+ .directive('collapse', ['$animate', function ($animate) {
96
13
 
97
14
  return {
98
15
  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
16
  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
- }
17
+ element.removeClass('collapse').addClass('collapsing');
18
+ $animate.addClass(element, 'in', {
19
+ to: { height: element[0].scrollHeight + 'px' }
20
+ }).then(expandDone);
128
21
  }
129
22
 
130
23
  function expandDone() {
131
24
  element.removeClass('collapsing');
132
- element.addClass('collapse in');
133
25
  element.css({height: 'auto'});
134
26
  }
135
27
 
136
28
  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
- }
29
+ element
30
+ // IMPORTANT: The height must be set before adding "collapsing" class.
31
+ // Otherwise, the browser attempts to animate from height 0 (in
32
+ // collapsing class) to the given height here.
33
+ .css({height: element[0].scrollHeight + 'px'})
34
+ // initially all panel collapse have the collapse class, this removal
35
+ // prevents the animation from jumping to collapsed state
36
+ .removeClass('collapse')
37
+ .addClass('collapsing');
38
+
39
+ $animate.removeClass(element, 'in', {
40
+ to: {height: '0'}
41
+ }).then(collapseDone);
151
42
  }
152
43
 
153
44
  function collapseDone() {
45
+ element.css({height: '0'}); // Required so that collapse works when animation is disabled
154
46
  element.removeClass('collapsing');
155
47
  element.addClass('collapse');
156
48
  }
@@ -272,7 +164,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
272
164
  // Pass the heading to the accordion-group controller
273
165
  // so that it can be transcluded into the right place in the template
274
166
  // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
275
- accordionGroupCtrl.setHeading(transclude(scope, function() {}));
167
+ accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
276
168
  }
277
169
  };
278
170
  })
@@ -295,7 +187,9 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
295
187
  });
296
188
  }
297
189
  };
298
- });
190
+ })
191
+
192
+ ;
299
193
 
300
194
  angular.module('ui.bootstrap.alert', [])
301
195
 
@@ -422,8 +316,8 @@ angular.module('ui.bootstrap.buttons', [])
422
316
  * AngularJS version of an image carousel.
423
317
  *
424
318
  */
425
- angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
426
- .controller('CarouselController', ['$scope', '$timeout', '$interval', '$transition', function ($scope, $timeout, $interval, $transition) {
319
+ angular.module('ui.bootstrap.carousel', [])
320
+ .controller('CarouselController', ['$scope', '$interval', '$animate', function ($scope, $interval, $animate) {
427
321
  var self = this,
428
322
  slides = self.slides = $scope.slides = [],
429
323
  currentIndex = -1,
@@ -433,82 +327,76 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
433
327
  var destroyed = false;
434
328
  /* direction: "prev" or "next" */
435
329
  self.select = $scope.select = function(nextSlide, direction) {
436
- var nextIndex = slides.indexOf(nextSlide);
330
+ var nextIndex = self.indexOfSlide(nextSlide);
437
331
  //Decide direction if it's not given
438
332
  if (direction === undefined) {
439
- direction = nextIndex > currentIndex ? 'next' : 'prev';
333
+ direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
440
334
  }
441
335
  if (nextSlide && nextSlide !== self.currentSlide) {
442
- if ($scope.$currentTransition) {
443
- $scope.$currentTransition.cancel();
444
- //Timeout so ng-class in template has time to fix classes for finished slide
445
- $timeout(goNext);
446
- } else {
447
- goNext();
448
- }
336
+ goNext();
449
337
  }
450
338
  function goNext() {
451
339
  // Scope has been destroyed, stop here.
452
340
  if (destroyed) { return; }
453
- //If we have a slide to transition from and we have a transition type and we're allowed, go
454
- if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
455
- //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
456
- nextSlide.$element.addClass(direction);
457
- var reflow = nextSlide.$element[0].offsetWidth; //force reflow
458
-
459
- //Set all other slides to stop doing their stuff for the new transition
460
- angular.forEach(slides, function(slide) {
461
- angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});
341
+
342
+ angular.extend(nextSlide, {direction: direction, active: true});
343
+ angular.extend(self.currentSlide || {}, {direction: direction, active: false});
344
+ if ($animate.enabled() && !$scope.noTransition && nextSlide.$element) {
345
+ $scope.$currentTransition = true;
346
+ nextSlide.$element.one('$animate:close', function closeFn() {
347
+ $scope.$currentTransition = null;
462
348
  });
463
- angular.extend(nextSlide, {direction: direction, active: true, entering: true});
464
- angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});
465
-
466
- $scope.$currentTransition = $transition(nextSlide.$element, {});
467
- //We have to create new pointers inside a closure since next & current will change
468
- (function(next,current) {
469
- $scope.$currentTransition.then(
470
- function(){ transitionDone(next, current); },
471
- function(){ transitionDone(next, current); }
472
- );
473
- }(nextSlide, self.currentSlide));
474
- } else {
475
- transitionDone(nextSlide, self.currentSlide);
476
349
  }
350
+
477
351
  self.currentSlide = nextSlide;
478
352
  currentIndex = nextIndex;
479
353
  //every time you change slides, reset the timer
480
354
  restartTimer();
481
355
  }
482
- function transitionDone(next, current) {
483
- angular.extend(next, {direction: '', active: true, leaving: false, entering: false});
484
- angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});
485
- $scope.$currentTransition = null;
486
- }
487
356
  };
488
357
  $scope.$on('$destroy', function () {
489
358
  destroyed = true;
490
359
  });
491
360
 
361
+ function getSlideByIndex(index) {
362
+ if (angular.isUndefined(slides[index].index)) {
363
+ return slides[index];
364
+ }
365
+ var i, len = slides.length;
366
+ for (i = 0; i < slides.length; ++i) {
367
+ if (slides[i].index == index) {
368
+ return slides[i];
369
+ }
370
+ }
371
+ }
372
+
373
+ self.getCurrentIndex = function() {
374
+ if (self.currentSlide && angular.isDefined(self.currentSlide.index)) {
375
+ return +self.currentSlide.index;
376
+ }
377
+ return currentIndex;
378
+ };
379
+
492
380
  /* Allow outside people to call indexOf on slides array */
493
381
  self.indexOfSlide = function(slide) {
494
- return slides.indexOf(slide);
382
+ return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
495
383
  };
496
384
 
497
385
  $scope.next = function() {
498
- var newIndex = (currentIndex + 1) % slides.length;
386
+ var newIndex = (self.getCurrentIndex() + 1) % slides.length;
499
387
 
500
388
  //Prevent this user-triggered transition from occurring if there is already one in progress
501
389
  if (!$scope.$currentTransition) {
502
- return self.select(slides[newIndex], 'next');
390
+ return self.select(getSlideByIndex(newIndex), 'next');
503
391
  }
504
392
  };
505
393
 
506
394
  $scope.prev = function() {
507
- var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
395
+ var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
508
396
 
509
397
  //Prevent this user-triggered transition from occurring if there is already one in progress
510
398
  if (!$scope.$currentTransition) {
511
- return self.select(slides[newIndex], 'prev');
399
+ return self.select(getSlideByIndex(newIndex), 'prev');
512
400
  }
513
401
  };
514
402
 
@@ -571,6 +459,11 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
571
459
  };
572
460
 
573
461
  self.removeSlide = function(slide) {
462
+ if (angular.isDefined(slide.index)) {
463
+ slides.sort(function(a, b) {
464
+ return +a.index > +b.index;
465
+ });
466
+ }
574
467
  //get the index of the slide inside the carousel
575
468
  var index = slides.indexOf(slide);
576
469
  slides.splice(index, 1);
@@ -650,13 +543,14 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
650
543
  * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element.
651
544
  *
652
545
  * @param {boolean=} active Model binding, whether or not this slide is currently active.
546
+ * @param {number=} index The index of the slide. The slides will be sorted by this parameter.
653
547
  *
654
548
  * @example
655
549
  <example module="ui.bootstrap">
656
550
  <file name="index.html">
657
551
  <div ng-controller="CarouselDemoCtrl">
658
552
  <carousel>
659
- <slide ng-repeat="slide in slides" active="slide.active">
553
+ <slide ng-repeat="slide in slides" active="slide.active" index="$index">
660
554
  <img ng-src="{{slide.image}}" style="margin:auto;">
661
555
  <div class="carousel-caption">
662
556
  <h4>Slide {{$index}}</h4>
@@ -690,7 +584,8 @@ function CarouselDemoCtrl($scope) {
690
584
  replace: true,
691
585
  templateUrl: 'template/carousel/slide.html',
692
586
  scope: {
693
- active: '=?'
587
+ active: '=?',
588
+ index: '=?'
694
589
  },
695
590
  link: function (scope, element, attrs, carouselCtrl) {
696
591
  carouselCtrl.addSlide(scope, element);
@@ -706,11 +601,64 @@ function CarouselDemoCtrl($scope) {
706
601
  });
707
602
  }
708
603
  };
709
- });
604
+ })
605
+
606
+ .animation('.item', [
607
+ '$animate',
608
+ function ($animate) {
609
+ return {
610
+ beforeAddClass: function (element, className, done) {
611
+ // Due to transclusion, noTransition property is on parent's scope
612
+ if (className == 'active' && element.parent() &&
613
+ !element.parent().scope().noTransition) {
614
+ var stopped = false;
615
+ var direction = element.isolateScope().direction;
616
+ var directionClass = direction == 'next' ? 'left' : 'right';
617
+ element.addClass(direction);
618
+ $animate.addClass(element, directionClass).then(function () {
619
+ if (!stopped) {
620
+ element.removeClass(directionClass + ' ' + direction);
621
+ }
622
+ done();
623
+ });
624
+
625
+ return function () {
626
+ stopped = true;
627
+ };
628
+ }
629
+ done();
630
+ },
631
+ beforeRemoveClass: function (element, className, done) {
632
+ // Due to transclusion, noTransition property is on parent's scope
633
+ if (className == 'active' && element.parent() &&
634
+ !element.parent().scope().noTransition) {
635
+ var stopped = false;
636
+ var direction = element.isolateScope().direction;
637
+ var directionClass = direction == 'next' ? 'left' : 'right';
638
+ $animate.addClass(element, directionClass).then(function () {
639
+ if (!stopped) {
640
+ element.removeClass(directionClass);
641
+ }
642
+ done();
643
+ });
644
+ return function () {
645
+ stopped = true;
646
+ };
647
+ }
648
+ done();
649
+ }
650
+ };
651
+
652
+ }])
653
+
654
+
655
+ ;
710
656
 
711
657
  angular.module('ui.bootstrap.dateparser', [])
712
658
 
713
659
  .service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {
660
+ // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
661
+ var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
714
662
 
715
663
  this.parsers = {};
716
664
 
@@ -756,6 +704,34 @@ angular.module('ui.bootstrap.dateparser', [])
756
704
  },
757
705
  'EEE': {
758
706
  regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
707
+ },
708
+ 'HH': {
709
+ regex: '(?:0|1)[0-9]|2[0-3]',
710
+ apply: function(value) { this.hours = +value; }
711
+ },
712
+ 'H': {
713
+ regex: '1?[0-9]|2[0-3]',
714
+ apply: function(value) { this.hours = +value; }
715
+ },
716
+ 'mm': {
717
+ regex: '[0-5][0-9]',
718
+ apply: function(value) { this.minutes = +value; }
719
+ },
720
+ 'm': {
721
+ regex: '[0-9]|[1-5][0-9]',
722
+ apply: function(value) { this.minutes = +value; }
723
+ },
724
+ 'sss': {
725
+ regex: '[0-9][0-9][0-9]',
726
+ apply: function(value) { this.milliseconds = +value; }
727
+ },
728
+ 'ss': {
729
+ regex: '[0-5][0-9]',
730
+ apply: function(value) { this.seconds = +value; }
731
+ },
732
+ 's': {
733
+ regex: '[0-9]|[1-5][0-9]',
734
+ apply: function(value) { this.seconds = +value; }
759
735
  }
760
736
  };
761
737
 
@@ -786,12 +762,13 @@ angular.module('ui.bootstrap.dateparser', [])
786
762
  };
787
763
  }
788
764
 
789
- this.parse = function(input, format) {
765
+ this.parse = function(input, format, baseDate) {
790
766
  if ( !angular.isString(input) || !format ) {
791
767
  return input;
792
768
  }
793
769
 
794
770
  format = $locale.DATETIME_FORMATS[format] || format;
771
+ format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
795
772
 
796
773
  if ( !this.parsers[format] ) {
797
774
  this.parsers[format] = createParser(format);
@@ -803,7 +780,20 @@ angular.module('ui.bootstrap.dateparser', [])
803
780
  results = input.match(regex);
804
781
 
805
782
  if ( results && results.length ) {
806
- var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt;
783
+ var fields, dt;
784
+ if (baseDate) {
785
+ fields = {
786
+ year: baseDate.getFullYear(),
787
+ month: baseDate.getMonth(),
788
+ date: baseDate.getDate(),
789
+ hours: baseDate.getHours(),
790
+ minutes: baseDate.getMinutes(),
791
+ seconds: baseDate.getSeconds(),
792
+ milliseconds: baseDate.getMilliseconds()
793
+ };
794
+ } else {
795
+ fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
796
+ }
807
797
 
808
798
  for( var i = 1, n = results.length; i < n; i++ ) {
809
799
  var mapper = map[i-1];
@@ -813,7 +803,8 @@ angular.module('ui.bootstrap.dateparser', [])
813
803
  }
814
804
 
815
805
  if ( isValid(fields.year, fields.month, fields.date) ) {
816
- dt = new Date( fields.year, fields.month, fields.date, fields.hours);
806
+ dt = new Date(fields.year, fields.month, fields.date, fields.hours, fields.minutes, fields.seconds,
807
+ fields.milliseconds || 0);
817
808
  }
818
809
 
819
810
  return dt;
@@ -823,6 +814,10 @@ angular.module('ui.bootstrap.dateparser', [])
823
814
  // Check if date is valid for specific month (and year for February).
824
815
  // Month: 0 = Jan, 1 = Feb, etc
825
816
  function isValid(year, month, date) {
817
+ if (date < 1) {
818
+ return false;
819
+ }
820
+
826
821
  if ( month === 1 && date > 28) {
827
822
  return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
828
823
  }
@@ -1004,7 +999,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1004
999
  startingDay: 0,
1005
1000
  yearRange: 20,
1006
1001
  minDate: null,
1007
- maxDate: null
1002
+ maxDate: null,
1003
+ shortcutPropagation: false
1008
1004
  })
1009
1005
 
1010
1006
  .controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
@@ -1016,7 +1012,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1016
1012
 
1017
1013
  // Configuration attributes
1018
1014
  angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
1019
- 'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {
1015
+ 'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function( key, index ) {
1020
1016
  self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
1021
1017
  });
1022
1018
 
@@ -1033,8 +1029,20 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1033
1029
  });
1034
1030
 
1035
1031
  $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
1032
+ $scope.maxMode = self.maxMode;
1036
1033
  $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
1037
- this.activeDate = angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date();
1034
+
1035
+ if(angular.isDefined($attrs.initDate)) {
1036
+ this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date();
1037
+ $scope.$parent.$watch($attrs.initDate, function(initDate){
1038
+ if(initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)){
1039
+ self.activeDate = initDate;
1040
+ self.refreshView();
1041
+ }
1042
+ });
1043
+ } else {
1044
+ this.activeDate = new Date();
1045
+ }
1038
1046
 
1039
1047
  $scope.isActive = function(dateObject) {
1040
1048
  if (self.compare(dateObject.date, self.activeDate) === 0) {
@@ -1053,8 +1061,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1053
1061
  };
1054
1062
 
1055
1063
  this.render = function() {
1056
- if ( ngModelCtrl.$modelValue ) {
1057
- var date = new Date( ngModelCtrl.$modelValue ),
1064
+ if ( ngModelCtrl.$viewValue ) {
1065
+ var date = new Date( ngModelCtrl.$viewValue ),
1058
1066
  isValid = !isNaN(date);
1059
1067
 
1060
1068
  if ( isValid ) {
@@ -1071,19 +1079,20 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1071
1079
  if ( this.element ) {
1072
1080
  this._refreshView();
1073
1081
 
1074
- var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
1082
+ var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
1075
1083
  ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
1076
1084
  }
1077
1085
  };
1078
1086
 
1079
1087
  this.createDateObject = function(date, format) {
1080
- var model = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
1088
+ var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
1081
1089
  return {
1082
1090
  date: date,
1083
1091
  label: dateFilter(date, format),
1084
1092
  selected: model && this.compare(date, model) === 0,
1085
1093
  disabled: this.isDisabled(date),
1086
- current: this.compare(date, new Date()) === 0
1094
+ current: this.compare(date, new Date()) === 0,
1095
+ customClass: this.customClass(date)
1087
1096
  };
1088
1097
  };
1089
1098
 
@@ -1091,6 +1100,10 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1091
1100
  return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));
1092
1101
  };
1093
1102
 
1103
+ this.customClass = function( date ) {
1104
+ return $scope.customClass({date: date, mode: $scope.datepickerMode});
1105
+ };
1106
+
1094
1107
  // Split array into smaller arrays
1095
1108
  this.split = function(arr, size) {
1096
1109
  var arrays = [];
@@ -1102,7 +1115,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1102
1115
 
1103
1116
  $scope.select = function( date ) {
1104
1117
  if ( $scope.datepickerMode === self.minMode ) {
1105
- var dt = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
1118
+ var dt = ngModelCtrl.$viewValue ? new Date( ngModelCtrl.$viewValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
1106
1119
  dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
1107
1120
  ngModelCtrl.$setViewValue( dt );
1108
1121
  ngModelCtrl.$render();
@@ -1149,7 +1162,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1149
1162
  }
1150
1163
 
1151
1164
  evt.preventDefault();
1152
- evt.stopPropagation();
1165
+ if(!self.shortcutPropagation){
1166
+ evt.stopPropagation();
1167
+ }
1153
1168
 
1154
1169
  if (key === 'enter' || key === 'space') {
1155
1170
  if ( self.isDisabled(self.activeDate)) {
@@ -1174,7 +1189,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1174
1189
  templateUrl: 'template/datepicker/datepicker.html',
1175
1190
  scope: {
1176
1191
  datepickerMode: '=?',
1177
- dateDisabled: '&'
1192
+ dateDisabled: '&',
1193
+ customClass: '&',
1194
+ shortcutPropagation: '&?'
1178
1195
  },
1179
1196
  require: ['datepicker', '?^ngModel'],
1180
1197
  controller: 'DatepickerController',
@@ -1249,9 +1266,12 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1249
1266
 
1250
1267
  if ( scope.showWeeks ) {
1251
1268
  scope.weekNumbers = [];
1252
- var weekNumber = getISO8601WeekNumber( scope.rows[0][0].date ),
1269
+ var thursdayIndex = (4 + 7 - ctrl.startingDay) % 7,
1253
1270
  numWeeks = scope.rows.length;
1254
- while( scope.weekNumbers.push(weekNumber++) < numWeeks ) {}
1271
+ for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
1272
+ scope.weekNumbers.push(
1273
+ getISO8601WeekNumber( scope.rows[curWeek][thursdayIndex].date ));
1274
+ }
1255
1275
  }
1256
1276
  };
1257
1277
 
@@ -1412,6 +1432,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1412
1432
 
1413
1433
  .constant('datepickerPopupConfig', {
1414
1434
  datepickerPopup: 'yyyy-MM-dd',
1435
+ html5Types: {
1436
+ date: 'yyyy-MM-dd',
1437
+ 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
1438
+ 'month': 'yyyy-MM'
1439
+ },
1415
1440
  currentText: 'Today',
1416
1441
  clearText: 'Clear',
1417
1442
  closeText: 'Done',
@@ -1430,7 +1455,8 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1430
1455
  currentText: '@',
1431
1456
  clearText: '@',
1432
1457
  closeText: '@',
1433
- dateDisabled: '&'
1458
+ dateDisabled: '&',
1459
+ customClass: '&'
1434
1460
  },
1435
1461
  link: function(scope, element, attrs, ngModel) {
1436
1462
  var dateFormat,
@@ -1443,10 +1469,34 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1443
1469
  return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
1444
1470
  };
1445
1471
 
1446
- attrs.$observe('datepickerPopup', function(value) {
1447
- dateFormat = value || datepickerPopupConfig.datepickerPopup;
1448
- ngModel.$render();
1449
- });
1472
+ var isHtml5DateInput = false;
1473
+ if (datepickerPopupConfig.html5Types[attrs.type]) {
1474
+ dateFormat = datepickerPopupConfig.html5Types[attrs.type];
1475
+ isHtml5DateInput = true;
1476
+ } else {
1477
+ dateFormat = attrs.datepickerPopup || datepickerPopupConfig.datepickerPopup;
1478
+ attrs.$observe('datepickerPopup', function(value, oldValue) {
1479
+ var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
1480
+ // Invalidate the $modelValue to ensure that formatters re-run
1481
+ // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
1482
+ if (newDateFormat !== dateFormat) {
1483
+ dateFormat = newDateFormat;
1484
+ ngModel.$modelValue = null;
1485
+
1486
+ if (!dateFormat) {
1487
+ throw new Error('datepickerPopup must have a date format specified.');
1488
+ }
1489
+ }
1490
+ });
1491
+ }
1492
+
1493
+ if (!dateFormat) {
1494
+ throw new Error('datepickerPopup must have a date format specified.');
1495
+ }
1496
+
1497
+ if (isHtml5DateInput && attrs.datepickerPopup) {
1498
+ throw new Error('HTML5 date input types do not support custom formats.');
1499
+ }
1450
1500
 
1451
1501
  // popup element used to display calendar
1452
1502
  var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
@@ -1461,14 +1511,27 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1461
1511
 
1462
1512
  // datepicker element
1463
1513
  var datepickerEl = angular.element(popupEl.children()[0]);
1514
+ if (isHtml5DateInput) {
1515
+ if (attrs.type == 'month') {
1516
+ datepickerEl.attr('datepicker-mode', '"month"');
1517
+ datepickerEl.attr('min-mode', 'month');
1518
+ }
1519
+ }
1520
+
1464
1521
  if ( attrs.datepickerOptions ) {
1465
- angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function( value, option ) {
1522
+ var options = scope.$parent.$eval(attrs.datepickerOptions);
1523
+ if(options.initDate) {
1524
+ scope.initDate = options.initDate;
1525
+ datepickerEl.attr( 'init-date', 'initDate' );
1526
+ delete options.initDate;
1527
+ }
1528
+ angular.forEach(options, function( value, option ) {
1466
1529
  datepickerEl.attr( cameltoDash(option), value );
1467
1530
  });
1468
1531
  }
1469
1532
 
1470
1533
  scope.watchData = {};
1471
- angular.forEach(['minDate', 'maxDate', 'datepickerMode'], function( key ) {
1534
+ angular.forEach(['minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function( key ) {
1472
1535
  if ( attrs[key] ) {
1473
1536
  var getAttribute = $parse(attrs[key]);
1474
1537
  scope.$parent.$watch(getAttribute, function(value){
@@ -1491,36 +1554,78 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1491
1554
  datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
1492
1555
  }
1493
1556
 
1557
+ if (attrs.showWeeks) {
1558
+ datepickerEl.attr('show-weeks', attrs.showWeeks);
1559
+ }
1560
+
1561
+ if (attrs.customClass){
1562
+ datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
1563
+ }
1564
+
1494
1565
  function parseDate(viewValue) {
1566
+ if (angular.isNumber(viewValue)) {
1567
+ // presumably timestamp to date object
1568
+ viewValue = new Date(viewValue);
1569
+ }
1570
+
1495
1571
  if (!viewValue) {
1496
- ngModel.$setValidity('date', true);
1497
1572
  return null;
1498
1573
  } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
1499
- ngModel.$setValidity('date', true);
1500
1574
  return viewValue;
1501
1575
  } else if (angular.isString(viewValue)) {
1502
- var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);
1576
+ var date = dateParser.parse(viewValue, dateFormat, scope.date) || new Date(viewValue);
1503
1577
  if (isNaN(date)) {
1504
- ngModel.$setValidity('date', false);
1505
1578
  return undefined;
1506
1579
  } else {
1507
- ngModel.$setValidity('date', true);
1508
1580
  return date;
1509
1581
  }
1510
1582
  } else {
1511
- ngModel.$setValidity('date', false);
1512
1583
  return undefined;
1513
1584
  }
1514
1585
  }
1515
- ngModel.$parsers.unshift(parseDate);
1586
+
1587
+ function validator(modelValue, viewValue) {
1588
+ var value = modelValue || viewValue;
1589
+ if (angular.isNumber(value)) {
1590
+ value = new Date(value);
1591
+ }
1592
+ if (!value) {
1593
+ return true;
1594
+ } else if (angular.isDate(value) && !isNaN(value)) {
1595
+ return true;
1596
+ } else if (angular.isString(value)) {
1597
+ var date = dateParser.parse(value, dateFormat) || new Date(value);
1598
+ return !isNaN(date);
1599
+ } else {
1600
+ return false;
1601
+ }
1602
+ }
1603
+
1604
+ if (!isHtml5DateInput) {
1605
+ // Internal API to maintain the correct ng-invalid-[key] class
1606
+ ngModel.$$parserName = 'date';
1607
+ ngModel.$validators.date = validator;
1608
+ ngModel.$parsers.unshift(parseDate);
1609
+ ngModel.$formatters.push(function (value) {
1610
+ scope.date = value;
1611
+ return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
1612
+ });
1613
+ }
1614
+ else {
1615
+ ngModel.$formatters.push(function (value) {
1616
+ scope.date = value;
1617
+ return value;
1618
+ });
1619
+ }
1516
1620
 
1517
1621
  // Inner change
1518
1622
  scope.dateSelection = function(dt) {
1519
1623
  if (angular.isDefined(dt)) {
1520
1624
  scope.date = dt;
1521
1625
  }
1522
- ngModel.$setViewValue(scope.date);
1523
- ngModel.$render();
1626
+ var date = scope.date ? dateFilter(scope.date, dateFormat) : '';
1627
+ element.val(date);
1628
+ ngModel.$setViewValue(date);
1524
1629
 
1525
1630
  if ( closeOnDateSelection ) {
1526
1631
  scope.isOpen = false;
@@ -1528,19 +1633,11 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1528
1633
  }
1529
1634
  };
1530
1635
 
1531
- element.bind('input change keyup', function() {
1532
- scope.$apply(function() {
1533
- scope.date = ngModel.$modelValue;
1534
- });
1636
+ // Detect changes in the view from the text box
1637
+ ngModel.$viewChangeListeners.push(function () {
1638
+ scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date) || new Date(ngModel.$viewValue);
1535
1639
  });
1536
1640
 
1537
- // Outter change
1538
- ngModel.$render = function() {
1539
- var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
1540
- element.val(date);
1541
- scope.date = parseDate( ngModel.$modelValue );
1542
- };
1543
-
1544
1641
  var documentClickBind = function(event) {
1545
1642
  if (scope.isOpen && event.target !== element[0]) {
1546
1643
  scope.$apply(function() {
@@ -1557,7 +1654,9 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1557
1654
  scope.keydown = function(evt) {
1558
1655
  if (evt.which === 27) {
1559
1656
  evt.preventDefault();
1560
- evt.stopPropagation();
1657
+ if (scope.isOpen) {
1658
+ evt.stopPropagation();
1659
+ }
1561
1660
  scope.close();
1562
1661
  } else if (evt.which === 40 && !scope.isOpen) {
1563
1662
  scope.isOpen = true;
@@ -1579,8 +1678,8 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1579
1678
  scope.select = function( date ) {
1580
1679
  if (date === 'today') {
1581
1680
  var today = new Date();
1582
- if (angular.isDate(ngModel.$modelValue)) {
1583
- date = new Date(ngModel.$modelValue);
1681
+ if (angular.isDate(scope.date)) {
1682
+ date = new Date(scope.date);
1584
1683
  date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
1585
1684
  } else {
1586
1685
  date = new Date(today.setHours(0, 0, 0, 0));
@@ -1628,13 +1727,13 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1628
1727
  };
1629
1728
  });
1630
1729
 
1631
- angular.module('ui.bootstrap.dropdown', [])
1730
+ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1632
1731
 
1633
1732
  .constant('dropdownConfig', {
1634
1733
  openClass: 'open'
1635
1734
  })
1636
1735
 
1637
- .service('dropdownService', ['$document', function($document) {
1736
+ .service('dropdownService', ['$document', '$rootScope', function($document, $rootScope) {
1638
1737
  var openScope = null;
1639
1738
 
1640
1739
  this.open = function( dropdownScope ) {
@@ -1663,14 +1762,23 @@ angular.module('ui.bootstrap.dropdown', [])
1663
1762
  // unbound this event handler. So check openScope before proceeding.
1664
1763
  if (!openScope) { return; }
1665
1764
 
1765
+ if( evt && openScope.getAutoClose() === 'disabled' ) { return ; }
1766
+
1666
1767
  var toggleElement = openScope.getToggleElement();
1667
1768
  if ( evt && toggleElement && toggleElement[0].contains(evt.target) ) {
1668
1769
  return;
1669
1770
  }
1670
1771
 
1671
- openScope.$apply(function() {
1672
- openScope.isOpen = false;
1673
- });
1772
+ var $element = openScope.getElement();
1773
+ if( evt && openScope.getAutoClose() === 'outsideClick' && $element && $element[0].contains(evt.target) ) {
1774
+ return;
1775
+ }
1776
+
1777
+ openScope.isOpen = false;
1778
+
1779
+ if (!$rootScope.$$phase) {
1780
+ openScope.$apply();
1781
+ }
1674
1782
  };
1675
1783
 
1676
1784
  var escapeKeyBind = function( evt ) {
@@ -1681,13 +1789,14 @@ angular.module('ui.bootstrap.dropdown', [])
1681
1789
  };
1682
1790
  }])
1683
1791
 
1684
- .controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) {
1792
+ .controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document) {
1685
1793
  var self = this,
1686
1794
  scope = $scope.$new(), // create a child scope so we are not polluting original one
1687
1795
  openClass = dropdownConfig.openClass,
1688
1796
  getIsOpen,
1689
1797
  setIsOpen = angular.noop,
1690
- toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop;
1798
+ toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
1799
+ appendToBody = false;
1691
1800
 
1692
1801
  this.init = function( element ) {
1693
1802
  self.$element = element;
@@ -1700,6 +1809,15 @@ angular.module('ui.bootstrap.dropdown', [])
1700
1809
  scope.isOpen = !!value;
1701
1810
  });
1702
1811
  }
1812
+
1813
+ appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
1814
+
1815
+ if ( appendToBody && self.dropdownMenu ) {
1816
+ $document.find('body').append( self.dropdownMenu );
1817
+ element.on('$destroy', function handleDestroyEvent() {
1818
+ self.dropdownMenu.remove();
1819
+ });
1820
+ }
1703
1821
  };
1704
1822
 
1705
1823
  this.toggle = function( open ) {
@@ -1715,6 +1833,14 @@ angular.module('ui.bootstrap.dropdown', [])
1715
1833
  return self.toggleElement;
1716
1834
  };
1717
1835
 
1836
+ scope.getAutoClose = function() {
1837
+ return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
1838
+ };
1839
+
1840
+ scope.getElement = function() {
1841
+ return self.$element;
1842
+ };
1843
+
1718
1844
  scope.focusToggleElement = function() {
1719
1845
  if ( self.toggleElement ) {
1720
1846
  self.toggleElement[0].focus();
@@ -1722,6 +1848,15 @@ angular.module('ui.bootstrap.dropdown', [])
1722
1848
  };
1723
1849
 
1724
1850
  scope.$watch('isOpen', function( isOpen, wasOpen ) {
1851
+ if ( appendToBody && self.dropdownMenu ) {
1852
+ var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true);
1853
+ self.dropdownMenu.css({
1854
+ top: pos.top + 'px',
1855
+ left: pos.left + 'px',
1856
+ display: isOpen ? 'block' : 'none'
1857
+ });
1858
+ }
1859
+
1725
1860
  $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);
1726
1861
 
1727
1862
  if ( isOpen ) {
@@ -1755,6 +1890,19 @@ angular.module('ui.bootstrap.dropdown', [])
1755
1890
  };
1756
1891
  })
1757
1892
 
1893
+ .directive('dropdownMenu', function() {
1894
+ return {
1895
+ restrict: 'AC',
1896
+ require: '?^dropdown',
1897
+ link: function(scope, element, attrs, dropdownCtrl) {
1898
+ if ( !dropdownCtrl ) {
1899
+ return;
1900
+ }
1901
+ dropdownCtrl.dropdownMenu = element;
1902
+ }
1903
+ };
1904
+ })
1905
+
1758
1906
  .directive('dropdownToggle', function() {
1759
1907
  return {
1760
1908
  require: '?^dropdown',
@@ -1790,7 +1938,7 @@ angular.module('ui.bootstrap.dropdown', [])
1790
1938
  };
1791
1939
  });
1792
1940
 
1793
- angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1941
+ angular.module('ui.bootstrap.modal', [])
1794
1942
 
1795
1943
  /**
1796
1944
  * A helper, internal data structure that acts as a map but also allows getting / removing
@@ -1854,20 +2002,23 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1854
2002
  restrict: 'EA',
1855
2003
  replace: true,
1856
2004
  templateUrl: 'template/modal/backdrop.html',
1857
- link: function (scope, element, attrs) {
1858
- scope.backdropClass = attrs.backdropClass || '';
1859
-
1860
- scope.animate = false;
1861
-
1862
- //trigger CSS transitions
1863
- $timeout(function () {
1864
- scope.animate = true;
1865
- });
2005
+ compile: function (tElement, tAttrs) {
2006
+ tElement.addClass(tAttrs.backdropClass);
2007
+ return linkFn;
1866
2008
  }
1867
2009
  };
2010
+
2011
+ function linkFn(scope, element, attrs) {
2012
+ scope.animate = false;
2013
+
2014
+ //trigger CSS transitions
2015
+ $timeout(function () {
2016
+ scope.animate = true;
2017
+ });
2018
+ }
1868
2019
  }])
1869
2020
 
1870
- .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
2021
+ .directive('modalWindow', ['$modalStack', '$q', function ($modalStack, $q) {
1871
2022
  return {
1872
2023
  restrict: 'EA',
1873
2024
  scope: {
@@ -1883,10 +2034,35 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1883
2034
  element.addClass(attrs.windowClass || '');
1884
2035
  scope.size = attrs.size;
1885
2036
 
1886
- $timeout(function () {
2037
+ scope.close = function (evt) {
2038
+ var modal = $modalStack.getTop();
2039
+ if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
2040
+ evt.preventDefault();
2041
+ evt.stopPropagation();
2042
+ $modalStack.dismiss(modal.key, 'backdrop click');
2043
+ }
2044
+ };
2045
+
2046
+ // This property is only added to the scope for the purpose of detecting when this directive is rendered.
2047
+ // We can detect that by using this property in the template associated with this directive and then use
2048
+ // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
2049
+ scope.$isRendered = true;
2050
+
2051
+ // Deferred object that will be resolved when this modal is render.
2052
+ var modalRenderDeferObj = $q.defer();
2053
+ // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
2054
+ // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
2055
+ attrs.$observe('modalRender', function (value) {
2056
+ if (value == 'true') {
2057
+ modalRenderDeferObj.resolve();
2058
+ }
2059
+ });
2060
+
2061
+ modalRenderDeferObj.promise.then(function () {
1887
2062
  // trigger CSS transitions
1888
2063
  scope.animate = true;
1889
2064
 
2065
+ var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]');
1890
2066
  /**
1891
2067
  * Auto-focusing of a freshly-opened modal element causes any child elements
1892
2068
  * with the autofocus attribute to lose focus. This is an issue on touch
@@ -1895,23 +2071,33 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1895
2071
  * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
1896
2072
  * the modal element if the modal does not contain an autofocus element.
1897
2073
  */
1898
- if (!element[0].querySelectorAll('[autofocus]').length) {
2074
+ if (inputsWithAutofocus.length) {
2075
+ inputsWithAutofocus[0].focus();
2076
+ } else {
1899
2077
  element[0].focus();
1900
2078
  }
1901
- });
1902
2079
 
1903
- scope.close = function (evt) {
2080
+ // Notify {@link $modalStack} that modal is rendered.
1904
2081
  var modal = $modalStack.getTop();
1905
- if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
1906
- evt.preventDefault();
1907
- evt.stopPropagation();
1908
- $modalStack.dismiss(modal.key, 'backdrop click');
2082
+ if (modal) {
2083
+ $modalStack.modalRendered(modal.key);
1909
2084
  }
1910
- };
2085
+ });
1911
2086
  }
1912
2087
  };
1913
2088
  }])
1914
2089
 
2090
+ .directive('modalAnimationClass', [
2091
+ function () {
2092
+ return {
2093
+ compile: function (tElement, tAttrs) {
2094
+ if (tAttrs.modalAnimation) {
2095
+ tElement.addClass(tAttrs.modalAnimationClass);
2096
+ }
2097
+ }
2098
+ };
2099
+ }])
2100
+
1915
2101
  .directive('modalTransclude', function () {
1916
2102
  return {
1917
2103
  link: function($scope, $element, $attrs, controller, $transclude) {
@@ -1923,8 +2109,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1923
2109
  };
1924
2110
  })
1925
2111
 
1926
- .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
1927
- function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {
2112
+ .factory('$modalStack', ['$animate', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
2113
+ function ($animate, $timeout, $document, $compile, $rootScope, $$stackedMap) {
1928
2114
 
1929
2115
  var OPENED_MODAL_CLASS = 'modal-open';
1930
2116
 
@@ -1958,8 +2144,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1958
2144
  openedWindows.remove(modalInstance);
1959
2145
 
1960
2146
  //remove window DOM element
1961
- removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, function() {
1962
- modalWindow.modalScope.$destroy();
2147
+ removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
1963
2148
  body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
1964
2149
  checkRemoveBackdrop();
1965
2150
  });
@@ -1969,8 +2154,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1969
2154
  //remove backdrop if no longer needed
1970
2155
  if (backdropDomEl && backdropIndex() == -1) {
1971
2156
  var backdropScopeRef = backdropScope;
1972
- removeAfterAnimate(backdropDomEl, backdropScope, 150, function () {
1973
- backdropScopeRef.$destroy();
2157
+ removeAfterAnimate(backdropDomEl, backdropScope, function () {
1974
2158
  backdropScopeRef = null;
1975
2159
  });
1976
2160
  backdropDomEl = undefined;
@@ -1978,19 +2162,14 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1978
2162
  }
1979
2163
  }
1980
2164
 
1981
- function removeAfterAnimate(domEl, scope, emulateTime, done) {
2165
+ function removeAfterAnimate(domEl, scope, done) {
1982
2166
  // Closing animation
1983
2167
  scope.animate = false;
1984
2168
 
1985
- var transitionEndEventName = $transition.transitionEndEventName;
1986
- if (transitionEndEventName) {
2169
+ if (domEl.attr('modal-animation') && $animate.enabled()) {
1987
2170
  // transition out
1988
- var timeout = $timeout(afterAnimating, emulateTime);
1989
-
1990
- domEl.bind(transitionEndEventName, function () {
1991
- $timeout.cancel(timeout);
1992
- afterAnimating();
1993
- scope.$apply();
2171
+ domEl.one('$animate:close', function closeFn() {
2172
+ $rootScope.$evalAsync(afterAnimating);
1994
2173
  });
1995
2174
  } else {
1996
2175
  // Ensure this call is async
@@ -2004,6 +2183,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2004
2183
  afterAnimating.done = true;
2005
2184
 
2006
2185
  domEl.remove();
2186
+ scope.$destroy();
2007
2187
  if (done) {
2008
2188
  done();
2009
2189
  }
@@ -2026,8 +2206,11 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2026
2206
 
2027
2207
  $modalStack.open = function (modalInstance, modal) {
2028
2208
 
2209
+ var modalOpener = $document[0].activeElement;
2210
+
2029
2211
  openedWindows.add(modalInstance, {
2030
2212
  deferred: modal.deferred,
2213
+ renderDeferred: modal.renderDeferred,
2031
2214
  modalScope: modal.scope,
2032
2215
  backdrop: modal.backdrop,
2033
2216
  keyboard: modal.keyboard
@@ -2039,13 +2222,16 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2039
2222
  if (currBackdropIndex >= 0 && !backdropDomEl) {
2040
2223
  backdropScope = $rootScope.$new(true);
2041
2224
  backdropScope.index = currBackdropIndex;
2042
- var angularBackgroundDomEl = angular.element('<div modal-backdrop></div>');
2225
+ var angularBackgroundDomEl = angular.element('<div modal-backdrop="modal-backdrop"></div>');
2043
2226
  angularBackgroundDomEl.attr('backdrop-class', modal.backdropClass);
2227
+ if (modal.animation) {
2228
+ angularBackgroundDomEl.attr('modal-animation', 'true');
2229
+ }
2044
2230
  backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope);
2045
2231
  body.append(backdropDomEl);
2046
2232
  }
2047
2233
 
2048
- var angularDomEl = angular.element('<div modal-window></div>');
2234
+ var angularDomEl = angular.element('<div modal-window="modal-window"></div>');
2049
2235
  angularDomEl.attr({
2050
2236
  'template-url': modal.windowTemplateUrl,
2051
2237
  'window-class': modal.windowClass,
@@ -2053,33 +2239,46 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2053
2239
  'index': openedWindows.length() - 1,
2054
2240
  'animate': 'animate'
2055
2241
  }).html(modal.content);
2242
+ if (modal.animation) {
2243
+ angularDomEl.attr('modal-animation', 'true');
2244
+ }
2056
2245
 
2057
2246
  var modalDomEl = $compile(angularDomEl)(modal.scope);
2058
2247
  openedWindows.top().value.modalDomEl = modalDomEl;
2248
+ openedWindows.top().value.modalOpener = modalOpener;
2059
2249
  body.append(modalDomEl);
2060
2250
  body.addClass(OPENED_MODAL_CLASS);
2061
2251
  };
2062
2252
 
2253
+ function broadcastClosing(modalWindow, resultOrReason, closing) {
2254
+ return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
2255
+ }
2256
+
2063
2257
  $modalStack.close = function (modalInstance, result) {
2064
2258
  var modalWindow = openedWindows.get(modalInstance);
2065
- if (modalWindow) {
2259
+ if (modalWindow && broadcastClosing(modalWindow, result, true)) {
2066
2260
  modalWindow.value.deferred.resolve(result);
2067
2261
  removeModalWindow(modalInstance);
2262
+ modalWindow.value.modalOpener.focus();
2263
+ return true;
2068
2264
  }
2265
+ return !modalWindow;
2069
2266
  };
2070
2267
 
2071
2268
  $modalStack.dismiss = function (modalInstance, reason) {
2072
2269
  var modalWindow = openedWindows.get(modalInstance);
2073
- if (modalWindow) {
2270
+ if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
2074
2271
  modalWindow.value.deferred.reject(reason);
2075
2272
  removeModalWindow(modalInstance);
2273
+ modalWindow.value.modalOpener.focus();
2274
+ return true;
2076
2275
  }
2276
+ return !modalWindow;
2077
2277
  };
2078
2278
 
2079
2279
  $modalStack.dismissAll = function (reason) {
2080
2280
  var topModal = this.getTop();
2081
- while (topModal) {
2082
- this.dismiss(topModal.key, reason);
2281
+ while (topModal && this.dismiss(topModal.key, reason)) {
2083
2282
  topModal = this.getTop();
2084
2283
  }
2085
2284
  };
@@ -2088,6 +2287,13 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2088
2287
  return openedWindows.top();
2089
2288
  };
2090
2289
 
2290
+ $modalStack.modalRendered = function (modalInstance) {
2291
+ var modalWindow = openedWindows.get(modalInstance);
2292
+ if (modalWindow) {
2293
+ modalWindow.value.renderDeferred.resolve();
2294
+ }
2295
+ };
2296
+
2091
2297
  return $modalStack;
2092
2298
  }])
2093
2299
 
@@ -2095,20 +2301,18 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2095
2301
 
2096
2302
  var $modalProvider = {
2097
2303
  options: {
2098
- backdrop: true, //can be also false or 'static'
2304
+ animation: true,
2305
+ backdrop: true, //can also be false or 'static'
2099
2306
  keyboard: true
2100
2307
  },
2101
- $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
2102
- function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
2308
+ $get: ['$injector', '$rootScope', '$q', '$templateRequest', '$controller', '$modalStack',
2309
+ function ($injector, $rootScope, $q, $templateRequest, $controller, $modalStack) {
2103
2310
 
2104
2311
  var $modal = {};
2105
2312
 
2106
2313
  function getTemplatePromise(options) {
2107
2314
  return options.template ? $q.when(options.template) :
2108
- $http.get(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl,
2109
- {cache: $templateCache}).then(function (result) {
2110
- return result.data;
2111
- });
2315
+ $templateRequest(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl);
2112
2316
  }
2113
2317
 
2114
2318
  function getResolvePromises(resolves) {
@@ -2125,16 +2329,18 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2125
2329
 
2126
2330
  var modalResultDeferred = $q.defer();
2127
2331
  var modalOpenedDeferred = $q.defer();
2332
+ var modalRenderDeferred = $q.defer();
2128
2333
 
2129
2334
  //prepare an instance of a modal to be injected into controllers and returned to a caller
2130
2335
  var modalInstance = {
2131
2336
  result: modalResultDeferred.promise,
2132
2337
  opened: modalOpenedDeferred.promise,
2338
+ rendered: modalRenderDeferred.promise,
2133
2339
  close: function (result) {
2134
- $modalStack.close(modalInstance, result);
2340
+ return $modalStack.close(modalInstance, result);
2135
2341
  },
2136
2342
  dismiss: function (reason) {
2137
- $modalStack.dismiss(modalInstance, reason);
2343
+ return $modalStack.dismiss(modalInstance, reason);
2138
2344
  }
2139
2345
  };
2140
2346
 
@@ -2177,7 +2383,9 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2177
2383
  $modalStack.open(modalInstance, {
2178
2384
  scope: modalScope,
2179
2385
  deferred: modalResultDeferred,
2386
+ renderDeferred: modalRenderDeferred,
2180
2387
  content: tplAndVars[0],
2388
+ animation: modalOptions.animation,
2181
2389
  backdrop: modalOptions.backdrop,
2182
2390
  keyboard: modalOptions.keyboard,
2183
2391
  backdropClass: modalOptions.backdropClass,
@@ -2192,8 +2400,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
2192
2400
 
2193
2401
  templateAndResolvePromise.then(function () {
2194
2402
  modalOpenedDeferred.resolve(true);
2195
- }, function () {
2196
- modalOpenedDeferred.reject(false);
2403
+ }, function (reason) {
2404
+ modalOpenedDeferred.reject(reason);
2197
2405
  });
2198
2406
 
2199
2407
  return modalInstance;
@@ -2229,6 +2437,20 @@ angular.module('ui.bootstrap.pagination', [])
2229
2437
  } else {
2230
2438
  this.itemsPerPage = config.itemsPerPage;
2231
2439
  }
2440
+
2441
+ $scope.$watch('totalItems', function() {
2442
+ $scope.totalPages = self.calculateTotalPages();
2443
+ });
2444
+
2445
+ $scope.$watch('totalPages', function(value) {
2446
+ setNumPages($scope.$parent, value); // Readonly variable
2447
+
2448
+ if ( $scope.page > value ) {
2449
+ $scope.selectPage(value);
2450
+ } else {
2451
+ ngModelCtrl.$render();
2452
+ }
2453
+ });
2232
2454
  };
2233
2455
 
2234
2456
  this.calculateTotalPages = function() {
@@ -2240,8 +2462,11 @@ angular.module('ui.bootstrap.pagination', [])
2240
2462
  $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1;
2241
2463
  };
2242
2464
 
2243
- $scope.selectPage = function(page) {
2465
+ $scope.selectPage = function(page, evt) {
2244
2466
  if ( $scope.page !== page && page > 0 && page <= $scope.totalPages) {
2467
+ if (evt && evt.target) {
2468
+ evt.target.blur();
2469
+ }
2245
2470
  ngModelCtrl.$setViewValue(page);
2246
2471
  ngModelCtrl.$render();
2247
2472
  }
@@ -2256,20 +2481,6 @@ angular.module('ui.bootstrap.pagination', [])
2256
2481
  $scope.noNext = function() {
2257
2482
  return $scope.page === $scope.totalPages;
2258
2483
  };
2259
-
2260
- $scope.$watch('totalItems', function() {
2261
- $scope.totalPages = self.calculateTotalPages();
2262
- });
2263
-
2264
- $scope.$watch('totalPages', function(value) {
2265
- setNumPages($scope.$parent, value); // Readonly variable
2266
-
2267
- if ( $scope.page > value ) {
2268
- $scope.selectPage(value);
2269
- } else {
2270
- ngModelCtrl.$render();
2271
- }
2272
- });
2273
2484
  }])
2274
2485
 
2275
2486
  .constant('paginationConfig', {
@@ -2437,7 +2648,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2437
2648
  var defaultOptions = {
2438
2649
  placement: 'top',
2439
2650
  animation: true,
2440
- popupDelay: 0
2651
+ popupDelay: 0,
2652
+ useContentExp: false
2441
2653
  };
2442
2654
 
2443
2655
  // Default hide triggers for each show trigger
@@ -2488,8 +2700,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2488
2700
  * TODO support multiple triggers
2489
2701
  */
2490
2702
  this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $document, $position, $interpolate ) {
2491
- return function $tooltip ( type, prefix, defaultTriggerShow ) {
2492
- var options = angular.extend( {}, defaultOptions, globalOptions );
2703
+ return function $tooltip ( type, prefix, defaultTriggerShow, options ) {
2704
+ options = angular.extend( {}, defaultOptions, globalOptions, options );
2493
2705
 
2494
2706
  /**
2495
2707
  * Returns an object of show and hide triggers.
@@ -2521,10 +2733,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2521
2733
  var template =
2522
2734
  '<div '+ directiveName +'-popup '+
2523
2735
  'title="'+startSym+'title'+endSym+'" '+
2524
- 'content="'+startSym+'content'+endSym+'" '+
2736
+ (options.useContentExp ?
2737
+ 'content-exp="contentExp()" ' :
2738
+ 'content="'+startSym+'content'+endSym+'" ') +
2525
2739
  'placement="'+startSym+'placement'+endSym+'" '+
2740
+ 'popup-class="'+startSym+'popupClass'+endSym+'" '+
2526
2741
  'animation="animation" '+
2527
2742
  'is-open="isOpen"'+
2743
+ 'origin-scope="origScope" '+
2528
2744
  '>'+
2529
2745
  '</div>';
2530
2746
 
@@ -2533,7 +2749,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2533
2749
  compile: function (tElem, tAttrs) {
2534
2750
  var tooltipLinker = $compile( template );
2535
2751
 
2536
- return function link ( scope, element, attrs ) {
2752
+ return function link ( scope, element, attrs, tooltipCtrl ) {
2537
2753
  var tooltip;
2538
2754
  var tooltipLinkedScope;
2539
2755
  var transitionTimeout;
@@ -2544,6 +2760,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2544
2760
  var ttScope = scope.$new(true);
2545
2761
 
2546
2762
  var positionTooltip = function () {
2763
+ if (!tooltip) { return; }
2547
2764
 
2548
2765
  var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
2549
2766
  ttPosition.top += 'px';
@@ -2553,6 +2770,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2553
2770
  tooltip.css( ttPosition );
2554
2771
  };
2555
2772
 
2773
+ // Set up the correct scope to allow transclusion later
2774
+ ttScope.origScope = scope;
2775
+
2556
2776
  // By default, the tooltip is not open.
2557
2777
  // TODO add ability to start tooltip opened
2558
2778
  ttScope.isOpen = false;
@@ -2604,7 +2824,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2604
2824
  }
2605
2825
 
2606
2826
  // Don't show empty tooltips.
2607
- if ( ! ttScope.content ) {
2827
+ if ( !(options.useContentExp ? ttScope.contentExp() : ttScope.content) ) {
2608
2828
  return angular.noop;
2609
2829
  }
2610
2830
 
@@ -2618,7 +2838,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2618
2838
 
2619
2839
  // And show the tooltip.
2620
2840
  ttScope.isOpen = true;
2621
- ttScope.$digest(); // digest required as $apply is not called
2841
+ ttScope.$apply(); // digest required as $apply is not called
2622
2842
 
2623
2843
  // Return positioning function as promise callback for correct
2624
2844
  // positioning after draw.
@@ -2659,6 +2879,18 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2659
2879
  element.after( tooltip );
2660
2880
  }
2661
2881
  });
2882
+
2883
+ tooltipLinkedScope.$watch(function () {
2884
+ $timeout(positionTooltip, 0, false);
2885
+ });
2886
+
2887
+ if (options.useContentExp) {
2888
+ tooltipLinkedScope.$watch('contentExp()', function (val) {
2889
+ if (!val && ttScope.isOpen ) {
2890
+ hide();
2891
+ }
2892
+ });
2893
+ }
2662
2894
  }
2663
2895
 
2664
2896
  function removeTooltip() {
@@ -2674,17 +2906,30 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2674
2906
  }
2675
2907
 
2676
2908
  function prepareTooltip() {
2909
+ prepPopupClass();
2677
2910
  prepPlacement();
2678
2911
  prepPopupDelay();
2679
2912
  }
2680
2913
 
2914
+ ttScope.contentExp = function () {
2915
+ return scope.$eval(attrs[type]);
2916
+ };
2917
+
2681
2918
  /**
2682
2919
  * Observe the relevant attributes.
2683
2920
  */
2684
- attrs.$observe( type, function ( val ) {
2685
- ttScope.content = val;
2921
+ if (!options.useContentExp) {
2922
+ attrs.$observe( type, function ( val ) {
2923
+ ttScope.content = val;
2924
+
2925
+ if (!val && ttScope.isOpen ) {
2926
+ hide();
2927
+ }
2928
+ });
2929
+ }
2686
2930
 
2687
- if (!val && ttScope.isOpen ) {
2931
+ attrs.$observe( 'disabled', function ( val ) {
2932
+ if (val && ttScope.isOpen ) {
2688
2933
  hide();
2689
2934
  }
2690
2935
  });
@@ -2693,6 +2938,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2693
2938
  ttScope.title = val;
2694
2939
  });
2695
2940
 
2941
+ function prepPopupClass() {
2942
+ ttScope.popupClass = attrs[prefix + 'Class'];
2943
+ }
2944
+
2696
2945
  function prepPlacement() {
2697
2946
  var val = attrs[ prefix + 'Placement' ];
2698
2947
  ttScope.placement = angular.isDefined( val ) ? val : options.placement;
@@ -2756,11 +3005,101 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2756
3005
  }];
2757
3006
  })
2758
3007
 
3008
+ // This is mostly ngInclude code but with a custom scope
3009
+ .directive( 'tooltipTemplateTransclude', [
3010
+ '$animate', '$sce', '$compile', '$templateRequest',
3011
+ function ($animate , $sce , $compile , $templateRequest) {
3012
+ return {
3013
+ link: function ( scope, elem, attrs ) {
3014
+ var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
3015
+
3016
+ var changeCounter = 0,
3017
+ currentScope,
3018
+ previousElement,
3019
+ currentElement;
3020
+
3021
+ var cleanupLastIncludeContent = function() {
3022
+ if (previousElement) {
3023
+ previousElement.remove();
3024
+ previousElement = null;
3025
+ }
3026
+ if (currentScope) {
3027
+ currentScope.$destroy();
3028
+ currentScope = null;
3029
+ }
3030
+ if (currentElement) {
3031
+ $animate.leave(currentElement).then(function() {
3032
+ previousElement = null;
3033
+ });
3034
+ previousElement = currentElement;
3035
+ currentElement = null;
3036
+ }
3037
+ };
3038
+
3039
+ scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function (src) {
3040
+ var thisChangeId = ++changeCounter;
3041
+
3042
+ if (src) {
3043
+ //set the 2nd param to true to ignore the template request error so that the inner
3044
+ //contents and scope can be cleaned up.
3045
+ $templateRequest(src, true).then(function(response) {
3046
+ if (thisChangeId !== changeCounter) { return; }
3047
+ var newScope = origScope.$new();
3048
+ var template = response;
3049
+
3050
+ var clone = $compile(template)(newScope, function(clone) {
3051
+ cleanupLastIncludeContent();
3052
+ $animate.enter(clone, elem);
3053
+ });
3054
+
3055
+ currentScope = newScope;
3056
+ currentElement = clone;
3057
+
3058
+ currentScope.$emit('$includeContentLoaded', src);
3059
+ }, function() {
3060
+ if (thisChangeId === changeCounter) {
3061
+ cleanupLastIncludeContent();
3062
+ scope.$emit('$includeContentError', src);
3063
+ }
3064
+ });
3065
+ scope.$emit('$includeContentRequested', src);
3066
+ } else {
3067
+ cleanupLastIncludeContent();
3068
+ }
3069
+ });
3070
+
3071
+ scope.$on('$destroy', cleanupLastIncludeContent);
3072
+ }
3073
+ };
3074
+ }])
3075
+
3076
+ /**
3077
+ * Note that it's intentional that these classes are *not* applied through $animate.
3078
+ * They must not be animated as they're expected to be present on the tooltip on
3079
+ * initialization.
3080
+ */
3081
+ .directive('tooltipClasses', function () {
3082
+ return {
3083
+ restrict: 'A',
3084
+ link: function (scope, element, attrs) {
3085
+ if (scope.placement) {
3086
+ element.addClass(scope.placement);
3087
+ }
3088
+ if (scope.popupClass) {
3089
+ element.addClass(scope.popupClass);
3090
+ }
3091
+ if (scope.animation()) {
3092
+ element.addClass(attrs.tooltipAnimationClass);
3093
+ }
3094
+ }
3095
+ };
3096
+ })
3097
+
2759
3098
  .directive( 'tooltipPopup', function () {
2760
3099
  return {
2761
3100
  restrict: 'EA',
2762
3101
  replace: true,
2763
- scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
3102
+ scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
2764
3103
  templateUrl: 'template/tooltip/tooltip-popup.html'
2765
3104
  };
2766
3105
  })
@@ -2769,16 +3108,56 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2769
3108
  return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
2770
3109
  }])
2771
3110
 
3111
+ .directive( 'tooltipTemplatePopup', function () {
3112
+ return {
3113
+ restrict: 'EA',
3114
+ replace: true,
3115
+ scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
3116
+ originScope: '&' },
3117
+ templateUrl: 'template/tooltip/tooltip-template-popup.html'
3118
+ };
3119
+ })
3120
+
3121
+ .directive( 'tooltipTemplate', [ '$tooltip', function ( $tooltip ) {
3122
+ return $tooltip('tooltipTemplate', 'tooltip', 'mouseenter', {
3123
+ useContentExp: true
3124
+ });
3125
+ }])
3126
+
3127
+ .directive( 'tooltipHtmlPopup', function () {
3128
+ return {
3129
+ restrict: 'EA',
3130
+ replace: true,
3131
+ scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
3132
+ templateUrl: 'template/tooltip/tooltip-html-popup.html'
3133
+ };
3134
+ })
3135
+
3136
+ .directive( 'tooltipHtml', [ '$tooltip', function ( $tooltip ) {
3137
+ return $tooltip('tooltipHtml', 'tooltip', 'mouseenter', {
3138
+ useContentExp: true
3139
+ });
3140
+ }])
3141
+
3142
+ /*
3143
+ Deprecated
3144
+ */
2772
3145
  .directive( 'tooltipHtmlUnsafePopup', function () {
2773
3146
  return {
2774
3147
  restrict: 'EA',
2775
3148
  replace: true,
2776
- scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
3149
+ scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
2777
3150
  templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
2778
3151
  };
2779
3152
  })
2780
3153
 
2781
- .directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
3154
+ .value('tooltipHtmlUnsafeSuppressDeprecated', false)
3155
+ .directive( 'tooltipHtmlUnsafe', [
3156
+ '$tooltip', 'tooltipHtmlUnsafeSuppressDeprecated', '$log',
3157
+ function ( $tooltip , tooltipHtmlUnsafeSuppressDeprecated , $log) {
3158
+ if (!tooltipHtmlUnsafeSuppressDeprecated) {
3159
+ $log.warn('tooltip-html-unsafe is now deprecated. Use tooltip-html or tooltip-template instead.');
3160
+ }
2782
3161
  return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
2783
3162
  }]);
2784
3163
 
@@ -2789,11 +3168,27 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2789
3168
  */
2790
3169
  angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
2791
3170
 
3171
+ .directive( 'popoverTemplatePopup', function () {
3172
+ return {
3173
+ restrict: 'EA',
3174
+ replace: true,
3175
+ scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
3176
+ originScope: '&' },
3177
+ templateUrl: 'template/popover/popover-template.html'
3178
+ };
3179
+ })
3180
+
3181
+ .directive( 'popoverTemplate', [ '$tooltip', function ( $tooltip ) {
3182
+ return $tooltip( 'popoverTemplate', 'popover', 'click', {
3183
+ useContentExp: true
3184
+ } );
3185
+ }])
3186
+
2792
3187
  .directive( 'popoverPopup', function () {
2793
3188
  return {
2794
3189
  restrict: 'EA',
2795
3190
  replace: true,
2796
- scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
3191
+ scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
2797
3192
  templateUrl: 'template/popover/popover.html'
2798
3193
  };
2799
3194
  })
@@ -2814,7 +3209,7 @@ angular.module('ui.bootstrap.progressbar', [])
2814
3209
  animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
2815
3210
 
2816
3211
  this.bars = [];
2817
- $scope.max = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : progressConfig.max;
3212
+ $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max;
2818
3213
 
2819
3214
  this.addBar = function(bar, element) {
2820
3215
  if ( !animate ) {
@@ -2858,6 +3253,7 @@ angular.module('ui.bootstrap.progressbar', [])
2858
3253
  require: '^progress',
2859
3254
  scope: {
2860
3255
  value: '=',
3256
+ max: '=?',
2861
3257
  type: '@'
2862
3258
  },
2863
3259
  templateUrl: 'template/progressbar/bar.html',
@@ -2875,6 +3271,7 @@ angular.module('ui.bootstrap.progressbar', [])
2875
3271
  controller: 'ProgressController',
2876
3272
  scope: {
2877
3273
  value: '=',
3274
+ max: '=?',
2878
3275
  type: '@'
2879
3276
  },
2880
3277
  templateUrl: 'template/progressbar/progressbar.html',
@@ -2883,6 +3280,7 @@ angular.module('ui.bootstrap.progressbar', [])
2883
3280
  }
2884
3281
  };
2885
3282
  });
3283
+
2886
3284
  angular.module('ui.bootstrap.rating', [])
2887
3285
 
2888
3286
  .constant('ratingConfig', {
@@ -2898,6 +3296,13 @@ angular.module('ui.bootstrap.rating', [])
2898
3296
  ngModelCtrl = ngModelCtrl_;
2899
3297
  ngModelCtrl.$render = this.render;
2900
3298
 
3299
+ ngModelCtrl.$formatters.push(function(value) {
3300
+ if (angular.isNumber(value) && value << 0 !== value) {
3301
+ value = Math.round(value);
3302
+ }
3303
+ return value;
3304
+ });
3305
+
2901
3306
  this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
2902
3307
  this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
2903
3308
 
@@ -2959,10 +3364,7 @@ angular.module('ui.bootstrap.rating', [])
2959
3364
  replace: true,
2960
3365
  link: function(scope, element, attrs, ctrls) {
2961
3366
  var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
2962
-
2963
- if ( ngModelCtrl ) {
2964
- ratingCtrl.init( ngModelCtrl );
2965
- }
3367
+ ratingCtrl.init( ngModelCtrl );
2966
3368
  }
2967
3369
  };
2968
3370
  });
@@ -2996,11 +3398,14 @@ angular.module('ui.bootstrap.tabs', [])
2996
3398
  tabs.push(tab);
2997
3399
  // we can't run the select function on the first tab
2998
3400
  // since that would select it twice
2999
- if (tabs.length === 1) {
3401
+ if (tabs.length === 1 && tab.active !== false) {
3000
3402
  tab.active = true;
3001
3403
  } else if (tab.active) {
3002
3404
  ctrl.select(tab);
3003
3405
  }
3406
+ else {
3407
+ tab.active = false;
3408
+ }
3004
3409
  };
3005
3410
 
3006
3411
  ctrl.removeTab = function removeTab(tab) {
@@ -3147,7 +3552,7 @@ angular.module('ui.bootstrap.tabs', [])
3147
3552
  </file>
3148
3553
  </example>
3149
3554
  */
3150
- .directive('tab', ['$parse', function($parse) {
3555
+ .directive('tab', ['$parse', '$log', function($parse, $log) {
3151
3556
  return {
3152
3557
  require: '^tabset',
3153
3558
  restrict: 'EA',
@@ -3173,7 +3578,18 @@ angular.module('ui.bootstrap.tabs', [])
3173
3578
  });
3174
3579
 
3175
3580
  scope.disabled = false;
3581
+ if ( attrs.disable ) {
3582
+ scope.$parent.$watch($parse(attrs.disable), function(value) {
3583
+ scope.disabled = !! value;
3584
+ });
3585
+ }
3586
+
3587
+ // Deprecation support of "disabled" parameter
3588
+ // fix(tab): IE9 disabled attr renders grey text on enabled tab #2677
3589
+ // This code is duplicated from the lines above to make it easy to remove once
3590
+ // the feature has been completely deprecated
3176
3591
  if ( attrs.disabled ) {
3592
+ $log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');
3177
3593
  scope.$parent.$watch($parse(attrs.disabled), function(value) {
3178
3594
  scope.disabled = !! value;
3179
3595
  });
@@ -3254,7 +3670,8 @@ angular.module('ui.bootstrap.timepicker', [])
3254
3670
  showMeridian: true,
3255
3671
  meridians: null,
3256
3672
  readonlyInput: false,
3257
- mousewheel: true
3673
+ mousewheel: true,
3674
+ arrowkeys: true
3258
3675
  })
3259
3676
 
3260
3677
  .controller('TimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'timepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {
@@ -3266,6 +3683,10 @@ angular.module('ui.bootstrap.timepicker', [])
3266
3683
  ngModelCtrl = ngModelCtrl_;
3267
3684
  ngModelCtrl.$render = this.render;
3268
3685
 
3686
+ ngModelCtrl.$formatters.unshift(function (modelValue) {
3687
+ return modelValue ? new Date( modelValue ) : null;
3688
+ });
3689
+
3269
3690
  var hoursInputEl = inputs.eq(0),
3270
3691
  minutesInputEl = inputs.eq(1);
3271
3692
 
@@ -3274,6 +3695,11 @@ angular.module('ui.bootstrap.timepicker', [])
3274
3695
  this.setupMousewheelEvents( hoursInputEl, minutesInputEl );
3275
3696
  }
3276
3697
 
3698
+ var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
3699
+ if (arrowkeys) {
3700
+ this.setupArrowkeyEvents( hoursInputEl, minutesInputEl );
3701
+ }
3702
+
3277
3703
  $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
3278
3704
  this.setupInputEvents( hoursInputEl, minutesInputEl );
3279
3705
  };
@@ -3336,7 +3762,7 @@ angular.module('ui.bootstrap.timepicker', [])
3336
3762
  }
3337
3763
 
3338
3764
  function pad( value ) {
3339
- return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
3765
+ return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value.toString();
3340
3766
  }
3341
3767
 
3342
3768
  // Respond on mousewheel spin
@@ -3362,6 +3788,35 @@ angular.module('ui.bootstrap.timepicker', [])
3362
3788
 
3363
3789
  };
3364
3790
 
3791
+ // Respond on up/down arrowkeys
3792
+ this.setupArrowkeyEvents = function( hoursInputEl, minutesInputEl ) {
3793
+ hoursInputEl.bind('keydown', function(e) {
3794
+ if ( e.which === 38 ) { // up
3795
+ e.preventDefault();
3796
+ $scope.incrementHours();
3797
+ $scope.$apply();
3798
+ }
3799
+ else if ( e.which === 40 ) { // down
3800
+ e.preventDefault();
3801
+ $scope.decrementHours();
3802
+ $scope.$apply();
3803
+ }
3804
+ });
3805
+
3806
+ minutesInputEl.bind('keydown', function(e) {
3807
+ if ( e.which === 38 ) { // up
3808
+ e.preventDefault();
3809
+ $scope.incrementMinutes();
3810
+ $scope.$apply();
3811
+ }
3812
+ else if ( e.which === 40 ) { // down
3813
+ e.preventDefault();
3814
+ $scope.decrementMinutes();
3815
+ $scope.$apply();
3816
+ }
3817
+ });
3818
+ };
3819
+
3365
3820
  this.setupInputEvents = function( hoursInputEl, minutesInputEl ) {
3366
3821
  if ( $scope.readonlyInput ) {
3367
3822
  $scope.updateHours = angular.noop;
@@ -3421,7 +3876,7 @@ angular.module('ui.bootstrap.timepicker', [])
3421
3876
  };
3422
3877
 
3423
3878
  this.render = function() {
3424
- var date = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : null;
3879
+ var date = ngModelCtrl.$viewValue;
3425
3880
 
3426
3881
  if ( isNaN(date) ) {
3427
3882
  ngModelCtrl.$setValidity('time', false);
@@ -3456,7 +3911,9 @@ angular.module('ui.bootstrap.timepicker', [])
3456
3911
  }
3457
3912
 
3458
3913
  $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
3459
- $scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);
3914
+ if (keyboardChange !== 'm') {
3915
+ $scope.minutes = pad(minutes);
3916
+ }
3460
3917
  $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
3461
3918
  }
3462
3919
 
@@ -3501,6 +3958,96 @@ angular.module('ui.bootstrap.timepicker', [])
3501
3958
  };
3502
3959
  });
3503
3960
 
3961
+ angular.module('ui.bootstrap.transition', [])
3962
+
3963
+ .value('$transitionSuppressDeprecated', false)
3964
+ /**
3965
+ * $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
3966
+ * @param {DOMElement} element The DOMElement that will be animated.
3967
+ * @param {string|object|function} trigger The thing that will cause the transition to start:
3968
+ * - As a string, it represents the css class to be added to the element.
3969
+ * - As an object, it represents a hash of style attributes to be applied to the element.
3970
+ * - As a function, it represents a function to be called that will cause the transition to occur.
3971
+ * @return {Promise} A promise that is resolved when the transition finishes.
3972
+ */
3973
+ .factory('$transition', [
3974
+ '$q', '$timeout', '$rootScope', '$log', '$transitionSuppressDeprecated',
3975
+ function($q , $timeout , $rootScope , $log , $transitionSuppressDeprecated) {
3976
+
3977
+ if (!$transitionSuppressDeprecated) {
3978
+ $log.warn('$transition is now deprecated. Use $animate from ngAnimate instead.');
3979
+ }
3980
+
3981
+ var $transition = function(element, trigger, options) {
3982
+ options = options || {};
3983
+ var deferred = $q.defer();
3984
+ var endEventName = $transition[options.animation ? 'animationEndEventName' : 'transitionEndEventName'];
3985
+
3986
+ var transitionEndHandler = function(event) {
3987
+ $rootScope.$apply(function() {
3988
+ element.unbind(endEventName, transitionEndHandler);
3989
+ deferred.resolve(element);
3990
+ });
3991
+ };
3992
+
3993
+ if (endEventName) {
3994
+ element.bind(endEventName, transitionEndHandler);
3995
+ }
3996
+
3997
+ // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
3998
+ $timeout(function() {
3999
+ if ( angular.isString(trigger) ) {
4000
+ element.addClass(trigger);
4001
+ } else if ( angular.isFunction(trigger) ) {
4002
+ trigger(element);
4003
+ } else if ( angular.isObject(trigger) ) {
4004
+ element.css(trigger);
4005
+ }
4006
+ //If browser does not support transitions, instantly resolve
4007
+ if ( !endEventName ) {
4008
+ deferred.resolve(element);
4009
+ }
4010
+ });
4011
+
4012
+ // Add our custom cancel function to the promise that is returned
4013
+ // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
4014
+ // i.e. it will therefore never raise a transitionEnd event for that transition
4015
+ deferred.promise.cancel = function() {
4016
+ if ( endEventName ) {
4017
+ element.unbind(endEventName, transitionEndHandler);
4018
+ }
4019
+ deferred.reject('Transition cancelled');
4020
+ };
4021
+
4022
+ return deferred.promise;
4023
+ };
4024
+
4025
+ // Work out the name of the transitionEnd event
4026
+ var transElement = document.createElement('trans');
4027
+ var transitionEndEventNames = {
4028
+ 'WebkitTransition': 'webkitTransitionEnd',
4029
+ 'MozTransition': 'transitionend',
4030
+ 'OTransition': 'oTransitionEnd',
4031
+ 'transition': 'transitionend'
4032
+ };
4033
+ var animationEndEventNames = {
4034
+ 'WebkitTransition': 'webkitAnimationEnd',
4035
+ 'MozTransition': 'animationend',
4036
+ 'OTransition': 'oAnimationEnd',
4037
+ 'transition': 'animationend'
4038
+ };
4039
+ function findEndEventName(endEventNames) {
4040
+ for (var name in endEventNames){
4041
+ if (transElement.style[name] !== undefined) {
4042
+ return endEventNames[name];
4043
+ }
4044
+ }
4045
+ }
4046
+ $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
4047
+ $transition.animationEndEventName = findEndEventName(animationEndEventNames);
4048
+ return $transition;
4049
+ }]);
4050
+
3504
4051
  angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
3505
4052
 
3506
4053
  /**
@@ -3546,7 +4093,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3546
4093
  //minimal no of characters that needs to be entered before typeahead kicks-in
3547
4094
  var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
3548
4095
 
3549
- //minimal wait time after last character typed before typehead kicks-in
4096
+ //minimal wait time after last character typed before typeahead kicks-in
3550
4097
  var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
3551
4098
 
3552
4099
  //should it restrict model values to the ones selected from the popup only?
@@ -3634,7 +4181,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3634
4181
  //but we are interested only in responses that correspond to the current view value
3635
4182
  var onCurrentRequest = (inputValue === modelCtrl.$viewValue);
3636
4183
  if (onCurrentRequest && hasFocus) {
3637
- if (matches.length > 0) {
4184
+ if (matches && matches.length > 0) {
3638
4185
 
3639
4186
  scope.activeIdx = focusFirst ? 0 : -1;
3640
4187
  scope.matches.length = 0;
@@ -3675,7 +4222,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3675
4222
  //we need to propagate user's query so we can higlight matches
3676
4223
  scope.query = undefined;
3677
4224
 
3678
- //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
4225
+ //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
3679
4226
  var timeoutPromise;
3680
4227
 
3681
4228
  var scheduleSearchWithTimeout = function(inputValue) {
@@ -3728,6 +4275,13 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3728
4275
  var candidateViewValue, emptyViewValue;
3729
4276
  var locals = {};
3730
4277
 
4278
+ // The validity may be set to false via $parsers (see above) if
4279
+ // the model is restricted to selected values. If the model
4280
+ // is set manually it is considered to be valid.
4281
+ if (!isEditable) {
4282
+ modelCtrl.$setValidity('editable', true);
4283
+ }
4284
+
3731
4285
  if (inputFormatter) {
3732
4286
 
3733
4287
  locals.$model = modelValue;
@@ -3755,6 +4309,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3755
4309
  model = parserResult.modelMapper(originalScope, locals);
3756
4310
  $setModelValue(originalScope, model);
3757
4311
  modelCtrl.$setValidity('editable', true);
4312
+ modelCtrl.$setValidity('parse', true);
3758
4313
 
3759
4314
  onSelectCallback(originalScope, {
3760
4315
  $item: item,
@@ -3824,9 +4379,12 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3824
4379
  if (appendToBody) {
3825
4380
  $popup.remove();
3826
4381
  }
4382
+ // Prevent jQuery cache memory leak
4383
+ popUpEl.remove();
3827
4384
  });
3828
4385
 
3829
4386
  var $popup = $compile(popUpEl)(scope);
4387
+
3830
4388
  if (appendToBody) {
3831
4389
  $document.find('body').append($popup);
3832
4390
  } else {
@@ -3872,7 +4430,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3872
4430
  };
3873
4431
  })
3874
4432
 
3875
- .directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {
4433
+ .directive('typeaheadMatch', ['$templateRequest', '$compile', '$parse', function ($templateRequest, $compile, $parse) {
3876
4434
  return {
3877
4435
  restrict:'EA',
3878
4436
  scope:{
@@ -3882,8 +4440,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
3882
4440
  },
3883
4441
  link:function (scope, element, attrs) {
3884
4442
  var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';
3885
- $http.get(tplUrl, {cache: $templateCache}).success(function(tplContent){
3886
- element.replaceWith($compile(tplContent.trim())(scope));
4443
+ $templateRequest(tplUrl).then(function(tplContent) {
4444
+ $compile(tplContent.trim())(scope, function(clonedElement){
4445
+ element.replaceWith(clonedElement);
4446
+ });
3887
4447
  });
3888
4448
  }
3889
4449
  };
@@ -3905,10 +4465,10 @@ angular.module("template/accordion/accordion-group.html", []).run(["$templateCac
3905
4465
  "<div class=\"panel panel-default\">\n" +
3906
4466
  " <div class=\"panel-heading\">\n" +
3907
4467
  " <h4 class=\"panel-title\">\n" +
3908
- " <a href class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
4468
+ " <a href=\"javascript:void(0)\" tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
3909
4469
  " </h4>\n" +
3910
4470
  " </div>\n" +
3911
- " <div class=\"panel-collapse\" collapse=\"!isOpen\">\n" +
4471
+ " <div class=\"panel-collapse collapse\" collapse=\"!isOpen\">\n" +
3912
4472
  " <div class=\"panel-body\" ng-transclude></div>\n" +
3913
4473
  " </div>\n" +
3914
4474
  "</div>\n" +
@@ -3936,7 +4496,7 @@ angular.module("template/carousel/carousel.html", []).run(["$templateCache", fun
3936
4496
  $templateCache.put("template/carousel/carousel.html",
3937
4497
  "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
3938
4498
  " <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
3939
- " <li ng-repeat=\"slide in slides track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\n" +
4499
+ " <li ng-repeat=\"slide in slides | orderBy:'index' track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\n" +
3940
4500
  " </ol>\n" +
3941
4501
  " <div class=\"carousel-inner\" ng-transclude></div>\n" +
3942
4502
  " <a class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-left\"></span></a>\n" +
@@ -3948,11 +4508,7 @@ angular.module("template/carousel/carousel.html", []).run(["$templateCache", fun
3948
4508
  angular.module("template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
3949
4509
  $templateCache.put("template/carousel/slide.html",
3950
4510
  "<div ng-class=\"{\n" +
3951
- " 'active': leaving || (active && !entering),\n" +
3952
- " 'prev': (next || active) && direction=='prev',\n" +
3953
- " 'next': (next || active) && direction=='next',\n" +
3954
- " 'right': direction=='prev',\n" +
3955
- " 'left': direction=='next'\n" +
4511
+ " 'active': active\n" +
3956
4512
  " }\" class=\"item text-center\" ng-transclude></div>\n" +
3957
4513
  "");
3958
4514
  }]);
@@ -3972,7 +4528,7 @@ angular.module("template/datepicker/day.html", []).run(["$templateCache", functi
3972
4528
  " <thead>\n" +
3973
4529
  " <tr>\n" +
3974
4530
  " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
3975
- " <th colspan=\"{{5 + showWeeks}}\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
4531
+ " <th colspan=\"{{5 + showWeeks}}\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
3976
4532
  " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
3977
4533
  " </tr>\n" +
3978
4534
  " <tr>\n" +
@@ -3983,7 +4539,7 @@ angular.module("template/datepicker/day.html", []).run(["$templateCache", functi
3983
4539
  " <tbody>\n" +
3984
4540
  " <tr ng-repeat=\"row in rows track by $index\">\n" +
3985
4541
  " <td ng-show=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
3986
- " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
4542
+ " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\" ng-class=\"dt.customClass\">\n" +
3987
4543
  " <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-muted': dt.secondary, 'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
3988
4544
  " </td>\n" +
3989
4545
  " </tr>\n" +
@@ -3998,7 +4554,7 @@ angular.module("template/datepicker/month.html", []).run(["$templateCache", func
3998
4554
  " <thead>\n" +
3999
4555
  " <tr>\n" +
4000
4556
  " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
4001
- " <th><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
4557
+ " <th><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
4002
4558
  " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
4003
4559
  " </tr>\n" +
4004
4560
  " </thead>\n" +
@@ -4034,7 +4590,7 @@ angular.module("template/datepicker/year.html", []).run(["$templateCache", funct
4034
4590
  " <thead>\n" +
4035
4591
  " <tr>\n" +
4036
4592
  " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
4037
- " <th colspan=\"3\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
4593
+ " <th colspan=\"3\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
4038
4594
  " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
4039
4595
  " </tr>\n" +
4040
4596
  " </thead>\n" +
@@ -4051,7 +4607,8 @@ angular.module("template/datepicker/year.html", []).run(["$templateCache", funct
4051
4607
 
4052
4608
  angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
4053
4609
  $templateCache.put("template/modal/backdrop.html",
4054
- "<div class=\"modal-backdrop fade {{ backdropClass }}\"\n" +
4610
+ "<div class=\"modal-backdrop\"\n" +
4611
+ " modal-animation-class=\"fade\"\n" +
4055
4612
  " ng-class=\"{in: animate}\"\n" +
4056
4613
  " ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
4057
4614
  "></div>\n" +
@@ -4060,33 +4617,51 @@ angular.module("template/modal/backdrop.html", []).run(["$templateCache", functi
4060
4617
 
4061
4618
  angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
4062
4619
  $templateCache.put("template/modal/window.html",
4063
- "<div tabindex=\"-1\" role=\"dialog\" class=\"modal fade\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
4064
- " <div class=\"modal-dialog\" ng-class=\"{'modal-sm': size == 'sm', 'modal-lg': size == 'lg'}\"><div class=\"modal-content\" modal-transclude></div></div>\n" +
4065
- "</div>");
4620
+ "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
4621
+ " modal-animation-class=\"fade\"\n" +
4622
+ " ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
4623
+ " <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" modal-transclude></div></div>\n" +
4624
+ "</div>\n" +
4625
+ "");
4066
4626
  }]);
4067
4627
 
4068
4628
  angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
4069
4629
  $templateCache.put("template/pagination/pager.html",
4070
4630
  "<ul class=\"pager\">\n" +
4071
- " <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1)\">{{getText('previous')}}</a></li>\n" +
4072
- " <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1)\">{{getText('next')}}</a></li>\n" +
4631
+ " <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{getText('previous')}}</a></li>\n" +
4632
+ " <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{getText('next')}}</a></li>\n" +
4073
4633
  "</ul>");
4074
4634
  }]);
4075
4635
 
4076
4636
  angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
4077
4637
  $templateCache.put("template/pagination/pagination.html",
4078
4638
  "<ul class=\"pagination\">\n" +
4079
- " <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(1)\">{{getText('first')}}</a></li>\n" +
4080
- " <li ng-if=\"directionLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(page - 1)\">{{getText('previous')}}</a></li>\n" +
4081
- " <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active}\"><a href ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\n" +
4082
- " <li ng-if=\"directionLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(page + 1)\">{{getText('next')}}</a></li>\n" +
4083
- " <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(totalPages)\">{{getText('last')}}</a></li>\n" +
4639
+ " <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(1, $event)\">{{getText('first')}}</a></li>\n" +
4640
+ " <li ng-if=\"directionLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{getText('previous')}}</a></li>\n" +
4641
+ " <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active}\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" +
4642
+ " <li ng-if=\"directionLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{getText('next')}}</a></li>\n" +
4643
+ " <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(totalPages, $event)\">{{getText('last')}}</a></li>\n" +
4084
4644
  "</ul>");
4085
4645
  }]);
4086
4646
 
4647
+ angular.module("template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
4648
+ $templateCache.put("template/tooltip/tooltip-html-popup.html",
4649
+ "<div class=\"tooltip\"\n" +
4650
+ " tooltip-animation-class=\"fade\"\n" +
4651
+ " tooltip-classes\n" +
4652
+ " ng-class=\"{ in: isOpen() }\">\n" +
4653
+ " <div class=\"tooltip-arrow\"></div>\n" +
4654
+ " <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
4655
+ "</div>\n" +
4656
+ "");
4657
+ }]);
4658
+
4087
4659
  angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) {
4088
4660
  $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html",
4089
- "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
4661
+ "<div class=\"tooltip\"\n" +
4662
+ " tooltip-animation-class=\"fade\"\n" +
4663
+ " tooltip-classes\n" +
4664
+ " ng-class=\"{ in: isOpen() }\">\n" +
4090
4665
  " <div class=\"tooltip-arrow\"></div>\n" +
4091
4666
  " <div class=\"tooltip-inner\" bind-html-unsafe=\"content\"></div>\n" +
4092
4667
  "</div>\n" +
@@ -4095,20 +4670,71 @@ angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$tem
4095
4670
 
4096
4671
  angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
4097
4672
  $templateCache.put("template/tooltip/tooltip-popup.html",
4098
- "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
4673
+ "<div class=\"tooltip\"\n" +
4674
+ " tooltip-animation-class=\"fade\"\n" +
4675
+ " tooltip-classes\n" +
4676
+ " ng-class=\"{ in: isOpen() }\">\n" +
4099
4677
  " <div class=\"tooltip-arrow\"></div>\n" +
4100
4678
  " <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
4101
4679
  "</div>\n" +
4102
4680
  "");
4103
4681
  }]);
4104
4682
 
4683
+ angular.module("template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) {
4684
+ $templateCache.put("template/tooltip/tooltip-template-popup.html",
4685
+ "<div class=\"tooltip\"\n" +
4686
+ " tooltip-animation-class=\"fade\"\n" +
4687
+ " tooltip-classes\n" +
4688
+ " ng-class=\"{ in: isOpen() }\">\n" +
4689
+ " <div class=\"tooltip-arrow\"></div>\n" +
4690
+ " <div class=\"tooltip-inner\"\n" +
4691
+ " tooltip-template-transclude=\"contentExp()\"\n" +
4692
+ " tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
4693
+ "</div>\n" +
4694
+ "");
4695
+ }]);
4696
+
4697
+ angular.module("template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
4698
+ $templateCache.put("template/popover/popover-template.html",
4699
+ "<div class=\"popover\"\n" +
4700
+ " tooltip-animation-class=\"fade\"\n" +
4701
+ " tooltip-classes\n" +
4702
+ " ng-class=\"{ in: isOpen() }\">\n" +
4703
+ " <div class=\"arrow\"></div>\n" +
4704
+ "\n" +
4705
+ " <div class=\"popover-inner\">\n" +
4706
+ " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
4707
+ " <div class=\"popover-content\"\n" +
4708
+ " tooltip-template-transclude=\"contentExp()\"\n" +
4709
+ " tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
4710
+ " </div>\n" +
4711
+ "</div>\n" +
4712
+ "");
4713
+ }]);
4714
+
4715
+ angular.module("template/popover/popover-window.html", []).run(["$templateCache", function($templateCache) {
4716
+ $templateCache.put("template/popover/popover-window.html",
4717
+ "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen, fade: animation }\">\n" +
4718
+ " <div class=\"arrow\"></div>\n" +
4719
+ "\n" +
4720
+ " <div class=\"popover-inner\">\n" +
4721
+ " <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
4722
+ " <div class=\"popover-content\" tooltip-template-transclude></div>\n" +
4723
+ " </div>\n" +
4724
+ "</div>\n" +
4725
+ "");
4726
+ }]);
4727
+
4105
4728
  angular.module("template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
4106
4729
  $templateCache.put("template/popover/popover.html",
4107
- "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
4730
+ "<div class=\"popover\"\n" +
4731
+ " tooltip-animation-class=\"fade\"\n" +
4732
+ " tooltip-classes\n" +
4733
+ " ng-class=\"{ in: isOpen() }\">\n" +
4108
4734
  " <div class=\"arrow\"></div>\n" +
4109
4735
  "\n" +
4110
4736
  " <div class=\"popover-inner\">\n" +
4111
- " <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
4737
+ " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
4112
4738
  " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
4113
4739
  " </div>\n" +
4114
4740
  "</div>\n" +
@@ -4117,7 +4743,8 @@ angular.module("template/popover/popover.html", []).run(["$templateCache", funct
4117
4743
 
4118
4744
  angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
4119
4745
  $templateCache.put("template/progressbar/bar.html",
4120
- "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: percent + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>");
4746
+ "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\n" +
4747
+ "");
4121
4748
  }]);
4122
4749
 
4123
4750
  angular.module("template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
@@ -4128,8 +4755,9 @@ angular.module("template/progressbar/progress.html", []).run(["$templateCache",
4128
4755
  angular.module("template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
4129
4756
  $templateCache.put("template/progressbar/progressbar.html",
4130
4757
  "<div class=\"progress\">\n" +
4131
- " <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: percent + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\n" +
4132
- "</div>");
4758
+ " <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\n" +
4759
+ "</div>\n" +
4760
+ "");
4133
4761
  }]);
4134
4762
 
4135
4763
  angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
@@ -4175,12 +4803,12 @@ angular.module("template/timepicker/timepicker.html", []).run(["$templateCache",
4175
4803
  " <td ng-show=\"showMeridian\"></td>\n" +
4176
4804
  " </tr>\n" +
4177
4805
  " <tr>\n" +
4178
- " <td style=\"width:50px;\" class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" +
4179
- " <input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
4806
+ " <td class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" +
4807
+ " <input style=\"width:50px;\" type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
4180
4808
  " </td>\n" +
4181
4809
  " <td>:</td>\n" +
4182
- " <td style=\"width:50px;\" class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
4183
- " <input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
4810
+ " <td class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
4811
+ " <input style=\"width:50px;\" type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
4184
4812
  " </td>\n" +
4185
4813
  " <td ng-show=\"showMeridian\"><button type=\"button\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\n" +
4186
4814
  " </tr>\n" +
@@ -4209,3 +4837,4 @@ angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCac
4209
4837
  "</ul>\n" +
4210
4838
  "");
4211
4839
  }]);
4840
+ !angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>');