angular-ui-bootstrap-rails 0.13.3 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 157585fd25062ce26b115bc7bf8f4ba28daae01f
|
4
|
+
data.tar.gz: 0917b5bc46d3c0a9aff497eee7686316b22a4565
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2262d699dd426b1cc8ce6f75cc9ea68c102f7bc9d2ba6f21809370f7f1922144c91884a6a37ea20297ad61095829482122410e96dd2358927ce1410cbf7fd647
|
7
|
+
data.tar.gz: c3bc1958dfb2c1f455b6ca9f9d87e11ebf67f51466e72b6e6d825a526c303869f23bdd77bbd037ac31a52332ea5178ad55c755242ba179fe361b9de5b59324c5
|
@@ -2,17 +2,16 @@
|
|
2
2
|
* angular-ui-bootstrap
|
3
3
|
* http://angular-ui.github.io/bootstrap/
|
4
4
|
|
5
|
-
* Version: 0.13.
|
5
|
+
* Version: 0.13.4 - 2015-09-03
|
6
6
|
* License: MIT
|
7
7
|
*/
|
8
8
|
angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.transition","ui.bootstrap.typeahead"]);
|
9
9
|
angular.module("ui.bootstrap.tpls", ["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-popup.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/tooltip/tooltip-template-popup.html","template/popover/popover-html.html","template/popover/popover-template.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]);
|
10
10
|
angular.module('ui.bootstrap.collapse', [])
|
11
11
|
|
12
|
-
.directive('collapse', ['$animate', function
|
13
|
-
|
12
|
+
.directive('collapse', ['$animate', function($animate) {
|
14
13
|
return {
|
15
|
-
link: function
|
14
|
+
link: function(scope, element, attrs) {
|
16
15
|
function expand() {
|
17
16
|
element.removeClass('collapse')
|
18
17
|
.addClass('collapsing')
|
@@ -30,7 +29,7 @@ angular.module('ui.bootstrap.collapse', [])
|
|
30
29
|
}
|
31
30
|
|
32
31
|
function collapse() {
|
33
|
-
if(!
|
32
|
+
if (!element.hasClass('collapse') && !element.hasClass('in')) {
|
34
33
|
return collapseDone();
|
35
34
|
}
|
36
35
|
|
@@ -57,7 +56,7 @@ angular.module('ui.bootstrap.collapse', [])
|
|
57
56
|
element.addClass('collapse');
|
58
57
|
}
|
59
58
|
|
60
|
-
scope.$watch(attrs.collapse, function
|
59
|
+
scope.$watch(attrs.collapse, function(shouldCollapse) {
|
61
60
|
if (shouldCollapse) {
|
62
61
|
collapse();
|
63
62
|
} else {
|
@@ -74,17 +73,17 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
74
73
|
closeOthers: true
|
75
74
|
})
|
76
75
|
|
77
|
-
.controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function
|
78
|
-
|
76
|
+
.controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function($scope, $attrs, accordionConfig) {
|
79
77
|
// This array keeps track of the accordion groups
|
80
78
|
this.groups = [];
|
81
79
|
|
82
80
|
// Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
|
83
81
|
this.closeOthers = function(openGroup) {
|
84
|
-
var closeOthers = angular.isDefined($attrs.closeOthers) ?
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
var closeOthers = angular.isDefined($attrs.closeOthers) ?
|
83
|
+
$scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
|
84
|
+
if (closeOthers) {
|
85
|
+
angular.forEach(this.groups, function(group) {
|
86
|
+
if (group !== openGroup) {
|
88
87
|
group.isOpen = false;
|
89
88
|
}
|
90
89
|
});
|
@@ -96,7 +95,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
96
95
|
var that = this;
|
97
96
|
this.groups.push(groupScope);
|
98
97
|
|
99
|
-
groupScope.$on('$destroy', function
|
98
|
+
groupScope.$on('$destroy', function(event) {
|
100
99
|
that.removeGroup(groupScope);
|
101
100
|
});
|
102
101
|
};
|
@@ -104,7 +103,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
104
103
|
// This is called from the accordion-group directive when to remove itself
|
105
104
|
this.removeGroup = function(group) {
|
106
105
|
var index = this.groups.indexOf(group);
|
107
|
-
if (
|
106
|
+
if (index !== -1) {
|
108
107
|
this.groups.splice(index, 1);
|
109
108
|
}
|
110
109
|
};
|
@@ -113,7 +112,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
113
112
|
|
114
113
|
// The accordion directive simply sets up the directive controller
|
115
114
|
// and adds an accordion CSS class to itself element.
|
116
|
-
.directive('accordion', function
|
115
|
+
.directive('accordion', function() {
|
117
116
|
return {
|
118
117
|
restrict: 'EA',
|
119
118
|
controller: 'AccordionController',
|
@@ -129,9 +128,9 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
129
128
|
// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
|
130
129
|
.directive('accordionGroup', function() {
|
131
130
|
return {
|
132
|
-
require:'^accordion', // We need this directive to be inside an accordion
|
133
|
-
restrict:'EA',
|
134
|
-
transclude:true, // It transcludes the contents of the directive into the template
|
131
|
+
require: '^accordion', // We need this directive to be inside an accordion
|
132
|
+
restrict: 'EA',
|
133
|
+
transclude: true, // It transcludes the contents of the directive into the template
|
135
134
|
replace: true, // The element containing the directive will be replaced with the template
|
136
135
|
templateUrl: function(element, attrs) {
|
137
136
|
return attrs.templateUrl || 'template/accordion/accordion-group.html';
|
@@ -149,15 +148,20 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
149
148
|
link: function(scope, element, attrs, accordionCtrl) {
|
150
149
|
accordionCtrl.addGroup(scope);
|
151
150
|
|
151
|
+
scope.openClass = attrs.openClass || 'panel-open';
|
152
|
+
scope.panelClass = attrs.panelClass;
|
152
153
|
scope.$watch('isOpen', function(value) {
|
153
|
-
|
154
|
+
element.toggleClass(scope.openClass, value);
|
155
|
+
if (value) {
|
154
156
|
accordionCtrl.closeOthers(scope);
|
155
157
|
}
|
156
158
|
});
|
157
159
|
|
158
|
-
scope.toggleOpen = function() {
|
159
|
-
if (
|
160
|
-
|
160
|
+
scope.toggleOpen = function($event) {
|
161
|
+
if (!scope.isDisabled) {
|
162
|
+
if (!$event || $event.which === 32) {
|
163
|
+
scope.isOpen = !scope.isOpen;
|
164
|
+
}
|
161
165
|
}
|
162
166
|
};
|
163
167
|
}
|
@@ -195,7 +199,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
195
199
|
require: '^accordionGroup',
|
196
200
|
link: function(scope, element, attr, controller) {
|
197
201
|
scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
|
198
|
-
if (
|
202
|
+
if (heading) {
|
199
203
|
element.find('span').html('');
|
200
204
|
element.find('span').append(heading);
|
201
205
|
}
|
@@ -208,14 +212,13 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
|
208
212
|
|
209
213
|
angular.module('ui.bootstrap.alert', [])
|
210
214
|
|
211
|
-
.controller('AlertController', ['$scope', '$attrs', function
|
215
|
+
.controller('AlertController', ['$scope', '$attrs', function($scope, $attrs) {
|
212
216
|
$scope.closeable = !!$attrs.close;
|
213
217
|
this.close = $scope.close;
|
214
218
|
}])
|
215
219
|
|
216
|
-
.directive('alert', function
|
220
|
+
.directive('alert', function() {
|
217
221
|
return {
|
218
|
-
restrict: 'EA',
|
219
222
|
controller: 'AlertController',
|
220
223
|
controllerAs: 'alert',
|
221
224
|
templateUrl: function(element, attrs) {
|
@@ -234,7 +237,7 @@ angular.module('ui.bootstrap.alert', [])
|
|
234
237
|
return {
|
235
238
|
require: 'alert',
|
236
239
|
link: function(scope, element, attrs, alertCtrl) {
|
237
|
-
$timeout(function(){
|
240
|
+
$timeout(function() {
|
238
241
|
alertCtrl.close();
|
239
242
|
}, parseInt(attrs.dismissOnTimeout, 10));
|
240
243
|
}
|
@@ -268,21 +271,23 @@ angular.module('ui.bootstrap.buttons', [])
|
|
268
271
|
this.toggleEvent = buttonConfig.toggleEvent || 'click';
|
269
272
|
}])
|
270
273
|
|
271
|
-
.directive('btnRadio', function
|
274
|
+
.directive('btnRadio', function() {
|
272
275
|
return {
|
273
276
|
require: ['btnRadio', 'ngModel'],
|
274
277
|
controller: 'ButtonsController',
|
275
278
|
controllerAs: 'buttons',
|
276
|
-
link: function
|
279
|
+
link: function(scope, element, attrs, ctrls) {
|
277
280
|
var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
278
281
|
|
282
|
+
element.find('input').css({display: 'none'});
|
283
|
+
|
279
284
|
//model -> UI
|
280
|
-
ngModelCtrl.$render = function
|
285
|
+
ngModelCtrl.$render = function() {
|
281
286
|
element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
|
282
287
|
};
|
283
288
|
|
284
289
|
//ui->model
|
285
|
-
element.bind(buttonsCtrl.toggleEvent, function
|
290
|
+
element.bind(buttonsCtrl.toggleEvent, function() {
|
286
291
|
if (attrs.disabled) {
|
287
292
|
return;
|
288
293
|
}
|
@@ -290,7 +295,7 @@ angular.module('ui.bootstrap.buttons', [])
|
|
290
295
|
var isActive = element.hasClass(buttonsCtrl.activeClass);
|
291
296
|
|
292
297
|
if (!isActive || angular.isDefined(attrs.uncheckable)) {
|
293
|
-
scope.$apply(function
|
298
|
+
scope.$apply(function() {
|
294
299
|
ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.btnRadio));
|
295
300
|
ngModelCtrl.$render();
|
296
301
|
});
|
@@ -300,14 +305,16 @@ angular.module('ui.bootstrap.buttons', [])
|
|
300
305
|
};
|
301
306
|
})
|
302
307
|
|
303
|
-
.directive('btnCheckbox', function
|
308
|
+
.directive('btnCheckbox', ['$document', function($document) {
|
304
309
|
return {
|
305
310
|
require: ['btnCheckbox', 'ngModel'],
|
306
311
|
controller: 'ButtonsController',
|
307
312
|
controllerAs: 'button',
|
308
|
-
link: function
|
313
|
+
link: function(scope, element, attrs, ctrls) {
|
309
314
|
var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
310
315
|
|
316
|
+
element.find('input').css({display: 'none'});
|
317
|
+
|
311
318
|
function getTrueValue() {
|
312
319
|
return getCheckboxValue(attrs.btnCheckboxTrue, true);
|
313
320
|
}
|
@@ -322,24 +329,36 @@ angular.module('ui.bootstrap.buttons', [])
|
|
322
329
|
}
|
323
330
|
|
324
331
|
//model -> UI
|
325
|
-
ngModelCtrl.$render = function
|
332
|
+
ngModelCtrl.$render = function() {
|
326
333
|
element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
|
327
334
|
};
|
328
335
|
|
329
336
|
//ui->model
|
330
|
-
element.bind(buttonsCtrl.toggleEvent, function
|
337
|
+
element.bind(buttonsCtrl.toggleEvent, function() {
|
331
338
|
if (attrs.disabled) {
|
332
339
|
return;
|
333
340
|
}
|
334
341
|
|
335
|
-
scope.$apply(function
|
342
|
+
scope.$apply(function() {
|
343
|
+
ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
|
344
|
+
ngModelCtrl.$render();
|
345
|
+
});
|
346
|
+
});
|
347
|
+
|
348
|
+
//accessibility
|
349
|
+
element.on('keypress', function(e) {
|
350
|
+
if (attrs.disabled || e.which !== 32 || $document[0].activeElement !== element[0]) {
|
351
|
+
return;
|
352
|
+
}
|
353
|
+
|
354
|
+
scope.$apply(function() {
|
336
355
|
ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
|
337
356
|
ngModelCtrl.$render();
|
338
357
|
});
|
339
358
|
});
|
340
359
|
}
|
341
360
|
};
|
342
|
-
});
|
361
|
+
}]);
|
343
362
|
|
344
363
|
/**
|
345
364
|
* @ngdoc overview
|
@@ -535,7 +554,7 @@ angular.module('ui.bootstrap.carousel', [])
|
|
535
554
|
} else if (currentIndex > index) {
|
536
555
|
currentIndex--;
|
537
556
|
}
|
538
|
-
|
557
|
+
|
539
558
|
//clean the currentSlide when no more slide
|
540
559
|
if (slides.length === 0) {
|
541
560
|
self.currentSlide = null;
|
@@ -659,6 +678,7 @@ function CarouselDemoCtrl($scope) {
|
|
659
678
|
},
|
660
679
|
scope: {
|
661
680
|
active: '=?',
|
681
|
+
actual: '=?',
|
662
682
|
index: '=?'
|
663
683
|
},
|
664
684
|
link: function (scope, element, attrs, carouselCtrl) {
|
@@ -823,6 +843,10 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
823
843
|
regex: '1?[0-9]|2[0-3]',
|
824
844
|
apply: function(value) { this.hours = +value; }
|
825
845
|
},
|
846
|
+
'h': {
|
847
|
+
regex: '[0-9]|1[0-2]',
|
848
|
+
apply: function(value) { this.hours = +value; }
|
849
|
+
},
|
826
850
|
'mm': {
|
827
851
|
regex: '[0-5][0-9]',
|
828
852
|
apply: function(value) { this.minutes = +value; }
|
@@ -885,14 +909,14 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
885
909
|
}
|
886
910
|
|
887
911
|
this.parse = function(input, format, baseDate) {
|
888
|
-
if (
|
912
|
+
if (!angular.isString(input) || !format) {
|
889
913
|
return input;
|
890
914
|
}
|
891
915
|
|
892
916
|
format = $locale.DATETIME_FORMATS[format] || format;
|
893
917
|
format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
|
894
918
|
|
895
|
-
if (
|
919
|
+
if (!this.parsers[format]) {
|
896
920
|
this.parsers[format] = createParser(format);
|
897
921
|
}
|
898
922
|
|
@@ -901,7 +925,7 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
901
925
|
map = parser.map,
|
902
926
|
results = input.match(regex);
|
903
927
|
|
904
|
-
if (
|
928
|
+
if (results && results.length) {
|
905
929
|
var fields, dt;
|
906
930
|
if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
|
907
931
|
fields = {
|
@@ -920,15 +944,16 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
920
944
|
fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
|
921
945
|
}
|
922
946
|
|
923
|
-
for(
|
947
|
+
for (var i = 1, n = results.length; i < n; i++) {
|
924
948
|
var mapper = map[i-1];
|
925
|
-
if (
|
949
|
+
if (mapper.apply) {
|
926
950
|
mapper.apply.call(fields, results[i]);
|
927
951
|
}
|
928
952
|
}
|
929
953
|
|
930
|
-
if (
|
931
|
-
dt = new Date(fields.year, fields.month, fields.date,
|
954
|
+
if (isValid(fields.year, fields.month, fields.date)) {
|
955
|
+
dt = new Date(fields.year, fields.month, fields.date,
|
956
|
+
fields.hours, fields.minutes, fields.seconds,
|
932
957
|
fields.milliseconds || 0);
|
933
958
|
}
|
934
959
|
|
@@ -943,12 +968,12 @@ angular.module('ui.bootstrap.dateparser', [])
|
|
943
968
|
return false;
|
944
969
|
}
|
945
970
|
|
946
|
-
if (
|
947
|
-
|
971
|
+
if (month === 1 && date > 28) {
|
972
|
+
return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
|
948
973
|
}
|
949
974
|
|
950
|
-
if (
|
951
|
-
|
975
|
+
if (month === 3 || month === 5 || month === 8 || month === 10) {
|
976
|
+
return date < 31;
|
952
977
|
}
|
953
978
|
|
954
979
|
return true;
|
@@ -963,8 +988,7 @@ angular.module('ui.bootstrap.position', [])
|
|
963
988
|
* relation to other, existing elements (this is the case for tooltips, popovers,
|
964
989
|
* typeahead suggestions etc.).
|
965
990
|
*/
|
966
|
-
.factory('$position', ['$document', '$window', function
|
967
|
-
|
991
|
+
.factory('$position', ['$document', '$window', function($document, $window) {
|
968
992
|
function getStyle(el, cssprop) {
|
969
993
|
if (el.currentStyle) { //IE
|
970
994
|
return el.currentStyle[cssprop];
|
@@ -987,7 +1011,7 @@ angular.module('ui.bootstrap.position', [])
|
|
987
1011
|
* returns the closest, non-statically positioned parentOffset of a given element
|
988
1012
|
* @param element
|
989
1013
|
*/
|
990
|
-
var parentOffsetEl = function
|
1014
|
+
var parentOffsetEl = function(element) {
|
991
1015
|
var docDomEl = $document[0];
|
992
1016
|
var offsetParent = element.offsetParent || docDomEl;
|
993
1017
|
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
|
@@ -1001,7 +1025,7 @@ angular.module('ui.bootstrap.position', [])
|
|
1001
1025
|
* Provides read-only equivalent of jQuery's position function:
|
1002
1026
|
* http://api.jquery.com/position/
|
1003
1027
|
*/
|
1004
|
-
position: function
|
1028
|
+
position: function(element) {
|
1005
1029
|
var elBCR = this.offset(element);
|
1006
1030
|
var offsetParentBCR = { top: 0, left: 0 };
|
1007
1031
|
var offsetParentEl = parentOffsetEl(element[0]);
|
@@ -1024,7 +1048,7 @@ angular.module('ui.bootstrap.position', [])
|
|
1024
1048
|
* Provides read-only equivalent of jQuery's offset function:
|
1025
1049
|
* http://api.jquery.com/offset/
|
1026
1050
|
*/
|
1027
|
-
offset: function
|
1051
|
+
offset: function(element) {
|
1028
1052
|
var boundingClientRect = element[0].getBoundingClientRect();
|
1029
1053
|
return {
|
1030
1054
|
width: boundingClientRect.width || element.prop('offsetWidth'),
|
@@ -1037,8 +1061,7 @@ angular.module('ui.bootstrap.position', [])
|
|
1037
1061
|
/**
|
1038
1062
|
* Provides coordinates for the targetEl in relation to hostEl
|
1039
1063
|
*/
|
1040
|
-
positionElements: function
|
1041
|
-
|
1064
|
+
positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
|
1042
1065
|
var positionStrParts = positionStr.split('-');
|
1043
1066
|
var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
|
1044
1067
|
|
@@ -1053,25 +1076,25 @@ angular.module('ui.bootstrap.position', [])
|
|
1053
1076
|
targetElHeight = targetEl.prop('offsetHeight');
|
1054
1077
|
|
1055
1078
|
var shiftWidth = {
|
1056
|
-
center: function
|
1079
|
+
center: function() {
|
1057
1080
|
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
|
1058
1081
|
},
|
1059
|
-
left: function
|
1082
|
+
left: function() {
|
1060
1083
|
return hostElPos.left;
|
1061
1084
|
},
|
1062
|
-
right: function
|
1085
|
+
right: function() {
|
1063
1086
|
return hostElPos.left + hostElPos.width;
|
1064
1087
|
}
|
1065
1088
|
};
|
1066
1089
|
|
1067
1090
|
var shiftHeight = {
|
1068
|
-
center: function
|
1091
|
+
center: function() {
|
1069
1092
|
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
|
1070
1093
|
},
|
1071
|
-
top: function
|
1094
|
+
top: function() {
|
1072
1095
|
return hostElPos.top;
|
1073
1096
|
},
|
1074
|
-
bottom: function
|
1097
|
+
bottom: function() {
|
1075
1098
|
return hostElPos.top + hostElPos.height;
|
1076
1099
|
}
|
1077
1100
|
};
|
@@ -1139,13 +1162,13 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1139
1162
|
|
1140
1163
|
// Configuration attributes
|
1141
1164
|
angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
|
1142
|
-
'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(
|
1165
|
+
'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(key, index) {
|
1143
1166
|
self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
|
1144
1167
|
});
|
1145
1168
|
|
1146
1169
|
// Watchable date attributes
|
1147
|
-
angular.forEach(['minDate', 'maxDate'], function(
|
1148
|
-
if (
|
1170
|
+
angular.forEach(['minDate', 'maxDate'], function(key) {
|
1171
|
+
if ($attrs[key]) {
|
1149
1172
|
$scope.$parent.$watch($parse($attrs[key]), function(value) {
|
1150
1173
|
self[key] = value ? new Date(value) : null;
|
1151
1174
|
self.refreshView();
|
@@ -1155,12 +1178,12 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1155
1178
|
}
|
1156
1179
|
});
|
1157
1180
|
|
1158
|
-
angular.forEach(['minMode', 'maxMode'], function(
|
1159
|
-
if (
|
1181
|
+
angular.forEach(['minMode', 'maxMode'], function(key) {
|
1182
|
+
if ($attrs[key]) {
|
1160
1183
|
$scope.$parent.$watch($parse($attrs[key]), function(value) {
|
1161
1184
|
self[key] = angular.isDefined(value) ? value : $attrs[key];
|
1162
1185
|
$scope[key] = self[key];
|
1163
|
-
if ((key == 'minMode' && self.modes.indexOf(
|
1186
|
+
if ((key == 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key])) || (key == 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key]))) {
|
1164
1187
|
$scope.datepickerMode = self[key];
|
1165
1188
|
}
|
1166
1189
|
});
|
@@ -1173,16 +1196,16 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1173
1196
|
$scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
|
1174
1197
|
$scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
|
1175
1198
|
|
1176
|
-
if(angular.isDefined($attrs.initDate)) {
|
1199
|
+
if (angular.isDefined($attrs.initDate)) {
|
1177
1200
|
this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date();
|
1178
|
-
$scope.$parent.$watch($attrs.initDate, function(initDate){
|
1179
|
-
if(initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)){
|
1201
|
+
$scope.$parent.$watch($attrs.initDate, function(initDate) {
|
1202
|
+
if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
|
1180
1203
|
self.activeDate = initDate;
|
1181
1204
|
self.refreshView();
|
1182
1205
|
}
|
1183
1206
|
});
|
1184
1207
|
} else {
|
1185
|
-
this.activeDate =
|
1208
|
+
this.activeDate = new Date();
|
1186
1209
|
}
|
1187
1210
|
|
1188
1211
|
$scope.isActive = function(dateObject) {
|
@@ -1193,7 +1216,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1193
1216
|
return false;
|
1194
1217
|
};
|
1195
1218
|
|
1196
|
-
this.init = function(
|
1219
|
+
this.init = function(ngModelCtrl_) {
|
1197
1220
|
ngModelCtrl = ngModelCtrl_;
|
1198
1221
|
|
1199
1222
|
ngModelCtrl.$render = function() {
|
@@ -1202,13 +1225,13 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1202
1225
|
};
|
1203
1226
|
|
1204
1227
|
this.render = function() {
|
1205
|
-
if (
|
1206
|
-
var date = new Date(
|
1228
|
+
if (ngModelCtrl.$viewValue) {
|
1229
|
+
var date = new Date(ngModelCtrl.$viewValue),
|
1207
1230
|
isValid = !isNaN(date);
|
1208
1231
|
|
1209
|
-
if (
|
1232
|
+
if (isValid) {
|
1210
1233
|
this.activeDate = date;
|
1211
|
-
} else if (
|
1234
|
+
} else if (!$datepickerSuppressError) {
|
1212
1235
|
$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.');
|
1213
1236
|
}
|
1214
1237
|
}
|
@@ -1216,7 +1239,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1216
1239
|
};
|
1217
1240
|
|
1218
1241
|
this.refreshView = function() {
|
1219
|
-
if (
|
1242
|
+
if (this.element) {
|
1220
1243
|
this._refreshView();
|
1221
1244
|
|
1222
1245
|
var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
|
@@ -1236,11 +1259,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1236
1259
|
};
|
1237
1260
|
};
|
1238
1261
|
|
1239
|
-
this.isDisabled = function(
|
1262
|
+
this.isDisabled = function(date) {
|
1240
1263
|
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})));
|
1241
1264
|
};
|
1242
1265
|
|
1243
|
-
this.customClass = function(
|
1266
|
+
this.customClass = function(date) {
|
1244
1267
|
return $scope.customClass({date: date, mode: $scope.datepickerMode});
|
1245
1268
|
};
|
1246
1269
|
|
@@ -1264,37 +1287,37 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1264
1287
|
date.setHours(hours === 23 ? hours + 2 : 0);
|
1265
1288
|
};
|
1266
1289
|
|
1267
|
-
$scope.select = function(
|
1268
|
-
if (
|
1269
|
-
var dt = ngModelCtrl.$viewValue ? new Date(
|
1270
|
-
dt.setFullYear(
|
1271
|
-
ngModelCtrl.$setViewValue(
|
1290
|
+
$scope.select = function(date) {
|
1291
|
+
if ($scope.datepickerMode === self.minMode) {
|
1292
|
+
var dt = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0);
|
1293
|
+
dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
1294
|
+
ngModelCtrl.$setViewValue(dt);
|
1272
1295
|
ngModelCtrl.$render();
|
1273
1296
|
} else {
|
1274
1297
|
self.activeDate = date;
|
1275
|
-
$scope.datepickerMode = self.modes[
|
1298
|
+
$scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
|
1276
1299
|
}
|
1277
1300
|
};
|
1278
1301
|
|
1279
|
-
$scope.move = function(
|
1302
|
+
$scope.move = function(direction) {
|
1280
1303
|
var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
|
1281
1304
|
month = self.activeDate.getMonth() + direction * (self.step.months || 0);
|
1282
1305
|
self.activeDate.setFullYear(year, month, 1);
|
1283
1306
|
self.refreshView();
|
1284
1307
|
};
|
1285
1308
|
|
1286
|
-
$scope.toggleMode = function(
|
1309
|
+
$scope.toggleMode = function(direction) {
|
1287
1310
|
direction = direction || 1;
|
1288
1311
|
|
1289
1312
|
if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
|
1290
1313
|
return;
|
1291
1314
|
}
|
1292
1315
|
|
1293
|
-
$scope.datepickerMode = self.modes[
|
1316
|
+
$scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
|
1294
1317
|
};
|
1295
1318
|
|
1296
1319
|
// Key event mapper
|
1297
|
-
$scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };
|
1320
|
+
$scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
|
1298
1321
|
|
1299
1322
|
var focusElement = function() {
|
1300
1323
|
self.element[0].focus();
|
@@ -1303,20 +1326,20 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1303
1326
|
// Listen for focus requests from popup directive
|
1304
1327
|
$scope.$on('datepicker.focus', focusElement);
|
1305
1328
|
|
1306
|
-
$scope.keydown = function(
|
1329
|
+
$scope.keydown = function(evt) {
|
1307
1330
|
var key = $scope.keys[evt.which];
|
1308
1331
|
|
1309
|
-
if (
|
1332
|
+
if (!key || evt.shiftKey || evt.altKey) {
|
1310
1333
|
return;
|
1311
1334
|
}
|
1312
1335
|
|
1313
1336
|
evt.preventDefault();
|
1314
|
-
if(!self.shortcutPropagation){
|
1315
|
-
|
1337
|
+
if (!self.shortcutPropagation) {
|
1338
|
+
evt.stopPropagation();
|
1316
1339
|
}
|
1317
1340
|
|
1318
1341
|
if (key === 'enter' || key === 'space') {
|
1319
|
-
if (
|
1342
|
+
if (self.isDisabled(self.activeDate)) {
|
1320
1343
|
return; // do nothing
|
1321
1344
|
}
|
1322
1345
|
$scope.select(self.activeDate);
|
@@ -1331,7 +1354,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1331
1354
|
};
|
1332
1355
|
}])
|
1333
1356
|
|
1334
|
-
.directive(
|
1357
|
+
.directive('datepicker', function() {
|
1335
1358
|
return {
|
1336
1359
|
restrict: 'EA',
|
1337
1360
|
replace: true,
|
@@ -1355,7 +1378,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1355
1378
|
};
|
1356
1379
|
})
|
1357
1380
|
|
1358
|
-
.directive('daypicker', ['dateFilter', function
|
1381
|
+
.directive('daypicker', ['dateFilter', function(dateFilter) {
|
1359
1382
|
return {
|
1360
1383
|
restrict: 'EA',
|
1361
1384
|
replace: true,
|
@@ -1368,17 +1391,17 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1368
1391
|
ctrl.element = element;
|
1369
1392
|
|
1370
1393
|
var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
1371
|
-
function getDaysInMonth(
|
1394
|
+
function getDaysInMonth(year, month) {
|
1372
1395
|
return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
|
1373
1396
|
}
|
1374
1397
|
|
1375
1398
|
function getDates(startDate, n) {
|
1376
1399
|
var dates = new Array(n), current = new Date(startDate), i = 0, date;
|
1377
|
-
while (
|
1400
|
+
while (i < n) {
|
1378
1401
|
date = new Date(current);
|
1379
1402
|
ctrl.fixTimeZone(date);
|
1380
1403
|
dates[i++] = date;
|
1381
|
-
current.setDate(
|
1404
|
+
current.setDate(current.getDate() + 1);
|
1382
1405
|
}
|
1383
1406
|
return dates;
|
1384
1407
|
}
|
@@ -1391,8 +1414,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1391
1414
|
numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
|
1392
1415
|
firstDate = new Date(firstDayOfMonth);
|
1393
1416
|
|
1394
|
-
if (
|
1395
|
-
firstDate.setDate(
|
1417
|
+
if (numDisplayedFromPreviousMonth > 0) {
|
1418
|
+
firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
|
1396
1419
|
}
|
1397
1420
|
|
1398
1421
|
// 42 is the number of days on a six-month calendar
|
@@ -1415,19 +1438,19 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1415
1438
|
scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
|
1416
1439
|
scope.rows = ctrl.split(days, 7);
|
1417
1440
|
|
1418
|
-
if (
|
1441
|
+
if (scope.showWeeks) {
|
1419
1442
|
scope.weekNumbers = [];
|
1420
1443
|
var thursdayIndex = (4 + 7 - ctrl.startingDay) % 7,
|
1421
1444
|
numWeeks = scope.rows.length;
|
1422
1445
|
for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
|
1423
1446
|
scope.weekNumbers.push(
|
1424
|
-
getISO8601WeekNumber(
|
1447
|
+
getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
|
1425
1448
|
}
|
1426
1449
|
}
|
1427
1450
|
};
|
1428
1451
|
|
1429
1452
|
ctrl.compare = function(date1, date2) {
|
1430
|
-
return (new Date(
|
1453
|
+
return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
|
1431
1454
|
};
|
1432
1455
|
|
1433
1456
|
function getISO8601WeekNumber(date) {
|
@@ -1439,7 +1462,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1439
1462
|
return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
|
1440
1463
|
}
|
1441
1464
|
|
1442
|
-
ctrl.handleKeyDown = function(
|
1465
|
+
ctrl.handleKeyDown = function(key, evt) {
|
1443
1466
|
var date = ctrl.activeDate.getDate();
|
1444
1467
|
|
1445
1468
|
if (key === 'left') {
|
@@ -1467,7 +1490,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1467
1490
|
};
|
1468
1491
|
}])
|
1469
1492
|
|
1470
|
-
.directive('monthpicker', ['dateFilter', function
|
1493
|
+
.directive('monthpicker', ['dateFilter', function(dateFilter) {
|
1471
1494
|
return {
|
1472
1495
|
restrict: 'EA',
|
1473
1496
|
replace: true,
|
@@ -1482,7 +1505,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1482
1505
|
year = ctrl.activeDate.getFullYear(),
|
1483
1506
|
date;
|
1484
1507
|
|
1485
|
-
for (
|
1508
|
+
for (var i = 0; i < 12; i++) {
|
1486
1509
|
date = new Date(year, i, 1);
|
1487
1510
|
ctrl.fixTimeZone(date);
|
1488
1511
|
months[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatMonth), {
|
@@ -1495,10 +1518,10 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1495
1518
|
};
|
1496
1519
|
|
1497
1520
|
ctrl.compare = function(date1, date2) {
|
1498
|
-
return new Date(
|
1521
|
+
return new Date(date1.getFullYear(), date1.getMonth()) - new Date(date2.getFullYear(), date2.getMonth());
|
1499
1522
|
};
|
1500
1523
|
|
1501
|
-
ctrl.handleKeyDown = function(
|
1524
|
+
ctrl.handleKeyDown = function(key, evt) {
|
1502
1525
|
var date = ctrl.activeDate.getMonth();
|
1503
1526
|
|
1504
1527
|
if (key === 'left') {
|
@@ -1525,7 +1548,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1525
1548
|
};
|
1526
1549
|
}])
|
1527
1550
|
|
1528
|
-
.directive('yearpicker', ['dateFilter', function
|
1551
|
+
.directive('yearpicker', ['dateFilter', function(dateFilter) {
|
1529
1552
|
return {
|
1530
1553
|
restrict: 'EA',
|
1531
1554
|
replace: true,
|
@@ -1544,7 +1567,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1544
1567
|
ctrl._refreshView = function() {
|
1545
1568
|
var years = new Array(range), date;
|
1546
1569
|
|
1547
|
-
for (
|
1570
|
+
for (var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++) {
|
1548
1571
|
date = new Date(start + i, 0, 1);
|
1549
1572
|
ctrl.fixTimeZone(date);
|
1550
1573
|
years[i] = angular.extend(ctrl.createDateObject(date, ctrl.formatYear), {
|
@@ -1560,7 +1583,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1560
1583
|
return date1.getFullYear() - date2.getFullYear();
|
1561
1584
|
};
|
1562
1585
|
|
1563
|
-
ctrl.handleKeyDown = function(
|
1586
|
+
ctrl.handleKeyDown = function(key, evt) {
|
1564
1587
|
var date = ctrl.activeDate.getFullYear();
|
1565
1588
|
|
1566
1589
|
if (key === 'left') {
|
@@ -1574,9 +1597,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1574
1597
|
} else if (key === 'pageup' || key === 'pagedown') {
|
1575
1598
|
date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
|
1576
1599
|
} else if (key === 'home') {
|
1577
|
-
date = getStartingYear(
|
1600
|
+
date = getStartingYear(ctrl.activeDate.getFullYear());
|
1578
1601
|
} else if (key === 'end') {
|
1579
|
-
date = getStartingYear(
|
1602
|
+
date = getStartingYear(ctrl.activeDate.getFullYear()) + range - 1;
|
1580
1603
|
}
|
1581
1604
|
ctrl.activeDate.setFullYear(date);
|
1582
1605
|
};
|
@@ -1605,7 +1628,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
|
1605
1628
|
})
|
1606
1629
|
|
1607
1630
|
.directive('datepickerPopup', ['$compile', '$parse', '$document', '$rootScope', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig', '$timeout',
|
1608
|
-
function
|
1631
|
+
function($compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout) {
|
1609
1632
|
return {
|
1610
1633
|
restrict: 'EA',
|
1611
1634
|
require: 'ngModel',
|
@@ -1623,14 +1646,28 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1623
1646
|
appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody,
|
1624
1647
|
onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus,
|
1625
1648
|
datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl,
|
1626
|
-
datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl
|
1649
|
+
datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl,
|
1650
|
+
cache = {};
|
1627
1651
|
|
1628
1652
|
scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
|
1629
1653
|
|
1630
|
-
scope.getText = function(
|
1654
|
+
scope.getText = function(key) {
|
1631
1655
|
return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
|
1632
1656
|
};
|
1633
1657
|
|
1658
|
+
scope.isDisabled = function(date) {
|
1659
|
+
if (date === 'today') {
|
1660
|
+
date = new Date();
|
1661
|
+
}
|
1662
|
+
|
1663
|
+
return ((scope.watchData.minDate && scope.compare(date, cache.minDate) < 0) ||
|
1664
|
+
(scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0));
|
1665
|
+
};
|
1666
|
+
|
1667
|
+
scope.compare = function(date1, date2) {
|
1668
|
+
return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
|
1669
|
+
};
|
1670
|
+
|
1634
1671
|
var isHtml5DateInput = false;
|
1635
1672
|
if (datepickerPopupConfig.html5Types[attrs.type]) {
|
1636
1673
|
dateFormat = datepickerPopupConfig.html5Types[attrs.type];
|
@@ -1668,7 +1705,7 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1668
1705
|
'template-url': datepickerPopupTemplateUrl
|
1669
1706
|
});
|
1670
1707
|
|
1671
|
-
function cameltoDash(
|
1708
|
+
function cameltoDash(string) {
|
1672
1709
|
return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
|
1673
1710
|
}
|
1674
1711
|
|
@@ -1677,38 +1714,41 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1677
1714
|
datepickerEl.attr('template-url', datepickerTemplateUrl);
|
1678
1715
|
|
1679
1716
|
if (isHtml5DateInput) {
|
1680
|
-
if (attrs.type
|
1717
|
+
if (attrs.type === 'month') {
|
1681
1718
|
datepickerEl.attr('datepicker-mode', '"month"');
|
1682
1719
|
datepickerEl.attr('min-mode', 'month');
|
1683
1720
|
}
|
1684
1721
|
}
|
1685
1722
|
|
1686
|
-
if (
|
1723
|
+
if (attrs.datepickerOptions) {
|
1687
1724
|
var options = scope.$parent.$eval(attrs.datepickerOptions);
|
1688
|
-
if(options && options.initDate) {
|
1725
|
+
if (options && options.initDate) {
|
1689
1726
|
scope.initDate = options.initDate;
|
1690
|
-
datepickerEl.attr(
|
1727
|
+
datepickerEl.attr('init-date', 'initDate');
|
1691
1728
|
delete options.initDate;
|
1692
1729
|
}
|
1693
|
-
angular.forEach(options, function(
|
1730
|
+
angular.forEach(options, function(value, option) {
|
1694
1731
|
datepickerEl.attr( cameltoDash(option), value );
|
1695
1732
|
});
|
1696
1733
|
}
|
1697
1734
|
|
1698
1735
|
scope.watchData = {};
|
1699
|
-
angular.forEach(['minMode', 'maxMode', 'minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function(
|
1700
|
-
if (
|
1736
|
+
angular.forEach(['minMode', 'maxMode', 'minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function(key) {
|
1737
|
+
if (attrs[key]) {
|
1701
1738
|
var getAttribute = $parse(attrs[key]);
|
1702
|
-
scope.$parent.$watch(getAttribute, function(value){
|
1739
|
+
scope.$parent.$watch(getAttribute, function(value) {
|
1703
1740
|
scope.watchData[key] = value;
|
1741
|
+
if (key === 'minDate' || key === 'maxDate') {
|
1742
|
+
cache[key] = new Date(value);
|
1743
|
+
}
|
1704
1744
|
});
|
1705
1745
|
datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
|
1706
1746
|
|
1707
1747
|
// Propagate changes from datepicker to outside
|
1708
|
-
if (
|
1748
|
+
if (key === 'datepickerMode') {
|
1709
1749
|
var setAttribute = getAttribute.assign;
|
1710
1750
|
scope.$watch('watchData.' + key, function(value, oldvalue) {
|
1711
|
-
if (
|
1751
|
+
if (angular.isFunction(setAttribute) && value !== oldvalue) {
|
1712
1752
|
setAttribute(scope.$parent, value);
|
1713
1753
|
}
|
1714
1754
|
});
|
@@ -1723,7 +1763,7 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1723
1763
|
datepickerEl.attr('show-weeks', attrs.showWeeks);
|
1724
1764
|
}
|
1725
1765
|
|
1726
|
-
if (attrs.customClass){
|
1766
|
+
if (attrs.customClass) {
|
1727
1767
|
datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
|
1728
1768
|
}
|
1729
1769
|
|
@@ -1776,13 +1816,12 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1776
1816
|
ngModel.$$parserName = 'date';
|
1777
1817
|
ngModel.$validators.date = validator;
|
1778
1818
|
ngModel.$parsers.unshift(parseDate);
|
1779
|
-
ngModel.$formatters.push(function
|
1819
|
+
ngModel.$formatters.push(function(value) {
|
1780
1820
|
scope.date = value;
|
1781
1821
|
return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat);
|
1782
1822
|
});
|
1783
|
-
}
|
1784
|
-
|
1785
|
-
ngModel.$formatters.push(function (value) {
|
1823
|
+
} else {
|
1824
|
+
ngModel.$formatters.push(function(value) {
|
1786
1825
|
scope.date = value;
|
1787
1826
|
return value;
|
1788
1827
|
});
|
@@ -1797,19 +1836,19 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1797
1836
|
element.val(date);
|
1798
1837
|
ngModel.$setViewValue(date);
|
1799
1838
|
|
1800
|
-
if (
|
1839
|
+
if (closeOnDateSelection) {
|
1801
1840
|
scope.isOpen = false;
|
1802
1841
|
element[0].focus();
|
1803
1842
|
}
|
1804
1843
|
};
|
1805
1844
|
|
1806
1845
|
// Detect changes in the view from the text box
|
1807
|
-
ngModel.$viewChangeListeners.push(function
|
1846
|
+
ngModel.$viewChangeListeners.push(function() {
|
1808
1847
|
scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date);
|
1809
1848
|
});
|
1810
1849
|
|
1811
1850
|
var documentClickBind = function(event) {
|
1812
|
-
if (scope.isOpen && !element[0].contains(event.target)) {
|
1851
|
+
if (scope.isOpen && !(element[0].contains(event.target) || popupEl[0].contains(event.target))) {
|
1813
1852
|
scope.$apply(function() {
|
1814
1853
|
scope.isOpen = false;
|
1815
1854
|
});
|
@@ -1857,7 +1896,7 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1857
1896
|
}
|
1858
1897
|
});
|
1859
1898
|
|
1860
|
-
scope.select = function(
|
1899
|
+
scope.select = function(date) {
|
1861
1900
|
if (date === 'today') {
|
1862
1901
|
var today = new Date();
|
1863
1902
|
if (angular.isDate(scope.date)) {
|
@@ -1867,7 +1906,7 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1867
1906
|
date = new Date(today.setHours(0, 0, 0, 0));
|
1868
1907
|
}
|
1869
1908
|
}
|
1870
|
-
scope.dateSelection(
|
1909
|
+
scope.dateSelection(date);
|
1871
1910
|
};
|
1872
1911
|
|
1873
1912
|
scope.close = function() {
|
@@ -1879,7 +1918,7 @@ function ($compile, $parse, $document, $rootScope, $position, dateFilter, datePa
|
|
1879
1918
|
// Prevent jQuery cache memory leak (template is now redundant after linking)
|
1880
1919
|
popupEl.remove();
|
1881
1920
|
|
1882
|
-
if (
|
1921
|
+
if (appendToBody) {
|
1883
1922
|
$document.find('body').append($popup);
|
1884
1923
|
} else {
|
1885
1924
|
element.after($popup);
|
@@ -1922,36 +1961,36 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
1922
1961
|
.service('dropdownService', ['$document', '$rootScope', function($document, $rootScope) {
|
1923
1962
|
var openScope = null;
|
1924
1963
|
|
1925
|
-
this.open = function(
|
1926
|
-
if (
|
1964
|
+
this.open = function(dropdownScope) {
|
1965
|
+
if (!openScope) {
|
1927
1966
|
$document.bind('click', closeDropdown);
|
1928
1967
|
$document.bind('keydown', keybindFilter);
|
1929
1968
|
}
|
1930
1969
|
|
1931
|
-
if (
|
1970
|
+
if (openScope && openScope !== dropdownScope) {
|
1932
1971
|
openScope.isOpen = false;
|
1933
1972
|
}
|
1934
1973
|
|
1935
1974
|
openScope = dropdownScope;
|
1936
1975
|
};
|
1937
1976
|
|
1938
|
-
this.close = function(
|
1939
|
-
if (
|
1977
|
+
this.close = function(dropdownScope) {
|
1978
|
+
if (openScope === dropdownScope) {
|
1940
1979
|
openScope = null;
|
1941
1980
|
$document.unbind('click', closeDropdown);
|
1942
1981
|
$document.unbind('keydown', keybindFilter);
|
1943
1982
|
}
|
1944
1983
|
};
|
1945
1984
|
|
1946
|
-
var closeDropdown = function(
|
1985
|
+
var closeDropdown = function(evt) {
|
1947
1986
|
// This method may still be called during the same mouse event that
|
1948
1987
|
// unbound this event handler. So check openScope before proceeding.
|
1949
1988
|
if (!openScope) { return; }
|
1950
1989
|
|
1951
|
-
if(
|
1990
|
+
if (evt && openScope.getAutoClose() === 'disabled') { return ; }
|
1952
1991
|
|
1953
1992
|
var toggleElement = openScope.getToggleElement();
|
1954
|
-
if (
|
1993
|
+
if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
|
1955
1994
|
return;
|
1956
1995
|
}
|
1957
1996
|
|
@@ -1968,12 +2007,11 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
1968
2007
|
}
|
1969
2008
|
};
|
1970
2009
|
|
1971
|
-
var keybindFilter = function(
|
1972
|
-
if (
|
2010
|
+
var keybindFilter = function(evt) {
|
2011
|
+
if (evt.which === 27) {
|
1973
2012
|
openScope.focusToggleElement();
|
1974
2013
|
closeDropdown();
|
1975
|
-
}
|
1976
|
-
else if ( openScope.isKeynavEnabled() && /(38|40)/.test(evt.which) && openScope.isOpen ) {
|
2014
|
+
} else if (openScope.isKeynavEnabled() && /(38|40)/.test(evt.which) && openScope.isOpen) {
|
1977
2015
|
evt.preventDefault();
|
1978
2016
|
evt.stopPropagation();
|
1979
2017
|
openScope.focusDropdownEntry(evt.which);
|
@@ -1984,19 +2022,20 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
1984
2022
|
.controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', '$position', '$document', '$compile', '$templateRequest', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate, $position, $document, $compile, $templateRequest) {
|
1985
2023
|
var self = this,
|
1986
2024
|
scope = $scope.$new(), // create a child scope so we are not polluting original one
|
1987
|
-
|
2025
|
+
templateScope,
|
1988
2026
|
openClass = dropdownConfig.openClass,
|
1989
2027
|
getIsOpen,
|
1990
2028
|
setIsOpen = angular.noop,
|
1991
2029
|
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
|
1992
2030
|
appendToBody = false,
|
1993
|
-
keynavEnabled =false,
|
1994
|
-
selectedOption = null
|
2031
|
+
keynavEnabled = false,
|
2032
|
+
selectedOption = null,
|
2033
|
+
body = $document.find('body');
|
1995
2034
|
|
1996
|
-
this.init = function(
|
2035
|
+
this.init = function(element) {
|
1997
2036
|
self.$element = element;
|
1998
2037
|
|
1999
|
-
if (
|
2038
|
+
if ($attrs.isOpen) {
|
2000
2039
|
getIsOpen = $parse($attrs.isOpen);
|
2001
2040
|
setIsOpen = getIsOpen.assign;
|
2002
2041
|
|
@@ -2008,15 +2047,16 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2008
2047
|
appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
|
2009
2048
|
keynavEnabled = angular.isDefined($attrs.keyboardNav);
|
2010
2049
|
|
2011
|
-
if (
|
2012
|
-
|
2050
|
+
if (appendToBody && self.dropdownMenu) {
|
2051
|
+
body.append(self.dropdownMenu);
|
2052
|
+
body.addClass('dropdown');
|
2013
2053
|
element.on('$destroy', function handleDestroyEvent() {
|
2014
2054
|
self.dropdownMenu.remove();
|
2015
2055
|
});
|
2016
2056
|
}
|
2017
2057
|
};
|
2018
2058
|
|
2019
|
-
this.toggle = function(
|
2059
|
+
this.toggle = function(open) {
|
2020
2060
|
return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
|
2021
2061
|
};
|
2022
2062
|
|
@@ -2048,7 +2088,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2048
2088
|
|
2049
2089
|
switch (keyCode) {
|
2050
2090
|
case (40): {
|
2051
|
-
if (
|
2091
|
+
if (!angular.isNumber(self.selectedOption)) {
|
2052
2092
|
self.selectedOption = 0;
|
2053
2093
|
} else {
|
2054
2094
|
self.selectedOption = (self.selectedOption === elems.length -1 ?
|
@@ -2058,12 +2098,11 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2058
2098
|
break;
|
2059
2099
|
}
|
2060
2100
|
case (38): {
|
2061
|
-
if (
|
2062
|
-
|
2101
|
+
if (!angular.isNumber(self.selectedOption)) {
|
2102
|
+
self.selectedOption = elems.length - 1;
|
2063
2103
|
} else {
|
2064
|
-
self.selectedOption =
|
2065
|
-
0 :
|
2066
|
-
self.selectedOption - 1);
|
2104
|
+
self.selectedOption = self.selectedOption === 0 ?
|
2105
|
+
0 : self.selectedOption - 1;
|
2067
2106
|
}
|
2068
2107
|
break;
|
2069
2108
|
}
|
@@ -2076,38 +2115,40 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2076
2115
|
};
|
2077
2116
|
|
2078
2117
|
scope.focusToggleElement = function() {
|
2079
|
-
if (
|
2118
|
+
if (self.toggleElement) {
|
2080
2119
|
self.toggleElement[0].focus();
|
2081
2120
|
}
|
2082
2121
|
};
|
2083
2122
|
|
2084
|
-
scope.$watch('isOpen', function(
|
2123
|
+
scope.$watch('isOpen', function(isOpen, wasOpen) {
|
2085
2124
|
if (appendToBody && self.dropdownMenu) {
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
2125
|
+
var pos = $position.positionElements(self.$element, self.dropdownMenu, 'bottom-left', true);
|
2126
|
+
var css = {
|
2127
|
+
top: pos.top + 'px',
|
2128
|
+
display: isOpen ? 'block' : 'none'
|
2129
|
+
};
|
2091
2130
|
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
2095
|
-
|
2096
|
-
|
2097
|
-
|
2098
|
-
|
2099
|
-
|
2131
|
+
var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
|
2132
|
+
if (!rightalign) {
|
2133
|
+
css.left = pos.left + 'px';
|
2134
|
+
css.right = 'auto';
|
2135
|
+
} else {
|
2136
|
+
css.left = 'auto';
|
2137
|
+
css.right = (window.innerWidth - (pos.left + self.$element.prop('offsetWidth'))) + 'px';
|
2138
|
+
}
|
2100
2139
|
|
2101
|
-
|
2140
|
+
self.dropdownMenu.css(css);
|
2102
2141
|
}
|
2103
2142
|
|
2104
|
-
|
2105
|
-
|
2106
|
-
|
2107
|
-
|
2143
|
+
var openContainer = appendToBody ? body : self.$element;
|
2144
|
+
|
2145
|
+
$animate[isOpen ? 'addClass' : 'removeClass'](openContainer, openClass).then(function() {
|
2146
|
+
if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
|
2147
|
+
toggleInvoker($scope, { open: !!isOpen });
|
2148
|
+
}
|
2108
2149
|
});
|
2109
2150
|
|
2110
|
-
if (
|
2151
|
+
if (isOpen) {
|
2111
2152
|
if (self.dropdownMenuTemplateUrl) {
|
2112
2153
|
$templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
|
2113
2154
|
templateScope = scope.$new();
|
@@ -2120,7 +2161,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2120
2161
|
}
|
2121
2162
|
|
2122
2163
|
scope.focusToggleElement();
|
2123
|
-
dropdownService.open(
|
2164
|
+
dropdownService.open(scope);
|
2124
2165
|
} else {
|
2125
2166
|
if (self.dropdownMenuTemplateUrl) {
|
2126
2167
|
if (templateScope) {
|
@@ -2131,7 +2172,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2131
2172
|
self.dropdownMenu = newEl;
|
2132
2173
|
}
|
2133
2174
|
|
2134
|
-
dropdownService.close(
|
2175
|
+
dropdownService.close(scope);
|
2135
2176
|
self.selectedOption = null;
|
2136
2177
|
}
|
2137
2178
|
|
@@ -2146,9 +2187,10 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2146
2187
|
}
|
2147
2188
|
});
|
2148
2189
|
|
2149
|
-
$scope.$on('$destroy', function() {
|
2190
|
+
var offDestroy = $scope.$on('$destroy', function() {
|
2150
2191
|
scope.$destroy();
|
2151
2192
|
});
|
2193
|
+
scope.$on('$destroy', offDestroy);
|
2152
2194
|
}])
|
2153
2195
|
|
2154
2196
|
.directive('dropdown', function() {
|
@@ -2187,9 +2229,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2187
2229
|
link: function (scope, element, attrs, dropdownCtrl) {
|
2188
2230
|
|
2189
2231
|
element.bind('keydown', function(e) {
|
2190
|
-
|
2191
2232
|
if ([38, 40].indexOf(e.which) !== -1) {
|
2192
|
-
|
2193
2233
|
e.preventDefault();
|
2194
2234
|
e.stopPropagation();
|
2195
2235
|
|
@@ -2197,24 +2237,28 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2197
2237
|
|
2198
2238
|
switch (e.which) {
|
2199
2239
|
case (40): { // Down
|
2200
|
-
if (
|
2240
|
+
if (!angular.isNumber(dropdownCtrl.selectedOption)) {
|
2201
2241
|
dropdownCtrl.selectedOption = 0;
|
2202
2242
|
} else {
|
2203
|
-
dropdownCtrl.selectedOption =
|
2243
|
+
dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === elems.length -1 ?
|
2244
|
+
dropdownCtrl.selectedOption : dropdownCtrl.selectedOption + 1;
|
2204
2245
|
}
|
2205
|
-
|
2246
|
+
break;
|
2206
2247
|
}
|
2207
|
-
break;
|
2208
2248
|
case (38): { // Up
|
2209
|
-
|
2249
|
+
if (!angular.isNumber(dropdownCtrl.selectedOption)) {
|
2250
|
+
dropdownCtrl.selectedOption = elems.length - 1;
|
2251
|
+
} else {
|
2252
|
+
dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === 0 ?
|
2253
|
+
0 : dropdownCtrl.selectedOption - 1;
|
2254
|
+
}
|
2255
|
+
break;
|
2210
2256
|
}
|
2211
|
-
break;
|
2212
2257
|
}
|
2213
2258
|
elems[dropdownCtrl.selectedOption].focus();
|
2214
2259
|
}
|
2215
2260
|
});
|
2216
2261
|
}
|
2217
|
-
|
2218
2262
|
};
|
2219
2263
|
})
|
2220
2264
|
|
@@ -2222,7 +2266,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2222
2266
|
return {
|
2223
2267
|
require: '?^dropdown',
|
2224
2268
|
link: function(scope, element, attrs, dropdownCtrl) {
|
2225
|
-
if (
|
2269
|
+
if (!dropdownCtrl) {
|
2226
2270
|
return;
|
2227
2271
|
}
|
2228
2272
|
|
@@ -2233,7 +2277,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
|
2233
2277
|
var toggleDropdown = function(event) {
|
2234
2278
|
event.preventDefault();
|
2235
2279
|
|
2236
|
-
if (
|
2280
|
+
if (!element.hasClass('disabled') && !attrs.disabled) {
|
2237
2281
|
scope.$apply(function() {
|
2238
2282
|
dropdownCtrl.toggle();
|
2239
2283
|
});
|
@@ -2261,19 +2305,19 @@ angular.module('ui.bootstrap.modal', [])
|
|
2261
2305
|
* A helper, internal data structure that acts as a map but also allows getting / removing
|
2262
2306
|
* elements in the LIFO order
|
2263
2307
|
*/
|
2264
|
-
.factory('$$stackedMap', function
|
2308
|
+
.factory('$$stackedMap', function() {
|
2265
2309
|
return {
|
2266
|
-
createNew: function
|
2310
|
+
createNew: function() {
|
2267
2311
|
var stack = [];
|
2268
2312
|
|
2269
2313
|
return {
|
2270
|
-
add: function
|
2314
|
+
add: function(key, value) {
|
2271
2315
|
stack.push({
|
2272
2316
|
key: key,
|
2273
2317
|
value: value
|
2274
2318
|
});
|
2275
2319
|
},
|
2276
|
-
get: function
|
2320
|
+
get: function(key) {
|
2277
2321
|
for (var i = 0; i < stack.length; i++) {
|
2278
2322
|
if (key == stack[i].key) {
|
2279
2323
|
return stack[i];
|
@@ -2287,10 +2331,10 @@ angular.module('ui.bootstrap.modal', [])
|
|
2287
2331
|
}
|
2288
2332
|
return keys;
|
2289
2333
|
},
|
2290
|
-
top: function
|
2334
|
+
top: function() {
|
2291
2335
|
return stack[stack.length - 1];
|
2292
2336
|
},
|
2293
|
-
remove: function
|
2337
|
+
remove: function(key) {
|
2294
2338
|
var idx = -1;
|
2295
2339
|
for (var i = 0; i < stack.length; i++) {
|
2296
2340
|
if (key == stack[i].key) {
|
@@ -2300,10 +2344,10 @@ angular.module('ui.bootstrap.modal', [])
|
|
2300
2344
|
}
|
2301
2345
|
return stack.splice(idx, 1)[0];
|
2302
2346
|
},
|
2303
|
-
removeTop: function
|
2347
|
+
removeTop: function() {
|
2304
2348
|
return stack.splice(stack.length - 1, 1)[0];
|
2305
2349
|
},
|
2306
|
-
length: function
|
2350
|
+
length: function() {
|
2307
2351
|
return stack.length;
|
2308
2352
|
}
|
2309
2353
|
};
|
@@ -2311,12 +2355,67 @@ angular.module('ui.bootstrap.modal', [])
|
|
2311
2355
|
};
|
2312
2356
|
})
|
2313
2357
|
|
2358
|
+
/**
|
2359
|
+
* A helper, internal data structure that stores all references attached to key
|
2360
|
+
*/
|
2361
|
+
.factory('$$multiMap', function() {
|
2362
|
+
return {
|
2363
|
+
createNew: function() {
|
2364
|
+
var map = {};
|
2365
|
+
|
2366
|
+
return {
|
2367
|
+
entries: function() {
|
2368
|
+
return Object.keys(map).map(function(key) {
|
2369
|
+
return {
|
2370
|
+
key: key,
|
2371
|
+
value: map[key]
|
2372
|
+
};
|
2373
|
+
});
|
2374
|
+
},
|
2375
|
+
get: function(key) {
|
2376
|
+
return map[key];
|
2377
|
+
},
|
2378
|
+
hasKey: function(key) {
|
2379
|
+
return !!map[key];
|
2380
|
+
},
|
2381
|
+
keys: function() {
|
2382
|
+
return Object.keys(map);
|
2383
|
+
},
|
2384
|
+
put: function(key, value) {
|
2385
|
+
if (!map[key]) {
|
2386
|
+
map[key] = [];
|
2387
|
+
}
|
2388
|
+
|
2389
|
+
map[key].push(value);
|
2390
|
+
},
|
2391
|
+
remove: function(key, value) {
|
2392
|
+
var values = map[key];
|
2393
|
+
|
2394
|
+
if (!values) {
|
2395
|
+
return;
|
2396
|
+
}
|
2397
|
+
|
2398
|
+
var idx = values.indexOf(value);
|
2399
|
+
|
2400
|
+
if (idx !== -1) {
|
2401
|
+
values.splice(idx, 1);
|
2402
|
+
}
|
2403
|
+
|
2404
|
+
if (!values.length) {
|
2405
|
+
delete map[key];
|
2406
|
+
}
|
2407
|
+
}
|
2408
|
+
};
|
2409
|
+
}
|
2410
|
+
};
|
2411
|
+
})
|
2412
|
+
|
2314
2413
|
/**
|
2315
2414
|
* A helper directive for the $modal service. It creates a backdrop element.
|
2316
2415
|
*/
|
2317
2416
|
.directive('modalBackdrop', [
|
2318
2417
|
'$animate', '$injector', '$modalStack',
|
2319
|
-
function
|
2418
|
+
function($animate , $injector, $modalStack) {
|
2320
2419
|
var $animateCss = null;
|
2321
2420
|
|
2322
2421
|
if ($injector.has('$animateCss')) {
|
@@ -2327,7 +2426,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2327
2426
|
restrict: 'EA',
|
2328
2427
|
replace: true,
|
2329
2428
|
templateUrl: 'template/modal/backdrop.html',
|
2330
|
-
compile: function
|
2429
|
+
compile: function(tElement, tAttrs) {
|
2331
2430
|
tElement.addClass(tAttrs.backdropClass);
|
2332
2431
|
return linkFn;
|
2333
2432
|
}
|
@@ -2343,7 +2442,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2343
2442
|
$animate.addClass(element, attrs.modalInClass);
|
2344
2443
|
}
|
2345
2444
|
|
2346
|
-
scope.$on($modalStack.NOW_CLOSING_EVENT, function
|
2445
|
+
scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
|
2347
2446
|
var done = setIsAsync();
|
2348
2447
|
if ($animateCss) {
|
2349
2448
|
$animateCss(element, {
|
@@ -2359,7 +2458,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2359
2458
|
|
2360
2459
|
.directive('modalWindow', [
|
2361
2460
|
'$modalStack', '$q', '$animate', '$injector',
|
2362
|
-
function
|
2461
|
+
function($modalStack , $q , $animate, $injector) {
|
2363
2462
|
var $animateCss = null;
|
2364
2463
|
|
2365
2464
|
if ($injector.has('$animateCss')) {
|
@@ -2376,13 +2475,13 @@ angular.module('ui.bootstrap.modal', [])
|
|
2376
2475
|
templateUrl: function(tElement, tAttrs) {
|
2377
2476
|
return tAttrs.templateUrl || 'template/modal/window.html';
|
2378
2477
|
},
|
2379
|
-
link: function
|
2478
|
+
link: function(scope, element, attrs) {
|
2380
2479
|
element.addClass(attrs.windowClass || '');
|
2381
2480
|
scope.size = attrs.size;
|
2382
2481
|
|
2383
|
-
scope.close = function
|
2482
|
+
scope.close = function(evt) {
|
2384
2483
|
var modal = $modalStack.getTop();
|
2385
|
-
if (modal && modal.value.backdrop && modal.value.backdrop
|
2484
|
+
if (modal && modal.value.backdrop && modal.value.backdrop !== 'static' && (evt.target === evt.currentTarget)) {
|
2386
2485
|
evt.preventDefault();
|
2387
2486
|
evt.stopPropagation();
|
2388
2487
|
$modalStack.dismiss(modal.key, 'backdrop click');
|
@@ -2398,23 +2497,25 @@ angular.module('ui.bootstrap.modal', [])
|
|
2398
2497
|
var modalRenderDeferObj = $q.defer();
|
2399
2498
|
// Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
|
2400
2499
|
// In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
|
2401
|
-
attrs.$observe('modalRender', function
|
2500
|
+
attrs.$observe('modalRender', function(value) {
|
2402
2501
|
if (value == 'true') {
|
2403
2502
|
modalRenderDeferObj.resolve();
|
2404
2503
|
}
|
2405
2504
|
});
|
2406
2505
|
|
2407
|
-
modalRenderDeferObj.promise.then(function
|
2506
|
+
modalRenderDeferObj.promise.then(function() {
|
2507
|
+
var animationPromise = null;
|
2508
|
+
|
2408
2509
|
if (attrs.modalInClass) {
|
2409
2510
|
if ($animateCss) {
|
2410
|
-
$animateCss(element, {
|
2511
|
+
animationPromise = $animateCss(element, {
|
2411
2512
|
addClass: attrs.modalInClass
|
2412
2513
|
}).start();
|
2413
2514
|
} else {
|
2414
|
-
$animate.addClass(element, attrs.modalInClass);
|
2515
|
+
animationPromise = $animate.addClass(element, attrs.modalInClass);
|
2415
2516
|
}
|
2416
2517
|
|
2417
|
-
scope.$on($modalStack.NOW_CLOSING_EVENT, function
|
2518
|
+
scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
|
2418
2519
|
var done = setIsAsync();
|
2419
2520
|
if ($animateCss) {
|
2420
2521
|
$animateCss(element, {
|
@@ -2426,20 +2527,23 @@ angular.module('ui.bootstrap.modal', [])
|
|
2426
2527
|
});
|
2427
2528
|
}
|
2428
2529
|
|
2429
|
-
|
2430
|
-
|
2431
|
-
|
2432
|
-
|
2433
|
-
|
2434
|
-
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2438
|
-
|
2439
|
-
|
2440
|
-
|
2441
|
-
|
2442
|
-
|
2530
|
+
|
2531
|
+
$q.when(animationPromise).then(function() {
|
2532
|
+
var inputsWithAutofocus = element[0].querySelectorAll('[autofocus]');
|
2533
|
+
/**
|
2534
|
+
* Auto-focusing of a freshly-opened modal element causes any child elements
|
2535
|
+
* with the autofocus attribute to lose focus. This is an issue on touch
|
2536
|
+
* based devices which will show and then hide the onscreen keyboard.
|
2537
|
+
* Attempts to refocus the autofocus element via JavaScript will not reopen
|
2538
|
+
* the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
|
2539
|
+
* the modal element if the modal does not contain an autofocus element.
|
2540
|
+
*/
|
2541
|
+
if (inputsWithAutofocus.length) {
|
2542
|
+
inputsWithAutofocus[0].focus();
|
2543
|
+
} else {
|
2544
|
+
element[0].focus();
|
2545
|
+
}
|
2546
|
+
});
|
2443
2547
|
|
2444
2548
|
// Notify {@link $modalStack} that modal is rendered.
|
2445
2549
|
var modal = $modalStack.getTop();
|
@@ -2454,7 +2558,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2454
2558
|
.directive('modalAnimationClass', [
|
2455
2559
|
function () {
|
2456
2560
|
return {
|
2457
|
-
compile: function
|
2561
|
+
compile: function(tElement, tAttrs) {
|
2458
2562
|
if (tAttrs.modalAnimation) {
|
2459
2563
|
tElement.addClass(tAttrs.modalAnimationClass);
|
2460
2564
|
}
|
@@ -2462,7 +2566,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2462
2566
|
};
|
2463
2567
|
}])
|
2464
2568
|
|
2465
|
-
.directive('modalTransclude', function
|
2569
|
+
.directive('modalTransclude', function() {
|
2466
2570
|
return {
|
2467
2571
|
link: function($scope, $element, $attrs, controller, $transclude) {
|
2468
2572
|
$transclude($scope.$parent, function(clone) {
|
@@ -2477,10 +2581,12 @@ angular.module('ui.bootstrap.modal', [])
|
|
2477
2581
|
'$animate', '$timeout', '$document', '$compile', '$rootScope',
|
2478
2582
|
'$q',
|
2479
2583
|
'$injector',
|
2584
|
+
'$$multiMap',
|
2480
2585
|
'$$stackedMap',
|
2481
|
-
function
|
2586
|
+
function($animate , $timeout , $document , $compile , $rootScope ,
|
2482
2587
|
$q,
|
2483
2588
|
$injector,
|
2589
|
+
$$multiMap,
|
2484
2590
|
$$stackedMap) {
|
2485
2591
|
var $animateCss = null;
|
2486
2592
|
|
@@ -2492,6 +2598,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2492
2598
|
|
2493
2599
|
var backdropDomEl, backdropScope;
|
2494
2600
|
var openedWindows = $$stackedMap.createNew();
|
2601
|
+
var openedClasses = $$multiMap.createNew();
|
2495
2602
|
var $modalStack = {
|
2496
2603
|
NOW_CLOSING_EVENT: 'modal.stack.now-closing'
|
2497
2604
|
};
|
@@ -2521,7 +2628,6 @@ angular.module('ui.bootstrap.modal', [])
|
|
2521
2628
|
});
|
2522
2629
|
|
2523
2630
|
function removeModalWindow(modalInstance, elementToReceiveFocus) {
|
2524
|
-
|
2525
2631
|
var body = $document.find('body').eq(0);
|
2526
2632
|
var modalWindow = openedWindows.get(modalInstance).value;
|
2527
2633
|
|
@@ -2529,7 +2635,9 @@ angular.module('ui.bootstrap.modal', [])
|
|
2529
2635
|
openedWindows.remove(modalInstance);
|
2530
2636
|
|
2531
2637
|
removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
|
2532
|
-
|
2638
|
+
var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
|
2639
|
+
openedClasses.remove(modalBodyClass, modalInstance);
|
2640
|
+
body.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
|
2533
2641
|
});
|
2534
2642
|
checkRemoveBackdrop();
|
2535
2643
|
|
@@ -2545,7 +2653,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2545
2653
|
//remove backdrop if no longer needed
|
2546
2654
|
if (backdropDomEl && backdropIndex() == -1) {
|
2547
2655
|
var backdropScopeRef = backdropScope;
|
2548
|
-
removeAfterAnimate(backdropDomEl, backdropScope, function
|
2656
|
+
removeAfterAnimate(backdropDomEl, backdropScope, function() {
|
2549
2657
|
backdropScopeRef = null;
|
2550
2658
|
});
|
2551
2659
|
backdropDomEl = undefined;
|
@@ -2556,7 +2664,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2556
2664
|
function removeAfterAnimate(domEl, scope, done) {
|
2557
2665
|
var asyncDeferred;
|
2558
2666
|
var asyncPromise = null;
|
2559
|
-
var setIsAsync = function
|
2667
|
+
var setIsAsync = function() {
|
2560
2668
|
if (!asyncDeferred) {
|
2561
2669
|
asyncDeferred = $q.defer();
|
2562
2670
|
asyncPromise = asyncDeferred.promise;
|
@@ -2595,7 +2703,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2595
2703
|
}
|
2596
2704
|
}
|
2597
2705
|
|
2598
|
-
$document.bind('keydown', function
|
2706
|
+
$document.bind('keydown', function(evt) {
|
2599
2707
|
if (evt.isDefaultPrevented()) {
|
2600
2708
|
return evt;
|
2601
2709
|
}
|
@@ -2605,7 +2713,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2605
2713
|
switch (evt.which){
|
2606
2714
|
case 27: {
|
2607
2715
|
evt.preventDefault();
|
2608
|
-
$rootScope.$apply(function
|
2716
|
+
$rootScope.$apply(function() {
|
2609
2717
|
$modalStack.dismiss(modal.key, 'escape key press');
|
2610
2718
|
});
|
2611
2719
|
break;
|
@@ -2633,9 +2741,9 @@ angular.module('ui.bootstrap.modal', [])
|
|
2633
2741
|
}
|
2634
2742
|
});
|
2635
2743
|
|
2636
|
-
$modalStack.open = function
|
2637
|
-
|
2638
|
-
|
2744
|
+
$modalStack.open = function(modalInstance, modal) {
|
2745
|
+
var modalOpener = $document[0].activeElement,
|
2746
|
+
modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
|
2639
2747
|
|
2640
2748
|
openedWindows.add(modalInstance, {
|
2641
2749
|
deferred: modal.deferred,
|
@@ -2646,6 +2754,8 @@ angular.module('ui.bootstrap.modal', [])
|
|
2646
2754
|
openedClass: modal.openedClass
|
2647
2755
|
});
|
2648
2756
|
|
2757
|
+
openedClasses.put(modalBodyClass, modalInstance);
|
2758
|
+
|
2649
2759
|
var body = $document.find('body').eq(0),
|
2650
2760
|
currBackdropIndex = backdropIndex();
|
2651
2761
|
|
@@ -2677,7 +2787,8 @@ angular.module('ui.bootstrap.modal', [])
|
|
2677
2787
|
openedWindows.top().value.modalDomEl = modalDomEl;
|
2678
2788
|
openedWindows.top().value.modalOpener = modalOpener;
|
2679
2789
|
body.append(modalDomEl);
|
2680
|
-
body.addClass(
|
2790
|
+
body.addClass(modalBodyClass);
|
2791
|
+
|
2681
2792
|
$modalStack.clearFocusListCache();
|
2682
2793
|
};
|
2683
2794
|
|
@@ -2685,7 +2796,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2685
2796
|
return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
|
2686
2797
|
}
|
2687
2798
|
|
2688
|
-
$modalStack.close = function
|
2799
|
+
$modalStack.close = function(modalInstance, result) {
|
2689
2800
|
var modalWindow = openedWindows.get(modalInstance);
|
2690
2801
|
if (modalWindow && broadcastClosing(modalWindow, result, true)) {
|
2691
2802
|
modalWindow.value.modalScope.$$uibDestructionScheduled = true;
|
@@ -2696,7 +2807,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2696
2807
|
return !modalWindow;
|
2697
2808
|
};
|
2698
2809
|
|
2699
|
-
$modalStack.dismiss = function
|
2810
|
+
$modalStack.dismiss = function(modalInstance, reason) {
|
2700
2811
|
var modalWindow = openedWindows.get(modalInstance);
|
2701
2812
|
if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
|
2702
2813
|
modalWindow.value.modalScope.$$uibDestructionScheduled = true;
|
@@ -2707,18 +2818,18 @@ angular.module('ui.bootstrap.modal', [])
|
|
2707
2818
|
return !modalWindow;
|
2708
2819
|
};
|
2709
2820
|
|
2710
|
-
$modalStack.dismissAll = function
|
2821
|
+
$modalStack.dismissAll = function(reason) {
|
2711
2822
|
var topModal = this.getTop();
|
2712
2823
|
while (topModal && this.dismiss(topModal.key, reason)) {
|
2713
2824
|
topModal = this.getTop();
|
2714
2825
|
}
|
2715
2826
|
};
|
2716
2827
|
|
2717
|
-
$modalStack.getTop = function
|
2828
|
+
$modalStack.getTop = function() {
|
2718
2829
|
return openedWindows.top();
|
2719
2830
|
};
|
2720
2831
|
|
2721
|
-
$modalStack.modalRendered = function
|
2832
|
+
$modalStack.modalRendered = function(modalInstance) {
|
2722
2833
|
var modalWindow = openedWindows.get(modalInstance);
|
2723
2834
|
if (modalWindow) {
|
2724
2835
|
modalWindow.value.renderDeferred.resolve();
|
@@ -2773,8 +2884,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2773
2884
|
return $modalStack;
|
2774
2885
|
}])
|
2775
2886
|
|
2776
|
-
.provider('$modal', function
|
2777
|
-
|
2887
|
+
.provider('$modal', function() {
|
2778
2888
|
var $modalProvider = {
|
2779
2889
|
options: {
|
2780
2890
|
animation: true,
|
@@ -2783,7 +2893,6 @@ angular.module('ui.bootstrap.modal', [])
|
|
2783
2893
|
},
|
2784
2894
|
$get: ['$injector', '$rootScope', '$q', '$templateRequest', '$controller', '$modalStack',
|
2785
2895
|
function ($injector, $rootScope, $q, $templateRequest, $controller, $modalStack) {
|
2786
|
-
|
2787
2896
|
var $modal = {};
|
2788
2897
|
|
2789
2898
|
function getTemplatePromise(options) {
|
@@ -2793,16 +2902,23 @@ angular.module('ui.bootstrap.modal', [])
|
|
2793
2902
|
|
2794
2903
|
function getResolvePromises(resolves) {
|
2795
2904
|
var promisesArr = [];
|
2796
|
-
angular.forEach(resolves, function
|
2905
|
+
angular.forEach(resolves, function(value) {
|
2797
2906
|
if (angular.isFunction(value) || angular.isArray(value)) {
|
2798
2907
|
promisesArr.push($q.when($injector.invoke(value)));
|
2799
2908
|
} else if (angular.isString(value)) {
|
2800
2909
|
promisesArr.push($q.when($injector.get(value)));
|
2910
|
+
} else {
|
2911
|
+
promisesArr.push($q.when(value));
|
2801
2912
|
}
|
2802
2913
|
});
|
2803
2914
|
return promisesArr;
|
2804
2915
|
}
|
2805
2916
|
|
2917
|
+
var promiseChain = null;
|
2918
|
+
$modal.getPromiseChain = function() {
|
2919
|
+
return promiseChain;
|
2920
|
+
};
|
2921
|
+
|
2806
2922
|
$modal.open = function (modalOptions) {
|
2807
2923
|
|
2808
2924
|
var modalResultDeferred = $q.defer();
|
@@ -2834,63 +2950,70 @@ angular.module('ui.bootstrap.modal', [])
|
|
2834
2950
|
var templateAndResolvePromise =
|
2835
2951
|
$q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
|
2836
2952
|
|
2953
|
+
// Wait for the resolution of the existing promise chain.
|
2954
|
+
// Then switch to our own combined promise dependency (regardless of how the previous modal fared).
|
2955
|
+
// Then add to $modalStack and resolve opened.
|
2956
|
+
// Finally clean up the chain variable if no subsequent modal has overwritten it.
|
2957
|
+
var samePromise;
|
2958
|
+
samePromise = promiseChain = $q.all([promiseChain])
|
2959
|
+
.then(function() { return templateAndResolvePromise; }, function() { return templateAndResolvePromise; })
|
2960
|
+
.then(function resolveSuccess(tplAndVars) {
|
2961
|
+
|
2962
|
+
var modalScope = (modalOptions.scope || $rootScope).$new();
|
2963
|
+
modalScope.$close = modalInstance.close;
|
2964
|
+
modalScope.$dismiss = modalInstance.dismiss;
|
2965
|
+
|
2966
|
+
modalScope.$on('$destroy', function() {
|
2967
|
+
if (!modalScope.$$uibDestructionScheduled) {
|
2968
|
+
modalScope.$dismiss('$uibUnscheduledDestruction');
|
2969
|
+
}
|
2970
|
+
});
|
2837
2971
|
|
2838
|
-
|
2839
|
-
|
2840
|
-
var modalScope = (modalOptions.scope || $rootScope).$new();
|
2841
|
-
modalScope.$close = modalInstance.close;
|
2842
|
-
modalScope.$dismiss = modalInstance.dismiss;
|
2843
|
-
|
2844
|
-
modalScope.$on('$destroy', function() {
|
2845
|
-
if (!modalScope.$$uibDestructionScheduled) {
|
2846
|
-
modalScope.$dismiss('$uibUnscheduledDestruction');
|
2847
|
-
}
|
2848
|
-
});
|
2972
|
+
var ctrlInstance, ctrlLocals = {};
|
2973
|
+
var resolveIter = 1;
|
2849
2974
|
|
2850
|
-
|
2851
|
-
|
2975
|
+
//controllers
|
2976
|
+
if (modalOptions.controller) {
|
2977
|
+
ctrlLocals.$scope = modalScope;
|
2978
|
+
ctrlLocals.$modalInstance = modalInstance;
|
2979
|
+
angular.forEach(modalOptions.resolve, function(value, key) {
|
2980
|
+
ctrlLocals[key] = tplAndVars[resolveIter++];
|
2981
|
+
});
|
2852
2982
|
|
2853
|
-
|
2854
|
-
|
2855
|
-
|
2856
|
-
|
2857
|
-
|
2858
|
-
ctrlLocals[key] = tplAndVars[resolveIter++];
|
2859
|
-
});
|
2983
|
+
ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
|
2984
|
+
if (modalOptions.controllerAs) {
|
2985
|
+
if (modalOptions.bindToController) {
|
2986
|
+
angular.extend(ctrlInstance, modalScope);
|
2987
|
+
}
|
2860
2988
|
|
2861
|
-
|
2862
|
-
if (modalOptions.controllerAs) {
|
2863
|
-
if (modalOptions.bindToController) {
|
2864
|
-
angular.extend(ctrlInstance, modalScope);
|
2989
|
+
modalScope[modalOptions.controllerAs] = ctrlInstance;
|
2865
2990
|
}
|
2866
|
-
|
2867
|
-
modalScope[modalOptions.controllerAs] = ctrlInstance;
|
2868
2991
|
}
|
2869
|
-
}
|
2870
2992
|
|
2871
|
-
|
2872
|
-
|
2873
|
-
|
2874
|
-
|
2875
|
-
|
2876
|
-
|
2877
|
-
|
2878
|
-
|
2879
|
-
|
2880
|
-
|
2881
|
-
|
2882
|
-
|
2883
|
-
|
2884
|
-
|
2993
|
+
$modalStack.open(modalInstance, {
|
2994
|
+
scope: modalScope,
|
2995
|
+
deferred: modalResultDeferred,
|
2996
|
+
renderDeferred: modalRenderDeferred,
|
2997
|
+
content: tplAndVars[0],
|
2998
|
+
animation: modalOptions.animation,
|
2999
|
+
backdrop: modalOptions.backdrop,
|
3000
|
+
keyboard: modalOptions.keyboard,
|
3001
|
+
backdropClass: modalOptions.backdropClass,
|
3002
|
+
windowClass: modalOptions.windowClass,
|
3003
|
+
windowTemplateUrl: modalOptions.windowTemplateUrl,
|
3004
|
+
size: modalOptions.size,
|
3005
|
+
openedClass: modalOptions.openedClass
|
3006
|
+
});
|
3007
|
+
modalOpenedDeferred.resolve(true);
|
2885
3008
|
|
2886
3009
|
}, function resolveError(reason) {
|
2887
|
-
modalResultDeferred.reject(reason);
|
2888
|
-
});
|
2889
|
-
|
2890
|
-
templateAndResolvePromise.then(function () {
|
2891
|
-
modalOpenedDeferred.resolve(true);
|
2892
|
-
}, function (reason) {
|
2893
3010
|
modalOpenedDeferred.reject(reason);
|
3011
|
+
modalResultDeferred.reject(reason);
|
3012
|
+
})
|
3013
|
+
.finally(function() {
|
3014
|
+
if (promiseChain === samePromise) {
|
3015
|
+
promiseChain = null;
|
3016
|
+
}
|
2894
3017
|
});
|
2895
3018
|
|
2896
3019
|
return modalInstance;
|
@@ -2904,7 +3027,7 @@ angular.module('ui.bootstrap.modal', [])
|
|
2904
3027
|
});
|
2905
3028
|
|
2906
3029
|
angular.module('ui.bootstrap.pagination', [])
|
2907
|
-
.controller('PaginationController', ['$scope', '$attrs', '$parse', function
|
3030
|
+
.controller('PaginationController', ['$scope', '$attrs', '$parse', function($scope, $attrs, $parse) {
|
2908
3031
|
var self = this,
|
2909
3032
|
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
|
2910
3033
|
setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
|
@@ -2965,12 +3088,14 @@ angular.module('ui.bootstrap.pagination', [])
|
|
2965
3088
|
}
|
2966
3089
|
};
|
2967
3090
|
|
2968
|
-
$scope.getText = function(
|
3091
|
+
$scope.getText = function(key) {
|
2969
3092
|
return $scope[key + 'Text'] || self.config[key + 'Text'];
|
2970
3093
|
};
|
3094
|
+
|
2971
3095
|
$scope.noPrevious = function() {
|
2972
3096
|
return $scope.page === 1;
|
2973
3097
|
};
|
3098
|
+
|
2974
3099
|
$scope.noNext = function() {
|
2975
3100
|
return $scope.page === $scope.totalPages;
|
2976
3101
|
};
|
@@ -3041,11 +3166,11 @@ angular.module('ui.bootstrap.pagination', [])
|
|
3041
3166
|
|
3042
3167
|
// Default page limits
|
3043
3168
|
var startPage = 1, endPage = totalPages;
|
3044
|
-
var isMaxSized =
|
3169
|
+
var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
|
3045
3170
|
|
3046
3171
|
// recompute if maxSize
|
3047
|
-
if (
|
3048
|
-
if (
|
3172
|
+
if (isMaxSized) {
|
3173
|
+
if (rotate) {
|
3049
3174
|
// Current page is displayed in the middle of the visible ones
|
3050
3175
|
startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);
|
3051
3176
|
endPage = startPage + maxSize - 1;
|
@@ -3071,13 +3196,13 @@ angular.module('ui.bootstrap.pagination', [])
|
|
3071
3196
|
}
|
3072
3197
|
|
3073
3198
|
// Add links to move between page sets
|
3074
|
-
if (
|
3075
|
-
if (
|
3199
|
+
if (isMaxSized && ! rotate) {
|
3200
|
+
if (startPage > 1) {
|
3076
3201
|
var previousPageSet = makePage(startPage - 1, '...', false);
|
3077
3202
|
pages.unshift(previousPageSet);
|
3078
3203
|
}
|
3079
3204
|
|
3080
|
-
if (
|
3205
|
+
if (endPage < totalPages) {
|
3081
3206
|
var nextPageSet = makePage(endPage + 1, '...', false);
|
3082
3207
|
pages.push(nextPageSet);
|
3083
3208
|
}
|
@@ -3110,11 +3235,15 @@ angular.module('ui.bootstrap.pagination', [])
|
|
3110
3235
|
scope: {
|
3111
3236
|
totalItems: '=',
|
3112
3237
|
previousText: '@',
|
3113
|
-
nextText: '@'
|
3238
|
+
nextText: '@',
|
3239
|
+
ngDisabled: '='
|
3114
3240
|
},
|
3115
3241
|
require: ['pager', '?ngModel'],
|
3116
3242
|
controller: 'PaginationController',
|
3117
|
-
|
3243
|
+
controllerAs: 'pagination',
|
3244
|
+
templateUrl: function(element, attrs) {
|
3245
|
+
return attrs.templateUrl || 'template/pagination/pager.html';
|
3246
|
+
},
|
3118
3247
|
replace: true,
|
3119
3248
|
link: function(scope, element, attrs, ctrls) {
|
3120
3249
|
var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
@@ -3134,13 +3263,13 @@ angular.module('ui.bootstrap.pagination', [])
|
|
3134
3263
|
* function, placement as a function, inside, support for more triggers than
|
3135
3264
|
* just mouse enter/leave, html tooltips, and selector delegation.
|
3136
3265
|
*/
|
3137
|
-
angular.module(
|
3266
|
+
angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
|
3138
3267
|
|
3139
3268
|
/**
|
3140
3269
|
* The $tooltip service creates tooltip- and popover-like directives as well as
|
3141
3270
|
* houses global options for them.
|
3142
3271
|
*/
|
3143
|
-
.provider(
|
3272
|
+
.provider('$tooltip', function() {
|
3144
3273
|
// The default options tooltip and popover.
|
3145
3274
|
var defaultOptions = {
|
3146
3275
|
placement: 'top',
|
@@ -3153,7 +3282,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3153
3282
|
var triggerMap = {
|
3154
3283
|
'mouseenter': 'mouseleave',
|
3155
3284
|
'click': 'click',
|
3156
|
-
'focus': 'blur'
|
3285
|
+
'focus': 'blur',
|
3286
|
+
'none': ''
|
3157
3287
|
};
|
3158
3288
|
|
3159
3289
|
// The options specified to the provider globally.
|
@@ -3168,23 +3298,23 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3168
3298
|
* $tooltipProvider.options( { placement: 'left' } );
|
3169
3299
|
* });
|
3170
3300
|
*/
|
3171
|
-
|
3172
|
-
|
3173
|
-
|
3301
|
+
this.options = function(value) {
|
3302
|
+
angular.extend(globalOptions, value);
|
3303
|
+
};
|
3174
3304
|
|
3175
3305
|
/**
|
3176
3306
|
* This allows you to extend the set of trigger mappings available. E.g.:
|
3177
3307
|
*
|
3178
3308
|
* $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
|
3179
3309
|
*/
|
3180
|
-
this.setTriggers = function setTriggers
|
3181
|
-
angular.extend(
|
3310
|
+
this.setTriggers = function setTriggers(triggers) {
|
3311
|
+
angular.extend(triggerMap, triggers);
|
3182
3312
|
};
|
3183
3313
|
|
3184
3314
|
/**
|
3185
3315
|
* This is a helper function for translating camel-case to snake-case.
|
3186
3316
|
*/
|
3187
|
-
function snake_case(name){
|
3317
|
+
function snake_case(name) {
|
3188
3318
|
var regexp = /[A-Z]/g;
|
3189
3319
|
var separator = '-';
|
3190
3320
|
return name.replace(regexp, function(letter, pos) {
|
@@ -3196,9 +3326,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3196
3326
|
* Returns the actual instance of the $tooltip service.
|
3197
3327
|
* TODO support multiple triggers
|
3198
3328
|
*/
|
3199
|
-
this.$get = [
|
3200
|
-
return function $tooltip
|
3201
|
-
options = angular.extend(
|
3329
|
+
this.$get = ['$window', '$compile', '$timeout', '$document', '$position', '$interpolate', '$rootScope', '$parse', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse) {
|
3330
|
+
return function $tooltip(type, prefix, defaultTriggerShow, options) {
|
3331
|
+
options = angular.extend({}, defaultOptions, globalOptions, options);
|
3202
3332
|
|
3203
3333
|
/**
|
3204
3334
|
* Returns an object of show and hide triggers.
|
@@ -3214,7 +3344,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3214
3344
|
* undefined; otherwise, it uses the `triggerMap` value of the show
|
3215
3345
|
* trigger; else it will just use the show trigger.
|
3216
3346
|
*/
|
3217
|
-
function getTriggers
|
3347
|
+
function getTriggers(trigger) {
|
3218
3348
|
var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
|
3219
3349
|
var hide = show.map(function(trigger) {
|
3220
3350
|
return triggerMap[trigger] || trigger;
|
@@ -3225,7 +3355,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3225
3355
|
};
|
3226
3356
|
}
|
3227
3357
|
|
3228
|
-
var directiveName = snake_case(
|
3358
|
+
var directiveName = snake_case(type);
|
3229
3359
|
|
3230
3360
|
var startSym = $interpolate.startSymbol();
|
3231
3361
|
var endSym = $interpolate.endSymbol();
|
@@ -3245,33 +3375,45 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3245
3375
|
|
3246
3376
|
return {
|
3247
3377
|
restrict: 'EA',
|
3248
|
-
compile: function
|
3378
|
+
compile: function(tElem, tAttrs) {
|
3249
3379
|
var tooltipLinker = $compile( template );
|
3250
3380
|
|
3251
|
-
return function link
|
3381
|
+
return function link(scope, element, attrs, tooltipCtrl) {
|
3252
3382
|
var tooltip;
|
3253
3383
|
var tooltipLinkedScope;
|
3254
3384
|
var transitionTimeout;
|
3255
3385
|
var popupTimeout;
|
3256
|
-
var
|
3257
|
-
var
|
3258
|
-
var
|
3386
|
+
var positionTimeout;
|
3387
|
+
var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
|
3388
|
+
var triggers = getTriggers(undefined);
|
3389
|
+
var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
|
3259
3390
|
var ttScope = scope.$new(true);
|
3260
3391
|
var repositionScheduled = false;
|
3392
|
+
var isOpenExp = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
|
3261
3393
|
|
3262
|
-
var positionTooltip = function
|
3394
|
+
var positionTooltip = function() {
|
3263
3395
|
if (!tooltip) { return; }
|
3264
3396
|
|
3265
|
-
|
3266
|
-
|
3267
|
-
|
3397
|
+
if (!positionTimeout) {
|
3398
|
+
positionTimeout = $timeout(function() {
|
3399
|
+
// Reset the positioning and box size for correct width and height values.
|
3400
|
+
tooltip.css({ top: 0, left: 0, width: 'auto', height: 'auto' });
|
3268
3401
|
|
3269
|
-
|
3270
|
-
|
3271
|
-
|
3402
|
+
var ttBox = $position.position(tooltip);
|
3403
|
+
var ttCss = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
|
3404
|
+
ttCss.top += 'px';
|
3405
|
+
ttCss.left += 'px';
|
3406
|
+
|
3407
|
+
ttCss.width = ttBox.width + 'px';
|
3408
|
+
ttCss.height = ttBox.height + 'px';
|
3409
|
+
|
3410
|
+
// Now set the calculated positioning and size.
|
3411
|
+
tooltip.css(ttCss);
|
3272
3412
|
|
3273
|
-
|
3274
|
-
|
3413
|
+
positionTimeout = null;
|
3414
|
+
|
3415
|
+
}, 0, false);
|
3416
|
+
}
|
3275
3417
|
};
|
3276
3418
|
|
3277
3419
|
// Set up the correct scope to allow transclusion later
|
@@ -3281,8 +3423,8 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3281
3423
|
// TODO add ability to start tooltip opened
|
3282
3424
|
ttScope.isOpen = false;
|
3283
3425
|
|
3284
|
-
function toggleTooltipBind
|
3285
|
-
if (
|
3426
|
+
function toggleTooltipBind() {
|
3427
|
+
if (!ttScope.isOpen) {
|
3286
3428
|
showTooltipBind();
|
3287
3429
|
} else {
|
3288
3430
|
hideTooltipBind();
|
@@ -3291,21 +3433,20 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3291
3433
|
|
3292
3434
|
// Show the tooltip with delay if specified, otherwise show it immediately
|
3293
3435
|
function showTooltipBind() {
|
3294
|
-
if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {
|
3436
|
+
if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
|
3295
3437
|
return;
|
3296
3438
|
}
|
3297
3439
|
|
3298
3440
|
prepareTooltip();
|
3299
3441
|
|
3300
|
-
if (
|
3442
|
+
if (ttScope.popupDelay) {
|
3301
3443
|
// Do nothing if the tooltip was already scheduled to pop-up.
|
3302
3444
|
// This happens if show is triggered multiple times before any hide is triggered.
|
3303
3445
|
if (!popupTimeout) {
|
3304
|
-
popupTimeout = $timeout(
|
3305
|
-
popupTimeout.then(function(reposition){reposition();});
|
3446
|
+
popupTimeout = $timeout(show, ttScope.popupDelay, false);
|
3306
3447
|
}
|
3307
3448
|
} else {
|
3308
|
-
show()
|
3449
|
+
show();
|
3309
3450
|
}
|
3310
3451
|
}
|
3311
3452
|
|
@@ -3318,50 +3459,56 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3318
3459
|
|
3319
3460
|
// Show the tooltip popup element.
|
3320
3461
|
function show() {
|
3321
|
-
|
3322
3462
|
popupTimeout = null;
|
3323
3463
|
|
3324
3464
|
// If there is a pending remove transition, we must cancel it, lest the
|
3325
3465
|
// tooltip be mysteriously removed.
|
3326
|
-
if (
|
3327
|
-
$timeout.cancel(
|
3466
|
+
if (transitionTimeout) {
|
3467
|
+
$timeout.cancel(transitionTimeout);
|
3328
3468
|
transitionTimeout = null;
|
3329
3469
|
}
|
3330
3470
|
|
3331
3471
|
// Don't show empty tooltips.
|
3332
|
-
if (
|
3472
|
+
if (!(options.useContentExp ? ttScope.contentExp() : ttScope.content)) {
|
3333
3473
|
return angular.noop;
|
3334
3474
|
}
|
3335
3475
|
|
3336
3476
|
createTooltip();
|
3337
3477
|
|
3338
|
-
// Set the initial positioning.
|
3339
|
-
tooltip.css({ top: 0, left: 0, display: 'block' });
|
3340
|
-
|
3341
|
-
positionTooltip();
|
3342
|
-
|
3343
3478
|
// And show the tooltip.
|
3344
3479
|
ttScope.isOpen = true;
|
3345
|
-
|
3480
|
+
if (isOpenExp) {
|
3481
|
+
isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
|
3482
|
+
}
|
3483
|
+
|
3484
|
+
if (!$rootScope.$$phase) {
|
3485
|
+
ttScope.$apply(); // digest required as $apply is not called
|
3486
|
+
}
|
3346
3487
|
|
3347
|
-
|
3348
|
-
|
3349
|
-
|
3488
|
+
tooltip.css({ display: 'block' });
|
3489
|
+
|
3490
|
+
positionTooltip();
|
3350
3491
|
}
|
3351
3492
|
|
3352
3493
|
// Hide the tooltip popup element.
|
3353
3494
|
function hide() {
|
3354
3495
|
// First things first: we don't show it anymore.
|
3355
3496
|
ttScope.isOpen = false;
|
3497
|
+
if (isOpenExp) {
|
3498
|
+
isOpenExp.assign(ttScope.origScope, ttScope.isOpen);
|
3499
|
+
}
|
3356
3500
|
|
3357
3501
|
//if tooltip is going to be shown after delay, we must cancel this
|
3358
|
-
$timeout.cancel(
|
3502
|
+
$timeout.cancel(popupTimeout);
|
3359
3503
|
popupTimeout = null;
|
3360
3504
|
|
3505
|
+
$timeout.cancel(positionTimeout);
|
3506
|
+
positionTimeout = null;
|
3507
|
+
|
3361
3508
|
// And now we remove it from the DOM. However, if we have animation, we
|
3362
3509
|
// need to wait for it to expire beforehand.
|
3363
3510
|
// FIXME: this is a placeholder for a port of the transitions library.
|
3364
|
-
if (
|
3511
|
+
if (ttScope.animation) {
|
3365
3512
|
if (!transitionTimeout) {
|
3366
3513
|
transitionTimeout = $timeout(removeTooltip, 500);
|
3367
3514
|
}
|
@@ -3376,16 +3523,16 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3376
3523
|
removeTooltip();
|
3377
3524
|
}
|
3378
3525
|
tooltipLinkedScope = ttScope.$new();
|
3379
|
-
tooltip = tooltipLinker(tooltipLinkedScope, function
|
3380
|
-
if (
|
3381
|
-
$document.find(
|
3526
|
+
tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
|
3527
|
+
if (appendToBody) {
|
3528
|
+
$document.find('body').append(tooltip);
|
3382
3529
|
} else {
|
3383
|
-
element.after(
|
3530
|
+
element.after(tooltip);
|
3384
3531
|
}
|
3385
3532
|
});
|
3386
3533
|
|
3387
3534
|
if (options.useContentExp) {
|
3388
|
-
tooltipLinkedScope.$watch('contentExp()', function
|
3535
|
+
tooltipLinkedScope.$watch('contentExp()', function(val) {
|
3389
3536
|
if (!val && ttScope.isOpen) {
|
3390
3537
|
hide();
|
3391
3538
|
}
|
@@ -3396,7 +3543,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3396
3543
|
repositionScheduled = true;
|
3397
3544
|
tooltipLinkedScope.$$postDigest(function() {
|
3398
3545
|
repositionScheduled = false;
|
3399
|
-
|
3546
|
+
if (ttScope.isOpen) {
|
3547
|
+
positionTooltip();
|
3548
|
+
}
|
3400
3549
|
});
|
3401
3550
|
}
|
3402
3551
|
});
|
@@ -3422,7 +3571,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3422
3571
|
prepPopupDelay();
|
3423
3572
|
}
|
3424
3573
|
|
3425
|
-
ttScope.contentExp = function
|
3574
|
+
ttScope.contentExp = function() {
|
3426
3575
|
return scope.$eval(attrs[type]);
|
3427
3576
|
};
|
3428
3577
|
|
@@ -3430,57 +3579,64 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3430
3579
|
* Observe the relevant attributes.
|
3431
3580
|
*/
|
3432
3581
|
if (!options.useContentExp) {
|
3433
|
-
attrs.$observe(
|
3582
|
+
attrs.$observe(type, function(val) {
|
3434
3583
|
ttScope.content = val;
|
3435
3584
|
|
3436
3585
|
if (!val && ttScope.isOpen) {
|
3437
3586
|
hide();
|
3438
3587
|
} else {
|
3439
|
-
|
3588
|
+
positionTooltip();
|
3440
3589
|
}
|
3441
3590
|
});
|
3442
3591
|
}
|
3443
3592
|
|
3444
|
-
attrs.$observe(
|
3593
|
+
attrs.$observe('disabled', function(val) {
|
3445
3594
|
if (popupTimeout && val) {
|
3446
3595
|
$timeout.cancel(popupTimeout);
|
3596
|
+
popupTimeout = null;
|
3447
3597
|
}
|
3448
3598
|
|
3449
|
-
if (val && ttScope.isOpen
|
3599
|
+
if (val && ttScope.isOpen) {
|
3450
3600
|
hide();
|
3451
3601
|
}
|
3452
3602
|
});
|
3453
3603
|
|
3454
|
-
attrs.$observe(
|
3604
|
+
attrs.$observe(prefix + 'Title', function(val) {
|
3455
3605
|
ttScope.title = val;
|
3456
|
-
|
3606
|
+
positionTooltip();
|
3457
3607
|
});
|
3458
3608
|
|
3459
|
-
attrs.$observe(
|
3609
|
+
attrs.$observe(prefix + 'Placement', function() {
|
3460
3610
|
if (ttScope.isOpen) {
|
3461
|
-
|
3462
|
-
|
3463
|
-
show()();
|
3464
|
-
}, 0, false);
|
3611
|
+
prepPlacement();
|
3612
|
+
positionTooltip();
|
3465
3613
|
}
|
3466
3614
|
});
|
3467
3615
|
|
3616
|
+
if (isOpenExp) {
|
3617
|
+
scope.$watch(isOpenExp, function(val) {
|
3618
|
+
if (val !== ttScope.isOpen) {
|
3619
|
+
toggleTooltipBind();
|
3620
|
+
}
|
3621
|
+
});
|
3622
|
+
}
|
3623
|
+
|
3468
3624
|
function prepPopupClass() {
|
3469
3625
|
ttScope.popupClass = attrs[prefix + 'Class'];
|
3470
3626
|
}
|
3471
3627
|
|
3472
3628
|
function prepPlacement() {
|
3473
|
-
var val = attrs[
|
3474
|
-
ttScope.placement = angular.isDefined(
|
3629
|
+
var val = attrs[prefix + 'Placement'];
|
3630
|
+
ttScope.placement = angular.isDefined(val) ? val : options.placement;
|
3475
3631
|
}
|
3476
3632
|
|
3477
3633
|
function prepPopupDelay() {
|
3478
|
-
var val = attrs[
|
3479
|
-
var delay = parseInt(
|
3480
|
-
ttScope.popupDelay = !
|
3634
|
+
var val = attrs[prefix + 'PopupDelay'];
|
3635
|
+
var delay = parseInt(val, 10);
|
3636
|
+
ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
|
3481
3637
|
}
|
3482
3638
|
|
3483
|
-
var unregisterTriggers = function
|
3639
|
+
var unregisterTriggers = function() {
|
3484
3640
|
triggers.show.forEach(function(trigger) {
|
3485
3641
|
element.unbind(trigger, showTooltipBind);
|
3486
3642
|
});
|
@@ -3490,19 +3646,22 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3490
3646
|
};
|
3491
3647
|
|
3492
3648
|
function prepTriggers() {
|
3493
|
-
var val = attrs[
|
3649
|
+
var val = attrs[prefix + 'Trigger'];
|
3494
3650
|
unregisterTriggers();
|
3495
3651
|
|
3496
|
-
triggers = getTriggers(
|
3652
|
+
triggers = getTriggers(val);
|
3497
3653
|
|
3498
|
-
triggers.show
|
3499
|
-
|
3500
|
-
|
3501
|
-
|
3502
|
-
|
3503
|
-
|
3504
|
-
|
3505
|
-
|
3654
|
+
if (triggers.show !== 'none') {
|
3655
|
+
triggers.show.forEach(function(trigger, idx) {
|
3656
|
+
// Using raw addEventListener due to jqLite/jQuery bug - #4060
|
3657
|
+
if (trigger === triggers.hide[idx]) {
|
3658
|
+
element[0].addEventListener(trigger, toggleTooltipBind);
|
3659
|
+
} else if (trigger) {
|
3660
|
+
element[0].addEventListener(trigger, showTooltipBind);
|
3661
|
+
element[0].addEventListener(triggers.hide[idx], hideTooltipBind);
|
3662
|
+
}
|
3663
|
+
});
|
3664
|
+
}
|
3506
3665
|
}
|
3507
3666
|
prepTriggers();
|
3508
3667
|
|
@@ -3515,18 +3674,19 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3515
3674
|
// if a tooltip is attached to <body> we need to remove it on
|
3516
3675
|
// location change as its parent scope will probably not be destroyed
|
3517
3676
|
// by the change.
|
3518
|
-
if (
|
3519
|
-
scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess
|
3520
|
-
|
3521
|
-
|
3522
|
-
|
3523
|
-
|
3677
|
+
if (appendToBody) {
|
3678
|
+
scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() {
|
3679
|
+
if (ttScope.isOpen) {
|
3680
|
+
hide();
|
3681
|
+
}
|
3682
|
+
});
|
3524
3683
|
}
|
3525
3684
|
|
3526
3685
|
// Make sure tooltip is destroyed and removed.
|
3527
3686
|
scope.$on('$destroy', function onDestroyTooltip() {
|
3528
|
-
$timeout.cancel(
|
3529
|
-
$timeout.cancel(
|
3687
|
+
$timeout.cancel(transitionTimeout);
|
3688
|
+
$timeout.cancel(popupTimeout);
|
3689
|
+
$timeout.cancel(positionTimeout);
|
3530
3690
|
unregisterTriggers();
|
3531
3691
|
removeTooltip();
|
3532
3692
|
ttScope = null;
|
@@ -3539,11 +3699,11 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|
3539
3699
|
})
|
3540
3700
|
|
3541
3701
|
// This is mostly ngInclude code but with a custom scope
|
3542
|
-
.directive(
|
3702
|
+
.directive('tooltipTemplateTransclude', [
|
3543
3703
|
'$animate', '$sce', '$compile', '$templateRequest',
|
3544
3704
|
function ($animate , $sce , $compile , $templateRequest) {
|
3545
3705
|
return {
|
3546
|
-
link: function
|
3706
|
+
link: function(scope, elem, attrs) {
|
3547
3707
|
var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
|
3548
3708
|
|
3549
3709
|
var changeCounter = 0,
|
@@ -3569,7 +3729,7 @@ function ($animate , $sce , $compile , $templateRequest) {
|
|
3569
3729
|
}
|
3570
3730
|
};
|
3571
3731
|
|
3572
|
-
scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function
|
3732
|
+
scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function(src) {
|
3573
3733
|
var thisChangeId = ++changeCounter;
|
3574
3734
|
|
3575
3735
|
if (src) {
|
@@ -3611,10 +3771,10 @@ function ($animate , $sce , $compile , $templateRequest) {
|
|
3611
3771
|
* They must not be animated as they're expected to be present on the tooltip on
|
3612
3772
|
* initialization.
|
3613
3773
|
*/
|
3614
|
-
.directive('tooltipClasses', function
|
3774
|
+
.directive('tooltipClasses', function() {
|
3615
3775
|
return {
|
3616
3776
|
restrict: 'A',
|
3617
|
-
link: function
|
3777
|
+
link: function(scope, element, attrs) {
|
3618
3778
|
if (scope.placement) {
|
3619
3779
|
element.addClass(scope.placement);
|
3620
3780
|
}
|
@@ -3628,7 +3788,7 @@ function ($animate , $sce , $compile , $templateRequest) {
|
|
3628
3788
|
};
|
3629
3789
|
})
|
3630
3790
|
|
3631
|
-
.directive(
|
3791
|
+
.directive('tooltipPopup', function() {
|
3632
3792
|
return {
|
3633
3793
|
restrict: 'EA',
|
3634
3794
|
replace: true,
|
@@ -3637,11 +3797,11 @@ function ($animate , $sce , $compile , $templateRequest) {
|
|
3637
3797
|
};
|
3638
3798
|
})
|
3639
3799
|
|
3640
|
-
.directive(
|
3641
|
-
return $tooltip(
|
3800
|
+
.directive('tooltip', [ '$tooltip', function($tooltip) {
|
3801
|
+
return $tooltip('tooltip', 'tooltip', 'mouseenter');
|
3642
3802
|
}])
|
3643
3803
|
|
3644
|
-
.directive(
|
3804
|
+
.directive('tooltipTemplatePopup', function() {
|
3645
3805
|
return {
|
3646
3806
|
restrict: 'EA',
|
3647
3807
|
replace: true,
|
@@ -3651,13 +3811,13 @@ function ($animate , $sce , $compile , $templateRequest) {
|
|
3651
3811
|
};
|
3652
3812
|
})
|
3653
3813
|
|
3654
|
-
.directive(
|
3814
|
+
.directive('tooltipTemplate', ['$tooltip', function($tooltip) {
|
3655
3815
|
return $tooltip('tooltipTemplate', 'tooltip', 'mouseenter', {
|
3656
3816
|
useContentExp: true
|
3657
3817
|
});
|
3658
3818
|
}])
|
3659
3819
|
|
3660
|
-
.directive(
|
3820
|
+
.directive('tooltipHtmlPopup', function() {
|
3661
3821
|
return {
|
3662
3822
|
restrict: 'EA',
|
3663
3823
|
replace: true,
|
@@ -3666,7 +3826,7 @@ function ($animate , $sce , $compile , $templateRequest) {
|
|
3666
3826
|
};
|
3667
3827
|
})
|
3668
3828
|
|
3669
|
-
.directive(
|
3829
|
+
.directive('tooltipHtml', ['$tooltip', function($tooltip) {
|
3670
3830
|
return $tooltip('tooltipHtml', 'tooltip', 'mouseenter', {
|
3671
3831
|
useContentExp: true
|
3672
3832
|
});
|
@@ -3675,7 +3835,7 @@ function ($animate , $sce , $compile , $templateRequest) {
|
|
3675
3835
|
/*
|
3676
3836
|
Deprecated
|
3677
3837
|
*/
|
3678
|
-
.directive(
|
3838
|
+
.directive('tooltipHtmlUnsafePopup', function() {
|
3679
3839
|
return {
|
3680
3840
|
restrict: 'EA',
|
3681
3841
|
replace: true,
|
@@ -3685,13 +3845,13 @@ Deprecated
|
|
3685
3845
|
})
|
3686
3846
|
|
3687
3847
|
.value('tooltipHtmlUnsafeSuppressDeprecated', false)
|
3688
|
-
.directive(
|
3848
|
+
.directive('tooltipHtmlUnsafe', [
|
3689
3849
|
'$tooltip', 'tooltipHtmlUnsafeSuppressDeprecated', '$log',
|
3690
|
-
function
|
3850
|
+
function($tooltip , tooltipHtmlUnsafeSuppressDeprecated , $log) {
|
3691
3851
|
if (!tooltipHtmlUnsafeSuppressDeprecated) {
|
3692
3852
|
$log.warn('tooltip-html-unsafe is now deprecated. Use tooltip-html or tooltip-template instead.');
|
3693
3853
|
}
|
3694
|
-
return $tooltip(
|
3854
|
+
return $tooltip('tooltipHtmlUnsafe', 'tooltip', 'mouseenter');
|
3695
3855
|
}]);
|
3696
3856
|
|
3697
3857
|
/**
|
@@ -3699,9 +3859,9 @@ function ( $tooltip , tooltipHtmlUnsafeSuppressDeprecated , $log) {
|
|
3699
3859
|
* function, placement as a function, inside, support for more triggers than
|
3700
3860
|
* just mouse enter/leave, and selector delegatation.
|
3701
3861
|
*/
|
3702
|
-
angular.module( 'ui.bootstrap.popover', [
|
3862
|
+
angular.module( 'ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
|
3703
3863
|
|
3704
|
-
.directive(
|
3864
|
+
.directive('popoverTemplatePopup', function() {
|
3705
3865
|
return {
|
3706
3866
|
restrict: 'EA',
|
3707
3867
|
replace: true,
|
@@ -3711,13 +3871,13 @@ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
|
|
3711
3871
|
};
|
3712
3872
|
})
|
3713
3873
|
|
3714
|
-
.directive(
|
3715
|
-
return $tooltip(
|
3874
|
+
.directive('popoverTemplate', ['$tooltip', function($tooltip) {
|
3875
|
+
return $tooltip('popoverTemplate', 'popover', 'click', {
|
3716
3876
|
useContentExp: true
|
3717
|
-
}
|
3877
|
+
});
|
3718
3878
|
}])
|
3719
3879
|
|
3720
|
-
.directive(
|
3880
|
+
.directive('popoverHtmlPopup', function() {
|
3721
3881
|
return {
|
3722
3882
|
restrict: 'EA',
|
3723
3883
|
replace: true,
|
@@ -3726,13 +3886,13 @@ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
|
|
3726
3886
|
};
|
3727
3887
|
})
|
3728
3888
|
|
3729
|
-
.directive(
|
3889
|
+
.directive('popoverHtml', ['$tooltip', function($tooltip) {
|
3730
3890
|
return $tooltip( 'popoverHtml', 'popover', 'click', {
|
3731
3891
|
useContentExp: true
|
3732
3892
|
});
|
3733
3893
|
}])
|
3734
3894
|
|
3735
|
-
.directive(
|
3895
|
+
.directive('popoverPopup', function() {
|
3736
3896
|
return {
|
3737
3897
|
restrict: 'EA',
|
3738
3898
|
replace: true,
|
@@ -3741,7 +3901,7 @@ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
|
|
3741
3901
|
};
|
3742
3902
|
})
|
3743
3903
|
|
3744
|
-
.directive(
|
3904
|
+
.directive('popover', ['$tooltip', function($tooltip) {
|
3745
3905
|
return $tooltip( 'popover', 'popover', 'click' );
|
3746
3906
|
}]);
|
3747
3907
|
|
@@ -3752,104 +3912,144 @@ angular.module('ui.bootstrap.progressbar', [])
|
|
3752
3912
|
max: 100
|
3753
3913
|
})
|
3754
3914
|
|
3755
|
-
.
|
3756
|
-
var self = this,
|
3757
|
-
animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
|
3915
|
+
.value('$progressSuppressWarning', false)
|
3758
3916
|
|
3759
|
-
|
3760
|
-
|
3917
|
+
.controller('ProgressController', ['$scope', '$attrs', 'progressConfig', function($scope, $attrs, progressConfig) {
|
3918
|
+
var self = this,
|
3919
|
+
animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
|
3761
3920
|
|
3762
|
-
|
3763
|
-
|
3764
|
-
element.css({'transition': 'none'});
|
3765
|
-
}
|
3921
|
+
this.bars = [];
|
3922
|
+
$scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max;
|
3766
3923
|
|
3767
|
-
|
3924
|
+
this.addBar = function(bar, element) {
|
3925
|
+
if (!animate) {
|
3926
|
+
element.css({'transition': 'none'});
|
3927
|
+
}
|
3768
3928
|
|
3769
|
-
|
3929
|
+
this.bars.push(bar);
|
3770
3930
|
|
3771
|
-
|
3772
|
-
bar.recalculatePercentage();
|
3773
|
-
});
|
3931
|
+
bar.max = $scope.max;
|
3774
3932
|
|
3775
|
-
|
3776
|
-
|
3933
|
+
bar.$watch('value', function(value) {
|
3934
|
+
bar.recalculatePercentage();
|
3935
|
+
});
|
3777
3936
|
|
3778
|
-
|
3779
|
-
|
3780
|
-
totalPercentage += bar.percent;
|
3781
|
-
});
|
3937
|
+
bar.recalculatePercentage = function() {
|
3938
|
+
bar.percent = +(100 * bar.value / bar.max).toFixed(2);
|
3782
3939
|
|
3783
|
-
|
3784
|
-
|
3785
|
-
|
3786
|
-
};
|
3940
|
+
var totalPercentage = self.bars.reduce(function(total, bar) {
|
3941
|
+
return total + bar.percent;
|
3942
|
+
}, 0);
|
3787
3943
|
|
3788
|
-
|
3789
|
-
|
3790
|
-
|
3791
|
-
});
|
3944
|
+
if (totalPercentage > 100) {
|
3945
|
+
bar.percent -= totalPercentage - 100;
|
3946
|
+
}
|
3792
3947
|
};
|
3793
3948
|
|
3794
|
-
|
3795
|
-
|
3796
|
-
|
3949
|
+
bar.$on('$destroy', function() {
|
3950
|
+
element = null;
|
3951
|
+
self.removeBar(bar);
|
3952
|
+
});
|
3953
|
+
};
|
3797
3954
|
|
3798
|
-
|
3799
|
-
|
3800
|
-
|
3801
|
-
|
3802
|
-
|
3955
|
+
this.removeBar = function(bar) {
|
3956
|
+
this.bars.splice(this.bars.indexOf(bar), 1);
|
3957
|
+
};
|
3958
|
+
|
3959
|
+
$scope.$watch('max', function(max) {
|
3960
|
+
self.bars.forEach(function(bar) {
|
3961
|
+
bar.max = $scope.max;
|
3962
|
+
bar.recalculatePercentage();
|
3803
3963
|
});
|
3964
|
+
});
|
3804
3965
|
}])
|
3805
3966
|
|
3806
|
-
.directive('
|
3807
|
-
|
3808
|
-
|
3809
|
-
|
3810
|
-
|
3811
|
-
|
3812
|
-
|
3813
|
-
|
3814
|
-
|
3815
|
-
|
3816
|
-
|
3817
|
-
|
3967
|
+
.directive('uibProgress', function() {
|
3968
|
+
return {
|
3969
|
+
restrict: 'EA',
|
3970
|
+
replace: true,
|
3971
|
+
transclude: true,
|
3972
|
+
controller: 'ProgressController',
|
3973
|
+
require: 'uibProgress',
|
3974
|
+
scope: {
|
3975
|
+
max: '=?'
|
3976
|
+
},
|
3977
|
+
templateUrl: 'template/progressbar/progress.html'
|
3978
|
+
};
|
3818
3979
|
})
|
3819
3980
|
|
3820
|
-
.directive('
|
3821
|
-
|
3822
|
-
|
3823
|
-
|
3824
|
-
|
3825
|
-
|
3826
|
-
|
3827
|
-
|
3828
|
-
|
3829
|
-
|
3830
|
-
|
3831
|
-
|
3832
|
-
|
3833
|
-
|
3834
|
-
|
3981
|
+
.directive('progress', ['$log', '$progressSuppressWarning', function($log, $progressSuppressWarning) {
|
3982
|
+
return {
|
3983
|
+
restrict: 'EA',
|
3984
|
+
replace: true,
|
3985
|
+
transclude: true,
|
3986
|
+
controller: 'ProgressController',
|
3987
|
+
require: 'progress',
|
3988
|
+
scope: {
|
3989
|
+
max: '=?'
|
3990
|
+
},
|
3991
|
+
templateUrl: 'template/progressbar/progress.html',
|
3992
|
+
link: function() {
|
3993
|
+
if ($progressSuppressWarning) {
|
3994
|
+
$log.warn('progress is now deprecated. Use uib-progress instead');
|
3995
|
+
}
|
3996
|
+
}
|
3997
|
+
};
|
3998
|
+
}])
|
3999
|
+
|
4000
|
+
.directive('uibBar', function() {
|
4001
|
+
return {
|
4002
|
+
restrict: 'EA',
|
4003
|
+
replace: true,
|
4004
|
+
transclude: true,
|
4005
|
+
require: '^uibProgress',
|
4006
|
+
scope: {
|
4007
|
+
value: '=',
|
4008
|
+
type: '@'
|
4009
|
+
},
|
4010
|
+
templateUrl: 'template/progressbar/bar.html',
|
4011
|
+
link: function(scope, element, attrs, progressCtrl) {
|
4012
|
+
progressCtrl.addBar(scope, element);
|
4013
|
+
}
|
4014
|
+
};
|
3835
4015
|
})
|
3836
4016
|
|
4017
|
+
.directive('bar', ['$log', '$progressSuppressWarning', function($log, $progressSuppressWarning) {
|
4018
|
+
return {
|
4019
|
+
restrict: 'EA',
|
4020
|
+
replace: true,
|
4021
|
+
transclude: true,
|
4022
|
+
require: '^progress',
|
4023
|
+
scope: {
|
4024
|
+
value: '=',
|
4025
|
+
type: '@'
|
4026
|
+
},
|
4027
|
+
templateUrl: 'template/progressbar/bar.html',
|
4028
|
+
link: function(scope, element, attrs, progressCtrl) {
|
4029
|
+
if ($progressSuppressWarning) {
|
4030
|
+
$log.warn('bar is now deprecated. Use uib-bar instead');
|
4031
|
+
}
|
4032
|
+
progressCtrl.addBar(scope, element);
|
4033
|
+
}
|
4034
|
+
};
|
4035
|
+
}])
|
4036
|
+
|
3837
4037
|
.directive('progressbar', function() {
|
3838
|
-
|
3839
|
-
|
3840
|
-
|
3841
|
-
|
3842
|
-
|
3843
|
-
|
3844
|
-
|
3845
|
-
|
3846
|
-
|
3847
|
-
|
3848
|
-
|
3849
|
-
|
3850
|
-
|
3851
|
-
|
3852
|
-
|
4038
|
+
return {
|
4039
|
+
restrict: 'EA',
|
4040
|
+
replace: true,
|
4041
|
+
transclude: true,
|
4042
|
+
controller: 'ProgressController',
|
4043
|
+
scope: {
|
4044
|
+
value: '=',
|
4045
|
+
max: '=?',
|
4046
|
+
type: '@'
|
4047
|
+
},
|
4048
|
+
templateUrl: 'template/progressbar/progressbar.html',
|
4049
|
+
link: function(scope, element, attrs, progressCtrl) {
|
4050
|
+
progressCtrl.addBar(scope, angular.element(element.children()[0]));
|
4051
|
+
}
|
4052
|
+
};
|
3853
4053
|
});
|
3854
4054
|
|
3855
4055
|
angular.module('ui.bootstrap.rating', [])
|
@@ -3877,12 +4077,13 @@ angular.module('ui.bootstrap.rating', [])
|
|
3877
4077
|
|
3878
4078
|
this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
|
3879
4079
|
this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
|
3880
|
-
var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ;
|
4080
|
+
var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ;
|
3881
4081
|
this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
|
3882
4082
|
tmpTitles : ratingConfig.titles;
|
3883
|
-
|
3884
|
-
var ratingStates = angular.isDefined($attrs.ratingStates) ?
|
3885
|
-
|
4083
|
+
|
4084
|
+
var ratingStates = angular.isDefined($attrs.ratingStates) ?
|
4085
|
+
$scope.$parent.$eval($attrs.ratingStates) :
|
4086
|
+
new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max);
|
3886
4087
|
$scope.range = this.buildTemplateObjects(ratingStates);
|
3887
4088
|
};
|
3888
4089
|
|
@@ -3892,7 +4093,7 @@ angular.module('ui.bootstrap.rating', [])
|
|
3892
4093
|
}
|
3893
4094
|
return states;
|
3894
4095
|
};
|
3895
|
-
|
4096
|
+
|
3896
4097
|
this.getTitle = function(index) {
|
3897
4098
|
if (index >= this.titles.length) {
|
3898
4099
|
return index + 1;
|
@@ -3900,16 +4101,16 @@ angular.module('ui.bootstrap.rating', [])
|
|
3900
4101
|
return this.titles[index];
|
3901
4102
|
}
|
3902
4103
|
};
|
3903
|
-
|
4104
|
+
|
3904
4105
|
$scope.rate = function(value) {
|
3905
|
-
if (
|
4106
|
+
if (!$scope.readonly && value >= 0 && value <= $scope.range.length) {
|
3906
4107
|
ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value);
|
3907
4108
|
ngModelCtrl.$render();
|
3908
4109
|
}
|
3909
4110
|
};
|
3910
4111
|
|
3911
4112
|
$scope.enter = function(value) {
|
3912
|
-
if (
|
4113
|
+
if (!$scope.readonly) {
|
3913
4114
|
$scope.value = value;
|
3914
4115
|
}
|
3915
4116
|
$scope.onHover({value: value});
|
@@ -3924,7 +4125,7 @@ angular.module('ui.bootstrap.rating', [])
|
|
3924
4125
|
if (/(37|38|39|40)/.test(evt.which)) {
|
3925
4126
|
evt.preventDefault();
|
3926
4127
|
evt.stopPropagation();
|
3927
|
-
$scope.rate(
|
4128
|
+
$scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1));
|
3928
4129
|
}
|
3929
4130
|
};
|
3930
4131
|
|
@@ -3972,10 +4173,15 @@ angular.module('ui.bootstrap.tabs', [])
|
|
3972
4173
|
if (tab.active && tab !== selectedTab) {
|
3973
4174
|
tab.active = false;
|
3974
4175
|
tab.onDeselect();
|
4176
|
+
selectedTab.selectCalled = false;
|
3975
4177
|
}
|
3976
4178
|
});
|
3977
4179
|
selectedTab.active = true;
|
3978
|
-
|
4180
|
+
// only call select if it has not already been called
|
4181
|
+
if (!selectedTab.selectCalled) {
|
4182
|
+
selectedTab.onSelect();
|
4183
|
+
selectedTab.selectCalled = true;
|
4184
|
+
}
|
3979
4185
|
};
|
3980
4186
|
|
3981
4187
|
ctrl.addTab = function addTab(tab) {
|
@@ -3986,8 +4192,7 @@ angular.module('ui.bootstrap.tabs', [])
|
|
3986
4192
|
tab.active = true;
|
3987
4193
|
} else if (tab.active) {
|
3988
4194
|
ctrl.select(tab);
|
3989
|
-
}
|
3990
|
-
else {
|
4195
|
+
} else {
|
3991
4196
|
tab.active = false;
|
3992
4197
|
}
|
3993
4198
|
};
|
@@ -4161,7 +4366,7 @@ angular.module('ui.bootstrap.tabs', [])
|
|
4161
4366
|
});
|
4162
4367
|
|
4163
4368
|
scope.disabled = false;
|
4164
|
-
if (
|
4369
|
+
if (attrs.disable) {
|
4165
4370
|
scope.$parent.$watch($parse(attrs.disable), function(value) {
|
4166
4371
|
scope.disabled = !! value;
|
4167
4372
|
});
|
@@ -4171,7 +4376,7 @@ angular.module('ui.bootstrap.tabs', [])
|
|
4171
4376
|
// fix(tab): IE9 disabled attr renders grey text on enabled tab #2677
|
4172
4377
|
// This code is duplicated from the lines above to make it easy to remove once
|
4173
4378
|
// the feature has been completely deprecated
|
4174
|
-
if (
|
4379
|
+
if (attrs.disabled) {
|
4175
4380
|
$log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');
|
4176
4381
|
scope.$parent.$watch($parse(attrs.disabled), function(value) {
|
4177
4382
|
scope.disabled = !! value;
|
@@ -4179,7 +4384,7 @@ angular.module('ui.bootstrap.tabs', [])
|
|
4179
4384
|
}
|
4180
4385
|
|
4181
4386
|
scope.select = function() {
|
4182
|
-
if (
|
4387
|
+
if (!scope.disabled) {
|
4183
4388
|
scope.active = true;
|
4184
4389
|
}
|
4185
4390
|
};
|
@@ -4196,7 +4401,7 @@ angular.module('ui.bootstrap.tabs', [])
|
|
4196
4401
|
};
|
4197
4402
|
}])
|
4198
4403
|
|
4199
|
-
.directive('tabHeadingTransclude',
|
4404
|
+
.directive('tabHeadingTransclude', function() {
|
4200
4405
|
return {
|
4201
4406
|
restrict: 'A',
|
4202
4407
|
require: '^tab',
|
@@ -4209,7 +4414,7 @@ angular.module('ui.bootstrap.tabs', [])
|
|
4209
4414
|
});
|
4210
4415
|
}
|
4211
4416
|
};
|
4212
|
-
}
|
4417
|
+
})
|
4213
4418
|
|
4214
4419
|
.directive('tabContentTransclude', function() {
|
4215
4420
|
return {
|
@@ -4232,17 +4437,18 @@ angular.module('ui.bootstrap.tabs', [])
|
|
4232
4437
|
});
|
4233
4438
|
}
|
4234
4439
|
};
|
4440
|
+
|
4235
4441
|
function isTabHeading(node) {
|
4236
|
-
return node.tagName &&
|
4442
|
+
return node.tagName && (
|
4237
4443
|
node.hasAttribute('tab-heading') ||
|
4238
4444
|
node.hasAttribute('data-tab-heading') ||
|
4445
|
+
node.hasAttribute('x-tab-heading') ||
|
4239
4446
|
node.tagName.toLowerCase() === 'tab-heading' ||
|
4240
|
-
node.tagName.toLowerCase() === 'data-tab-heading'
|
4447
|
+
node.tagName.toLowerCase() === 'data-tab-heading' ||
|
4448
|
+
node.tagName.toLowerCase() === 'x-tab-heading'
|
4241
4449
|
);
|
4242
4450
|
}
|
4243
|
-
})
|
4244
|
-
|
4245
|
-
;
|
4451
|
+
});
|
4246
4452
|
|
4247
4453
|
angular.module('ui.bootstrap.timepicker', [])
|
4248
4454
|
|
@@ -4262,29 +4468,29 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4262
4468
|
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
|
4263
4469
|
meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
|
4264
4470
|
|
4265
|
-
this.init = function(
|
4471
|
+
this.init = function(ngModelCtrl_, inputs) {
|
4266
4472
|
ngModelCtrl = ngModelCtrl_;
|
4267
4473
|
ngModelCtrl.$render = this.render;
|
4268
4474
|
|
4269
|
-
ngModelCtrl.$formatters.unshift(function
|
4270
|
-
return modelValue ? new Date(
|
4475
|
+
ngModelCtrl.$formatters.unshift(function(modelValue) {
|
4476
|
+
return modelValue ? new Date(modelValue) : null;
|
4271
4477
|
});
|
4272
4478
|
|
4273
4479
|
var hoursInputEl = inputs.eq(0),
|
4274
4480
|
minutesInputEl = inputs.eq(1);
|
4275
4481
|
|
4276
4482
|
var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
|
4277
|
-
if (
|
4278
|
-
this.setupMousewheelEvents(
|
4483
|
+
if (mousewheel) {
|
4484
|
+
this.setupMousewheelEvents(hoursInputEl, minutesInputEl);
|
4279
4485
|
}
|
4280
4486
|
|
4281
4487
|
var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
|
4282
4488
|
if (arrowkeys) {
|
4283
|
-
this.setupArrowkeyEvents(
|
4489
|
+
this.setupArrowkeyEvents(hoursInputEl, minutesInputEl);
|
4284
4490
|
}
|
4285
4491
|
|
4286
4492
|
$scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
|
4287
|
-
this.setupInputEvents(
|
4493
|
+
this.setupInputEvents(hoursInputEl, minutesInputEl);
|
4288
4494
|
};
|
4289
4495
|
|
4290
4496
|
var hourStep = timepickerConfig.hourStep;
|
@@ -4320,7 +4526,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4320
4526
|
};
|
4321
4527
|
|
4322
4528
|
$scope.noDecrementHours = function() {
|
4323
|
-
var decrementedSelected = addMinutes(selected, -
|
4529
|
+
var decrementedSelected = addMinutes(selected, -hourStep * 60);
|
4324
4530
|
return decrementedSelected < min ||
|
4325
4531
|
(decrementedSelected > selected && decrementedSelected > max);
|
4326
4532
|
};
|
@@ -4332,7 +4538,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4332
4538
|
};
|
4333
4539
|
|
4334
4540
|
$scope.noDecrementMinutes = function() {
|
4335
|
-
var decrementedSelected = addMinutes(selected, -
|
4541
|
+
var decrementedSelected = addMinutes(selected, -minuteStep);
|
4336
4542
|
return decrementedSelected < min ||
|
4337
4543
|
(decrementedSelected > selected && decrementedSelected > max);
|
4338
4544
|
};
|
@@ -4341,7 +4547,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4341
4547
|
if (selected.getHours() < 13) {
|
4342
4548
|
return addMinutes(selected, 12 * 60) > max;
|
4343
4549
|
} else {
|
4344
|
-
return addMinutes(selected, -
|
4550
|
+
return addMinutes(selected, -12 * 60) < min;
|
4345
4551
|
}
|
4346
4552
|
};
|
4347
4553
|
|
@@ -4351,11 +4557,11 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4351
4557
|
$scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
|
4352
4558
|
$scope.showMeridian = !!value;
|
4353
4559
|
|
4354
|
-
if (
|
4560
|
+
if (ngModelCtrl.$error.time) {
|
4355
4561
|
// Evaluate from template
|
4356
4562
|
var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
|
4357
|
-
if (angular.isDefined(
|
4358
|
-
selected.setHours(
|
4563
|
+
if (angular.isDefined(hours) && angular.isDefined(minutes)) {
|
4564
|
+
selected.setHours(hours);
|
4359
4565
|
refresh();
|
4360
4566
|
}
|
4361
4567
|
} else {
|
@@ -4365,18 +4571,18 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4365
4571
|
}
|
4366
4572
|
|
4367
4573
|
// Get $scope.hours in 24H mode if valid
|
4368
|
-
function getHoursFromTemplate
|
4369
|
-
var hours = parseInt(
|
4370
|
-
var valid =
|
4371
|
-
if (
|
4574
|
+
function getHoursFromTemplate() {
|
4575
|
+
var hours = parseInt($scope.hours, 10);
|
4576
|
+
var valid = $scope.showMeridian ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
|
4577
|
+
if (!valid) {
|
4372
4578
|
return undefined;
|
4373
4579
|
}
|
4374
4580
|
|
4375
|
-
if (
|
4376
|
-
if (
|
4581
|
+
if ($scope.showMeridian) {
|
4582
|
+
if (hours === 12) {
|
4377
4583
|
hours = 0;
|
4378
4584
|
}
|
4379
|
-
if (
|
4585
|
+
if ($scope.meridian === meridians[1]) {
|
4380
4586
|
hours = hours + 12;
|
4381
4587
|
}
|
4382
4588
|
}
|
@@ -4385,15 +4591,15 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4385
4591
|
|
4386
4592
|
function getMinutesFromTemplate() {
|
4387
4593
|
var minutes = parseInt($scope.minutes, 10);
|
4388
|
-
return (
|
4594
|
+
return (minutes >= 0 && minutes < 60) ? minutes : undefined;
|
4389
4595
|
}
|
4390
4596
|
|
4391
|
-
function pad(
|
4392
|
-
return (
|
4597
|
+
function pad(value) {
|
4598
|
+
return (angular.isDefined(value) && value.toString().length < 2) ? '0' + value : value.toString();
|
4393
4599
|
}
|
4394
4600
|
|
4395
4601
|
// Respond on mousewheel spin
|
4396
|
-
this.setupMousewheelEvents = function(
|
4602
|
+
this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl) {
|
4397
4603
|
var isScrollingUp = function(e) {
|
4398
4604
|
if (e.originalEvent) {
|
4399
4605
|
e = e.originalEvent;
|
@@ -4404,26 +4610,25 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4404
4610
|
};
|
4405
4611
|
|
4406
4612
|
hoursInputEl.bind('mousewheel wheel', function(e) {
|
4407
|
-
$scope.$apply(
|
4613
|
+
$scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
|
4408
4614
|
e.preventDefault();
|
4409
4615
|
});
|
4410
4616
|
|
4411
4617
|
minutesInputEl.bind('mousewheel wheel', function(e) {
|
4412
|
-
$scope.$apply(
|
4618
|
+
$scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
|
4413
4619
|
e.preventDefault();
|
4414
4620
|
});
|
4415
4621
|
|
4416
4622
|
};
|
4417
4623
|
|
4418
4624
|
// Respond on up/down arrowkeys
|
4419
|
-
this.setupArrowkeyEvents = function(
|
4625
|
+
this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl) {
|
4420
4626
|
hoursInputEl.bind('keydown', function(e) {
|
4421
|
-
if (
|
4627
|
+
if (e.which === 38) { // up
|
4422
4628
|
e.preventDefault();
|
4423
4629
|
$scope.incrementHours();
|
4424
4630
|
$scope.$apply();
|
4425
|
-
}
|
4426
|
-
else if ( e.which === 40 ) { // down
|
4631
|
+
} else if (e.which === 40) { // down
|
4427
4632
|
e.preventDefault();
|
4428
4633
|
$scope.decrementHours();
|
4429
4634
|
$scope.$apply();
|
@@ -4431,12 +4636,11 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4431
4636
|
});
|
4432
4637
|
|
4433
4638
|
minutesInputEl.bind('keydown', function(e) {
|
4434
|
-
if (
|
4639
|
+
if (e.which === 38) { // up
|
4435
4640
|
e.preventDefault();
|
4436
4641
|
$scope.incrementMinutes();
|
4437
4642
|
$scope.$apply();
|
4438
|
-
}
|
4439
|
-
else if ( e.which === 40 ) { // down
|
4643
|
+
} else if (e.which === 40) { // down
|
4440
4644
|
e.preventDefault();
|
4441
4645
|
$scope.decrementMinutes();
|
4442
4646
|
$scope.$apply();
|
@@ -4444,15 +4648,15 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4444
4648
|
});
|
4445
4649
|
};
|
4446
4650
|
|
4447
|
-
this.setupInputEvents = function(
|
4448
|
-
if (
|
4651
|
+
this.setupInputEvents = function(hoursInputEl, minutesInputEl) {
|
4652
|
+
if ($scope.readonlyInput) {
|
4449
4653
|
$scope.updateHours = angular.noop;
|
4450
4654
|
$scope.updateMinutes = angular.noop;
|
4451
4655
|
return;
|
4452
4656
|
}
|
4453
4657
|
|
4454
4658
|
var invalidate = function(invalidHours, invalidMinutes) {
|
4455
|
-
ngModelCtrl.$setViewValue(
|
4659
|
+
ngModelCtrl.$setViewValue(null);
|
4456
4660
|
ngModelCtrl.$setValidity('time', false);
|
4457
4661
|
if (angular.isDefined(invalidHours)) {
|
4458
4662
|
$scope.invalidHours = invalidHours;
|
@@ -4463,14 +4667,15 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4463
4667
|
};
|
4464
4668
|
|
4465
4669
|
$scope.updateHours = function() {
|
4466
|
-
var hours = getHoursFromTemplate()
|
4670
|
+
var hours = getHoursFromTemplate(),
|
4671
|
+
minutes = getMinutesFromTemplate();
|
4467
4672
|
|
4468
|
-
if (
|
4469
|
-
selected.setHours(
|
4673
|
+
if (angular.isDefined(hours) && angular.isDefined(minutes)) {
|
4674
|
+
selected.setHours(hours);
|
4470
4675
|
if (selected < min || selected > max) {
|
4471
4676
|
invalidate(true);
|
4472
4677
|
} else {
|
4473
|
-
refresh(
|
4678
|
+
refresh('h');
|
4474
4679
|
}
|
4475
4680
|
} else {
|
4476
4681
|
invalidate(true);
|
@@ -4478,22 +4683,23 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4478
4683
|
};
|
4479
4684
|
|
4480
4685
|
hoursInputEl.bind('blur', function(e) {
|
4481
|
-
if (
|
4482
|
-
$scope.$apply(
|
4483
|
-
$scope.hours = pad(
|
4686
|
+
if (!$scope.invalidHours && $scope.hours < 10) {
|
4687
|
+
$scope.$apply(function() {
|
4688
|
+
$scope.hours = pad($scope.hours);
|
4484
4689
|
});
|
4485
4690
|
}
|
4486
4691
|
});
|
4487
4692
|
|
4488
4693
|
$scope.updateMinutes = function() {
|
4489
|
-
var minutes = getMinutesFromTemplate()
|
4694
|
+
var minutes = getMinutesFromTemplate(),
|
4695
|
+
hours = getHoursFromTemplate();
|
4490
4696
|
|
4491
|
-
if (
|
4492
|
-
selected.setMinutes(
|
4697
|
+
if (angular.isDefined(minutes) && angular.isDefined(hours)) {
|
4698
|
+
selected.setMinutes(minutes);
|
4493
4699
|
if (selected < min || selected > max) {
|
4494
4700
|
invalidate(undefined, true);
|
4495
4701
|
} else {
|
4496
|
-
refresh(
|
4702
|
+
refresh('m');
|
4497
4703
|
}
|
4498
4704
|
} else {
|
4499
4705
|
invalidate(undefined, true);
|
@@ -4501,9 +4707,9 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4501
4707
|
};
|
4502
4708
|
|
4503
4709
|
minutesInputEl.bind('blur', function(e) {
|
4504
|
-
if (
|
4505
|
-
$scope.$apply(
|
4506
|
-
$scope.minutes = pad(
|
4710
|
+
if (!$scope.invalidMinutes && $scope.minutes < 10) {
|
4711
|
+
$scope.$apply(function() {
|
4712
|
+
$scope.minutes = pad($scope.minutes);
|
4507
4713
|
});
|
4508
4714
|
}
|
4509
4715
|
});
|
@@ -4513,11 +4719,11 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4513
4719
|
this.render = function() {
|
4514
4720
|
var date = ngModelCtrl.$viewValue;
|
4515
4721
|
|
4516
|
-
if (
|
4722
|
+
if (isNaN(date)) {
|
4517
4723
|
ngModelCtrl.$setValidity('time', false);
|
4518
4724
|
$log.error('Timepicker 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.');
|
4519
4725
|
} else {
|
4520
|
-
if (
|
4726
|
+
if (date) {
|
4521
4727
|
selected = date;
|
4522
4728
|
}
|
4523
4729
|
|
@@ -4533,10 +4739,10 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4533
4739
|
};
|
4534
4740
|
|
4535
4741
|
// Call internally when we know that model is valid.
|
4536
|
-
function refresh(
|
4742
|
+
function refresh(keyboardChange) {
|
4537
4743
|
makeValid();
|
4538
|
-
ngModelCtrl.$setViewValue(
|
4539
|
-
updateTemplate(
|
4744
|
+
ngModelCtrl.$setViewValue(new Date(selected));
|
4745
|
+
updateTemplate(keyboardChange);
|
4540
4746
|
}
|
4541
4747
|
|
4542
4748
|
function makeValid() {
|
@@ -4545,11 +4751,11 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4545
4751
|
$scope.invalidMinutes = false;
|
4546
4752
|
}
|
4547
4753
|
|
4548
|
-
function updateTemplate(
|
4754
|
+
function updateTemplate(keyboardChange) {
|
4549
4755
|
var hours = selected.getHours(), minutes = selected.getMinutes();
|
4550
4756
|
|
4551
|
-
if (
|
4552
|
-
hours = (
|
4757
|
+
if ($scope.showMeridian) {
|
4758
|
+
hours = (hours === 0 || hours === 12) ? 12 : hours % 12; // Convert 24 to 12 hour system
|
4553
4759
|
}
|
4554
4760
|
|
4555
4761
|
$scope.hours = keyboardChange === 'h' ? hours : pad(hours);
|
@@ -4559,15 +4765,15 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4559
4765
|
$scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
|
4560
4766
|
}
|
4561
4767
|
|
4562
|
-
function addMinutes(date,
|
4768
|
+
function addMinutes(date, minutes) {
|
4563
4769
|
var dt = new Date(date.getTime() + minutes * 60000);
|
4564
4770
|
var newDate = new Date(date);
|
4565
4771
|
newDate.setHours(dt.getHours(), dt.getMinutes());
|
4566
4772
|
return newDate;
|
4567
4773
|
}
|
4568
4774
|
|
4569
|
-
function addMinutesToSelected(
|
4570
|
-
selected = addMinutes(
|
4775
|
+
function addMinutesToSelected(minutes) {
|
4776
|
+
selected = addMinutes(selected, minutes);
|
4571
4777
|
refresh();
|
4572
4778
|
}
|
4573
4779
|
|
@@ -4579,21 +4785,25 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4579
4785
|
addMinutesToSelected(hourStep * 60);
|
4580
4786
|
}
|
4581
4787
|
};
|
4788
|
+
|
4582
4789
|
$scope.decrementHours = function() {
|
4583
4790
|
if (!$scope.noDecrementHours()) {
|
4584
4791
|
addMinutesToSelected(-hourStep * 60);
|
4585
4792
|
}
|
4586
4793
|
};
|
4794
|
+
|
4587
4795
|
$scope.incrementMinutes = function() {
|
4588
4796
|
if (!$scope.noIncrementMinutes()) {
|
4589
4797
|
addMinutesToSelected(minuteStep);
|
4590
4798
|
}
|
4591
4799
|
};
|
4800
|
+
|
4592
4801
|
$scope.decrementMinutes = function() {
|
4593
4802
|
if (!$scope.noDecrementMinutes()) {
|
4594
4803
|
addMinutesToSelected(-minuteStep);
|
4595
4804
|
}
|
4596
4805
|
};
|
4806
|
+
|
4597
4807
|
$scope.toggleMeridian = function() {
|
4598
4808
|
if (!$scope.noToggleMeridian()) {
|
4599
4809
|
addMinutesToSelected(12 * 60 * (selected.getHours() < 12 ? 1 : -1));
|
@@ -4601,19 +4811,22 @@ angular.module('ui.bootstrap.timepicker', [])
|
|
4601
4811
|
};
|
4602
4812
|
}])
|
4603
4813
|
|
4604
|
-
.directive('timepicker', function
|
4814
|
+
.directive('timepicker', function() {
|
4605
4815
|
return {
|
4606
4816
|
restrict: 'EA',
|
4607
4817
|
require: ['timepicker', '?^ngModel'],
|
4608
4818
|
controller:'TimepickerController',
|
4819
|
+
controllerAs: 'timepicker',
|
4609
4820
|
replace: true,
|
4610
4821
|
scope: {},
|
4611
|
-
templateUrl:
|
4822
|
+
templateUrl: function(element, attrs) {
|
4823
|
+
return attrs.templateUrl || 'template/timepicker/timepicker.html';
|
4824
|
+
},
|
4612
4825
|
link: function(scope, element, attrs, ctrls) {
|
4613
4826
|
var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
4614
4827
|
|
4615
|
-
if (
|
4616
|
-
timepickerCtrl.init(
|
4828
|
+
if (ngModelCtrl) {
|
4829
|
+
timepickerCtrl.init(ngModelCtrl, element.find('input'));
|
4617
4830
|
}
|
4618
4831
|
}
|
4619
4832
|
};
|
@@ -4709,20 +4922,19 @@ function($q , $timeout , $rootScope , $log , $transitionSuppressDeprecated)
|
|
4709
4922
|
return $transition;
|
4710
4923
|
}]);
|
4711
4924
|
|
4712
|
-
angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'
|
4925
|
+
angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
|
4713
4926
|
|
4714
4927
|
/**
|
4715
4928
|
* A helper service that can parse typeahead's syntax (string provided by users)
|
4716
4929
|
* Extracted to a separate service for ease of unit testing
|
4717
4930
|
*/
|
4718
|
-
.factory('typeaheadParser', ['$parse', function
|
4931
|
+
.factory('typeaheadParser', ['$parse', function($parse) {
|
4719
4932
|
|
4720
4933
|
// 00000111000000000000022200000000000000003333333333333330000000000044000
|
4721
4934
|
var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
|
4722
4935
|
|
4723
4936
|
return {
|
4724
|
-
parse:function
|
4725
|
-
|
4937
|
+
parse: function(input) {
|
4726
4938
|
var match = input.match(TYPEAHEAD_REGEXP);
|
4727
4939
|
if (!match) {
|
4728
4940
|
throw new Error(
|
@@ -4741,461 +4953,469 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
4741
4953
|
}])
|
4742
4954
|
|
4743
4955
|
.directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$position', 'typeaheadParser',
|
4744
|
-
|
4745
|
-
|
4746
|
-
|
4747
|
-
var eventDebounceTime = 200;
|
4956
|
+
function($compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) {
|
4957
|
+
var HOT_KEYS = [9, 13, 27, 38, 40];
|
4958
|
+
var eventDebounceTime = 200;
|
4748
4959
|
|
4749
|
-
|
4750
|
-
|
4751
|
-
|
4752
|
-
|
4753
|
-
|
4754
|
-
|
4755
|
-
|
4756
|
-
|
4757
|
-
|
4758
|
-
minLength
|
4759
|
-
|
4760
|
-
|
4761
|
-
//minimal wait time after last character typed before typeahead kicks-in
|
4762
|
-
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
|
4960
|
+
return {
|
4961
|
+
require: ['ngModel', '^?ngModelOptions'],
|
4962
|
+
link: function(originalScope, element, attrs, ctrls) {
|
4963
|
+
var modelCtrl = ctrls[0];
|
4964
|
+
var ngModelOptions = ctrls[1];
|
4965
|
+
//SUPPORTED ATTRIBUTES (OPTIONS)
|
4966
|
+
|
4967
|
+
//minimal no of characters that needs to be entered before typeahead kicks-in
|
4968
|
+
var minLength = originalScope.$eval(attrs.typeaheadMinLength);
|
4969
|
+
if (!minLength && minLength !== 0) {
|
4970
|
+
minLength = 1;
|
4971
|
+
}
|
4763
4972
|
|
4764
|
-
|
4765
|
-
|
4973
|
+
//minimal wait time after last character typed before typeahead kicks-in
|
4974
|
+
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
|
4766
4975
|
|
4767
|
-
|
4768
|
-
|
4976
|
+
//should it restrict model values to the ones selected from the popup only?
|
4977
|
+
var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
|
4769
4978
|
|
4770
|
-
|
4771
|
-
|
4979
|
+
//binding to a variable that indicates if matches are being retrieved asynchronously
|
4980
|
+
var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
|
4772
4981
|
|
4773
|
-
|
4774
|
-
|
4982
|
+
//a callback executed when a match is selected
|
4983
|
+
var onSelectCallback = $parse(attrs.typeaheadOnSelect);
|
4775
4984
|
|
4776
|
-
|
4777
|
-
|
4985
|
+
//should it select highlighted popup value when losing focus?
|
4986
|
+
var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
|
4778
4987
|
|
4779
|
-
|
4988
|
+
//binding to a variable that indicates if there were no results after the query is completed
|
4989
|
+
var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
|
4780
4990
|
|
4781
|
-
|
4991
|
+
var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
|
4782
4992
|
|
4783
|
-
|
4993
|
+
var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
|
4784
4994
|
|
4785
|
-
|
4786
|
-
var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
|
4995
|
+
var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
|
4787
4996
|
|
4788
|
-
|
4997
|
+
//If input matches an item of the list exactly, select it automatically
|
4998
|
+
var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
|
4789
4999
|
|
4790
|
-
|
4791
|
-
var $setModelValue = $parse(attrs.ngModel).assign;
|
5000
|
+
//INTERNAL VARIABLES
|
4792
5001
|
|
4793
|
-
|
4794
|
-
|
5002
|
+
//model setter executed upon match selection
|
5003
|
+
var parsedModel = $parse(attrs.ngModel);
|
5004
|
+
var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
|
5005
|
+
var $setModelValue = function(scope, newValue) {
|
5006
|
+
if (angular.isFunction(parsedModel(originalScope)) &&
|
5007
|
+
ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
|
5008
|
+
return invokeModelSetter(scope, {$$$p: newValue});
|
5009
|
+
} else {
|
5010
|
+
return parsedModel.assign(scope, newValue);
|
5011
|
+
}
|
5012
|
+
};
|
4795
5013
|
|
4796
|
-
|
5014
|
+
//expressions used by typeahead
|
5015
|
+
var parserResult = typeaheadParser.parse(attrs.typeahead);
|
4797
5016
|
|
4798
|
-
|
4799
|
-
//mousedown & mouseup events
|
4800
|
-
//Issue #3699
|
4801
|
-
var selected;
|
5017
|
+
var hasFocus;
|
4802
5018
|
|
4803
|
-
|
4804
|
-
|
4805
|
-
|
4806
|
-
|
4807
|
-
scope.$destroy();
|
4808
|
-
});
|
5019
|
+
//Used to avoid bug in iOS webview where iOS keyboard does not fire
|
5020
|
+
//mousedown & mouseup events
|
5021
|
+
//Issue #3699
|
5022
|
+
var selected;
|
4809
5023
|
|
4810
|
-
|
4811
|
-
|
4812
|
-
|
4813
|
-
|
4814
|
-
|
4815
|
-
|
4816
|
-
|
5024
|
+
//create a child scope for the typeahead directive so we are not polluting original scope
|
5025
|
+
//with typeahead-specific data (matches, query etc.)
|
5026
|
+
var scope = originalScope.$new();
|
5027
|
+
var offDestroy = originalScope.$on('$destroy', function() {
|
5028
|
+
scope.$destroy();
|
5029
|
+
});
|
5030
|
+
scope.$on('$destroy', offDestroy);
|
5031
|
+
|
5032
|
+
// WAI-ARIA
|
5033
|
+
var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
|
5034
|
+
element.attr({
|
5035
|
+
'aria-autocomplete': 'list',
|
5036
|
+
'aria-expanded': false,
|
5037
|
+
'aria-owns': popupId
|
5038
|
+
});
|
4817
5039
|
|
4818
|
-
|
4819
|
-
|
4820
|
-
|
4821
|
-
|
4822
|
-
|
4823
|
-
|
4824
|
-
|
4825
|
-
|
4826
|
-
|
4827
|
-
|
4828
|
-
|
4829
|
-
|
4830
|
-
|
4831
|
-
|
4832
|
-
|
5040
|
+
//pop-up element used to display matches
|
5041
|
+
var popUpEl = angular.element('<div typeahead-popup></div>');
|
5042
|
+
popUpEl.attr({
|
5043
|
+
id: popupId,
|
5044
|
+
matches: 'matches',
|
5045
|
+
active: 'activeIdx',
|
5046
|
+
select: 'select(activeIdx)',
|
5047
|
+
'move-in-progress': 'moveInProgress',
|
5048
|
+
query: 'query',
|
5049
|
+
position: 'position'
|
5050
|
+
});
|
5051
|
+
//custom item template
|
5052
|
+
if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
|
5053
|
+
popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
|
5054
|
+
}
|
4833
5055
|
|
4834
|
-
|
4835
|
-
|
4836
|
-
|
4837
|
-
element.attr('aria-expanded', false);
|
4838
|
-
};
|
5056
|
+
if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
|
5057
|
+
popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
|
5058
|
+
}
|
4839
5059
|
|
4840
|
-
|
4841
|
-
|
4842
|
-
|
5060
|
+
var resetMatches = function() {
|
5061
|
+
scope.matches = [];
|
5062
|
+
scope.activeIdx = -1;
|
5063
|
+
element.attr('aria-expanded', false);
|
5064
|
+
};
|
4843
5065
|
|
4844
|
-
|
4845
|
-
|
4846
|
-
|
4847
|
-
if (index < 0) {
|
4848
|
-
element.removeAttr('aria-activedescendant');
|
4849
|
-
} else {
|
4850
|
-
element.attr('aria-activedescendant', getMatchId(index));
|
4851
|
-
}
|
4852
|
-
});
|
5066
|
+
var getMatchId = function(index) {
|
5067
|
+
return popupId + '-option-' + index;
|
5068
|
+
};
|
4853
5069
|
|
4854
|
-
|
5070
|
+
// Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
|
5071
|
+
// This attribute is added or removed automatically when the `activeIdx` changes.
|
5072
|
+
scope.$watch('activeIdx', function(index) {
|
5073
|
+
if (index < 0) {
|
5074
|
+
element.removeAttr('aria-activedescendant');
|
5075
|
+
} else {
|
5076
|
+
element.attr('aria-activedescendant', getMatchId(index));
|
5077
|
+
}
|
5078
|
+
});
|
4855
5079
|
|
4856
|
-
|
4857
|
-
|
4858
|
-
|
5080
|
+
var inputIsExactMatch = function(inputValue, index) {
|
5081
|
+
if (scope.matches.length > index && inputValue) {
|
5082
|
+
return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
|
5083
|
+
}
|
4859
5084
|
|
4860
|
-
|
4861
|
-
|
5085
|
+
return false;
|
5086
|
+
};
|
4862
5087
|
|
4863
|
-
|
4864
|
-
|
4865
|
-
|
4866
|
-
|
4867
|
-
|
4868
|
-
|
4869
|
-
|
4870
|
-
|
4871
|
-
|
4872
|
-
|
4873
|
-
|
4874
|
-
|
4875
|
-
|
4876
|
-
|
4877
|
-
|
4878
|
-
|
4879
|
-
|
4880
|
-
|
4881
|
-
|
4882
|
-
|
4883
|
-
|
4884
|
-
|
4885
|
-
|
4886
|
-
|
4887
|
-
});
|
4888
|
-
}
|
5088
|
+
var getMatchesAsync = function(inputValue) {
|
5089
|
+
var locals = {$viewValue: inputValue};
|
5090
|
+
isLoadingSetter(originalScope, true);
|
5091
|
+
isNoResultsSetter(originalScope, false);
|
5092
|
+
$q.when(parserResult.source(originalScope, locals)).then(function(matches) {
|
5093
|
+
//it might happen that several async queries were in progress if a user were typing fast
|
5094
|
+
//but we are interested only in responses that correspond to the current view value
|
5095
|
+
var onCurrentRequest = (inputValue === modelCtrl.$viewValue);
|
5096
|
+
if (onCurrentRequest && hasFocus) {
|
5097
|
+
if (matches && matches.length > 0) {
|
5098
|
+
|
5099
|
+
scope.activeIdx = focusFirst ? 0 : -1;
|
5100
|
+
isNoResultsSetter(originalScope, false);
|
5101
|
+
scope.matches.length = 0;
|
5102
|
+
|
5103
|
+
//transform labels
|
5104
|
+
for (var i = 0; i < matches.length; i++) {
|
5105
|
+
locals[parserResult.itemName] = matches[i];
|
5106
|
+
scope.matches.push({
|
5107
|
+
id: getMatchId(i),
|
5108
|
+
label: parserResult.viewMapper(scope, locals),
|
5109
|
+
model: matches[i]
|
5110
|
+
});
|
5111
|
+
}
|
4889
5112
|
|
4890
|
-
|
4891
|
-
|
4892
|
-
|
4893
|
-
|
4894
|
-
|
5113
|
+
scope.query = inputValue;
|
5114
|
+
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
|
5115
|
+
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
|
5116
|
+
//due to other elements being rendered
|
5117
|
+
recalculatePosition();
|
4895
5118
|
|
4896
|
-
|
5119
|
+
element.attr('aria-expanded', true);
|
4897
5120
|
|
4898
|
-
|
4899
|
-
|
4900
|
-
|
5121
|
+
//Select the single remaining option if user input matches
|
5122
|
+
if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
|
5123
|
+
scope.select(0);
|
5124
|
+
}
|
5125
|
+
} else {
|
5126
|
+
resetMatches();
|
5127
|
+
isNoResultsSetter(originalScope, true);
|
4901
5128
|
}
|
4902
|
-
} else {
|
4903
|
-
resetMatches();
|
4904
|
-
isNoResultsSetter(originalScope, true);
|
4905
5129
|
}
|
4906
|
-
|
4907
|
-
|
5130
|
+
if (onCurrentRequest) {
|
5131
|
+
isLoadingSetter(originalScope, false);
|
5132
|
+
}
|
5133
|
+
}, function() {
|
5134
|
+
resetMatches();
|
4908
5135
|
isLoadingSetter(originalScope, false);
|
4909
|
-
|
4910
|
-
|
4911
|
-
|
4912
|
-
isLoadingSetter(originalScope, false);
|
4913
|
-
isNoResultsSetter(originalScope, true);
|
4914
|
-
});
|
4915
|
-
};
|
4916
|
-
|
4917
|
-
// bind events only if appendToBody params exist - performance feature
|
4918
|
-
if (appendToBody) {
|
4919
|
-
angular.element($window).bind('resize', fireRecalculating);
|
4920
|
-
$document.find('body').bind('scroll', fireRecalculating);
|
4921
|
-
}
|
5136
|
+
isNoResultsSetter(originalScope, true);
|
5137
|
+
});
|
5138
|
+
};
|
4922
5139
|
|
4923
|
-
|
4924
|
-
|
5140
|
+
// bind events only if appendToBody params exist - performance feature
|
5141
|
+
if (appendToBody) {
|
5142
|
+
angular.element($window).bind('resize', fireRecalculating);
|
5143
|
+
$document.find('body').bind('scroll', fireRecalculating);
|
5144
|
+
}
|
4925
5145
|
|
4926
|
-
|
4927
|
-
|
5146
|
+
// Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
|
5147
|
+
var timeoutEventPromise;
|
4928
5148
|
|
4929
|
-
|
4930
|
-
|
4931
|
-
scope.moveInProgress = true;
|
4932
|
-
scope.$digest();
|
4933
|
-
}
|
5149
|
+
// Default progress type
|
5150
|
+
scope.moveInProgress = false;
|
4934
5151
|
|
4935
|
-
|
4936
|
-
|
4937
|
-
|
4938
|
-
|
5152
|
+
function fireRecalculating() {
|
5153
|
+
if (!scope.moveInProgress) {
|
5154
|
+
scope.moveInProgress = true;
|
5155
|
+
scope.$digest();
|
5156
|
+
}
|
4939
5157
|
|
4940
|
-
|
4941
|
-
|
4942
|
-
|
4943
|
-
if (scope.matches.length) {
|
4944
|
-
recalculatePosition();
|
5158
|
+
// Cancel previous timeout
|
5159
|
+
if (timeoutEventPromise) {
|
5160
|
+
$timeout.cancel(timeoutEventPromise);
|
4945
5161
|
}
|
4946
5162
|
|
4947
|
-
|
4948
|
-
|
4949
|
-
|
4950
|
-
|
5163
|
+
// Debounced executing recalculate after events fired
|
5164
|
+
timeoutEventPromise = $timeout(function() {
|
5165
|
+
// if popup is visible
|
5166
|
+
if (scope.matches.length) {
|
5167
|
+
recalculatePosition();
|
5168
|
+
}
|
4951
5169
|
|
4952
|
-
|
4953
|
-
|
4954
|
-
|
4955
|
-
|
4956
|
-
scope.position.top += element.prop('offsetHeight');
|
4957
|
-
}
|
5170
|
+
scope.moveInProgress = false;
|
5171
|
+
scope.$digest();
|
5172
|
+
}, eventDebounceTime);
|
5173
|
+
}
|
4958
5174
|
|
4959
|
-
|
5175
|
+
// recalculate actual position and set new values to scope
|
5176
|
+
// after digest loop is popup in right position
|
5177
|
+
function recalculatePosition() {
|
5178
|
+
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
|
5179
|
+
scope.position.top += element.prop('offsetHeight');
|
5180
|
+
}
|
4960
5181
|
|
4961
|
-
|
4962
|
-
scope.query = undefined;
|
5182
|
+
resetMatches();
|
4963
5183
|
|
4964
|
-
|
4965
|
-
|
5184
|
+
//we need to propagate user's query so we can higlight matches
|
5185
|
+
scope.query = undefined;
|
4966
5186
|
|
4967
|
-
|
4968
|
-
timeoutPromise
|
4969
|
-
getMatchesAsync(inputValue);
|
4970
|
-
}, waitTime);
|
4971
|
-
};
|
5187
|
+
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
|
5188
|
+
var timeoutPromise;
|
4972
5189
|
|
4973
|
-
|
4974
|
-
|
4975
|
-
|
4976
|
-
|
4977
|
-
|
5190
|
+
var scheduleSearchWithTimeout = function(inputValue) {
|
5191
|
+
timeoutPromise = $timeout(function() {
|
5192
|
+
getMatchesAsync(inputValue);
|
5193
|
+
}, waitTime);
|
5194
|
+
};
|
4978
5195
|
|
4979
|
-
|
4980
|
-
|
4981
|
-
|
5196
|
+
var cancelPreviousTimeout = function() {
|
5197
|
+
if (timeoutPromise) {
|
5198
|
+
$timeout.cancel(timeoutPromise);
|
5199
|
+
}
|
5200
|
+
};
|
4982
5201
|
|
4983
|
-
|
5202
|
+
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
|
5203
|
+
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
|
5204
|
+
modelCtrl.$parsers.unshift(function(inputValue) {
|
5205
|
+
hasFocus = true;
|
4984
5206
|
|
4985
|
-
|
4986
|
-
|
4987
|
-
|
4988
|
-
|
5207
|
+
if (minLength === 0 || inputValue && inputValue.length >= minLength) {
|
5208
|
+
if (waitTime > 0) {
|
5209
|
+
cancelPreviousTimeout();
|
5210
|
+
scheduleSearchWithTimeout(inputValue);
|
5211
|
+
} else {
|
5212
|
+
getMatchesAsync(inputValue);
|
5213
|
+
}
|
4989
5214
|
} else {
|
4990
|
-
|
5215
|
+
isLoadingSetter(originalScope, false);
|
5216
|
+
cancelPreviousTimeout();
|
5217
|
+
resetMatches();
|
4991
5218
|
}
|
4992
|
-
} else {
|
4993
|
-
isLoadingSetter(originalScope, false);
|
4994
|
-
cancelPreviousTimeout();
|
4995
|
-
resetMatches();
|
4996
|
-
}
|
4997
5219
|
|
4998
|
-
|
4999
|
-
|
5000
|
-
} else {
|
5001
|
-
if (!inputValue) {
|
5002
|
-
// Reset in case user had typed something previously.
|
5003
|
-
modelCtrl.$setValidity('editable', true);
|
5004
|
-
return null;
|
5220
|
+
if (isEditable) {
|
5221
|
+
return inputValue;
|
5005
5222
|
} else {
|
5006
|
-
|
5007
|
-
|
5223
|
+
if (!inputValue) {
|
5224
|
+
// Reset in case user had typed something previously.
|
5225
|
+
modelCtrl.$setValidity('editable', true);
|
5226
|
+
return null;
|
5227
|
+
} else {
|
5228
|
+
modelCtrl.$setValidity('editable', false);
|
5229
|
+
return undefined;
|
5230
|
+
}
|
5008
5231
|
}
|
5009
|
-
}
|
5010
|
-
});
|
5011
|
-
|
5012
|
-
modelCtrl.$formatters.push(function (modelValue) {
|
5232
|
+
});
|
5013
5233
|
|
5014
|
-
|
5015
|
-
|
5234
|
+
modelCtrl.$formatters.push(function(modelValue) {
|
5235
|
+
var candidateViewValue, emptyViewValue;
|
5236
|
+
var locals = {};
|
5016
5237
|
|
5017
|
-
|
5018
|
-
|
5019
|
-
|
5020
|
-
|
5021
|
-
|
5022
|
-
|
5023
|
-
|
5024
|
-
if (inputFormatter) {
|
5238
|
+
// The validity may be set to false via $parsers (see above) if
|
5239
|
+
// the model is restricted to selected values. If the model
|
5240
|
+
// is set manually it is considered to be valid.
|
5241
|
+
if (!isEditable) {
|
5242
|
+
modelCtrl.$setValidity('editable', true);
|
5243
|
+
}
|
5025
5244
|
|
5026
|
-
|
5027
|
-
|
5245
|
+
if (inputFormatter) {
|
5246
|
+
locals.$model = modelValue;
|
5247
|
+
return inputFormatter(originalScope, locals);
|
5248
|
+
} else {
|
5249
|
+
//it might happen that we don't have enough info to properly render input value
|
5250
|
+
//we need to check for this situation and simply return model value if we can't apply custom formatting
|
5251
|
+
locals[parserResult.itemName] = modelValue;
|
5252
|
+
candidateViewValue = parserResult.viewMapper(originalScope, locals);
|
5253
|
+
locals[parserResult.itemName] = undefined;
|
5254
|
+
emptyViewValue = parserResult.viewMapper(originalScope, locals);
|
5255
|
+
|
5256
|
+
return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
|
5257
|
+
}
|
5258
|
+
});
|
5028
5259
|
|
5029
|
-
|
5260
|
+
scope.select = function(activeIdx) {
|
5261
|
+
//called from within the $digest() cycle
|
5262
|
+
var locals = {};
|
5263
|
+
var model, item;
|
5030
5264
|
|
5031
|
-
|
5032
|
-
|
5033
|
-
|
5034
|
-
|
5035
|
-
|
5036
|
-
|
5265
|
+
selected = true;
|
5266
|
+
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
|
5267
|
+
model = parserResult.modelMapper(originalScope, locals);
|
5268
|
+
$setModelValue(originalScope, model);
|
5269
|
+
modelCtrl.$setValidity('editable', true);
|
5270
|
+
modelCtrl.$setValidity('parse', true);
|
5037
5271
|
|
5038
|
-
|
5039
|
-
|
5040
|
-
|
5272
|
+
onSelectCallback(originalScope, {
|
5273
|
+
$item: item,
|
5274
|
+
$model: model,
|
5275
|
+
$label: parserResult.viewMapper(originalScope, locals)
|
5276
|
+
});
|
5041
5277
|
|
5042
|
-
|
5043
|
-
//called from within the $digest() cycle
|
5044
|
-
var locals = {};
|
5045
|
-
var model, item;
|
5046
|
-
|
5047
|
-
selected = true;
|
5048
|
-
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
|
5049
|
-
model = parserResult.modelMapper(originalScope, locals);
|
5050
|
-
$setModelValue(originalScope, model);
|
5051
|
-
modelCtrl.$setValidity('editable', true);
|
5052
|
-
modelCtrl.$setValidity('parse', true);
|
5053
|
-
|
5054
|
-
onSelectCallback(originalScope, {
|
5055
|
-
$item: item,
|
5056
|
-
$model: model,
|
5057
|
-
$label: parserResult.viewMapper(originalScope, locals)
|
5058
|
-
});
|
5278
|
+
resetMatches();
|
5059
5279
|
|
5060
|
-
|
5280
|
+
//return focus to the input element if a match was selected via a mouse click event
|
5281
|
+
// use timeout to avoid $rootScope:inprog error
|
5282
|
+
if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
|
5283
|
+
$timeout(function() { element[0].focus(); }, 0, false);
|
5284
|
+
}
|
5285
|
+
};
|
5061
5286
|
|
5062
|
-
//
|
5063
|
-
|
5064
|
-
|
5065
|
-
|
5287
|
+
//bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
|
5288
|
+
element.bind('keydown', function(evt) {
|
5289
|
+
//typeahead is open and an "interesting" key was pressed
|
5290
|
+
if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
|
5291
|
+
return;
|
5292
|
+
}
|
5066
5293
|
|
5067
|
-
|
5068
|
-
|
5294
|
+
// if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
|
5295
|
+
if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
|
5296
|
+
resetMatches();
|
5297
|
+
scope.$digest();
|
5298
|
+
return;
|
5299
|
+
}
|
5069
5300
|
|
5070
|
-
|
5071
|
-
if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
|
5072
|
-
return;
|
5073
|
-
}
|
5301
|
+
evt.preventDefault();
|
5074
5302
|
|
5075
|
-
|
5076
|
-
|
5077
|
-
|
5078
|
-
scope.$digest();
|
5079
|
-
return;
|
5080
|
-
}
|
5303
|
+
if (evt.which === 40) {
|
5304
|
+
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
|
5305
|
+
scope.$digest();
|
5081
5306
|
|
5082
|
-
|
5307
|
+
} else if (evt.which === 38) {
|
5308
|
+
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
|
5309
|
+
scope.$digest();
|
5083
5310
|
|
5084
|
-
|
5085
|
-
|
5086
|
-
|
5311
|
+
} else if (evt.which === 13 || evt.which === 9) {
|
5312
|
+
scope.$apply(function () {
|
5313
|
+
scope.select(scope.activeIdx);
|
5314
|
+
});
|
5087
5315
|
|
5088
|
-
|
5089
|
-
|
5090
|
-
scope.$digest();
|
5316
|
+
} else if (evt.which === 27) {
|
5317
|
+
evt.stopPropagation();
|
5091
5318
|
|
5092
|
-
|
5093
|
-
|
5094
|
-
|
5095
|
-
|
5319
|
+
resetMatches();
|
5320
|
+
scope.$digest();
|
5321
|
+
}
|
5322
|
+
});
|
5096
5323
|
|
5097
|
-
|
5098
|
-
|
5324
|
+
element.bind('blur', function() {
|
5325
|
+
if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
|
5326
|
+
selected = true;
|
5327
|
+
scope.$apply(function() {
|
5328
|
+
scope.select(scope.activeIdx);
|
5329
|
+
});
|
5330
|
+
}
|
5331
|
+
hasFocus = false;
|
5332
|
+
selected = false;
|
5333
|
+
});
|
5099
5334
|
|
5100
|
-
|
5101
|
-
|
5102
|
-
|
5103
|
-
|
5335
|
+
// Keep reference to click handler to unbind it.
|
5336
|
+
var dismissClickHandler = function(evt) {
|
5337
|
+
// Issue #3973
|
5338
|
+
// Firefox treats right click as a click on document
|
5339
|
+
if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
|
5340
|
+
resetMatches();
|
5341
|
+
if (!$rootScope.$$phase) {
|
5342
|
+
scope.$digest();
|
5343
|
+
}
|
5344
|
+
}
|
5345
|
+
};
|
5104
5346
|
|
5105
|
-
|
5106
|
-
if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
|
5107
|
-
selected = true;
|
5108
|
-
scope.$apply(function () {
|
5109
|
-
scope.select(scope.activeIdx);
|
5110
|
-
});
|
5111
|
-
}
|
5112
|
-
hasFocus = false;
|
5113
|
-
selected = false;
|
5114
|
-
});
|
5347
|
+
$document.bind('click', dismissClickHandler);
|
5115
5348
|
|
5116
|
-
|
5117
|
-
|
5118
|
-
|
5119
|
-
|
5120
|
-
if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
|
5121
|
-
resetMatches();
|
5122
|
-
if (!$rootScope.$$phase) {
|
5123
|
-
scope.$digest();
|
5349
|
+
originalScope.$on('$destroy', function() {
|
5350
|
+
$document.unbind('click', dismissClickHandler);
|
5351
|
+
if (appendToBody) {
|
5352
|
+
$popup.remove();
|
5124
5353
|
}
|
5125
|
-
|
5126
|
-
|
5354
|
+
// Prevent jQuery cache memory leak
|
5355
|
+
popUpEl.remove();
|
5356
|
+
});
|
5127
5357
|
|
5128
|
-
|
5358
|
+
var $popup = $compile(popUpEl)(scope);
|
5129
5359
|
|
5130
|
-
originalScope.$on('$destroy', function(){
|
5131
|
-
$document.unbind('click', dismissClickHandler);
|
5132
5360
|
if (appendToBody) {
|
5133
|
-
$
|
5361
|
+
$document.find('body').append($popup);
|
5362
|
+
} else {
|
5363
|
+
element.after($popup);
|
5134
5364
|
}
|
5135
|
-
// Prevent jQuery cache memory leak
|
5136
|
-
popUpEl.remove();
|
5137
|
-
});
|
5138
|
-
|
5139
|
-
var $popup = $compile(popUpEl)(scope);
|
5140
|
-
|
5141
|
-
if (appendToBody) {
|
5142
|
-
$document.find('body').append($popup);
|
5143
|
-
} else {
|
5144
|
-
element.after($popup);
|
5145
5365
|
}
|
5146
|
-
}
|
5147
|
-
};
|
5366
|
+
};
|
5148
5367
|
|
5149
|
-
}])
|
5368
|
+
}])
|
5150
5369
|
|
5151
|
-
.directive('typeaheadPopup', function
|
5370
|
+
.directive('typeaheadPopup', function() {
|
5152
5371
|
return {
|
5153
|
-
restrict:'EA',
|
5154
|
-
scope:{
|
5155
|
-
matches:'=',
|
5156
|
-
query:'=',
|
5157
|
-
active:'=',
|
5158
|
-
position:'&',
|
5159
|
-
moveInProgress:'=',
|
5160
|
-
select:'&'
|
5372
|
+
restrict: 'EA',
|
5373
|
+
scope: {
|
5374
|
+
matches: '=',
|
5375
|
+
query: '=',
|
5376
|
+
active: '=',
|
5377
|
+
position: '&',
|
5378
|
+
moveInProgress: '=',
|
5379
|
+
select: '&'
|
5161
5380
|
},
|
5162
|
-
replace:true,
|
5163
|
-
templateUrl:
|
5164
|
-
|
5165
|
-
|
5381
|
+
replace: true,
|
5382
|
+
templateUrl: function(element, attrs) {
|
5383
|
+
return attrs.popupTemplateUrl || 'template/typeahead/typeahead-popup.html';
|
5384
|
+
},
|
5385
|
+
link: function(scope, element, attrs) {
|
5166
5386
|
scope.templateUrl = attrs.templateUrl;
|
5167
5387
|
|
5168
|
-
scope.isOpen = function
|
5388
|
+
scope.isOpen = function() {
|
5169
5389
|
return scope.matches.length > 0;
|
5170
5390
|
};
|
5171
5391
|
|
5172
|
-
scope.isActive = function
|
5392
|
+
scope.isActive = function(matchIdx) {
|
5173
5393
|
return scope.active == matchIdx;
|
5174
5394
|
};
|
5175
5395
|
|
5176
|
-
scope.selectActive = function
|
5396
|
+
scope.selectActive = function(matchIdx) {
|
5177
5397
|
scope.active = matchIdx;
|
5178
5398
|
};
|
5179
5399
|
|
5180
|
-
scope.selectMatch = function
|
5400
|
+
scope.selectMatch = function(activeIdx) {
|
5181
5401
|
scope.select({activeIdx:activeIdx});
|
5182
5402
|
};
|
5183
5403
|
}
|
5184
5404
|
};
|
5185
5405
|
})
|
5186
5406
|
|
5187
|
-
.directive('typeaheadMatch', ['$templateRequest', '$compile', '$parse', function
|
5407
|
+
.directive('typeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
|
5188
5408
|
return {
|
5189
|
-
restrict:'EA',
|
5190
|
-
scope:{
|
5191
|
-
index:'=',
|
5192
|
-
match:'=',
|
5193
|
-
query:'='
|
5409
|
+
restrict: 'EA',
|
5410
|
+
scope: {
|
5411
|
+
index: '=',
|
5412
|
+
match: '=',
|
5413
|
+
query: '='
|
5194
5414
|
},
|
5195
|
-
link:function
|
5415
|
+
link:function(scope, element, attrs) {
|
5196
5416
|
var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';
|
5197
5417
|
$templateRequest(tplUrl).then(function(tplContent) {
|
5198
|
-
$compile(tplContent.trim())(scope, function(clonedElement){
|
5418
|
+
$compile(tplContent.trim())(scope, function(clonedElement) {
|
5199
5419
|
element.replaceWith(clonedElement);
|
5200
5420
|
});
|
5201
5421
|
});
|
@@ -5203,27 +5423,42 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
|
|
5203
5423
|
};
|
5204
5424
|
}])
|
5205
5425
|
|
5206
|
-
.filter('typeaheadHighlight', function() {
|
5426
|
+
.filter('typeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
|
5427
|
+
var isSanitizePresent;
|
5428
|
+
isSanitizePresent = $injector.has('$sanitize');
|
5207
5429
|
|
5208
5430
|
function escapeRegexp(queryToEscape) {
|
5431
|
+
// Regex: capture the whole query string and replace it with the string that will be used to match
|
5432
|
+
// the results, for example if the capture is "a" the result will be \a
|
5209
5433
|
return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
|
5210
5434
|
}
|
5211
5435
|
|
5436
|
+
function containsHtml(matchItem) {
|
5437
|
+
return /<.*>/g.test(matchItem);
|
5438
|
+
}
|
5439
|
+
|
5212
5440
|
return function(matchItem, query) {
|
5213
|
-
|
5441
|
+
if (!isSanitizePresent && containsHtml(matchItem)) {
|
5442
|
+
$log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
|
5443
|
+
}
|
5444
|
+
matchItem = query? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
|
5445
|
+
if (!isSanitizePresent) {
|
5446
|
+
matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
|
5447
|
+
}
|
5448
|
+
return matchItem;
|
5214
5449
|
};
|
5215
|
-
});
|
5450
|
+
}]);
|
5216
5451
|
|
5217
5452
|
angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
|
5218
5453
|
$templateCache.put("template/accordion/accordion-group.html",
|
5219
|
-
"<div class=\"panel
|
5220
|
-
" <div class=\"panel-heading\">\n" +
|
5454
|
+
"<div class=\"panel {{panelClass || 'panel-default'}}\">\n" +
|
5455
|
+
" <div class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" +
|
5221
5456
|
" <h4 class=\"panel-title\">\n" +
|
5222
5457
|
" <a href tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
|
5223
5458
|
" </h4>\n" +
|
5224
5459
|
" </div>\n" +
|
5225
5460
|
" <div class=\"panel-collapse collapse\" collapse=\"!isOpen\">\n" +
|
5226
|
-
"
|
5461
|
+
" <div class=\"panel-body\" ng-transclude></div>\n" +
|
5227
5462
|
" </div>\n" +
|
5228
5463
|
"</div>\n" +
|
5229
5464
|
"");
|
@@ -5326,14 +5561,14 @@ angular.module("template/datepicker/month.html", []).run(["$templateCache", func
|
|
5326
5561
|
angular.module("template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
|
5327
5562
|
$templateCache.put("template/datepicker/popup.html",
|
5328
5563
|
"<ul class=\"dropdown-menu\" ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" +
|
5329
|
-
"
|
5330
|
-
"
|
5331
|
-
"
|
5332
|
-
"
|
5333
|
-
"
|
5334
|
-
"
|
5335
|
-
"
|
5336
|
-
"
|
5564
|
+
" <li ng-transclude></li>\n" +
|
5565
|
+
" <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\n" +
|
5566
|
+
" <span class=\"btn-group pull-left\">\n" +
|
5567
|
+
" <button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
|
5568
|
+
" <button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
|
5569
|
+
" </span>\n" +
|
5570
|
+
" <button type=\"button\" class=\"btn btn-sm btn-success pull-right\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
|
5571
|
+
" </li>\n" +
|
5337
5572
|
"</ul>\n" +
|
5338
5573
|
"");
|
5339
5574
|
}]);
|
@@ -5374,7 +5609,7 @@ angular.module("template/modal/window.html", []).run(["$templateCache", function
|
|
5374
5609
|
"<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
|
5375
5610
|
" modal-animation-class=\"fade\"\n" +
|
5376
5611
|
" modal-in-class=\"in\"\n" +
|
5377
|
-
"
|
5612
|
+
" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
|
5378
5613
|
" <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" modal-transclude></div></div>\n" +
|
5379
5614
|
"</div>\n" +
|
5380
5615
|
"");
|
@@ -5383,9 +5618,10 @@ angular.module("template/modal/window.html", []).run(["$templateCache", function
|
|
5383
5618
|
angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
|
5384
5619
|
$templateCache.put("template/pagination/pager.html",
|
5385
5620
|
"<ul class=\"pager\">\n" +
|
5386
|
-
" <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
|
5387
|
-
" <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
|
5388
|
-
"</ul
|
5621
|
+
" <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
|
5622
|
+
" <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
|
5623
|
+
"</ul>\n" +
|
5624
|
+
"");
|
5389
5625
|
}]);
|
5390
5626
|
|
5391
5627
|
angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
|
@@ -5584,7 +5820,7 @@ angular.module("template/timepicker/timepicker.html", []).run(["$templateCache",
|
|
5584
5820
|
|
5585
5821
|
angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
|
5586
5822
|
$templateCache.put("template/typeahead/typeahead-match.html",
|
5587
|
-
"<a href tabindex=\"-1\" bind-html
|
5823
|
+
"<a href tabindex=\"-1\" ng-bind-html=\"match.label | typeaheadHighlight:query\"></a>\n" +
|
5588
5824
|
"");
|
5589
5825
|
}]);
|
5590
5826
|
|
@@ -5597,4 +5833,4 @@ angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCac
|
|
5597
5833
|
"</ul>\n" +
|
5598
5834
|
"");
|
5599
5835
|
}]);
|
5600
|
-
!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>');
|
5836
|
+
!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>');
|