angular-ui-bootstrap-rails 0.13.0 → 0.13.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f1ee13223e10e01488c80f62c38379b74ba8356f
4
- data.tar.gz: 739f975e2c2127906e92f7f7e0874fe8f860c152
3
+ metadata.gz: 59d59d03b0afa5ce0e3fcf95c11e8b109ea2b778
4
+ data.tar.gz: 2232fbd146aeba4da61b47fad18ec41397c6add8
5
5
  SHA512:
6
- metadata.gz: bf506d4e2f2a5a15d9734806559796fcfadcbf1e798f39b2e1924a8e36cc6648a880fe0832ee75cd48a4fe5db0f3518d4701b6c459314e1342a8e4ba4de7df61
7
- data.tar.gz: 81bdc9fb3544c92f857ac6308ab304dcbcfa6ce7153f6392f0eceb3557e7ecb5b96f5e4e903d982c2e1e658235973d9a99e74a210caeef197bba325260e8f84f
6
+ metadata.gz: 09cbf801cb2571e2cacca5f3206eb24b2a1dd5743dd5b3f0630a8f81cc39956b15e39b0b60f5b818175353f68f3c2f7fc39c3c2a1a544f48ee93b6b675bc32d1
7
+ data.tar.gz: 2aa07b54f3147d270336f18aae6e51f6274d79dac79af00b326692af75463a6ac5ca054d661d65cb876a0d3a3f7c0411950a65b50a62202211109c84461b588e
@@ -1,7 +1,7 @@
1
1
  module AngularUI
2
2
  module Bootstrap
3
3
  module Rails
4
- VERSION = "0.13.0"
4
+ VERSION = "0.13.3"
5
5
  end
6
6
  end
7
7
  end
@@ -2,11 +2,11 @@
2
2
  * angular-ui-bootstrap
3
3
  * http://angular-ui.github.io/bootstrap/
4
4
 
5
- * Version: 0.13.0 - 2015-05-02
5
+ * Version: 0.13.3 - 2015-08-09
6
6
  * License: MIT
7
7
  */
8
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"]);
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-html.html","template/popover/popover-template.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]);
10
10
  angular.module('ui.bootstrap.collapse', [])
11
11
 
