angular-ui-bootstrap-rails 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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>');