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