angular-ui-bootstrap-rails 0.13.0 → 0.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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>');