angular-ui-bootstrap-rails 0.13.3 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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>');
|