12
12
  .directive('collapse', ['$animate', function ($animate) {
@@ -14,7 +14,11 @@ angular.module('ui.bootstrap.collapse', [])
14
14
  return {
15
15
  link: function (scope, element, attrs) {
16
16
  function expand() {
17
- element.removeClass('collapse').addClass('collapsing');
17
+ element.removeClass('collapse')
18
+ .addClass('collapsing')
19
+ .attr('aria-expanded', true)
20
+ .attr('aria-hidden', false);
21
+
18
22
  $animate.addClass(element, 'in', {
19
23
  to: { height: element[0].scrollHeight + 'px' }
20
24
  }).then(expandDone);
@@ -26,6 +30,10 @@ angular.module('ui.bootstrap.collapse', [])
26
30
  }
27
31
 
28
32
  function collapse() {
33
+ if(! element.hasClass('collapse') && ! element.hasClass('in')) {
34
+ return collapseDone();
35
+ }
36
+
29
37
  element
30
38
  // IMPORTANT: The height must be set before adding "collapsing" class.
31
39
  // Otherwise, the browser attempts to animate from height 0 (in
@@ -34,7 +42,9 @@ angular.module('ui.bootstrap.collapse', [])
34
42
  // initially all panel collapse have the collapse class, this removal
35
43
  // prevents the animation from jumping to collapsed state
36
44
  .removeClass('collapse')
37
- .addClass('collapsing');
45
+ .addClass('collapsing')
46
+ .attr('aria-expanded', false)
47
+ .attr('aria-hidden', true);
38
48
 
39
49
  $animate.removeClass(element, 'in', {
40
50
  to: {height: '0'}
@@ -105,11 +115,14 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
105
115
  // and adds an accordion CSS class to itself element.
106
116
  .directive('accordion', function () {
107
117
  return {
108
- restrict:'EA',
109
- controller:'AccordionController',
118
+ restrict: 'EA',
119
+ controller: 'AccordionController',
120
+ controllerAs: 'accordion',
110
121
  transclude: true,
111
122
  replace: false,
112
- templateUrl: 'template/accordion/accordion.html'
123
+ templateUrl: function(element, attrs) {
124
+ return attrs.templateUrl || 'template/accordion/accordion.html';
125
+ }
113
126
  };
114
127
  })
115
128
 
@@ -120,7 +133,9 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
120
133
  restrict:'EA',
121
134
  transclude:true, // It transcludes the contents of the directive into the template
122
135
  replace: true, // The element containing the directive will be replaced with the template
123
- templateUrl:'template/accordion/accordion-group.html',
136
+ templateUrl: function(element, attrs) {
137
+ return attrs.templateUrl || 'template/accordion/accordion-group.html';
138
+ },
124
139
  scope: {
125
140
  heading: '@', // Interpolate the heading attribute onto this scope
126
141
  isOpen: '=?',
@@ -181,8 +196,8 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
181
196
  link: function(scope, element, attr, controller) {
182
197
  scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
183
198
  if ( heading ) {
184
- element.html('');
185
- element.append(heading);
199
+ element.find('span').html('');
200
+ element.find('span').append(heading);
186
201
  }
187
202
  });
188
203
  }
@@ -194,17 +209,20 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
194
209
  angular.module('ui.bootstrap.alert', [])
195
210
 
196
211
  .controller('AlertController', ['$scope', '$attrs', function ($scope, $attrs) {
197
- $scope.closeable = 'close' in $attrs;
212
+ $scope.closeable = !!$attrs.close;
198
213
  this.close = $scope.close;
199
214
  }])
200
215
 
201
216
  .directive('alert', function () {
202
217
  return {
203
- restrict:'EA',
204
- controller:'AlertController',
205
- templateUrl:'template/alert/alert.html',
206
- transclude:true,
207
- replace:true,
218
+ restrict: 'EA',
219
+ controller: 'AlertController',
220
+ controllerAs: 'alert',
221
+ templateUrl: function(element, attrs) {
222
+ return attrs.templateUrl || 'template/alert/alert.html';
223
+ },
224
+ transclude: true,
225
+ replace: true,
208
226
  scope: {
209
227
  type: '@',
210
228
  close: '&'
@@ -225,14 +243,19 @@ angular.module('ui.bootstrap.alert', [])
225
243
 
226
244
  angular.module('ui.bootstrap.bindHtml', [])
227
245
 
228
- .directive('bindHtmlUnsafe', function () {
246
+ .value('$bindHtmlUnsafeSuppressDeprecated', false)
247
+
248
+ .directive('bindHtmlUnsafe', ['$log', '$bindHtmlUnsafeSuppressDeprecated', function ($log, $bindHtmlUnsafeSuppressDeprecated) {
229
249
  return function (scope, element, attr) {
250
+ if (!$bindHtmlUnsafeSuppressDeprecated) {
251
+ $log.warn('bindHtmlUnsafe is now deprecated. Use ngBindHtml instead');
252
+ }
230
253
  element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
231
254
  scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
232
255
  element.html(value || '');
233
256
  });
234
257
  };
235
- });
258
+ }]);
236
259
  angular.module('ui.bootstrap.buttons', [])
237
260
 
238
261
  .constant('buttonConfig', {
@@ -249,6 +272,7 @@ angular.module('ui.bootstrap.buttons', [])
249
272
  return {
250
273
  require: ['btnRadio', 'ngModel'],
251
274
  controller: 'ButtonsController',
275
+ controllerAs: 'buttons',
252
276
  link: function (scope, element, attrs, ctrls) {
253
277
  var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
254
278
 
@@ -259,6 +283,10 @@ angular.module('ui.bootstrap.buttons', [])
259
283
 
260
284
  //ui->model
261
285
  element.bind(buttonsCtrl.toggleEvent, function () {
286
+ if (attrs.disabled) {
287
+ return;
288
+ }
289
+
262
290
  var isActive = element.hasClass(buttonsCtrl.activeClass);
263
291
 
264
292
  if (!isActive || angular.isDefined(attrs.uncheckable)) {
@@ -276,6 +304,7 @@ angular.module('ui.bootstrap.buttons', [])
276
304
  return {
277
305
  require: ['btnCheckbox', 'ngModel'],
278
306
  controller: 'ButtonsController',
307
+ controllerAs: 'button',
279
308
  link: function (scope, element, attrs, ctrls) {
280
309
  var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
281
310
 
@@ -299,6 +328,10 @@ angular.module('ui.bootstrap.buttons', [])
299
328
 
300
329
  //ui->model
301
330
  element.bind(buttonsCtrl.toggleEvent, function () {
331
+ if (attrs.disabled) {
332
+ return;
333
+ }
334
+
302
335
  scope.$apply(function () {
303
336
  ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
304
337
  ngModelCtrl.$render();
@@ -317,9 +350,12 @@ angular.module('ui.bootstrap.buttons', [])
317
350
  *
318
351
  */
319
352
  angular.module('ui.bootstrap.carousel', [])
320
- .controller('CarouselController', ['$scope', '$interval', '$animate', function ($scope, $interval, $animate) {
353
+ .controller('CarouselController', ['$scope', '$element', '$interval', '$animate', function ($scope, $element, $interval, $animate) {
321
354
  var self = this,
322
355
  slides = self.slides = $scope.slides = [],
356
+ NEW_ANIMATE = angular.version.minor >= 4,
357
+ NO_TRANSITION = 'uib-noTransition',
358
+ SLIDE_DIRECTION = 'uib-slideDirection',
323
359
  currentIndex = -1,
324
360
  currentInterval, isPlaying;
325
361
  self.currentSlide = null;
@@ -327,33 +363,52 @@ angular.module('ui.bootstrap.carousel', [])
327
363
  var destroyed = false;
328
364
  /* direction: "prev" or "next" */
329
365
  self.select = $scope.select = function(nextSlide, direction) {
330
- var nextIndex = self.indexOfSlide(nextSlide);
366
+ var nextIndex = $scope.indexOfSlide(nextSlide);
331
367
  //Decide direction if it's not given
332
368
  if (direction === undefined) {
333
369
  direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
334
370
  }
335
- if (nextSlide && nextSlide !== self.currentSlide) {
336
- goNext();
371
+ //Prevent this user-triggered transition from occurring if there is already one in progress
372
+ if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) {
373
+ goNext(nextSlide, nextIndex, direction);
337
374
  }
338
- function goNext() {
339
- // Scope has been destroyed, stop here.
340
- if (destroyed) { return; }
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() {
375
+ };
376
+
377
+ function goNext(slide, index, direction) {
378
+ // Scope has been destroyed, stop here.
379
+ if (destroyed) { return; }
380
+
381
+ angular.extend(slide, {direction: direction, active: true});
382
+ angular.extend(self.currentSlide || {}, {direction: direction, active: false});
383
+ if ($animate.enabled() && !$scope.noTransition && !$scope.$currentTransition &&
384
+ slide.$element && self.slides.length > 1) {
385
+ slide.$element.data(SLIDE_DIRECTION, slide.direction);
386
+ if (self.currentSlide && self.currentSlide.$element) {
387
+ self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction);
388
+ }
389
+
390
+ $scope.$currentTransition = true;
391
+ if (NEW_ANIMATE) {
392
+ $animate.on('addClass', slide.$element, function (element, phase) {
393
+ if (phase === 'close') {
394
+ $scope.$currentTransition = null;
395
+ $animate.off('addClass', element);
396
+ }
397
+ });
398
+ } else {
399
+ slide.$element.one('$animate:close', function closeFn() {
347
400
  $scope.$currentTransition = null;
348
401
  });
349
402
  }
350
-
351
- self.currentSlide = nextSlide;
352
- currentIndex = nextIndex;
353
- //every time you change slides, reset the timer
354
- restartTimer();
355
403
  }
356
- };
404
+
405
+ self.currentSlide = slide;
406
+ currentIndex = index;
407
+
408
+ //every time you change slides, reset the timer
409
+ restartTimer();
410
+ }
411
+
357
412
  $scope.$on('$destroy', function () {
358
413
  destroyed = true;
359
414
  });
@@ -378,26 +433,30 @@ angular.module('ui.bootstrap.carousel', [])
378
433
  };
379
434
 
380
435
  /* Allow outside people to call indexOf on slides array */
381
- self.indexOfSlide = function(slide) {
436
+ $scope.indexOfSlide = function(slide) {
382
437
  return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
383
438
  };
384
439
 
385
440
  $scope.next = function() {
386
441
  var newIndex = (self.getCurrentIndex() + 1) % slides.length;
387
442
 
388
- //Prevent this user-triggered transition from occurring if there is already one in progress
389
- if (!$scope.$currentTransition) {
390
- return self.select(getSlideByIndex(newIndex), 'next');
443
+ if (newIndex === 0 && $scope.noWrap()) {
444
+ $scope.pause();
445
+ return;
391
446
  }
447
+
448
+ return self.select(getSlideByIndex(newIndex), 'next');
392
449
  };
393
450
 
394
451
  $scope.prev = function() {
395
452
  var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
396
453
 
397
- //Prevent this user-triggered transition from occurring if there is already one in progress
398
- if (!$scope.$currentTransition) {
399
- return self.select(getSlideByIndex(newIndex), 'prev');
454
+ if ($scope.noWrap() && newIndex === slides.length - 1){
455
+ $scope.pause();
456
+ return;
400
457
  }
458
+
459
+ return self.select(getSlideByIndex(newIndex), 'prev');
401
460
  };
402
461
 
403
462
  $scope.isActive = function(slide) {
@@ -424,7 +483,7 @@ angular.module('ui.bootstrap.carousel', [])
424
483
 
425
484
  function timerFn() {
426
485
  var interval = +$scope.interval;
427
- if (isPlaying && !isNaN(interval) && interval > 0) {
486
+ if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
428
487
  $scope.next();
429
488
  } else {
430
489
  $scope.pause();
@@ -476,8 +535,17 @@ angular.module('ui.bootstrap.carousel', [])
476
535
  } else if (currentIndex > index) {
477
536
  currentIndex--;
478
537
  }
538
+
539
+ //clean the currentSlide when no more slide
540
+ if (slides.length === 0) {
541
+ self.currentSlide = null;
542
+ }
479
543
  };
480
544
 
545
+ $scope.$watch('noTransition', function(noTransition) {
546
+ $element.data(NO_TRANSITION, noTransition);
547
+ });
548
+
481
549
  }])
482
550
 
483
551
  /**
@@ -524,12 +592,16 @@ angular.module('ui.bootstrap.carousel', [])
524
592
  transclude: true,
525
593
  replace: true,
526
594
  controller: 'CarouselController',
595
+ controllerAs: 'carousel',
527
596
  require: 'carousel',
528
- templateUrl: 'template/carousel/carousel.html',
597
+ templateUrl: function(element, attrs) {
598
+ return attrs.templateUrl || 'template/carousel/carousel.html';
599
+ },
529
600
  scope: {
530
601
  interval: '=',
531
602
  noTransition: '=',
532
- noPause: '='
603
+ noPause: '=',
604
+ noWrap: '&'
533
605
  }
534
606
  };
535
607
  }])
@@ -582,7 +654,9 @@ function CarouselDemoCtrl($scope) {
582
654
  restrict: 'EA',
583
655
  transclude: true,
584
656
  replace: true,
585
- templateUrl: 'template/carousel/slide.html',
657
+ templateUrl: function(element, attrs) {
658
+ return attrs.templateUrl || 'template/carousel/slide.html';
659
+ },
586
660
  scope: {
587
661
  active: '=?',
588
662
  index: '=?'
@@ -604,23 +678,47 @@ function CarouselDemoCtrl($scope) {
604
678
  })
605
679
 
606
680
  .animation('.item', [
607
- '$animate',
608
- function ($animate) {
681
+ '$injector', '$animate',
682
+ function ($injector, $animate) {
683
+ var NO_TRANSITION = 'uib-noTransition',
684
+ SLIDE_DIRECTION = 'uib-slideDirection',
685
+ $animateCss = null;
686
+
687
+ if ($injector.has('$animateCss')) {
688
+ $animateCss = $injector.get('$animateCss');
689
+ }
690
+
691
+ function removeClass(element, className, callback) {
692
+ element.removeClass(className);
693
+ if (callback) {
694
+ callback();
695
+ }
696
+ }
697
+
609
698
  return {
610
699
  beforeAddClass: function (element, className, done) {
611
700
  // Due to transclusion, noTransition property is on parent's scope
612
701
  if (className == 'active' && element.parent() &&
613
- !element.parent().scope().noTransition) {
702
+ !element.parent().data(NO_TRANSITION)) {
614
703
  var stopped = false;
615
- var direction = element.isolateScope().direction;
704
+ var direction = element.data(SLIDE_DIRECTION);
616
705
  var directionClass = direction == 'next' ? 'left' : 'right';
706
+ var removeClassFn = removeClass.bind(this, element,
707
+ directionClass + ' ' + direction, done);
617
708
  element.addClass(direction);
618
- $animate.addClass(element, directionClass).then(function () {
619
- if (!stopped) {
620
- element.removeClass(directionClass + ' ' + direction);
621
- }
622
- done();
623
- });
709
+
710
+ if ($animateCss) {
711
+ $animateCss(element, {addClass: directionClass})
712
+ .start()
713
+ .done(removeClassFn);
714
+ } else {
715
+ $animate.addClass(element, directionClass).then(function () {
716
+ if (!stopped) {
717
+ removeClassFn();
718
+ }
719
+ done();
720
+ });
721
+ }
624
722
 
625
723
  return function () {
626
724
  stopped = true;
@@ -630,17 +728,25 @@ function ($animate) {
630
728
  },
631
729
  beforeRemoveClass: function (element, className, done) {
632
730
  // Due to transclusion, noTransition property is on parent's scope
633
- if (className == 'active' && element.parent() &&
634
- !element.parent().scope().noTransition) {
731
+ if (className === 'active' && element.parent() &&
732
+ !element.parent().data(NO_TRANSITION)) {
635
733
  var stopped = false;
636
- var direction = element.isolateScope().direction;
734
+ var direction = element.data(SLIDE_DIRECTION);
637
735
  var directionClass = direction == 'next' ? 'left' : 'right';
638
- $animate.addClass(element, directionClass).then(function () {
639
- if (!stopped) {
640
- element.removeClass(directionClass);
641
- }
642
- done();
643
- });
736
+ var removeClassFn = removeClass.bind(this, element, directionClass, done);
737
+
738
+ if ($animateCss) {
739
+ $animateCss(element, {addClass: directionClass})
740
+ .start()
741
+ .done(removeClassFn);
742
+ } else {
743
+ $animate.addClass(element, directionClass).then(function () {
744
+ if (!stopped) {
745
+ removeClassFn();
746
+ }
747
+ done();
748
+ });
749
+ }
644
750
  return function () {
645
751
  stopped = true;
646
752
  };
@@ -656,7 +762,7 @@ function ($animate) {
656
762
 
657
763
  angular.module('ui.bootstrap.dateparser', [])
658
764
 
659
- .service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {
765
+ .service('dateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
660
766
  // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
661
767
  var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
662
768
 
@@ -709,6 +815,10 @@ angular.module('ui.bootstrap.dateparser', [])
709
815
  regex: '(?:0|1)[0-9]|2[0-3]',
710
816
  apply: function(value) { this.hours = +value; }
711
817
  },
818
+ 'hh': {
819
+ regex: '0[0-9]|1[0-2]',
820
+ apply: function(value) { this.hours = +value; }
821
+ },
712
822
  'H': {
713
823
  regex: '1?[0-9]|2[0-3]',
714
824
  apply: function(value) { this.hours = +value; }
@@ -732,6 +842,18 @@ angular.module('ui.bootstrap.dateparser', [])
732
842
  's': {
733
843
  regex: '[0-9]|[1-5][0-9]',
734
844
  apply: function(value) { this.seconds = +value; }
845
+ },
846
+ 'a': {
847
+ regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
848
+ apply: function(value) {
849
+ if (this.hours === 12) {
850
+ this.hours = 0;
851
+ }
852
+
853
+ if (value === 'PM') {
854
+ this.hours += 12;
855
+ }
856
+ }
735
857
  }
736
858
  };
737
859
 
@@ -781,7 +903,7 @@ angular.module('ui.bootstrap.dateparser', [])
781
903
 
782
904
  if ( results && results.length ) {
783
905
  var fields, dt;
784
- if (baseDate) {
906
+ if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
785
907
  fields = {
786
908
  year: baseDate.getFullYear(),
787
909
  month: baseDate.getMonth(),
@@ -792,6 +914,9 @@ angular.module('ui.bootstrap.dateparser', [])
792
914
  milliseconds: baseDate.getMilliseconds()
793
915
  };
794
916
  } else {
917
+ if (baseDate) {
918
+ $log.warn('dateparser:', 'baseDate is not a valid date');
919
+ }
795
920
  fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
796
921
  }
797
922
 
@@ -985,6 +1110,8 @@ angular.module('ui.bootstrap.position', [])
985
1110
 
986
1111
  angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
987
1112
 
1113
+ .value('$datepickerSuppressError', false)
1114
+
988
1115
  .constant('datepickerConfig', {
989
1116
  formatDay: 'dd',
990
1117
  formatMonth: 'MMMM',
@@ -1003,7 +1130,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1003
1130
  shortcutPropagation: false
1004
1131
  })
1005
1132
 
1006
- .controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
1133
+ .controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'datepickerConfig', '$datepickerSuppressError', function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError) {
1007
1134
  var self = this,
1008
1135
  ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
1009
1136
 
@@ -1012,8 +1139,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1012
1139
 
1013
1140
  // Configuration attributes
1014
1141
  angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
1015
- 'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function( key, index ) {
1016
- self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
1142
+ 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function( key, index ) {
1143
+ self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
1017
1144
  });
1018
1145
 
1019
1146
  // Watchable date attributes
@@ -1028,8 +1155,22 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1028
1155
  }
1029
1156
  });
1030
1157
 
1158
+ angular.forEach(['minMode', 'maxMode'], function( key ) {
1159
+ if ( $attrs[key] ) {
1160
+ $scope.$parent.$watch($parse($attrs[key]), function(value) {
1161
+ self[key] = angular.isDefined(value) ? value : $attrs[key];
1162
+ $scope[key] = self[key];
1163
+ if ((key == 'minMode' && self.modes.indexOf( $scope.datepickerMode ) < self.modes.indexOf( self[key] )) || (key == 'maxMode' && self.modes.indexOf( $scope.datepickerMode ) > self.modes.indexOf( self[key] ))) {
1164
+ $scope.datepickerMode = self[key];
1165
+ }
1166
+ });
1167
+ } else {
1168
+ self[key] = datepickerConfig[key] || null;
1169
+ $scope[key] = self[key];
1170
+ }
1171
+ });
1172
+
1031
1173
  $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
1032
- $scope.maxMode = self.maxMode;
1033
1174
  $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
1034
1175
 
1035
1176
  if(angular.isDefined($attrs.initDate)) {
@@ -1067,10 +1208,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1067
1208
 
1068
1209
  if ( isValid ) {
1069
1210
  this.activeDate = date;
1070
- } else {
1211
+ } else if ( !$datepickerSuppressError ) {
1071
1212
  $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
1072
1213
  }
1073
- ngModelCtrl.$setValidity('date', isValid);
1074
1214
  }
1075
1215
  this.refreshView();
1076
1216
  };
@@ -1080,7 +1220,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1080
1220
  this._refreshView();
1081
1221
 
1082
1222
  var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
1083
- ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
1223
+ ngModelCtrl.$setValidity('dateDisabled', !date || (this.element && !this.isDisabled(date)));
1084
1224
  }
1085
1225
  };
1086
1226
 
@@ -1100,9 +1240,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1100
1240
  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})));
1101
1241
  };
1102
1242
 
1103
- this.customClass = function( date ) {
1104
- return $scope.customClass({date: date, mode: $scope.datepickerMode});
1105
- };
1243
+ this.customClass = function( date ) {
1244
+ return $scope.customClass({date: date, mode: $scope.datepickerMode});
1245
+ };
1106
1246
 
1107
1247
  // Split array into smaller arrays
1108
1248
  this.split = function(arr, size) {
@@ -1113,6 +1253,17 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1113
1253
  return arrays;
1114
1254
  };
1115
1255
 
1256
+ // Fix a hard-reprodusible bug with timezones
1257
+ // The bug depends on OS, browser, current timezone and current date
1258
+ // i.e.
1259
+ // var date = new Date(2014, 0, 1);
1260
+ // console.log(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
1261
+ // can result in "2013 11 31 23" because of the bug.
1262
+ this.fixTimeZone = function(date) {
1263
+ var hours = date.getHours();
1264
+ date.setHours(hours === 23 ? hours + 2 : 0);
1265
+ };
1266
+
1116
1267
  $scope.select = function( date ) {
1117
1268
  if ( $scope.datepickerMode === self.minMode ) {
1118
1269
  var dt = ngModelCtrl.$viewValue ? new Date( ngModelCtrl.$viewValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
@@ -1146,9 +1297,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1146
1297
  $scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };
1147
1298
 
1148
1299
  var focusElement = function() {
1149
- $timeout(function() {
1150
- self.element[0].focus();
1151
- }, 0 , false);
1300
+ self.element[0].focus();
1152
1301
  };
1153
1302
 
1154
1303
  // Listen for focus requests from popup directive
@@ -1186,21 +1335,22 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1186
1335
  return {
1187
1336
  restrict: 'EA',
1188
1337
  replace: true,
1189
- templateUrl: 'template/datepicker/datepicker.html',
1338
+ templateUrl: function(element, attrs) {
1339
+ return attrs.templateUrl || 'template/datepicker/datepicker.html';
1340
+ },
1190
1341
  scope: {
1191
1342
  datepickerMode: '=?',
1192
1343
  dateDisabled: '&',
1193
1344
  customClass: '&',
1194
1345
  shortcutPropagation: '&?'
1195
1346
  },
1196
- require: ['datepicker', '?^ngModel'],
1347
+ require: ['datepicker', '^ngModel'],
1197
1348
  controller: 'DatepickerController',
1349
+ controllerAs: 'datepicker',
1198
1350
  link: function(scope, element, attrs, ctrls) {
1199
1351
  var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
1200
1352
 
1201
- if ( ngModelCtrl ) {
1202
- datepickerCtrl.init( ngModelCtrl );
1203
- }
1353
+ datepickerCtrl.init(ngModelCtrl);
1204
1354
  }
1205
1355
  };
1206
1356
  })
@@ -1223,10 +1373,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1223
1373
  }
1224
1374
 
1225
1375
  function getDates(startDate, n) {
1226
- var dates = new Array(n), current = new Date(startDate), i = 0;
1227
- current.setHours(12); // Prevent repeated dates because of timezone bug
1376
+ var dates = new Array(n), current = new Date(startDate), i = 0, date;
1228
1377
  while ( i < n ) {
1229
- dates[i++] = new Date(current);
1378
+ date = new Date(current);
1379
+ ctrl.fixTimeZone(date);
1380
+ dates[i++] = date;
1230
1381
  current.setDate( current.getDate() + 1 );
1231
1382
  }
1232
1383
  return dates;
@@ -1328,10 +1479,13 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1328
1479
 
1329
1480
  ctrl._refreshView = function() {
1330
1481
  var months = new Array(12),
1331
- year = ctrl.activeDate.getFullYear();
1482
+ year = ctrl.activeDate.getFullYear(),
1483
+ date;
1332
1484
 
1333
1485
  for ( var i = 0; i < 12; i++ ) {
1334
- months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {
1486
+ date = new Date(year, i, 1);
1487
+ ctrl.fixTimeZone(date);
1488
+ months[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatMonth), {
1335
1489
  uid: scope.uniqueId + '-' + i
1336
1490
  });
1337
1491
  }
@@ -1388,10 +1542,12 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1388
1542
  }
1389
1543
 
1390
1544
  ctrl._refreshView = function() {
1391
- var years = new Array(range);
1545
+ var years = new Array(range), date;
1392
1546
 
1393
1547
  for ( var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++ ) {
1394
- years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {
1548
+ date = new Date(start + i, 0, 1);
1549
+ ctrl.fixTimeZone(date);
1550
+ years[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatYear), {
1395
1551
  uid: scope.uniqueId + '-' + i
1396
1552
  });
1397
1553
  }
@@ -1432,6 +1588,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1432
1588
 
1433
1589
  .constant('datepickerPopupConfig', {
1434
1590
  datepickerPopup: 'yyyy-MM-dd',
1591
+ datepickerPopupTemplateUrl: 'template/datepicker/popup.html',
1592
+ datepickerTemplateUrl: 'template/datepicker/datepicker.html',
1435
1593
  html5Types: {
1436
1594
  date: 'yyyy-MM-dd',
1437
1595
  'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
@@ -1442,11 +1600,12 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
1442
1600
  closeText: 'Done',
1443
1601
  closeOnDateSelection: true,
1444
1602
  appendToBody: false,
1445
- showButtonBar: true
1603
+ showButtonBar: true,
1604
+ onOpenFocus: true
1446
1605
  })
1447
1606
 
1448
- .directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig',
1449
- function ($compile, $parse, $document, $position, dateFilter, dateParser, datepickerPopupConfig) {
1607
+ .directive('datepickerPopup', ['$compile', '$parse', '$document', '$rootScope', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig', '$timeout',
1608
+ function ($compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout) {
1450
1609
  return {
1451
1610
  restrict: 'EA',
1452
1611
  require: 'ngModel',
@@ -1461,7 +1620,10 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1461
1620
  link: function(scope, element, attrs, ngModel) {
1462
1621
  var dateFormat,
1463
1622
  closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
1464
- appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
1623
+ appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody,
1624
+ onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus,
1625
+ datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl,
1626
+ datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
1465
1627
 
1466
1628
  scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
1467
1629
 
@@ -1502,7 +1664,8 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1502
1664
  var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
1503
1665
  popupEl.attr({
1504
1666
  'ng-model': 'date',
1505
- 'ng-change': 'dateSelection()'
1667
+ 'ng-change': 'dateSelection(date)',
1668
+ 'template-url': datepickerPopupTemplateUrl
1506
1669
  });
1507
1670
 
1508
1671
  function cameltoDash( string ){
@@ -1511,6 +1674,8 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1511
1674
 
1512
1675
  // datepicker element
1513
1676
  var datepickerEl = angular.element(popupEl.children()[0]);
1677
+ datepickerEl.attr('template-url', datepickerTemplateUrl);
1678
+
1514
1679
  if (isHtml5DateInput) {
1515
1680
  if (attrs.type == 'month') {
1516
1681
  datepickerEl.attr('datepicker-mode', '"month"');
@@ -1520,7 +1685,7 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1520
1685
 
1521
1686
  if ( attrs.datepickerOptions ) {
1522
1687
  var options = scope.$parent.$eval(attrs.datepickerOptions);
1523
- if(options.initDate) {
1688
+ if(options && options.initDate) {
1524
1689
  scope.initDate = options.initDate;
1525
1690
  datepickerEl.attr( 'init-date', 'initDate' );
1526
1691
  delete options.initDate;
@@ -1531,7 +1696,7 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1531
1696
  }
1532
1697
 
1533
1698
  scope.watchData = {};
1534
- angular.forEach(['minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function( key ) {
1699
+ angular.forEach(['minMode', 'maxMode', 'minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function( key ) {
1535
1700
  if ( attrs[key] ) {
1536
1701
  var getAttribute = $parse(attrs[key]);
1537
1702
  scope.$parent.$watch(getAttribute, function(value){
@@ -1543,7 +1708,7 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1543
1708
  if ( key === 'datepickerMode' ) {
1544
1709
  var setAttribute = getAttribute.assign;
1545
1710
  scope.$watch('watchData.' + key, function(value, oldvalue) {
1546
- if ( value !== oldvalue ) {
1711
+ if ( angular.isFunction(setAttribute) && value !== oldvalue ) {
1547
1712
  setAttribute(scope.$parent, value);
1548
1713
  }
1549
1714
  });
@@ -1573,7 +1738,7 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1573
1738
  } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
1574
1739
  return viewValue;
1575
1740
  } else if (angular.isString(viewValue)) {
1576
- var date = dateParser.parse(viewValue, dateFormat, scope.date) || new Date(viewValue);
1741
+ var date = dateParser.parse(viewValue, dateFormat, scope.date);
1577
1742
  if (isNaN(date)) {
1578
1743
  return undefined;
1579
1744
  } else {
@@ -1586,6 +1751,11 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1586
1751
 
1587
1752
  function validator(modelValue, viewValue) {
1588
1753
  var value = modelValue || viewValue;
1754
+
1755
+ if (!attrs.ngRequired && !value) {
1756
+ return true;
1757
+ }
1758
+
1589
1759
  if (angular.isNumber(value)) {
1590
1760
  value = new Date(value);
1591
1761
  }
@@ -1594,7 +1764,7 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1594
1764
  } else if (angular.isDate(value) && !isNaN(value)) {
1595
1765
  return true;
1596
1766
  } else if (angular.isString(value)) {
1597
- var date = dateParser.parse(value, dateFormat) || new Date(value);
1767
+ var date = dateParser.parse(value, dateFormat);
1598
1768
  return !isNaN(date);
1599
1769
  } else {
1600
1770
  return false;
@@ -1623,7 +1793,7 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1623
1793
  if (angular.isDefined(dt)) {
1624
1794
  scope.date = dt;
1625
1795
  }
1626
- var date = scope.date ? dateFilter(scope.date, dateFormat) : '';
1796
+ var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
1627
1797
  element.val(date);
1628
1798
  ngModel.$setViewValue(date);
1629
1799
 
@@ -1635,41 +1805,53 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1635
1805
 
1636
1806
  // Detect changes in the view from the text box
1637
1807
  ngModel.$viewChangeListeners.push(function () {
1638
- scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date) || new Date(ngModel.$viewValue);
1808
+ scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date);
1639
1809
  });
1640
1810
 
1641
1811
  var documentClickBind = function(event) {
1642
- if (scope.isOpen && event.target !== element[0]) {
1812
+ if (scope.isOpen && !element[0].contains(event.target)) {
1643
1813
  scope.$apply(function() {
1644
1814
  scope.isOpen = false;
1645
1815
  });
1646
1816
  }
1647
1817
  };
1648
1818
 
1649
- var keydown = function(evt, noApply) {
1650
- scope.keydown(evt);
1819
+ var inputKeydownBind = function(evt) {
1820
+ if (evt.which === 27 && scope.isOpen) {
1821
+ evt.preventDefault();
1822
+ evt.stopPropagation();
1823
+ scope.$apply(function() {
1824
+ scope.isOpen = false;
1825
+ });
1826
+ element[0].focus();
1827
+ } else if (evt.which === 40 && !scope.isOpen) {
1828
+ evt.preventDefault();
1829
+ evt.stopPropagation();
1830
+ scope.$apply(function() {
1831
+ scope.isOpen = true;
1832
+ });
1833
+ }
1651
1834
  };
1652
- element.bind('keydown', keydown);
1835
+ element.bind('keydown', inputKeydownBind);
1653
1836
 
1654
1837
  scope.keydown = function(evt) {
1655
1838
  if (evt.which === 27) {
1656
- evt.preventDefault();
1657
- if (scope.isOpen) {
1658
- evt.stopPropagation();
1659
- }
1660
- scope.close();
1661
- } else if (evt.which === 40 && !scope.isOpen) {
1662
- scope.isOpen = true;
1839
+ scope.isOpen = false;
1840
+ element[0].focus();
1663
1841
  }
1664
1842
  };
1665
1843
 
1666
1844
  scope.$watch('isOpen', function(value) {
1667
1845
  if (value) {
1668
- scope.$broadcast('datepicker.focus');
1669
1846
  scope.position = appendToBody ? $position.offset(element) : $position.position(element);
1670
1847
  scope.position.top = scope.position.top + element.prop('offsetHeight');
1671
1848
 
1672
- $document.bind('click', documentClickBind);
1849
+ $timeout(function() {
1850
+ if (onOpenFocus) {
1851
+ scope.$broadcast('datepicker.focus');
1852
+ }
1853
+ $document.bind('click', documentClickBind);
1854
+ }, 0, false);
1673
1855
  } else {
1674
1856
  $document.unbind('click', documentClickBind);
1675
1857
  }
@@ -1704,8 +1886,16 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1704
1886
  }
1705
1887
 
1706
1888
  scope.$on('$destroy', function() {
1889
+ if (scope.isOpen === true) {
1890
+ if (!$rootScope.$$phase) {
1891
+ scope.$apply(function() {
1892
+ scope.isOpen = false;
1893
+ });
1894
+ }
1895
+ }
1896
+
1707
1897
  $popup.remove();
1708
- element.unbind('keydown', keydown);
1898
+ element.unbind('keydown', inputKeydownBind);
1709
1899
  $document.unbind('click', documentClickBind);
1710
1900
  });
1711
1901
  }
@@ -1717,12 +1907,8 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
1717
1907
  restrict:'EA',
1718
1908
  replace: true,
1719
1909
  transclude: true,
1720
- templateUrl: 'template/datepicker/popup.html',
1721
- link:function (scope, element, attrs) {
1722
- element.bind('click', function(event) {
1723
- event.preventDefault();
1724
- event.stopPropagation();
1725
- });
1910
+ templateUrl: function(element, attrs) {
1911
+ return attrs.templateUrl || 'template/datepicker/popup.html';
1726
1912
  }
1727
1913
  };
1728
1914
  });
@@ -1739,11 +1925,11 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1739
1925
  this.open = function( dropdownScope ) {
1740
1926
  if ( !openScope ) {
1741
1927
  $document.bind('click', closeDropdown);
1742
- $document.bind('keydown', escapeKeyBind);
1928
+ $document.bind('keydown', keybindFilter);
1743
1929
  }
1744
1930
 
1745
1931
  if ( openScope && openScope !== dropdownScope ) {
1746
- openScope.isOpen = false;
1932
+ openScope.isOpen = false;
1747
1933
  }
1748
1934
 
1749
1935
  openScope = dropdownScope;
@@ -1753,7 +1939,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1753
1939
  if ( openScope === dropdownScope ) {
1754
1940
  openScope = null;
1755
1941
  $document.unbind('click', closeDropdown);
1756
- $document.unbind('keydown', escapeKeyBind);
1942
+ $document.unbind('keydown', keybindFilter);
1757
1943
  }
1758
1944
  };
1759
1945
 
@@ -1766,11 +1952,12 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1766
1952
 
1767
1953
  var toggleElement = openScope.getToggleElement();
1768
1954
  if ( evt && toggleElement && toggleElement[0].contains(evt.target) ) {
1769
- return;
1955
+ return;
1770
1956
  }
1771
1957
 
1772
- var $element = openScope.getElement();
1773
- if( evt && openScope.getAutoClose() === 'outsideClick' && $element && $element[0].contains(evt.target) ) {
1958
+ var dropdownElement = openScope.getDropdownElement();
1959
+ if (evt && openScope.getAutoClose() === 'outsideClick' &&
1960
+ dropdownElement && dropdownElement[0].contains(evt.target)) {
1774
1961
  return;
1775
1962
  }
1776
1963
 
@@ -1781,22 +1968,30 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1781
1968
  }
1782
1969
  };
1783
1970
 
1784
- var escapeKeyBind = function( evt ) {
1971
+ var keybindFilter = function( evt ) {
1785
1972
  if ( evt.which === 27 ) {
1786
1973
  openScope.focusToggleElement();
1787
1974
  closeDropdown();
1788
1975
  }
1976
+ else if ( openScope.isKeynavEnabled() && /(38|40)/.test(evt.which) && openScope.isOpen ) {
1977
+ evt.preventDefault();
1978
+ evt.stopPropagation();
1979
+ openScope.focusDropdownEntry(evt.which);
1980
+ }
1789
1981
  };
1790
1982
  }])
1791
1983
 
1792
- .controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document) {
1984
+ .controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', '$compile', '$templateRequest', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document, $compile, $templateRequest) {
1793
1985
  var self = this,
1794
- scope = $scope.$new(), // create a child scope so we are not polluting original one
1795
- openClass = dropdownConfig.openClass,
1796
- getIsOpen,
1797
- setIsOpen = angular.noop,
1798
- toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
1799
- appendToBody = false;
1986
+ scope = $scope.$new(), // create a child scope so we are not polluting original one
1987
+ templateScope,
1988
+ openClass = dropdownConfig.openClass,
1989
+ getIsOpen,
1990
+ setIsOpen = angular.noop,
1991
+ toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
1992
+ appendToBody = false,
1993
+ keynavEnabled =false,
1994
+ selectedOption = null;
1800
1995
 
1801
1996
  this.init = function( element ) {
1802
1997
  self.$element = element;
@@ -1811,6 +2006,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1811
2006
  }
1812
2007
 
1813
2008
  appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
2009
+ keynavEnabled = angular.isDefined($attrs.keyboardNav);
1814
2010
 
1815
2011
  if ( appendToBody && self.dropdownMenu ) {
1816
2012
  $document.find('body').append( self.dropdownMenu );
@@ -1841,6 +2037,44 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1841
2037
  return self.$element;
1842
2038
  };
1843
2039
 
2040
+ scope.isKeynavEnabled = function() {
2041
+ return keynavEnabled;
2042
+ };
2043
+
2044
+ scope.focusDropdownEntry = function(keyCode) {
2045
+ var elems = self.dropdownMenu ? //If append to body is used.
2046
+ (angular.element(self.dropdownMenu).find('a')) :
2047
+ (angular.element(self.$element).find('ul').eq(0).find('a'));
2048
+
2049
+ switch (keyCode) {
2050
+ case (40): {
2051
+ if ( !angular.isNumber(self.selectedOption)) {
2052
+ self.selectedOption = 0;
2053
+ } else {
2054
+ self.selectedOption = (self.selectedOption === elems.length -1 ?
2055
+ self.selectedOption :
2056
+ self.selectedOption + 1);
2057
+ }
2058
+ break;
2059
+ }
2060
+ case (38): {
2061
+ if ( !angular.isNumber(self.selectedOption)) {
2062
+ return;
2063
+ } else {
2064
+ self.selectedOption = (self.selectedOption === 0 ?
2065
+ 0 :
2066
+ self.selectedOption - 1);
2067
+ }
2068
+ break;
2069
+ }
2070
+ }
2071
+ elems[self.selectedOption].focus();
2072
+ };
2073
+
2074
+ scope.getDropdownElement = function() {
2075
+ return self.dropdownMenu;
2076
+ };
2077
+
1844
2078
  scope.focusToggleElement = function() {
1845
2079
  if ( self.toggleElement ) {
1846
2080
  self.toggleElement[0].focus();
@@ -1848,32 +2082,68 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1848
2082
  };
1849
2083
 
1850
2084
  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
- });
2085
+ if (appendToBody && self.dropdownMenu) {
2086
+ var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true);
2087
+ var css = {
2088
+ top: pos.top + 'px',
2089
+ display: isOpen ? 'block' : 'none'
2090
+ };
2091
+
2092
+ var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
2093
+ if (!rightalign) {
2094
+ css.left = pos.left + 'px';
2095
+ css.right = 'auto';
2096
+ } else {
2097
+ css.left = 'auto';
2098
+ css.right = (window.innerWidth - (pos.left + self.$element.prop('offsetWidth'))) + 'px';
2099
+ }
2100
+
2101
+ self.dropdownMenu.css(css);
1858
2102
  }
1859
2103
 
1860
- $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);
2104
+ $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass).then(function() {
2105
+ if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
2106
+ toggleInvoker($scope, { open: !!isOpen });
2107
+ }
2108
+ });
1861
2109
 
1862
2110
  if ( isOpen ) {
2111
+ if (self.dropdownMenuTemplateUrl) {
2112
+ $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
2113
+ templateScope = scope.$new();
2114
+ $compile(tplContent.trim())(templateScope, function(dropdownElement) {
2115
+ var newEl = dropdownElement;
2116
+ self.dropdownMenu.replaceWith(newEl);
2117
+ self.dropdownMenu = newEl;
2118
+ });
2119
+ });
2120
+ }
2121
+
1863
2122
  scope.focusToggleElement();
1864
2123
  dropdownService.open( scope );
1865
2124
  } else {
2125
+ if (self.dropdownMenuTemplateUrl) {
2126
+ if (templateScope) {
2127
+ templateScope.$destroy();
2128
+ }
2129
+ var newEl = angular.element('<ul class="dropdown-menu"></ul>');
2130
+ self.dropdownMenu.replaceWith(newEl);
2131
+ self.dropdownMenu = newEl;
2132
+ }
2133
+
1866
2134
  dropdownService.close( scope );
2135
+ self.selectedOption = null;
1867
2136
  }
1868
2137
 
1869
- setIsOpen($scope, isOpen);
1870
- if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
1871
- toggleInvoker($scope, { open: !!isOpen });
2138
+ if (angular.isFunction(setIsOpen)) {
2139
+ setIsOpen($scope, isOpen);
1872
2140
  }
1873
2141
  });
1874
2142
 
1875
2143
  $scope.$on('$locationChangeSuccess', function() {
1876
- scope.isOpen = false;
2144
+ if (scope.getAutoClose() !== 'disabled') {
2145
+ scope.isOpen = false;
2146
+ }
1877
2147
  });
1878
2148
 
1879
2149
  $scope.$on('$destroy', function() {
@@ -1886,6 +2156,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1886
2156
  controller: 'DropdownController',
1887
2157
  link: function(scope, element, attrs, dropdownCtrl) {
1888
2158
  dropdownCtrl.init( element );
2159
+ element.addClass('dropdown');
1889
2160
  }
1890
2161
  };
1891
2162
  })
@@ -1895,11 +2166,55 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1895
2166
  restrict: 'AC',
1896
2167
  require: '?^dropdown',
1897
2168
  link: function(scope, element, attrs, dropdownCtrl) {
1898
- if ( !dropdownCtrl ) {
2169
+ if (!dropdownCtrl) {
1899
2170
  return;
1900
2171
  }
1901
- dropdownCtrl.dropdownMenu = element;
2172
+ var tplUrl = attrs.templateUrl;
2173
+ if (tplUrl) {
2174
+ dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
2175
+ }
2176
+ if (!dropdownCtrl.dropdownMenu) {
2177
+ dropdownCtrl.dropdownMenu = element;
2178
+ }
2179
+ }
2180
+ };
2181
+ })
2182
+
2183
+ .directive('keyboardNav', function() {
2184
+ return {
2185
+ restrict: 'A',
2186
+ require: '?^dropdown',
2187
+ link: function (scope, element, attrs, dropdownCtrl) {
2188
+
2189
+ element.bind('keydown', function(e) {
2190
+
2191
+ if ([38, 40].indexOf(e.which) !== -1) {
2192
+
2193
+ e.preventDefault();
2194
+ e.stopPropagation();
2195
+
2196
+ var elems = dropdownCtrl.dropdownMenu.find('a');
2197
+
2198
+ switch (e.which) {
2199
+ case (40): { // Down
2200
+ if ( !angular.isNumber(dropdownCtrl.selectedOption)) {
2201
+ dropdownCtrl.selectedOption = 0;
2202
+ } else {
2203
+ dropdownCtrl.selectedOption = (dropdownCtrl.selectedOption === elems.length -1 ? dropdownCtrl.selectedOption : dropdownCtrl.selectedOption+1);
2204
+ }
2205
+
2206
+ }
2207
+ break;
2208
+ case (38): { // Up
2209
+ dropdownCtrl.selectedOption = (dropdownCtrl.selectedOption === 0 ? 0 : dropdownCtrl.selectedOption-1);
2210
+ }
2211
+ break;
2212
+ }
2213
+ elems[dropdownCtrl.selectedOption].focus();
2214
+ }
2215
+ });
1902
2216
  }
2217
+
1903
2218
  };
1904
2219
  })
1905
2220
 
@@ -1911,6 +2226,8 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
1911
2226
  return;
1912
2227
  }
1913
2228
 
2229
+ element.addClass('dropdown-toggle');
2230
+
1914
2231
  dropdownCtrl.toggleElement = element;
1915
2232
 
1916
2233
  var toggleDropdown = function(event) {
@@ -1997,7 +2314,15 @@ angular.module('ui.bootstrap.modal', [])
1997
2314
  /**
1998
2315
  * A helper directive for the $modal service. It creates a backdrop element.
1999
2316
  */
2000
- .directive('modalBackdrop', ['$timeout', function ($timeout) {
2317
+ .directive('modalBackdrop', [
2318
+ '$animate', '$injector', '$modalStack',
2319
+ function ($animate , $injector, $modalStack) {
2320
+ var $animateCss = null;
2321
+
2322
+ if ($injector.has('$animateCss')) {
2323
+ $animateCss = $injector.get('$animateCss');
2324
+ }
2325
+
2001
2326
  return {
2002
2327
  restrict: 'EA',
2003
2328
  replace: true,
@@ -2009,21 +2334,42 @@ angular.module('ui.bootstrap.modal', [])
2009
2334
  };
2010
2335
 
2011
2336
  function linkFn(scope, element, attrs) {
2012
- scope.animate = false;
2337
+ if (attrs.modalInClass) {
2338
+ if ($animateCss) {
2339
+ $animateCss(element, {
2340
+ addClass: attrs.modalInClass
2341
+ }).start();
2342
+ } else {
2343
+ $animate.addClass(element, attrs.modalInClass);
2344
+ }
2013
2345
 
2014
- //trigger CSS transitions
2015
- $timeout(function () {
2016
- scope.animate = true;
2017
- });
2346
+ scope.$on($modalStack.NOW_CLOSING_EVENT, function (e, setIsAsync) {
2347
+ var done = setIsAsync();
2348
+ if ($animateCss) {
2349
+ $animateCss(element, {
2350
+ removeClass: attrs.modalInClass
2351
+ }).start().then(done);
2352
+ } else {
2353
+ $animate.removeClass(element, attrs.modalInClass).then(done);
2354
+ }
2355
+ });
2356
+ }
2018
2357
  }
2019
2358
  }])
2020
2359
 
2021
- .directive('modalWindow', ['$modalStack', '$q', function ($modalStack, $q) {
2360
+ .directive('modalWindow', [
2361
+ '$modalStack', '$q', '$animate', '$injector',
2362
+ function ($modalStack , $q , $animate, $injector) {
2363
+ var $animateCss = null;
2364
+
2365
+ if ($injector.has('$animateCss')) {
2366
+ $animateCss = $injector.get('$animateCss');
2367
+ }
2368
+
2022
2369
  return {
2023
2370
  restrict: 'EA',
2024
2371
  scope: {
2025
- index: '@',
2026
- animate: '='
2372
+ index: '@'
2027
2373
  },
2028
2374
  replace: true,
2029
2375
  transclude: true,
@@ -2059,8 +2405,26 @@ angular.module('ui.bootstrap.modal', [])
2059
2405
  });
2060
2406
 
2061
2407
  modalRenderDeferObj.promise.then(function () {
2062
- // trigger CSS transitions
2063
- scope.animate = true;
2408
+ if (attrs.modalInClass) {
2409
+ if ($animateCss) {
2410
+ $animateCss(element, {
2411
+ addClass: attrs.modalInClass
2412
+ }).start();
2413
+ } else {
2414
+ $animate.addClass(element, attrs.modalInClass);
2415
+ }
2416
+
2417
+ scope.$on($modalStack.NOW_CLOSING_EVENT, function (e, setIsAsync) {
2418
+ var done = setIsAsync();
2419
+ if ($animateCss) {
2420
+ $animateCss(element, {
2421
+ removeClass: attrs.modalInClass
2422
+ }).start().then(done);
2423
+ } else {
2424
+ $animate.removeClass(element, attrs.modalInClass).then(done);
2425
+ }
2426
+ });
2427
+ }
2064
2428
 
2065
2429
  var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]');
2066
2430
  /**
@@ -2109,14 +2473,35 @@ angular.module('ui.bootstrap.modal', [])
2109
2473
  };
2110
2474
  })
2111
2475
 
2112
- .factory('$modalStack', ['$animate', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
2113
- function ($animate, $timeout, $document, $compile, $rootScope, $$stackedMap) {
2476
+ .factory('$modalStack', [
2477
+ '$animate', '$timeout', '$document', '$compile', '$rootScope',
2478
+ '$q',
2479
+ '$injector',
2480
+ '$$stackedMap',
2481
+ function ($animate , $timeout , $document , $compile , $rootScope ,
2482
+ $q,
2483
+ $injector,
2484
+ $$stackedMap) {
2485
+ var $animateCss = null;
2486
+
2487
+ if ($injector.has('$animateCss')) {
2488
+ $animateCss = $injector.get('$animateCss');
2489
+ }
2114
2490
 
2115
2491
  var OPENED_MODAL_CLASS = 'modal-open';
2116
2492
 
2117
2493
  var backdropDomEl, backdropScope;
2118
2494
  var openedWindows = $$stackedMap.createNew();
2119
- var $modalStack = {};
2495
+ var $modalStack = {
2496
+ NOW_CLOSING_EVENT: 'modal.stack.now-closing'
2497
+ };
2498
+
2499
+ //Modal focus behavior
2500
+ var focusableElementList;
2501
+ var focusIndex = 0;
2502
+ var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' +
2503
+ 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
2504
+ 'iframe, object, embed, *[tabindex], *[contenteditable=true]';
2120
2505
 
2121
2506
  function backdropIndex() {
2122
2507
  var topBackdropIndex = -1;
@@ -2129,13 +2514,13 @@ angular.module('ui.bootstrap.modal', [])
2129
2514
  return topBackdropIndex;
2130
2515
  }
2131
2516
 
2132
- $rootScope.$watch(backdropIndex, function(newBackdropIndex){
2517
+ $rootScope.$watch(backdropIndex, function(newBackdropIndex) {
2133
2518
  if (backdropScope) {
2134
2519
  backdropScope.index = newBackdropIndex;
2135
2520
  }
2136
2521
  });
2137
2522
 
2138
- function removeModalWindow(modalInstance) {
2523
+ function removeModalWindow(modalInstance, elementToReceiveFocus) {
2139
2524
 
2140
2525
  var body = $document.find('body').eq(0);
2141
2526
  var modalWindow = openedWindows.get(modalInstance).value;
@@ -2143,11 +2528,17 @@ angular.module('ui.bootstrap.modal', [])
2143
2528
  //clean up the stack
2144
2529
  openedWindows.remove(modalInstance);
2145
2530
 
2146
- //remove window DOM element
2147
2531
  removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
2148
- body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
2149
- checkRemoveBackdrop();
2532
+ body.toggleClass(modalInstance.openedClass || OPENED_MODAL_CLASS, openedWindows.length() > 0);
2150
2533
  });
2534
+ checkRemoveBackdrop();
2535
+
2536
+ //move focus to specified element if available, or else to body
2537
+ if (elementToReceiveFocus && elementToReceiveFocus.focus) {
2538
+ elementToReceiveFocus.focus();
2539
+ } else {
2540
+ body.focus();
2541
+ }
2151
2542
  }
2152
2543
 
2153
2544
  function checkRemoveBackdrop() {
@@ -2163,18 +2554,24 @@ angular.module('ui.bootstrap.modal', [])
2163
2554
  }
2164
2555
 
2165
2556
  function removeAfterAnimate(domEl, scope, done) {
2166
- // Closing animation
2167
- scope.animate = false;
2557
+ var asyncDeferred;
2558
+ var asyncPromise = null;
2559
+ var setIsAsync = function () {
2560
+ if (!asyncDeferred) {
2561
+ asyncDeferred = $q.defer();
2562
+ asyncPromise = asyncDeferred.promise;
2563
+ }
2168
2564
 
2169
- if (domEl.attr('modal-animation') && $animate.enabled()) {
2170
- // transition out
2171
- domEl.one('$animate:close', function closeFn() {
2172
- $rootScope.$evalAsync(afterAnimating);
2173
- });
2174
- } else {
2175
- // Ensure this call is async
2176
- $timeout(afterAnimating);
2177
- }
2565
+ return function asyncDone() {
2566
+ asyncDeferred.resolve();
2567
+ };
2568
+ };
2569
+ scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync);
2570
+
2571
+ // Note that it's intentional that asyncPromise might be null.
2572
+ // That's when setIsAsync has not been called during the
2573
+ // NOW_CLOSING_EVENT broadcast.
2574
+ return $q.when(asyncPromise).then(afterAnimating);
2178
2575
 
2179
2576
  function afterAnimating() {
2180
2577
  if (afterAnimating.done) {
@@ -2182,7 +2579,15 @@ angular.module('ui.bootstrap.modal', [])
2182
2579
  }
2183
2580
  afterAnimating.done = true;
2184
2581
 
2185
- domEl.remove();
2582
+ if ($animateCss) {
2583
+ $animateCss(domEl, {
2584
+ event: 'leave'
2585
+ }).start().then(function() {
2586
+ domEl.remove();
2587
+ });
2588
+ } else {
2589
+ $animate.leave(domEl);
2590
+ }
2186
2591
  scope.$destroy();
2187
2592
  if (done) {
2188
2593
  done();
@@ -2191,15 +2596,39 @@ angular.module('ui.bootstrap.modal', [])
2191
2596
  }
2192
2597
 
2193
2598
  $document.bind('keydown', function (evt) {
2194
- var modal;
2599
+ if (evt.isDefaultPrevented()) {
2600
+ return evt;
2601
+ }
2195
2602
 
2196
- if (evt.which === 27) {
2197
- modal = openedWindows.top();
2198
- if (modal && modal.value.keyboard) {
2199
- evt.preventDefault();
2200
- $rootScope.$apply(function () {
2201
- $modalStack.dismiss(modal.key, 'escape key press');
2202
- });
2603
+ var modal = openedWindows.top();
2604
+ if (modal && modal.value.keyboard) {
2605
+ switch (evt.which){
2606
+ case 27: {
2607
+ evt.preventDefault();
2608
+ $rootScope.$apply(function () {
2609
+ $modalStack.dismiss(modal.key, 'escape key press');
2610
+ });
2611
+ break;
2612
+ }
2613
+ case 9: {
2614
+ $modalStack.loadFocusElementList(modal);
2615
+ var focusChanged = false;
2616
+ if (evt.shiftKey) {
2617
+ if ($modalStack.isFocusInFirstItem(evt)) {
2618
+ focusChanged = $modalStack.focusLastFocusableElement();
2619
+ }
2620
+ } else {
2621
+ if ($modalStack.isFocusInLastItem(evt)) {
2622
+ focusChanged = $modalStack.focusFirstFocusableElement();
2623
+ }
2624
+ }
2625
+
2626
+ if (focusChanged) {
2627
+ evt.preventDefault();
2628
+ evt.stopPropagation();
2629
+ }
2630
+ break;
2631
+ }
2203
2632
  }
2204
2633
  }
2205
2634
  });
@@ -2213,7 +2642,8 @@ angular.module('ui.bootstrap.modal', [])
2213
2642
  renderDeferred: modal.renderDeferred,
2214
2643
  modalScope: modal.scope,
2215
2644
  backdrop: modal.backdrop,
2216
- keyboard: modal.keyboard
2645
+ keyboard: modal.keyboard,
2646
+ openedClass: modal.openedClass
2217
2647
  });
2218
2648
 
2219
2649
  var body = $document.find('body').eq(0),
@@ -2247,7 +2677,8 @@ angular.module('ui.bootstrap.modal', [])
2247
2677
  openedWindows.top().value.modalDomEl = modalDomEl;
2248
2678
  openedWindows.top().value.modalOpener = modalOpener;
2249
2679
  body.append(modalDomEl);
2250
- body.addClass(OPENED_MODAL_CLASS);
2680
+ body.addClass(modal.openedClass || OPENED_MODAL_CLASS);
2681
+ $modalStack.clearFocusListCache();
2251
2682
  };
2252
2683
 
2253
2684
  function broadcastClosing(modalWindow, resultOrReason, closing) {
@@ -2257,9 +2688,9 @@ angular.module('ui.bootstrap.modal', [])
2257
2688
  $modalStack.close = function (modalInstance, result) {
2258
2689
  var modalWindow = openedWindows.get(modalInstance);
2259
2690
  if (modalWindow && broadcastClosing(modalWindow, result, true)) {
2691
+ modalWindow.value.modalScope.$$uibDestructionScheduled = true;
2260
2692
  modalWindow.value.deferred.resolve(result);
2261
- removeModalWindow(modalInstance);
2262
- modalWindow.value.modalOpener.focus();
2693
+ removeModalWindow(modalInstance, modalWindow.value.modalOpener);
2263
2694
  return true;
2264
2695
  }
2265
2696
  return !modalWindow;
@@ -2268,9 +2699,9 @@ angular.module('ui.bootstrap.modal', [])
2268
2699
  $modalStack.dismiss = function (modalInstance, reason) {
2269
2700
  var modalWindow = openedWindows.get(modalInstance);
2270
2701
  if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
2702
+ modalWindow.value.modalScope.$$uibDestructionScheduled = true;
2271
2703
  modalWindow.value.deferred.reject(reason);
2272
- removeModalWindow(modalInstance);
2273
- modalWindow.value.modalOpener.focus();
2704
+ removeModalWindow(modalInstance, modalWindow.value.modalOpener);
2274
2705
  return true;
2275
2706
  }
2276
2707
  return !modalWindow;
@@ -2294,6 +2725,51 @@ angular.module('ui.bootstrap.modal', [])
2294
2725
  }
2295
2726
  };
2296
2727
 
2728
+ $modalStack.focusFirstFocusableElement = function() {
2729
+ if (focusableElementList.length > 0) {
2730
+ focusableElementList[0].focus();
2731
+ return true;
2732
+ }
2733
+ return false;
2734
+ };
2735
+ $modalStack.focusLastFocusableElement = function() {
2736
+ if (focusableElementList.length > 0) {
2737
+ focusableElementList[focusableElementList.length - 1].focus();
2738
+ return true;
2739
+ }
2740
+ return false;
2741
+ };
2742
+
2743
+ $modalStack.isFocusInFirstItem = function(evt) {
2744
+ if (focusableElementList.length > 0) {
2745
+ return (evt.target || evt.srcElement) == focusableElementList[0];
2746
+ }
2747
+ return false;
2748
+ };
2749
+
2750
+ $modalStack.isFocusInLastItem = function(evt) {
2751
+ if (focusableElementList.length > 0) {
2752
+ return (evt.target || evt.srcElement) == focusableElementList[focusableElementList.length - 1];
2753
+ }
2754
+ return false;
2755
+ };
2756
+
2757
+ $modalStack.clearFocusListCache = function() {
2758
+ focusableElementList = [];
2759
+ focusIndex = 0;
2760
+ };
2761
+
2762
+ $modalStack.loadFocusElementList = function(modalWindow) {
2763
+ if (focusableElementList === undefined || !focusableElementList.length0) {
2764
+ if (modalWindow) {
2765
+ var modalDomE1 = modalWindow.value.modalDomEl;
2766
+ if (modalDomE1 && modalDomE1.length) {
2767
+ focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector);
2768
+ }
2769
+ }
2770
+ }
2771
+ };
2772
+
2297
2773
  return $modalStack;
2298
2774
  }])
2299
2775
 
@@ -2320,6 +2796,8 @@ angular.module('ui.bootstrap.modal', [])
2320
2796
  angular.forEach(resolves, function (value) {
2321
2797
  if (angular.isFunction(value) || angular.isArray(value)) {
2322
2798
  promisesArr.push($q.when($injector.invoke(value)));
2799
+ } else if (angular.isString(value)) {
2800
+ promisesArr.push($q.when($injector.get(value)));
2323
2801
  }
2324
2802
  });
2325
2803
  return promisesArr;
@@ -2363,6 +2841,12 @@ angular.module('ui.bootstrap.modal', [])
2363
2841
  modalScope.$close = modalInstance.close;
2364
2842
  modalScope.$dismiss = modalInstance.dismiss;
2365
2843
 
2844
+ modalScope.$on('$destroy', function() {
2845
+ if (!modalScope.$$uibDestructionScheduled) {
2846
+ modalScope.$dismiss('$uibUnscheduledDestruction');
2847
+ }
2848
+ });
2849
+
2366
2850
  var ctrlInstance, ctrlLocals = {};
2367
2851
  var resolveIter = 1;
2368
2852
 
@@ -2376,6 +2860,10 @@ angular.module('ui.bootstrap.modal', [])
2376
2860
 
2377
2861
  ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
2378
2862
  if (modalOptions.controllerAs) {
2863
+ if (modalOptions.bindToController) {
2864
+ angular.extend(ctrlInstance, modalScope);
2865
+ }
2866
+
2379
2867
  modalScope[modalOptions.controllerAs] = ctrlInstance;
2380
2868
  }
2381
2869
  }
@@ -2391,7 +2879,8 @@ angular.module('ui.bootstrap.modal', [])
2391
2879
  backdropClass: modalOptions.backdropClass,
2392
2880
  windowClass: modalOptions.windowClass,
2393
2881
  windowTemplateUrl: modalOptions.windowTemplateUrl,
2394
- size: modalOptions.size
2882
+ size: modalOptions.size,
2883
+ openedClass: modalOptions.openedClass
2395
2884
  });
2396
2885
 
2397
2886
  }, function resolveError(reason) {
@@ -2415,7 +2904,6 @@ angular.module('ui.bootstrap.modal', [])
2415
2904
  });
2416
2905
 
2417
2906
  angular.module('ui.bootstrap.pagination', [])
2418
-
2419
2907
  .controller('PaginationController', ['$scope', '$attrs', '$parse', function ($scope, $attrs, $parse) {
2420
2908
  var self = this,
2421
2909
  ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
@@ -2463,7 +2951,12 @@ angular.module('ui.bootstrap.pagination', [])
2463
2951
  };
2464
2952
 
2465
2953
  $scope.selectPage = function(page, evt) {
2466
- if ( $scope.page !== page && page > 0 && page <= $scope.totalPages) {
2954
+ if (evt) {
2955
+ evt.preventDefault();
2956
+ }
2957
+
2958
+ var clickAllowed = !$scope.ngDisabled || !evt;
2959
+ if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
2467
2960
  if (evt && evt.target) {
2468
2961
  evt.target.blur();
2469
2962
  }
@@ -2502,11 +2995,15 @@ angular.module('ui.bootstrap.pagination', [])
2502
2995
  firstText: '@',
2503
2996
  previousText: '@',
2504
2997
  nextText: '@',
2505
- lastText: '@'
2998
+ lastText: '@',
2999
+ ngDisabled:'='
2506
3000
  },
2507
3001
  require: ['pagination', '?ngModel'],
2508
3002
  controller: 'PaginationController',
2509
- templateUrl: 'template/pagination/pagination.html',
3003
+ controllerAs: 'pagination',
3004
+ templateUrl: function(element, attrs) {
3005
+ return attrs.templateUrl || 'template/pagination/pagination.html';
3006
+ },
2510
3007
  replace: true,
2511
3008
  link: function(scope, element, attrs, ctrls) {
2512
3009
  var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
@@ -2699,7 +3196,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2699
3196
  * Returns the actual instance of the $tooltip service.
2700
3197
  * TODO support multiple triggers
2701
3198
  */
2702
- this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $document, $position, $interpolate ) {
3199
+ this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', '$rootScope', function ( $window, $compile, $timeout, $document, $position, $interpolate, $rootScope ) {
2703
3200
  return function $tooltip ( type, prefix, defaultTriggerShow, options ) {
2704
3201
  options = angular.extend( {}, defaultOptions, globalOptions, options );
2705
3202
 
@@ -2718,8 +3215,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2718
3215
  * trigger; else it will just use the show trigger.
2719
3216
  */
2720
3217
  function getTriggers ( trigger ) {
2721
- var show = trigger || options.trigger || defaultTriggerShow;
2722
- var hide = triggerMap[show] || show;
3218
+ var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
3219
+ var hide = show.map(function(trigger) {
3220
+ return triggerMap[trigger] || trigger;
3221
+ });
2723
3222
  return {
2724
3223
  show: show,
2725
3224
  hide: hide
@@ -2758,6 +3257,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2758
3257
  var triggers = getTriggers( undefined );
2759
3258
  var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
2760
3259
  var ttScope = scope.$new(true);
3260
+ var repositionScheduled = false;
2761
3261
 
2762
3262
  var positionTooltip = function () {
2763
3263
  if (!tooltip) { return; }
@@ -2770,6 +3270,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2770
3270
  tooltip.css( ttPosition );
2771
3271
  };
2772
3272
 
3273
+ var positionTooltipAsync = function () {
3274
+ $timeout(positionTooltip, 0, false);
3275
+ };
3276
+
2773
3277
  // Set up the correct scope to allow transclusion later
2774
3278
  ttScope.origScope = scope;
2775
3279
 
@@ -2806,9 +3310,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2806
3310
  }
2807
3311
 
2808
3312
  function hideTooltipBind () {
2809
- scope.$apply(function () {
2810
- hide();
2811
- });
3313
+ hide();
3314
+ if (!$rootScope.$$phase) {
3315
+ $rootScope.$digest();
3316
+ }
2812
3317
  }
2813
3318
 
2814
3319
  // Show the tooltip popup element.
@@ -2832,7 +3337,6 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2832
3337
 
2833
3338
  // Set the initial positioning.
2834
3339
  tooltip.css({ top: 0, left: 0, display: 'block' });
2835
- ttScope.$digest();
2836
3340
 
2837
3341
  positionTooltip();
2838
3342
 
@@ -2880,16 +3384,23 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2880
3384
  }
2881
3385
  });
2882
3386
 
2883
- tooltipLinkedScope.$watch(function () {
2884
- $timeout(positionTooltip, 0, false);
2885
- });
2886
-
2887
3387
  if (options.useContentExp) {
2888
3388
  tooltipLinkedScope.$watch('contentExp()', function (val) {
2889
- if (!val && ttScope.isOpen ) {
3389
+ if (!val && ttScope.isOpen) {
2890
3390
  hide();
2891
3391
  }
2892
3392
  });
3393
+
3394
+ tooltipLinkedScope.$watch(function() {
3395
+ if (!repositionScheduled) {
3396
+ repositionScheduled = true;
3397
+ tooltipLinkedScope.$$postDigest(function() {
3398
+ repositionScheduled = false;
3399
+ positionTooltipAsync();
3400
+ });
3401
+ }
3402
+ });
3403
+
2893
3404
  }
2894
3405
  }
2895
3406
 
@@ -2922,13 +3433,19 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2922
3433
  attrs.$observe( type, function ( val ) {
2923
3434
  ttScope.content = val;
2924
3435
 
2925
- if (!val && ttScope.isOpen ) {
3436
+ if (!val && ttScope.isOpen) {
2926
3437
  hide();
3438
+ } else {
3439
+ positionTooltipAsync();
2927
3440
  }
2928
3441
  });
2929
3442
  }
2930
3443
 
2931
3444
  attrs.$observe( 'disabled', function ( val ) {
3445
+ if (popupTimeout && val) {
3446
+ $timeout.cancel(popupTimeout);
3447
+ }
3448
+
2932
3449
  if (val && ttScope.isOpen ) {
2933
3450
  hide();
2934
3451
  }
@@ -2936,6 +3453,16 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2936
3453
 
2937
3454
  attrs.$observe( prefix+'Title', function ( val ) {
2938
3455
  ttScope.title = val;
3456
+ positionTooltipAsync();
3457
+ });
3458
+
3459
+ attrs.$observe( prefix + 'Placement', function () {
3460
+ if (ttScope.isOpen) {
3461
+ $timeout(function () {
3462
+ prepPlacement();
3463
+ show()();
3464
+ }, 0, false);
3465
+ }
2939
3466
  });
2940
3467
 
2941
3468
  function prepPopupClass() {
@@ -2954,8 +3481,12 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2954
3481
  }
2955
3482
 
2956
3483
  var unregisterTriggers = function () {
2957
- element.unbind(triggers.show, showTooltipBind);
2958
- element.unbind(triggers.hide, hideTooltipBind);
3484
+ triggers.show.forEach(function(trigger) {
3485
+ element.unbind(trigger, showTooltipBind);
3486
+ });
3487
+ triggers.hide.forEach(function(trigger) {
3488
+ element.unbind(trigger, hideTooltipBind);
3489
+ });
2959
3490
  };
2960
3491
 
2961
3492
  function prepTriggers() {
@@ -2964,12 +3495,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
2964
3495
 
2965
3496
  triggers = getTriggers( val );
2966
3497
 
2967
- if ( triggers.show === triggers.hide ) {
2968
- element.bind( triggers.show, toggleTooltipBind );
2969
- } else {
2970
- element.bind( triggers.show, showTooltipBind );
2971
- element.bind( triggers.hide, hideTooltipBind );
2972
- }
3498
+ triggers.show.forEach(function(trigger, idx) {
3499
+ if (trigger === triggers.hide[idx]) {
3500
+ element.bind(trigger, toggleTooltipBind);
3501
+ } else if (trigger) {
3502
+ element.bind(trigger, showTooltipBind);
3503
+ element.bind(triggers.hide[idx], hideTooltipBind);
3504
+ }
3505
+ });
2973
3506
  }
2974
3507
  prepTriggers();
2975
3508
 
@@ -3164,7 +3697,7 @@ function ( $tooltip , tooltipHtmlUnsafeSuppressDeprecated , $log) {
3164
3697
  /**
3165
3698
  * The following features are still outstanding: popup delay, animation as a
3166
3699
  * function, placement as a function, inside, support for more triggers than
3167
- * just mouse enter/leave, html popovers, and selector delegatation.
3700
+ * just mouse enter/leave, and selector delegatation.
3168
3701
  */
3169
3702
  angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
3170
3703
 
@@ -3184,6 +3717,21 @@ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
3184
3717
  } );
3185
3718
  }])
3186
3719
 
3720
+ .directive( 'popoverHtmlPopup', function () {
3721
+ return {
3722
+ restrict: 'EA',
3723
+ replace: true,
3724
+ scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
3725
+ templateUrl: 'template/popover/popover-html.html'
3726
+ };
3727
+ })
3728
+
3729
+ .directive( 'popoverHtml', [ '$tooltip', function ( $tooltip ) {
3730
+ return $tooltip( 'popoverHtml', 'popover', 'click', {
3731
+ useContentExp: true
3732
+ });
3733
+ }])
3734
+
3187
3735
  .directive( 'popoverPopup', function () {
3188
3736
  return {
3189
3737
  restrict: 'EA',
@@ -3218,10 +3766,25 @@ angular.module('ui.bootstrap.progressbar', [])
3218
3766
 
3219
3767
  this.bars.push(bar);
3220
3768
 
3769
+ bar.max = $scope.max;
3770
+
3221
3771
  bar.$watch('value', function( value ) {
3222
- bar.percent = +(100 * value / $scope.max).toFixed(2);
3772
+ bar.recalculatePercentage();
3223
3773
  });
3224
3774
 
3775
+ bar.recalculatePercentage = function() {
3776
+ bar.percent = +(100 * bar.value / bar.max).toFixed(2);
3777
+
3778
+ var totalPercentage = 0;
3779
+ self.bars.forEach(function (bar) {
3780
+ totalPercentage += bar.percent;
3781
+ });
3782
+
3783
+ if (totalPercentage > 100) {
3784
+ bar.percent -= totalPercentage - 100;
3785
+ }
3786
+ };
3787
+
3225
3788
  bar.$on('$destroy', function() {
3226
3789
  element = null;
3227
3790
  self.removeBar(bar);
@@ -3231,6 +3794,13 @@ angular.module('ui.bootstrap.progressbar', [])
3231
3794
  this.removeBar = function(bar) {
3232
3795
  this.bars.splice(this.bars.indexOf(bar), 1);
3233
3796
  };
3797
+
3798
+ $scope.$watch('max', function(max) {
3799
+ self.bars.forEach(function (bar) {
3800
+ bar.max = $scope.max;
3801
+ bar.recalculatePercentage();
3802
+ });
3803
+ });
3234
3804
  }])
3235
3805
 
3236
3806
  .directive('progress', function() {
@@ -3240,7 +3810,9 @@ angular.module('ui.bootstrap.progressbar', [])
3240
3810
  transclude: true,
3241
3811
  controller: 'ProgressController',
3242
3812
  require: 'progress',
3243
- scope: {},
3813
+ scope: {
3814
+ max: '=?'
3815
+ },
3244
3816
  templateUrl: 'template/progressbar/progress.html'
3245
3817
  };
3246
3818
  })
@@ -3253,7 +3825,6 @@ angular.module('ui.bootstrap.progressbar', [])
3253
3825
  require: '^progress',
3254
3826
  scope: {
3255
3827
  value: '=',
3256
- max: '=?',
3257
3828
  type: '@'
3258
3829
  },
3259
3830
  templateUrl: 'template/progressbar/bar.html',
@@ -3286,7 +3857,8 @@ angular.module('ui.bootstrap.rating', [])
3286
3857
  .constant('ratingConfig', {
3287
3858
  max: 5,
3288
3859
  stateOn: null,
3289
- stateOff: null
3860
+ stateOff: null,
3861
+ titles : ['one', 'two', 'three', 'four', 'five']
3290
3862
  })
3291
3863
 
3292
3864
  .controller('RatingController', ['$scope', '$attrs', 'ratingConfig', function($scope, $attrs, ratingConfig) {
@@ -3305,6 +3877,9 @@ angular.module('ui.bootstrap.rating', [])
3305
3877
 
3306
3878
  this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
3307
3879
  this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
3880
+ var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ;
3881
+ this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
3882
+ tmpTitles : ratingConfig.titles;
3308
3883
 
3309
3884
  var ratingStates = angular.isDefined($attrs.ratingStates) ? $scope.$parent.$eval($attrs.ratingStates) :
3310
3885
  new Array( angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max );
@@ -3313,14 +3888,22 @@ angular.module('ui.bootstrap.rating', [])
3313
3888
 
3314
3889
  this.buildTemplateObjects = function(states) {
3315
3890
  for (var i = 0, n = states.length; i < n; i++) {
3316
- states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff }, states[i]);
3891
+ states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]);
3317
3892
  }
3318
3893
  return states;
3319
3894
  };
3320
3895
 
3896
+ this.getTitle = function(index) {
3897
+ if (index >= this.titles.length) {
3898
+ return index + 1;
3899
+ } else {
3900
+ return this.titles[index];
3901
+ }
3902
+ };
3903
+
3321
3904
  $scope.rate = function(value) {
3322
3905
  if ( !$scope.readonly && value >= 0 && value <= $scope.range.length ) {
3323
- ngModelCtrl.$setViewValue(value);
3906
+ ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value);
3324
3907
  ngModelCtrl.$render();
3325
3908
  }
3326
3909
  };
@@ -3369,6 +3952,7 @@ angular.module('ui.bootstrap.rating', [])
3369
3952
  };
3370
3953
  });
3371
3954
 
3955
+
3372
3956
  /**
3373
3957
  * @ngdoc overview
3374
3958
  * @name ui.bootstrap.tabs
@@ -3569,47 +4153,45 @@ angular.module('ui.bootstrap.tabs', [])
3569
4153
  controller: function() {
3570
4154
  //Empty controller so other directives can require being 'under' a tab
3571
4155
  },
3572
- compile: function(elm, attrs, transclude) {
3573
- return function postLink(scope, elm, attrs, tabsetCtrl) {
3574
- scope.$watch('active', function(active) {
3575
- if (active) {
3576
- tabsetCtrl.select(scope);
3577
- }
3578
- });
3579
-
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
3591
- if ( attrs.disabled ) {
3592
- $log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');
3593
- scope.$parent.$watch($parse(attrs.disabled), function(value) {
3594
- scope.disabled = !! value;
3595
- });
4156
+ link: function(scope, elm, attrs, tabsetCtrl, transclude) {
4157
+ scope.$watch('active', function(active) {
4158
+ if (active) {
4159
+ tabsetCtrl.select(scope);
3596
4160
  }
4161
+ });
3597
4162
 
3598
- scope.select = function() {
3599
- if ( !scope.disabled ) {
3600
- scope.active = true;
3601
- }
3602
- };
4163
+ scope.disabled = false;
4164
+ if ( attrs.disable ) {
4165
+ scope.$parent.$watch($parse(attrs.disable), function(value) {
4166
+ scope.disabled = !! value;
4167
+ });
4168
+ }
3603
4169
 
3604
- tabsetCtrl.addTab(scope);
3605
- scope.$on('$destroy', function() {
3606
- tabsetCtrl.removeTab(scope);
4170
+ // Deprecation support of "disabled" parameter
4171
+ // fix(tab): IE9 disabled attr renders grey text on enabled tab #2677
4172
+ // This code is duplicated from the lines above to make it easy to remove once
4173
+ // the feature has been completely deprecated
4174
+ if ( attrs.disabled ) {
4175
+ $log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');
4176
+ scope.$parent.$watch($parse(attrs.disabled), function(value) {
4177
+ scope.disabled = !! value;
3607
4178
  });
4179
+ }
3608
4180
 
3609
- //We need to transclude later, once the content container is ready.
3610
- //when this link happens, we're inside a tab heading.
3611
- scope.$transcludeFn = transclude;
4181
+ scope.select = function() {
4182
+ if ( !scope.disabled ) {
4183
+ scope.active = true;
4184
+ }
3612
4185
  };
4186
+
4187
+ tabsetCtrl.addTab(scope);
4188
+ scope.$on('$destroy', function() {
4189
+ tabsetCtrl.removeTab(scope);
4190
+ });
4191
+
4192
+ //We need to transclude later, once the content container is ready.
4193
+ //when this link happens, we're inside a tab heading.
4194
+ scope.$transcludeFn = transclude;
3613
4195
  }
3614
4196
  };
3615
4197
  }])
@@ -3671,7 +4253,8 @@ angular.module('ui.bootstrap.timepicker', [])
3671
4253
  meridians: null,
3672
4254
  readonlyInput: false,
3673
4255
  mousewheel: true,
3674
- arrowkeys: true
4256
+ arrowkeys: true,
4257
+ showSpinners: true
3675
4258
  })
3676
4259
 
3677
4260
  .controller('TimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'timepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {
@@ -3718,6 +4301,50 @@ angular.module('ui.bootstrap.timepicker', [])
3718
4301
  });
3719
4302
  }
3720
4303
 
4304
+ var min;
4305
+ $scope.$parent.$watch($parse($attrs.min), function(value) {
4306
+ var dt = new Date(value);
4307
+ min = isNaN(dt) ? undefined : dt;
4308
+ });
4309
+
4310
+ var max;
4311
+ $scope.$parent.$watch($parse($attrs.max), function(value) {
4312
+ var dt = new Date(value);
4313
+ max = isNaN(dt) ? undefined : dt;
4314
+ });
4315
+
4316
+ $scope.noIncrementHours = function() {
4317
+ var incrementedSelected = addMinutes(selected, hourStep * 60);
4318
+ return incrementedSelected > max ||
4319
+ (incrementedSelected < selected && incrementedSelected < min);
4320
+ };
4321
+
4322
+ $scope.noDecrementHours = function() {
4323
+ var decrementedSelected = addMinutes(selected, - hourStep * 60);
4324
+ return decrementedSelected < min ||
4325
+ (decrementedSelected > selected && decrementedSelected > max);
4326
+ };
4327
+
4328
+ $scope.noIncrementMinutes = function() {
4329
+ var incrementedSelected = addMinutes(selected, minuteStep);
4330
+ return incrementedSelected > max ||
4331
+ (incrementedSelected < selected && incrementedSelected < min);
4332
+ };
4333
+
4334
+ $scope.noDecrementMinutes = function() {
4335
+ var decrementedSelected = addMinutes(selected, - minuteStep);
4336
+ return decrementedSelected < min ||
4337
+ (decrementedSelected > selected && decrementedSelected > max);
4338
+ };
4339
+
4340
+ $scope.noToggleMeridian = function() {
4341
+ if (selected.getHours() < 13) {
4342
+ return addMinutes(selected, 12 * 60) > max;
4343
+ } else {
4344
+ return addMinutes(selected, - 12 * 60) < min;
4345
+ }
4346
+ };
4347
+
3721
4348
  // 12H / 24H mode
3722
4349
  $scope.showMeridian = timepickerConfig.showMeridian;
3723
4350
  if ($attrs.showMeridian) {
@@ -3840,7 +4467,11 @@ angular.module('ui.bootstrap.timepicker', [])
3840
4467
 
3841
4468
  if ( angular.isDefined(hours) ) {
3842
4469
  selected.setHours( hours );
3843
- refresh( 'h' );
4470
+ if (selected < min || selected > max) {
4471
+ invalidate(true);
4472
+ } else {
4473
+ refresh( 'h' );
4474
+ }
3844
4475
  } else {
3845
4476
  invalidate(true);
3846
4477
  }
@@ -3859,7 +4490,11 @@ angular.module('ui.bootstrap.timepicker', [])
3859
4490
 
3860
4491
  if ( angular.isDefined(minutes) ) {
3861
4492
  selected.setMinutes( minutes );
3862
- refresh( 'm' );
4493
+ if (selected < min || selected > max) {
4494
+ invalidate(undefined, true);
4495
+ } else {
4496
+ refresh( 'm' );
4497
+ }
3863
4498
  } else {
3864
4499
  invalidate(undefined, true);
3865
4500
  }
@@ -3885,7 +4520,14 @@ angular.module('ui.bootstrap.timepicker', [])
3885
4520
  if ( date ) {
3886
4521
  selected = date;
3887
4522
  }
3888
- makeValid();
4523
+
4524
+ if (selected < min || selected > max) {
4525
+ ngModelCtrl.$setValidity('time', false);
4526
+ $scope.invalidHours = true;
4527
+ $scope.invalidMinutes = true;
4528
+ } else {
4529
+ makeValid();
4530
+ }
3889
4531
  updateTemplate();
3890
4532
  }
3891
4533
  };
@@ -3917,26 +4559,45 @@ angular.module('ui.bootstrap.timepicker', [])
3917
4559
  $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
3918
4560
  }
3919
4561
 
3920
- function addMinutes( minutes ) {
3921
- var dt = new Date( selected.getTime() + minutes * 60000 );
3922
- selected.setHours( dt.getHours(), dt.getMinutes() );
4562
+ function addMinutes(date, minutes) {
4563
+ var dt = new Date(date.getTime() + minutes * 60000);
4564
+ var newDate = new Date(date);
4565
+ newDate.setHours(dt.getHours(), dt.getMinutes());
4566
+ return newDate;
4567
+ }
4568
+
4569
+ function addMinutesToSelected( minutes ) {
4570
+ selected = addMinutes( selected, minutes );
3923
4571
  refresh();
3924
4572
  }
3925
4573
 
4574
+ $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
4575
+ $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
4576
+
3926
4577
  $scope.incrementHours = function() {
3927
- addMinutes( hourStep * 60 );
4578
+ if (!$scope.noIncrementHours()) {
4579
+ addMinutesToSelected(hourStep * 60);
4580
+ }
3928
4581
  };
3929
4582
  $scope.decrementHours = function() {
3930
- addMinutes( - hourStep * 60 );
4583
+ if (!$scope.noDecrementHours()) {
4584
+ addMinutesToSelected(-hourStep * 60);
4585
+ }
3931
4586
  };
3932
4587
  $scope.incrementMinutes = function() {
3933
- addMinutes( minuteStep );
4588
+ if (!$scope.noIncrementMinutes()) {
4589
+ addMinutesToSelected(minuteStep);
4590
+ }
3934
4591
  };
3935
4592
  $scope.decrementMinutes = function() {
3936
- addMinutes( - minuteStep );
4593
+ if (!$scope.noDecrementMinutes()) {
4594
+ addMinutesToSelected(-minuteStep);
4595
+ }
3937
4596
  };
3938
4597
  $scope.toggleMeridian = function() {
3939
- addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
4598
+ if (!$scope.noToggleMeridian()) {
4599
+ addMinutesToSelected(12 * 60 * (selected.getHours() < 12 ? 1 : -1));
4600
+ }
3940
4601
  };
3941
4602
  }])
3942
4603
 
@@ -4079,10 +4740,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4079
4740
  };
4080
4741
  }])
4081
4742
 
4082
- .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',
4083
- function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
4743
+ .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$position', 'typeaheadParser',
4744
+ function ($compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) {
4084
4745
 
4085
4746
  var HOT_KEYS = [9, 13, 27, 38, 40];
4747
+ var eventDebounceTime = 200;
4086
4748
 
4087
4749
  return {
4088
4750
  require:'ngModel',
@@ -4091,7 +4753,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4091
4753
  //SUPPORTED ATTRIBUTES (OPTIONS)
4092
4754
 
4093
4755
  //minimal no of characters that needs to be entered before typeahead kicks-in
4094
- var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
4756
+ var minLength = originalScope.$eval(attrs.typeaheadMinLength);
4757
+ if (!minLength && minLength !== 0) {
4758
+ minLength = 1;
4759
+ }
4095
4760
 
4096
4761
  //minimal wait time after last character typed before typeahead kicks-in
4097
4762
  var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
@@ -4105,12 +4770,21 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4105
4770
  //a callback executed when a match is selected
4106
4771
  var onSelectCallback = $parse(attrs.typeaheadOnSelect);
4107
4772
 
4773
+ //should it select highlighted popup value when losing focus?
4774
+ var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
4775
+
4776
+ //binding to a variable that indicates if there were no results after the query is completed
4777
+ var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
4778
+
4108
4779
  var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
4109
4780
 
4110
4781
  var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
4111
4782
 
4112
4783
  var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
4113
4784
 
4785
+ //If input matches an item of the list exactly, select it automatically
4786
+ var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
4787
+
4114
4788
  //INTERNAL VARIABLES
4115
4789
 
4116
4790
  //model setter executed upon match selection
@@ -4121,6 +4795,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4121
4795
 
4122
4796
  var hasFocus;
4123
4797
 
4798
+ //Used to avoid bug in iOS webview where iOS keyboard does not fire
4799
+ //mousedown & mouseup events
4800
+ //Issue #3699
4801
+ var selected;
4802
+
4124
4803
  //create a child scope for the typeahead directive so we are not polluting original scope
4125
4804
  //with typeahead-specific data (matches, query etc.)
4126
4805
  var scope = originalScope.$new();
@@ -4143,6 +4822,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4143
4822
  matches: 'matches',
4144
4823
  active: 'activeIdx',
4145
4824
  select: 'select(activeIdx)',
4825
+ 'move-in-progress': 'moveInProgress',
4146
4826
  query: 'query',
4147
4827
  position: 'position'
4148
4828
  });
@@ -4171,10 +4851,20 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4171
4851
  }
4172
4852
  });
4173
4853
 
4854
+ var inputIsExactMatch = function(inputValue, index) {
4855
+
4856
+ if (scope.matches.length > index && inputValue) {
4857
+ return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
4858
+ }
4859
+
4860
+ return false;
4861
+ };
4862
+
4174
4863
  var getMatchesAsync = function(inputValue) {
4175
4864
 
4176
4865
  var locals = {$viewValue: inputValue};
4177
4866
  isLoadingSetter(originalScope, true);
4867
+ isNoResultsSetter(originalScope, false);
4178
4868
  $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
4179
4869
 
4180
4870
  //it might happen that several async queries were in progress if a user were typing fast
@@ -4184,6 +4874,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4184
4874
  if (matches && matches.length > 0) {
4185
4875
 
4186
4876
  scope.activeIdx = focusFirst ? 0 : -1;
4877
+ isNoResultsSetter(originalScope, false);
4187
4878
  scope.matches.length = 0;
4188
4879
 
4189
4880
  //transform labels
@@ -4200,12 +4891,17 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4200
4891
  //position pop-up with matches - we need to re-calculate its position each time we are opening a window
4201
4892
  //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
4202
4893
  //due to other elements being rendered
4203
- scope.position = appendToBody ? $position.offset(element) : $position.position(element);
4204
- scope.position.top = scope.position.top + element.prop('offsetHeight');
4894
+ recalculatePosition();
4205
4895
 
4206
4896
  element.attr('aria-expanded', true);
4897
+
4898
+ //Select the single remaining option if user input matches
4899
+ if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
4900
+ scope.select(0);
4901
+ }
4207
4902
  } else {
4208
4903
  resetMatches();
4904
+ isNoResultsSetter(originalScope, true);
4209
4905
  }
4210
4906
  }
4211
4907
  if (onCurrentRequest) {
@@ -4214,9 +4910,52 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4214
4910
  }, function(){
4215
4911
  resetMatches();
4216
4912
  isLoadingSetter(originalScope, false);
4913
+ isNoResultsSetter(originalScope, true);
4217
4914
  });
4218
4915
  };
4219
4916
 
4917
+ // bind events only if appendToBody params exist - performance feature
4918
+ if (appendToBody) {
4919
+ angular.element($window).bind('resize', fireRecalculating);
4920
+ $document.find('body').bind('scroll', fireRecalculating);
4921
+ }
4922
+
4923
+ // Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
4924
+ var timeoutEventPromise;
4925
+
4926
+ // Default progress type
4927
+ scope.moveInProgress = false;
4928
+
4929
+ function fireRecalculating() {
4930
+ if(!scope.moveInProgress){
4931
+ scope.moveInProgress = true;
4932
+ scope.$digest();
4933
+ }
4934
+
4935
+ // Cancel previous timeout
4936
+ if (timeoutEventPromise) {
4937
+ $timeout.cancel(timeoutEventPromise);
4938
+ }
4939
+
4940
+ // Debounced executing recalculate after events fired
4941
+ timeoutEventPromise = $timeout(function () {
4942
+ // if popup is visible
4943
+ if (scope.matches.length) {
4944
+ recalculatePosition();
4945
+ }
4946
+
4947
+ scope.moveInProgress = false;
4948
+ scope.$digest();
4949
+ }, eventDebounceTime);
4950
+ }
4951
+
4952
+ // recalculate actual position and set new values to scope
4953
+ // after digest loop is popup in right position
4954
+ function recalculatePosition() {
4955
+ scope.position = appendToBody ? $position.offset(element) : $position.position(element);
4956
+ scope.position.top += element.prop('offsetHeight');
4957
+ }
4958
+
4220
4959
  resetMatches();
4221
4960
 
4222
4961
  //we need to propagate user's query so we can higlight matches
@@ -4243,7 +4982,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4243
4982
 
4244
4983
  hasFocus = true;
4245
4984
 
4246
- if (inputValue && inputValue.length >= minSearch) {
4985
+ if (minLength === 0 || inputValue && inputValue.length >= minLength) {
4247
4986
  if (waitTime > 0) {
4248
4987
  cancelPreviousTimeout();
4249
4988
  scheduleSearchWithTimeout(inputValue);
@@ -4262,7 +5001,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4262
5001
  if (!inputValue) {
4263
5002
  // Reset in case user had typed something previously.
4264
5003
  modelCtrl.$setValidity('editable', true);
4265
- return inputValue;
5004
+ return null;
4266
5005
  } else {
4267
5006
  modelCtrl.$setValidity('editable', false);
4268
5007
  return undefined;
@@ -4305,6 +5044,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4305
5044
  var locals = {};
4306
5045
  var model, item;
4307
5046
 
5047
+ selected = true;
4308
5048
  locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
4309
5049
  model = parserResult.modelMapper(originalScope, locals);
4310
5050
  $setModelValue(originalScope, model);
@@ -4332,8 +5072,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4332
5072
  return;
4333
5073
  }
4334
5074
 
4335
- // if there's nothing selected (i.e. focusFirst) and enter is hit, don't do anything
4336
- if (scope.activeIdx == -1 && (evt.which === 13 || evt.which === 9)) {
5075
+ // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
5076
+ if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
5077
+ resetMatches();
5078
+ scope.$digest();
4337
5079
  return;
4338
5080
  }
4339
5081
 
@@ -4360,15 +5102,26 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4360
5102
  }
4361
5103
  });
4362
5104
 
4363
- element.bind('blur', function (evt) {
5105
+ element.bind('blur', function () {
5106
+ if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
5107
+ selected = true;
5108
+ scope.$apply(function () {
5109
+ scope.select(scope.activeIdx);
5110
+ });
5111
+ }
4364
5112
  hasFocus = false;
5113
+ selected = false;
4365
5114
  });
4366
5115
 
4367
5116
  // Keep reference to click handler to unbind it.
4368
5117
  var dismissClickHandler = function (evt) {
4369
- if (element[0] !== evt.target) {
5118
+ // Issue #3973
5119
+ // Firefox treats right click as a click on document
5120
+ if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
4370
5121
  resetMatches();
4371
- scope.$digest();
5122
+ if (!$rootScope.$$phase) {
5123
+ scope.$digest();
5124
+ }
4372
5125
  }
4373
5126
  };
4374
5127
 
@@ -4402,7 +5155,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4402
5155
  matches:'=',
4403
5156
  query:'=',
4404
5157
  active:'=',
4405
- position:'=',
5158
+ position:'&',
5159
+ moveInProgress:'=',
4406
5160
  select:'&'
4407
5161
  },
4408
5162
  replace:true,
@@ -4462,10 +5216,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
4462
5216
 
4463
5217
  angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
4464
5218
  $templateCache.put("template/accordion/accordion-group.html",
4465
- "<div class=\"panel panel-default\">\n" +
5219
+ "<div class=\"panel panel-default\" ng-class=\"{'panel-open': isOpen}\">\n" +
4466
5220
  " <div class=\"panel-heading\">\n" +
4467
5221
  " <h4 class=\"panel-title\">\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" +
5222
+ " <a href tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
4469
5223
  " </h4>\n" +
4470
5224
  " </div>\n" +
4471
5225
  " <div class=\"panel-collapse collapse\" collapse=\"!isOpen\">\n" +
@@ -4482,8 +5236,8 @@ angular.module("template/accordion/accordion.html", []).run(["$templateCache", f
4482
5236
 
4483
5237
  angular.module("template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
4484
5238
  $templateCache.put("template/alert/alert.html",
4485
- "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissable' : null]\" role=\"alert\">\n" +
4486
- " <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close()\">\n" +
5239
+ "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" +
5240
+ " <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close($event)\">\n" +
4487
5241
  " <span aria-hidden=\"true\">&times;</span>\n" +
4488
5242
  " <span class=\"sr-only\">Close</span>\n" +
4489
5243
  " </button>\n" +
@@ -4496,7 +5250,7 @@ angular.module("template/carousel/carousel.html", []).run(["$templateCache", fun
4496
5250
  $templateCache.put("template/carousel/carousel.html",
4497
5251
  "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
4498
5252
  " <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
4499
- " <li ng-repeat=\"slide in slides | orderBy:'index' track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\n" +
5253
+ " <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\n" +
4500
5254
  " </ol>\n" +
4501
5255
  " <div class=\"carousel-inner\" ng-transclude></div>\n" +
4502
5256
  " <a class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-left\"></span></a>\n" +
@@ -4524,23 +5278,23 @@ angular.module("template/datepicker/datepicker.html", []).run(["$templateCache",
4524
5278
 
4525
5279
  angular.module("template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
4526
5280
  $templateCache.put("template/datepicker/day.html",
4527
- "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
5281
+ "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
4528
5282
  " <thead>\n" +
4529
5283
  " <tr>\n" +
4530
5284
  " <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" +
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" +
5285
+ " <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" +
4532
5286
  " <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" +
4533
5287
  " </tr>\n" +
4534
5288
  " <tr>\n" +
4535
- " <th ng-show=\"showWeeks\" class=\"text-center\"></th>\n" +
4536
- " <th ng-repeat=\"label in labels track by $index\" class=\"text-center\"><small aria-label=\"{{label.full}}\">{{label.abbr}}</small></th>\n" +
5289
+ " <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
5290
+ " <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
4537
5291
  " </tr>\n" +
4538
5292
  " </thead>\n" +
4539
5293
  " <tbody>\n" +
4540
5294
  " <tr ng-repeat=\"row in rows track by $index\">\n" +
4541
- " <td ng-show=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\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" +
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" +
5295
+ " <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
5296
+ " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\" ng-class=\"::dt.customClass\">\n" +
5297
+ " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
4544
5298
  " </td>\n" +
4545
5299
  " </tr>\n" +
4546
5300
  " </tbody>\n" +
@@ -4550,18 +5304,18 @@ angular.module("template/datepicker/day.html", []).run(["$templateCache", functi
4550
5304
 
4551
5305
  angular.module("template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
4552
5306
  $templateCache.put("template/datepicker/month.html",
4553
- "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
5307
+ "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
4554
5308
  " <thead>\n" +
4555
5309
  " <tr>\n" +
4556
5310
  " <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" +
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" +
5311
+ " <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" +
4558
5312
  " <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" +
4559
5313
  " </tr>\n" +
4560
5314
  " </thead>\n" +
4561
5315
  " <tbody>\n" +
4562
5316
  " <tr ng-repeat=\"row in rows track by $index\">\n" +
4563
- " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
4564
- " <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
5317
+ " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\" ng-class=\"::dt.customClass\">\n" +
5318
+ " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
4565
5319
  " </td>\n" +
4566
5320
  " </tr>\n" +
4567
5321
  " </tbody>\n" +
@@ -4571,7 +5325,7 @@ angular.module("template/datepicker/month.html", []).run(["$templateCache", func
4571
5325
 
4572
5326
  angular.module("template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
4573
5327
  $templateCache.put("template/datepicker/popup.html",
4574
- "<ul class=\"dropdown-menu\" ng-style=\"{display: (isOpen && 'block') || 'none', top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\">\n" +
5328
+ "<ul class=\"dropdown-menu\" ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" +
4575
5329
  " <li ng-transclude></li>\n" +
4576
5330
  " <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\n" +
4577
5331
  " <span class=\"btn-group pull-left\">\n" +
@@ -4586,18 +5340,18 @@ angular.module("template/datepicker/popup.html", []).run(["$templateCache", func
4586
5340
 
4587
5341
  angular.module("template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
4588
5342
  $templateCache.put("template/datepicker/year.html",
4589
- "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
5343
+ "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
4590
5344
  " <thead>\n" +
4591
5345
  " <tr>\n" +
4592
5346
  " <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" +
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" +
5347
+ " <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" +
4594
5348
  " <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" +
4595
5349
  " </tr>\n" +
4596
5350
  " </thead>\n" +
4597
5351
  " <tbody>\n" +
4598
5352
  " <tr ng-repeat=\"row in rows track by $index\">\n" +
4599
- " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
4600
- " <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
5353
+ " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\">\n" +
5354
+ " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
4601
5355
  " </td>\n" +
4602
5356
  " </tr>\n" +
4603
5357
  " </tbody>\n" +
@@ -4609,7 +5363,7 @@ angular.module("template/modal/backdrop.html", []).run(["$templateCache", functi
4609
5363
  $templateCache.put("template/modal/backdrop.html",
4610
5364
  "<div class=\"modal-backdrop\"\n" +
4611
5365
  " modal-animation-class=\"fade\"\n" +
4612
- " ng-class=\"{in: animate}\"\n" +
5366
+ " modal-in-class=\"in\"\n" +
4613
5367
  " ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
4614
5368
  "></div>\n" +
4615
5369
  "");
@@ -4619,7 +5373,8 @@ angular.module("template/modal/window.html", []).run(["$templateCache", function
4619
5373
  $templateCache.put("template/modal/window.html",
4620
5374
  "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
4621
5375
  " modal-animation-class=\"fade\"\n" +
4622
- " ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
5376
+ " modal-in-class=\"in\"\n" +
5377
+ " ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
4623
5378
  " <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" modal-transclude></div></div>\n" +
4624
5379
  "</div>\n" +
4625
5380
  "");
@@ -4628,20 +5383,21 @@ angular.module("template/modal/window.html", []).run(["$templateCache", function
4628
5383
  angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
4629
5384
  $templateCache.put("template/pagination/pager.html",
4630
5385
  "<ul class=\"pager\">\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" +
5386
+ " <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
5387
+ " <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
4633
5388
  "</ul>");
4634
5389
  }]);
4635
5390
 
4636
5391
  angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
4637
5392
  $templateCache.put("template/pagination/pagination.html",
4638
5393
  "<ul class=\"pagination\">\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" +
4644
- "</ul>");
5394
+ " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" +
5395
+ " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
5396
+ " <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" +
5397
+ " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
5398
+ " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" +
5399
+ "</ul>\n" +
5400
+ "");
4645
5401
  }]);
4646
5402
 
4647
5403
  angular.module("template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
@@ -4694,8 +5450,8 @@ angular.module("template/tooltip/tooltip-template-popup.html", []).run(["$templa
4694
5450
  "");
4695
5451
  }]);
4696
5452
 
4697
- angular.module("template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
4698
- $templateCache.put("template/popover/popover-template.html",
5453
+ angular.module("template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
5454
+ $templateCache.put("template/popover/popover-html.html",
4699
5455
  "<div class=\"popover\"\n" +
4700
5456
  " tooltip-animation-class=\"fade\"\n" +
4701
5457
  " tooltip-classes\n" +
@@ -4704,22 +5460,25 @@ angular.module("template/popover/popover-template.html", []).run(["$templateCach
4704
5460
  "\n" +
4705
5461
  " <div class=\"popover-inner\">\n" +
4706
5462
  " <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" +
5463
+ " <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" +
4710
5464
  " </div>\n" +
4711
5465
  "</div>\n" +
4712
5466
  "");
4713
5467
  }]);
4714
5468
 
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" +
5469
+ angular.module("template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
5470
+ $templateCache.put("template/popover/popover-template.html",
5471
+ "<div class=\"popover\"\n" +
5472
+ " tooltip-animation-class=\"fade\"\n" +
5473
+ " tooltip-classes\n" +
5474
+ " ng-class=\"{ in: isOpen() }\">\n" +
4718
5475
  " <div class=\"arrow\"></div>\n" +
4719
5476
  "\n" +
4720
5477
  " <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" +
5478
+ " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
5479
+ " <div class=\"popover-content\"\n" +
5480
+ " tooltip-template-transclude=\"contentExp()\"\n" +
5481
+ " tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
4723
5482
  " </div>\n" +
4724
5483
  "</div>\n" +
4725
5484
  "");
@@ -4743,7 +5502,7 @@ angular.module("template/popover/popover.html", []).run(["$templateCache", funct
4743
5502
 
4744
5503
  angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
4745
5504
  $templateCache.put("template/progressbar/bar.html",
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" +
5505
+ "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" style=\"min-width: 0;\" ng-transclude></div>\n" +
4747
5506
  "");
4748
5507
  }]);
4749
5508
 
@@ -4755,7 +5514,7 @@ angular.module("template/progressbar/progress.html", []).run(["$templateCache",
4755
5514
  angular.module("template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
4756
5515
  $templateCache.put("template/progressbar/progressbar.html",
4757
5516
  "<div class=\"progress\">\n" +
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" +
5517
+ " <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" style=\"min-width: 0;\" ng-transclude></div>\n" +
4759
5518
  "</div>\n" +
4760
5519
  "");
4761
5520
  }]);
@@ -4763,10 +5522,10 @@ angular.module("template/progressbar/progressbar.html", []).run(["$templateCache
4763
5522
  angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
4764
5523
  $templateCache.put("template/rating/rating.html",
4765
5524
  "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
4766
- " <i ng-repeat=\"r in range track by $index\" ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\">\n" +
4767
- " <span class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
4768
- " </i>\n" +
4769
- "</span>");
5525
+ " <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
5526
+ " <i ng-repeat-end ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\" ng-attr-title=\"{{r.title}}\" ></i>\n" +
5527
+ "</span>\n" +
5528
+ "");
4770
5529
  }]);
4771
5530
 
4772
5531
  angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
@@ -4795,46 +5554,47 @@ angular.module("template/tabs/tabset.html", []).run(["$templateCache", function(
4795
5554
  angular.module("template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
4796
5555
  $templateCache.put("template/timepicker/timepicker.html",
4797
5556
  "<table>\n" +
4798
- " <tbody>\n" +
4799
- " <tr class=\"text-center\">\n" +
4800
- " <td><a ng-click=\"incrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
4801
- " <td>&nbsp;</td>\n" +
4802
- " <td><a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
4803
- " <td ng-show=\"showMeridian\"></td>\n" +
4804
- " </tr>\n" +
4805
- " <tr>\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" +
4808
- " </td>\n" +
4809
- " <td>:</td>\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" +
4812
- " </td>\n" +
4813
- " <td ng-show=\"showMeridian\"><button type=\"button\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\n" +
4814
- " </tr>\n" +
4815
- " <tr class=\"text-center\">\n" +
4816
- " <td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
4817
- " <td>&nbsp;</td>\n" +
4818
- " <td><a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
4819
- " <td ng-show=\"showMeridian\"></td>\n" +
4820
- " </tr>\n" +
4821
- " </tbody>\n" +
5557
+ " <tbody>\n" +
5558
+ " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
5559
+ " <td><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
5560
+ " <td>&nbsp;</td>\n" +
5561
+ " <td><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
5562
+ " <td ng-show=\"showMeridian\"></td>\n" +
5563
+ " </tr>\n" +
5564
+ " <tr>\n" +
5565
+ " <td class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" +
5566
+ " <input style=\"width:50px;\" type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\">\n" +
5567
+ " </td>\n" +
5568
+ " <td>:</td>\n" +
5569
+ " <td class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
5570
+ " <input style=\"width:50px;\" type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\">\n" +
5571
+ " </td>\n" +
5572
+ " <td ng-show=\"showMeridian\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\n" +
5573
+ " </tr>\n" +
5574
+ " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
5575
+ " <td><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
5576
+ " <td>&nbsp;</td>\n" +
5577
+ " <td><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
5578
+ " <td ng-show=\"showMeridian\"></td>\n" +
5579
+ " </tr>\n" +
5580
+ " </tbody>\n" +
4822
5581
  "</table>\n" +
4823
5582
  "");
4824
5583
  }]);
4825
5584
 
4826
5585
  angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
4827
5586
  $templateCache.put("template/typeahead/typeahead-match.html",
4828
- "<a tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>");
5587
+ "<a href tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>\n" +
5588
+ "");
4829
5589
  }]);
4830
5590
 
4831
5591
  angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
4832
5592
  $templateCache.put("template/typeahead/typeahead-popup.html",
4833
- "<ul class=\"dropdown-menu\" ng-show=\"isOpen()\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
4834
- " <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{match.id}}\">\n" +
5593
+ "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
5594
+ " <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{::match.id}}\">\n" +
4835
5595
  " <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
4836
5596
  " </li>\n" +
4837
5597
  "</ul>\n" +
4838
5598
  "");
4839
5599
  }]);
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>');
5600
+ !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>');