angular-ui-bootstrap-rails 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,154 +2,46 @@
|
|
2
2
|
* angular-ui-bootstrap
|
3
3
|
* http://angular-ui.github.io/bootstrap/
|
4
4
|
|
5
|
-
* Version: 0.
|
5
|
+
* Version: 0.13.0 - 2015-05-02
|
6
6
|
* License: MIT
|
7
7
|
*/
|
8
|
-
angular.module("ui.bootstrap", ["ui.bootstrap.
|
9
|
-
angular.module('ui.bootstrap.
|
10
|
-
|
11
|
-
/**
|
12
|
-
* $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
|
13
|
-
* @param {DOMElement} element The DOMElement that will be animated.
|
14
|
-
* @param {string|object|function} trigger The thing that will cause the transition to start:
|
15
|
-
* - As a string, it represents the css class to be added to the element.
|
16
|
-
* - As an object, it represents a hash of style attributes to be applied to the element.
|
17
|
-
* - As a function, it represents a function to be called that will cause the transition to occur.
|
18
|
-
* @return {Promise} A promise that is resolved when the transition finishes.
|
19
|
-
*/
|
20
|
-
.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
|
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"]);
|
9
|
+
angular.module('ui.bootstrap.collapse', [])
|
21
10
|
|
22
|
-
|
23
|
-
options = options || {};
|
24
|
-
var deferred = $q.defer();
|
25
|
-
var endEventName = $transition[options.animation ? 'animationEndEventName' : 'transitionEndEventName'];
|
26
|
-
|
27
|
-
var transitionEndHandler = function(event) {
|
28
|
-
$rootScope.$apply(function() {
|
29
|
-
element.unbind(endEventName, transitionEndHandler);
|
30
|
-
deferred.resolve(element);
|
31
|
-
});
|
32
|
-
};
|
33
|
-
|
34
|
-
if (endEventName) {
|
35
|
-
element.bind(endEventName, transitionEndHandler);
|
36
|
-
}
|
37
|
-
|
38
|
-
// Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
|
39
|
-
$timeout(function() {
|
40
|
-
if ( angular.isString(trigger) ) {
|
41
|
-
element.addClass(trigger);
|
42
|
-
} else if ( angular.isFunction(trigger) ) {
|
43
|
-
trigger(element);
|
44
|
-
} else if ( angular.isObject(trigger) ) {
|
45
|
-
element.css(trigger);
|
46
|
-
}
|
47
|
-
//If browser does not support transitions, instantly resolve
|
48
|
-
if ( !endEventName ) {
|
49
|
-
deferred.resolve(element);
|
50
|
-
}
|
51
|
-
});
|
52
|
-
|
53
|
-
// Add our custom cancel function to the promise that is returned
|
54
|
-
// We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
|
55
|
-
// i.e. it will therefore never raise a transitionEnd event for that transition
|
56
|
-
deferred.promise.cancel = function() {
|
57
|
-
if ( endEventName ) {
|
58
|
-
element.unbind(endEventName, transitionEndHandler);
|
59
|
-
}
|
60
|
-
deferred.reject('Transition cancelled');
|
61
|
-
};
|
62
|
-
|
63
|
-
return deferred.promise;
|
64
|
-
};
|
65
|
-
|
66
|
-
// Work out the name of the transitionEnd event
|
67
|
-
var transElement = document.createElement('trans');
|
68
|
-
var transitionEndEventNames = {
|
69
|
-
'WebkitTransition': 'webkitTransitionEnd',
|
70
|
-
'MozTransition': 'transitionend',
|
71
|
-
'OTransition': 'oTransitionEnd',
|
72
|
-
'transition': 'transitionend'
|
73
|
-
};
|
74
|
-
var animationEndEventNames = {
|
75
|
-
'WebkitTransition': 'webkitAnimationEnd',
|
76
|
-
'MozTransition': 'animationend',
|
77
|
-
'OTransition': 'oAnimationEnd',
|
78
|
-
'transition': 'animationend'
|
79
|
-
};
|
80
|
-
function findEndEventName(endEventNames) {
|
81
|
-
for (var name in endEventNames){
|
82
|
-
if (transElement.style[name] !== undefined) {
|
83
|
-
return endEventNames[name];
|
84
|
-
}
|
85
|
-
}
|
86
|
-
}
|
87
|
-
$transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
|
88
|
-
$transition.animationEndEventName = findEndEventName(animationEndEventNames);
|
89
|
-
return $transition;
|
90
|
-
}]);
|
91
|
-
|
92
|
-
angular.module('ui.bootstrap.collapse', ['ui.bootstrap.transition'])
|
93
|
-
|
94
|
-
.directive('collapse', ['$transition', function ($transition) {
|
11
|
+
.directive('collapse', ['$animate', function ($animate) {
|
95
12
|
|
96
13
|
return {
|
97
14
|
link: function (scope, element, attrs) {
|
98
|
-
|
99
|
-
var initialAnimSkip = true;
|
100
|
-
var currentTransition;
|
101
|
-
|
102
|
-
function doTransition(change) {
|
103
|
-
var newTransition = $transition(element, change);
|
104
|
-
if (currentTransition) {
|
105
|
-
currentTransition.cancel();
|
106
|
-
}
|
107
|
-
currentTransition = newTransition;
|
108
|
-
newTransition.then(newTransitionDone, newTransitionDone);
|
109
|
-
return newTransition;
|
110
|
-
|
111
|
-
function newTransitionDone() {
|
112
|
-
// Make sure it's this transition, otherwise, leave it alone.
|
113
|
-
if (currentTransition === newTransition) {
|
114
|
-
currentTransition = undefined;
|
115
|
-
}
|
116
|
-
}
|
117
|
-
}
|
118
|
-
|
119
15
|
function expand() {
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
}
|
124
|
-
element.removeClass('collapse').addClass('collapsing');
|
125
|
-
doTransition({ height: element[0].scrollHeight + 'px' }).then(expandDone);
|
126
|
-
}
|
16
|
+
element.removeClass('collapse').addClass('collapsing');
|
17
|
+
$animate.addClass(element, 'in', {
|
18
|
+
to: { height: element[0].scrollHeight + 'px' }
|
19
|
+
}).then(expandDone);
|
127
20
|
}
|
128
21
|
|
129
22
|
function expandDone() {
|
130
23
|
element.removeClass('collapsing');
|
131
|
-
element.addClass('collapse in');
|
132
24
|
element.css({height: 'auto'});
|
133
25
|
}
|
134
26
|
|
135
27
|
function collapse() {
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
//
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
}
|
28
|
+
element
|
29
|
+
// IMPORTANT: The height must be set before adding "collapsing" class.
|
30
|
+
// Otherwise, the browser attempts to animate from height 0 (in
|
31
|
+
// collapsing class) to the given height here.
|
32
|
+
.css({height: element[0].scrollHeight + 'px'})
|
33
|
+
// initially all panel collapse have the collapse class, this removal
|
34
|
+
// prevents the animation from jumping to collapsed state
|
35
|
+
.removeClass('collapse')
|
36
|
+
.addClass('collapsing');
|
37
|
+
|
38
|
+
$animate.removeClass(element, 'in', {
|
39
|
+
to: {height: '0'}
|
40
|
+
}).then(collapseDone);
|
150
41
|
}
|
151
42
|
|
152
43
|
function collapseDone() {
|
44
|
+
element.css({height: '0'}); // Required so that collapse works when animation is disabled
|
153
45
|
element.removeClass('collapsing');
|
154
46
|
element.addClass('collapse');
|
155
47
|
}
|
@@ -271,7 +163,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
271
163
|
// Pass the heading to the accordion-group controller
|
272
164
|
// so that it can be transcluded into the right place in the template
|
273
165
|
// [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
|
274
|
-
accordionGroupCtrl.setHeading(transclude(scope,
|
166
|
+
accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
|
275
167
|
}
|
276
168
|
};
|
277
169
|
})
|
@@ -294,7 +186,9 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
294
186
|
});
|
295
187
|
}
|
296
188
|
};
|
297
|
-
})
|
189
|
+
})
|
190
|
+
|
191
|
+
;
|
298
192
|
|
299
193
|
angular.module('ui.bootstrap.alert', [])
|
300
194
|
|
@@ -421,8 +315,8 @@ angular.module('ui.bootstrap.buttons', [])
|
|
421
315
|
* AngularJS version of an image carousel.
|
422
316
|
*
|
423
317
|
*/
|
424
|
-
angular.module('ui.bootstrap.carousel', [
|
425
|
-
.controller('CarouselController', ['$scope', '$
|
318
|
+
angular.module('ui.bootstrap.carousel', [])
|
319
|
+
.controller('CarouselController', ['$scope', '$interval', '$animate', function ($scope, $interval, $animate) {
|
426
320
|
var self = this,
|
427
321
|
slides = self.slides = $scope.slides = [],
|
428
322
|
currentIndex = -1,
|
@@ -432,82 +326,76 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
|
|
432
326
|
var destroyed = false;
|
433
327
|
/* direction: "prev" or "next" */
|
434
328
|
self.select = $scope.select = function(nextSlide, direction) {
|
435
|
-
var nextIndex =
|
329
|
+
var nextIndex = self.indexOfSlide(nextSlide);
|
436
330
|
//Decide direction if it's not given
|
437
331
|
if (direction === undefined) {
|
438
|
-
direction = nextIndex >
|
332
|
+
direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
|
439
333
|
}
|
440
334
|
if (nextSlide && nextSlide !== self.currentSlide) {
|
441
|
-
|
442
|
-
$scope.$currentTransition.cancel();
|
443
|
-
//Timeout so ng-class in template has time to fix classes for finished slide
|
444
|
-
$timeout(goNext);
|
445
|
-
} else {
|
446
|
-
goNext();
|
447
|
-
}
|
335
|
+
goNext();
|
448
336
|
}
|
449
337
|
function goNext() {
|
450
338
|
// Scope has been destroyed, stop here.
|
451
339
|
if (destroyed) { return; }
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
angular.forEach(slides, function(slide) {
|
460
|
-
angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});
|
340
|
+
|
341
|
+
angular.extend(nextSlide, {direction: direction, active: true});
|
342
|
+
angular.extend(self.currentSlide || {}, {direction: direction, active: false});
|
343
|
+
if ($animate.enabled() && !$scope.noTransition && nextSlide.$element) {
|
344
|
+
$scope.$currentTransition = true;
|
345
|
+
nextSlide.$element.one('$animate:close', function closeFn() {
|
346
|
+
$scope.$currentTransition = null;
|
461
347
|
});
|
462
|
-
angular.extend(nextSlide, {direction: direction, active: true, entering: true});
|
463
|
-
angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});
|
464
|
-
|
465
|
-
$scope.$currentTransition = $transition(nextSlide.$element, {});
|
466
|
-
//We have to create new pointers inside a closure since next & current will change
|
467
|
-
(function(next,current) {
|
468
|
-
$scope.$currentTransition.then(
|
469
|
-
function(){ transitionDone(next, current); },
|
470
|
-
function(){ transitionDone(next, current); }
|
471
|
-
);
|
472
|
-
}(nextSlide, self.currentSlide));
|
473
|
-
} else {
|
474
|
-
transitionDone(nextSlide, self.currentSlide);
|
475
348
|
}
|
349
|
+
|
476
350
|
self.currentSlide = nextSlide;
|
477
351
|
currentIndex = nextIndex;
|
478
352
|
//every time you change slides, reset the timer
|
479
353
|
restartTimer();
|
480
354
|
}
|
481
|
-
function transitionDone(next, current) {
|
482
|
-
angular.extend(next, {direction: '', active: true, leaving: false, entering: false});
|
483
|
-
angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});
|
484
|
-
$scope.$currentTransition = null;
|
485
|
-
}
|
486
355
|
};
|
487
356
|
$scope.$on('$destroy', function () {
|
488
357
|
destroyed = true;
|
489
358
|
});
|
490
359
|
|
360
|
+
function getSlideByIndex(index) {
|
361
|
+
if (angular.isUndefined(slides[index].index)) {
|
362
|
+
return slides[index];
|
363
|
+
}
|
364
|
+
var i, len = slides.length;
|
365
|
+
for (i = 0; i < slides.length; ++i) {
|
366
|
+
if (slides[i].index == index) {
|
367
|
+
return slides[i];
|
368
|
+
}
|
369
|
+
}
|
370
|
+
}
|
371
|
+
|
372
|
+
self.getCurrentIndex = function() {
|
373
|
+
if (self.currentSlide && angular.isDefined(self.currentSlide.index)) {
|
374
|
+
return +self.currentSlide.index;
|
375
|
+
}
|
376
|
+
return currentIndex;
|
377
|
+
};
|
378
|
+
|
491
379
|
/* Allow outside people to call indexOf on slides array */
|
492
380
|
self.indexOfSlide = function(slide) {
|
493
|
-
return slides.indexOf(slide);
|
381
|
+
return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
|
494
382
|
};
|
495
383
|
|
496
384
|
$scope.next = function() {
|
497
|
-
var newIndex = (
|
385
|
+
var newIndex = (self.getCurrentIndex() + 1) % slides.length;
|
498
386
|
|
499
387
|
//Prevent this user-triggered transition from occurring if there is already one in progress
|
500
388
|
if (!$scope.$currentTransition) {
|
501
|
-
return self.select(
|
389
|
+
return self.select(getSlideByIndex(newIndex), 'next');
|
502
390
|
}
|
503
391
|
};
|
504
392
|
|
505
393
|
$scope.prev = function() {
|
506
|
-
var newIndex =
|
394
|
+
var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
|
507
395
|
|
508
396
|
//Prevent this user-triggered transition from occurring if there is already one in progress
|
509
397
|
if (!$scope.$currentTransition) {
|
510
|
-
return self.select(
|
398
|
+
return self.select(getSlideByIndex(newIndex), 'prev');
|
511
399
|
}
|
512
400
|
};
|
513
401
|
|
@@ -570,6 +458,11 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
|
|
570
458
|
};
|
571
459
|
|
572
460
|
self.removeSlide = function(slide) {
|
461
|
+
if (angular.isDefined(slide.index)) {
|
462
|
+
slides.sort(function(a, b) {
|
463
|
+
return +a.index > +b.index;
|
464
|
+
});
|
465
|
+
}
|
573
466
|
//get the index of the slide inside the carousel
|
574
467
|
var index = slides.indexOf(slide);
|
575
468
|
slides.splice(index, 1);
|
@@ -649,13 +542,14 @@ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
|
|
649
542
|
* Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element.
|
650
543
|
*
|
651
544
|
* @param {boolean=} active Model binding, whether or not this slide is currently active.
|
545
|
+
* @param {number=} index The index of the slide. The slides will be sorted by this parameter.
|
652
546
|
*
|
653
547
|
* @example
|
654
548
|
<example module="ui.bootstrap">
|
655
549
|
<file name="index.html">
|
656
550
|
<div ng-controller="CarouselDemoCtrl">
|
657
551
|
<carousel>
|
658
|
-
<slide ng-repeat="slide in slides" active="slide.active">
|
552
|
+
<slide ng-repeat="slide in slides" active="slide.active" index="$index">
|
659
553
|
<img ng-src="{{slide.image}}" style="margin:auto;">
|
660
554
|
<div class="carousel-caption">
|
661
555
|
<h4>Slide {{$index}}</h4>
|
@@ -689,7 +583,8 @@ function CarouselDemoCtrl($scope) {
|
|
689
583
|
replace: true,
|
690
584
|
templateUrl: 'template/carousel/slide.html',
|
691
585
|
scope: {
|
692
|
-
active: '=?'
|
586
|
+
active: '=?',
|
587
|
+
index: '=?'
|
693
588
|
},
|
694
589
|
link: function (scope, element, attrs, carouselCtrl) {
|
695
590
|
carouselCtrl.addSlide(scope, element);
|
@@ -705,11 +600,64 @@ function CarouselDemoCtrl($scope) {
|
|
705
600
|
});
|
706
601
|
}
|
707
602
|
};
|
708
|
-
})
|
603
|
+
})
|
604
|
+
|
605
|
+
.animation('.item', [
|
606
|
+
'$animate',
|
607
|
+
function ($animate) {
|
608
|
+
return {
|
609
|
+
beforeAddClass: function (element, className, done) {
|
610
|
+
// Due to transclusion, noTransition property is on parent's scope
|
611
|
+
if (className == 'active' && element.parent() &&
|
612
|
+
!element.parent().scope().noTransition) {
|
613
|
+
var stopped = false;
|
614
|
+
var direction = element.isolateScope().direction;
|
615
|
+
var directionClass = direction == 'next' ? 'left' : 'right';
|
616
|
+
element.addClass(direction);
|
617
|
+
$animate.addClass(element, directionClass).then(function () {
|
618
|
+
if (!stopped) {
|
619
|
+
element.removeClass(directionClass + ' ' + direction);
|
620
|
+
}
|
621
|
+
done();
|
622
|
+
});
|
623
|
+
|
624
|
+
return function () {
|
625
|
+
stopped = true;
|
626
|
+
};
|
627
|
+
}
|
628
|
+
done();
|
629
|
+
},
|
630
|
+
beforeRemoveClass: function (element, className, done) {
|
631
|
+
// Due to transclusion, noTransition property is on parent's scope
|
632
|
+
if (className == 'active' && element.parent() &&
|
633
|
+
!element.parent().scope().noTransition) {
|
634
|
+
var stopped = false;
|
635
|
+
var direction = element.isolateScope().direction;
|
636
|
+
var directionClass = direction == 'next' ? 'left' : 'right';
|
637
|
+
$animate.addClass(element, directionClass).then(function () {
|
638
|
+
if (!stopped) {
|
639
|
+
element.removeClass(directionClass);
|
640
|
+
}
|
641
|
+
done();
|
642
|
+
});
|
643
|
+
return function () {
|
644
|
+
stopped = true;
|
645
|
+
};
|
646
|
+
}
|
647
|
+
done();
|
648
|
+
}
|
649
|
+
};
|
650
|
+
|
651
|
+
}])
|
652
|
+
|
653
|
+
|
654
|
+
;
|
709
655
|
|
710
656
|
angular.module('ui.bootstrap.dateparser', [])
|
711
657
|
|
712
658
|
.service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {
|
659
|
+
// Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
|
660
|
+
var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
|
713
661
|
|
714
662
|
this.parsers = {};
|
715
663
|
|
@@ -755,6 +703,34 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
755
703
|
},
|
756
704
|
'EEE': {
|
757
705
|
regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
|
706
|
+
},
|
707
|
+
'HH': {
|
708
|
+
regex: '(?:0|1)[0-9]|2[0-3]',
|
709
|
+
apply: function(value) { this.hours = +value; }
|
710
|
+
},
|
711
|
+
'H': {
|
712
|
+
regex: '1?[0-9]|2[0-3]',
|
713
|
+
apply: function(value) { this.hours = +value; }
|
714
|
+
},
|
715
|
+
'mm': {
|
716
|
+
regex: '[0-5][0-9]',
|
717
|
+
apply: function(value) { this.minutes = +value; }
|
718
|
+
},
|
719
|
+
'm': {
|
720
|
+
regex: '[0-9]|[1-5][0-9]',
|
721
|
+
apply: function(value) { this.minutes = +value; }
|
722
|
+
},
|
723
|
+
'sss': {
|
724
|
+
regex: '[0-9][0-9][0-9]',
|
725
|
+
apply: function(value) { this.milliseconds = +value; }
|
726
|
+
},
|
727
|
+
'ss': {
|
728
|
+
regex: '[0-5][0-9]',
|
729
|
+
apply: function(value) { this.seconds = +value; }
|
730
|
+
},
|
731
|
+
's': {
|
732
|
+
regex: '[0-9]|[1-5][0-9]',
|
733
|
+
apply: function(value) { this.seconds = +value; }
|
758
734
|
}
|
759
735
|
};
|
760
736
|
|
@@ -785,12 +761,13 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
785
761
|
};
|
786
762
|
}
|
787
763
|
|
788
|
-
this.parse = function(input, format) {
|
764
|
+
this.parse = function(input, format, baseDate) {
|
789
765
|
if ( !angular.isString(input) || !format ) {
|
790
766
|
return input;
|
791
767
|
}
|
792
768
|
|
793
769
|
format = $locale.DATETIME_FORMATS[format] || format;
|
770
|
+
format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
|
794
771
|
|
795
772
|
if ( !this.parsers[format] ) {
|
796
773
|
this.parsers[format] = createParser(format);
|
@@ -802,7 +779,20 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
802
779
|
results = input.match(regex);
|
803
780
|
|
804
781
|
if ( results && results.length ) {
|
805
|
-
var fields
|
782
|
+
var fields, dt;
|
783
|
+
if (baseDate) {
|
784
|
+
fields = {
|
785
|
+
year: baseDate.getFullYear(),
|
786
|
+
month: baseDate.getMonth(),
|
787
|
+
date: baseDate.getDate(),
|
788
|
+
hours: baseDate.getHours(),
|
789
|
+
minutes: baseDate.getMinutes(),
|
790
|
+
seconds: baseDate.getSeconds(),
|
791
|
+
milliseconds: baseDate.getMilliseconds()
|
792
|
+
};
|
793
|
+
} else {
|
794
|
+
fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
|
795
|
+
}
|
806
796
|
|
807
797
|
for( var i = 1, n = results.length; i < n; i++ ) {
|
808
798
|
var mapper = map[i-1];
|
@@ -812,7 +802,8 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
812
802
|
}
|
813
803
|
|
814
804
|
if ( isValid(fields.year, fields.month, fields.date) ) {
|
815
|
-
dt = new Date(
|
805
|
+
dt = new Date(fields.year, fields.month, fields.date, fields.hours, fields.minutes, fields.seconds,
|
806
|
+
fields.milliseconds || 0);
|
816
807
|
}
|
817
808
|
|
818
809
|
return dt;
|
@@ -822,6 +813,10 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
822
813
|
// Check if date is valid for specific month (and year for February).
|
823
814
|
// Month: 0 = Jan, 1 = Feb, etc
|
824
815
|
function isValid(year, month, date) {
|
816
|
+
if (date < 1) {
|
817
|
+
return false;
|
818
|
+
}
|
819
|
+
|
825
820
|
if ( month === 1 && date > 28) {
|
826
821
|
return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
|
827
822
|
}
|
@@ -1003,7 +998,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1003
998
|
startingDay: 0,
|
1004
999
|
yearRange: 20,
|
1005
1000
|
minDate: null,
|
1006
|
-
maxDate: null
|
1001
|
+
maxDate: null,
|
1002
|
+
shortcutPropagation: false
|
1007
1003
|
})
|
1008
1004
|
|
1009
1005
|
.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
|
@@ -1015,7 +1011,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1015
1011
|
|
1016
1012
|
// Configuration attributes
|
1017
1013
|
angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
|
1018
|
-
'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {
|
1014
|
+
'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function( key, index ) {
|
1019
1015
|
self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
|
1020
1016
|
});
|
1021
1017
|
|
@@ -1032,8 +1028,20 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1032
1028
|
});
|
1033
1029
|
|
1034
1030
|
$scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
|
1031
|
+
$scope.maxMode = self.maxMode;
|
1035
1032
|
$scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
|
1036
|
-
|
1033
|
+
|
1034
|
+
if(angular.isDefined($attrs.initDate)) {
|
1035
|
+
this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date();
|
1036
|
+
$scope.$parent.$watch($attrs.initDate, function(initDate){
|
1037
|
+
if(initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)){
|
1038
|
+
self.activeDate = initDate;
|
1039
|
+
self.refreshView();
|
1040
|
+
}
|
1041
|
+
});
|
1042
|
+
} else {
|
1043
|
+
this.activeDate = new Date();
|
1044
|
+
}
|
1037
1045
|
|
1038
1046
|
$scope.isActive = function(dateObject) {
|
1039
1047
|
if (self.compare(dateObject.date, self.activeDate) === 0) {
|
@@ -1052,8 +1060,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1052
1060
|
};
|
1053
1061
|
|
1054
1062
|
this.render = function() {
|
1055
|
-
if ( ngModelCtrl.$
|
1056
|
-
var date = new Date( ngModelCtrl.$
|
1063
|
+
if ( ngModelCtrl.$viewValue ) {
|
1064
|
+
var date = new Date( ngModelCtrl.$viewValue ),
|
1057
1065
|
isValid = !isNaN(date);
|
1058
1066
|
|
1059
1067
|
if ( isValid ) {
|
@@ -1070,19 +1078,20 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1070
1078
|
if ( this.element ) {
|
1071
1079
|
this._refreshView();
|
1072
1080
|
|
1073
|
-
var date = ngModelCtrl.$
|
1081
|
+
var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
|
1074
1082
|
ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
|
1075
1083
|
}
|
1076
1084
|
};
|
1077
1085
|
|
1078
1086
|
this.createDateObject = function(date, format) {
|
1079
|
-
var model = ngModelCtrl.$
|
1087
|
+
var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
|
1080
1088
|
return {
|
1081
1089
|
date: date,
|
1082
1090
|
label: dateFilter(date, format),
|
1083
1091
|
selected: model && this.compare(date, model) === 0,
|
1084
1092
|
disabled: this.isDisabled(date),
|
1085
|
-
current: this.compare(date, new Date()) === 0
|
1093
|
+
current: this.compare(date, new Date()) === 0,
|
1094
|
+
customClass: this.customClass(date)
|
1086
1095
|
};
|
1087
1096
|
};
|
1088
1097
|
|
@@ -1090,6 +1099,10 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1090
1099
|
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})));
|
1091
1100
|
};
|
1092
1101
|
|
1102
|
+
this.customClass = function( date ) {
|
1103
|
+
return $scope.customClass({date: date, mode: $scope.datepickerMode});
|
1104
|
+
};
|
1105
|
+
|
1093
1106
|
// Split array into smaller arrays
|
1094
1107
|
this.split = function(arr, size) {
|
1095
1108
|
var arrays = [];
|
@@ -1101,7 +1114,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1101
1114
|
|
1102
1115
|
$scope.select = function( date ) {
|
1103
1116
|
if ( $scope.datepickerMode === self.minMode ) {
|
1104
|
-
var dt = ngModelCtrl.$
|
1117
|
+
var dt = ngModelCtrl.$viewValue ? new Date( ngModelCtrl.$viewValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
|
1105
1118
|
dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
|
1106
1119
|
ngModelCtrl.$setViewValue( dt );
|
1107
1120
|
ngModelCtrl.$render();
|
@@ -1148,7 +1161,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1148
1161
|
}
|
1149
1162
|
|
1150
1163
|
evt.preventDefault();
|
1151
|
-
|
1164
|
+
if(!self.shortcutPropagation){
|
1165
|
+
evt.stopPropagation();
|
1166
|
+
}
|
1152
1167
|
|
1153
1168
|
if (key === 'enter' || key === 'space') {
|
1154
1169
|
if ( self.isDisabled(self.activeDate)) {
|
@@ -1173,7 +1188,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1173
1188
|
templateUrl: 'template/datepicker/datepicker.html',
|
1174
1189
|
scope: {
|
1175
1190
|
datepickerMode: '=?',
|
1176
|
-
dateDisabled: '&'
|
1191
|
+
dateDisabled: '&',
|
1192
|
+
customClass: '&',
|
1193
|
+
shortcutPropagation: '&?'
|
1177
1194
|
},
|
1178
1195
|
require: ['datepicker', '?^ngModel'],
|
1179
1196
|
controller: 'DatepickerController',
|
@@ -1248,9 +1265,12 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1248
1265
|
|
1249
1266
|
if ( scope.showWeeks ) {
|
1250
1267
|
scope.weekNumbers = [];
|
1251
|
-
var
|
1268
|
+
var thursdayIndex = (4 + 7 - ctrl.startingDay) % 7,
|
1252
1269
|
numWeeks = scope.rows.length;
|
1253
|
-
|
1270
|
+
for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
|
1271
|
+
scope.weekNumbers.push(
|
1272
|
+
getISO8601WeekNumber( scope.rows[curWeek][thursdayIndex].date ));
|
1273
|
+
}
|
1254
1274
|
}
|
1255
1275
|
};
|
1256
1276
|
|
@@ -1411,6 +1431,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1411
1431
|
|
1412
1432
|
.constant('datepickerPopupConfig', {
|
1413
1433
|
datepickerPopup: 'yyyy-MM-dd',
|
1434
|
+
html5Types: {
|
1435
|
+
date: 'yyyy-MM-dd',
|
1436
|
+
'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
|
1437
|
+
'month': 'yyyy-MM'
|
1438
|
+
},
|
1414
1439
|
currentText: 'Today',
|
1415
1440
|
clearText: 'Clear',
|
1416
1441
|
closeText: 'Done',
|
@@ -1429,7 +1454,8 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
|
|
1429
1454
|
currentText: '@',
|
1430
1455
|
clearText: '@',
|
1431
1456
|
closeText: '@',
|
1432
|
-
dateDisabled: '&'
|
1457
|
+
dateDisabled: '&',
|
1458
|
+
customClass: '&'
|
1433
1459
|
},
|
1434
1460
|
link: function(scope, element, attrs, ngModel) {
|
1435
1461
|
var dateFormat,
|
@@ -1442,10 +1468,34 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
|
|
1442
1468
|
return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
|
1443
1469
|
};
|
1444
1470
|
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1471
|
+
var isHtml5DateInput = false;
|
1472
|
+
if (datepickerPopupConfig.html5Types[attrs.type]) {
|
1473
|
+
dateFormat = datepickerPopupConfig.html5Types[attrs.type];
|
1474
|
+
isHtml5DateInput = true;
|
1475
|
+
} else {
|
1476
|
+
dateFormat = attrs.datepickerPopup || datepickerPopupConfig.datepickerPopup;
|
1477
|
+
attrs.$observe('datepickerPopup', function(value, oldValue) {
|
1478
|
+
var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
|
1479
|
+
// Invalidate the $modelValue to ensure that formatters re-run
|
1480
|
+
// FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
|
1481
|
+
if (newDateFormat !== dateFormat) {
|
1482
|
+
dateFormat = newDateFormat;
|
1483
|
+
ngModel.$modelValue = null;
|
1484
|
+
|
1485
|
+
if (!dateFormat) {
|
1486
|
+
throw new Error('datepickerPopup must have a date format specified.');
|
1487
|
+
}
|
1488
|
+
}
|
1489
|
+
});
|
1490
|
+
}
|
1491
|
+
|
1492
|
+
if (!dateFormat) {
|
1493
|
+
throw new Error('datepickerPopup must have a date format specified.');
|
1494
|
+
}
|
1495
|
+
|
1496
|
+
if (isHtml5DateInput && attrs.datepickerPopup) {
|
1497
|
+
throw new Error('HTML5 date input types do not support custom formats.');
|
1498
|
+
}
|
1449
1499
|
|
1450
1500
|
// popup element used to display calendar
|
1451
1501
|
var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
|
@@ -1460,14 +1510,27 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
|
|
1460
1510
|
|
1461
1511
|
// datepicker element
|
1462
1512
|
var datepickerEl = angular.element(popupEl.children()[0]);
|
1513
|
+
if (isHtml5DateInput) {
|
1514
|
+
if (attrs.type == 'month') {
|
1515
|
+
datepickerEl.attr('datepicker-mode', '"month"');
|
1516
|
+
datepickerEl.attr('min-mode', 'month');
|
1517
|
+
}
|
1518
|
+
}
|
1519
|
+
|
1463
1520
|
if ( attrs.datepickerOptions ) {
|
1464
|
-
|
1521
|
+
var options = scope.$parent.$eval(attrs.datepickerOptions);
|
1522
|
+
if(options.initDate) {
|
1523
|
+
scope.initDate = options.initDate;
|
1524
|
+
datepickerEl.attr( 'init-date', 'initDate' );
|
1525
|
+
delete options.initDate;
|
1526
|
+
}
|
1527
|
+
angular.forEach(options, function( value, option ) {
|
1465
1528
|
datepickerEl.attr( cameltoDash(option), value );
|
1466
1529
|
});
|
1467
1530
|
}
|
1468
1531
|
|
1469
1532
|
scope.watchData = {};
|
1470
|
-
angular.forEach(['minDate', 'maxDate', 'datepickerMode'], function( key ) {
|
1533
|
+
angular.forEach(['minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function( key ) {
|
1471
1534
|
if ( attrs[key] ) {
|
1472
1535
|
var getAttribute = $parse(attrs[key]);
|
1473
1536
|
scope.$parent.$watch(getAttribute, function(value){
|
@@ -1490,36 +1553,78 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
|
|
1490
1553
|
datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
|
1491
1554
|
}
|
1492
1555
|
|
1556
|
+
if (attrs.showWeeks) {
|
1557
|
+
datepickerEl.attr('show-weeks', attrs.showWeeks);
|
1558
|
+
}
|
1559
|
+
|
1560
|
+
if (attrs.customClass){
|
1561
|
+
datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
|
1562
|
+
}
|
1563
|
+
|
1493
1564
|
function parseDate(viewValue) {
|
1565
|
+
if (angular.isNumber(viewValue)) {
|
1566
|
+
// presumably timestamp to date object
|
1567
|
+
viewValue = new Date(viewValue);
|
1568
|
+
}
|
1569
|
+
|
1494
1570
|
if (!viewValue) {
|
1495
|
-
ngModel.$setValidity('date', true);
|
1496
1571
|
return null;
|
1497
1572
|
} else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
|
1498
|
-
ngModel.$setValidity('date', true);
|
1499
1573
|
return viewValue;
|
1500
1574
|
} else if (angular.isString(viewValue)) {
|
1501
|
-
var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);
|
1575
|
+
var date = dateParser.parse(viewValue, dateFormat, scope.date) || new Date(viewValue);
|
1502
1576
|
if (isNaN(date)) {
|
1503
|
-
ngModel.$setValidity('date', false);
|
1504
1577
|
return undefined;
|
1505
1578
|
} else {
|
1506
|
-
ngModel.$setValidity('date', true);
|
1507
1579
|
return date;
|
1508
1580
|
}
|
1509
1581
|
} else {
|
1510
|
-
ngModel.$setValidity('date', false);
|
1511
1582
|
return undefined;
|
1512
1583
|
}
|
1513
1584
|
}
|
1514
|
-
|
1585
|
+
|
1586
|
+
function validator(modelValue, viewValue) {
|
1587
|
+
var value = modelValue || viewValue;
|
1588
|
+
if (angular.isNumber(value)) {
|
1589
|
+
value = new Date(value);
|
1590
|
+
}
|
1591
|
+
if (!value) {
|
1592
|
+
return true;
|
1593
|
+
} else if (angular.isDate(value) && !isNaN(value)) {
|
1594
|
+
return true;
|
1595
|
+
} else if (angular.isString(value)) {
|
1596
|
+
var date = dateParser.parse(value, dateFormat) || new Date(value);
|
1597
|
+
return !isNaN(date);
|
1598
|
+
} else {
|
1599
|
+
return false;
|
1600
|
+
}
|
1601
|
+
}
|
1602
|
+
|
1603
|
+
if (!isHtml5DateInput) {
|
1604
|
+
// Internal API to maintain the correct ng-invalid-[key] class
|
1605
|
+
ngModel.$$parserName = 'date';
|
1606
|
+
ngModel.$validators.date = validator;
|
1607
|
+
ngModel.$parsers.unshift(parseDate);
|
1608
|
+
ngModel.$formatters.push(function (value) {
|
1609
|
+
scope.date = value;
|
1610
|
+
return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
|
1611
|
+
});
|
1612
|
+
}
|
1613
|
+
else {
|
1614
|
+
ngModel.$formatters.push(function (value) {
|
1615
|
+
scope.date = value;
|
1616
|
+
return value;
|
1617
|
+
});
|
1618
|
+
}
|
1515
1619
|
|
1516
1620
|
// Inner change
|
1517
1621
|
scope.dateSelection = function(dt) {
|
1518
1622
|
if (angular.isDefined(dt)) {
|
1519
1623
|
scope.date = dt;
|
1520
1624
|
}
|
1521
|
-
|
1522
|
-
|
1625
|
+
var date = scope.date ? dateFilter(scope.date, dateFormat) : '';
|
1626
|
+
element.val(date);
|
1627
|
+
ngModel.$setViewValue(date);
|
1523
1628
|
|
1524
1629
|
if ( closeOnDateSelection ) {
|
1525
1630
|
scope.isOpen = false;
|
@@ -1527,19 +1632,11 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
|
|
1527
1632
|
}
|
1528
1633
|
};
|
1529
1634
|
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
});
|
1635
|
+
// Detect changes in the view from the text box
|
1636
|
+
ngModel.$viewChangeListeners.push(function () {
|
1637
|
+
scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date) || new Date(ngModel.$viewValue);
|
1534
1638
|
});
|
1535
1639
|
|
1536
|
-
// Outter change
|
1537
|
-
ngModel.$render = function() {
|
1538
|
-
var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
|
1539
|
-
element.val(date);
|
1540
|
-
scope.date = parseDate( ngModel.$modelValue );
|
1541
|
-
};
|
1542
|
-
|
1543
1640
|
var documentClickBind = function(event) {
|
1544
1641
|
if (scope.isOpen && event.target !== element[0]) {
|
1545
1642
|
scope.$apply(function() {
|
@@ -1556,7 +1653,9 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
|
|
1556
1653
|
scope.keydown = function(evt) {
|
1557
1654
|
if (evt.which === 27) {
|
1558
1655
|
evt.preventDefault();
|
1559
|
-
|
1656
|
+
if (scope.isOpen) {
|
1657
|
+
evt.stopPropagation();
|
1658
|
+
}
|
1560
1659
|
scope.close();
|
1561
1660
|
} else if (evt.which === 40 && !scope.isOpen) {
|
1562
1661
|
scope.isOpen = true;
|
@@ -1578,8 +1677,8 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
|
|
1578
1677
|
scope.select = function( date ) {
|
1579
1678
|
if (date === 'today') {
|
1580
1679
|
var today = new Date();
|
1581
|
-
if (angular.isDate(
|
1582
|
-
date = new Date(
|
1680
|
+
if (angular.isDate(scope.date)) {
|
1681
|
+
date = new Date(scope.date);
|
1583
1682
|
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
|
1584
1683
|
} else {
|
1585
1684
|
date = new Date(today.setHours(0, 0, 0, 0));
|
@@ -1627,13 +1726,13 @@ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepi
|
|
1627
1726
|
};
|
1628
1727
|
});
|
1629
1728
|
|
1630
|
-
angular.module('ui.bootstrap.dropdown', [])
|
1729
|
+
angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
1631
1730
|
|
1632
1731
|
.constant('dropdownConfig', {
|
1633
1732
|
openClass: 'open'
|
1634
1733
|
})
|
1635
1734
|
|
1636
|
-
.service('dropdownService', ['$document', function($document) {
|
1735
|
+
.service('dropdownService', ['$document', '$rootScope', function($document, $rootScope) {
|
1637
1736
|
var openScope = null;
|
1638
1737
|
|
1639
1738
|
this.open = function( dropdownScope ) {
|
@@ -1662,14 +1761,23 @@ angular.module('ui.bootstrap.dropdown', [])
|
|
1662
1761
|
// unbound this event handler. So check openScope before proceeding.
|
1663
1762
|
if (!openScope) { return; }
|
1664
1763
|
|
1764
|
+
if( evt && openScope.getAutoClose() === 'disabled' ) { return ; }
|
1765
|
+
|
1665
1766
|
var toggleElement = openScope.getToggleElement();
|
1666
1767
|
if ( evt && toggleElement && toggleElement[0].contains(evt.target) ) {
|
1667
1768
|
return;
|
1668
1769
|
}
|
1669
1770
|
|
1670
|
-
openScope
|
1671
|
-
|
1672
|
-
|
1771
|
+
var $element = openScope.getElement();
|
1772
|
+
if( evt && openScope.getAutoClose() === 'outsideClick' && $element && $element[0].contains(evt.target) ) {
|
1773
|
+
return;
|
1774
|
+
}
|
1775
|
+
|
1776
|
+
openScope.isOpen = false;
|
1777
|
+
|
1778
|
+
if (!$rootScope.$$phase) {
|
1779
|
+
openScope.$apply();
|
1780
|
+
}
|
1673
1781
|
};
|
1674
1782
|
|
1675
1783
|
var escapeKeyBind = function( evt ) {
|
@@ -1680,13 +1788,14 @@ angular.module('ui.bootstrap.dropdown', [])
|
|
1680
1788
|
};
|
1681
1789
|
}])
|
1682
1790
|
|
1683
|
-
.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) {
|
1791
|
+
.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document) {
|
1684
1792
|
var self = this,
|
1685
1793
|
scope = $scope.$new(), // create a child scope so we are not polluting original one
|
1686
1794
|
openClass = dropdownConfig.openClass,
|
1687
1795
|
getIsOpen,
|
1688
1796
|
setIsOpen = angular.noop,
|
1689
|
-
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop
|
1797
|
+
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
|
1798
|
+
appendToBody = false;
|
1690
1799
|
|
1691
1800
|
this.init = function( element ) {
|
1692
1801
|
self.$element = element;
|
@@ -1699,6 +1808,15 @@ angular.module('ui.bootstrap.dropdown', [])
|
|
1699
1808
|
scope.isOpen = !!value;
|
1700
1809
|
});
|
1701
1810
|
}
|
1811
|
+
|
1812
|
+
appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
|
1813
|
+
|
1814
|
+
if ( appendToBody && self.dropdownMenu ) {
|
1815
|
+
$document.find('body').append( self.dropdownMenu );
|
1816
|
+
element.on('$destroy', function handleDestroyEvent() {
|
1817
|
+
self.dropdownMenu.remove();
|
1818
|
+
});
|
1819
|
+
}
|
1702
1820
|
};
|
1703
1821
|
|
1704
1822
|
this.toggle = function( open ) {
|
@@ -1714,6 +1832,14 @@ angular.module('ui.bootstrap.dropdown', [])
|
|
1714
1832
|
return self.toggleElement;
|
1715
1833
|
};
|
1716
1834
|
|
1835
|
+
scope.getAutoClose = function() {
|
1836
|
+
return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
|
1837
|
+
};
|
1838
|
+
|
1839
|
+
scope.getElement = function() {
|
1840
|
+
return self.$element;
|
1841
|
+
};
|
1842
|
+
|
1717
1843
|
scope.focusToggleElement = function() {
|
1718
1844
|
if ( self.toggleElement ) {
|
1719
1845
|
self.toggleElement[0].focus();
|
@@ -1721,6 +1847,15 @@ angular.module('ui.bootstrap.dropdown', [])
|
|
1721
1847
|
};
|
1722
1848
|
|
1723
1849
|
scope.$watch('isOpen', function( isOpen, wasOpen ) {
|
1850
|
+
if ( appendToBody && self.dropdownMenu ) {
|
1851
|
+
var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true);
|
1852
|
+
self.dropdownMenu.css({
|
1853
|
+
top: pos.top + 'px',
|
1854
|
+
left: pos.left + 'px',
|
1855
|
+
display: isOpen ? 'block' : 'none'
|
1856
|
+
});
|
1857
|
+
}
|
1858
|
+
|
1724
1859
|
$animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);
|
1725
1860
|
|
1726
1861
|
if ( isOpen ) {
|
@@ -1754,6 +1889,19 @@ angular.module('ui.bootstrap.dropdown', [])
|
|
1754
1889
|
};
|
1755
1890
|
})
|
1756
1891
|
|
1892
|
+
.directive('dropdownMenu', function() {
|
1893
|
+
return {
|
1894
|
+
restrict: 'AC',
|
1895
|
+
require: '?^dropdown',
|
1896
|
+
link: function(scope, element, attrs, dropdownCtrl) {
|
1897
|
+
if ( !dropdownCtrl ) {
|
1898
|
+
return;
|
1899
|
+
}
|
1900
|
+
dropdownCtrl.dropdownMenu = element;
|
1901
|
+
}
|
1902
|
+
};
|
1903
|
+
})
|
1904
|
+
|
1757
1905
|
.directive('dropdownToggle', function() {
|
1758
1906
|
return {
|
1759
1907
|
require: '?^dropdown',
|
@@ -1789,7 +1937,7 @@ angular.module('ui.bootstrap.dropdown', [])
|
|
1789
1937
|
};
|
1790
1938
|
});
|
1791
1939
|
|
1792
|
-
angular.module('ui.bootstrap.modal', [
|
1940
|
+
angular.module('ui.bootstrap.modal', [])
|
1793
1941
|
|
1794
1942
|
/**
|
1795
1943
|
* A helper, internal data structure that acts as a map but also allows getting / removing
|
@@ -1853,20 +2001,23 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
1853
2001
|
restrict: 'EA',
|
1854
2002
|
replace: true,
|
1855
2003
|
templateUrl: 'template/modal/backdrop.html',
|
1856
|
-
|
1857
|
-
|
1858
|
-
|
1859
|
-
scope.animate = false;
|
1860
|
-
|
1861
|
-
//trigger CSS transitions
|
1862
|
-
$timeout(function () {
|
1863
|
-
scope.animate = true;
|
1864
|
-
});
|
2004
|
+
compile: function (tElement, tAttrs) {
|
2005
|
+
tElement.addClass(tAttrs.backdropClass);
|
2006
|
+
return linkFn;
|
1865
2007
|
}
|
1866
2008
|
};
|
2009
|
+
|
2010
|
+
function linkFn(scope, element, attrs) {
|
2011
|
+
scope.animate = false;
|
2012
|
+
|
2013
|
+
//trigger CSS transitions
|
2014
|
+
$timeout(function () {
|
2015
|
+
scope.animate = true;
|
2016
|
+
});
|
2017
|
+
}
|
1867
2018
|
}])
|
1868
2019
|
|
1869
|
-
.directive('modalWindow', ['$modalStack', '$
|
2020
|
+
.directive('modalWindow', ['$modalStack', '$q', function ($modalStack, $q) {
|
1870
2021
|
return {
|
1871
2022
|
restrict: 'EA',
|
1872
2023
|
scope: {
|
@@ -1882,10 +2033,35 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
1882
2033
|
element.addClass(attrs.windowClass || '');
|
1883
2034
|
scope.size = attrs.size;
|
1884
2035
|
|
1885
|
-
|
2036
|
+
scope.close = function (evt) {
|
2037
|
+
var modal = $modalStack.getTop();
|
2038
|
+
if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
|
2039
|
+
evt.preventDefault();
|
2040
|
+
evt.stopPropagation();
|
2041
|
+
$modalStack.dismiss(modal.key, 'backdrop click');
|
2042
|
+
}
|
2043
|
+
};
|
2044
|
+
|
2045
|
+
// This property is only added to the scope for the purpose of detecting when this directive is rendered.
|
2046
|
+
// We can detect that by using this property in the template associated with this directive and then use
|
2047
|
+
// {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
|
2048
|
+
scope.$isRendered = true;
|
2049
|
+
|
2050
|
+
// Deferred object that will be resolved when this modal is render.
|
2051
|
+
var modalRenderDeferObj = $q.defer();
|
2052
|
+
// Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
|
2053
|
+
// In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
|
2054
|
+
attrs.$observe('modalRender', function (value) {
|
2055
|
+
if (value == 'true') {
|
2056
|
+
modalRenderDeferObj.resolve();
|
2057
|
+
}
|
2058
|
+
});
|
2059
|
+
|
2060
|
+
modalRenderDeferObj.promise.then(function () {
|
1886
2061
|
// trigger CSS transitions
|
1887
2062
|
scope.animate = true;
|
1888
2063
|
|
2064
|
+
var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]');
|
1889
2065
|
/**
|
1890
2066
|
* Auto-focusing of a freshly-opened modal element causes any child elements
|
1891
2067
|
* with the autofocus attribute to lose focus. This is an issue on touch
|
@@ -1894,23 +2070,33 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
1894
2070
|
* the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
|
1895
2071
|
* the modal element if the modal does not contain an autofocus element.
|
1896
2072
|
*/
|
1897
|
-
if (
|
2073
|
+
if (inputsWithAutofocus.length) {
|
2074
|
+
inputsWithAutofocus[0].focus();
|
2075
|
+
} else {
|
1898
2076
|
element[0].focus();
|
1899
2077
|
}
|
1900
|
-
});
|
1901
2078
|
|
1902
|
-
|
2079
|
+
// Notify {@link $modalStack} that modal is rendered.
|
1903
2080
|
var modal = $modalStack.getTop();
|
1904
|
-
if (modal
|
1905
|
-
|
1906
|
-
evt.stopPropagation();
|
1907
|
-
$modalStack.dismiss(modal.key, 'backdrop click');
|
2081
|
+
if (modal) {
|
2082
|
+
$modalStack.modalRendered(modal.key);
|
1908
2083
|
}
|
1909
|
-
};
|
2084
|
+
});
|
1910
2085
|
}
|
1911
2086
|
};
|
1912
2087
|
}])
|
1913
2088
|
|
2089
|
+
.directive('modalAnimationClass', [
|
2090
|
+
function () {
|
2091
|
+
return {
|
2092
|
+
compile: function (tElement, tAttrs) {
|
2093
|
+
if (tAttrs.modalAnimation) {
|
2094
|
+
tElement.addClass(tAttrs.modalAnimationClass);
|
2095
|
+
}
|
2096
|
+
}
|
2097
|
+
};
|
2098
|
+
}])
|
2099
|
+
|
1914
2100
|
.directive('modalTransclude', function () {
|
1915
2101
|
return {
|
1916
2102
|
link: function($scope, $element, $attrs, controller, $transclude) {
|
@@ -1922,8 +2108,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
1922
2108
|
};
|
1923
2109
|
})
|
1924
2110
|
|
1925
|
-
.factory('$modalStack', ['$
|
1926
|
-
function ($
|
2111
|
+
.factory('$modalStack', ['$animate', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
|
2112
|
+
function ($animate, $timeout, $document, $compile, $rootScope, $$stackedMap) {
|
1927
2113
|
|
1928
2114
|
var OPENED_MODAL_CLASS = 'modal-open';
|
1929
2115
|
|
@@ -1957,8 +2143,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
1957
2143
|
openedWindows.remove(modalInstance);
|
1958
2144
|
|
1959
2145
|
//remove window DOM element
|
1960
|
-
removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope,
|
1961
|
-
modalWindow.modalScope.$destroy();
|
2146
|
+
removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
|
1962
2147
|
body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
|
1963
2148
|
checkRemoveBackdrop();
|
1964
2149
|
});
|
@@ -1968,8 +2153,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
1968
2153
|
//remove backdrop if no longer needed
|
1969
2154
|
if (backdropDomEl && backdropIndex() == -1) {
|
1970
2155
|
var backdropScopeRef = backdropScope;
|
1971
|
-
removeAfterAnimate(backdropDomEl, backdropScope,
|
1972
|
-
backdropScopeRef.$destroy();
|
2156
|
+
removeAfterAnimate(backdropDomEl, backdropScope, function () {
|
1973
2157
|
backdropScopeRef = null;
|
1974
2158
|
});
|
1975
2159
|
backdropDomEl = undefined;
|
@@ -1977,19 +2161,14 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
1977
2161
|
}
|
1978
2162
|
}
|
1979
2163
|
|
1980
|
-
function removeAfterAnimate(domEl, scope,
|
2164
|
+
function removeAfterAnimate(domEl, scope, done) {
|
1981
2165
|
// Closing animation
|
1982
2166
|
scope.animate = false;
|
1983
2167
|
|
1984
|
-
|
1985
|
-
if (transitionEndEventName) {
|
2168
|
+
if (domEl.attr('modal-animation') && $animate.enabled()) {
|
1986
2169
|
// transition out
|
1987
|
-
|
1988
|
-
|
1989
|
-
domEl.bind(transitionEndEventName, function () {
|
1990
|
-
$timeout.cancel(timeout);
|
1991
|
-
afterAnimating();
|
1992
|
-
scope.$apply();
|
2170
|
+
domEl.one('$animate:close', function closeFn() {
|
2171
|
+
$rootScope.$evalAsync(afterAnimating);
|
1993
2172
|
});
|
1994
2173
|
} else {
|
1995
2174
|
// Ensure this call is async
|
@@ -2003,6 +2182,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2003
2182
|
afterAnimating.done = true;
|
2004
2183
|
|
2005
2184
|
domEl.remove();
|
2185
|
+
scope.$destroy();
|
2006
2186
|
if (done) {
|
2007
2187
|
done();
|
2008
2188
|
}
|
@@ -2025,8 +2205,11 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2025
2205
|
|
2026
2206
|
$modalStack.open = function (modalInstance, modal) {
|
2027
2207
|
|
2208
|
+
var modalOpener = $document[0].activeElement;
|
2209
|
+
|
2028
2210
|
openedWindows.add(modalInstance, {
|
2029
2211
|
deferred: modal.deferred,
|
2212
|
+
renderDeferred: modal.renderDeferred,
|
2030
2213
|
modalScope: modal.scope,
|
2031
2214
|
backdrop: modal.backdrop,
|
2032
2215
|
keyboard: modal.keyboard
|
@@ -2038,13 +2221,16 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2038
2221
|
if (currBackdropIndex >= 0 && !backdropDomEl) {
|
2039
2222
|
backdropScope = $rootScope.$new(true);
|
2040
2223
|
backdropScope.index = currBackdropIndex;
|
2041
|
-
var angularBackgroundDomEl = angular.element('<div modal-backdrop></div>');
|
2224
|
+
var angularBackgroundDomEl = angular.element('<div modal-backdrop="modal-backdrop"></div>');
|
2042
2225
|
angularBackgroundDomEl.attr('backdrop-class', modal.backdropClass);
|
2226
|
+
if (modal.animation) {
|
2227
|
+
angularBackgroundDomEl.attr('modal-animation', 'true');
|
2228
|
+
}
|
2043
2229
|
backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope);
|
2044
2230
|
body.append(backdropDomEl);
|
2045
2231
|
}
|
2046
2232
|
|
2047
|
-
var angularDomEl = angular.element('<div modal-window></div>');
|
2233
|
+
var angularDomEl = angular.element('<div modal-window="modal-window"></div>');
|
2048
2234
|
angularDomEl.attr({
|
2049
2235
|
'template-url': modal.windowTemplateUrl,
|
2050
2236
|
'window-class': modal.windowClass,
|
@@ -2052,33 +2238,46 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2052
2238
|
'index': openedWindows.length() - 1,
|
2053
2239
|
'animate': 'animate'
|
2054
2240
|
}).html(modal.content);
|
2241
|
+
if (modal.animation) {
|
2242
|
+
angularDomEl.attr('modal-animation', 'true');
|
2243
|
+
}
|
2055
2244
|
|
2056
2245
|
var modalDomEl = $compile(angularDomEl)(modal.scope);
|
2057
2246
|
openedWindows.top().value.modalDomEl = modalDomEl;
|
2247
|
+
openedWindows.top().value.modalOpener = modalOpener;
|
2058
2248
|
body.append(modalDomEl);
|
2059
2249
|
body.addClass(OPENED_MODAL_CLASS);
|
2060
2250
|
};
|
2061
2251
|
|
2252
|
+
function broadcastClosing(modalWindow, resultOrReason, closing) {
|
2253
|
+
return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
|
2254
|
+
}
|
2255
|
+
|
2062
2256
|
$modalStack.close = function (modalInstance, result) {
|
2063
2257
|
var modalWindow = openedWindows.get(modalInstance);
|
2064
|
-
if (modalWindow) {
|
2258
|
+
if (modalWindow && broadcastClosing(modalWindow, result, true)) {
|
2065
2259
|
modalWindow.value.deferred.resolve(result);
|
2066
2260
|
removeModalWindow(modalInstance);
|
2261
|
+
modalWindow.value.modalOpener.focus();
|
2262
|
+
return true;
|
2067
2263
|
}
|
2264
|
+
return !modalWindow;
|
2068
2265
|
};
|
2069
2266
|
|
2070
2267
|
$modalStack.dismiss = function (modalInstance, reason) {
|
2071
2268
|
var modalWindow = openedWindows.get(modalInstance);
|
2072
|
-
if (modalWindow) {
|
2269
|
+
if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
|
2073
2270
|
modalWindow.value.deferred.reject(reason);
|
2074
2271
|
removeModalWindow(modalInstance);
|
2272
|
+
modalWindow.value.modalOpener.focus();
|
2273
|
+
return true;
|
2075
2274
|
}
|
2275
|
+
return !modalWindow;
|
2076
2276
|
};
|
2077
2277
|
|
2078
2278
|
$modalStack.dismissAll = function (reason) {
|
2079
2279
|
var topModal = this.getTop();
|
2080
|
-
while (topModal) {
|
2081
|
-
this.dismiss(topModal.key, reason);
|
2280
|
+
while (topModal && this.dismiss(topModal.key, reason)) {
|
2082
2281
|
topModal = this.getTop();
|
2083
2282
|
}
|
2084
2283
|
};
|
@@ -2087,6 +2286,13 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2087
2286
|
return openedWindows.top();
|
2088
2287
|
};
|
2089
2288
|
|
2289
|
+
$modalStack.modalRendered = function (modalInstance) {
|
2290
|
+
var modalWindow = openedWindows.get(modalInstance);
|
2291
|
+
if (modalWindow) {
|
2292
|
+
modalWindow.value.renderDeferred.resolve();
|
2293
|
+
}
|
2294
|
+
};
|
2295
|
+
|
2090
2296
|
return $modalStack;
|
2091
2297
|
}])
|
2092
2298
|
|
@@ -2094,20 +2300,18 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2094
2300
|
|
2095
2301
|
var $modalProvider = {
|
2096
2302
|
options: {
|
2097
|
-
|
2303
|
+
animation: true,
|
2304
|
+
backdrop: true, //can also be false or 'static'
|
2098
2305
|
keyboard: true
|
2099
2306
|
},
|
2100
|
-
$get: ['$injector', '$rootScope', '$q', '$
|
2101
|
-
function ($injector, $rootScope, $q, $
|
2307
|
+
$get: ['$injector', '$rootScope', '$q', '$templateRequest', '$controller', '$modalStack',
|
2308
|
+
function ($injector, $rootScope, $q, $templateRequest, $controller, $modalStack) {
|
2102
2309
|
|
2103
2310
|
var $modal = {};
|
2104
2311
|
|
2105
2312
|
function getTemplatePromise(options) {
|
2106
2313
|
return options.template ? $q.when(options.template) :
|
2107
|
-
$
|
2108
|
-
{cache: $templateCache}).then(function (result) {
|
2109
|
-
return result.data;
|
2110
|
-
});
|
2314
|
+
$templateRequest(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl);
|
2111
2315
|
}
|
2112
2316
|
|
2113
2317
|
function getResolvePromises(resolves) {
|
@@ -2124,16 +2328,18 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2124
2328
|
|
2125
2329
|
var modalResultDeferred = $q.defer();
|
2126
2330
|
var modalOpenedDeferred = $q.defer();
|
2331
|
+
var modalRenderDeferred = $q.defer();
|
2127
2332
|
|
2128
2333
|
//prepare an instance of a modal to be injected into controllers and returned to a caller
|
2129
2334
|
var modalInstance = {
|
2130
2335
|
result: modalResultDeferred.promise,
|
2131
2336
|
opened: modalOpenedDeferred.promise,
|
2337
|
+
rendered: modalRenderDeferred.promise,
|
2132
2338
|
close: function (result) {
|
2133
|
-
$modalStack.close(modalInstance, result);
|
2339
|
+
return $modalStack.close(modalInstance, result);
|
2134
2340
|
},
|
2135
2341
|
dismiss: function (reason) {
|
2136
|
-
$modalStack.dismiss(modalInstance, reason);
|
2342
|
+
return $modalStack.dismiss(modalInstance, reason);
|
2137
2343
|
}
|
2138
2344
|
};
|
2139
2345
|
|
@@ -2176,7 +2382,9 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2176
2382
|
$modalStack.open(modalInstance, {
|
2177
2383
|
scope: modalScope,
|
2178
2384
|
deferred: modalResultDeferred,
|
2385
|
+
renderDeferred: modalRenderDeferred,
|
2179
2386
|
content: tplAndVars[0],
|
2387
|
+
animation: modalOptions.animation,
|
2180
2388
|
backdrop: modalOptions.backdrop,
|
2181
2389
|
keyboard: modalOptions.keyboard,
|
2182
2390
|
backdropClass: modalOptions.backdropClass,
|
@@ -2191,8 +2399,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
|
|
2191
2399
|
|
2192
2400
|
templateAndResolvePromise.then(function () {
|
2193
2401
|
modalOpenedDeferred.resolve(true);
|
2194
|
-
}, function () {
|
2195
|
-
modalOpenedDeferred.reject(
|
2402
|
+
}, function (reason) {
|
2403
|
+
modalOpenedDeferred.reject(reason);
|
2196
2404
|
});
|
2197
2405
|
|
2198
2406
|
return modalInstance;
|
@@ -2228,6 +2436,20 @@ angular.module('ui.bootstrap.pagination', [])
|
|
2228
2436
|
} else {
|
2229
2437
|
this.itemsPerPage = config.itemsPerPage;
|
2230
2438
|
}
|
2439
|
+
|
2440
|
+
$scope.$watch('totalItems', function() {
|
2441
|
+
$scope.totalPages = self.calculateTotalPages();
|
2442
|
+
});
|
2443
|
+
|
2444
|
+
$scope.$watch('totalPages', function(value) {
|
2445
|
+
setNumPages($scope.$parent, value); // Readonly variable
|
2446
|
+
|
2447
|
+
if ( $scope.page > value ) {
|
2448
|
+
$scope.selectPage(value);
|
2449
|
+
} else {
|
2450
|
+
ngModelCtrl.$render();
|
2451
|
+
}
|
2452
|
+
});
|
2231
2453
|
};
|
2232
2454
|
|
2233
2455
|
this.calculateTotalPages = function() {
|
@@ -2239,8 +2461,11 @@ angular.module('ui.bootstrap.pagination', [])
|
|
2239
2461
|
$scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1;
|
2240
2462
|
};
|
2241
2463
|
|
2242
|
-
$scope.selectPage = function(page) {
|
2464
|
+
$scope.selectPage = function(page, evt) {
|
2243
2465
|
if ( $scope.page !== page && page > 0 && page <= $scope.totalPages) {
|
2466
|
+
if (evt && evt.target) {
|
2467
|
+
evt.target.blur();
|
2468
|
+
}
|
2244
2469
|
ngModelCtrl.$setViewValue(page);
|
2245
2470
|
ngModelCtrl.$render();
|
2246
2471
|
}
|
@@ -2255,20 +2480,6 @@ angular.module('ui.bootstrap.pagination', [])
|
|
2255
2480
|
$scope.noNext = function() {
|
2256
2481
|
return $scope.page === $scope.totalPages;
|
2257
2482
|
};
|
2258
|
-
|
2259
|
-
$scope.$watch('totalItems', function() {
|
2260
|
-
$scope.totalPages = self.calculateTotalPages();
|
2261
|
-
});
|
2262
|
-
|
2263
|
-
$scope.$watch('totalPages', function(value) {
|
2264
|
-
setNumPages($scope.$parent, value); // Readonly variable
|
2265
|
-
|
2266
|
-
if ( $scope.page > value ) {
|
2267
|
-
$scope.selectPage(value);
|
2268
|
-
} else {
|
2269
|
-
ngModelCtrl.$render();
|
2270
|
-
}
|
2271
|
-
});
|
2272
2483
|
}])
|
2273
2484
|
|
2274
2485
|
.constant('paginationConfig', {
|
@@ -2436,7 +2647,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2436
2647
|
var defaultOptions = {
|
2437
2648
|
placement: 'top',
|
2438
2649
|
animation: true,
|
2439
|
-
popupDelay: 0
|
2650
|
+
popupDelay: 0,
|
2651
|
+
useContentExp: false
|
2440
2652
|
};
|
2441
2653
|
|
2442
2654
|
// Default hide triggers for each show trigger
|
@@ -2487,8 +2699,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2487
2699
|
* TODO support multiple triggers
|
2488
2700
|
*/
|
2489
2701
|
this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $document, $position, $interpolate ) {
|
2490
|
-
return function $tooltip ( type, prefix, defaultTriggerShow ) {
|
2491
|
-
|
2702
|
+
return function $tooltip ( type, prefix, defaultTriggerShow, options ) {
|
2703
|
+
options = angular.extend( {}, defaultOptions, globalOptions, options );
|
2492
2704
|
|
2493
2705
|
/**
|
2494
2706
|
* Returns an object of show and hide triggers.
|
@@ -2520,10 +2732,14 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2520
2732
|
var template =
|
2521
2733
|
'<div '+ directiveName +'-popup '+
|
2522
2734
|
'title="'+startSym+'title'+endSym+'" '+
|
2523
|
-
|
2735
|
+
(options.useContentExp ?
|
2736
|
+
'content-exp="contentExp()" ' :
|
2737
|
+
'content="'+startSym+'content'+endSym+'" ') +
|
2524
2738
|
'placement="'+startSym+'placement'+endSym+'" '+
|
2739
|
+
'popup-class="'+startSym+'popupClass'+endSym+'" '+
|
2525
2740
|
'animation="animation" '+
|
2526
2741
|
'is-open="isOpen"'+
|
2742
|
+
'origin-scope="origScope" '+
|
2527
2743
|
'>'+
|
2528
2744
|
'</div>';
|
2529
2745
|
|
@@ -2532,7 +2748,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2532
2748
|
compile: function (tElem, tAttrs) {
|
2533
2749
|
var tooltipLinker = $compile( template );
|
2534
2750
|
|
2535
|
-
return function link ( scope, element, attrs ) {
|
2751
|
+
return function link ( scope, element, attrs, tooltipCtrl ) {
|
2536
2752
|
var tooltip;
|
2537
2753
|
var tooltipLinkedScope;
|
2538
2754
|
var transitionTimeout;
|
@@ -2543,6 +2759,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2543
2759
|
var ttScope = scope.$new(true);
|
2544
2760
|
|
2545
2761
|
var positionTooltip = function () {
|
2762
|
+
if (!tooltip) { return; }
|
2546
2763
|
|
2547
2764
|
var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
|
2548
2765
|
ttPosition.top += 'px';
|
@@ -2552,6 +2769,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2552
2769
|
tooltip.css( ttPosition );
|
2553
2770
|
};
|
2554
2771
|
|
2772
|
+
// Set up the correct scope to allow transclusion later
|
2773
|
+
ttScope.origScope = scope;
|
2774
|
+
|
2555
2775
|
// By default, the tooltip is not open.
|
2556
2776
|
// TODO add ability to start tooltip opened
|
2557
2777
|
ttScope.isOpen = false;
|
@@ -2603,7 +2823,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2603
2823
|
}
|
2604
2824
|
|
2605
2825
|
// Don't show empty tooltips.
|
2606
|
-
if ( ! ttScope.content ) {
|
2826
|
+
if ( !(options.useContentExp ? ttScope.contentExp() : ttScope.content) ) {
|
2607
2827
|
return angular.noop;
|
2608
2828
|
}
|
2609
2829
|
|
@@ -2617,7 +2837,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2617
2837
|
|
2618
2838
|
// And show the tooltip.
|
2619
2839
|
ttScope.isOpen = true;
|
2620
|
-
ttScope.$
|
2840
|
+
ttScope.$apply(); // digest required as $apply is not called
|
2621
2841
|
|
2622
2842
|
// Return positioning function as promise callback for correct
|
2623
2843
|
// positioning after draw.
|
@@ -2658,6 +2878,18 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2658
2878
|
element.after( tooltip );
|
2659
2879
|
}
|
2660
2880
|
});
|
2881
|
+
|
2882
|
+
tooltipLinkedScope.$watch(function () {
|
2883
|
+
$timeout(positionTooltip, 0, false);
|
2884
|
+
});
|
2885
|
+
|
2886
|
+
if (options.useContentExp) {
|
2887
|
+
tooltipLinkedScope.$watch('contentExp()', function (val) {
|
2888
|
+
if (!val && ttScope.isOpen ) {
|
2889
|
+
hide();
|
2890
|
+
}
|
2891
|
+
});
|
2892
|
+
}
|
2661
2893
|
}
|
2662
2894
|
|
2663
2895
|
function removeTooltip() {
|
@@ -2673,17 +2905,30 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2673
2905
|
}
|
2674
2906
|
|
2675
2907
|
function prepareTooltip() {
|
2908
|
+
prepPopupClass();
|
2676
2909
|
prepPlacement();
|
2677
2910
|
prepPopupDelay();
|
2678
2911
|
}
|
2679
2912
|
|
2913
|
+
ttScope.contentExp = function () {
|
2914
|
+
return scope.$eval(attrs[type]);
|
2915
|
+
};
|
2916
|
+
|
2680
2917
|
/**
|
2681
2918
|
* Observe the relevant attributes.
|
2682
2919
|
*/
|
2683
|
-
|
2684
|
-
|
2920
|
+
if (!options.useContentExp) {
|
2921
|
+
attrs.$observe( type, function ( val ) {
|
2922
|
+
ttScope.content = val;
|
2923
|
+
|
2924
|
+
if (!val && ttScope.isOpen ) {
|
2925
|
+
hide();
|
2926
|
+
}
|
2927
|
+
});
|
2928
|
+
}
|
2685
2929
|
|
2686
|
-
|
2930
|
+
attrs.$observe( 'disabled', function ( val ) {
|
2931
|
+
if (val && ttScope.isOpen ) {
|
2687
2932
|
hide();
|
2688
2933
|
}
|
2689
2934
|
});
|
@@ -2692,6 +2937,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2692
2937
|
ttScope.title = val;
|
2693
2938
|
});
|
2694
2939
|
|
2940
|
+
function prepPopupClass() {
|
2941
|
+
ttScope.popupClass = attrs[prefix + 'Class'];
|
2942
|
+
}
|
2943
|
+
|
2695
2944
|
function prepPlacement() {
|
2696
2945
|
var val = attrs[ prefix + 'Placement' ];
|
2697
2946
|
ttScope.placement = angular.isDefined( val ) ? val : options.placement;
|
@@ -2755,11 +3004,101 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2755
3004
|
}];
|
2756
3005
|
})
|
2757
3006
|
|
3007
|
+
// This is mostly ngInclude code but with a custom scope
|
3008
|
+
.directive( 'tooltipTemplateTransclude', [
|
3009
|
+
'$animate', '$sce', '$compile', '$templateRequest',
|
3010
|
+
function ($animate , $sce , $compile , $templateRequest) {
|
3011
|
+
return {
|
3012
|
+
link: function ( scope, elem, attrs ) {
|
3013
|
+
var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
|
3014
|
+
|
3015
|
+
var changeCounter = 0,
|
3016
|
+
currentScope,
|
3017
|
+
previousElement,
|
3018
|
+
currentElement;
|
3019
|
+
|
3020
|
+
var cleanupLastIncludeContent = function() {
|
3021
|
+
if (previousElement) {
|
3022
|
+
previousElement.remove();
|
3023
|
+
previousElement = null;
|
3024
|
+
}
|
3025
|
+
if (currentScope) {
|
3026
|
+
currentScope.$destroy();
|
3027
|
+
currentScope = null;
|
3028
|
+
}
|
3029
|
+
if (currentElement) {
|
3030
|
+
$animate.leave(currentElement).then(function() {
|
3031
|
+
previousElement = null;
|
3032
|
+
});
|
3033
|
+
previousElement = currentElement;
|
3034
|
+
currentElement = null;
|
3035
|
+
}
|
3036
|
+
};
|
3037
|
+
|
3038
|
+
scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function (src) {
|
3039
|
+
var thisChangeId = ++changeCounter;
|
3040
|
+
|
3041
|
+
if (src) {
|
3042
|
+
//set the 2nd param to true to ignore the template request error so that the inner
|
3043
|
+
//contents and scope can be cleaned up.
|
3044
|
+
$templateRequest(src, true).then(function(response) {
|
3045
|
+
if (thisChangeId !== changeCounter) { return; }
|
3046
|
+
var newScope = origScope.$new();
|
3047
|
+
var template = response;
|
3048
|
+
|
3049
|
+
var clone = $compile(template)(newScope, function(clone) {
|
3050
|
+
cleanupLastIncludeContent();
|
3051
|
+
$animate.enter(clone, elem);
|
3052
|
+
});
|
3053
|
+
|
3054
|
+
currentScope = newScope;
|
3055
|
+
currentElement = clone;
|
3056
|
+
|
3057
|
+
currentScope.$emit('$includeContentLoaded', src);
|
3058
|
+
}, function() {
|
3059
|
+
if (thisChangeId === changeCounter) {
|
3060
|
+
cleanupLastIncludeContent();
|
3061
|
+
scope.$emit('$includeContentError', src);
|
3062
|
+
}
|
3063
|
+
});
|
3064
|
+
scope.$emit('$includeContentRequested', src);
|
3065
|
+
} else {
|
3066
|
+
cleanupLastIncludeContent();
|
3067
|
+
}
|
3068
|
+
});
|
3069
|
+
|
3070
|
+
scope.$on('$destroy', cleanupLastIncludeContent);
|
3071
|
+
}
|
3072
|
+
};
|
3073
|
+
}])
|
3074
|
+
|
3075
|
+
/**
|
3076
|
+
* Note that it's intentional that these classes are *not* applied through $animate.
|
3077
|
+
* They must not be animated as they're expected to be present on the tooltip on
|
3078
|
+
* initialization.
|
3079
|
+
*/
|
3080
|
+
.directive('tooltipClasses', function () {
|
3081
|
+
return {
|
3082
|
+
restrict: 'A',
|
3083
|
+
link: function (scope, element, attrs) {
|
3084
|
+
if (scope.placement) {
|
3085
|
+
element.addClass(scope.placement);
|
3086
|
+
}
|
3087
|
+
if (scope.popupClass) {
|
3088
|
+
element.addClass(scope.popupClass);
|
3089
|
+
}
|
3090
|
+
if (scope.animation()) {
|
3091
|
+
element.addClass(attrs.tooltipAnimationClass);
|
3092
|
+
}
|
3093
|
+
}
|
3094
|
+
};
|
3095
|
+
})
|
3096
|
+
|
2758
3097
|
.directive( 'tooltipPopup', function () {
|
2759
3098
|
return {
|
2760
3099
|
restrict: 'EA',
|
2761
3100
|
replace: true,
|
2762
|
-
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
|
3101
|
+
scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
|
2763
3102
|
templateUrl: 'template/tooltip/tooltip-popup.html'
|
2764
3103
|
};
|
2765
3104
|
})
|
@@ -2768,16 +3107,56 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2768
3107
|
return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
|
2769
3108
|
}])
|
2770
3109
|
|
3110
|
+
.directive( 'tooltipTemplatePopup', function () {
|
3111
|
+
return {
|
3112
|
+
restrict: 'EA',
|
3113
|
+
replace: true,
|
3114
|
+
scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
|
3115
|
+
originScope: '&' },
|
3116
|
+
templateUrl: 'template/tooltip/tooltip-template-popup.html'
|
3117
|
+
};
|
3118
|
+
})
|
3119
|
+
|
3120
|
+
.directive( 'tooltipTemplate', [ '$tooltip', function ( $tooltip ) {
|
3121
|
+
return $tooltip('tooltipTemplate', 'tooltip', 'mouseenter', {
|
3122
|
+
useContentExp: true
|
3123
|
+
});
|
3124
|
+
}])
|
3125
|
+
|
3126
|
+
.directive( 'tooltipHtmlPopup', function () {
|
3127
|
+
return {
|
3128
|
+
restrict: 'EA',
|
3129
|
+
replace: true,
|
3130
|
+
scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
|
3131
|
+
templateUrl: 'template/tooltip/tooltip-html-popup.html'
|
3132
|
+
};
|
3133
|
+
})
|
3134
|
+
|
3135
|
+
.directive( 'tooltipHtml', [ '$tooltip', function ( $tooltip ) {
|
3136
|
+
return $tooltip('tooltipHtml', 'tooltip', 'mouseenter', {
|
3137
|
+
useContentExp: true
|
3138
|
+
});
|
3139
|
+
}])
|
3140
|
+
|
3141
|
+
/*
|
3142
|
+
Deprecated
|
3143
|
+
*/
|
2771
3144
|
.directive( 'tooltipHtmlUnsafePopup', function () {
|
2772
3145
|
return {
|
2773
3146
|
restrict: 'EA',
|
2774
3147
|
replace: true,
|
2775
|
-
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
|
3148
|
+
scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
|
2776
3149
|
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
|
2777
3150
|
};
|
2778
3151
|
})
|
2779
3152
|
|
2780
|
-
.
|
3153
|
+
.value('tooltipHtmlUnsafeSuppressDeprecated', false)
|
3154
|
+
.directive( 'tooltipHtmlUnsafe', [
|
3155
|
+
'$tooltip', 'tooltipHtmlUnsafeSuppressDeprecated', '$log',
|
3156
|
+
function ( $tooltip , tooltipHtmlUnsafeSuppressDeprecated , $log) {
|
3157
|
+
if (!tooltipHtmlUnsafeSuppressDeprecated) {
|
3158
|
+
$log.warn('tooltip-html-unsafe is now deprecated. Use tooltip-html or tooltip-template instead.');
|
3159
|
+
}
|
2781
3160
|
return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
|
2782
3161
|
}]);
|
2783
3162
|
|
@@ -2788,11 +3167,27 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
2788
3167
|
*/
|
2789
3168
|
angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
|
2790
3169
|
|
3170
|
+
.directive( 'popoverTemplatePopup', function () {
|
3171
|
+
return {
|
3172
|
+
restrict: 'EA',
|
3173
|
+
replace: true,
|
3174
|
+
scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
|
3175
|
+
originScope: '&' },
|
3176
|
+
templateUrl: 'template/popover/popover-template.html'
|
3177
|
+
};
|
3178
|
+
})
|
3179
|
+
|
3180
|
+
.directive( 'popoverTemplate', [ '$tooltip', function ( $tooltip ) {
|
3181
|
+
return $tooltip( 'popoverTemplate', 'popover', 'click', {
|
3182
|
+
useContentExp: true
|
3183
|
+
} );
|
3184
|
+
}])
|
3185
|
+
|
2791
3186
|
.directive( 'popoverPopup', function () {
|
2792
3187
|
return {
|
2793
3188
|
restrict: 'EA',
|
2794
3189
|
replace: true,
|
2795
|
-
scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
|
3190
|
+
scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
|
2796
3191
|
templateUrl: 'template/popover/popover.html'
|
2797
3192
|
};
|
2798
3193
|
})
|
@@ -2813,7 +3208,7 @@ angular.module('ui.bootstrap.progressbar', [])
|
|
2813
3208
|
animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
|
2814
3209
|
|
2815
3210
|
this.bars = [];
|
2816
|
-
$scope.max = angular.isDefined($
|
3211
|
+
$scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max;
|
2817
3212
|
|
2818
3213
|
this.addBar = function(bar, element) {
|
2819
3214
|
if ( !animate ) {
|
@@ -2857,6 +3252,7 @@ angular.module('ui.bootstrap.progressbar', [])
|
|
2857
3252
|
require: '^progress',
|
2858
3253
|
scope: {
|
2859
3254
|
value: '=',
|
3255
|
+
max: '=?',
|
2860
3256
|
type: '@'
|
2861
3257
|
},
|
2862
3258
|
templateUrl: 'template/progressbar/bar.html',
|
@@ -2874,6 +3270,7 @@ angular.module('ui.bootstrap.progressbar', [])
|
|
2874
3270
|
controller: 'ProgressController',
|
2875
3271
|
scope: {
|
2876
3272
|
value: '=',
|
3273
|
+
max: '=?',
|
2877
3274
|
type: '@'
|
2878
3275
|
},
|
2879
3276
|
templateUrl: 'template/progressbar/progressbar.html',
|
@@ -2882,6 +3279,7 @@ angular.module('ui.bootstrap.progressbar', [])
|
|
2882
3279
|
}
|
2883
3280
|
};
|
2884
3281
|
});
|
3282
|
+
|
2885
3283
|
angular.module('ui.bootstrap.rating', [])
|
2886
3284
|
|
2887
3285
|
.constant('ratingConfig', {
|
@@ -2897,6 +3295,13 @@ angular.module('ui.bootstrap.rating', [])
|
|
2897
3295
|
ngModelCtrl = ngModelCtrl_;
|
2898
3296
|
ngModelCtrl.$render = this.render;
|
2899
3297
|
|
3298
|
+
ngModelCtrl.$formatters.push(function(value) {
|
3299
|
+
if (angular.isNumber(value) && value << 0 !== value) {
|
3300
|
+
value = Math.round(value);
|
3301
|
+
}
|
3302
|
+
return value;
|
3303
|
+
});
|
3304
|
+
|
2900
3305
|
this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
|
2901
3306
|
this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
|
2902
3307
|
|
@@ -2958,10 +3363,7 @@ angular.module('ui.bootstrap.rating', [])
|
|
2958
3363
|
replace: true,
|
2959
3364
|
link: function(scope, element, attrs, ctrls) {
|
2960
3365
|
var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
2961
|
-
|
2962
|
-
if ( ngModelCtrl ) {
|
2963
|
-
ratingCtrl.init( ngModelCtrl );
|
2964
|
-
}
|
3366
|
+
ratingCtrl.init( ngModelCtrl );
|
2965
3367
|
}
|
2966
3368
|
};
|
2967
3369
|
});
|
@@ -2995,11 +3397,14 @@ angular.module('ui.bootstrap.tabs', [])
|
|
2995
3397
|
tabs.push(tab);
|
2996
3398
|
// we can't run the select function on the first tab
|
2997
3399
|
// since that would select it twice
|
2998
|
-
if (tabs.length === 1) {
|
3400
|
+
if (tabs.length === 1 && tab.active !== false) {
|
2999
3401
|
tab.active = true;
|
3000
3402
|
} else if (tab.active) {
|
3001
3403
|
ctrl.select(tab);
|
3002
3404
|
}
|
3405
|
+
else {
|
3406
|
+
tab.active = false;
|
3407
|
+
}
|
3003
3408
|
};
|
3004
3409
|
|
3005
3410
|
ctrl.removeTab = function removeTab(tab) {
|
@@ -3146,7 +3551,7 @@ angular.module('ui.bootstrap.tabs', [])
|
|
3146
3551
|
</file>
|
3147
3552
|
</example>
|
3148
3553
|
*/
|
3149
|
-
.directive('tab', ['$parse', function($parse) {
|
3554
|
+
.directive('tab', ['$parse', '$log', function($parse, $log) {
|
3150
3555
|
return {
|
3151
3556
|
require: '^tabset',
|
3152
3557
|
restrict: 'EA',
|
@@ -3172,7 +3577,18 @@ angular.module('ui.bootstrap.tabs', [])
|
|
3172
3577
|
});
|
3173
3578
|
|
3174
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
|
3175
3590
|
if ( attrs.disabled ) {
|
3591
|
+
$log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');
|
3176
3592
|
scope.$parent.$watch($parse(attrs.disabled), function(value) {
|
3177
3593
|
scope.disabled = !! value;
|
3178
3594
|
});
|
@@ -3253,7 +3669,8 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
3253
3669
|
showMeridian: true,
|
3254
3670
|
meridians: null,
|
3255
3671
|
readonlyInput: false,
|
3256
|
-
mousewheel: true
|
3672
|
+
mousewheel: true,
|
3673
|
+
arrowkeys: true
|
3257
3674
|
})
|
3258
3675
|
|
3259
3676
|
.controller('TimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'timepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {
|
@@ -3265,6 +3682,10 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
3265
3682
|
ngModelCtrl = ngModelCtrl_;
|
3266
3683
|
ngModelCtrl.$render = this.render;
|
3267
3684
|
|
3685
|
+
ngModelCtrl.$formatters.unshift(function (modelValue) {
|
3686
|
+
return modelValue ? new Date( modelValue ) : null;
|
3687
|
+
});
|
3688
|
+
|
3268
3689
|
var hoursInputEl = inputs.eq(0),
|
3269
3690
|
minutesInputEl = inputs.eq(1);
|
3270
3691
|
|
@@ -3273,6 +3694,11 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
3273
3694
|
this.setupMousewheelEvents( hoursInputEl, minutesInputEl );
|
3274
3695
|
}
|
3275
3696
|
|
3697
|
+
var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
|
3698
|
+
if (arrowkeys) {
|
3699
|
+
this.setupArrowkeyEvents( hoursInputEl, minutesInputEl );
|
3700
|
+
}
|
3701
|
+
|
3276
3702
|
$scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
|
3277
3703
|
this.setupInputEvents( hoursInputEl, minutesInputEl );
|
3278
3704
|
};
|
@@ -3335,7 +3761,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
3335
3761
|
}
|
3336
3762
|
|
3337
3763
|
function pad( value ) {
|
3338
|
-
return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
|
3764
|
+
return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value.toString();
|
3339
3765
|
}
|
3340
3766
|
|
3341
3767
|
// Respond on mousewheel spin
|
@@ -3361,6 +3787,35 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
3361
3787
|
|
3362
3788
|
};
|
3363
3789
|
|
3790
|
+
// Respond on up/down arrowkeys
|
3791
|
+
this.setupArrowkeyEvents = function( hoursInputEl, minutesInputEl ) {
|
3792
|
+
hoursInputEl.bind('keydown', function(e) {
|
3793
|
+
if ( e.which === 38 ) { // up
|
3794
|
+
e.preventDefault();
|
3795
|
+
$scope.incrementHours();
|
3796
|
+
$scope.$apply();
|
3797
|
+
}
|
3798
|
+
else if ( e.which === 40 ) { // down
|
3799
|
+
e.preventDefault();
|
3800
|
+
$scope.decrementHours();
|
3801
|
+
$scope.$apply();
|
3802
|
+
}
|
3803
|
+
});
|
3804
|
+
|
3805
|
+
minutesInputEl.bind('keydown', function(e) {
|
3806
|
+
if ( e.which === 38 ) { // up
|
3807
|
+
e.preventDefault();
|
3808
|
+
$scope.incrementMinutes();
|
3809
|
+
$scope.$apply();
|
3810
|
+
}
|
3811
|
+
else if ( e.which === 40 ) { // down
|
3812
|
+
e.preventDefault();
|
3813
|
+
$scope.decrementMinutes();
|
3814
|
+
$scope.$apply();
|
3815
|
+
}
|
3816
|
+
});
|
3817
|
+
};
|
3818
|
+
|
3364
3819
|
this.setupInputEvents = function( hoursInputEl, minutesInputEl ) {
|
3365
3820
|
if ( $scope.readonlyInput ) {
|
3366
3821
|
$scope.updateHours = angular.noop;
|
@@ -3420,7 +3875,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
3420
3875
|
};
|
3421
3876
|
|
3422
3877
|
this.render = function() {
|
3423
|
-
var date = ngModelCtrl.$
|
3878
|
+
var date = ngModelCtrl.$viewValue;
|
3424
3879
|
|
3425
3880
|
if ( isNaN(date) ) {
|
3426
3881
|
ngModelCtrl.$setValidity('time', false);
|
@@ -3455,7 +3910,9 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
3455
3910
|
}
|
3456
3911
|
|
3457
3912
|
$scope.hours = keyboardChange === 'h' ? hours : pad(hours);
|
3458
|
-
|
3913
|
+
if (keyboardChange !== 'm') {
|
3914
|
+
$scope.minutes = pad(minutes);
|
3915
|
+
}
|
3459
3916
|
$scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
|
3460
3917
|
}
|
3461
3918
|
|
@@ -3500,6 +3957,96 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
3500
3957
|
};
|
3501
3958
|
});
|
3502
3959
|
|
3960
|
+
angular.module('ui.bootstrap.transition', [])
|
3961
|
+
|
3962
|
+
.value('$transitionSuppressDeprecated', false)
|
3963
|
+
/**
|
3964
|
+
* $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
|
3965
|
+
* @param {DOMElement} element The DOMElement that will be animated.
|
3966
|
+
* @param {string|object|function} trigger The thing that will cause the transition to start:
|
3967
|
+
* - As a string, it represents the css class to be added to the element.
|
3968
|
+
* - As an object, it represents a hash of style attributes to be applied to the element.
|
3969
|
+
* - As a function, it represents a function to be called that will cause the transition to occur.
|
3970
|
+
* @return {Promise} A promise that is resolved when the transition finishes.
|
3971
|
+
*/
|
3972
|
+
.factory('$transition', [
|
3973
|
+
'$q', '$timeout', '$rootScope', '$log', '$transitionSuppressDeprecated',
|
3974
|
+
function($q , $timeout , $rootScope , $log , $transitionSuppressDeprecated) {
|
3975
|
+
|
3976
|
+
if (!$transitionSuppressDeprecated) {
|
3977
|
+
$log.warn('$transition is now deprecated. Use $animate from ngAnimate instead.');
|
3978
|
+
}
|
3979
|
+
|
3980
|
+
var $transition = function(element, trigger, options) {
|
3981
|
+
options = options || {};
|
3982
|
+
var deferred = $q.defer();
|
3983
|
+
var endEventName = $transition[options.animation ? 'animationEndEventName' : 'transitionEndEventName'];
|
3984
|
+
|
3985
|
+
var transitionEndHandler = function(event) {
|
3986
|
+
$rootScope.$apply(function() {
|
3987
|
+
element.unbind(endEventName, transitionEndHandler);
|
3988
|
+
deferred.resolve(element);
|
3989
|
+
});
|
3990
|
+
};
|
3991
|
+
|
3992
|
+
if (endEventName) {
|
3993
|
+
element.bind(endEventName, transitionEndHandler);
|
3994
|
+
}
|
3995
|
+
|
3996
|
+
// Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
|
3997
|
+
$timeout(function() {
|
3998
|
+
if ( angular.isString(trigger) ) {
|
3999
|
+
element.addClass(trigger);
|
4000
|
+
} else if ( angular.isFunction(trigger) ) {
|
4001
|
+
trigger(element);
|
4002
|
+
} else if ( angular.isObject(trigger) ) {
|
4003
|
+
element.css(trigger);
|
4004
|
+
}
|
4005
|
+
//If browser does not support transitions, instantly resolve
|
4006
|
+
if ( !endEventName ) {
|
4007
|
+
deferred.resolve(element);
|
4008
|
+
}
|
4009
|
+
});
|
4010
|
+
|
4011
|
+
// Add our custom cancel function to the promise that is returned
|
4012
|
+
// We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
|
4013
|
+
// i.e. it will therefore never raise a transitionEnd event for that transition
|
4014
|
+
deferred.promise.cancel = function() {
|
4015
|
+
if ( endEventName ) {
|
4016
|
+
element.unbind(endEventName, transitionEndHandler);
|
4017
|
+
}
|
4018
|
+
deferred.reject('Transition cancelled');
|
4019
|
+
};
|
4020
|
+
|
4021
|
+
return deferred.promise;
|
4022
|
+
};
|
4023
|
+
|
4024
|
+
// Work out the name of the transitionEnd event
|
4025
|
+
var transElement = document.createElement('trans');
|
4026
|
+
var transitionEndEventNames = {
|
4027
|
+
'WebkitTransition': 'webkitTransitionEnd',
|
4028
|
+
'MozTransition': 'transitionend',
|
4029
|
+
'OTransition': 'oTransitionEnd',
|
4030
|
+
'transition': 'transitionend'
|
4031
|
+
};
|
4032
|
+
var animationEndEventNames = {
|
4033
|
+
'WebkitTransition': 'webkitAnimationEnd',
|
4034
|
+
'MozTransition': 'animationend',
|
4035
|
+
'OTransition': 'oAnimationEnd',
|
4036
|
+
'transition': 'animationend'
|
4037
|
+
};
|
4038
|
+
function findEndEventName(endEventNames) {
|
4039
|
+
for (var name in endEventNames){
|
4040
|
+
if (transElement.style[name] !== undefined) {
|
4041
|
+
return endEventNames[name];
|
4042
|
+
}
|
4043
|
+
}
|
4044
|
+
}
|
4045
|
+
$transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
|
4046
|
+
$transition.animationEndEventName = findEndEventName(animationEndEventNames);
|
4047
|
+
return $transition;
|
4048
|
+
}]);
|
4049
|
+
|
3503
4050
|
angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
|
3504
4051
|
|
3505
4052
|
/**
|
@@ -3545,7 +4092,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3545
4092
|
//minimal no of characters that needs to be entered before typeahead kicks-in
|
3546
4093
|
var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
|
3547
4094
|
|
3548
|
-
//minimal wait time after last character typed before
|
4095
|
+
//minimal wait time after last character typed before typeahead kicks-in
|
3549
4096
|
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
|
3550
4097
|
|
3551
4098
|
//should it restrict model values to the ones selected from the popup only?
|
@@ -3633,7 +4180,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3633
4180
|
//but we are interested only in responses that correspond to the current view value
|
3634
4181
|
var onCurrentRequest = (inputValue === modelCtrl.$viewValue);
|
3635
4182
|
if (onCurrentRequest && hasFocus) {
|
3636
|
-
if (matches.length > 0) {
|
4183
|
+
if (matches && matches.length > 0) {
|
3637
4184
|
|
3638
4185
|
scope.activeIdx = focusFirst ? 0 : -1;
|
3639
4186
|
scope.matches.length = 0;
|
@@ -3674,7 +4221,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3674
4221
|
//we need to propagate user's query so we can higlight matches
|
3675
4222
|
scope.query = undefined;
|
3676
4223
|
|
3677
|
-
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
|
4224
|
+
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
|
3678
4225
|
var timeoutPromise;
|
3679
4226
|
|
3680
4227
|
var scheduleSearchWithTimeout = function(inputValue) {
|
@@ -3727,6 +4274,13 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3727
4274
|
var candidateViewValue, emptyViewValue;
|
3728
4275
|
var locals = {};
|
3729
4276
|
|
4277
|
+
// The validity may be set to false via $parsers (see above) if
|
4278
|
+
// the model is restricted to selected values. If the model
|
4279
|
+
// is set manually it is considered to be valid.
|
4280
|
+
if (!isEditable) {
|
4281
|
+
modelCtrl.$setValidity('editable', true);
|
4282
|
+
}
|
4283
|
+
|
3730
4284
|
if (inputFormatter) {
|
3731
4285
|
|
3732
4286
|
locals.$model = modelValue;
|
@@ -3754,6 +4308,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3754
4308
|
model = parserResult.modelMapper(originalScope, locals);
|
3755
4309
|
$setModelValue(originalScope, model);
|
3756
4310
|
modelCtrl.$setValidity('editable', true);
|
4311
|
+
modelCtrl.$setValidity('parse', true);
|
3757
4312
|
|
3758
4313
|
onSelectCallback(originalScope, {
|
3759
4314
|
$item: item,
|
@@ -3823,9 +4378,12 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3823
4378
|
if (appendToBody) {
|
3824
4379
|
$popup.remove();
|
3825
4380
|
}
|
4381
|
+
// Prevent jQuery cache memory leak
|
4382
|
+
popUpEl.remove();
|
3826
4383
|
});
|
3827
4384
|
|
3828
4385
|
var $popup = $compile(popUpEl)(scope);
|
4386
|
+
|
3829
4387
|
if (appendToBody) {
|
3830
4388
|
$document.find('body').append($popup);
|
3831
4389
|
} else {
|
@@ -3871,7 +4429,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3871
4429
|
};
|
3872
4430
|
})
|
3873
4431
|
|
3874
|
-
.directive('typeaheadMatch', ['$
|
4432
|
+
.directive('typeaheadMatch', ['$templateRequest', '$compile', '$parse', function ($templateRequest, $compile, $parse) {
|
3875
4433
|
return {
|
3876
4434
|
restrict:'EA',
|
3877
4435
|
scope:{
|
@@ -3881,8 +4439,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3881
4439
|
},
|
3882
4440
|
link:function (scope, element, attrs) {
|
3883
4441
|
var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';
|
3884
|
-
$
|
3885
|
-
|
4442
|
+
$templateRequest(tplUrl).then(function(tplContent) {
|
4443
|
+
$compile(tplContent.trim())(scope, function(clonedElement){
|
4444
|
+
element.replaceWith(clonedElement);
|
4445
|
+
});
|
3886
4446
|
});
|
3887
4447
|
}
|
3888
4448
|
};
|
@@ -3898,3 +4458,4 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
3898
4458
|
return query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;
|
3899
4459
|
};
|
3900
4460
|
});
|
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>');
|