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