1 | /* |
||
2 | * angular-ui-bootstrap |
||
3 | * http://angular-ui.github.io/bootstrap/ |
||
4 | |||
5 | * Version: 1.3.2 - 2016-04-14 |
||
6 | * License: MIT |
||
7 | */angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]); |
||
8 | angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/backdrop.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]); |
||
9 | angular.module('ui.bootstrap.collapse', []) |
||
10 | |||
11 | View Code Duplication | .directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) { |
|
12 | var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; |
||
13 | return { |
||
14 | link: function(scope, element, attrs) { |
||
15 | var expandingExpr = $parse(attrs.expanding), |
||
16 | expandedExpr = $parse(attrs.expanded), |
||
17 | collapsingExpr = $parse(attrs.collapsing), |
||
18 | collapsedExpr = $parse(attrs.collapsed); |
||
19 | |||
20 | if (!scope.$eval(attrs.uibCollapse)) { |
||
21 | element.addClass('in') |
||
22 | .addClass('collapse') |
||
23 | .attr('aria-expanded', true) |
||
24 | .attr('aria-hidden', false) |
||
25 | .css({height: 'auto'}); |
||
26 | } |
||
27 | |||
28 | function expand() { |
||
29 | if (element.hasClass('collapse') && element.hasClass('in')) { |
||
30 | return; |
||
31 | } |
||
32 | |||
33 | $q.resolve(expandingExpr(scope)) |
||
34 | .then(function() { |
||
35 | element.removeClass('collapse') |
||
36 | .addClass('collapsing') |
||
37 | .attr('aria-expanded', true) |
||
38 | .attr('aria-hidden', false); |
||
39 | |||
40 | if ($animateCss) { |
||
41 | $animateCss(element, { |
||
42 | addClass: 'in', |
||
43 | easing: 'ease', |
||
44 | to: { height: element[0].scrollHeight + 'px' } |
||
45 | }).start()['finally'](expandDone); |
||
46 | } else { |
||
47 | $animate.addClass(element, 'in', { |
||
48 | to: { height: element[0].scrollHeight + 'px' } |
||
49 | }).then(expandDone); |
||
50 | } |
||
51 | }); |
||
52 | } |
||
53 | |||
54 | function expandDone() { |
||
55 | element.removeClass('collapsing') |
||
56 | .addClass('collapse') |
||
57 | .css({height: 'auto'}); |
||
58 | expandedExpr(scope); |
||
59 | } |
||
60 | |||
61 | function collapse() { |
||
62 | if (!element.hasClass('collapse') && !element.hasClass('in')) { |
||
63 | return collapseDone(); |
||
64 | } |
||
65 | |||
66 | $q.resolve(collapsingExpr(scope)) |
||
67 | .then(function() { |
||
68 | element |
||
69 | // IMPORTANT: The height must be set before adding "collapsing" class. |
||
70 | // Otherwise, the browser attempts to animate from height 0 (in |
||
71 | // collapsing class) to the given height here. |
||
72 | .css({height: element[0].scrollHeight + 'px'}) |
||
73 | // initially all panel collapse have the collapse class, this removal |
||
74 | // prevents the animation from jumping to collapsed state |
||
75 | .removeClass('collapse') |
||
76 | .addClass('collapsing') |
||
77 | .attr('aria-expanded', false) |
||
78 | .attr('aria-hidden', true); |
||
79 | |||
80 | if ($animateCss) { |
||
81 | $animateCss(element, { |
||
82 | removeClass: 'in', |
||
83 | to: {height: '0'} |
||
84 | }).start()['finally'](collapseDone); |
||
85 | } else { |
||
86 | $animate.removeClass(element, 'in', { |
||
87 | to: {height: '0'} |
||
88 | }).then(collapseDone); |
||
89 | } |
||
90 | }); |
||
91 | } |
||
92 | |||
93 | function collapseDone() { |
||
94 | element.css({height: '0'}); // Required so that collapse works when animation is disabled |
||
95 | element.removeClass('collapsing') |
||
96 | .addClass('collapse'); |
||
97 | collapsedExpr(scope); |
||
98 | } |
||
99 | |||
100 | scope.$watch(attrs.uibCollapse, function(shouldCollapse) { |
||
101 | if (shouldCollapse) { |
||
102 | collapse(); |
||
103 | } else { |
||
104 | expand(); |
||
105 | } |
||
106 | }); |
||
107 | } |
||
108 | }; |
||
109 | }]); |
||
110 | |||
111 | angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse']) |
||
112 | |||
113 | .constant('uibAccordionConfig', { |
||
114 | closeOthers: true |
||
115 | }) |
||
116 | |||
117 | View Code Duplication | .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) { |
|
118 | // This array keeps track of the accordion groups |
||
119 | this.groups = []; |
||
120 | |||
121 | // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to |
||
122 | this.closeOthers = function(openGroup) { |
||
123 | var closeOthers = angular.isDefined($attrs.closeOthers) ? |
||
124 | $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers; |
||
125 | if (closeOthers) { |
||
126 | angular.forEach(this.groups, function(group) { |
||
127 | if (group !== openGroup) { |
||
128 | group.isOpen = false; |
||
129 | } |
||
130 | }); |
||
131 | } |
||
132 | }; |
||
133 | |||
134 | // This is called from the accordion-group directive to add itself to the accordion |
||
135 | this.addGroup = function(groupScope) { |
||
136 | var that = this; |
||
137 | this.groups.push(groupScope); |
||
138 | |||
139 | groupScope.$on('$destroy', function(event) { |
||
140 | that.removeGroup(groupScope); |
||
141 | }); |
||
142 | }; |
||
143 | |||
144 | // This is called from the accordion-group directive when to remove itself |
||
145 | this.removeGroup = function(group) { |
||
146 | var index = this.groups.indexOf(group); |
||
147 | if (index !== -1) { |
||
148 | this.groups.splice(index, 1); |
||
149 | } |
||
150 | }; |
||
151 | }]) |
||
152 | |||
153 | // The accordion directive simply sets up the directive controller |
||
154 | // and adds an accordion CSS class to itself element. |
||
155 | .directive('uibAccordion', function() { |
||
156 | return { |
||
157 | controller: 'UibAccordionController', |
||
158 | controllerAs: 'accordion', |
||
159 | transclude: true, |
||
160 | templateUrl: function(element, attrs) { |
||
161 | return attrs.templateUrl || 'uib/template/accordion/accordion.html'; |
||
162 | } |
||
163 | }; |
||
164 | }) |
||
165 | |||
166 | // The accordion-group directive indicates a block of html that will expand and collapse in an accordion |
||
167 | View Code Duplication | .directive('uibAccordionGroup', function() { |
|
168 | return { |
||
169 | require: '^uibAccordion', // We need this directive to be inside an accordion |
||
170 | transclude: true, // It transcludes the contents of the directive into the template |
||
171 | replace: true, // The element containing the directive will be replaced with the template |
||
172 | templateUrl: function(element, attrs) { |
||
173 | return attrs.templateUrl || 'uib/template/accordion/accordion-group.html'; |
||
174 | }, |
||
175 | scope: { |
||
176 | heading: '@', // Interpolate the heading attribute onto this scope |
||
177 | panelClass: '@?', // Ditto with panelClass |
||
178 | isOpen: '=?', |
||
179 | isDisabled: '=?' |
||
180 | }, |
||
181 | controller: function() { |
||
182 | this.setHeading = function(element) { |
||
183 | this.heading = element; |
||
184 | }; |
||
185 | }, |
||
186 | link: function(scope, element, attrs, accordionCtrl) { |
||
187 | accordionCtrl.addGroup(scope); |
||
188 | |||
189 | scope.openClass = attrs.openClass || 'panel-open'; |
||
190 | scope.panelClass = attrs.panelClass || 'panel-default'; |
||
191 | scope.$watch('isOpen', function(value) { |
||
192 | element.toggleClass(scope.openClass, !!value); |
||
193 | if (value) { |
||
194 | accordionCtrl.closeOthers(scope); |
||
195 | } |
||
196 | }); |
||
197 | |||
198 | scope.toggleOpen = function($event) { |
||
199 | if (!scope.isDisabled) { |
||
200 | if (!$event || $event.which === 32) { |
||
201 | scope.isOpen = !scope.isOpen; |
||
202 | } |
||
203 | } |
||
204 | }; |
||
205 | |||
206 | var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000); |
||
207 | scope.headingId = id + '-tab'; |
||
208 | scope.panelId = id + '-panel'; |
||
209 | } |
||
210 | }; |
||
211 | }) |
||
212 | |||
213 | // Use accordion-heading below an accordion-group to provide a heading containing HTML |
||
214 | .directive('uibAccordionHeading', function() { |
||
215 | return { |
||
216 | transclude: true, // Grab the contents to be used as the heading |
||
217 | template: '', // In effect remove this element! |
||
218 | replace: true, |
||
219 | require: '^uibAccordionGroup', |
||
220 | link: function(scope, element, attrs, accordionGroupCtrl, transclude) { |
||
221 | // Pass the heading to the accordion-group controller |
||
222 | // so that it can be transcluded into the right place in the template |
||
223 | // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] |
||
224 | accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); |
||
225 | } |
||
226 | }; |
||
227 | }) |
||
228 | |||
229 | // Use in the accordion-group template to indicate where you want the heading to be transcluded |
||
230 | // You must provide the property on the accordion-group controller that will hold the transcluded element |
||
231 | .directive('uibAccordionTransclude', function() { |
||
232 | return { |
||
233 | require: '^uibAccordionGroup', |
||
234 | link: function(scope, element, attrs, controller) { |
||
235 | scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) { |
||
236 | if (heading) { |
||
237 | var elem = angular.element(element[0].querySelector('[uib-accordion-header]')); |
||
238 | elem.html(''); |
||
239 | elem.append(heading); |
||
240 | } |
||
241 | }); |
||
242 | } |
||
243 | }; |
||
244 | }); |
||
245 | |||
246 | angular.module('ui.bootstrap.alert', []) |
||
247 | |||
248 | .controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) { |
||
249 | $scope.closeable = !!$attrs.close; |
||
250 | |||
251 | var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ? |
||
252 | $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null; |
||
253 | |||
254 | if (dismissOnTimeout) { |
||
255 | $timeout(function() { |
||
256 | $scope.close(); |
||
257 | }, parseInt(dismissOnTimeout, 10)); |
||
258 | } |
||
259 | }]) |
||
260 | |||
261 | .directive('uibAlert', function() { |
||
262 | return { |
||
263 | controller: 'UibAlertController', |
||
264 | controllerAs: 'alert', |
||
265 | templateUrl: function(element, attrs) { |
||
266 | return attrs.templateUrl || 'uib/template/alert/alert.html'; |
||
267 | }, |
||
268 | transclude: true, |
||
269 | replace: true, |
||
270 | scope: { |
||
271 | type: '@', |
||
272 | close: '&' |
||
273 | } |
||
274 | }; |
||
275 | }); |
||
276 | |||
277 | angular.module('ui.bootstrap.buttons', []) |
||
278 | |||
279 | .constant('uibButtonConfig', { |
||
280 | activeClass: 'active', |
||
281 | toggleEvent: 'click' |
||
282 | }) |
||
283 | |||
284 | .controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) { |
||
285 | this.activeClass = buttonConfig.activeClass || 'active'; |
||
286 | this.toggleEvent = buttonConfig.toggleEvent || 'click'; |
||
287 | }]) |
||
288 | |||
289 | View Code Duplication | .directive('uibBtnRadio', ['$parse', function($parse) { |
|
290 | return { |
||
291 | require: ['uibBtnRadio', 'ngModel'], |
||
292 | controller: 'UibButtonsController', |
||
293 | controllerAs: 'buttons', |
||
294 | link: function(scope, element, attrs, ctrls) { |
||
295 | var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; |
||
296 | var uncheckableExpr = $parse(attrs.uibUncheckable); |
||
297 | |||
298 | element.find('input').css({display: 'none'}); |
||
299 | |||
300 | //model -> UI |
||
301 | ngModelCtrl.$render = function() { |
||
302 | element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio))); |
||
303 | }; |
||
304 | |||
305 | //ui->model |
||
306 | element.on(buttonsCtrl.toggleEvent, function() { |
||
307 | if (attrs.disabled) { |
||
308 | return; |
||
309 | } |
||
310 | |||
311 | var isActive = element.hasClass(buttonsCtrl.activeClass); |
||
312 | |||
313 | if (!isActive || angular.isDefined(attrs.uncheckable)) { |
||
314 | scope.$apply(function() { |
||
315 | ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio)); |
||
316 | ngModelCtrl.$render(); |
||
317 | }); |
||
318 | } |
||
319 | }); |
||
320 | |||
321 | if (attrs.uibUncheckable) { |
||
322 | scope.$watch(uncheckableExpr, function(uncheckable) { |
||
323 | attrs.$set('uncheckable', uncheckable ? '' : undefined); |
||
324 | }); |
||
325 | } |
||
326 | } |
||
327 | }; |
||
328 | }]) |
||
329 | |||
330 | View Code Duplication | .directive('uibBtnCheckbox', function() { |
|
331 | return { |
||
332 | require: ['uibBtnCheckbox', 'ngModel'], |
||
333 | controller: 'UibButtonsController', |
||
334 | controllerAs: 'button', |
||
335 | link: function(scope, element, attrs, ctrls) { |
||
336 | var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; |
||
337 | |||
338 | element.find('input').css({display: 'none'}); |
||
339 | |||
340 | function getTrueValue() { |
||
341 | return getCheckboxValue(attrs.btnCheckboxTrue, true); |
||
342 | } |
||
343 | |||
344 | function getFalseValue() { |
||
345 | return getCheckboxValue(attrs.btnCheckboxFalse, false); |
||
346 | } |
||
347 | |||
348 | function getCheckboxValue(attribute, defaultValue) { |
||
349 | return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue; |
||
350 | } |
||
351 | |||
352 | //model -> UI |
||
353 | ngModelCtrl.$render = function() { |
||
354 | element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); |
||
355 | }; |
||
356 | |||
357 | //ui->model |
||
358 | element.on(buttonsCtrl.toggleEvent, function() { |
||
359 | if (attrs.disabled) { |
||
360 | return; |
||
361 | } |
||
362 | |||
363 | scope.$apply(function() { |
||
364 | ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); |
||
365 | ngModelCtrl.$render(); |
||
366 | }); |
||
367 | }); |
||
368 | } |
||
369 | }; |
||
370 | }); |
||
371 | |||
372 | angular.module('ui.bootstrap.carousel', []) |
||
373 | |||
374 | View Code Duplication | .controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) { |
|
375 | var self = this, |
||
376 | slides = self.slides = $scope.slides = [], |
||
377 | SLIDE_DIRECTION = 'uib-slideDirection', |
||
378 | currentIndex = $scope.active, |
||
379 | currentInterval, isPlaying, bufferedTransitions = []; |
||
380 | |||
381 | var destroyed = false; |
||
382 | |||
383 | self.addSlide = function(slide, element) { |
||
384 | slides.push({ |
||
385 | slide: slide, |
||
386 | element: element |
||
387 | }); |
||
388 | slides.sort(function(a, b) { |
||
389 | return +a.slide.index - +b.slide.index; |
||
390 | }); |
||
391 | //if this is the first slide or the slide is set to active, select it |
||
392 | if (slide.index === $scope.active || slides.length === 1 && !angular.isNumber($scope.active)) { |
||
393 | if ($scope.$currentTransition) { |
||
394 | $scope.$currentTransition = null; |
||
395 | } |
||
396 | |||
397 | currentIndex = slide.index; |
||
398 | $scope.active = slide.index; |
||
399 | setActive(currentIndex); |
||
400 | self.select(slides[findSlideIndex(slide)]); |
||
401 | if (slides.length === 1) { |
||
402 | $scope.play(); |
||
403 | } |
||
404 | } |
||
405 | }; |
||
406 | |||
407 | self.getCurrentIndex = function() { |
||
408 | for (var i = 0; i < slides.length; i++) { |
||
409 | if (slides[i].slide.index === currentIndex) { |
||
410 | return i; |
||
411 | } |
||
412 | } |
||
413 | }; |
||
414 | |||
415 | self.next = $scope.next = function() { |
||
416 | var newIndex = (self.getCurrentIndex() + 1) % slides.length; |
||
417 | |||
418 | if (newIndex === 0 && $scope.noWrap()) { |
||
419 | $scope.pause(); |
||
420 | return; |
||
421 | } |
||
422 | |||
423 | return self.select(slides[newIndex], 'next'); |
||
424 | }; |
||
425 | |||
426 | self.prev = $scope.prev = function() { |
||
427 | var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; |
||
428 | |||
429 | if ($scope.noWrap() && newIndex === slides.length - 1) { |
||
430 | $scope.pause(); |
||
431 | return; |
||
432 | } |
||
433 | |||
434 | return self.select(slides[newIndex], 'prev'); |
||
435 | }; |
||
436 | |||
437 | self.removeSlide = function(slide) { |
||
438 | var index = findSlideIndex(slide); |
||
439 | |||
440 | var bufferedIndex = bufferedTransitions.indexOf(slides[index]); |
||
441 | if (bufferedIndex !== -1) { |
||
442 | bufferedTransitions.splice(bufferedIndex, 1); |
||
443 | } |
||
444 | |||
445 | //get the index of the slide inside the carousel |
||
446 | slides.splice(index, 1); |
||
447 | if (slides.length > 0 && currentIndex === index) { |
||
448 | if (index >= slides.length) { |
||
449 | currentIndex = slides.length - 1; |
||
450 | $scope.active = currentIndex; |
||
451 | setActive(currentIndex); |
||
452 | self.select(slides[slides.length - 1]); |
||
453 | } else { |
||
454 | currentIndex = index; |
||
455 | $scope.active = currentIndex; |
||
456 | setActive(currentIndex); |
||
457 | self.select(slides[index]); |
||
458 | } |
||
459 | } else if (currentIndex > index) { |
||
460 | currentIndex--; |
||
461 | $scope.active = currentIndex; |
||
462 | } |
||
463 | |||
464 | //clean the active value when no more slide |
||
465 | if (slides.length === 0) { |
||
466 | currentIndex = null; |
||
467 | $scope.active = null; |
||
468 | clearBufferedTransitions(); |
||
469 | } |
||
470 | }; |
||
471 | |||
472 | /* direction: "prev" or "next" */ |
||
473 | self.select = $scope.select = function(nextSlide, direction) { |
||
474 | var nextIndex = findSlideIndex(nextSlide.slide); |
||
475 | //Decide direction if it's not given |
||
476 | if (direction === undefined) { |
||
477 | direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; |
||
478 | } |
||
479 | //Prevent this user-triggered transition from occurring if there is already one in progress |
||
480 | if (nextSlide.slide.index !== currentIndex && |
||
481 | !$scope.$currentTransition) { |
||
482 | goNext(nextSlide.slide, nextIndex, direction); |
||
483 | } else if (nextSlide && nextSlide.slide.index !== currentIndex && $scope.$currentTransition) { |
||
484 | bufferedTransitions.push(slides[nextIndex]); |
||
485 | } |
||
486 | }; |
||
487 | |||
488 | /* Allow outside people to call indexOf on slides array */ |
||
489 | $scope.indexOfSlide = function(slide) { |
||
490 | return +slide.slide.index; |
||
491 | }; |
||
492 | |||
493 | $scope.isActive = function(slide) { |
||
494 | return $scope.active === slide.slide.index; |
||
495 | }; |
||
496 | |||
497 | $scope.isPrevDisabled = function() { |
||
498 | return $scope.active === 0 && $scope.noWrap(); |
||
499 | }; |
||
500 | |||
501 | $scope.isNextDisabled = function() { |
||
502 | return $scope.active === slides.length - 1 && $scope.noWrap(); |
||
503 | }; |
||
504 | |||
505 | $scope.pause = function() { |
||
506 | if (!$scope.noPause) { |
||
507 | isPlaying = false; |
||
508 | resetTimer(); |
||
509 | } |
||
510 | }; |
||
511 | |||
512 | $scope.play = function() { |
||
513 | if (!isPlaying) { |
||
514 | isPlaying = true; |
||
515 | restartTimer(); |
||
516 | } |
||
517 | }; |
||
518 | |||
519 | $scope.$on('$destroy', function() { |
||
520 | destroyed = true; |
||
521 | resetTimer(); |
||
522 | }); |
||
523 | |||
524 | $scope.$watch('noTransition', function(noTransition) { |
||
525 | $animate.enabled($element, !noTransition); |
||
526 | }); |
||
527 | |||
528 | $scope.$watch('interval', restartTimer); |
||
529 | |||
530 | $scope.$watchCollection('slides', resetTransition); |
||
531 | |||
532 | $scope.$watch('active', function(index) { |
||
533 | if (angular.isNumber(index) && currentIndex !== index) { |
||
534 | for (var i = 0; i < slides.length; i++) { |
||
535 | if (slides[i].slide.index === index) { |
||
536 | index = i; |
||
537 | break; |
||
538 | } |
||
539 | } |
||
540 | |||
541 | var slide = slides[index]; |
||
542 | if (slide) { |
||
543 | setActive(index); |
||
544 | self.select(slides[index]); |
||
545 | currentIndex = index; |
||
546 | } |
||
547 | } |
||
548 | }); |
||
549 | |||
550 | function clearBufferedTransitions() { |
||
551 | while (bufferedTransitions.length) { |
||
552 | bufferedTransitions.shift(); |
||
553 | } |
||
554 | } |
||
555 | |||
556 | function getSlideByIndex(index) { |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
557 | for (var i = 0, l = slides.length; i < l; ++i) { |
||
558 | if (slides[i].index === index) { |
||
559 | return slides[i]; |
||
560 | } |
||
561 | } |
||
562 | } |
||
563 | |||
564 | function setActive(index) { |
||
565 | for (var i = 0; i < slides.length; i++) { |
||
566 | slides[i].slide.active = i === index; |
||
567 | } |
||
568 | } |
||
569 | |||
570 | function goNext(slide, index, direction) { |
||
571 | if (destroyed) { |
||
572 | return; |
||
573 | } |
||
574 | |||
575 | angular.extend(slide, {direction: direction}); |
||
576 | angular.extend(slides[currentIndex].slide || {}, {direction: direction}); |
||
577 | if ($animate.enabled($element) && !$scope.$currentTransition && |
||
578 | slides[index].element && self.slides.length > 1) { |
||
579 | slides[index].element.data(SLIDE_DIRECTION, slide.direction); |
||
580 | var currentIdx = self.getCurrentIndex(); |
||
581 | |||
582 | if (angular.isNumber(currentIdx) && slides[currentIdx].element) { |
||
583 | slides[currentIdx].element.data(SLIDE_DIRECTION, slide.direction); |
||
584 | } |
||
585 | |||
586 | $scope.$currentTransition = true; |
||
587 | $animate.on('addClass', slides[index].element, function(element, phase) { |
||
588 | if (phase === 'close') { |
||
589 | $scope.$currentTransition = null; |
||
590 | $animate.off('addClass', element); |
||
591 | if (bufferedTransitions.length) { |
||
592 | var nextSlide = bufferedTransitions.pop().slide; |
||
593 | var nextIndex = nextSlide.index; |
||
594 | var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; |
||
595 | clearBufferedTransitions(); |
||
596 | |||
597 | goNext(nextSlide, nextIndex, nextDirection); |
||
598 | } |
||
599 | } |
||
600 | }); |
||
601 | } |
||
602 | |||
603 | $scope.active = slide.index; |
||
604 | currentIndex = slide.index; |
||
605 | setActive(index); |
||
606 | |||
607 | //every time you change slides, reset the timer |
||
608 | restartTimer(); |
||
609 | } |
||
610 | |||
611 | function findSlideIndex(slide) { |
||
612 | for (var i = 0; i < slides.length; i++) { |
||
613 | if (slides[i].slide === slide) { |
||
614 | return i; |
||
615 | } |
||
616 | } |
||
617 | } |
||
618 | |||
619 | function resetTimer() { |
||
620 | if (currentInterval) { |
||
621 | $interval.cancel(currentInterval); |
||
622 | currentInterval = null; |
||
623 | } |
||
624 | } |
||
625 | |||
626 | function resetTransition(slides) { |
||
627 | if (!slides.length) { |
||
628 | $scope.$currentTransition = null; |
||
629 | clearBufferedTransitions(); |
||
630 | } |
||
631 | } |
||
632 | |||
633 | function restartTimer() { |
||
634 | resetTimer(); |
||
635 | var interval = +$scope.interval; |
||
636 | if (!isNaN(interval) && interval > 0) { |
||
637 | currentInterval = $interval(timerFn, interval); |
||
638 | } |
||
639 | } |
||
640 | |||
641 | function timerFn() { |
||
642 | var interval = +$scope.interval; |
||
643 | if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) { |
||
644 | $scope.next(); |
||
645 | } else { |
||
646 | $scope.pause(); |
||
647 | } |
||
648 | } |
||
649 | }]) |
||
650 | |||
651 | View Code Duplication | .directive('uibCarousel', function() { |
|
652 | return { |
||
653 | transclude: true, |
||
654 | replace: true, |
||
655 | controller: 'UibCarouselController', |
||
656 | controllerAs: 'carousel', |
||
657 | templateUrl: function(element, attrs) { |
||
658 | return attrs.templateUrl || 'uib/template/carousel/carousel.html'; |
||
659 | }, |
||
660 | scope: { |
||
661 | active: '=', |
||
662 | interval: '=', |
||
663 | noTransition: '=', |
||
664 | noPause: '=', |
||
665 | noWrap: '&' |
||
666 | } |
||
667 | }; |
||
668 | }) |
||
669 | |||
670 | .directive('uibSlide', function() { |
||
671 | return { |
||
672 | require: '^uibCarousel', |
||
673 | transclude: true, |
||
674 | replace: true, |
||
675 | templateUrl: function(element, attrs) { |
||
676 | return attrs.templateUrl || 'uib/template/carousel/slide.html'; |
||
677 | }, |
||
678 | scope: { |
||
679 | actual: '=?', |
||
680 | index: '=?' |
||
681 | }, |
||
682 | link: function (scope, element, attrs, carouselCtrl) { |
||
683 | carouselCtrl.addSlide(scope, element); |
||
684 | //when the scope is destroyed then remove the slide from the current slides array |
||
685 | scope.$on('$destroy', function() { |
||
686 | carouselCtrl.removeSlide(scope); |
||
687 | }); |
||
688 | } |
||
689 | }; |
||
690 | }) |
||
691 | |||
692 | .animation('.item', ['$animateCss', |
||
693 | View Code Duplication | function($animateCss) { |
|
694 | var SLIDE_DIRECTION = 'uib-slideDirection'; |
||
695 | |||
696 | function removeClass(element, className, callback) { |
||
697 | element.removeClass(className); |
||
698 | if (callback) { |
||
699 | callback(); |
||
700 | } |
||
701 | } |
||
702 | |||
703 | return { |
||
704 | beforeAddClass: function(element, className, done) { |
||
705 | if (className === 'active') { |
||
706 | var stopped = false; |
||
0 ignored issues
–
show
|
|||
707 | var direction = element.data(SLIDE_DIRECTION); |
||
708 | var directionClass = direction === 'next' ? 'left' : 'right'; |
||
709 | var removeClassFn = removeClass.bind(this, element, |
||
0 ignored issues
–
show
|
|||
710 | directionClass + ' ' + direction, done); |
||
711 | element.addClass(direction); |
||
712 | |||
713 | $animateCss(element, {addClass: directionClass}) |
||
714 | .start() |
||
715 | .done(removeClassFn); |
||
716 | |||
717 | return function() { |
||
718 | stopped = true; |
||
0 ignored issues
–
show
|
|||
719 | }; |
||
720 | } |
||
721 | done(); |
||
722 | }, |
||
723 | beforeRemoveClass: function (element, className, done) { |
||
724 | if (className === 'active') { |
||
725 | var stopped = false; |
||
0 ignored issues
–
show
|
|||
726 | var direction = element.data(SLIDE_DIRECTION); |
||
727 | var directionClass = direction === 'next' ? 'left' : 'right'; |
||
728 | var removeClassFn = removeClass.bind(this, element, directionClass, done); |
||
0 ignored issues
–
show
|
|||
729 | |||
730 | $animateCss(element, {addClass: directionClass}) |
||
731 | .start() |
||
732 | .done(removeClassFn); |
||
733 | |||
734 | return function() { |
||
735 | stopped = true; |
||
0 ignored issues
–
show
|
|||
736 | }; |
||
737 | } |
||
738 | done(); |
||
739 | } |
||
740 | }; |
||
741 | }]); |
||
742 | |||
743 | angular.module('ui.bootstrap.dateparser', []) |
||
744 | |||
745 | View Code Duplication | .service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', function($log, $locale, dateFilter, orderByFilter) { |
|
746 | // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js |
||
747 | var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; |
||
748 | |||
749 | var localeId; |
||
750 | var formatCodeToRegex; |
||
751 | |||
752 | this.init = function() { |
||
753 | localeId = $locale.id; |
||
754 | |||
755 | this.parsers = {}; |
||
756 | this.formatters = {}; |
||
757 | |||
758 | formatCodeToRegex = [ |
||
759 | { |
||
760 | key: 'yyyy', |
||
761 | regex: '\\d{4}', |
||
762 | apply: function(value) { this.year = +value; }, |
||
763 | formatter: function(date) { |
||
764 | var _date = new Date(); |
||
765 | _date.setFullYear(Math.abs(date.getFullYear())); |
||
766 | return dateFilter(_date, 'yyyy'); |
||
767 | } |
||
768 | }, |
||
769 | { |
||
770 | key: 'yy', |
||
771 | regex: '\\d{2}', |
||
772 | apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; }, |
||
773 | formatter: function(date) { |
||
774 | var _date = new Date(); |
||
775 | _date.setFullYear(Math.abs(date.getFullYear())); |
||
776 | return dateFilter(_date, 'yy'); |
||
777 | } |
||
778 | }, |
||
779 | { |
||
780 | key: 'y', |
||
781 | regex: '\\d{1,4}', |
||
782 | apply: function(value) { this.year = +value; }, |
||
783 | formatter: function(date) { |
||
784 | var _date = new Date(); |
||
785 | _date.setFullYear(Math.abs(date.getFullYear())); |
||
786 | return dateFilter(_date, 'y'); |
||
787 | } |
||
788 | }, |
||
789 | { |
||
790 | key: 'M!', |
||
791 | regex: '0?[1-9]|1[0-2]', |
||
792 | apply: function(value) { this.month = value - 1; }, |
||
793 | formatter: function(date) { |
||
794 | var value = date.getMonth(); |
||
795 | if (/^[0-9]$/.test(value)) { |
||
796 | return dateFilter(date, 'MM'); |
||
797 | } |
||
798 | |||
799 | return dateFilter(date, 'M'); |
||
800 | } |
||
801 | }, |
||
802 | { |
||
803 | key: 'MMMM', |
||
804 | regex: $locale.DATETIME_FORMATS.MONTH.join('|'), |
||
805 | apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }, |
||
806 | formatter: function(date) { return dateFilter(date, 'MMMM'); } |
||
807 | }, |
||
808 | { |
||
809 | key: 'MMM', |
||
810 | regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), |
||
811 | apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }, |
||
812 | formatter: function(date) { return dateFilter(date, 'MMM'); } |
||
813 | }, |
||
814 | { |
||
815 | key: 'MM', |
||
816 | regex: '0[1-9]|1[0-2]', |
||
817 | apply: function(value) { this.month = value - 1; }, |
||
818 | formatter: function(date) { return dateFilter(date, 'MM'); } |
||
819 | }, |
||
820 | { |
||
821 | key: 'M', |
||
822 | regex: '[1-9]|1[0-2]', |
||
823 | apply: function(value) { this.month = value - 1; }, |
||
824 | formatter: function(date) { return dateFilter(date, 'M'); } |
||
825 | }, |
||
826 | { |
||
827 | key: 'd!', |
||
828 | regex: '[0-2]?[0-9]{1}|3[0-1]{1}', |
||
829 | apply: function(value) { this.date = +value; }, |
||
830 | formatter: function(date) { |
||
831 | var value = date.getDate(); |
||
832 | if (/^[1-9]$/.test(value)) { |
||
833 | return dateFilter(date, 'dd'); |
||
834 | } |
||
835 | |||
836 | return dateFilter(date, 'd'); |
||
837 | } |
||
838 | }, |
||
839 | { |
||
840 | key: 'dd', |
||
841 | regex: '[0-2][0-9]{1}|3[0-1]{1}', |
||
842 | apply: function(value) { this.date = +value; }, |
||
843 | formatter: function(date) { return dateFilter(date, 'dd'); } |
||
844 | }, |
||
845 | { |
||
846 | key: 'd', |
||
847 | regex: '[1-2]?[0-9]{1}|3[0-1]{1}', |
||
848 | apply: function(value) { this.date = +value; }, |
||
849 | formatter: function(date) { return dateFilter(date, 'd'); } |
||
850 | }, |
||
851 | { |
||
852 | key: 'EEEE', |
||
853 | regex: $locale.DATETIME_FORMATS.DAY.join('|'), |
||
854 | formatter: function(date) { return dateFilter(date, 'EEEE'); } |
||
855 | }, |
||
856 | { |
||
857 | key: 'EEE', |
||
858 | regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'), |
||
859 | formatter: function(date) { return dateFilter(date, 'EEE'); } |
||
860 | }, |
||
861 | { |
||
862 | key: 'HH', |
||
863 | regex: '(?:0|1)[0-9]|2[0-3]', |
||
864 | apply: function(value) { this.hours = +value; }, |
||
865 | formatter: function(date) { return dateFilter(date, 'HH'); } |
||
866 | }, |
||
867 | { |
||
868 | key: 'hh', |
||
869 | regex: '0[0-9]|1[0-2]', |
||
870 | apply: function(value) { this.hours = +value; }, |
||
871 | formatter: function(date) { return dateFilter(date, 'hh'); } |
||
872 | }, |
||
873 | { |
||
874 | key: 'H', |
||
875 | regex: '1?[0-9]|2[0-3]', |
||
876 | apply: function(value) { this.hours = +value; }, |
||
877 | formatter: function(date) { return dateFilter(date, 'H'); } |
||
878 | }, |
||
879 | { |
||
880 | key: 'h', |
||
881 | regex: '[0-9]|1[0-2]', |
||
882 | apply: function(value) { this.hours = +value; }, |
||
883 | formatter: function(date) { return dateFilter(date, 'h'); } |
||
884 | }, |
||
885 | { |
||
886 | key: 'mm', |
||
887 | regex: '[0-5][0-9]', |
||
888 | apply: function(value) { this.minutes = +value; }, |
||
889 | formatter: function(date) { return dateFilter(date, 'mm'); } |
||
890 | }, |
||
891 | { |
||
892 | key: 'm', |
||
893 | regex: '[0-9]|[1-5][0-9]', |
||
894 | apply: function(value) { this.minutes = +value; }, |
||
895 | formatter: function(date) { return dateFilter(date, 'm'); } |
||
896 | }, |
||
897 | { |
||
898 | key: 'sss', |
||
899 | regex: '[0-9][0-9][0-9]', |
||
900 | apply: function(value) { this.milliseconds = +value; }, |
||
901 | formatter: function(date) { return dateFilter(date, 'sss'); } |
||
902 | }, |
||
903 | { |
||
904 | key: 'ss', |
||
905 | regex: '[0-5][0-9]', |
||
906 | apply: function(value) { this.seconds = +value; }, |
||
907 | formatter: function(date) { return dateFilter(date, 'ss'); } |
||
908 | }, |
||
909 | { |
||
910 | key: 's', |
||
911 | regex: '[0-9]|[1-5][0-9]', |
||
912 | apply: function(value) { this.seconds = +value; }, |
||
913 | formatter: function(date) { return dateFilter(date, 's'); } |
||
914 | }, |
||
915 | { |
||
916 | key: 'a', |
||
917 | regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), |
||
918 | apply: function(value) { |
||
919 | if (this.hours === 12) { |
||
920 | this.hours = 0; |
||
921 | } |
||
922 | |||
923 | if (value === 'PM') { |
||
924 | this.hours += 12; |
||
925 | } |
||
926 | }, |
||
927 | formatter: function(date) { return dateFilter(date, 'a'); } |
||
928 | }, |
||
929 | { |
||
930 | key: 'Z', |
||
931 | regex: '[+-]\\d{4}', |
||
932 | apply: function(value) { |
||
933 | var matches = value.match(/([+-])(\d{2})(\d{2})/), |
||
934 | sign = matches[1], |
||
935 | hours = matches[2], |
||
936 | minutes = matches[3]; |
||
937 | this.hours += toInt(sign + hours); |
||
938 | this.minutes += toInt(sign + minutes); |
||
939 | }, |
||
940 | formatter: function(date) { |
||
941 | return dateFilter(date, 'Z'); |
||
942 | } |
||
943 | }, |
||
944 | { |
||
945 | key: 'ww', |
||
946 | regex: '[0-4][0-9]|5[0-3]', |
||
947 | formatter: function(date) { return dateFilter(date, 'ww'); } |
||
948 | }, |
||
949 | { |
||
950 | key: 'w', |
||
951 | regex: '[0-9]|[1-4][0-9]|5[0-3]', |
||
952 | formatter: function(date) { return dateFilter(date, 'w'); } |
||
953 | }, |
||
954 | { |
||
955 | key: 'GGGG', |
||
956 | regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'), |
||
957 | formatter: function(date) { return dateFilter(date, 'GGGG'); } |
||
958 | }, |
||
959 | { |
||
960 | key: 'GGG', |
||
961 | regex: $locale.DATETIME_FORMATS.ERAS.join('|'), |
||
962 | formatter: function(date) { return dateFilter(date, 'GGG'); } |
||
963 | }, |
||
964 | { |
||
965 | key: 'GG', |
||
966 | regex: $locale.DATETIME_FORMATS.ERAS.join('|'), |
||
967 | formatter: function(date) { return dateFilter(date, 'GG'); } |
||
968 | }, |
||
969 | { |
||
970 | key: 'G', |
||
971 | regex: $locale.DATETIME_FORMATS.ERAS.join('|'), |
||
972 | formatter: function(date) { return dateFilter(date, 'G'); } |
||
973 | } |
||
974 | ]; |
||
975 | }; |
||
976 | |||
977 | this.init(); |
||
978 | |||
979 | function createParser(format, func) { |
||
980 | var map = [], regex = format.split(''); |
||
981 | |||
982 | // check for literal values |
||
983 | var quoteIndex = format.indexOf('\''); |
||
984 | if (quoteIndex > -1) { |
||
985 | var inLiteral = false; |
||
986 | format = format.split(''); |
||
987 | for (var i = quoteIndex; i < format.length; i++) { |
||
988 | if (inLiteral) { |
||
989 | if (format[i] === '\'') { |
||
990 | if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote |
||
991 | format[i+1] = '$'; |
||
992 | regex[i+1] = ''; |
||
993 | } else { // end of literal |
||
994 | regex[i] = ''; |
||
995 | inLiteral = false; |
||
996 | } |
||
997 | } |
||
998 | format[i] = '$'; |
||
999 | } else { |
||
1000 | if (format[i] === '\'') { // start of literal |
||
1001 | format[i] = '$'; |
||
1002 | regex[i] = ''; |
||
1003 | inLiteral = true; |
||
1004 | } |
||
1005 | } |
||
1006 | } |
||
1007 | |||
1008 | format = format.join(''); |
||
1009 | } |
||
1010 | |||
1011 | angular.forEach(formatCodeToRegex, function(data) { |
||
1012 | var index = format.indexOf(data.key); |
||
1013 | |||
1014 | if (index > -1) { |
||
1015 | format = format.split(''); |
||
1016 | |||
1017 | regex[index] = '(' + data.regex + ')'; |
||
1018 | format[index] = '$'; // Custom symbol to define consumed part of format |
||
1019 | for (var i = index + 1, n = index + data.key.length; i < n; i++) { |
||
1020 | regex[i] = ''; |
||
1021 | format[i] = '$'; |
||
1022 | } |
||
1023 | format = format.join(''); |
||
1024 | |||
1025 | map.push({ |
||
1026 | index: index, |
||
1027 | key: data.key, |
||
1028 | apply: data[func], |
||
1029 | matcher: data.regex |
||
1030 | }); |
||
1031 | } |
||
1032 | }); |
||
1033 | |||
1034 | return { |
||
1035 | regex: new RegExp('^' + regex.join('') + '$'), |
||
1036 | map: orderByFilter(map, 'index') |
||
1037 | }; |
||
1038 | } |
||
1039 | |||
1040 | this.filter = function(date, format) { |
||
1041 | if (!angular.isDate(date) || isNaN(date) || !format) { |
||
1042 | return ''; |
||
1043 | } |
||
1044 | |||
1045 | format = $locale.DATETIME_FORMATS[format] || format; |
||
1046 | |||
1047 | if ($locale.id !== localeId) { |
||
1048 | this.init(); |
||
1049 | } |
||
1050 | |||
1051 | if (!this.formatters[format]) { |
||
1052 | this.formatters[format] = createParser(format, 'formatter'); |
||
1053 | } |
||
1054 | |||
1055 | var parser = this.formatters[format], |
||
1056 | map = parser.map; |
||
1057 | |||
1058 | var _format = format; |
||
1059 | |||
1060 | return map.reduce(function(str, mapper, i) { |
||
1061 | var match = _format.match(new RegExp('(.*)' + mapper.key)); |
||
1062 | if (match && angular.isString(match[1])) { |
||
1063 | str += match[1]; |
||
1064 | _format = _format.replace(match[1] + mapper.key, ''); |
||
1065 | } |
||
1066 | |||
1067 | var endStr = i === map.length - 1 ? _format : ''; |
||
1068 | |||
1069 | if (mapper.apply) { |
||
1070 | return str + mapper.apply.call(null, date) + endStr; |
||
1071 | } |
||
1072 | |||
1073 | return str + endStr; |
||
1074 | }, ''); |
||
1075 | }; |
||
1076 | |||
1077 | this.parse = function(input, format, baseDate) { |
||
1078 | if (!angular.isString(input) || !format) { |
||
1079 | return input; |
||
1080 | } |
||
1081 | |||
1082 | format = $locale.DATETIME_FORMATS[format] || format; |
||
1083 | format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&'); |
||
1084 | |||
1085 | if ($locale.id !== localeId) { |
||
1086 | this.init(); |
||
1087 | } |
||
1088 | |||
1089 | if (!this.parsers[format]) { |
||
1090 | this.parsers[format] = createParser(format, 'apply'); |
||
1091 | } |
||
1092 | |||
1093 | var parser = this.parsers[format], |
||
1094 | regex = parser.regex, |
||
1095 | map = parser.map, |
||
1096 | results = input.match(regex), |
||
1097 | tzOffset = false; |
||
1098 | if (results && results.length) { |
||
1099 | var fields, dt; |
||
1100 | if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { |
||
1101 | fields = { |
||
1102 | year: baseDate.getFullYear(), |
||
1103 | month: baseDate.getMonth(), |
||
1104 | date: baseDate.getDate(), |
||
1105 | hours: baseDate.getHours(), |
||
1106 | minutes: baseDate.getMinutes(), |
||
1107 | seconds: baseDate.getSeconds(), |
||
1108 | milliseconds: baseDate.getMilliseconds() |
||
1109 | }; |
||
1110 | } else { |
||
1111 | if (baseDate) { |
||
1112 | $log.warn('dateparser:', 'baseDate is not a valid date'); |
||
1113 | } |
||
1114 | fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }; |
||
1115 | } |
||
1116 | |||
1117 | for (var i = 1, n = results.length; i < n; i++) { |
||
1118 | var mapper = map[i - 1]; |
||
1119 | if (mapper.matcher === 'Z') { |
||
1120 | tzOffset = true; |
||
1121 | } |
||
1122 | |||
1123 | if (mapper.apply) { |
||
1124 | mapper.apply.call(fields, results[i]); |
||
1125 | } |
||
1126 | } |
||
1127 | |||
1128 | var datesetter = tzOffset ? Date.prototype.setUTCFullYear : |
||
1129 | Date.prototype.setFullYear; |
||
1130 | var timesetter = tzOffset ? Date.prototype.setUTCHours : |
||
1131 | Date.prototype.setHours; |
||
1132 | |||
1133 | if (isValid(fields.year, fields.month, fields.date)) { |
||
1134 | if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) { |
||
1135 | dt = new Date(baseDate); |
||
1136 | datesetter.call(dt, fields.year, fields.month, fields.date); |
||
1137 | timesetter.call(dt, fields.hours, fields.minutes, |
||
1138 | fields.seconds, fields.milliseconds); |
||
1139 | } else { |
||
1140 | dt = new Date(0); |
||
1141 | datesetter.call(dt, fields.year, fields.month, fields.date); |
||
1142 | timesetter.call(dt, fields.hours || 0, fields.minutes || 0, |
||
1143 | fields.seconds || 0, fields.milliseconds || 0); |
||
1144 | } |
||
1145 | } |
||
1146 | |||
1147 | return dt; |
||
1148 | } |
||
1149 | }; |
||
1150 | |||
1151 | // Check if date is valid for specific month (and year for February). |
||
1152 | // Month: 0 = Jan, 1 = Feb, etc |
||
1153 | function isValid(year, month, date) { |
||
1154 | if (date < 1) { |
||
1155 | return false; |
||
1156 | } |
||
1157 | |||
1158 | if (month === 1 && date > 28) { |
||
1159 | return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0); |
||
1160 | } |
||
1161 | |||
1162 | if (month === 3 || month === 5 || month === 8 || month === 10) { |
||
1163 | return date < 31; |
||
1164 | } |
||
1165 | |||
1166 | return true; |
||
1167 | } |
||
1168 | |||
1169 | function toInt(str) { |
||
1170 | return parseInt(str, 10); |
||
1171 | } |
||
1172 | |||
1173 | this.toTimezone = toTimezone; |
||
1174 | this.fromTimezone = fromTimezone; |
||
1175 | this.timezoneToOffset = timezoneToOffset; |
||
1176 | this.addDateMinutes = addDateMinutes; |
||
1177 | this.convertTimezoneToLocal = convertTimezoneToLocal; |
||
1178 | |||
1179 | function toTimezone(date, timezone) { |
||
1180 | return date && timezone ? convertTimezoneToLocal(date, timezone) : date; |
||
1181 | } |
||
1182 | |||
1183 | function fromTimezone(date, timezone) { |
||
1184 | return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date; |
||
1185 | } |
||
1186 | |||
1187 | //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207 |
||
1188 | function timezoneToOffset(timezone, fallback) { |
||
1189 | var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; |
||
1190 | return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; |
||
1191 | } |
||
1192 | |||
1193 | function addDateMinutes(date, minutes) { |
||
1194 | date = new Date(date.getTime()); |
||
1195 | date.setMinutes(date.getMinutes() + minutes); |
||
1196 | return date; |
||
1197 | } |
||
1198 | |||
1199 | function convertTimezoneToLocal(date, timezone, reverse) { |
||
1200 | reverse = reverse ? -1 : 1; |
||
1201 | var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset()); |
||
1202 | return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset())); |
||
1203 | } |
||
1204 | }]); |
||
1205 | |||
1206 | // Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to |
||
1207 | // at most one element. |
||
1208 | angular.module('ui.bootstrap.isClass', []) |
||
1209 | .directive('uibIsClass', [ |
||
1210 | '$animate', |
||
1211 | View Code Duplication | function ($animate) { |
|
1212 | // 11111111 22222222 |
||
1213 | var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/; |
||
1214 | // 11111111 22222222 |
||
1215 | var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/; |
||
1216 | |||
1217 | var dataPerTracked = {}; |
||
0 ignored issues
–
show
|
|||
1218 | |||
1219 | return { |
||
1220 | restrict: 'A', |
||
1221 | compile: function(tElement, tAttrs) { |
||
1222 | var linkedScopes = []; |
||
1223 | var instances = []; |
||
1224 | var expToData = {}; |
||
1225 | var lastActivated = null; |
||
1226 | var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP); |
||
1227 | var onExp = onExpMatches[2]; |
||
1228 | var expsStr = onExpMatches[1]; |
||
1229 | var exps = expsStr.split(','); |
||
1230 | |||
1231 | return linkFn; |
||
1232 | |||
1233 | function linkFn(scope, element, attrs) { |
||
1234 | linkedScopes.push(scope); |
||
1235 | instances.push({ |
||
1236 | scope: scope, |
||
1237 | element: element |
||
1238 | }); |
||
1239 | |||
1240 | exps.forEach(function(exp, k) { |
||
1241 | addForExp(exp, scope); |
||
1242 | }); |
||
1243 | |||
1244 | scope.$on('$destroy', removeScope); |
||
1245 | } |
||
1246 | |||
1247 | function addForExp(exp, scope) { |
||
1248 | var matches = exp.match(IS_REGEXP); |
||
1249 | var clazz = scope.$eval(matches[1]); |
||
1250 | var compareWithExp = matches[2]; |
||
1251 | var data = expToData[exp]; |
||
1252 | if (!data) { |
||
1253 | var watchFn = function(compareWithVal) { |
||
1254 | var newActivated = null; |
||
1255 | instances.some(function(instance) { |
||
1256 | var thisVal = instance.scope.$eval(onExp); |
||
1257 | if (thisVal === compareWithVal) { |
||
1258 | newActivated = instance; |
||
1259 | return true; |
||
1260 | } |
||
1261 | }); |
||
1262 | if (data.lastActivated !== newActivated) { |
||
1263 | if (data.lastActivated) { |
||
1264 | $animate.removeClass(data.lastActivated.element, clazz); |
||
1265 | } |
||
1266 | if (newActivated) { |
||
1267 | $animate.addClass(newActivated.element, clazz); |
||
1268 | } |
||
1269 | data.lastActivated = newActivated; |
||
1270 | } |
||
1271 | }; |
||
1272 | expToData[exp] = data = { |
||
1273 | lastActivated: null, |
||
1274 | scope: scope, |
||
1275 | watchFn: watchFn, |
||
1276 | compareWithExp: compareWithExp, |
||
1277 | watcher: scope.$watch(compareWithExp, watchFn) |
||
1278 | }; |
||
1279 | } |
||
1280 | data.watchFn(scope.$eval(compareWithExp)); |
||
1281 | } |
||
1282 | |||
1283 | function removeScope(e) { |
||
1284 | var removedScope = e.targetScope; |
||
1285 | var index = linkedScopes.indexOf(removedScope); |
||
1286 | linkedScopes.splice(index, 1); |
||
1287 | instances.splice(index, 1); |
||
1288 | if (linkedScopes.length) { |
||
1289 | var newWatchScope = linkedScopes[0]; |
||
1290 | angular.forEach(expToData, function(data) { |
||
1291 | if (data.scope === removedScope) { |
||
1292 | data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn); |
||
1293 | data.scope = newWatchScope; |
||
1294 | } |
||
1295 | }); |
||
1296 | } else { |
||
1297 | expToData = {}; |
||
1298 | } |
||
1299 | } |
||
1300 | } |
||
1301 | }; |
||
1302 | }]); |
||
1303 | angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass']) |
||
1304 | |||
1305 | .value('$datepickerSuppressError', false) |
||
1306 | |||
1307 | .value('$datepickerLiteralWarning', true) |
||
1308 | |||
1309 | .constant('uibDatepickerConfig', { |
||
1310 | datepickerMode: 'day', |
||
1311 | formatDay: 'dd', |
||
1312 | formatMonth: 'MMMM', |
||
1313 | formatYear: 'yyyy', |
||
1314 | formatDayHeader: 'EEE', |
||
1315 | formatDayTitle: 'MMMM yyyy', |
||
1316 | formatMonthTitle: 'yyyy', |
||
1317 | maxDate: null, |
||
1318 | maxMode: 'year', |
||
1319 | minDate: null, |
||
1320 | minMode: 'day', |
||
1321 | ngModelOptions: {}, |
||
1322 | shortcutPropagation: false, |
||
1323 | showWeeks: true, |
||
1324 | yearColumns: 5, |
||
1325 | yearRows: 4 |
||
1326 | }) |
||
1327 | |||
1328 | .controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser', |
||
1329 | View Code Duplication | function($scope, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) { |
|
1330 | var self = this, |
||
1331 | ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl; |
||
1332 | ngModelOptions = {}, |
||
1333 | watchListeners = [], |
||
1334 | optionsUsed = !!$attrs.datepickerOptions; |
||
0 ignored issues
–
show
|
|||
1335 | |||
1336 | if (!$scope.datepickerOptions) { |
||
1337 | $scope.datepickerOptions = {}; |
||
1338 | } |
||
1339 | |||
1340 | // Modes chain |
||
1341 | this.modes = ['day', 'month', 'year']; |
||
1342 | |||
1343 | [ |
||
1344 | 'customClass', |
||
1345 | 'dateDisabled', |
||
1346 | 'datepickerMode', |
||
1347 | 'formatDay', |
||
1348 | 'formatDayHeader', |
||
1349 | 'formatDayTitle', |
||
1350 | 'formatMonth', |
||
1351 | 'formatMonthTitle', |
||
1352 | 'formatYear', |
||
1353 | 'maxDate', |
||
1354 | 'maxMode', |
||
1355 | 'minDate', |
||
1356 | 'minMode', |
||
1357 | 'showWeeks', |
||
1358 | 'shortcutPropagation', |
||
1359 | 'startingDay', |
||
1360 | 'yearColumns', |
||
1361 | 'yearRows' |
||
1362 | ].forEach(function(key) { |
||
1363 | switch (key) { |
||
1364 | case 'customClass': |
||
1365 | case 'dateDisabled': |
||
1366 | $scope[key] = $scope.datepickerOptions[key] || angular.noop; |
||
1367 | break; |
||
1368 | case 'datepickerMode': |
||
1369 | $scope.datepickerMode = angular.isDefined($scope.datepickerOptions.datepickerMode) ? |
||
1370 | $scope.datepickerOptions.datepickerMode : datepickerConfig.datepickerMode; |
||
1371 | break; |
||
1372 | case 'formatDay': |
||
1373 | case 'formatDayHeader': |
||
1374 | case 'formatDayTitle': |
||
1375 | case 'formatMonth': |
||
1376 | case 'formatMonthTitle': |
||
1377 | case 'formatYear': |
||
1378 | self[key] = angular.isDefined($scope.datepickerOptions[key]) ? |
||
1379 | $interpolate($scope.datepickerOptions[key])($scope.$parent) : |
||
1380 | datepickerConfig[key]; |
||
1381 | break; |
||
1382 | case 'showWeeks': |
||
1383 | case 'shortcutPropagation': |
||
1384 | case 'yearColumns': |
||
1385 | case 'yearRows': |
||
1386 | self[key] = angular.isDefined($scope.datepickerOptions[key]) ? |
||
1387 | $scope.datepickerOptions[key] : datepickerConfig[key]; |
||
1388 | break; |
||
1389 | case 'startingDay': |
||
1390 | if (angular.isDefined($scope.datepickerOptions.startingDay)) { |
||
1391 | self.startingDay = $scope.datepickerOptions.startingDay; |
||
1392 | } else if (angular.isNumber(datepickerConfig.startingDay)) { |
||
1393 | self.startingDay = datepickerConfig.startingDay; |
||
1394 | } else { |
||
1395 | self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7; |
||
1396 | } |
||
1397 | |||
1398 | break; |
||
1399 | case 'maxDate': |
||
1400 | case 'minDate': |
||
1401 | $scope.$watch('datepickerOptions.' + key, function(value) { |
||
1402 | if (value) { |
||
1403 | if (angular.isDate(value)) { |
||
1404 | self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone); |
||
1405 | } else { |
||
1406 | if ($datepickerLiteralWarning) { |
||
1407 | $log.warn('Literal date support has been deprecated, please switch to date object usage'); |
||
1408 | } |
||
1409 | |||
1410 | self[key] = new Date(dateFilter(value, 'medium')); |
||
1411 | } |
||
1412 | } else { |
||
1413 | self[key] = datepickerConfig[key] ? |
||
1414 | dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : |
||
1415 | null; |
||
1416 | } |
||
1417 | |||
1418 | self.refreshView(); |
||
1419 | }); |
||
1420 | |||
1421 | break; |
||
1422 | case 'maxMode': |
||
1423 | case 'minMode': |
||
1424 | if ($scope.datepickerOptions[key]) { |
||
1425 | $scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) { |
||
1426 | self[key] = $scope[key] = angular.isDefined(value) ? value : datepickerOptions[key]; |
||
1427 | if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) || |
||
1428 | key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) { |
||
1429 | $scope.datepickerMode = self[key]; |
||
1430 | $scope.datepickerOptions.datepickerMode = self[key]; |
||
1431 | } |
||
1432 | }); |
||
1433 | } else { |
||
1434 | self[key] = $scope[key] = datepickerConfig[key] || null; |
||
1435 | } |
||
1436 | |||
1437 | break; |
||
1438 | } |
||
1439 | }); |
||
1440 | |||
1441 | $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); |
||
1442 | |||
1443 | $scope.disabled = angular.isDefined($attrs.disabled) || false; |
||
1444 | if (angular.isDefined($attrs.ngDisabled)) { |
||
1445 | watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) { |
||
1446 | $scope.disabled = disabled; |
||
1447 | self.refreshView(); |
||
1448 | })); |
||
1449 | } |
||
1450 | |||
1451 | $scope.isActive = function(dateObject) { |
||
1452 | if (self.compare(dateObject.date, self.activeDate) === 0) { |
||
1453 | $scope.activeDateId = dateObject.uid; |
||
1454 | return true; |
||
1455 | } |
||
1456 | return false; |
||
1457 | }; |
||
1458 | |||
1459 | this.init = function(ngModelCtrl_) { |
||
1460 | ngModelCtrl = ngModelCtrl_; |
||
1461 | ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions; |
||
1462 | if ($scope.datepickerOptions.initDate) { |
||
1463 | self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date(); |
||
1464 | $scope.$watch('datepickerOptions.initDate', function(initDate) { |
||
1465 | if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { |
||
1466 | self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone); |
||
1467 | self.refreshView(); |
||
1468 | } |
||
1469 | }); |
||
1470 | } else { |
||
1471 | self.activeDate = new Date(); |
||
1472 | } |
||
1473 | |||
1474 | this.activeDate = ngModelCtrl.$modelValue ? |
||
1475 | dateParser.fromTimezone(new Date(ngModelCtrl.$modelValue), ngModelOptions.timezone) : |
||
1476 | dateParser.fromTimezone(new Date(), ngModelOptions.timezone); |
||
1477 | |||
1478 | ngModelCtrl.$render = function() { |
||
1479 | self.render(); |
||
1480 | }; |
||
1481 | }; |
||
1482 | |||
1483 | this.render = function() { |
||
1484 | if (ngModelCtrl.$viewValue) { |
||
1485 | var date = new Date(ngModelCtrl.$viewValue), |
||
1486 | isValid = !isNaN(date); |
||
1487 | |||
1488 | if (isValid) { |
||
1489 | this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone); |
||
1490 | } else if (!$datepickerSuppressError) { |
||
1491 | $log.error('Datepicker directive: "ng-model" value must be a Date object'); |
||
1492 | } |
||
1493 | } |
||
1494 | this.refreshView(); |
||
1495 | }; |
||
1496 | |||
1497 | this.refreshView = function() { |
||
1498 | if (this.element) { |
||
1499 | $scope.selectedDt = null; |
||
1500 | this._refreshView(); |
||
1501 | if ($scope.activeDt) { |
||
1502 | $scope.activeDateId = $scope.activeDt.uid; |
||
1503 | } |
||
1504 | |||
1505 | var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; |
||
1506 | date = dateParser.fromTimezone(date, ngModelOptions.timezone); |
||
1507 | ngModelCtrl.$setValidity('dateDisabled', !date || |
||
1508 | this.element && !this.isDisabled(date)); |
||
1509 | } |
||
1510 | }; |
||
1511 | |||
1512 | this.createDateObject = function(date, format) { |
||
1513 | var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; |
||
1514 | model = dateParser.fromTimezone(model, ngModelOptions.timezone); |
||
1515 | var today = new Date(); |
||
1516 | today = dateParser.fromTimezone(today, ngModelOptions.timezone); |
||
1517 | var time = this.compare(date, today); |
||
1518 | var dt = { |
||
1519 | date: date, |
||
1520 | label: dateParser.filter(date, format), |
||
1521 | selected: model && this.compare(date, model) === 0, |
||
1522 | disabled: this.isDisabled(date), |
||
1523 | past: time < 0, |
||
1524 | current: time === 0, |
||
1525 | future: time > 0, |
||
1526 | customClass: this.customClass(date) || null |
||
1527 | }; |
||
1528 | |||
1529 | if (model && this.compare(date, model) === 0) { |
||
1530 | $scope.selectedDt = dt; |
||
1531 | } |
||
1532 | |||
1533 | if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) { |
||
1534 | $scope.activeDt = dt; |
||
1535 | } |
||
1536 | |||
1537 | return dt; |
||
1538 | }; |
||
1539 | |||
1540 | this.isDisabled = function(date) { |
||
1541 | return $scope.disabled || |
||
1542 | this.minDate && this.compare(date, this.minDate) < 0 || |
||
1543 | this.maxDate && this.compare(date, this.maxDate) > 0 || |
||
1544 | $scope.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode}); |
||
1545 | }; |
||
1546 | |||
1547 | this.customClass = function(date) { |
||
1548 | return $scope.customClass({date: date, mode: $scope.datepickerMode}); |
||
1549 | }; |
||
1550 | |||
1551 | // Split array into smaller arrays |
||
1552 | this.split = function(arr, size) { |
||
1553 | var arrays = []; |
||
1554 | while (arr.length > 0) { |
||
1555 | arrays.push(arr.splice(0, size)); |
||
1556 | } |
||
1557 | return arrays; |
||
1558 | }; |
||
1559 | |||
1560 | $scope.select = function(date) { |
||
1561 | if ($scope.datepickerMode === self.minMode) { |
||
1562 | var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0); |
||
1563 | dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); |
||
1564 | dt = dateParser.toTimezone(dt, ngModelOptions.timezone); |
||
1565 | ngModelCtrl.$setViewValue(dt); |
||
1566 | ngModelCtrl.$render(); |
||
1567 | } else { |
||
1568 | self.activeDate = date; |
||
1569 | setMode(self.modes[self.modes.indexOf($scope.datepickerMode) - 1]); |
||
1570 | |||
1571 | $scope.$emit('uib:datepicker.mode'); |
||
1572 | } |
||
1573 | |||
1574 | $scope.$broadcast('uib:datepicker.focus'); |
||
1575 | }; |
||
1576 | |||
1577 | $scope.move = function(direction) { |
||
1578 | var year = self.activeDate.getFullYear() + direction * (self.step.years || 0), |
||
1579 | month = self.activeDate.getMonth() + direction * (self.step.months || 0); |
||
1580 | self.activeDate.setFullYear(year, month, 1); |
||
1581 | self.refreshView(); |
||
1582 | }; |
||
1583 | |||
1584 | $scope.toggleMode = function(direction) { |
||
1585 | direction = direction || 1; |
||
1586 | |||
1587 | if ($scope.datepickerMode === self.maxMode && direction === 1 || |
||
1588 | $scope.datepickerMode === self.minMode && direction === -1) { |
||
1589 | return; |
||
1590 | } |
||
1591 | |||
1592 | setMode(self.modes[self.modes.indexOf($scope.datepickerMode) + direction]); |
||
1593 | |||
1594 | $scope.$emit('uib:datepicker.mode'); |
||
1595 | }; |
||
1596 | |||
1597 | // Key event mapper |
||
1598 | $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; |
||
1599 | |||
1600 | var focusElement = function() { |
||
1601 | self.element[0].focus(); |
||
1602 | }; |
||
1603 | |||
1604 | // Listen for focus requests from popup directive |
||
1605 | $scope.$on('uib:datepicker.focus', focusElement); |
||
1606 | |||
1607 | $scope.keydown = function(evt) { |
||
1608 | var key = $scope.keys[evt.which]; |
||
1609 | |||
1610 | if (!key || evt.shiftKey || evt.altKey || $scope.disabled) { |
||
1611 | return; |
||
1612 | } |
||
1613 | |||
1614 | evt.preventDefault(); |
||
1615 | if (!self.shortcutPropagation) { |
||
1616 | evt.stopPropagation(); |
||
1617 | } |
||
1618 | |||
1619 | if (key === 'enter' || key === 'space') { |
||
1620 | if (self.isDisabled(self.activeDate)) { |
||
1621 | return; // do nothing |
||
1622 | } |
||
1623 | $scope.select(self.activeDate); |
||
1624 | } else if (evt.ctrlKey && (key === 'up' || key === 'down')) { |
||
1625 | $scope.toggleMode(key === 'up' ? 1 : -1); |
||
1626 | } else { |
||
1627 | self.handleKeyDown(key, evt); |
||
1628 | self.refreshView(); |
||
1629 | } |
||
1630 | }; |
||
1631 | |||
1632 | $scope.$on('$destroy', function() { |
||
1633 | //Clear all watch listeners on destroy |
||
1634 | while (watchListeners.length) { |
||
1635 | watchListeners.shift()(); |
||
1636 | } |
||
1637 | }); |
||
1638 | |||
1639 | function setMode(mode) { |
||
1640 | $scope.datepickerMode = mode; |
||
1641 | $scope.datepickerOptions.datepickerMode = mode; |
||
1642 | } |
||
1643 | }]) |
||
1644 | |||
1645 | View Code Duplication | .controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { |
|
1646 | var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; |
||
1647 | |||
1648 | this.step = { months: 1 }; |
||
1649 | this.element = $element; |
||
1650 | function getDaysInMonth(year, month) { |
||
1651 | return month === 1 && year % 4 === 0 && |
||
1652 | (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month]; |
||
1653 | } |
||
1654 | |||
1655 | this.init = function(ctrl) { |
||
1656 | angular.extend(ctrl, this); |
||
1657 | scope.showWeeks = ctrl.showWeeks; |
||
1658 | ctrl.refreshView(); |
||
1659 | }; |
||
1660 | |||
1661 | this.getDates = function(startDate, n) { |
||
1662 | var dates = new Array(n), current = new Date(startDate), i = 0, date; |
||
1663 | while (i < n) { |
||
1664 | date = new Date(current); |
||
1665 | dates[i++] = date; |
||
1666 | current.setDate(current.getDate() + 1); |
||
1667 | } |
||
1668 | return dates; |
||
1669 | }; |
||
1670 | |||
1671 | this._refreshView = function() { |
||
1672 | var year = this.activeDate.getFullYear(), |
||
1673 | month = this.activeDate.getMonth(), |
||
1674 | firstDayOfMonth = new Date(this.activeDate); |
||
1675 | |||
1676 | firstDayOfMonth.setFullYear(year, month, 1); |
||
1677 | |||
1678 | var difference = this.startingDay - firstDayOfMonth.getDay(), |
||
1679 | numDisplayedFromPreviousMonth = difference > 0 ? |
||
1680 | 7 - difference : - difference, |
||
1681 | firstDate = new Date(firstDayOfMonth); |
||
1682 | |||
1683 | if (numDisplayedFromPreviousMonth > 0) { |
||
1684 | firstDate.setDate(-numDisplayedFromPreviousMonth + 1); |
||
1685 | } |
||
1686 | |||
1687 | // 42 is the number of days on a six-week calendar |
||
1688 | var days = this.getDates(firstDate, 42); |
||
1689 | for (var i = 0; i < 42; i ++) { |
||
1690 | days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), { |
||
1691 | secondary: days[i].getMonth() !== month, |
||
1692 | uid: scope.uniqueId + '-' + i |
||
1693 | }); |
||
1694 | } |
||
1695 | |||
1696 | scope.labels = new Array(7); |
||
1697 | for (var j = 0; j < 7; j++) { |
||
1698 | scope.labels[j] = { |
||
1699 | abbr: dateFilter(days[j].date, this.formatDayHeader), |
||
1700 | full: dateFilter(days[j].date, 'EEEE') |
||
1701 | }; |
||
1702 | } |
||
1703 | |||
1704 | scope.title = dateFilter(this.activeDate, this.formatDayTitle); |
||
1705 | scope.rows = this.split(days, 7); |
||
1706 | |||
1707 | if (scope.showWeeks) { |
||
1708 | scope.weekNumbers = []; |
||
1709 | var thursdayIndex = (4 + 7 - this.startingDay) % 7, |
||
1710 | numWeeks = scope.rows.length; |
||
1711 | for (var curWeek = 0; curWeek < numWeeks; curWeek++) { |
||
1712 | scope.weekNumbers.push( |
||
1713 | getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date)); |
||
1714 | } |
||
1715 | } |
||
1716 | }; |
||
1717 | |||
1718 | this.compare = function(date1, date2) { |
||
1719 | var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()); |
||
1720 | var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); |
||
1721 | _date1.setFullYear(date1.getFullYear()); |
||
1722 | _date2.setFullYear(date2.getFullYear()); |
||
1723 | return _date1 - _date2; |
||
1724 | }; |
||
1725 | |||
1726 | function getISO8601WeekNumber(date) { |
||
1727 | var checkDate = new Date(date); |
||
1728 | checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday |
||
1729 | var time = checkDate.getTime(); |
||
1730 | checkDate.setMonth(0); // Compare with Jan 1 |
||
1731 | checkDate.setDate(1); |
||
1732 | return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; |
||
1733 | } |
||
1734 | |||
1735 | this.handleKeyDown = function(key, evt) { |
||
1736 | var date = this.activeDate.getDate(); |
||
1737 | |||
1738 | if (key === 'left') { |
||
1739 | date = date - 1; |
||
1740 | } else if (key === 'up') { |
||
1741 | date = date - 7; |
||
1742 | } else if (key === 'right') { |
||
1743 | date = date + 1; |
||
1744 | } else if (key === 'down') { |
||
1745 | date = date + 7; |
||
1746 | } else if (key === 'pageup' || key === 'pagedown') { |
||
1747 | var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1); |
||
1748 | this.activeDate.setMonth(month, 1); |
||
1749 | date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date); |
||
1750 | } else if (key === 'home') { |
||
1751 | date = 1; |
||
1752 | } else if (key === 'end') { |
||
1753 | date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()); |
||
1754 | } |
||
1755 | this.activeDate.setDate(date); |
||
1756 | }; |
||
1757 | }]) |
||
1758 | |||
1759 | View Code Duplication | .controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { |
|
1760 | this.step = { years: 1 }; |
||
1761 | this.element = $element; |
||
1762 | |||
1763 | this.init = function(ctrl) { |
||
1764 | angular.extend(ctrl, this); |
||
1765 | ctrl.refreshView(); |
||
1766 | }; |
||
1767 | |||
1768 | this._refreshView = function() { |
||
1769 | var months = new Array(12), |
||
1770 | year = this.activeDate.getFullYear(), |
||
1771 | date; |
||
1772 | |||
1773 | for (var i = 0; i < 12; i++) { |
||
1774 | date = new Date(this.activeDate); |
||
1775 | date.setFullYear(year, i, 1); |
||
1776 | months[i] = angular.extend(this.createDateObject(date, this.formatMonth), { |
||
1777 | uid: scope.uniqueId + '-' + i |
||
1778 | }); |
||
1779 | } |
||
1780 | |||
1781 | scope.title = dateFilter(this.activeDate, this.formatMonthTitle); |
||
1782 | scope.rows = this.split(months, 3); |
||
1783 | }; |
||
1784 | |||
1785 | this.compare = function(date1, date2) { |
||
1786 | var _date1 = new Date(date1.getFullYear(), date1.getMonth()); |
||
1787 | var _date2 = new Date(date2.getFullYear(), date2.getMonth()); |
||
1788 | _date1.setFullYear(date1.getFullYear()); |
||
1789 | _date2.setFullYear(date2.getFullYear()); |
||
1790 | return _date1 - _date2; |
||
1791 | }; |
||
1792 | |||
1793 | this.handleKeyDown = function(key, evt) { |
||
1794 | var date = this.activeDate.getMonth(); |
||
1795 | |||
1796 | if (key === 'left') { |
||
1797 | date = date - 1; |
||
1798 | } else if (key === 'up') { |
||
1799 | date = date - 3; |
||
1800 | } else if (key === 'right') { |
||
1801 | date = date + 1; |
||
1802 | } else if (key === 'down') { |
||
1803 | date = date + 3; |
||
1804 | } else if (key === 'pageup' || key === 'pagedown') { |
||
1805 | var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1); |
||
1806 | this.activeDate.setFullYear(year); |
||
1807 | } else if (key === 'home') { |
||
1808 | date = 0; |
||
1809 | } else if (key === 'end') { |
||
1810 | date = 11; |
||
1811 | } |
||
1812 | this.activeDate.setMonth(date); |
||
1813 | }; |
||
1814 | }]) |
||
1815 | |||
1816 | View Code Duplication | .controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { |
|
1817 | var columns, range; |
||
1818 | this.element = $element; |
||
1819 | |||
1820 | function getStartingYear(year) { |
||
1821 | return parseInt((year - 1) / range, 10) * range + 1; |
||
1822 | } |
||
1823 | |||
1824 | this.yearpickerInit = function() { |
||
1825 | columns = this.yearColumns; |
||
1826 | range = this.yearRows * columns; |
||
1827 | this.step = { years: range }; |
||
1828 | }; |
||
1829 | |||
1830 | this._refreshView = function() { |
||
1831 | var years = new Array(range), date; |
||
0 ignored issues
–
show
|
|||
1832 | |||
1833 | for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) { |
||
1834 | date = new Date(this.activeDate); |
||
1835 | date.setFullYear(start + i, 0, 1); |
||
1836 | years[i] = angular.extend(this.createDateObject(date, this.formatYear), { |
||
1837 | uid: scope.uniqueId + '-' + i |
||
1838 | }); |
||
1839 | } |
||
1840 | |||
1841 | scope.title = [years[0].label, years[range - 1].label].join(' - '); |
||
1842 | scope.rows = this.split(years, columns); |
||
1843 | scope.columns = columns; |
||
1844 | }; |
||
1845 | |||
1846 | this.compare = function(date1, date2) { |
||
1847 | return date1.getFullYear() - date2.getFullYear(); |
||
1848 | }; |
||
1849 | |||
1850 | this.handleKeyDown = function(key, evt) { |
||
1851 | var date = this.activeDate.getFullYear(); |
||
1852 | |||
1853 | if (key === 'left') { |
||
1854 | date = date - 1; |
||
1855 | } else if (key === 'up') { |
||
1856 | date = date - columns; |
||
1857 | } else if (key === 'right') { |
||
1858 | date = date + 1; |
||
1859 | } else if (key === 'down') { |
||
1860 | date = date + columns; |
||
1861 | } else if (key === 'pageup' || key === 'pagedown') { |
||
1862 | date += (key === 'pageup' ? - 1 : 1) * range; |
||
1863 | } else if (key === 'home') { |
||
1864 | date = getStartingYear(this.activeDate.getFullYear()); |
||
1865 | } else if (key === 'end') { |
||
1866 | date = getStartingYear(this.activeDate.getFullYear()) + range - 1; |
||
1867 | } |
||
1868 | this.activeDate.setFullYear(date); |
||
1869 | }; |
||
1870 | }]) |
||
1871 | |||
1872 | View Code Duplication | .directive('uibDatepicker', function() { |
|
1873 | return { |
||
1874 | replace: true, |
||
1875 | templateUrl: function(element, attrs) { |
||
1876 | return attrs.templateUrl || 'uib/template/datepicker/datepicker.html'; |
||
1877 | }, |
||
1878 | scope: { |
||
1879 | datepickerOptions: '=?' |
||
1880 | }, |
||
1881 | require: ['uibDatepicker', '^ngModel'], |
||
1882 | controller: 'UibDatepickerController', |
||
1883 | controllerAs: 'datepicker', |
||
1884 | link: function(scope, element, attrs, ctrls) { |
||
1885 | var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; |
||
1886 | |||
1887 | datepickerCtrl.init(ngModelCtrl); |
||
1888 | } |
||
1889 | }; |
||
1890 | }) |
||
1891 | |||
1892 | .directive('uibDaypicker', function() { |
||
1893 | return { |
||
1894 | replace: true, |
||
1895 | templateUrl: function(element, attrs) { |
||
1896 | return attrs.templateUrl || 'uib/template/datepicker/day.html'; |
||
1897 | }, |
||
1898 | require: ['^uibDatepicker', 'uibDaypicker'], |
||
1899 | controller: 'UibDaypickerController', |
||
1900 | link: function(scope, element, attrs, ctrls) { |
||
1901 | var datepickerCtrl = ctrls[0], |
||
1902 | daypickerCtrl = ctrls[1]; |
||
1903 | |||
1904 | daypickerCtrl.init(datepickerCtrl); |
||
1905 | } |
||
1906 | }; |
||
1907 | }) |
||
1908 | |||
1909 | .directive('uibMonthpicker', function() { |
||
1910 | return { |
||
1911 | replace: true, |
||
1912 | templateUrl: function(element, attrs) { |
||
1913 | return attrs.templateUrl || 'uib/template/datepicker/month.html'; |
||
1914 | }, |
||
1915 | require: ['^uibDatepicker', 'uibMonthpicker'], |
||
1916 | controller: 'UibMonthpickerController', |
||
1917 | link: function(scope, element, attrs, ctrls) { |
||
1918 | var datepickerCtrl = ctrls[0], |
||
1919 | monthpickerCtrl = ctrls[1]; |
||
1920 | |||
1921 | monthpickerCtrl.init(datepickerCtrl); |
||
1922 | } |
||
1923 | }; |
||
1924 | }) |
||
1925 | |||
1926 | .directive('uibYearpicker', function() { |
||
1927 | return { |
||
1928 | replace: true, |
||
1929 | templateUrl: function(element, attrs) { |
||
1930 | return attrs.templateUrl || 'uib/template/datepicker/year.html'; |
||
1931 | }, |
||
1932 | require: ['^uibDatepicker', 'uibYearpicker'], |
||
1933 | controller: 'UibYearpickerController', |
||
1934 | link: function(scope, element, attrs, ctrls) { |
||
1935 | var ctrl = ctrls[0]; |
||
1936 | angular.extend(ctrl, ctrls[1]); |
||
1937 | ctrl.yearpickerInit(); |
||
1938 | |||
1939 | ctrl.refreshView(); |
||
1940 | } |
||
1941 | }; |
||
1942 | }); |
||
1943 | |||
1944 | angular.module('ui.bootstrap.position', []) |
||
1945 | |||
1946 | /** |
||
1947 | * A set of utility methods for working with the DOM. |
||
1948 | * It is meant to be used where we need to absolute-position elements in |
||
1949 | * relation to another element (this is the case for tooltips, popovers, |
||
1950 | * typeahead suggestions etc.). |
||
1951 | */ |
||
1952 | View Code Duplication | .factory('$uibPosition', ['$document', '$window', function($document, $window) { |
|
1953 | /** |
||
1954 | * Used by scrollbarWidth() function to cache scrollbar's width. |
||
1955 | * Do not access this variable directly, use scrollbarWidth() instead. |
||
1956 | */ |
||
1957 | var SCROLLBAR_WIDTH; |
||
1958 | /** |
||
1959 | * scrollbar on body and html element in IE and Edge overlay |
||
1960 | * content and should be considered 0 width. |
||
1961 | */ |
||
1962 | var BODY_SCROLLBAR_WIDTH; |
||
1963 | var OVERFLOW_REGEX = { |
||
1964 | normal: /(auto|scroll)/, |
||
1965 | hidden: /(auto|scroll|hidden)/ |
||
1966 | }; |
||
1967 | var PLACEMENT_REGEX = { |
||
1968 | auto: /\s?auto?\s?/i, |
||
1969 | primary: /^(top|bottom|left|right)$/, |
||
1970 | secondary: /^(top|bottom|left|right|center)$/, |
||
1971 | vertical: /^(top|bottom)$/ |
||
1972 | }; |
||
1973 | var BODY_REGEX = /(HTML|BODY)/; |
||
1974 | |||
1975 | return { |
||
1976 | |||
1977 | /** |
||
1978 | * Provides a raw DOM element from a jQuery/jQLite element. |
||
1979 | * |
||
1980 | * @param {element} elem - The element to convert. |
||
1981 | * |
||
1982 | * @returns {element} A HTML element. |
||
1983 | */ |
||
1984 | getRawNode: function(elem) { |
||
1985 | return elem.nodeName ? elem : elem[0] || elem; |
||
1986 | }, |
||
1987 | |||
1988 | /** |
||
1989 | * Provides a parsed number for a style property. Strips |
||
1990 | * units and casts invalid numbers to 0. |
||
1991 | * |
||
1992 | * @param {string} value - The style value to parse. |
||
1993 | * |
||
1994 | * @returns {number} A valid number. |
||
1995 | */ |
||
1996 | parseStyle: function(value) { |
||
1997 | value = parseFloat(value); |
||
1998 | return isFinite(value) ? value : 0; |
||
1999 | }, |
||
2000 | |||
2001 | /** |
||
2002 | * Provides the closest positioned ancestor. |
||
2003 | * |
||
2004 | * @param {element} element - The element to get the offest parent for. |
||
2005 | * |
||
2006 | * @returns {element} The closest positioned ancestor. |
||
2007 | */ |
||
2008 | offsetParent: function(elem) { |
||
2009 | elem = this.getRawNode(elem); |
||
2010 | |||
2011 | var offsetParent = elem.offsetParent || $document[0].documentElement; |
||
2012 | |||
2013 | function isStaticPositioned(el) { |
||
2014 | return ($window.getComputedStyle(el).position || 'static') === 'static'; |
||
2015 | } |
||
2016 | |||
2017 | while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) { |
||
2018 | offsetParent = offsetParent.offsetParent; |
||
2019 | } |
||
2020 | |||
2021 | return offsetParent || $document[0].documentElement; |
||
2022 | }, |
||
2023 | |||
2024 | /** |
||
2025 | * Provides the scrollbar width, concept from TWBS measureScrollbar() |
||
2026 | * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js |
||
2027 | * In IE and Edge, scollbar on body and html element overlay and should |
||
2028 | * return a width of 0. |
||
2029 | * |
||
2030 | * @returns {number} The width of the browser scollbar. |
||
2031 | */ |
||
2032 | scrollbarWidth: function(isBody) { |
||
2033 | if (isBody) { |
||
2034 | if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) { |
||
2035 | var bodyElem = $document.find('body'); |
||
2036 | bodyElem.addClass('uib-position-body-scrollbar-measure'); |
||
2037 | BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth; |
||
2038 | BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0; |
||
2039 | bodyElem.removeClass('uib-position-body-scrollbar-measure'); |
||
2040 | } |
||
2041 | return BODY_SCROLLBAR_WIDTH; |
||
2042 | } |
||
2043 | |||
2044 | if (angular.isUndefined(SCROLLBAR_WIDTH)) { |
||
2045 | var scrollElem = angular.element('<div class="uib-position-scrollbar-measure"></div>'); |
||
2046 | $document.find('body').append(scrollElem); |
||
2047 | SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth; |
||
2048 | SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0; |
||
2049 | scrollElem.remove(); |
||
2050 | } |
||
2051 | |||
2052 | return SCROLLBAR_WIDTH; |
||
2053 | }, |
||
2054 | |||
2055 | /** |
||
2056 | * Provides the padding required on an element to replace the scrollbar. |
||
2057 | * |
||
2058 | * @returns {object} An object with the following properties: |
||
2059 | * <ul> |
||
2060 | * <li>**scrollbarWidth**: the width of the scrollbar</li> |
||
2061 | * <li>**widthOverflow**: whether the the width is overflowing</li> |
||
2062 | * <li>**right**: the amount of right padding on the element needed to replace the scrollbar</li> |
||
2063 | * <li>**rightOriginal**: the amount of right padding currently on the element</li> |
||
2064 | * <li>**heightOverflow**: whether the the height is overflowing</li> |
||
2065 | * <li>**bottom**: the amount of bottom padding on the element needed to replace the scrollbar</li> |
||
2066 | * <li>**bottomOriginal**: the amount of bottom padding currently on the element</li> |
||
2067 | * </ul> |
||
2068 | */ |
||
2069 | scrollbarPadding: function(elem) { |
||
2070 | elem = this.getRawNode(elem); |
||
2071 | |||
2072 | var elemStyle = $window.getComputedStyle(elem); |
||
2073 | var paddingRight = this.parseStyle(elemStyle.paddingRight); |
||
2074 | var paddingBottom = this.parseStyle(elemStyle.paddingBottom); |
||
2075 | var scrollParent = this.scrollParent(elem, false, true); |
||
2076 | var scrollbarWidth = this.scrollbarWidth(scrollParent, BODY_REGEX.test(scrollParent.tagName)); |
||
2077 | |||
2078 | return { |
||
2079 | scrollbarWidth: scrollbarWidth, |
||
2080 | widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth, |
||
2081 | right: paddingRight + scrollbarWidth, |
||
2082 | originalRight: paddingRight, |
||
2083 | heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight, |
||
2084 | bottom: paddingBottom + scrollbarWidth, |
||
2085 | originalBottom: paddingBottom |
||
2086 | }; |
||
2087 | }, |
||
2088 | |||
2089 | /** |
||
2090 | * Checks to see if the element is scrollable. |
||
2091 | * |
||
2092 | * @param {element} elem - The element to check. |
||
2093 | * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, |
||
2094 | * default is false. |
||
2095 | * |
||
2096 | * @returns {boolean} Whether the element is scrollable. |
||
2097 | */ |
||
2098 | isScrollable: function(elem, includeHidden) { |
||
2099 | elem = this.getRawNode(elem); |
||
2100 | |||
2101 | var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; |
||
2102 | var elemStyle = $window.getComputedStyle(elem); |
||
2103 | return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX); |
||
2104 | }, |
||
2105 | |||
2106 | /** |
||
2107 | * Provides the closest scrollable ancestor. |
||
2108 | * A port of the jQuery UI scrollParent method: |
||
2109 | * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js |
||
2110 | * |
||
2111 | * @param {element} elem - The element to find the scroll parent of. |
||
2112 | * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, |
||
2113 | * default is false. |
||
2114 | * @param {boolean=} [includeSelf=false] - Should the element being passed be |
||
2115 | * included in the scrollable llokup. |
||
2116 | * |
||
2117 | * @returns {element} A HTML element. |
||
2118 | */ |
||
2119 | scrollParent: function(elem, includeHidden, includeSelf) { |
||
2120 | elem = this.getRawNode(elem); |
||
2121 | |||
2122 | var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; |
||
2123 | var documentEl = $document[0].documentElement; |
||
2124 | var elemStyle = $window.getComputedStyle(elem); |
||
2125 | if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) { |
||
2126 | return elem; |
||
2127 | } |
||
2128 | var excludeStatic = elemStyle.position === 'absolute'; |
||
2129 | var scrollParent = elem.parentElement || documentEl; |
||
2130 | |||
2131 | if (scrollParent === documentEl || elemStyle.position === 'fixed') { |
||
2132 | return documentEl; |
||
2133 | } |
||
2134 | |||
2135 | while (scrollParent.parentElement && scrollParent !== documentEl) { |
||
2136 | var spStyle = $window.getComputedStyle(scrollParent); |
||
2137 | if (excludeStatic && spStyle.position !== 'static') { |
||
2138 | excludeStatic = false; |
||
2139 | } |
||
2140 | |||
2141 | if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) { |
||
2142 | break; |
||
2143 | } |
||
2144 | scrollParent = scrollParent.parentElement; |
||
2145 | } |
||
2146 | |||
2147 | return scrollParent; |
||
2148 | }, |
||
2149 | |||
2150 | /** |
||
2151 | * Provides read-only equivalent of jQuery's position function: |
||
2152 | * http://api.jquery.com/position/ - distance to closest positioned |
||
2153 | * ancestor. Does not account for margins by default like jQuery position. |
||
2154 | * |
||
2155 | * @param {element} elem - The element to caclulate the position on. |
||
2156 | * @param {boolean=} [includeMargins=false] - Should margins be accounted |
||
2157 | * for, default is false. |
||
2158 | * |
||
2159 | * @returns {object} An object with the following properties: |
||
2160 | * <ul> |
||
2161 | * <li>**width**: the width of the element</li> |
||
2162 | * <li>**height**: the height of the element</li> |
||
2163 | * <li>**top**: distance to top edge of offset parent</li> |
||
2164 | * <li>**left**: distance to left edge of offset parent</li> |
||
2165 | * </ul> |
||
2166 | */ |
||
2167 | position: function(elem, includeMagins) { |
||
2168 | elem = this.getRawNode(elem); |
||
2169 | |||
2170 | var elemOffset = this.offset(elem); |
||
2171 | if (includeMagins) { |
||
2172 | var elemStyle = $window.getComputedStyle(elem); |
||
2173 | elemOffset.top -= this.parseStyle(elemStyle.marginTop); |
||
2174 | elemOffset.left -= this.parseStyle(elemStyle.marginLeft); |
||
2175 | } |
||
2176 | var parent = this.offsetParent(elem); |
||
2177 | var parentOffset = {top: 0, left: 0}; |
||
2178 | |||
2179 | if (parent !== $document[0].documentElement) { |
||
2180 | parentOffset = this.offset(parent); |
||
2181 | parentOffset.top += parent.clientTop - parent.scrollTop; |
||
2182 | parentOffset.left += parent.clientLeft - parent.scrollLeft; |
||
2183 | } |
||
2184 | |||
2185 | return { |
||
2186 | width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth), |
||
2187 | height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight), |
||
2188 | top: Math.round(elemOffset.top - parentOffset.top), |
||
2189 | left: Math.round(elemOffset.left - parentOffset.left) |
||
2190 | }; |
||
2191 | }, |
||
2192 | |||
2193 | /** |
||
2194 | * Provides read-only equivalent of jQuery's offset function: |
||
2195 | * http://api.jquery.com/offset/ - distance to viewport. Does |
||
2196 | * not account for borders, margins, or padding on the body |
||
2197 | * element. |
||
2198 | * |
||
2199 | * @param {element} elem - The element to calculate the offset on. |
||
2200 | * |
||
2201 | * @returns {object} An object with the following properties: |
||
2202 | * <ul> |
||
2203 | * <li>**width**: the width of the element</li> |
||
2204 | * <li>**height**: the height of the element</li> |
||
2205 | * <li>**top**: distance to top edge of viewport</li> |
||
2206 | * <li>**right**: distance to bottom edge of viewport</li> |
||
2207 | * </ul> |
||
2208 | */ |
||
2209 | offset: function(elem) { |
||
2210 | elem = this.getRawNode(elem); |
||
2211 | |||
2212 | var elemBCR = elem.getBoundingClientRect(); |
||
2213 | return { |
||
2214 | width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth), |
||
2215 | height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight), |
||
2216 | top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)), |
||
2217 | left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)) |
||
2218 | }; |
||
2219 | }, |
||
2220 | |||
2221 | /** |
||
2222 | * Provides offset distance to the closest scrollable ancestor |
||
2223 | * or viewport. Accounts for border and scrollbar width. |
||
2224 | * |
||
2225 | * Right and bottom dimensions represent the distance to the |
||
2226 | * respective edge of the viewport element. If the element |
||
2227 | * edge extends beyond the viewport, a negative value will be |
||
2228 | * reported. |
||
2229 | * |
||
2230 | * @param {element} elem - The element to get the viewport offset for. |
||
2231 | * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead |
||
2232 | * of the first scrollable element, default is false. |
||
2233 | * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element |
||
2234 | * be accounted for, default is true. |
||
2235 | * |
||
2236 | * @returns {object} An object with the following properties: |
||
2237 | * <ul> |
||
2238 | * <li>**top**: distance to the top content edge of viewport element</li> |
||
2239 | * <li>**bottom**: distance to the bottom content edge of viewport element</li> |
||
2240 | * <li>**left**: distance to the left content edge of viewport element</li> |
||
2241 | * <li>**right**: distance to the right content edge of viewport element</li> |
||
2242 | * </ul> |
||
2243 | */ |
||
2244 | viewportOffset: function(elem, useDocument, includePadding) { |
||
2245 | elem = this.getRawNode(elem); |
||
2246 | includePadding = includePadding !== false ? true : false; |
||
2247 | |||
2248 | var elemBCR = elem.getBoundingClientRect(); |
||
2249 | var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0}; |
||
2250 | |||
2251 | var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem); |
||
2252 | var offsetParentBCR = offsetParent.getBoundingClientRect(); |
||
2253 | |||
2254 | offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop; |
||
2255 | offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft; |
||
2256 | if (offsetParent === $document[0].documentElement) { |
||
2257 | offsetBCR.top += $window.pageYOffset; |
||
2258 | offsetBCR.left += $window.pageXOffset; |
||
2259 | } |
||
2260 | offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight; |
||
2261 | offsetBCR.right = offsetBCR.left + offsetParent.clientWidth; |
||
2262 | |||
2263 | if (includePadding) { |
||
2264 | var offsetParentStyle = $window.getComputedStyle(offsetParent); |
||
2265 | offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop); |
||
2266 | offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom); |
||
2267 | offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft); |
||
2268 | offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight); |
||
2269 | } |
||
2270 | |||
2271 | return { |
||
2272 | top: Math.round(elemBCR.top - offsetBCR.top), |
||
2273 | bottom: Math.round(offsetBCR.bottom - elemBCR.bottom), |
||
2274 | left: Math.round(elemBCR.left - offsetBCR.left), |
||
2275 | right: Math.round(offsetBCR.right - elemBCR.right) |
||
2276 | }; |
||
2277 | }, |
||
2278 | |||
2279 | /** |
||
2280 | * Provides an array of placement values parsed from a placement string. |
||
2281 | * Along with the 'auto' indicator, supported placement strings are: |
||
2282 | * <ul> |
||
2283 | * <li>top: element on top, horizontally centered on host element.</li> |
||
2284 | * <li>top-left: element on top, left edge aligned with host element left edge.</li> |
||
2285 | * <li>top-right: element on top, lerightft edge aligned with host element right edge.</li> |
||
2286 | * <li>bottom: element on bottom, horizontally centered on host element.</li> |
||
2287 | * <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li> |
||
2288 | * <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li> |
||
2289 | * <li>left: element on left, vertically centered on host element.</li> |
||
2290 | * <li>left-top: element on left, top edge aligned with host element top edge.</li> |
||
2291 | * <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li> |
||
2292 | * <li>right: element on right, vertically centered on host element.</li> |
||
2293 | * <li>right-top: element on right, top edge aligned with host element top edge.</li> |
||
2294 | * <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li> |
||
2295 | * </ul> |
||
2296 | * A placement string with an 'auto' indicator is expected to be |
||
2297 | * space separated from the placement, i.e: 'auto bottom-left' If |
||
2298 | * the primary and secondary placement values do not match 'top, |
||
2299 | * bottom, left, right' then 'top' will be the primary placement and |
||
2300 | * 'center' will be the secondary placement. If 'auto' is passed, true |
||
2301 | * will be returned as the 3rd value of the array. |
||
2302 | * |
||
2303 | * @param {string} placement - The placement string to parse. |
||
2304 | * |
||
2305 | * @returns {array} An array with the following values |
||
2306 | * <ul> |
||
2307 | * <li>**[0]**: The primary placement.</li> |
||
2308 | * <li>**[1]**: The secondary placement.</li> |
||
2309 | * <li>**[2]**: If auto is passed: true, else undefined.</li> |
||
2310 | * </ul> |
||
2311 | */ |
||
2312 | parsePlacement: function(placement) { |
||
2313 | var autoPlace = PLACEMENT_REGEX.auto.test(placement); |
||
2314 | if (autoPlace) { |
||
2315 | placement = placement.replace(PLACEMENT_REGEX.auto, ''); |
||
2316 | } |
||
2317 | |||
2318 | placement = placement.split('-'); |
||
2319 | |||
2320 | placement[0] = placement[0] || 'top'; |
||
2321 | if (!PLACEMENT_REGEX.primary.test(placement[0])) { |
||
2322 | placement[0] = 'top'; |
||
2323 | } |
||
2324 | |||
2325 | placement[1] = placement[1] || 'center'; |
||
2326 | if (!PLACEMENT_REGEX.secondary.test(placement[1])) { |
||
2327 | placement[1] = 'center'; |
||
2328 | } |
||
2329 | |||
2330 | if (autoPlace) { |
||
2331 | placement[2] = true; |
||
2332 | } else { |
||
2333 | placement[2] = false; |
||
2334 | } |
||
2335 | |||
2336 | return placement; |
||
2337 | }, |
||
2338 | |||
2339 | /** |
||
2340 | * Provides coordinates for an element to be positioned relative to |
||
2341 | * another element. Passing 'auto' as part of the placement parameter |
||
2342 | * will enable smart placement - where the element fits. i.e: |
||
2343 | * 'auto left-top' will check to see if there is enough space to the left |
||
2344 | * of the hostElem to fit the targetElem, if not place right (same for secondary |
||
2345 | * top placement). Available space is calculated using the viewportOffset |
||
2346 | * function. |
||
2347 | * |
||
2348 | * @param {element} hostElem - The element to position against. |
||
2349 | * @param {element} targetElem - The element to position. |
||
2350 | * @param {string=} [placement=top] - The placement for the targetElem, |
||
2351 | * default is 'top'. 'center' is assumed as secondary placement for |
||
2352 | * 'top', 'left', 'right', and 'bottom' placements. Available placements are: |
||
2353 | * <ul> |
||
2354 | * <li>top</li> |
||
2355 | * <li>top-right</li> |
||
2356 | * <li>top-left</li> |
||
2357 | * <li>bottom</li> |
||
2358 | * <li>bottom-left</li> |
||
2359 | * <li>bottom-right</li> |
||
2360 | * <li>left</li> |
||
2361 | * <li>left-top</li> |
||
2362 | * <li>left-bottom</li> |
||
2363 | * <li>right</li> |
||
2364 | * <li>right-top</li> |
||
2365 | * <li>right-bottom</li> |
||
2366 | * </ul> |
||
2367 | * @param {boolean=} [appendToBody=false] - Should the top and left values returned |
||
2368 | * be calculated from the body element, default is false. |
||
2369 | * |
||
2370 | * @returns {object} An object with the following properties: |
||
2371 | * <ul> |
||
2372 | * <li>**top**: Value for targetElem top.</li> |
||
2373 | * <li>**left**: Value for targetElem left.</li> |
||
2374 | * <li>**placement**: The resolved placement.</li> |
||
2375 | * </ul> |
||
2376 | */ |
||
2377 | positionElements: function(hostElem, targetElem, placement, appendToBody) { |
||
2378 | hostElem = this.getRawNode(hostElem); |
||
2379 | targetElem = this.getRawNode(targetElem); |
||
2380 | |||
2381 | // need to read from prop to support tests. |
||
2382 | var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth'); |
||
2383 | var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight'); |
||
2384 | |||
2385 | placement = this.parsePlacement(placement); |
||
2386 | |||
2387 | var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem); |
||
2388 | var targetElemPos = {top: 0, left: 0, placement: ''}; |
||
2389 | |||
2390 | if (placement[2]) { |
||
2391 | var viewportOffset = this.viewportOffset(hostElem, appendToBody); |
||
2392 | |||
2393 | var targetElemStyle = $window.getComputedStyle(targetElem); |
||
2394 | var adjustedSize = { |
||
2395 | width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))), |
||
2396 | height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom))) |
||
2397 | }; |
||
2398 | |||
2399 | placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' : |
||
2400 | placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' : |
||
2401 | placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' : |
||
2402 | placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' : |
||
2403 | placement[0]; |
||
2404 | |||
2405 | placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' : |
||
2406 | placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' : |
||
2407 | placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' : |
||
2408 | placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' : |
||
2409 | placement[1]; |
||
2410 | |||
2411 | if (placement[1] === 'center') { |
||
2412 | if (PLACEMENT_REGEX.vertical.test(placement[0])) { |
||
2413 | var xOverflow = hostElemPos.width / 2 - targetWidth / 2; |
||
2414 | if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) { |
||
2415 | placement[1] = 'left'; |
||
2416 | } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) { |
||
2417 | placement[1] = 'right'; |
||
2418 | } |
||
2419 | } else { |
||
2420 | var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2; |
||
2421 | if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) { |
||
2422 | placement[1] = 'top'; |
||
2423 | } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) { |
||
2424 | placement[1] = 'bottom'; |
||
2425 | } |
||
2426 | } |
||
2427 | } |
||
2428 | } |
||
2429 | |||
2430 | switch (placement[0]) { |
||
2431 | case 'top': |
||
2432 | targetElemPos.top = hostElemPos.top - targetHeight; |
||
2433 | break; |
||
2434 | case 'bottom': |
||
2435 | targetElemPos.top = hostElemPos.top + hostElemPos.height; |
||
2436 | break; |
||
2437 | case 'left': |
||
2438 | targetElemPos.left = hostElemPos.left - targetWidth; |
||
2439 | break; |
||
2440 | case 'right': |
||
2441 | targetElemPos.left = hostElemPos.left + hostElemPos.width; |
||
2442 | break; |
||
2443 | } |
||
2444 | |||
2445 | switch (placement[1]) { |
||
2446 | case 'top': |
||
2447 | targetElemPos.top = hostElemPos.top; |
||
2448 | break; |
||
2449 | case 'bottom': |
||
2450 | targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight; |
||
2451 | break; |
||
2452 | case 'left': |
||
2453 | targetElemPos.left = hostElemPos.left; |
||
2454 | break; |
||
2455 | case 'right': |
||
2456 | targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth; |
||
2457 | break; |
||
2458 | case 'center': |
||
2459 | if (PLACEMENT_REGEX.vertical.test(placement[0])) { |
||
2460 | targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2; |
||
2461 | } else { |
||
2462 | targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2; |
||
2463 | } |
||
2464 | break; |
||
2465 | } |
||
2466 | |||
2467 | targetElemPos.top = Math.round(targetElemPos.top); |
||
2468 | targetElemPos.left = Math.round(targetElemPos.left); |
||
2469 | targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1]; |
||
2470 | |||
2471 | return targetElemPos; |
||
2472 | }, |
||
2473 | |||
2474 | /** |
||
2475 | * Provides a way for positioning tooltip & dropdown |
||
2476 | * arrows when using placement options beyond the standard |
||
2477 | * left, right, top, or bottom. |
||
2478 | * |
||
2479 | * @param {element} elem - The tooltip/dropdown element. |
||
2480 | * @param {string} placement - The placement for the elem. |
||
2481 | */ |
||
2482 | positionArrow: function(elem, placement) { |
||
2483 | elem = this.getRawNode(elem); |
||
2484 | |||
2485 | var innerElem = elem.querySelector('.tooltip-inner, .popover-inner'); |
||
2486 | if (!innerElem) { |
||
2487 | return; |
||
2488 | } |
||
2489 | |||
2490 | var isTooltip = angular.element(innerElem).hasClass('tooltip-inner'); |
||
2491 | |||
2492 | var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow'); |
||
2493 | if (!arrowElem) { |
||
2494 | return; |
||
2495 | } |
||
2496 | |||
2497 | var arrowCss = { |
||
2498 | top: '', |
||
2499 | bottom: '', |
||
2500 | left: '', |
||
2501 | right: '' |
||
2502 | }; |
||
2503 | |||
2504 | placement = this.parsePlacement(placement); |
||
2505 | if (placement[1] === 'center') { |
||
2506 | // no adjustment necessary - just reset styles |
||
2507 | angular.element(arrowElem).css(arrowCss); |
||
2508 | return; |
||
2509 | } |
||
2510 | |||
2511 | var borderProp = 'border-' + placement[0] + '-width'; |
||
2512 | var borderWidth = $window.getComputedStyle(arrowElem)[borderProp]; |
||
2513 | |||
2514 | var borderRadiusProp = 'border-'; |
||
2515 | if (PLACEMENT_REGEX.vertical.test(placement[0])) { |
||
2516 | borderRadiusProp += placement[0] + '-' + placement[1]; |
||
2517 | } else { |
||
2518 | borderRadiusProp += placement[1] + '-' + placement[0]; |
||
2519 | } |
||
2520 | borderRadiusProp += '-radius'; |
||
2521 | var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp]; |
||
2522 | |||
2523 | switch (placement[0]) { |
||
2524 | case 'top': |
||
2525 | arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth; |
||
2526 | break; |
||
2527 | case 'bottom': |
||
2528 | arrowCss.top = isTooltip ? '0' : '-' + borderWidth; |
||
2529 | break; |
||
2530 | case 'left': |
||
2531 | arrowCss.right = isTooltip ? '0' : '-' + borderWidth; |
||
2532 | break; |
||
2533 | case 'right': |
||
2534 | arrowCss.left = isTooltip ? '0' : '-' + borderWidth; |
||
2535 | break; |
||
2536 | } |
||
2537 | |||
2538 | arrowCss[placement[1]] = borderRadius; |
||
2539 | |||
2540 | angular.element(arrowElem).css(arrowCss); |
||
2541 | } |
||
2542 | }; |
||
2543 | }]); |
||
2544 | |||
2545 | angular.module('ui.bootstrap.datepickerPopup', ['ui.bootstrap.datepicker', 'ui.bootstrap.position']) |
||
2546 | |||
2547 | .value('$datepickerPopupLiteralWarning', true) |
||
2548 | |||
2549 | .constant('uibDatepickerPopupConfig', { |
||
2550 | altInputFormats: [], |
||
2551 | appendToBody: false, |
||
2552 | clearText: 'Clear', |
||
2553 | closeOnDateSelection: true, |
||
2554 | closeText: 'Done', |
||
2555 | currentText: 'Today', |
||
2556 | datepickerPopup: 'dd/MM/yyyy', |
||
2557 | datepickerPopupTemplateUrl: 'uib/template/datepickerPopup/popup.html', |
||
2558 | datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html', |
||
2559 | html5Types: { |
||
2560 | date: 'yyyy-MM-dd', |
||
2561 | 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss', |
||
2562 | 'month': 'yyyy-MM' |
||
2563 | }, |
||
2564 | onOpenFocus: true, |
||
2565 | showButtonBar: true, |
||
2566 | placement: 'auto bottom-left' |
||
2567 | }) |
||
2568 | |||
2569 | .controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$log', '$parse', '$window', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', '$datepickerPopupLiteralWarning', |
||
2570 | View Code Duplication | function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig, $datepickerPopupLiteralWarning) { |
|
2571 | var cache = {}, |
||
0 ignored issues
–
show
|
|||
2572 | isHtml5DateInput = false; |
||
2573 | var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus, |
||
2574 | datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl, |
||
2575 | ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = [], |
||
2576 | timezone; |
||
2577 | |||
2578 | this.init = function(_ngModel_) { |
||
2579 | ngModel = _ngModel_; |
||
2580 | ngModelOptions = _ngModel_.$options; |
||
2581 | closeOnDateSelection = angular.isDefined($attrs.closeOnDateSelection) ? |
||
2582 | $scope.$parent.$eval($attrs.closeOnDateSelection) : |
||
2583 | datepickerPopupConfig.closeOnDateSelection; |
||
2584 | appendToBody = angular.isDefined($attrs.datepickerAppendToBody) ? |
||
2585 | $scope.$parent.$eval($attrs.datepickerAppendToBody) : |
||
2586 | datepickerPopupConfig.appendToBody; |
||
2587 | onOpenFocus = angular.isDefined($attrs.onOpenFocus) ? |
||
2588 | $scope.$parent.$eval($attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus; |
||
2589 | datepickerPopupTemplateUrl = angular.isDefined($attrs.datepickerPopupTemplateUrl) ? |
||
2590 | $attrs.datepickerPopupTemplateUrl : |
||
2591 | datepickerPopupConfig.datepickerPopupTemplateUrl; |
||
2592 | datepickerTemplateUrl = angular.isDefined($attrs.datepickerTemplateUrl) ? |
||
2593 | $attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl; |
||
2594 | altInputFormats = angular.isDefined($attrs.altInputFormats) ? |
||
2595 | $scope.$parent.$eval($attrs.altInputFormats) : |
||
2596 | datepickerPopupConfig.altInputFormats; |
||
2597 | |||
2598 | $scope.showButtonBar = angular.isDefined($attrs.showButtonBar) ? |
||
2599 | $scope.$parent.$eval($attrs.showButtonBar) : |
||
2600 | datepickerPopupConfig.showButtonBar; |
||
2601 | |||
2602 | if (datepickerPopupConfig.html5Types[$attrs.type]) { |
||
2603 | dateFormat = datepickerPopupConfig.html5Types[$attrs.type]; |
||
2604 | isHtml5DateInput = true; |
||
2605 | } else { |
||
2606 | dateFormat = $attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup; |
||
2607 | $attrs.$observe('uibDatepickerPopup', function(value, oldValue) { |
||
2608 | var newDateFormat = value || datepickerPopupConfig.datepickerPopup; |
||
2609 | // Invalidate the $modelValue to ensure that formatters re-run |
||
2610 | // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764 |
||
2611 | if (newDateFormat !== dateFormat) { |
||
2612 | dateFormat = newDateFormat; |
||
2613 | ngModel.$modelValue = null; |
||
2614 | |||
2615 | if (!dateFormat) { |
||
2616 | throw new Error('uibDatepickerPopup must have a date format specified.'); |
||
2617 | } |
||
2618 | } |
||
2619 | }); |
||
2620 | } |
||
2621 | |||
2622 | if (!dateFormat) { |
||
2623 | throw new Error('uibDatepickerPopup must have a date format specified.'); |
||
2624 | } |
||
2625 | |||
2626 | if (isHtml5DateInput && $attrs.uibDatepickerPopup) { |
||
2627 | throw new Error('HTML5 date input types do not support custom formats.'); |
||
2628 | } |
||
2629 | |||
2630 | // popup element used to display calendar |
||
2631 | popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>'); |
||
2632 | if (ngModelOptions) { |
||
2633 | timezone = ngModelOptions.timezone; |
||
2634 | $scope.ngModelOptions = angular.copy(ngModelOptions); |
||
2635 | $scope.ngModelOptions.timezone = null; |
||
2636 | if ($scope.ngModelOptions.updateOnDefault === true) { |
||
2637 | $scope.ngModelOptions.updateOn = $scope.ngModelOptions.updateOn ? |
||
2638 | $scope.ngModelOptions.updateOn + ' default' : 'default'; |
||
2639 | } |
||
2640 | |||
2641 | popupEl.attr('ng-model-options', 'ngModelOptions'); |
||
2642 | } else { |
||
2643 | timezone = null; |
||
2644 | } |
||
2645 | |||
2646 | popupEl.attr({ |
||
2647 | 'ng-model': 'date', |
||
2648 | 'ng-change': 'dateSelection(date)', |
||
2649 | 'template-url': datepickerPopupTemplateUrl |
||
2650 | }); |
||
2651 | |||
2652 | // datepicker element |
||
2653 | datepickerEl = angular.element(popupEl.children()[0]); |
||
2654 | datepickerEl.attr('template-url', datepickerTemplateUrl); |
||
2655 | |||
2656 | if (!$scope.datepickerOptions) { |
||
2657 | $scope.datepickerOptions = {}; |
||
2658 | } |
||
2659 | |||
2660 | if (isHtml5DateInput) { |
||
2661 | if ($attrs.type === 'month') { |
||
2662 | $scope.datepickerOptions.datepickerMode = 'month'; |
||
2663 | $scope.datepickerOptions.minMode = 'month'; |
||
2664 | } |
||
2665 | } |
||
2666 | |||
2667 | datepickerEl.attr('datepicker-options', 'datepickerOptions'); |
||
2668 | |||
2669 | if (!isHtml5DateInput) { |
||
2670 | // Internal API to maintain the correct ng-invalid-[key] class |
||
2671 | ngModel.$$parserName = 'date'; |
||
2672 | ngModel.$validators.date = validator; |
||
2673 | ngModel.$parsers.unshift(parseDate); |
||
2674 | ngModel.$formatters.push(function(value) { |
||
2675 | if (ngModel.$isEmpty(value)) { |
||
2676 | $scope.date = value; |
||
2677 | return value; |
||
2678 | } |
||
2679 | |||
2680 | $scope.date = dateParser.fromTimezone(value, timezone); |
||
2681 | |||
2682 | if (angular.isNumber($scope.date)) { |
||
2683 | $scope.date = new Date($scope.date); |
||
2684 | } |
||
2685 | |||
2686 | return dateParser.filter($scope.date, dateFormat); |
||
2687 | }); |
||
2688 | } else { |
||
2689 | ngModel.$formatters.push(function(value) { |
||
2690 | $scope.date = dateParser.fromTimezone(value, timezone); |
||
2691 | return value; |
||
2692 | }); |
||
2693 | } |
||
2694 | |||
2695 | // Detect changes in the view from the text box |
||
2696 | ngModel.$viewChangeListeners.push(function() { |
||
2697 | $scope.date = parseDateString(ngModel.$viewValue); |
||
2698 | }); |
||
2699 | |||
2700 | $element.on('keydown', inputKeydownBind); |
||
2701 | |||
2702 | $popup = $compile(popupEl)($scope); |
||
2703 | // Prevent jQuery cache memory leak (template is now redundant after linking) |
||
2704 | popupEl.remove(); |
||
2705 | |||
2706 | if (appendToBody) { |
||
2707 | $document.find('body').append($popup); |
||
2708 | } else { |
||
2709 | $element.after($popup); |
||
2710 | } |
||
2711 | |||
2712 | $scope.$on('$destroy', function() { |
||
2713 | if ($scope.isOpen === true) { |
||
2714 | if (!$rootScope.$$phase) { |
||
2715 | $scope.$apply(function() { |
||
2716 | $scope.isOpen = false; |
||
2717 | }); |
||
2718 | } |
||
2719 | } |
||
2720 | |||
2721 | $popup.remove(); |
||
2722 | $element.off('keydown', inputKeydownBind); |
||
2723 | $document.off('click', documentClickBind); |
||
2724 | if (scrollParentEl) { |
||
2725 | scrollParentEl.off('scroll', positionPopup); |
||
2726 | } |
||
2727 | angular.element($window).off('resize', positionPopup); |
||
2728 | |||
2729 | //Clear all watch listeners on destroy |
||
2730 | while (watchListeners.length) { |
||
2731 | watchListeners.shift()(); |
||
2732 | } |
||
2733 | }); |
||
2734 | }; |
||
2735 | |||
2736 | $scope.getText = function(key) { |
||
2737 | return $scope[key + 'Text'] || datepickerPopupConfig[key + 'Text']; |
||
2738 | }; |
||
2739 | |||
2740 | $scope.isDisabled = function(date) { |
||
2741 | if (date === 'today') { |
||
2742 | date = dateParser.fromTimezone(new Date(), timezone); |
||
2743 | } |
||
2744 | |||
2745 | var dates = {}; |
||
2746 | angular.forEach(['minDate', 'maxDate'], function(key) { |
||
2747 | if (!$scope.datepickerOptions[key]) { |
||
2748 | dates[key] = null; |
||
2749 | } else if (angular.isDate($scope.datepickerOptions[key])) { |
||
2750 | dates[key] = dateParser.fromTimezone(new Date($scope.datepickerOptions[key]), timezone); |
||
2751 | } else { |
||
2752 | if ($datepickerPopupLiteralWarning) { |
||
2753 | $log.warn('Literal date support has been deprecated, please switch to date object usage'); |
||
2754 | } |
||
2755 | |||
2756 | dates[key] = new Date(dateFilter($scope.datepickerOptions[key], 'medium')); |
||
2757 | } |
||
2758 | }); |
||
2759 | |||
2760 | return $scope.datepickerOptions && |
||
2761 | dates.minDate && $scope.compare(date, dates.minDate) < 0 || |
||
2762 | dates.maxDate && $scope.compare(date, dates.maxDate) > 0; |
||
2763 | }; |
||
2764 | |||
2765 | $scope.compare = function(date1, date2) { |
||
2766 | return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); |
||
2767 | }; |
||
2768 | |||
2769 | // Inner change |
||
2770 | $scope.dateSelection = function(dt) { |
||
2771 | if (angular.isDefined(dt)) { |
||
2772 | $scope.date = dt; |
||
2773 | } |
||
2774 | var date = $scope.date ? dateParser.filter($scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function |
||
2775 | $element.val(date); |
||
2776 | ngModel.$setViewValue(date); |
||
2777 | |||
2778 | if (closeOnDateSelection) { |
||
2779 | $scope.isOpen = false; |
||
2780 | $element[0].focus(); |
||
2781 | } |
||
2782 | }; |
||
2783 | |||
2784 | $scope.keydown = function(evt) { |
||
2785 | if (evt.which === 27) { |
||
2786 | evt.stopPropagation(); |
||
2787 | $scope.isOpen = false; |
||
2788 | $element[0].focus(); |
||
2789 | } |
||
2790 | }; |
||
2791 | |||
2792 | $scope.select = function(date, evt) { |
||
2793 | evt.stopPropagation(); |
||
2794 | |||
2795 | if (date === 'today') { |
||
2796 | var today = new Date(); |
||
2797 | if (angular.isDate($scope.date)) { |
||
2798 | date = new Date($scope.date); |
||
2799 | date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); |
||
2800 | } else { |
||
2801 | date = new Date(today.setHours(0, 0, 0, 0)); |
||
2802 | } |
||
2803 | } |
||
2804 | $scope.dateSelection(date); |
||
2805 | }; |
||
2806 | |||
2807 | $scope.close = function(evt) { |
||
2808 | evt.stopPropagation(); |
||
2809 | |||
2810 | $scope.isOpen = false; |
||
2811 | $element[0].focus(); |
||
2812 | }; |
||
2813 | |||
2814 | $scope.disabled = angular.isDefined($attrs.disabled) || false; |
||
2815 | if ($attrs.ngDisabled) { |
||
2816 | watchListeners.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(disabled) { |
||
2817 | $scope.disabled = disabled; |
||
2818 | })); |
||
2819 | } |
||
2820 | |||
2821 | $scope.$watch('isOpen', function(value) { |
||
2822 | if (value) { |
||
2823 | if (!$scope.disabled) { |
||
2824 | $timeout(function() { |
||
2825 | positionPopup(); |
||
2826 | |||
2827 | if (onOpenFocus) { |
||
2828 | $scope.$broadcast('uib:datepicker.focus'); |
||
2829 | } |
||
2830 | |||
2831 | $document.on('click', documentClickBind); |
||
2832 | |||
2833 | var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; |
||
2834 | if (appendToBody || $position.parsePlacement(placement)[2]) { |
||
2835 | scrollParentEl = scrollParentEl || angular.element($position.scrollParent($element)); |
||
2836 | if (scrollParentEl) { |
||
2837 | scrollParentEl.on('scroll', positionPopup); |
||
2838 | } |
||
2839 | } else { |
||
2840 | scrollParentEl = null; |
||
2841 | } |
||
2842 | |||
2843 | angular.element($window).on('resize', positionPopup); |
||
2844 | }, 0, false); |
||
2845 | } else { |
||
2846 | $scope.isOpen = false; |
||
2847 | } |
||
2848 | } else { |
||
2849 | $document.off('click', documentClickBind); |
||
2850 | if (scrollParentEl) { |
||
2851 | scrollParentEl.off('scroll', positionPopup); |
||
2852 | } |
||
2853 | angular.element($window).off('resize', positionPopup); |
||
2854 | } |
||
2855 | }); |
||
2856 | |||
2857 | function cameltoDash(string) { |
||
0 ignored issues
–
show
|
|||
2858 | return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); }); |
||
2859 | } |
||
2860 | |||
2861 | function parseDateString(viewValue) { |
||
2862 | var date = dateParser.parse(viewValue, dateFormat, $scope.date); |
||
2863 | if (isNaN(date)) { |
||
2864 | for (var i = 0; i < altInputFormats.length; i++) { |
||
2865 | date = dateParser.parse(viewValue, altInputFormats[i], $scope.date); |
||
2866 | if (!isNaN(date)) { |
||
2867 | return date; |
||
2868 | } |
||
2869 | } |
||
2870 | } |
||
2871 | return date; |
||
2872 | } |
||
2873 | |||
2874 | function parseDate(viewValue) { |
||
2875 | if (angular.isNumber(viewValue)) { |
||
2876 | // presumably timestamp to date object |
||
2877 | viewValue = new Date(viewValue); |
||
2878 | } |
||
2879 | |||
2880 | if (!viewValue) { |
||
2881 | return null; |
||
2882 | } |
||
2883 | |||
2884 | if (angular.isDate(viewValue) && !isNaN(viewValue)) { |
||
2885 | return viewValue; |
||
2886 | } |
||
2887 | |||
2888 | if (angular.isString(viewValue)) { |
||
2889 | var date = parseDateString(viewValue); |
||
2890 | if (!isNaN(date)) { |
||
2891 | return dateParser.toTimezone(date, timezone); |
||
2892 | } |
||
2893 | } |
||
2894 | |||
2895 | return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined; |
||
2896 | } |
||
2897 | |||
2898 | function validator(modelValue, viewValue) { |
||
2899 | var value = modelValue || viewValue; |
||
2900 | |||
2901 | if (!$attrs.ngRequired && !value) { |
||
2902 | return true; |
||
2903 | } |
||
2904 | |||
2905 | if (angular.isNumber(value)) { |
||
2906 | value = new Date(value); |
||
2907 | } |
||
2908 | |||
2909 | if (!value) { |
||
2910 | return true; |
||
2911 | } |
||
2912 | |||
2913 | if (angular.isDate(value) && !isNaN(value)) { |
||
2914 | return true; |
||
2915 | } |
||
2916 | |||
2917 | if (angular.isString(value)) { |
||
2918 | return !isNaN(parseDateString(viewValue)); |
||
2919 | } |
||
2920 | |||
2921 | return false; |
||
2922 | } |
||
2923 | |||
2924 | function documentClickBind(event) { |
||
2925 | if (!$scope.isOpen && $scope.disabled) { |
||
2926 | return; |
||
2927 | } |
||
2928 | |||
2929 | var popup = $popup[0]; |
||
2930 | var dpContainsTarget = $element[0].contains(event.target); |
||
2931 | // The popup node may not be an element node |
||
2932 | // In some browsers (IE) only element nodes have the 'contains' function |
||
2933 | var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target); |
||
2934 | if ($scope.isOpen && !(dpContainsTarget || popupContainsTarget)) { |
||
2935 | $scope.$apply(function() { |
||
2936 | $scope.isOpen = false; |
||
2937 | }); |
||
2938 | } |
||
2939 | } |
||
2940 | |||
2941 | function inputKeydownBind(evt) { |
||
2942 | if (evt.which === 27 && $scope.isOpen) { |
||
2943 | evt.preventDefault(); |
||
2944 | evt.stopPropagation(); |
||
2945 | $scope.$apply(function() { |
||
2946 | $scope.isOpen = false; |
||
2947 | }); |
||
2948 | $element[0].focus(); |
||
2949 | } else if (evt.which === 40 && !$scope.isOpen) { |
||
2950 | evt.preventDefault(); |
||
2951 | evt.stopPropagation(); |
||
2952 | $scope.$apply(function() { |
||
2953 | $scope.isOpen = true; |
||
2954 | }); |
||
2955 | } |
||
2956 | } |
||
2957 | |||
2958 | function positionPopup() { |
||
2959 | if ($scope.isOpen) { |
||
2960 | var dpElement = angular.element($popup[0].querySelector('.uib-datepicker-popup')); |
||
2961 | var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; |
||
2962 | var position = $position.positionElements($element, dpElement, placement, appendToBody); |
||
2963 | dpElement.css({top: position.top + 'px', left: position.left + 'px'}); |
||
2964 | if (dpElement.hasClass('uib-position-measure')) { |
||
2965 | dpElement.removeClass('uib-position-measure'); |
||
2966 | } |
||
2967 | } |
||
2968 | } |
||
2969 | |||
2970 | $scope.$on('uib:datepicker.mode', function() { |
||
2971 | $timeout(positionPopup, 0, false); |
||
2972 | }); |
||
2973 | }]) |
||
2974 | |||
2975 | .directive('uibDatepickerPopup', function() { |
||
2976 | return { |
||
2977 | require: ['ngModel', 'uibDatepickerPopup'], |
||
2978 | controller: 'UibDatepickerPopupController', |
||
2979 | scope: { |
||
2980 | datepickerOptions: '=?', |
||
2981 | isOpen: '=?', |
||
2982 | currentText: '@', |
||
2983 | clearText: '@', |
||
2984 | closeText: '@' |
||
2985 | }, |
||
2986 | link: function(scope, element, attrs, ctrls) { |
||
2987 | var ngModel = ctrls[0], |
||
2988 | ctrl = ctrls[1]; |
||
2989 | |||
2990 | ctrl.init(ngModel); |
||
2991 | } |
||
2992 | }; |
||
2993 | }) |
||
2994 | |||
2995 | .directive('uibDatepickerPopupWrap', function() { |
||
2996 | return { |
||
2997 | replace: true, |
||
2998 | transclude: true, |
||
2999 | templateUrl: function(element, attrs) { |
||
3000 | return attrs.templateUrl || 'uib/template/datepickerPopup/popup.html'; |
||
3001 | } |
||
3002 | }; |
||
3003 | }); |
||
3004 | |||
3005 | angular.module('ui.bootstrap.debounce', []) |
||
3006 | /** |
||
3007 | * A helper, internal service that debounces a function |
||
3008 | */ |
||
3009 | .factory('$$debounce', ['$timeout', function($timeout) { |
||
3010 | return function(callback, debounceTime) { |
||
3011 | var timeoutPromise; |
||
3012 | |||
3013 | return function() { |
||
3014 | var self = this; |
||
3015 | var args = Array.prototype.slice.call(arguments); |
||
3016 | if (timeoutPromise) { |
||
3017 | $timeout.cancel(timeoutPromise); |
||
3018 | } |
||
3019 | |||
3020 | timeoutPromise = $timeout(function() { |
||
3021 | callback.apply(self, args); |
||
3022 | }, debounceTime); |
||
3023 | }; |
||
3024 | }; |
||
3025 | }]); |
||
3026 | |||
3027 | angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position']) |
||
3028 | |||
3029 | .constant('uibDropdownConfig', { |
||
3030 | appendToOpenClass: 'uib-dropdown-open', |
||
3031 | openClass: 'open' |
||
3032 | }) |
||
3033 | |||
3034 | View Code Duplication | .service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) { |
|
3035 | var openScope = null; |
||
3036 | |||
3037 | this.open = function(dropdownScope, element) { |
||
3038 | if (!openScope) { |
||
3039 | $document.on('click', closeDropdown); |
||
3040 | element.on('keydown', keybindFilter); |
||
3041 | } |
||
3042 | |||
3043 | if (openScope && openScope !== dropdownScope) { |
||
3044 | openScope.isOpen = false; |
||
3045 | } |
||
3046 | |||
3047 | openScope = dropdownScope; |
||
3048 | }; |
||
3049 | |||
3050 | this.close = function(dropdownScope, element) { |
||
3051 | if (openScope === dropdownScope) { |
||
3052 | openScope = null; |
||
3053 | $document.off('click', closeDropdown); |
||
3054 | element.off('keydown', keybindFilter); |
||
3055 | } |
||
3056 | }; |
||
3057 | |||
3058 | var closeDropdown = function(evt) { |
||
3059 | // This method may still be called during the same mouse event that |
||
3060 | // unbound this event handler. So check openScope before proceeding. |
||
3061 | if (!openScope) { return; } |
||
3062 | |||
3063 | if (evt && openScope.getAutoClose() === 'disabled') { return; } |
||
3064 | |||
3065 | if (evt && evt.which === 3) { return; } |
||
3066 | |||
3067 | var toggleElement = openScope.getToggleElement(); |
||
3068 | if (evt && toggleElement && toggleElement[0].contains(evt.target)) { |
||
3069 | return; |
||
3070 | } |
||
3071 | |||
3072 | var dropdownElement = openScope.getDropdownElement(); |
||
3073 | if (evt && openScope.getAutoClose() === 'outsideClick' && |
||
3074 | dropdownElement && dropdownElement[0].contains(evt.target)) { |
||
3075 | return; |
||
3076 | } |
||
3077 | |||
3078 | openScope.isOpen = false; |
||
3079 | |||
3080 | if (!$rootScope.$$phase) { |
||
3081 | openScope.$apply(); |
||
3082 | } |
||
3083 | }; |
||
3084 | |||
3085 | var keybindFilter = function(evt) { |
||
3086 | if (evt.which === 27) { |
||
3087 | evt.stopPropagation(); |
||
3088 | openScope.focusToggleElement(); |
||
3089 | closeDropdown(); |
||
3090 | } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) { |
||
3091 | evt.preventDefault(); |
||
3092 | evt.stopPropagation(); |
||
3093 | openScope.focusDropdownEntry(evt.which); |
||
3094 | } |
||
3095 | }; |
||
3096 | }]) |
||
3097 | |||
3098 | View Code Duplication | .controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) { |
|
3099 | var self = this, |
||
3100 | scope = $scope.$new(), // create a child scope so we are not polluting original one |
||
3101 | templateScope, |
||
3102 | appendToOpenClass = dropdownConfig.appendToOpenClass, |
||
3103 | openClass = dropdownConfig.openClass, |
||
3104 | getIsOpen, |
||
3105 | setIsOpen = angular.noop, |
||
3106 | toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, |
||
3107 | appendToBody = false, |
||
3108 | appendTo = null, |
||
3109 | keynavEnabled = false, |
||
3110 | selectedOption = null, |
||
3111 | body = $document.find('body'); |
||
3112 | |||
3113 | $element.addClass('dropdown'); |
||
3114 | |||
3115 | this.init = function() { |
||
3116 | if ($attrs.isOpen) { |
||
3117 | getIsOpen = $parse($attrs.isOpen); |
||
3118 | setIsOpen = getIsOpen.assign; |
||
3119 | |||
3120 | $scope.$watch(getIsOpen, function(value) { |
||
3121 | scope.isOpen = !!value; |
||
3122 | }); |
||
3123 | } |
||
3124 | |||
3125 | if (angular.isDefined($attrs.dropdownAppendTo)) { |
||
3126 | var appendToEl = $parse($attrs.dropdownAppendTo)(scope); |
||
3127 | if (appendToEl) { |
||
3128 | appendTo = angular.element(appendToEl); |
||
3129 | } |
||
3130 | } |
||
3131 | |||
3132 | appendToBody = angular.isDefined($attrs.dropdownAppendToBody); |
||
3133 | keynavEnabled = angular.isDefined($attrs.keyboardNav); |
||
3134 | |||
3135 | if (appendToBody && !appendTo) { |
||
3136 | appendTo = body; |
||
3137 | } |
||
3138 | |||
3139 | if (appendTo && self.dropdownMenu) { |
||
3140 | appendTo.append(self.dropdownMenu); |
||
3141 | $element.on('$destroy', function handleDestroyEvent() { |
||
3142 | self.dropdownMenu.remove(); |
||
3143 | }); |
||
3144 | } |
||
3145 | }; |
||
3146 | |||
3147 | this.toggle = function(open) { |
||
3148 | scope.isOpen = arguments.length ? !!open : !scope.isOpen; |
||
3149 | if (angular.isFunction(setIsOpen)) { |
||
3150 | setIsOpen(scope, scope.isOpen); |
||
3151 | } |
||
3152 | |||
3153 | return scope.isOpen; |
||
3154 | }; |
||
3155 | |||
3156 | // Allow other directives to watch status |
||
3157 | this.isOpen = function() { |
||
3158 | return scope.isOpen; |
||
3159 | }; |
||
3160 | |||
3161 | scope.getToggleElement = function() { |
||
3162 | return self.toggleElement; |
||
3163 | }; |
||
3164 | |||
3165 | scope.getAutoClose = function() { |
||
3166 | return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' |
||
3167 | }; |
||
3168 | |||
3169 | scope.getElement = function() { |
||
3170 | return $element; |
||
3171 | }; |
||
3172 | |||
3173 | scope.isKeynavEnabled = function() { |
||
3174 | return keynavEnabled; |
||
3175 | }; |
||
3176 | |||
3177 | scope.focusDropdownEntry = function(keyCode) { |
||
3178 | var elems = self.dropdownMenu ? //If append to body is used. |
||
3179 | angular.element(self.dropdownMenu).find('a') : |
||
3180 | $element.find('ul').eq(0).find('a'); |
||
3181 | |||
3182 | switch (keyCode) { |
||
3183 | case 40: { |
||
3184 | if (!angular.isNumber(self.selectedOption)) { |
||
3185 | self.selectedOption = 0; |
||
3186 | } else { |
||
3187 | self.selectedOption = self.selectedOption === elems.length - 1 ? |
||
3188 | self.selectedOption : |
||
3189 | self.selectedOption + 1; |
||
3190 | } |
||
3191 | break; |
||
3192 | } |
||
3193 | case 38: { |
||
3194 | if (!angular.isNumber(self.selectedOption)) { |
||
3195 | self.selectedOption = elems.length - 1; |
||
3196 | } else { |
||
3197 | self.selectedOption = self.selectedOption === 0 ? |
||
3198 | 0 : self.selectedOption - 1; |
||
3199 | } |
||
3200 | break; |
||
3201 | } |
||
3202 | } |
||
3203 | elems[self.selectedOption].focus(); |
||
3204 | }; |
||
3205 | |||
3206 | scope.getDropdownElement = function() { |
||
3207 | return self.dropdownMenu; |
||
3208 | }; |
||
3209 | |||
3210 | scope.focusToggleElement = function() { |
||
3211 | if (self.toggleElement) { |
||
3212 | self.toggleElement[0].focus(); |
||
3213 | } |
||
3214 | }; |
||
3215 | |||
3216 | scope.$watch('isOpen', function(isOpen, wasOpen) { |
||
3217 | if (appendTo && self.dropdownMenu) { |
||
3218 | var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true), |
||
3219 | css, |
||
3220 | rightalign; |
||
3221 | |||
3222 | css = { |
||
3223 | top: pos.top + 'px', |
||
3224 | display: isOpen ? 'block' : 'none' |
||
3225 | }; |
||
3226 | |||
3227 | rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); |
||
3228 | if (!rightalign) { |
||
3229 | css.left = pos.left + 'px'; |
||
3230 | css.right = 'auto'; |
||
3231 | } else { |
||
3232 | css.left = 'auto'; |
||
3233 | css.right = window.innerWidth - |
||
3234 | (pos.left + $element.prop('offsetWidth')) + 'px'; |
||
3235 | } |
||
3236 | |||
3237 | // Need to adjust our positioning to be relative to the appendTo container |
||
3238 | // if it's not the body element |
||
3239 | if (!appendToBody) { |
||
3240 | var appendOffset = $position.offset(appendTo); |
||
3241 | |||
3242 | css.top = pos.top - appendOffset.top + 'px'; |
||
3243 | |||
3244 | if (!rightalign) { |
||
3245 | css.left = pos.left - appendOffset.left + 'px'; |
||
3246 | } else { |
||
3247 | css.right = window.innerWidth - |
||
3248 | (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px'; |
||
3249 | } |
||
3250 | } |
||
3251 | |||
3252 | self.dropdownMenu.css(css); |
||
3253 | } |
||
3254 | |||
3255 | var openContainer = appendTo ? appendTo : $element; |
||
3256 | var hasOpenClass = openContainer.hasClass(appendTo ? appendToOpenClass : openClass); |
||
3257 | |||
3258 | if (hasOpenClass === !isOpen) { |
||
3259 | $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() { |
||
3260 | if (angular.isDefined(isOpen) && isOpen !== wasOpen) { |
||
3261 | toggleInvoker($scope, { open: !!isOpen }); |
||
3262 | } |
||
3263 | }); |
||
3264 | } |
||
3265 | |||
3266 | if (isOpen) { |
||
3267 | if (self.dropdownMenuTemplateUrl) { |
||
3268 | $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) { |
||
3269 | templateScope = scope.$new(); |
||
3270 | $compile(tplContent.trim())(templateScope, function(dropdownElement) { |
||
3271 | var newEl = dropdownElement; |
||
3272 | self.dropdownMenu.replaceWith(newEl); |
||
3273 | self.dropdownMenu = newEl; |
||
3274 | }); |
||
3275 | }); |
||
3276 | } |
||
3277 | |||
3278 | scope.focusToggleElement(); |
||
3279 | uibDropdownService.open(scope, $element); |
||
3280 | } else { |
||
3281 | if (self.dropdownMenuTemplateUrl) { |
||
3282 | if (templateScope) { |
||
3283 | templateScope.$destroy(); |
||
3284 | } |
||
3285 | var newEl = angular.element('<ul class="dropdown-menu"></ul>'); |
||
3286 | self.dropdownMenu.replaceWith(newEl); |
||
3287 | self.dropdownMenu = newEl; |
||
3288 | } |
||
3289 | |||
3290 | uibDropdownService.close(scope, $element); |
||
3291 | self.selectedOption = null; |
||
3292 | } |
||
3293 | |||
3294 | if (angular.isFunction(setIsOpen)) { |
||
3295 | setIsOpen($scope, isOpen); |
||
3296 | } |
||
3297 | }); |
||
3298 | }]) |
||
3299 | |||
3300 | .directive('uibDropdown', function() { |
||
3301 | return { |
||
3302 | controller: 'UibDropdownController', |
||
3303 | link: function(scope, element, attrs, dropdownCtrl) { |
||
3304 | dropdownCtrl.init(); |
||
3305 | } |
||
3306 | }; |
||
3307 | }) |
||
3308 | |||
3309 | View Code Duplication | .directive('uibDropdownMenu', function() { |
|
3310 | return { |
||
3311 | restrict: 'A', |
||
3312 | require: '?^uibDropdown', |
||
3313 | link: function(scope, element, attrs, dropdownCtrl) { |
||
3314 | if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) { |
||
3315 | return; |
||
3316 | } |
||
3317 | |||
3318 | element.addClass('dropdown-menu'); |
||
3319 | |||
3320 | var tplUrl = attrs.templateUrl; |
||
3321 | if (tplUrl) { |
||
3322 | dropdownCtrl.dropdownMenuTemplateUrl = tplUrl; |
||
3323 | } |
||
3324 | |||
3325 | if (!dropdownCtrl.dropdownMenu) { |
||
3326 | dropdownCtrl.dropdownMenu = element; |
||
3327 | } |
||
3328 | } |
||
3329 | }; |
||
3330 | }) |
||
3331 | |||
3332 | View Code Duplication | .directive('uibDropdownToggle', function() { |
|
3333 | return { |
||
3334 | require: '?^uibDropdown', |
||
3335 | link: function(scope, element, attrs, dropdownCtrl) { |
||
3336 | if (!dropdownCtrl) { |
||
3337 | return; |
||
3338 | } |
||
3339 | |||
3340 | element.addClass('dropdown-toggle'); |
||
3341 | |||
3342 | dropdownCtrl.toggleElement = element; |
||
3343 | |||
3344 | var toggleDropdown = function(event) { |
||
3345 | event.preventDefault(); |
||
3346 | |||
3347 | if (!element.hasClass('disabled') && !attrs.disabled) { |
||
3348 | scope.$apply(function() { |
||
3349 | dropdownCtrl.toggle(); |
||
3350 | }); |
||
3351 | } |
||
3352 | }; |
||
3353 | |||
3354 | element.bind('click', toggleDropdown); |
||
3355 | |||
3356 | // WAI-ARIA |
||
3357 | element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); |
||
3358 | scope.$watch(dropdownCtrl.isOpen, function(isOpen) { |
||
3359 | element.attr('aria-expanded', !!isOpen); |
||
3360 | }); |
||
3361 | |||
3362 | scope.$on('$destroy', function() { |
||
3363 | element.unbind('click', toggleDropdown); |
||
3364 | }); |
||
3365 | } |
||
3366 | }; |
||
3367 | }); |
||
3368 | |||
3369 | angular.module('ui.bootstrap.stackedMap', []) |
||
3370 | /** |
||
3371 | * A helper, internal data structure that acts as a map but also allows getting / removing |
||
3372 | * elements in the LIFO order |
||
3373 | */ |
||
3374 | View Code Duplication | .factory('$$stackedMap', function() { |
|
3375 | return { |
||
3376 | createNew: function() { |
||
3377 | var stack = []; |
||
3378 | |||
3379 | return { |
||
3380 | add: function(key, value) { |
||
3381 | stack.push({ |
||
3382 | key: key, |
||
3383 | value: value |
||
3384 | }); |
||
3385 | }, |
||
3386 | get: function(key) { |
||
3387 | for (var i = 0; i < stack.length; i++) { |
||
3388 | if (key === stack[i].key) { |
||
3389 | return stack[i]; |
||
3390 | } |
||
3391 | } |
||
3392 | }, |
||
3393 | keys: function() { |
||
3394 | var keys = []; |
||
3395 | for (var i = 0; i < stack.length; i++) { |
||
3396 | keys.push(stack[i].key); |
||
3397 | } |
||
3398 | return keys; |
||
3399 | }, |
||
3400 | top: function() { |
||
3401 | return stack[stack.length - 1]; |
||
3402 | }, |
||
3403 | remove: function(key) { |
||
3404 | var idx = -1; |
||
3405 | for (var i = 0; i < stack.length; i++) { |
||
3406 | if (key === stack[i].key) { |
||
3407 | idx = i; |
||
3408 | break; |
||
3409 | } |
||
3410 | } |
||
3411 | return stack.splice(idx, 1)[0]; |
||
3412 | }, |
||
3413 | removeTop: function() { |
||
3414 | return stack.splice(stack.length - 1, 1)[0]; |
||
3415 | }, |
||
3416 | length: function() { |
||
3417 | return stack.length; |
||
3418 | } |
||
3419 | }; |
||
3420 | } |
||
3421 | }; |
||
3422 | }); |
||
3423 | angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.position']) |
||
3424 | /** |
||
3425 | * A helper, internal data structure that stores all references attached to key |
||
3426 | */ |
||
3427 | View Code Duplication | .factory('$$multiMap', function() { |
|
3428 | return { |
||
3429 | createNew: function() { |
||
3430 | var map = {}; |
||
3431 | |||
3432 | return { |
||
3433 | entries: function() { |
||
3434 | return Object.keys(map).map(function(key) { |
||
3435 | return { |
||
3436 | key: key, |
||
3437 | value: map[key] |
||
3438 | }; |
||
3439 | }); |
||
3440 | }, |
||
3441 | get: function(key) { |
||
3442 | return map[key]; |
||
3443 | }, |
||
3444 | hasKey: function(key) { |
||
3445 | return !!map[key]; |
||
3446 | }, |
||
3447 | keys: function() { |
||
3448 | return Object.keys(map); |
||
3449 | }, |
||
3450 | put: function(key, value) { |
||
3451 | if (!map[key]) { |
||
3452 | map[key] = []; |
||
3453 | } |
||
3454 | |||
3455 | map[key].push(value); |
||
3456 | }, |
||
3457 | remove: function(key, value) { |
||
3458 | var values = map[key]; |
||
3459 | |||
3460 | if (!values) { |
||
3461 | return; |
||
3462 | } |
||
3463 | |||
3464 | var idx = values.indexOf(value); |
||
3465 | |||
3466 | if (idx !== -1) { |
||
3467 | values.splice(idx, 1); |
||
3468 | } |
||
3469 | |||
3470 | if (!values.length) { |
||
3471 | delete map[key]; |
||
3472 | } |
||
3473 | } |
||
3474 | }; |
||
3475 | } |
||
3476 | }; |
||
3477 | }) |
||
3478 | |||
3479 | /** |
||
3480 | * Pluggable resolve mechanism for the modal resolve resolution |
||
3481 | * Supports UI Router's $resolve service |
||
3482 | */ |
||
3483 | View Code Duplication | .provider('$uibResolve', function() { |
|
3484 | var resolve = this; |
||
3485 | this.resolver = null; |
||
3486 | |||
3487 | this.setResolver = function(resolver) { |
||
3488 | this.resolver = resolver; |
||
3489 | }; |
||
3490 | |||
3491 | this.$get = ['$injector', '$q', function($injector, $q) { |
||
3492 | var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null; |
||
3493 | return { |
||
3494 | resolve: function(invocables, locals, parent, self) { |
||
3495 | if (resolver) { |
||
3496 | return resolver.resolve(invocables, locals, parent, self); |
||
3497 | } |
||
3498 | |||
3499 | var promises = []; |
||
3500 | |||
3501 | angular.forEach(invocables, function(value) { |
||
3502 | if (angular.isFunction(value) || angular.isArray(value)) { |
||
3503 | promises.push($q.resolve($injector.invoke(value))); |
||
3504 | } else if (angular.isString(value)) { |
||
3505 | promises.push($q.resolve($injector.get(value))); |
||
3506 | } else { |
||
3507 | promises.push($q.resolve(value)); |
||
3508 | } |
||
3509 | }); |
||
3510 | |||
3511 | return $q.all(promises).then(function(resolves) { |
||
3512 | var resolveObj = {}; |
||
3513 | var resolveIter = 0; |
||
3514 | angular.forEach(invocables, function(value, key) { |
||
3515 | resolveObj[key] = resolves[resolveIter++]; |
||
3516 | }); |
||
3517 | |||
3518 | return resolveObj; |
||
3519 | }); |
||
3520 | } |
||
3521 | }; |
||
3522 | }]; |
||
3523 | }) |
||
3524 | |||
3525 | /** |
||
3526 | * A helper directive for the $modal service. It creates a backdrop element. |
||
3527 | */ |
||
3528 | .directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack', |
||
3529 | function($animate, $injector, $modalStack) { |
||
3530 | return { |
||
3531 | replace: true, |
||
3532 | templateUrl: 'uib/template/modal/backdrop.html', |
||
3533 | compile: function(tElement, tAttrs) { |
||
3534 | tElement.addClass(tAttrs.backdropClass); |
||
3535 | return linkFn; |
||
3536 | } |
||
3537 | }; |
||
3538 | |||
3539 | function linkFn(scope, element, attrs) { |
||
3540 | if (attrs.modalInClass) { |
||
3541 | $animate.addClass(element, attrs.modalInClass); |
||
3542 | |||
3543 | scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { |
||
3544 | var done = setIsAsync(); |
||
3545 | if (scope.modalOptions.animation) { |
||
3546 | $animate.removeClass(element, attrs.modalInClass).then(done); |
||
3547 | } else { |
||
3548 | done(); |
||
3549 | } |
||
3550 | }); |
||
3551 | } |
||
3552 | } |
||
3553 | }]) |
||
3554 | |||
3555 | .directive('uibModalWindow', ['$uibModalStack', '$q', '$animateCss', '$document', |
||
3556 | View Code Duplication | function($modalStack, $q, $animateCss, $document) { |
|
3557 | return { |
||
3558 | scope: { |
||
3559 | index: '@' |
||
3560 | }, |
||
3561 | replace: true, |
||
3562 | transclude: true, |
||
3563 | templateUrl: function(tElement, tAttrs) { |
||
3564 | return tAttrs.templateUrl || 'uib/template/modal/window.html'; |
||
3565 | }, |
||
3566 | link: function(scope, element, attrs) { |
||
3567 | element.addClass(attrs.windowClass || ''); |
||
3568 | element.addClass(attrs.windowTopClass || ''); |
||
3569 | scope.size = attrs.size; |
||
3570 | |||
3571 | scope.close = function(evt) { |
||
3572 | var modal = $modalStack.getTop(); |
||
3573 | if (modal && modal.value.backdrop && |
||
3574 | modal.value.backdrop !== 'static' && |
||
3575 | evt.target === evt.currentTarget) { |
||
3576 | evt.preventDefault(); |
||
3577 | evt.stopPropagation(); |
||
3578 | $modalStack.dismiss(modal.key, 'backdrop click'); |
||
3579 | } |
||
3580 | }; |
||
3581 | |||
3582 | // moved from template to fix issue #2280 |
||
3583 | element.on('click', scope.close); |
||
3584 | |||
3585 | // This property is only added to the scope for the purpose of detecting when this directive is rendered. |
||
3586 | // We can detect that by using this property in the template associated with this directive and then use |
||
3587 | // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. |
||
3588 | scope.$isRendered = true; |
||
3589 | |||
3590 | // Deferred object that will be resolved when this modal is render. |
||
3591 | var modalRenderDeferObj = $q.defer(); |
||
3592 | // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. |
||
3593 | // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. |
||
3594 | attrs.$observe('modalRender', function(value) { |
||
3595 | if (value === 'true') { |
||
3596 | modalRenderDeferObj.resolve(); |
||
3597 | } |
||
3598 | }); |
||
3599 | |||
3600 | modalRenderDeferObj.promise.then(function() { |
||
3601 | var animationPromise = null; |
||
3602 | |||
3603 | if (attrs.modalInClass) { |
||
3604 | animationPromise = $animateCss(element, { |
||
3605 | addClass: attrs.modalInClass |
||
3606 | }).start(); |
||
3607 | |||
3608 | scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { |
||
3609 | var done = setIsAsync(); |
||
3610 | $animateCss(element, { |
||
3611 | removeClass: attrs.modalInClass |
||
3612 | }).start().then(done); |
||
3613 | }); |
||
3614 | } |
||
3615 | |||
3616 | |||
3617 | $q.when(animationPromise).then(function() { |
||
3618 | // Notify {@link $modalStack} that modal is rendered. |
||
3619 | var modal = $modalStack.getTop(); |
||
3620 | if (modal) { |
||
3621 | $modalStack.modalRendered(modal.key); |
||
3622 | } |
||
3623 | |||
3624 | /** |
||
3625 | * If something within the freshly-opened modal already has focus (perhaps via a |
||
3626 | * directive that causes focus). then no need to try and focus anything. |
||
3627 | */ |
||
3628 | if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) { |
||
3629 | var inputWithAutofocus = element[0].querySelector('[autofocus]'); |
||
3630 | /** |
||
3631 | * Auto-focusing of a freshly-opened modal element causes any child elements |
||
3632 | * with the autofocus attribute to lose focus. This is an issue on touch |
||
3633 | * based devices which will show and then hide the onscreen keyboard. |
||
3634 | * Attempts to refocus the autofocus element via JavaScript will not reopen |
||
3635 | * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus |
||
3636 | * the modal element if the modal does not contain an autofocus element. |
||
3637 | */ |
||
3638 | if (inputWithAutofocus) { |
||
3639 | inputWithAutofocus.focus(); |
||
3640 | } else { |
||
3641 | element[0].focus(); |
||
3642 | } |
||
3643 | } |
||
3644 | }); |
||
3645 | }); |
||
3646 | } |
||
3647 | }; |
||
3648 | }]) |
||
3649 | |||
3650 | .directive('uibModalAnimationClass', function() { |
||
3651 | return { |
||
3652 | compile: function(tElement, tAttrs) { |
||
3653 | if (tAttrs.modalAnimation) { |
||
3654 | tElement.addClass(tAttrs.uibModalAnimationClass); |
||
3655 | } |
||
3656 | } |
||
3657 | }; |
||
3658 | }) |
||
3659 | |||
3660 | .directive('uibModalTransclude', function() { |
||
3661 | return { |
||
3662 | link: function(scope, element, attrs, controller, transclude) { |
||
3663 | transclude(scope.$parent, function(clone) { |
||
3664 | element.empty(); |
||
3665 | element.append(clone); |
||
3666 | }); |
||
3667 | } |
||
3668 | }; |
||
3669 | }) |
||
3670 | |||
3671 | .factory('$uibModalStack', ['$animate', '$animateCss', '$document', |
||
3672 | '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition', |
||
3673 | View Code Duplication | function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap, $uibPosition) { |
|
3674 | var OPENED_MODAL_CLASS = 'modal-open'; |
||
3675 | |||
3676 | var backdropDomEl, backdropScope; |
||
3677 | var openedWindows = $$stackedMap.createNew(); |
||
3678 | var openedClasses = $$multiMap.createNew(); |
||
3679 | var $modalStack = { |
||
3680 | NOW_CLOSING_EVENT: 'modal.stack.now-closing' |
||
3681 | }; |
||
3682 | var topModalIndex = 0; |
||
3683 | var previousTopOpenedModal = null; |
||
3684 | |||
3685 | //Modal focus behavior |
||
3686 | var tabableSelector = 'a[href], area[href], input:not([disabled]), ' + |
||
3687 | 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' + |
||
3688 | 'iframe, object, embed, *[tabindex], *[contenteditable=true]'; |
||
3689 | var scrollbarPadding; |
||
3690 | |||
3691 | function isVisible(element) { |
||
3692 | return !!(element.offsetWidth || |
||
3693 | element.offsetHeight || |
||
3694 | element.getClientRects().length); |
||
3695 | } |
||
3696 | |||
3697 | function backdropIndex() { |
||
3698 | var topBackdropIndex = -1; |
||
3699 | var opened = openedWindows.keys(); |
||
3700 | for (var i = 0; i < opened.length; i++) { |
||
3701 | if (openedWindows.get(opened[i]).value.backdrop) { |
||
3702 | topBackdropIndex = i; |
||
3703 | } |
||
3704 | } |
||
3705 | |||
3706 | // If any backdrop exist, ensure that it's index is always |
||
3707 | // right below the top modal |
||
3708 | if (topBackdropIndex > -1 && topBackdropIndex < topModalIndex) { |
||
3709 | topBackdropIndex = topModalIndex; |
||
3710 | } |
||
3711 | return topBackdropIndex; |
||
3712 | } |
||
3713 | |||
3714 | $rootScope.$watch(backdropIndex, function(newBackdropIndex) { |
||
3715 | if (backdropScope) { |
||
3716 | backdropScope.index = newBackdropIndex; |
||
3717 | } |
||
3718 | }); |
||
3719 | |||
3720 | function removeModalWindow(modalInstance, elementToReceiveFocus) { |
||
3721 | var modalWindow = openedWindows.get(modalInstance).value; |
||
3722 | var appendToElement = modalWindow.appendTo; |
||
3723 | |||
3724 | //clean up the stack |
||
3725 | openedWindows.remove(modalInstance); |
||
3726 | previousTopOpenedModal = openedWindows.top(); |
||
3727 | if (previousTopOpenedModal) { |
||
3728 | topModalIndex = parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10); |
||
3729 | } |
||
3730 | |||
3731 | removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { |
||
3732 | var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; |
||
3733 | openedClasses.remove(modalBodyClass, modalInstance); |
||
3734 | var areAnyOpen = openedClasses.hasKey(modalBodyClass); |
||
3735 | appendToElement.toggleClass(modalBodyClass, areAnyOpen); |
||
3736 | if (!areAnyOpen && scrollbarPadding && scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { |
||
3737 | if (scrollbarPadding.originalRight) { |
||
3738 | appendToElement.css({paddingRight: scrollbarPadding.originalRight + 'px'}); |
||
3739 | } else { |
||
3740 | appendToElement.css({paddingRight: ''}); |
||
3741 | } |
||
3742 | scrollbarPadding = null; |
||
3743 | } |
||
3744 | toggleTopWindowClass(true); |
||
3745 | }, modalWindow.closedDeferred); |
||
3746 | checkRemoveBackdrop(); |
||
3747 | |||
3748 | //move focus to specified element if available, or else to body |
||
3749 | if (elementToReceiveFocus && elementToReceiveFocus.focus) { |
||
3750 | elementToReceiveFocus.focus(); |
||
3751 | } else if (appendToElement.focus) { |
||
3752 | appendToElement.focus(); |
||
3753 | } |
||
3754 | } |
||
3755 | |||
3756 | // Add or remove "windowTopClass" from the top window in the stack |
||
3757 | function toggleTopWindowClass(toggleSwitch) { |
||
3758 | var modalWindow; |
||
3759 | |||
3760 | if (openedWindows.length() > 0) { |
||
3761 | modalWindow = openedWindows.top().value; |
||
3762 | modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch); |
||
3763 | } |
||
3764 | } |
||
3765 | |||
3766 | function checkRemoveBackdrop() { |
||
3767 | //remove backdrop if no longer needed |
||
3768 | if (backdropDomEl && backdropIndex() === -1) { |
||
3769 | var backdropScopeRef = backdropScope; |
||
0 ignored issues
–
show
|
|||
3770 | removeAfterAnimate(backdropDomEl, backdropScope, function() { |
||
3771 | backdropScopeRef = null; |
||
0 ignored issues
–
show
|
|||
3772 | }); |
||
3773 | backdropDomEl = undefined; |
||
3774 | backdropScope = undefined; |
||
3775 | } |
||
3776 | } |
||
3777 | |||
3778 | function removeAfterAnimate(domEl, scope, done, closedDeferred) { |
||
3779 | var asyncDeferred; |
||
3780 | var asyncPromise = null; |
||
3781 | var setIsAsync = function() { |
||
3782 | if (!asyncDeferred) { |
||
3783 | asyncDeferred = $q.defer(); |
||
3784 | asyncPromise = asyncDeferred.promise; |
||
3785 | } |
||
3786 | |||
3787 | return function asyncDone() { |
||
3788 | asyncDeferred.resolve(); |
||
3789 | }; |
||
3790 | }; |
||
3791 | scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync); |
||
3792 | |||
3793 | // Note that it's intentional that asyncPromise might be null. |
||
3794 | // That's when setIsAsync has not been called during the |
||
3795 | // NOW_CLOSING_EVENT broadcast. |
||
3796 | return $q.when(asyncPromise).then(afterAnimating); |
||
3797 | |||
3798 | function afterAnimating() { |
||
3799 | if (afterAnimating.done) { |
||
3800 | return; |
||
3801 | } |
||
3802 | afterAnimating.done = true; |
||
3803 | |||
3804 | $animate.leave(domEl).then(function() { |
||
3805 | domEl.remove(); |
||
3806 | if (closedDeferred) { |
||
3807 | closedDeferred.resolve(); |
||
3808 | } |
||
3809 | }); |
||
3810 | |||
3811 | scope.$destroy(); |
||
3812 | if (done) { |
||
3813 | done(); |
||
3814 | } |
||
3815 | } |
||
3816 | } |
||
3817 | |||
3818 | $document.on('keydown', keydownListener); |
||
3819 | |||
3820 | $rootScope.$on('$destroy', function() { |
||
3821 | $document.off('keydown', keydownListener); |
||
3822 | }); |
||
3823 | |||
3824 | function keydownListener(evt) { |
||
3825 | if (evt.isDefaultPrevented()) { |
||
3826 | return evt; |
||
3827 | } |
||
3828 | |||
3829 | var modal = openedWindows.top(); |
||
3830 | if (modal) { |
||
3831 | switch (evt.which) { |
||
3832 | case 27: { |
||
3833 | if (modal.value.keyboard) { |
||
3834 | evt.preventDefault(); |
||
3835 | $rootScope.$apply(function() { |
||
3836 | $modalStack.dismiss(modal.key, 'escape key press'); |
||
3837 | }); |
||
3838 | } |
||
3839 | break; |
||
3840 | } |
||
3841 | case 9: { |
||
3842 | var list = $modalStack.loadFocusElementList(modal); |
||
3843 | var focusChanged = false; |
||
3844 | if (evt.shiftKey) { |
||
3845 | if ($modalStack.isFocusInFirstItem(evt, list) || $modalStack.isModalFocused(evt, modal)) { |
||
3846 | focusChanged = $modalStack.focusLastFocusableElement(list); |
||
3847 | } |
||
3848 | } else { |
||
3849 | if ($modalStack.isFocusInLastItem(evt, list)) { |
||
3850 | focusChanged = $modalStack.focusFirstFocusableElement(list); |
||
3851 | } |
||
3852 | } |
||
3853 | |||
3854 | if (focusChanged) { |
||
3855 | evt.preventDefault(); |
||
3856 | evt.stopPropagation(); |
||
3857 | } |
||
3858 | |||
3859 | break; |
||
3860 | } |
||
3861 | } |
||
0 ignored issues
–
show
|
|||
3862 | } |
||
3863 | } |
||
3864 | |||
3865 | $modalStack.open = function(modalInstance, modal) { |
||
3866 | var modalOpener = $document[0].activeElement, |
||
3867 | modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS; |
||
3868 | |||
3869 | toggleTopWindowClass(false); |
||
3870 | |||
3871 | // Store the current top first, to determine what index we ought to use |
||
3872 | // for the current top modal |
||
3873 | previousTopOpenedModal = openedWindows.top(); |
||
3874 | |||
3875 | openedWindows.add(modalInstance, { |
||
3876 | deferred: modal.deferred, |
||
3877 | renderDeferred: modal.renderDeferred, |
||
3878 | closedDeferred: modal.closedDeferred, |
||
3879 | modalScope: modal.scope, |
||
3880 | backdrop: modal.backdrop, |
||
3881 | keyboard: modal.keyboard, |
||
3882 | openedClass: modal.openedClass, |
||
3883 | windowTopClass: modal.windowTopClass, |
||
3884 | animation: modal.animation, |
||
3885 | appendTo: modal.appendTo |
||
3886 | }); |
||
3887 | |||
3888 | openedClasses.put(modalBodyClass, modalInstance); |
||
3889 | |||
3890 | var appendToElement = modal.appendTo, |
||
3891 | currBackdropIndex = backdropIndex(); |
||
3892 | |||
3893 | if (!appendToElement.length) { |
||
3894 | throw new Error('appendTo element not found. Make sure that the element passed is in DOM.'); |
||
3895 | } |
||
3896 | |||
3897 | if (currBackdropIndex >= 0 && !backdropDomEl) { |
||
3898 | backdropScope = $rootScope.$new(true); |
||
3899 | backdropScope.modalOptions = modal; |
||
3900 | backdropScope.index = currBackdropIndex; |
||
3901 | backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>'); |
||
3902 | backdropDomEl.attr('backdrop-class', modal.backdropClass); |
||
3903 | if (modal.animation) { |
||
3904 | backdropDomEl.attr('modal-animation', 'true'); |
||
3905 | } |
||
3906 | $compile(backdropDomEl)(backdropScope); |
||
3907 | $animate.enter(backdropDomEl, appendToElement); |
||
3908 | scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement); |
||
3909 | if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { |
||
3910 | appendToElement.css({paddingRight: scrollbarPadding.right + 'px'}); |
||
3911 | } |
||
3912 | } |
||
3913 | |||
3914 | // Set the top modal index based on the index of the previous top modal |
||
3915 | topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0; |
||
3916 | var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>'); |
||
3917 | angularDomEl.attr({ |
||
3918 | 'template-url': modal.windowTemplateUrl, |
||
3919 | 'window-class': modal.windowClass, |
||
3920 | 'window-top-class': modal.windowTopClass, |
||
3921 | 'size': modal.size, |
||
3922 | 'index': topModalIndex, |
||
3923 | 'animate': 'animate' |
||
3924 | }).html(modal.content); |
||
3925 | if (modal.animation) { |
||
3926 | angularDomEl.attr('modal-animation', 'true'); |
||
3927 | } |
||
3928 | |||
3929 | appendToElement.addClass(modalBodyClass); |
||
3930 | $animate.enter($compile(angularDomEl)(modal.scope), appendToElement); |
||
3931 | |||
3932 | openedWindows.top().value.modalDomEl = angularDomEl; |
||
3933 | openedWindows.top().value.modalOpener = modalOpener; |
||
3934 | }; |
||
3935 | |||
3936 | function broadcastClosing(modalWindow, resultOrReason, closing) { |
||
3937 | return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented; |
||
3938 | } |
||
3939 | |||
3940 | $modalStack.close = function(modalInstance, result) { |
||
3941 | var modalWindow = openedWindows.get(modalInstance); |
||
3942 | if (modalWindow && broadcastClosing(modalWindow, result, true)) { |
||
3943 | modalWindow.value.modalScope.$$uibDestructionScheduled = true; |
||
3944 | modalWindow.value.deferred.resolve(result); |
||
3945 | removeModalWindow(modalInstance, modalWindow.value.modalOpener); |
||
3946 | return true; |
||
3947 | } |
||
3948 | return !modalWindow; |
||
3949 | }; |
||
3950 | |||
3951 | $modalStack.dismiss = function(modalInstance, reason) { |
||
3952 | var modalWindow = openedWindows.get(modalInstance); |
||
3953 | if (modalWindow && broadcastClosing(modalWindow, reason, false)) { |
||
3954 | modalWindow.value.modalScope.$$uibDestructionScheduled = true; |
||
3955 | modalWindow.value.deferred.reject(reason); |
||
3956 | removeModalWindow(modalInstance, modalWindow.value.modalOpener); |
||
3957 | return true; |
||
3958 | } |
||
3959 | return !modalWindow; |
||
3960 | }; |
||
3961 | |||
3962 | $modalStack.dismissAll = function(reason) { |
||
3963 | var topModal = this.getTop(); |
||
3964 | while (topModal && this.dismiss(topModal.key, reason)) { |
||
3965 | topModal = this.getTop(); |
||
3966 | } |
||
3967 | }; |
||
3968 | |||
3969 | $modalStack.getTop = function() { |
||
3970 | return openedWindows.top(); |
||
3971 | }; |
||
3972 | |||
3973 | $modalStack.modalRendered = function(modalInstance) { |
||
3974 | var modalWindow = openedWindows.get(modalInstance); |
||
3975 | if (modalWindow) { |
||
3976 | modalWindow.value.renderDeferred.resolve(); |
||
3977 | } |
||
3978 | }; |
||
3979 | |||
3980 | $modalStack.focusFirstFocusableElement = function(list) { |
||
3981 | if (list.length > 0) { |
||
3982 | list[0].focus(); |
||
3983 | return true; |
||
3984 | } |
||
3985 | return false; |
||
3986 | }; |
||
3987 | |||
3988 | $modalStack.focusLastFocusableElement = function(list) { |
||
3989 | if (list.length > 0) { |
||
3990 | list[list.length - 1].focus(); |
||
3991 | return true; |
||
3992 | } |
||
3993 | return false; |
||
3994 | }; |
||
3995 | |||
3996 | $modalStack.isModalFocused = function(evt, modalWindow) { |
||
3997 | if (evt && modalWindow) { |
||
3998 | var modalDomEl = modalWindow.value.modalDomEl; |
||
3999 | if (modalDomEl && modalDomEl.length) { |
||
4000 | return (evt.target || evt.srcElement) === modalDomEl[0]; |
||
4001 | } |
||
4002 | } |
||
4003 | return false; |
||
4004 | }; |
||
4005 | |||
4006 | $modalStack.isFocusInFirstItem = function(evt, list) { |
||
4007 | if (list.length > 0) { |
||
4008 | return (evt.target || evt.srcElement) === list[0]; |
||
4009 | } |
||
4010 | return false; |
||
4011 | }; |
||
4012 | |||
4013 | $modalStack.isFocusInLastItem = function(evt, list) { |
||
4014 | if (list.length > 0) { |
||
4015 | return (evt.target || evt.srcElement) === list[list.length - 1]; |
||
4016 | } |
||
4017 | return false; |
||
4018 | }; |
||
4019 | |||
4020 | $modalStack.loadFocusElementList = function(modalWindow) { |
||
4021 | if (modalWindow) { |
||
4022 | var modalDomE1 = modalWindow.value.modalDomEl; |
||
4023 | if (modalDomE1 && modalDomE1.length) { |
||
4024 | var elements = modalDomE1[0].querySelectorAll(tabableSelector); |
||
4025 | return elements ? |
||
4026 | Array.prototype.filter.call(elements, function(element) { |
||
4027 | return isVisible(element); |
||
4028 | }) : elements; |
||
4029 | } |
||
4030 | } |
||
4031 | }; |
||
4032 | |||
4033 | return $modalStack; |
||
4034 | }]) |
||
4035 | |||
4036 | View Code Duplication | .provider('$uibModal', function() { |
|
4037 | var $modalProvider = { |
||
4038 | options: { |
||
4039 | animation: true, |
||
4040 | backdrop: true, //can also be false or 'static' |
||
4041 | keyboard: true |
||
4042 | }, |
||
4043 | $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack', |
||
4044 | function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) { |
||
4045 | var $modal = {}; |
||
4046 | |||
4047 | function getTemplatePromise(options) { |
||
4048 | return options.template ? $q.when(options.template) : |
||
4049 | $templateRequest(angular.isFunction(options.templateUrl) ? |
||
4050 | options.templateUrl() : options.templateUrl); |
||
4051 | } |
||
4052 | |||
4053 | var promiseChain = null; |
||
4054 | $modal.getPromiseChain = function() { |
||
4055 | return promiseChain; |
||
4056 | }; |
||
4057 | |||
4058 | $modal.open = function(modalOptions) { |
||
4059 | var modalResultDeferred = $q.defer(); |
||
4060 | var modalOpenedDeferred = $q.defer(); |
||
4061 | var modalClosedDeferred = $q.defer(); |
||
4062 | var modalRenderDeferred = $q.defer(); |
||
4063 | |||
4064 | //prepare an instance of a modal to be injected into controllers and returned to a caller |
||
4065 | var modalInstance = { |
||
4066 | result: modalResultDeferred.promise, |
||
4067 | opened: modalOpenedDeferred.promise, |
||
4068 | closed: modalClosedDeferred.promise, |
||
4069 | rendered: modalRenderDeferred.promise, |
||
4070 | close: function (result) { |
||
4071 | return $modalStack.close(modalInstance, result); |
||
4072 | }, |
||
4073 | dismiss: function (reason) { |
||
4074 | return $modalStack.dismiss(modalInstance, reason); |
||
4075 | } |
||
4076 | }; |
||
4077 | |||
4078 | //merge and clean up options |
||
4079 | modalOptions = angular.extend({}, $modalProvider.options, modalOptions); |
||
4080 | modalOptions.resolve = modalOptions.resolve || {}; |
||
4081 | modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0); |
||
4082 | |||
4083 | //verify options |
||
4084 | if (!modalOptions.template && !modalOptions.templateUrl) { |
||
4085 | throw new Error('One of template or templateUrl options is required.'); |
||
4086 | } |
||
4087 | |||
4088 | var templateAndResolvePromise = |
||
4089 | $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]); |
||
4090 | |||
4091 | function resolveWithTemplate() { |
||
4092 | return templateAndResolvePromise; |
||
4093 | } |
||
4094 | |||
4095 | // Wait for the resolution of the existing promise chain. |
||
4096 | // Then switch to our own combined promise dependency (regardless of how the previous modal fared). |
||
4097 | // Then add to $modalStack and resolve opened. |
||
4098 | // Finally clean up the chain variable if no subsequent modal has overwritten it. |
||
4099 | var samePromise; |
||
4100 | samePromise = promiseChain = $q.all([promiseChain]) |
||
4101 | .then(resolveWithTemplate, resolveWithTemplate) |
||
4102 | .then(function resolveSuccess(tplAndVars) { |
||
4103 | var providedScope = modalOptions.scope || $rootScope; |
||
4104 | |||
4105 | var modalScope = providedScope.$new(); |
||
4106 | modalScope.$close = modalInstance.close; |
||
4107 | modalScope.$dismiss = modalInstance.dismiss; |
||
4108 | |||
4109 | modalScope.$on('$destroy', function() { |
||
4110 | if (!modalScope.$$uibDestructionScheduled) { |
||
4111 | modalScope.$dismiss('$uibUnscheduledDestruction'); |
||
4112 | } |
||
4113 | }); |
||
4114 | |||
4115 | var ctrlInstance, ctrlInstantiate, ctrlLocals = {}; |
||
4116 | |||
4117 | //controllers |
||
4118 | if (modalOptions.controller) { |
||
4119 | ctrlLocals.$scope = modalScope; |
||
4120 | ctrlLocals.$uibModalInstance = modalInstance; |
||
4121 | angular.forEach(tplAndVars[1], function(value, key) { |
||
4122 | ctrlLocals[key] = value; |
||
4123 | }); |
||
4124 | |||
4125 | // the third param will make the controller instantiate later,private api |
||
4126 | // @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126 |
||
4127 | ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true); |
||
4128 | if (modalOptions.controllerAs) { |
||
4129 | ctrlInstance = ctrlInstantiate.instance; |
||
4130 | |||
4131 | if (modalOptions.bindToController) { |
||
4132 | ctrlInstance.$close = modalScope.$close; |
||
4133 | ctrlInstance.$dismiss = modalScope.$dismiss; |
||
4134 | angular.extend(ctrlInstance, providedScope); |
||
4135 | } |
||
4136 | |||
4137 | ctrlInstance = ctrlInstantiate(); |
||
4138 | |||
4139 | modalScope[modalOptions.controllerAs] = ctrlInstance; |
||
4140 | } else { |
||
4141 | ctrlInstance = ctrlInstantiate(); |
||
4142 | } |
||
4143 | |||
4144 | if (angular.isFunction(ctrlInstance.$onInit)) { |
||
4145 | ctrlInstance.$onInit(); |
||
4146 | } |
||
4147 | } |
||
4148 | |||
4149 | $modalStack.open(modalInstance, { |
||
4150 | scope: modalScope, |
||
4151 | deferred: modalResultDeferred, |
||
4152 | renderDeferred: modalRenderDeferred, |
||
4153 | closedDeferred: modalClosedDeferred, |
||
4154 | content: tplAndVars[0], |
||
4155 | animation: modalOptions.animation, |
||
4156 | backdrop: modalOptions.backdrop, |
||
4157 | keyboard: modalOptions.keyboard, |
||
4158 | backdropClass: modalOptions.backdropClass, |
||
4159 | windowTopClass: modalOptions.windowTopClass, |
||
4160 | windowClass: modalOptions.windowClass, |
||
4161 | windowTemplateUrl: modalOptions.windowTemplateUrl, |
||
4162 | size: modalOptions.size, |
||
4163 | openedClass: modalOptions.openedClass, |
||
4164 | appendTo: modalOptions.appendTo |
||
4165 | }); |
||
4166 | modalOpenedDeferred.resolve(true); |
||
4167 | |||
4168 | }, function resolveError(reason) { |
||
4169 | modalOpenedDeferred.reject(reason); |
||
4170 | modalResultDeferred.reject(reason); |
||
4171 | })['finally'](function() { |
||
4172 | if (promiseChain === samePromise) { |
||
4173 | promiseChain = null; |
||
4174 | } |
||
4175 | }); |
||
4176 | |||
4177 | return modalInstance; |
||
4178 | }; |
||
4179 | |||
4180 | return $modal; |
||
4181 | } |
||
4182 | ] |
||
4183 | }; |
||
4184 | |||
4185 | return $modalProvider; |
||
4186 | }); |
||
4187 | |||
4188 | angular.module('ui.bootstrap.paging', []) |
||
4189 | /** |
||
4190 | * Helper internal service for generating common controller code between the |
||
4191 | * pager and pagination components |
||
4192 | */ |
||
4193 | View Code Duplication | .factory('uibPaging', ['$parse', function($parse) { |
|
4194 | return { |
||
4195 | create: function(ctrl, $scope, $attrs) { |
||
4196 | ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; |
||
4197 | ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl |
||
4198 | ctrl._watchers = []; |
||
4199 | |||
4200 | ctrl.init = function(ngModelCtrl, config) { |
||
4201 | ctrl.ngModelCtrl = ngModelCtrl; |
||
4202 | ctrl.config = config; |
||
4203 | |||
4204 | ngModelCtrl.$render = function() { |
||
4205 | ctrl.render(); |
||
4206 | }; |
||
4207 | |||
4208 | if ($attrs.itemsPerPage) { |
||
4209 | ctrl._watchers.push($scope.$parent.$watch($attrs.itemsPerPage, function(value) { |
||
4210 | ctrl.itemsPerPage = parseInt(value, 10); |
||
4211 | $scope.totalPages = ctrl.calculateTotalPages(); |
||
4212 | ctrl.updatePage(); |
||
4213 | })); |
||
4214 | } else { |
||
4215 | ctrl.itemsPerPage = config.itemsPerPage; |
||
4216 | } |
||
4217 | |||
4218 | $scope.$watch('totalItems', function(newTotal, oldTotal) { |
||
4219 | if (angular.isDefined(newTotal) || newTotal !== oldTotal) { |
||
4220 | $scope.totalPages = ctrl.calculateTotalPages(); |
||
4221 | ctrl.updatePage(); |
||
4222 | } |
||
4223 | }); |
||
4224 | }; |
||
4225 | |||
4226 | ctrl.calculateTotalPages = function() { |
||
4227 | var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage); |
||
4228 | return Math.max(totalPages || 0, 1); |
||
4229 | }; |
||
4230 | |||
4231 | ctrl.render = function() { |
||
4232 | $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1; |
||
4233 | }; |
||
4234 | |||
4235 | $scope.selectPage = function(page, evt) { |
||
4236 | if (evt) { |
||
4237 | evt.preventDefault(); |
||
4238 | } |
||
4239 | |||
4240 | var clickAllowed = !$scope.ngDisabled || !evt; |
||
4241 | if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) { |
||
4242 | if (evt && evt.target) { |
||
4243 | evt.target.blur(); |
||
4244 | } |
||
4245 | ctrl.ngModelCtrl.$setViewValue(page); |
||
4246 | ctrl.ngModelCtrl.$render(); |
||
4247 | } |
||
4248 | }; |
||
4249 | |||
4250 | $scope.getText = function(key) { |
||
4251 | return $scope[key + 'Text'] || ctrl.config[key + 'Text']; |
||
4252 | }; |
||
4253 | |||
4254 | $scope.noPrevious = function() { |
||
4255 | return $scope.page === 1; |
||
4256 | }; |
||
4257 | |||
4258 | $scope.noNext = function() { |
||
4259 | return $scope.page === $scope.totalPages; |
||
4260 | }; |
||
4261 | |||
4262 | ctrl.updatePage = function() { |
||
4263 | ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable |
||
4264 | |||
4265 | if ($scope.page > $scope.totalPages) { |
||
4266 | $scope.selectPage($scope.totalPages); |
||
4267 | } else { |
||
4268 | ctrl.ngModelCtrl.$render(); |
||
4269 | } |
||
4270 | }; |
||
4271 | |||
4272 | $scope.$on('$destroy', function() { |
||
4273 | while (ctrl._watchers.length) { |
||
4274 | ctrl._watchers.shift()(); |
||
4275 | } |
||
4276 | }); |
||
4277 | } |
||
4278 | }; |
||
4279 | }]); |
||
4280 | |||
4281 | angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging']) |
||
4282 | |||
4283 | .controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) { |
||
4284 | $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align; |
||
4285 | |||
4286 | uibPaging.create(this, $scope, $attrs); |
||
4287 | }]) |
||
4288 | |||
4289 | .constant('uibPagerConfig', { |
||
4290 | itemsPerPage: 10, |
||
4291 | previousText: '« Previous', |
||
4292 | nextText: 'Next »', |
||
4293 | align: true |
||
4294 | }) |
||
4295 | |||
4296 | View Code Duplication | .directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) { |
|
4297 | return { |
||
4298 | scope: { |
||
4299 | totalItems: '=', |
||
4300 | previousText: '@', |
||
4301 | nextText: '@', |
||
4302 | ngDisabled: '=' |
||
4303 | }, |
||
4304 | require: ['uibPager', '?ngModel'], |
||
4305 | controller: 'UibPagerController', |
||
4306 | controllerAs: 'pager', |
||
4307 | templateUrl: function(element, attrs) { |
||
4308 | return attrs.templateUrl || 'uib/template/pager/pager.html'; |
||
4309 | }, |
||
4310 | replace: true, |
||
4311 | link: function(scope, element, attrs, ctrls) { |
||
4312 | var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; |
||
4313 | |||
4314 | if (!ngModelCtrl) { |
||
4315 | return; // do nothing if no ng-model |
||
4316 | } |
||
4317 | |||
4318 | paginationCtrl.init(ngModelCtrl, uibPagerConfig); |
||
4319 | } |
||
4320 | }; |
||
4321 | }]); |
||
4322 | |||
4323 | angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging']) |
||
4324 | View Code Duplication | .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) { |
|
4325 | var ctrl = this; |
||
4326 | // Setup configuration parameters |
||
4327 | var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize, |
||
4328 | rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate, |
||
4329 | forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses, |
||
4330 | boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers, |
||
4331 | pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity; |
||
4332 | $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks; |
||
4333 | $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks; |
||
4334 | |||
4335 | uibPaging.create(this, $scope, $attrs); |
||
4336 | |||
4337 | if ($attrs.maxSize) { |
||
4338 | ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) { |
||
4339 | maxSize = parseInt(value, 10); |
||
4340 | ctrl.render(); |
||
4341 | })); |
||
4342 | } |
||
4343 | |||
4344 | // Create page object used in template |
||
4345 | function makePage(number, text, isActive) { |
||
4346 | return { |
||
4347 | number: number, |
||
4348 | text: text, |
||
4349 | active: isActive |
||
4350 | }; |
||
4351 | } |
||
4352 | |||
4353 | function getPages(currentPage, totalPages) { |
||
4354 | var pages = []; |
||
4355 | |||
4356 | // Default page limits |
||
4357 | var startPage = 1, endPage = totalPages; |
||
4358 | var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages; |
||
4359 | |||
4360 | // recompute if maxSize |
||
4361 | if (isMaxSized) { |
||
4362 | if (rotate) { |
||
4363 | // Current page is displayed in the middle of the visible ones |
||
4364 | startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1); |
||
4365 | endPage = startPage + maxSize - 1; |
||
4366 | |||
4367 | // Adjust if limit is exceeded |
||
4368 | if (endPage > totalPages) { |
||
4369 | endPage = totalPages; |
||
4370 | startPage = endPage - maxSize + 1; |
||
4371 | } |
||
4372 | } else { |
||
4373 | // Visible pages are paginated with maxSize |
||
4374 | startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1; |
||
4375 | |||
4376 | // Adjust last page if limit is exceeded |
||
4377 | endPage = Math.min(startPage + maxSize - 1, totalPages); |
||
4378 | } |
||
4379 | } |
||
4380 | |||
4381 | // Add page number links |
||
4382 | for (var number = startPage; number <= endPage; number++) { |
||
4383 | var page = makePage(number, pageLabel(number), number === currentPage); |
||
4384 | pages.push(page); |
||
4385 | } |
||
4386 | |||
4387 | // Add links to move between page sets |
||
4388 | if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) { |
||
4389 | if (startPage > 1) { |
||
4390 | if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning |
||
4391 | var previousPageSet = makePage(startPage - 1, '...', false); |
||
4392 | pages.unshift(previousPageSet); |
||
4393 | } |
||
4394 | if (boundaryLinkNumbers) { |
||
4395 | if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential |
||
4396 | var secondPageLink = makePage(2, '2', false); |
||
4397 | pages.unshift(secondPageLink); |
||
4398 | } |
||
4399 | //add the first page |
||
4400 | var firstPageLink = makePage(1, '1', false); |
||
4401 | pages.unshift(firstPageLink); |
||
4402 | } |
||
4403 | } |
||
4404 | |||
4405 | if (endPage < totalPages) { |
||
4406 | if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end |
||
4407 | var nextPageSet = makePage(endPage + 1, '...', false); |
||
4408 | pages.push(nextPageSet); |
||
4409 | } |
||
4410 | if (boundaryLinkNumbers) { |
||
4411 | if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential |
||
4412 | var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false); |
||
4413 | pages.push(secondToLastPageLink); |
||
4414 | } |
||
4415 | //add the last page |
||
4416 | var lastPageLink = makePage(totalPages, totalPages, false); |
||
4417 | pages.push(lastPageLink); |
||
4418 | } |
||
4419 | } |
||
4420 | } |
||
4421 | return pages; |
||
4422 | } |
||
4423 | |||
4424 | var originalRender = this.render; |
||
4425 | this.render = function() { |
||
4426 | originalRender(); |
||
4427 | if ($scope.page > 0 && $scope.page <= $scope.totalPages) { |
||
4428 | $scope.pages = getPages($scope.page, $scope.totalPages); |
||
4429 | } |
||
4430 | }; |
||
4431 | }]) |
||
4432 | |||
4433 | .constant('uibPaginationConfig', { |
||
4434 | itemsPerPage: 10, |
||
4435 | boundaryLinks: false, |
||
4436 | boundaryLinkNumbers: false, |
||
4437 | directionLinks: true, |
||
4438 | firstText: 'First', |
||
4439 | previousText: 'Previous', |
||
4440 | nextText: 'Next', |
||
4441 | lastText: 'Last', |
||
4442 | rotate: true, |
||
4443 | forceEllipses: false |
||
4444 | }) |
||
4445 | |||
4446 | View Code Duplication | .directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) { |
|
4447 | return { |
||
4448 | scope: { |
||
4449 | totalItems: '=', |
||
4450 | firstText: '@', |
||
4451 | previousText: '@', |
||
4452 | nextText: '@', |
||
4453 | lastText: '@', |
||
4454 | ngDisabled:'=' |
||
4455 | }, |
||
4456 | require: ['uibPagination', '?ngModel'], |
||
4457 | controller: 'UibPaginationController', |
||
4458 | controllerAs: 'pagination', |
||
4459 | templateUrl: function(element, attrs) { |
||
4460 | return attrs.templateUrl || 'uib/template/pagination/pagination.html'; |
||
4461 | }, |
||
4462 | replace: true, |
||
4463 | link: function(scope, element, attrs, ctrls) { |
||
4464 | var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; |
||
4465 | |||
4466 | if (!ngModelCtrl) { |
||
4467 | return; // do nothing if no ng-model |
||
4468 | } |
||
4469 | |||
4470 | paginationCtrl.init(ngModelCtrl, uibPaginationConfig); |
||
4471 | } |
||
4472 | }; |
||
4473 | }]); |
||
4474 | |||
4475 | /** |
||
4476 | * The following features are still outstanding: animation as a |
||
4477 | * function, placement as a function, inside, support for more triggers than |
||
4478 | * just mouse enter/leave, html tooltips, and selector delegation. |
||
4479 | */ |
||
4480 | angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap']) |
||
4481 | |||
4482 | /** |
||
4483 | * The $tooltip service creates tooltip- and popover-like directives as well as |
||
4484 | * houses global options for them. |
||
4485 | */ |
||
4486 | View Code Duplication | .provider('$uibTooltip', function() { |
|
4487 | // The default options tooltip and popover. |
||
4488 | var defaultOptions = { |
||
4489 | placement: 'top', |
||
4490 | placementClassPrefix: '', |
||
4491 | animation: true, |
||
4492 | popupDelay: 0, |
||
4493 | popupCloseDelay: 0, |
||
4494 | useContentExp: false |
||
4495 | }; |
||
4496 | |||
4497 | // Default hide triggers for each show trigger |
||
4498 | var triggerMap = { |
||
4499 | 'mouseenter': 'mouseleave', |
||
4500 | 'click': 'click', |
||
4501 | 'outsideClick': 'outsideClick', |
||
4502 | 'focus': 'blur', |
||
4503 | 'none': '' |
||
4504 | }; |
||
4505 | |||
4506 | // The options specified to the provider globally. |
||
4507 | var globalOptions = {}; |
||
4508 | |||
4509 | /** |
||
4510 | * `options({})` allows global configuration of all tooltips in the |
||
4511 | * application. |
||
4512 | * |
||
4513 | * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { |
||
4514 | * // place tooltips left instead of top by default |
||
4515 | * $tooltipProvider.options( { placement: 'left' } ); |
||
4516 | * }); |
||
4517 | */ |
||
4518 | this.options = function(value) { |
||
4519 | angular.extend(globalOptions, value); |
||
4520 | }; |
||
4521 | |||
4522 | /** |
||
4523 | * This allows you to extend the set of trigger mappings available. E.g.: |
||
4524 | * |
||
4525 | * $tooltipProvider.setTriggers( { 'openTrigger': 'closeTrigger' } ); |
||
4526 | */ |
||
4527 | this.setTriggers = function setTriggers(triggers) { |
||
4528 | angular.extend(triggerMap, triggers); |
||
4529 | }; |
||
4530 | |||
4531 | /** |
||
4532 | * This is a helper function for translating camel-case to snake_case. |
||
4533 | */ |
||
4534 | function snake_case(name) { |
||
4535 | var regexp = /[A-Z]/g; |
||
4536 | var separator = '-'; |
||
4537 | return name.replace(regexp, function(letter, pos) { |
||
4538 | return (pos ? separator : '') + letter.toLowerCase(); |
||
4539 | }); |
||
4540 | } |
||
4541 | |||
4542 | /** |
||
4543 | * Returns the actual instance of the $tooltip service. |
||
4544 | * TODO support multiple triggers |
||
4545 | */ |
||
4546 | this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { |
||
4547 | var openedTooltips = $$stackedMap.createNew(); |
||
4548 | $document.on('keypress', keypressListener); |
||
4549 | |||
4550 | $rootScope.$on('$destroy', function() { |
||
4551 | $document.off('keypress', keypressListener); |
||
4552 | }); |
||
4553 | |||
4554 | function keypressListener(e) { |
||
4555 | if (e.which === 27) { |
||
4556 | var last = openedTooltips.top(); |
||
4557 | if (last) { |
||
4558 | last.value.close(); |
||
4559 | openedTooltips.removeTop(); |
||
4560 | last = null; |
||
0 ignored issues
–
show
|
|||
4561 | } |
||
4562 | } |
||
4563 | } |
||
4564 | |||
4565 | return function $tooltip(ttType, prefix, defaultTriggerShow, options) { |
||
4566 | options = angular.extend({}, defaultOptions, globalOptions, options); |
||
4567 | |||
4568 | /** |
||
4569 | * Returns an object of show and hide triggers. |
||
4570 | * |
||
4571 | * If a trigger is supplied, |
||
4572 | * it is used to show the tooltip; otherwise, it will use the `trigger` |
||
4573 | * option passed to the `$tooltipProvider.options` method; else it will |
||
4574 | * default to the trigger supplied to this directive factory. |
||
4575 | * |
||
4576 | * The hide trigger is based on the show trigger. If the `trigger` option |
||
4577 | * was passed to the `$tooltipProvider.options` method, it will use the |
||
4578 | * mapped trigger from `triggerMap` or the passed trigger if the map is |
||
4579 | * undefined; otherwise, it uses the `triggerMap` value of the show |
||
4580 | * trigger; else it will just use the show trigger. |
||
4581 | */ |
||
4582 | function getTriggers(trigger) { |
||
4583 | var show = (trigger || options.trigger || defaultTriggerShow).split(' '); |
||
4584 | var hide = show.map(function(trigger) { |
||
4585 | return triggerMap[trigger] || trigger; |
||
4586 | }); |
||
4587 | return { |
||
4588 | show: show, |
||
4589 | hide: hide |
||
4590 | }; |
||
4591 | } |
||
4592 | |||
4593 | var directiveName = snake_case(ttType); |
||
4594 | |||
4595 | var startSym = $interpolate.startSymbol(); |
||
4596 | var endSym = $interpolate.endSymbol(); |
||
4597 | var template = |
||
4598 | '<div '+ directiveName + '-popup ' + |
||
4599 | 'uib-title="' + startSym + 'title' + endSym + '" ' + |
||
4600 | (options.useContentExp ? |
||
4601 | 'content-exp="contentExp()" ' : |
||
4602 | 'content="' + startSym + 'content' + endSym + '" ') + |
||
4603 | 'placement="' + startSym + 'placement' + endSym + '" ' + |
||
4604 | 'popup-class="' + startSym + 'popupClass' + endSym + '" ' + |
||
4605 | 'animation="animation" ' + |
||
4606 | 'is-open="isOpen" ' + |
||
4607 | 'origin-scope="origScope" ' + |
||
4608 | 'class="uib-position-measure"' + |
||
4609 | '>' + |
||
4610 | '</div>'; |
||
4611 | |||
4612 | return { |
||
4613 | compile: function(tElem, tAttrs) { |
||
4614 | var tooltipLinker = $compile(template); |
||
4615 | |||
4616 | return function link(scope, element, attrs, tooltipCtrl) { |
||
4617 | var tooltip; |
||
4618 | var tooltipLinkedScope; |
||
4619 | var transitionTimeout; |
||
4620 | var showTimeout; |
||
4621 | var hideTimeout; |
||
4622 | var positionTimeout; |
||
4623 | var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false; |
||
4624 | var triggers = getTriggers(undefined); |
||
4625 | var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); |
||
4626 | var ttScope = scope.$new(true); |
||
4627 | var repositionScheduled = false; |
||
4628 | var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; |
||
4629 | var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; |
||
4630 | var observers = []; |
||
4631 | var lastPlacement; |
||
4632 | |||
4633 | var positionTooltip = function() { |
||
4634 | // check if tooltip exists and is not empty |
||
4635 | if (!tooltip || !tooltip.html()) { return; } |
||
4636 | |||
4637 | if (!positionTimeout) { |
||
4638 | positionTimeout = $timeout(function() { |
||
4639 | var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); |
||
4640 | tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' }); |
||
4641 | |||
4642 | if (!tooltip.hasClass(ttPosition.placement.split('-')[0])) { |
||
4643 | tooltip.removeClass(lastPlacement.split('-')[0]); |
||
4644 | tooltip.addClass(ttPosition.placement.split('-')[0]); |
||
4645 | } |
||
4646 | |||
4647 | if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) { |
||
4648 | tooltip.removeClass(options.placementClassPrefix + lastPlacement); |
||
4649 | tooltip.addClass(options.placementClassPrefix + ttPosition.placement); |
||
4650 | } |
||
4651 | |||
4652 | // first time through tt element will have the |
||
4653 | // uib-position-measure class or if the placement |
||
4654 | // has changed we need to position the arrow. |
||
4655 | if (tooltip.hasClass('uib-position-measure')) { |
||
4656 | $position.positionArrow(tooltip, ttPosition.placement); |
||
4657 | tooltip.removeClass('uib-position-measure'); |
||
4658 | } else if (lastPlacement !== ttPosition.placement) { |
||
4659 | $position.positionArrow(tooltip, ttPosition.placement); |
||
4660 | } |
||
4661 | lastPlacement = ttPosition.placement; |
||
4662 | |||
4663 | positionTimeout = null; |
||
4664 | }, 0, false); |
||
4665 | } |
||
4666 | }; |
||
4667 | |||
4668 | // Set up the correct scope to allow transclusion later |
||
4669 | ttScope.origScope = scope; |
||
4670 | |||
4671 | // By default, the tooltip is not open. |
||
4672 | // TODO add ability to start tooltip opened |
||
4673 | ttScope.isOpen = false; |
||
4674 | openedTooltips.add(ttScope, { |
||
4675 | close: hide |
||
4676 | }); |
||
4677 | |||
4678 | function toggleTooltipBind() { |
||
4679 | if (!ttScope.isOpen) { |
||
4680 | showTooltipBind(); |
||
4681 | } else { |
||
4682 | hideTooltipBind(); |
||
4683 | } |
||
4684 | } |
||
4685 | |||
4686 | // Show the tooltip with delay if specified, otherwise show it immediately |
||
4687 | function showTooltipBind() { |
||
4688 | if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { |
||
4689 | return; |
||
4690 | } |
||
4691 | |||
4692 | cancelHide(); |
||
4693 | prepareTooltip(); |
||
4694 | |||
4695 | if (ttScope.popupDelay) { |
||
4696 | // Do nothing if the tooltip was already scheduled to pop-up. |
||
4697 | // This happens if show is triggered multiple times before any hide is triggered. |
||
4698 | if (!showTimeout) { |
||
4699 | showTimeout = $timeout(show, ttScope.popupDelay, false); |
||
4700 | } |
||
4701 | } else { |
||
4702 | show(); |
||
4703 | } |
||
4704 | } |
||
4705 | |||
4706 | function hideTooltipBind() { |
||
4707 | cancelShow(); |
||
4708 | |||
4709 | if (ttScope.popupCloseDelay) { |
||
4710 | if (!hideTimeout) { |
||
4711 | hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); |
||
4712 | } |
||
4713 | } else { |
||
4714 | hide(); |
||
4715 | } |
||
4716 | } |
||
4717 | |||
4718 | // Show the tooltip popup element. |
||
4719 | function show() { |
||
4720 | cancelShow(); |
||
4721 | cancelHide(); |
||
4722 | |||
4723 | // Don't show empty tooltips. |
||
4724 | if (!ttScope.content) { |
||
4725 | return angular.noop; |
||
4726 | } |
||
4727 | |||
4728 | createTooltip(); |
||
4729 | |||
4730 | // And show the tooltip. |
||
4731 | ttScope.$evalAsync(function() { |
||
4732 | ttScope.isOpen = true; |
||
4733 | assignIsOpen(true); |
||
4734 | positionTooltip(); |
||
4735 | }); |
||
4736 | } |
||
4737 | |||
4738 | function cancelShow() { |
||
4739 | if (showTimeout) { |
||
4740 | $timeout.cancel(showTimeout); |
||
4741 | showTimeout = null; |
||
4742 | } |
||
4743 | |||
4744 | if (positionTimeout) { |
||
4745 | $timeout.cancel(positionTimeout); |
||
4746 | positionTimeout = null; |
||
4747 | } |
||
4748 | } |
||
4749 | |||
4750 | // Hide the tooltip popup element. |
||
4751 | function hide() { |
||
4752 | if (!ttScope) { |
||
4753 | return; |
||
4754 | } |
||
4755 | |||
4756 | // First things first: we don't show it anymore. |
||
4757 | ttScope.$evalAsync(function() { |
||
4758 | if (ttScope) { |
||
4759 | ttScope.isOpen = false; |
||
4760 | assignIsOpen(false); |
||
4761 | // And now we remove it from the DOM. However, if we have animation, we |
||
4762 | // need to wait for it to expire beforehand. |
||
4763 | // FIXME: this is a placeholder for a port of the transitions library. |
||
4764 | // The fade transition in TWBS is 150ms. |
||
4765 | if (ttScope.animation) { |
||
4766 | if (!transitionTimeout) { |
||
4767 | transitionTimeout = $timeout(removeTooltip, 150, false); |
||
4768 | } |
||
4769 | } else { |
||
4770 | removeTooltip(); |
||
4771 | } |
||
4772 | } |
||
4773 | }); |
||
4774 | } |
||
4775 | |||
4776 | function cancelHide() { |
||
4777 | if (hideTimeout) { |
||
4778 | $timeout.cancel(hideTimeout); |
||
4779 | hideTimeout = null; |
||
4780 | } |
||
4781 | |||
4782 | if (transitionTimeout) { |
||
4783 | $timeout.cancel(transitionTimeout); |
||
4784 | transitionTimeout = null; |
||
4785 | } |
||
4786 | } |
||
4787 | |||
4788 | function createTooltip() { |
||
4789 | // There can only be one tooltip element per directive shown at once. |
||
4790 | if (tooltip) { |
||
4791 | return; |
||
4792 | } |
||
4793 | |||
4794 | tooltipLinkedScope = ttScope.$new(); |
||
4795 | tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { |
||
4796 | if (appendToBody) { |
||
4797 | $document.find('body').append(tooltip); |
||
4798 | } else { |
||
4799 | element.after(tooltip); |
||
4800 | } |
||
4801 | }); |
||
4802 | |||
4803 | prepObservers(); |
||
4804 | } |
||
4805 | |||
4806 | function removeTooltip() { |
||
4807 | cancelShow(); |
||
4808 | cancelHide(); |
||
4809 | unregisterObservers(); |
||
4810 | |||
4811 | if (tooltip) { |
||
4812 | tooltip.remove(); |
||
4813 | tooltip = null; |
||
4814 | } |
||
4815 | if (tooltipLinkedScope) { |
||
4816 | tooltipLinkedScope.$destroy(); |
||
4817 | tooltipLinkedScope = null; |
||
4818 | } |
||
4819 | } |
||
4820 | |||
4821 | /** |
||
4822 | * Set the initial scope values. Once |
||
4823 | * the tooltip is created, the observers |
||
4824 | * will be added to keep things in sync. |
||
4825 | */ |
||
4826 | function prepareTooltip() { |
||
4827 | ttScope.title = attrs[prefix + 'Title']; |
||
4828 | if (contentParse) { |
||
4829 | ttScope.content = contentParse(scope); |
||
4830 | } else { |
||
4831 | ttScope.content = attrs[ttType]; |
||
4832 | } |
||
4833 | |||
4834 | ttScope.popupClass = attrs[prefix + 'Class']; |
||
4835 | ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; |
||
4836 | var placement = $position.parsePlacement(ttScope.placement); |
||
4837 | lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0]; |
||
4838 | |||
4839 | var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); |
||
4840 | var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); |
||
4841 | ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; |
||
4842 | ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; |
||
4843 | } |
||
4844 | |||
4845 | function assignIsOpen(isOpen) { |
||
4846 | if (isOpenParse && angular.isFunction(isOpenParse.assign)) { |
||
4847 | isOpenParse.assign(scope, isOpen); |
||
4848 | } |
||
4849 | } |
||
4850 | |||
4851 | ttScope.contentExp = function() { |
||
4852 | return ttScope.content; |
||
4853 | }; |
||
4854 | |||
4855 | /** |
||
4856 | * Observe the relevant attributes. |
||
4857 | */ |
||
4858 | attrs.$observe('disabled', function(val) { |
||
4859 | if (val) { |
||
4860 | cancelShow(); |
||
4861 | } |
||
4862 | |||
4863 | if (val && ttScope.isOpen) { |
||
4864 | hide(); |
||
4865 | } |
||
4866 | }); |
||
4867 | |||
4868 | if (isOpenParse) { |
||
4869 | scope.$watch(isOpenParse, function(val) { |
||
4870 | if (ttScope && !val === ttScope.isOpen) { |
||
4871 | toggleTooltipBind(); |
||
4872 | } |
||
4873 | }); |
||
4874 | } |
||
4875 | |||
4876 | function prepObservers() { |
||
4877 | observers.length = 0; |
||
4878 | |||
4879 | if (contentParse) { |
||
4880 | observers.push( |
||
4881 | scope.$watch(contentParse, function(val) { |
||
4882 | ttScope.content = val; |
||
4883 | if (!val && ttScope.isOpen) { |
||
4884 | hide(); |
||
4885 | } |
||
4886 | }) |
||
4887 | ); |
||
4888 | |||
4889 | observers.push( |
||
4890 | tooltipLinkedScope.$watch(function() { |
||
4891 | if (!repositionScheduled) { |
||
4892 | repositionScheduled = true; |
||
4893 | tooltipLinkedScope.$$postDigest(function() { |
||
4894 | repositionScheduled = false; |
||
4895 | if (ttScope && ttScope.isOpen) { |
||
4896 | positionTooltip(); |
||
4897 | } |
||
4898 | }); |
||
4899 | } |
||
4900 | }) |
||
4901 | ); |
||
4902 | } else { |
||
4903 | observers.push( |
||
4904 | attrs.$observe(ttType, function(val) { |
||
4905 | ttScope.content = val; |
||
4906 | if (!val && ttScope.isOpen) { |
||
4907 | hide(); |
||
4908 | } else { |
||
4909 | positionTooltip(); |
||
4910 | } |
||
4911 | }) |
||
4912 | ); |
||
4913 | } |
||
4914 | |||
4915 | observers.push( |
||
4916 | attrs.$observe(prefix + 'Title', function(val) { |
||
4917 | ttScope.title = val; |
||
4918 | if (ttScope.isOpen) { |
||
4919 | positionTooltip(); |
||
4920 | } |
||
4921 | }) |
||
4922 | ); |
||
4923 | |||
4924 | observers.push( |
||
4925 | attrs.$observe(prefix + 'Placement', function(val) { |
||
4926 | ttScope.placement = val ? val : options.placement; |
||
4927 | if (ttScope.isOpen) { |
||
4928 | positionTooltip(); |
||
4929 | } |
||
4930 | }) |
||
4931 | ); |
||
4932 | } |
||
4933 | |||
4934 | function unregisterObservers() { |
||
4935 | if (observers.length) { |
||
4936 | angular.forEach(observers, function(observer) { |
||
4937 | observer(); |
||
4938 | }); |
||
4939 | observers.length = 0; |
||
4940 | } |
||
4941 | } |
||
4942 | |||
4943 | // hide tooltips/popovers for outsideClick trigger |
||
4944 | function bodyHideTooltipBind(e) { |
||
4945 | if (!ttScope || !ttScope.isOpen || !tooltip) { |
||
4946 | return; |
||
4947 | } |
||
4948 | // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked |
||
4949 | if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { |
||
4950 | hideTooltipBind(); |
||
4951 | } |
||
4952 | } |
||
4953 | |||
4954 | var unregisterTriggers = function() { |
||
4955 | triggers.show.forEach(function(trigger) { |
||
4956 | if (trigger === 'outsideClick') { |
||
4957 | element.off('click', toggleTooltipBind); |
||
4958 | } else { |
||
4959 | element.off(trigger, showTooltipBind); |
||
4960 | element.off(trigger, toggleTooltipBind); |
||
4961 | } |
||
4962 | }); |
||
4963 | triggers.hide.forEach(function(trigger) { |
||
4964 | if (trigger === 'outsideClick') { |
||
4965 | $document.off('click', bodyHideTooltipBind); |
||
4966 | } else { |
||
4967 | element.off(trigger, hideTooltipBind); |
||
4968 | } |
||
4969 | }); |
||
4970 | }; |
||
4971 | |||
4972 | function prepTriggers() { |
||
4973 | var val = attrs[prefix + 'Trigger']; |
||
4974 | unregisterTriggers(); |
||
4975 | |||
4976 | triggers = getTriggers(val); |
||
4977 | |||
4978 | if (triggers.show !== 'none') { |
||
4979 | triggers.show.forEach(function(trigger, idx) { |
||
4980 | if (trigger === 'outsideClick') { |
||
4981 | element.on('click', toggleTooltipBind); |
||
4982 | $document.on('click', bodyHideTooltipBind); |
||
4983 | } else if (trigger === triggers.hide[idx]) { |
||
4984 | element.on(trigger, toggleTooltipBind); |
||
4985 | } else if (trigger) { |
||
4986 | element.on(trigger, showTooltipBind); |
||
4987 | element.on(triggers.hide[idx], hideTooltipBind); |
||
4988 | } |
||
4989 | |||
4990 | element.on('keypress', function(e) { |
||
4991 | if (e.which === 27) { |
||
4992 | hideTooltipBind(); |
||
4993 | } |
||
4994 | }); |
||
4995 | }); |
||
4996 | } |
||
4997 | } |
||
4998 | |||
4999 | prepTriggers(); |
||
5000 | |||
5001 | var animation = scope.$eval(attrs[prefix + 'Animation']); |
||
5002 | ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; |
||
5003 | |||
5004 | var appendToBodyVal; |
||
5005 | var appendKey = prefix + 'AppendToBody'; |
||
5006 | if (appendKey in attrs && attrs[appendKey] === undefined) { |
||
5007 | appendToBodyVal = true; |
||
5008 | } else { |
||
5009 | appendToBodyVal = scope.$eval(attrs[appendKey]); |
||
5010 | } |
||
5011 | |||
5012 | appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; |
||
5013 | |||
5014 | // Make sure tooltip is destroyed and removed. |
||
5015 | scope.$on('$destroy', function onDestroyTooltip() { |
||
5016 | unregisterTriggers(); |
||
5017 | removeTooltip(); |
||
5018 | openedTooltips.remove(ttScope); |
||
5019 | ttScope = null; |
||
5020 | }); |
||
5021 | }; |
||
5022 | } |
||
5023 | }; |
||
5024 | }; |
||
5025 | }]; |
||
5026 | }) |
||
5027 | |||
5028 | // This is mostly ngInclude code but with a custom scope |
||
5029 | .directive('uibTooltipTemplateTransclude', [ |
||
5030 | '$animate', '$sce', '$compile', '$templateRequest', |
||
5031 | View Code Duplication | function ($animate, $sce, $compile, $templateRequest) { |
|
5032 | return { |
||
5033 | link: function(scope, elem, attrs) { |
||
5034 | var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); |
||
5035 | |||
5036 | var changeCounter = 0, |
||
5037 | currentScope, |
||
5038 | previousElement, |
||
5039 | currentElement; |
||
5040 | |||
5041 | var cleanupLastIncludeContent = function() { |
||
5042 | if (previousElement) { |
||
5043 | previousElement.remove(); |
||
5044 | previousElement = null; |
||
5045 | } |
||
5046 | |||
5047 | if (currentScope) { |
||
5048 | currentScope.$destroy(); |
||
5049 | currentScope = null; |
||
5050 | } |
||
5051 | |||
5052 | if (currentElement) { |
||
5053 | $animate.leave(currentElement).then(function() { |
||
5054 | previousElement = null; |
||
5055 | }); |
||
5056 | previousElement = currentElement; |
||
5057 | currentElement = null; |
||
5058 | } |
||
5059 | }; |
||
5060 | |||
5061 | scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) { |
||
5062 | var thisChangeId = ++changeCounter; |
||
5063 | |||
5064 | if (src) { |
||
5065 | //set the 2nd param to true to ignore the template request error so that the inner |
||
5066 | //contents and scope can be cleaned up. |
||
5067 | $templateRequest(src, true).then(function(response) { |
||
5068 | if (thisChangeId !== changeCounter) { return; } |
||
5069 | var newScope = origScope.$new(); |
||
5070 | var template = response; |
||
5071 | |||
5072 | var clone = $compile(template)(newScope, function(clone) { |
||
5073 | cleanupLastIncludeContent(); |
||
5074 | $animate.enter(clone, elem); |
||
5075 | }); |
||
5076 | |||
5077 | currentScope = newScope; |
||
5078 | currentElement = clone; |
||
5079 | |||
5080 | currentScope.$emit('$includeContentLoaded', src); |
||
5081 | }, function() { |
||
5082 | if (thisChangeId === changeCounter) { |
||
5083 | cleanupLastIncludeContent(); |
||
5084 | scope.$emit('$includeContentError', src); |
||
5085 | } |
||
5086 | }); |
||
5087 | scope.$emit('$includeContentRequested', src); |
||
5088 | } else { |
||
5089 | cleanupLastIncludeContent(); |
||
5090 | } |
||
5091 | }); |
||
5092 | |||
5093 | scope.$on('$destroy', cleanupLastIncludeContent); |
||
5094 | } |
||
5095 | }; |
||
5096 | }]) |
||
5097 | |||
5098 | /** |
||
5099 | * Note that it's intentional that these classes are *not* applied through $animate. |
||
5100 | * They must not be animated as they're expected to be present on the tooltip on |
||
5101 | * initialization. |
||
5102 | */ |
||
5103 | .directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) { |
||
5104 | return { |
||
5105 | restrict: 'A', |
||
5106 | link: function(scope, element, attrs) { |
||
5107 | // need to set the primary position so the |
||
5108 | // arrow has space during position measure. |
||
5109 | // tooltip.positionTooltip() |
||
5110 | if (scope.placement) { |
||
5111 | // // There are no top-left etc... classes |
||
5112 | // // in TWBS, so we need the primary position. |
||
5113 | var position = $uibPosition.parsePlacement(scope.placement); |
||
5114 | element.addClass(position[0]); |
||
5115 | } |
||
5116 | |||
5117 | if (scope.popupClass) { |
||
5118 | element.addClass(scope.popupClass); |
||
5119 | } |
||
5120 | |||
5121 | if (scope.animation()) { |
||
5122 | element.addClass(attrs.tooltipAnimationClass); |
||
5123 | } |
||
5124 | } |
||
5125 | }; |
||
5126 | }]) |
||
5127 | |||
5128 | .directive('uibTooltipPopup', function() { |
||
5129 | return { |
||
5130 | replace: true, |
||
5131 | scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, |
||
5132 | templateUrl: 'uib/template/tooltip/tooltip-popup.html' |
||
5133 | }; |
||
5134 | }) |
||
5135 | |||
5136 | .directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) { |
||
5137 | return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter'); |
||
5138 | }]) |
||
5139 | |||
5140 | .directive('uibTooltipTemplatePopup', function() { |
||
5141 | return { |
||
5142 | replace: true, |
||
5143 | scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', |
||
5144 | originScope: '&' }, |
||
5145 | templateUrl: 'uib/template/tooltip/tooltip-template-popup.html' |
||
5146 | }; |
||
5147 | }) |
||
5148 | |||
5149 | .directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) { |
||
5150 | return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', { |
||
5151 | useContentExp: true |
||
5152 | }); |
||
5153 | }]) |
||
5154 | |||
5155 | .directive('uibTooltipHtmlPopup', function() { |
||
5156 | return { |
||
5157 | replace: true, |
||
5158 | scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, |
||
5159 | templateUrl: 'uib/template/tooltip/tooltip-html-popup.html' |
||
5160 | }; |
||
5161 | }) |
||
5162 | |||
5163 | .directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) { |
||
5164 | return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', { |
||
5165 | useContentExp: true |
||
5166 | }); |
||
5167 | }]); |
||
5168 | |||
5169 | /** |
||
5170 | * The following features are still outstanding: popup delay, animation as a |
||
5171 | * function, placement as a function, inside, support for more triggers than |
||
5172 | * just mouse enter/leave, and selector delegatation. |
||
5173 | */ |
||
5174 | angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip']) |
||
5175 | |||
5176 | .directive('uibPopoverTemplatePopup', function() { |
||
5177 | return { |
||
5178 | replace: true, |
||
5179 | scope: { uibTitle: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', |
||
5180 | originScope: '&' }, |
||
5181 | templateUrl: 'uib/template/popover/popover-template.html' |
||
5182 | }; |
||
5183 | }) |
||
5184 | |||
5185 | .directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) { |
||
5186 | return $uibTooltip('uibPopoverTemplate', 'popover', 'click', { |
||
5187 | useContentExp: true |
||
5188 | }); |
||
5189 | }]) |
||
5190 | |||
5191 | .directive('uibPopoverHtmlPopup', function() { |
||
5192 | return { |
||
5193 | replace: true, |
||
5194 | scope: { contentExp: '&', uibTitle: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, |
||
5195 | templateUrl: 'uib/template/popover/popover-html.html' |
||
5196 | }; |
||
5197 | }) |
||
5198 | |||
5199 | .directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) { |
||
5200 | return $uibTooltip('uibPopoverHtml', 'popover', 'click', { |
||
5201 | useContentExp: true |
||
5202 | }); |
||
5203 | }]) |
||
5204 | |||
5205 | .directive('uibPopoverPopup', function() { |
||
5206 | return { |
||
5207 | replace: true, |
||
5208 | scope: { uibTitle: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, |
||
5209 | templateUrl: 'uib/template/popover/popover.html' |
||
5210 | }; |
||
5211 | }) |
||
5212 | |||
5213 | .directive('uibPopover', ['$uibTooltip', function($uibTooltip) { |
||
5214 | return $uibTooltip('uibPopover', 'popover', 'click'); |
||
5215 | }]); |
||
5216 | |||
5217 | angular.module('ui.bootstrap.progressbar', []) |
||
5218 | |||
5219 | .constant('uibProgressConfig', { |
||
5220 | animate: true, |
||
5221 | max: 100 |
||
5222 | }) |
||
5223 | |||
5224 | View Code Duplication | .controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) { |
|
5225 | var self = this, |
||
5226 | animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate; |
||
5227 | |||
5228 | this.bars = []; |
||
5229 | $scope.max = getMaxOrDefault(); |
||
5230 | |||
5231 | this.addBar = function(bar, element, attrs) { |
||
5232 | if (!animate) { |
||
5233 | element.css({'transition': 'none'}); |
||
5234 | } |
||
5235 | |||
5236 | this.bars.push(bar); |
||
5237 | |||
5238 | bar.max = getMaxOrDefault(); |
||
5239 | bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar'; |
||
5240 | |||
5241 | bar.$watch('value', function(value) { |
||
5242 | bar.recalculatePercentage(); |
||
5243 | }); |
||
5244 | |||
5245 | bar.recalculatePercentage = function() { |
||
5246 | var totalPercentage = self.bars.reduce(function(total, bar) { |
||
5247 | bar.percent = +(100 * bar.value / bar.max).toFixed(2); |
||
5248 | return total + bar.percent; |
||
5249 | }, 0); |
||
5250 | |||
5251 | if (totalPercentage > 100) { |
||
5252 | bar.percent -= totalPercentage - 100; |
||
5253 | } |
||
5254 | }; |
||
5255 | |||
5256 | bar.$on('$destroy', function() { |
||
5257 | element = null; |
||
5258 | self.removeBar(bar); |
||
5259 | }); |
||
5260 | }; |
||
5261 | |||
5262 | this.removeBar = function(bar) { |
||
5263 | this.bars.splice(this.bars.indexOf(bar), 1); |
||
5264 | this.bars.forEach(function (bar) { |
||
5265 | bar.recalculatePercentage(); |
||
5266 | }); |
||
5267 | }; |
||
5268 | |||
5269 | //$attrs.$observe('maxParam', function(maxParam) { |
||
5270 | $scope.$watch('maxParam', function(maxParam) { |
||
5271 | self.bars.forEach(function(bar) { |
||
5272 | bar.max = getMaxOrDefault(); |
||
5273 | bar.recalculatePercentage(); |
||
5274 | }); |
||
5275 | }); |
||
5276 | |||
5277 | function getMaxOrDefault () { |
||
5278 | return angular.isDefined($scope.maxParam) ? $scope.maxParam : progressConfig.max; |
||
5279 | } |
||
5280 | }]) |
||
5281 | |||
5282 | .directive('uibProgress', function() { |
||
5283 | return { |
||
5284 | replace: true, |
||
5285 | transclude: true, |
||
5286 | controller: 'UibProgressController', |
||
5287 | require: 'uibProgress', |
||
5288 | scope: { |
||
5289 | maxParam: '=?max' |
||
5290 | }, |
||
5291 | templateUrl: 'uib/template/progressbar/progress.html' |
||
5292 | }; |
||
5293 | }) |
||
5294 | |||
5295 | .directive('uibBar', function() { |
||
5296 | return { |
||
5297 | replace: true, |
||
5298 | transclude: true, |
||
5299 | require: '^uibProgress', |
||
5300 | scope: { |
||
5301 | value: '=', |
||
5302 | type: '@' |
||
5303 | }, |
||
5304 | templateUrl: 'uib/template/progressbar/bar.html', |
||
5305 | link: function(scope, element, attrs, progressCtrl) { |
||
5306 | progressCtrl.addBar(scope, element, attrs); |
||
5307 | } |
||
5308 | }; |
||
5309 | }) |
||
5310 | |||
5311 | .directive('uibProgressbar', function() { |
||
5312 | return { |
||
5313 | replace: true, |
||
5314 | transclude: true, |
||
5315 | controller: 'UibProgressController', |
||
5316 | scope: { |
||
5317 | value: '=', |
||
5318 | maxParam: '=?max', |
||
5319 | type: '@' |
||
5320 | }, |
||
5321 | templateUrl: 'uib/template/progressbar/progressbar.html', |
||
5322 | link: function(scope, element, attrs, progressCtrl) { |
||
5323 | progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); |
||
5324 | } |
||
5325 | }; |
||
5326 | }); |
||
5327 | |||
5328 | angular.module('ui.bootstrap.rating', []) |
||
5329 | |||
5330 | .constant('uibRatingConfig', { |
||
5331 | max: 5, |
||
5332 | stateOn: null, |
||
5333 | stateOff: null, |
||
5334 | enableReset: true, |
||
5335 | titles : ['one', 'two', 'three', 'four', 'five'] |
||
5336 | }) |
||
5337 | |||
5338 | View Code Duplication | .controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) { |
|
5339 | var ngModelCtrl = { $setViewValue: angular.noop }, |
||
5340 | self = this; |
||
5341 | |||
5342 | this.init = function(ngModelCtrl_) { |
||
5343 | ngModelCtrl = ngModelCtrl_; |
||
5344 | ngModelCtrl.$render = this.render; |
||
5345 | |||
5346 | ngModelCtrl.$formatters.push(function(value) { |
||
5347 | if (angular.isNumber(value) && value << 0 !== value) { |
||
5348 | value = Math.round(value); |
||
5349 | } |
||
5350 | |||
5351 | return value; |
||
5352 | }); |
||
5353 | |||
5354 | this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn; |
||
5355 | this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff; |
||
5356 | this.enableReset = angular.isDefined($attrs.enableReset) ? |
||
5357 | $scope.$parent.$eval($attrs.enableReset) : ratingConfig.enableReset; |
||
5358 | var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles; |
||
5359 | this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ? |
||
5360 | tmpTitles : ratingConfig.titles; |
||
5361 | |||
5362 | var ratingStates = angular.isDefined($attrs.ratingStates) ? |
||
5363 | $scope.$parent.$eval($attrs.ratingStates) : |
||
5364 | new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max); |
||
0 ignored issues
–
show
|
|||
5365 | $scope.range = this.buildTemplateObjects(ratingStates); |
||
5366 | }; |
||
5367 | |||
5368 | this.buildTemplateObjects = function(states) { |
||
5369 | for (var i = 0, n = states.length; i < n; i++) { |
||
5370 | states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]); |
||
5371 | } |
||
5372 | return states; |
||
5373 | }; |
||
5374 | |||
5375 | this.getTitle = function(index) { |
||
5376 | if (index >= this.titles.length) { |
||
5377 | return index + 1; |
||
5378 | } |
||
5379 | |||
5380 | return this.titles[index]; |
||
5381 | }; |
||
5382 | |||
5383 | $scope.rate = function(value) { |
||
5384 | if (!$scope.readonly && value >= 0 && value <= $scope.range.length) { |
||
5385 | var newViewValue = self.enableReset && ngModelCtrl.$viewValue === value ? 0 : value; |
||
5386 | ngModelCtrl.$setViewValue(newViewValue); |
||
5387 | ngModelCtrl.$render(); |
||
5388 | } |
||
5389 | }; |
||
5390 | |||
5391 | $scope.enter = function(value) { |
||
5392 | if (!$scope.readonly) { |
||
5393 | $scope.value = value; |
||
5394 | } |
||
5395 | $scope.onHover({value: value}); |
||
5396 | }; |
||
5397 | |||
5398 | $scope.reset = function() { |
||
5399 | $scope.value = ngModelCtrl.$viewValue; |
||
5400 | $scope.onLeave(); |
||
5401 | }; |
||
5402 | |||
5403 | $scope.onKeydown = function(evt) { |
||
5404 | if (/(37|38|39|40)/.test(evt.which)) { |
||
5405 | evt.preventDefault(); |
||
5406 | evt.stopPropagation(); |
||
5407 | $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1)); |
||
5408 | } |
||
5409 | }; |
||
5410 | |||
5411 | this.render = function() { |
||
5412 | $scope.value = ngModelCtrl.$viewValue; |
||
5413 | $scope.title = self.getTitle($scope.value - 1); |
||
5414 | }; |
||
5415 | }]) |
||
5416 | |||
5417 | .directive('uibRating', function() { |
||
5418 | return { |
||
5419 | require: ['uibRating', 'ngModel'], |
||
5420 | scope: { |
||
5421 | readonly: '=?readOnly', |
||
5422 | onHover: '&', |
||
5423 | onLeave: '&' |
||
5424 | }, |
||
5425 | controller: 'UibRatingController', |
||
5426 | templateUrl: 'uib/template/rating/rating.html', |
||
5427 | replace: true, |
||
5428 | link: function(scope, element, attrs, ctrls) { |
||
5429 | var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; |
||
5430 | ratingCtrl.init(ngModelCtrl); |
||
5431 | } |
||
5432 | }; |
||
5433 | }); |
||
5434 | |||
5435 | angular.module('ui.bootstrap.tabs', []) |
||
5436 | |||
5437 | View Code Duplication | .controller('UibTabsetController', ['$scope', function ($scope) { |
|
5438 | var ctrl = this, |
||
5439 | oldIndex; |
||
5440 | ctrl.tabs = []; |
||
5441 | |||
5442 | ctrl.select = function(index, evt) { |
||
5443 | if (!destroyed) { |
||
5444 | var previousIndex = findTabIndex(oldIndex); |
||
5445 | var previousSelected = ctrl.tabs[previousIndex]; |
||
5446 | if (previousSelected) { |
||
5447 | previousSelected.tab.onDeselect({ |
||
5448 | $event: evt |
||
5449 | }); |
||
5450 | if (evt && evt.isDefaultPrevented()) { |
||
5451 | return; |
||
5452 | } |
||
5453 | previousSelected.tab.active = false; |
||
5454 | } |
||
5455 | |||
5456 | var selected = ctrl.tabs[index]; |
||
5457 | if (selected) { |
||
5458 | selected.tab.onSelect({ |
||
5459 | $event: evt |
||
5460 | }); |
||
5461 | selected.tab.active = true; |
||
5462 | ctrl.active = selected.index; |
||
5463 | oldIndex = selected.index; |
||
5464 | } else if (!selected && angular.isNumber(oldIndex)) { |
||
5465 | ctrl.active = null; |
||
5466 | oldIndex = null; |
||
5467 | } |
||
5468 | } |
||
5469 | }; |
||
5470 | |||
5471 | ctrl.addTab = function addTab(tab) { |
||
5472 | ctrl.tabs.push({ |
||
5473 | tab: tab, |
||
5474 | index: tab.index |
||
5475 | }); |
||
5476 | ctrl.tabs.sort(function(t1, t2) { |
||
5477 | if (t1.index > t2.index) { |
||
5478 | return 1; |
||
5479 | } |
||
5480 | |||
5481 | if (t1.index < t2.index) { |
||
5482 | return -1; |
||
5483 | } |
||
5484 | |||
5485 | return 0; |
||
5486 | }); |
||
5487 | |||
5488 | if (tab.index === ctrl.active || !angular.isNumber(ctrl.active) && ctrl.tabs.length === 1) { |
||
5489 | var newActiveIndex = findTabIndex(tab.index); |
||
5490 | ctrl.select(newActiveIndex); |
||
5491 | } |
||
5492 | }; |
||
5493 | |||
5494 | ctrl.removeTab = function removeTab(tab) { |
||
5495 | var index; |
||
5496 | for (var i = 0; i < ctrl.tabs.length; i++) { |
||
5497 | if (ctrl.tabs[i].tab === tab) { |
||
5498 | index = i; |
||
5499 | break; |
||
5500 | } |
||
5501 | } |
||
5502 | |||
5503 | if (ctrl.tabs[index].index === ctrl.active) { |
||
5504 | var newActiveTabIndex = index === ctrl.tabs.length - 1 ? |
||
5505 | index - 1 : index + 1 % ctrl.tabs.length; |
||
5506 | ctrl.select(newActiveTabIndex); |
||
5507 | } |
||
5508 | |||
5509 | ctrl.tabs.splice(index, 1); |
||
5510 | }; |
||
5511 | |||
5512 | $scope.$watch('tabset.active', function(val) { |
||
5513 | if (angular.isNumber(val) && val !== oldIndex) { |
||
5514 | ctrl.select(findTabIndex(val)); |
||
5515 | } |
||
5516 | }); |
||
5517 | |||
5518 | var destroyed; |
||
5519 | $scope.$on('$destroy', function() { |
||
5520 | destroyed = true; |
||
5521 | }); |
||
5522 | |||
5523 | function findTabIndex(index) { |
||
5524 | for (var i = 0; i < ctrl.tabs.length; i++) { |
||
5525 | if (ctrl.tabs[i].index === index) { |
||
5526 | return i; |
||
5527 | } |
||
5528 | } |
||
5529 | } |
||
5530 | }]) |
||
5531 | |||
5532 | View Code Duplication | .directive('uibTabset', function() { |
|
5533 | return { |
||
5534 | transclude: true, |
||
5535 | replace: true, |
||
5536 | scope: {}, |
||
5537 | bindToController: { |
||
5538 | active: '=?', |
||
5539 | type: '@' |
||
5540 | }, |
||
5541 | controller: 'UibTabsetController', |
||
5542 | controllerAs: 'tabset', |
||
5543 | templateUrl: function(element, attrs) { |
||
5544 | return attrs.templateUrl || 'uib/template/tabs/tabset.html'; |
||
5545 | }, |
||
5546 | link: function(scope, element, attrs) { |
||
5547 | scope.vertical = angular.isDefined(attrs.vertical) ? |
||
5548 | scope.$parent.$eval(attrs.vertical) : false; |
||
5549 | scope.justified = angular.isDefined(attrs.justified) ? |
||
5550 | scope.$parent.$eval(attrs.justified) : false; |
||
5551 | if (angular.isUndefined(attrs.active)) { |
||
5552 | scope.active = 0; |
||
5553 | } |
||
5554 | } |
||
5555 | }; |
||
5556 | }) |
||
5557 | |||
5558 | View Code Duplication | .directive('uibTab', ['$parse', function($parse) { |
|
5559 | return { |
||
5560 | require: '^uibTabset', |
||
5561 | replace: true, |
||
5562 | templateUrl: function(element, attrs) { |
||
5563 | return attrs.templateUrl || 'uib/template/tabs/tab.html'; |
||
5564 | }, |
||
5565 | transclude: true, |
||
5566 | scope: { |
||
5567 | heading: '@', |
||
5568 | index: '=?', |
||
5569 | classes: '@?', |
||
5570 | onSelect: '&select', //This callback is called in contentHeadingTransclude |
||
5571 | //once it inserts the tab's content into the dom |
||
5572 | onDeselect: '&deselect' |
||
5573 | }, |
||
5574 | controller: function() { |
||
5575 | //Empty controller so other directives can require being 'under' a tab |
||
5576 | }, |
||
5577 | controllerAs: 'tab', |
||
5578 | link: function(scope, elm, attrs, tabsetCtrl, transclude) { |
||
5579 | scope.disabled = false; |
||
5580 | if (attrs.disable) { |
||
5581 | scope.$parent.$watch($parse(attrs.disable), function(value) { |
||
5582 | scope.disabled = !! value; |
||
5583 | }); |
||
5584 | } |
||
5585 | |||
5586 | if (angular.isUndefined(attrs.index)) { |
||
5587 | if (tabsetCtrl.tabs && tabsetCtrl.tabs.length) { |
||
5588 | scope.index = Math.max.apply(null, tabsetCtrl.tabs.map(function(t) { return t.index; })) + 1; |
||
5589 | } else { |
||
5590 | scope.index = 0; |
||
5591 | } |
||
5592 | } |
||
5593 | |||
5594 | if (angular.isUndefined(attrs.classes)) { |
||
5595 | scope.classes = ''; |
||
5596 | } |
||
5597 | |||
5598 | scope.select = function(evt) { |
||
5599 | if (!scope.disabled) { |
||
5600 | var index; |
||
5601 | for (var i = 0; i < tabsetCtrl.tabs.length; i++) { |
||
5602 | if (tabsetCtrl.tabs[i].tab === scope) { |
||
5603 | index = i; |
||
5604 | break; |
||
5605 | } |
||
5606 | } |
||
5607 | |||
5608 | tabsetCtrl.select(index, evt); |
||
0 ignored issues
–
show
|
|||
5609 | } |
||
5610 | }; |
||
5611 | |||
5612 | tabsetCtrl.addTab(scope); |
||
5613 | scope.$on('$destroy', function() { |
||
5614 | tabsetCtrl.removeTab(scope); |
||
5615 | }); |
||
5616 | |||
5617 | //We need to transclude later, once the content container is ready. |
||
5618 | //when this link happens, we're inside a tab heading. |
||
5619 | scope.$transcludeFn = transclude; |
||
5620 | } |
||
5621 | }; |
||
5622 | }]) |
||
5623 | |||
5624 | .directive('uibTabHeadingTransclude', function() { |
||
5625 | return { |
||
5626 | restrict: 'A', |
||
5627 | require: '^uibTab', |
||
5628 | link: function(scope, elm) { |
||
5629 | scope.$watch('headingElement', function updateHeadingElement(heading) { |
||
5630 | if (heading) { |
||
5631 | elm.html(''); |
||
5632 | elm.append(heading); |
||
5633 | } |
||
5634 | }); |
||
5635 | } |
||
5636 | }; |
||
5637 | }) |
||
5638 | |||
5639 | View Code Duplication | .directive('uibTabContentTransclude', function() { |
|
5640 | return { |
||
5641 | restrict: 'A', |
||
5642 | require: '^uibTabset', |
||
5643 | link: function(scope, elm, attrs) { |
||
5644 | var tab = scope.$eval(attrs.uibTabContentTransclude).tab; |
||
5645 | |||
5646 | //Now our tab is ready to be transcluded: both the tab heading area |
||
5647 | //and the tab content area are loaded. Transclude 'em both. |
||
5648 | tab.$transcludeFn(tab.$parent, function(contents) { |
||
5649 | angular.forEach(contents, function(node) { |
||
5650 | if (isTabHeading(node)) { |
||
5651 | //Let tabHeadingTransclude know. |
||
5652 | tab.headingElement = node; |
||
5653 | } else { |
||
5654 | elm.append(node); |
||
5655 | } |
||
5656 | }); |
||
5657 | }); |
||
5658 | } |
||
5659 | }; |
||
5660 | |||
5661 | function isTabHeading(node) { |
||
5662 | return node.tagName && ( |
||
5663 | node.hasAttribute('uib-tab-heading') || |
||
5664 | node.hasAttribute('data-uib-tab-heading') || |
||
5665 | node.hasAttribute('x-uib-tab-heading') || |
||
5666 | node.tagName.toLowerCase() === 'uib-tab-heading' || |
||
5667 | node.tagName.toLowerCase() === 'data-uib-tab-heading' || |
||
5668 | node.tagName.toLowerCase() === 'x-uib-tab-heading' || |
||
5669 | node.tagName.toLowerCase() === 'uib:tab-heading' |
||
5670 | ); |
||
5671 | } |
||
5672 | }); |
||
5673 | |||
5674 | angular.module('ui.bootstrap.timepicker', []) |
||
5675 | |||
5676 | .constant('uibTimepickerConfig', { |
||
5677 | hourStep: 1, |
||
5678 | minuteStep: 1, |
||
5679 | secondStep: 1, |
||
5680 | showMeridian: true, |
||
5681 | showSeconds: false, |
||
5682 | meridians: null, |
||
5683 | readonlyInput: false, |
||
5684 | mousewheel: true, |
||
5685 | arrowkeys: true, |
||
5686 | showSpinners: true, |
||
5687 | templateUrl: 'uib/template/timepicker/timepicker.html' |
||
5688 | }) |
||
5689 | |||
5690 | View Code Duplication | .controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) { |
|
5691 | var selected = new Date(), |
||
5692 | watchers = [], |
||
5693 | ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl |
||
5694 | meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS, |
||
5695 | padHours = angular.isDefined($attrs.padHours) ? $scope.$parent.$eval($attrs.padHours) : true; |
||
5696 | |||
5697 | $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0; |
||
5698 | $element.removeAttr('tabindex'); |
||
5699 | |||
5700 | this.init = function(ngModelCtrl_, inputs) { |
||
5701 | ngModelCtrl = ngModelCtrl_; |
||
5702 | ngModelCtrl.$render = this.render; |
||
5703 | |||
5704 | ngModelCtrl.$formatters.unshift(function(modelValue) { |
||
5705 | return modelValue ? new Date(modelValue) : null; |
||
5706 | }); |
||
5707 | |||
5708 | var hoursInputEl = inputs.eq(0), |
||
5709 | minutesInputEl = inputs.eq(1), |
||
5710 | secondsInputEl = inputs.eq(2); |
||
5711 | |||
5712 | var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel; |
||
5713 | |||
5714 | if (mousewheel) { |
||
5715 | this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl); |
||
5716 | } |
||
5717 | |||
5718 | var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys; |
||
5719 | if (arrowkeys) { |
||
5720 | this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl); |
||
5721 | } |
||
5722 | |||
5723 | $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput; |
||
5724 | this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl); |
||
5725 | }; |
||
5726 | |||
5727 | var hourStep = timepickerConfig.hourStep; |
||
5728 | if ($attrs.hourStep) { |
||
5729 | watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) { |
||
5730 | hourStep = +value; |
||
5731 | })); |
||
5732 | } |
||
5733 | |||
5734 | var minuteStep = timepickerConfig.minuteStep; |
||
5735 | if ($attrs.minuteStep) { |
||
5736 | watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) { |
||
5737 | minuteStep = +value; |
||
5738 | })); |
||
5739 | } |
||
5740 | |||
5741 | var min; |
||
5742 | watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) { |
||
5743 | var dt = new Date(value); |
||
5744 | min = isNaN(dt) ? undefined : dt; |
||
5745 | })); |
||
5746 | |||
5747 | var max; |
||
5748 | watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) { |
||
5749 | var dt = new Date(value); |
||
5750 | max = isNaN(dt) ? undefined : dt; |
||
5751 | })); |
||
5752 | |||
5753 | var disabled = false; |
||
5754 | if ($attrs.ngDisabled) { |
||
5755 | watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) { |
||
5756 | disabled = value; |
||
5757 | })); |
||
5758 | } |
||
5759 | |||
5760 | $scope.noIncrementHours = function() { |
||
5761 | var incrementedSelected = addMinutes(selected, hourStep * 60); |
||
5762 | return disabled || incrementedSelected > max || |
||
5763 | incrementedSelected < selected && incrementedSelected < min; |
||
5764 | }; |
||
5765 | |||
5766 | $scope.noDecrementHours = function() { |
||
5767 | var decrementedSelected = addMinutes(selected, -hourStep * 60); |
||
5768 | return disabled || decrementedSelected < min || |
||
5769 | decrementedSelected > selected && decrementedSelected > max; |
||
5770 | }; |
||
5771 | |||
5772 | $scope.noIncrementMinutes = function() { |
||
5773 | var incrementedSelected = addMinutes(selected, minuteStep); |
||
5774 | return disabled || incrementedSelected > max || |
||
5775 | incrementedSelected < selected && incrementedSelected < min; |
||
5776 | }; |
||
5777 | |||
5778 | $scope.noDecrementMinutes = function() { |
||
5779 | var decrementedSelected = addMinutes(selected, -minuteStep); |
||
5780 | return disabled || decrementedSelected < min || |
||
5781 | decrementedSelected > selected && decrementedSelected > max; |
||
5782 | }; |
||
5783 | |||
5784 | $scope.noIncrementSeconds = function() { |
||
5785 | var incrementedSelected = addSeconds(selected, secondStep); |
||
5786 | return disabled || incrementedSelected > max || |
||
5787 | incrementedSelected < selected && incrementedSelected < min; |
||
5788 | }; |
||
5789 | |||
5790 | $scope.noDecrementSeconds = function() { |
||
5791 | var decrementedSelected = addSeconds(selected, -secondStep); |
||
5792 | return disabled || decrementedSelected < min || |
||
5793 | decrementedSelected > selected && decrementedSelected > max; |
||
5794 | }; |
||
5795 | |||
5796 | $scope.noToggleMeridian = function() { |
||
5797 | if (selected.getHours() < 12) { |
||
5798 | return disabled || addMinutes(selected, 12 * 60) > max; |
||
5799 | } |
||
5800 | |||
5801 | return disabled || addMinutes(selected, -12 * 60) < min; |
||
5802 | }; |
||
5803 | |||
5804 | var secondStep = timepickerConfig.secondStep; |
||
5805 | if ($attrs.secondStep) { |
||
5806 | watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) { |
||
5807 | secondStep = +value; |
||
5808 | })); |
||
5809 | } |
||
5810 | |||
5811 | $scope.showSeconds = timepickerConfig.showSeconds; |
||
5812 | if ($attrs.showSeconds) { |
||
5813 | watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) { |
||
5814 | $scope.showSeconds = !!value; |
||
5815 | })); |
||
5816 | } |
||
5817 | |||
5818 | // 12H / 24H mode |
||
5819 | $scope.showMeridian = timepickerConfig.showMeridian; |
||
5820 | if ($attrs.showMeridian) { |
||
5821 | watchers.push($scope.$parent.$watch($parse($attrs.showMeridian), function(value) { |
||
5822 | $scope.showMeridian = !!value; |
||
5823 | |||
5824 | if (ngModelCtrl.$error.time) { |
||
5825 | // Evaluate from template |
||
5826 | var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate(); |
||
5827 | if (angular.isDefined(hours) && angular.isDefined(minutes)) { |
||
5828 | selected.setHours(hours); |
||
5829 | refresh(); |
||
5830 | } |
||
5831 | } else { |
||
5832 | updateTemplate(); |
||
5833 | } |
||
5834 | })); |
||
5835 | } |
||
5836 | |||
5837 | // Get $scope.hours in 24H mode if valid |
||
5838 | function getHoursFromTemplate() { |
||
5839 | var hours = +$scope.hours; |
||
5840 | var valid = $scope.showMeridian ? hours > 0 && hours < 13 : |
||
5841 | hours >= 0 && hours < 24; |
||
5842 | if (!valid || $scope.hours === '') { |
||
5843 | return undefined; |
||
5844 | } |
||
5845 | |||
5846 | if ($scope.showMeridian) { |
||
5847 | if (hours === 12) { |
||
5848 | hours = 0; |
||
5849 | } |
||
5850 | if ($scope.meridian === meridians[1]) { |
||
5851 | hours = hours + 12; |
||
5852 | } |
||
5853 | } |
||
5854 | return hours; |
||
5855 | } |
||
5856 | |||
5857 | function getMinutesFromTemplate() { |
||
5858 | var minutes = +$scope.minutes; |
||
5859 | var valid = minutes >= 0 && minutes < 60; |
||
5860 | if (!valid || $scope.minutes === '') { |
||
5861 | return undefined; |
||
5862 | } |
||
5863 | return minutes; |
||
5864 | } |
||
5865 | |||
5866 | function getSecondsFromTemplate() { |
||
5867 | var seconds = +$scope.seconds; |
||
5868 | return seconds >= 0 && seconds < 60 ? seconds : undefined; |
||
5869 | } |
||
5870 | |||
5871 | function pad(value, noPad) { |
||
5872 | if (value === null) { |
||
5873 | return ''; |
||
5874 | } |
||
5875 | |||
5876 | return angular.isDefined(value) && value.toString().length < 2 && !noPad ? |
||
5877 | '0' + value : value.toString(); |
||
5878 | } |
||
5879 | |||
5880 | // Respond on mousewheel spin |
||
5881 | this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { |
||
5882 | var isScrollingUp = function(e) { |
||
5883 | if (e.originalEvent) { |
||
5884 | e = e.originalEvent; |
||
5885 | } |
||
5886 | //pick correct delta variable depending on event |
||
5887 | var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY; |
||
5888 | return e.detail || delta > 0; |
||
5889 | }; |
||
5890 | |||
5891 | hoursInputEl.bind('mousewheel wheel', function(e) { |
||
5892 | if (!disabled) { |
||
5893 | $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours()); |
||
5894 | } |
||
5895 | e.preventDefault(); |
||
5896 | }); |
||
5897 | |||
5898 | minutesInputEl.bind('mousewheel wheel', function(e) { |
||
5899 | if (!disabled) { |
||
5900 | $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes()); |
||
5901 | } |
||
5902 | e.preventDefault(); |
||
5903 | }); |
||
5904 | |||
5905 | secondsInputEl.bind('mousewheel wheel', function(e) { |
||
5906 | if (!disabled) { |
||
5907 | $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds()); |
||
5908 | } |
||
5909 | e.preventDefault(); |
||
5910 | }); |
||
5911 | }; |
||
5912 | |||
5913 | // Respond on up/down arrowkeys |
||
5914 | this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { |
||
5915 | hoursInputEl.bind('keydown', function(e) { |
||
5916 | if (!disabled) { |
||
5917 | if (e.which === 38) { // up |
||
5918 | e.preventDefault(); |
||
5919 | $scope.incrementHours(); |
||
5920 | $scope.$apply(); |
||
5921 | } else if (e.which === 40) { // down |
||
5922 | e.preventDefault(); |
||
5923 | $scope.decrementHours(); |
||
5924 | $scope.$apply(); |
||
5925 | } |
||
5926 | } |
||
5927 | }); |
||
5928 | |||
5929 | minutesInputEl.bind('keydown', function(e) { |
||
5930 | if (!disabled) { |
||
5931 | if (e.which === 38) { // up |
||
5932 | e.preventDefault(); |
||
5933 | $scope.incrementMinutes(); |
||
5934 | $scope.$apply(); |
||
5935 | } else if (e.which === 40) { // down |
||
5936 | e.preventDefault(); |
||
5937 | $scope.decrementMinutes(); |
||
5938 | $scope.$apply(); |
||
5939 | } |
||
5940 | } |
||
5941 | }); |
||
5942 | |||
5943 | secondsInputEl.bind('keydown', function(e) { |
||
5944 | if (!disabled) { |
||
5945 | if (e.which === 38) { // up |
||
5946 | e.preventDefault(); |
||
5947 | $scope.incrementSeconds(); |
||
5948 | $scope.$apply(); |
||
5949 | } else if (e.which === 40) { // down |
||
5950 | e.preventDefault(); |
||
5951 | $scope.decrementSeconds(); |
||
5952 | $scope.$apply(); |
||
5953 | } |
||
5954 | } |
||
5955 | }); |
||
5956 | }; |
||
5957 | |||
5958 | this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { |
||
5959 | if ($scope.readonlyInput) { |
||
5960 | $scope.updateHours = angular.noop; |
||
5961 | $scope.updateMinutes = angular.noop; |
||
5962 | $scope.updateSeconds = angular.noop; |
||
5963 | return; |
||
5964 | } |
||
5965 | |||
5966 | var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) { |
||
5967 | ngModelCtrl.$setViewValue(null); |
||
5968 | ngModelCtrl.$setValidity('time', false); |
||
5969 | if (angular.isDefined(invalidHours)) { |
||
5970 | $scope.invalidHours = invalidHours; |
||
5971 | } |
||
5972 | |||
5973 | if (angular.isDefined(invalidMinutes)) { |
||
5974 | $scope.invalidMinutes = invalidMinutes; |
||
5975 | } |
||
5976 | |||
5977 | if (angular.isDefined(invalidSeconds)) { |
||
5978 | $scope.invalidSeconds = invalidSeconds; |
||
5979 | } |
||
5980 | }; |
||
5981 | |||
5982 | $scope.updateHours = function() { |
||
5983 | var hours = getHoursFromTemplate(), |
||
5984 | minutes = getMinutesFromTemplate(); |
||
5985 | |||
5986 | ngModelCtrl.$setDirty(); |
||
5987 | |||
5988 | if (angular.isDefined(hours) && angular.isDefined(minutes)) { |
||
5989 | selected.setHours(hours); |
||
5990 | selected.setMinutes(minutes); |
||
5991 | if (selected < min || selected > max) { |
||
5992 | invalidate(true); |
||
5993 | } else { |
||
5994 | refresh('h'); |
||
5995 | } |
||
5996 | } else { |
||
5997 | invalidate(true); |
||
5998 | } |
||
5999 | }; |
||
6000 | |||
6001 | hoursInputEl.bind('blur', function(e) { |
||
6002 | ngModelCtrl.$setTouched(); |
||
6003 | if (modelIsEmpty()) { |
||
6004 | makeValid(); |
||
6005 | } else if ($scope.hours === null || $scope.hours === '') { |
||
6006 | invalidate(true); |
||
6007 | } else if (!$scope.invalidHours && $scope.hours < 10) { |
||
6008 | $scope.$apply(function() { |
||
6009 | $scope.hours = pad($scope.hours, !padHours); |
||
6010 | }); |
||
6011 | } |
||
6012 | }); |
||
6013 | |||
6014 | $scope.updateMinutes = function() { |
||
6015 | var minutes = getMinutesFromTemplate(), |
||
6016 | hours = getHoursFromTemplate(); |
||
6017 | |||
6018 | ngModelCtrl.$setDirty(); |
||
6019 | |||
6020 | if (angular.isDefined(minutes) && angular.isDefined(hours)) { |
||
6021 | selected.setHours(hours); |
||
6022 | selected.setMinutes(minutes); |
||
6023 | if (selected < min || selected > max) { |
||
6024 | invalidate(undefined, true); |
||
6025 | } else { |
||
6026 | refresh('m'); |
||
6027 | } |
||
6028 | } else { |
||
6029 | invalidate(undefined, true); |
||
6030 | } |
||
6031 | }; |
||
6032 | |||
6033 | minutesInputEl.bind('blur', function(e) { |
||
6034 | ngModelCtrl.$setTouched(); |
||
6035 | if (modelIsEmpty()) { |
||
6036 | makeValid(); |
||
6037 | } else if ($scope.minutes === null) { |
||
6038 | invalidate(undefined, true); |
||
6039 | } else if (!$scope.invalidMinutes && $scope.minutes < 10) { |
||
6040 | $scope.$apply(function() { |
||
6041 | $scope.minutes = pad($scope.minutes); |
||
6042 | }); |
||
6043 | } |
||
6044 | }); |
||
6045 | |||
6046 | $scope.updateSeconds = function() { |
||
6047 | var seconds = getSecondsFromTemplate(); |
||
6048 | |||
6049 | ngModelCtrl.$setDirty(); |
||
6050 | |||
6051 | if (angular.isDefined(seconds)) { |
||
6052 | selected.setSeconds(seconds); |
||
6053 | refresh('s'); |
||
6054 | } else { |
||
6055 | invalidate(undefined, undefined, true); |
||
6056 | } |
||
6057 | }; |
||
6058 | |||
6059 | secondsInputEl.bind('blur', function(e) { |
||
6060 | if (modelIsEmpty()) { |
||
6061 | makeValid(); |
||
6062 | } else if (!$scope.invalidSeconds && $scope.seconds < 10) { |
||
6063 | $scope.$apply( function() { |
||
6064 | $scope.seconds = pad($scope.seconds); |
||
6065 | }); |
||
6066 | } |
||
6067 | }); |
||
6068 | |||
6069 | }; |
||
6070 | |||
6071 | this.render = function() { |
||
6072 | var date = ngModelCtrl.$viewValue; |
||
6073 | |||
6074 | if (isNaN(date)) { |
||
6075 | ngModelCtrl.$setValidity('time', false); |
||
6076 | $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.'); |
||
6077 | } else { |
||
6078 | if (date) { |
||
6079 | selected = date; |
||
6080 | } |
||
6081 | |||
6082 | if (selected < min || selected > max) { |
||
6083 | ngModelCtrl.$setValidity('time', false); |
||
6084 | $scope.invalidHours = true; |
||
6085 | $scope.invalidMinutes = true; |
||
6086 | } else { |
||
6087 | makeValid(); |
||
6088 | } |
||
6089 | updateTemplate(); |
||
6090 | } |
||
6091 | }; |
||
6092 | |||
6093 | // Call internally when we know that model is valid. |
||
6094 | function refresh(keyboardChange) { |
||
6095 | makeValid(); |
||
6096 | ngModelCtrl.$setViewValue(new Date(selected)); |
||
6097 | updateTemplate(keyboardChange); |
||
6098 | } |
||
6099 | |||
6100 | function makeValid() { |
||
6101 | ngModelCtrl.$setValidity('time', true); |
||
6102 | $scope.invalidHours = false; |
||
6103 | $scope.invalidMinutes = false; |
||
6104 | $scope.invalidSeconds = false; |
||
6105 | } |
||
6106 | |||
6107 | function updateTemplate(keyboardChange) { |
||
6108 | if (!ngModelCtrl.$modelValue) { |
||
6109 | $scope.hours = null; |
||
6110 | $scope.minutes = null; |
||
6111 | $scope.seconds = null; |
||
6112 | $scope.meridian = meridians[0]; |
||
6113 | } else { |
||
6114 | var hours = selected.getHours(), |
||
6115 | minutes = selected.getMinutes(), |
||
6116 | seconds = selected.getSeconds(); |
||
6117 | |||
6118 | if ($scope.showMeridian) { |
||
6119 | hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system |
||
6120 | } |
||
6121 | |||
6122 | $scope.hours = keyboardChange === 'h' ? hours : pad(hours, !padHours); |
||
6123 | if (keyboardChange !== 'm') { |
||
6124 | $scope.minutes = pad(minutes); |
||
6125 | } |
||
6126 | $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; |
||
6127 | |||
6128 | if (keyboardChange !== 's') { |
||
6129 | $scope.seconds = pad(seconds); |
||
6130 | } |
||
6131 | $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; |
||
6132 | } |
||
6133 | } |
||
6134 | |||
6135 | function addSecondsToSelected(seconds) { |
||
6136 | selected = addSeconds(selected, seconds); |
||
6137 | refresh(); |
||
6138 | } |
||
6139 | |||
6140 | function addMinutes(selected, minutes) { |
||
6141 | return addSeconds(selected, minutes*60); |
||
6142 | } |
||
6143 | |||
6144 | function addSeconds(date, seconds) { |
||
6145 | var dt = new Date(date.getTime() + seconds * 1000); |
||
6146 | var newDate = new Date(date); |
||
6147 | newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds()); |
||
6148 | return newDate; |
||
6149 | } |
||
6150 | |||
6151 | function modelIsEmpty() { |
||
6152 | return ($scope.hours === null || $scope.hours === '') && |
||
6153 | ($scope.minutes === null || $scope.minutes === '') && |
||
6154 | (!$scope.showSeconds || $scope.showSeconds && ($scope.seconds === null || $scope.seconds === '')); |
||
6155 | } |
||
6156 | |||
6157 | $scope.showSpinners = angular.isDefined($attrs.showSpinners) ? |
||
6158 | $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; |
||
6159 | |||
6160 | $scope.incrementHours = function() { |
||
6161 | if (!$scope.noIncrementHours()) { |
||
6162 | addSecondsToSelected(hourStep * 60 * 60); |
||
6163 | } |
||
6164 | }; |
||
6165 | |||
6166 | $scope.decrementHours = function() { |
||
6167 | if (!$scope.noDecrementHours()) { |
||
6168 | addSecondsToSelected(-hourStep * 60 * 60); |
||
6169 | } |
||
6170 | }; |
||
6171 | |||
6172 | $scope.incrementMinutes = function() { |
||
6173 | if (!$scope.noIncrementMinutes()) { |
||
6174 | addSecondsToSelected(minuteStep * 60); |
||
6175 | } |
||
6176 | }; |
||
6177 | |||
6178 | $scope.decrementMinutes = function() { |
||
6179 | if (!$scope.noDecrementMinutes()) { |
||
6180 | addSecondsToSelected(-minuteStep * 60); |
||
6181 | } |
||
6182 | }; |
||
6183 | |||
6184 | $scope.incrementSeconds = function() { |
||
6185 | if (!$scope.noIncrementSeconds()) { |
||
6186 | addSecondsToSelected(secondStep); |
||
6187 | } |
||
6188 | }; |
||
6189 | |||
6190 | $scope.decrementSeconds = function() { |
||
6191 | if (!$scope.noDecrementSeconds()) { |
||
6192 | addSecondsToSelected(-secondStep); |
||
6193 | } |
||
6194 | }; |
||
6195 | |||
6196 | $scope.toggleMeridian = function() { |
||
6197 | var minutes = getMinutesFromTemplate(), |
||
6198 | hours = getHoursFromTemplate(); |
||
6199 | |||
6200 | if (!$scope.noToggleMeridian()) { |
||
6201 | if (angular.isDefined(minutes) && angular.isDefined(hours)) { |
||
6202 | addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60)); |
||
6203 | } else { |
||
6204 | $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0]; |
||
6205 | } |
||
6206 | } |
||
6207 | }; |
||
6208 | |||
6209 | $scope.blur = function() { |
||
6210 | ngModelCtrl.$setTouched(); |
||
6211 | }; |
||
6212 | |||
6213 | $scope.$on('$destroy', function() { |
||
6214 | while (watchers.length) { |
||
6215 | watchers.shift()(); |
||
6216 | } |
||
6217 | }); |
||
6218 | }]) |
||
6219 | |||
6220 | View Code Duplication | .directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) { |
|
6221 | return { |
||
6222 | require: ['uibTimepicker', '?^ngModel'], |
||
6223 | controller: 'UibTimepickerController', |
||
6224 | controllerAs: 'timepicker', |
||
6225 | replace: true, |
||
6226 | scope: {}, |
||
6227 | templateUrl: function(element, attrs) { |
||
6228 | return attrs.templateUrl || uibTimepickerConfig.templateUrl; |
||
6229 | }, |
||
6230 | link: function(scope, element, attrs, ctrls) { |
||
6231 | var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; |
||
6232 | |||
6233 | if (ngModelCtrl) { |
||
6234 | timepickerCtrl.init(ngModelCtrl, element.find('input')); |
||
6235 | } |
||
6236 | } |
||
6237 | }; |
||
6238 | }]); |
||
6239 | |||
6240 | angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position']) |
||
6241 | |||
6242 | /** |
||
6243 | * A helper service that can parse typeahead's syntax (string provided by users) |
||
6244 | * Extracted to a separate service for ease of unit testing |
||
6245 | */ |
||
6246 | View Code Duplication | .factory('uibTypeaheadParser', ['$parse', function($parse) { |
|
6247 | // 00000111000000000000022200000000000000003333333333333330000000000044000 |
||
6248 | var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/; |
||
6249 | return { |
||
6250 | parse: function(input) { |
||
6251 | var match = input.match(TYPEAHEAD_REGEXP); |
||
6252 | if (!match) { |
||
6253 | throw new Error( |
||
6254 | 'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' + |
||
6255 | ' but got "' + input + '".'); |
||
6256 | } |
||
6257 | |||
6258 | return { |
||
6259 | itemName: match[3], |
||
6260 | source: $parse(match[4]), |
||
6261 | viewMapper: $parse(match[2] || match[1]), |
||
6262 | modelMapper: $parse(match[1]) |
||
6263 | }; |
||
6264 | } |
||
6265 | }; |
||
6266 | }]) |
||
6267 | |||
6268 | .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser', |
||
6269 | View Code Duplication | function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) { |
|
6270 | var HOT_KEYS = [9, 13, 27, 38, 40]; |
||
6271 | var eventDebounceTime = 200; |
||
6272 | var modelCtrl, ngModelOptions; |
||
6273 | //SUPPORTED ATTRIBUTES (OPTIONS) |
||
6274 | |||
6275 | //minimal no of characters that needs to be entered before typeahead kicks-in |
||
6276 | var minLength = originalScope.$eval(attrs.typeaheadMinLength); |
||
6277 | if (!minLength && minLength !== 0) { |
||
6278 | minLength = 1; |
||
6279 | } |
||
6280 | |||
6281 | originalScope.$watch(attrs.typeaheadMinLength, function (newVal) { |
||
6282 | minLength = !newVal && newVal !== 0 ? 1 : newVal; |
||
6283 | }); |
||
6284 | |||
6285 | //minimal wait time after last character typed before typeahead kicks-in |
||
6286 | var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; |
||
6287 | |||
6288 | //should it restrict model values to the ones selected from the popup only? |
||
6289 | var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; |
||
6290 | originalScope.$watch(attrs.typeaheadEditable, function (newVal) { |
||
6291 | isEditable = newVal !== false; |
||
6292 | }); |
||
6293 | |||
6294 | //binding to a variable that indicates if matches are being retrieved asynchronously |
||
6295 | var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; |
||
6296 | |||
6297 | //a callback executed when a match is selected |
||
6298 | var onSelectCallback = $parse(attrs.typeaheadOnSelect); |
||
6299 | |||
6300 | //should it select highlighted popup value when losing focus? |
||
6301 | var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false; |
||
6302 | |||
6303 | //binding to a variable that indicates if there were no results after the query is completed |
||
6304 | var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop; |
||
6305 | |||
6306 | var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; |
||
6307 | |||
6308 | var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; |
||
6309 | |||
6310 | var appendTo = attrs.typeaheadAppendTo ? |
||
6311 | originalScope.$eval(attrs.typeaheadAppendTo) : null; |
||
6312 | |||
6313 | var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; |
||
6314 | |||
6315 | //If input matches an item of the list exactly, select it automatically |
||
6316 | var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; |
||
6317 | |||
6318 | //binding to a variable that indicates if dropdown is open |
||
6319 | var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop; |
||
6320 | |||
6321 | var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false; |
||
6322 | |||
6323 | //INTERNAL VARIABLES |
||
6324 | |||
6325 | //model setter executed upon match selection |
||
6326 | var parsedModel = $parse(attrs.ngModel); |
||
6327 | var invokeModelSetter = $parse(attrs.ngModel + '($$$p)'); |
||
6328 | var $setModelValue = function(scope, newValue) { |
||
6329 | if (angular.isFunction(parsedModel(originalScope)) && |
||
6330 | ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) { |
||
6331 | return invokeModelSetter(scope, {$$$p: newValue}); |
||
6332 | } |
||
6333 | |||
6334 | return parsedModel.assign(scope, newValue); |
||
6335 | }; |
||
6336 | |||
6337 | //expressions used by typeahead |
||
6338 | var parserResult = typeaheadParser.parse(attrs.uibTypeahead); |
||
6339 | |||
6340 | var hasFocus; |
||
6341 | |||
6342 | //Used to avoid bug in iOS webview where iOS keyboard does not fire |
||
6343 | //mousedown & mouseup events |
||
6344 | //Issue #3699 |
||
6345 | var selected; |
||
6346 | |||
6347 | //create a child scope for the typeahead directive so we are not polluting original scope |
||
6348 | //with typeahead-specific data (matches, query etc.) |
||
6349 | var scope = originalScope.$new(); |
||
6350 | var offDestroy = originalScope.$on('$destroy', function() { |
||
6351 | scope.$destroy(); |
||
6352 | }); |
||
6353 | scope.$on('$destroy', offDestroy); |
||
6354 | |||
6355 | // WAI-ARIA |
||
6356 | var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000); |
||
6357 | element.attr({ |
||
6358 | 'aria-autocomplete': 'list', |
||
6359 | 'aria-expanded': false, |
||
6360 | 'aria-owns': popupId |
||
6361 | }); |
||
6362 | |||
6363 | var inputsContainer, hintInputElem; |
||
6364 | //add read-only input to show hint |
||
6365 | if (showHint) { |
||
6366 | inputsContainer = angular.element('<div></div>'); |
||
6367 | inputsContainer.css('position', 'relative'); |
||
6368 | element.after(inputsContainer); |
||
6369 | hintInputElem = element.clone(); |
||
6370 | hintInputElem.attr('placeholder', ''); |
||
6371 | hintInputElem.attr('tabindex', '-1'); |
||
6372 | hintInputElem.val(''); |
||
6373 | hintInputElem.css({ |
||
6374 | 'position': 'absolute', |
||
6375 | 'top': '0px', |
||
6376 | 'left': '0px', |
||
6377 | 'border-color': 'transparent', |
||
6378 | 'box-shadow': 'none', |
||
6379 | 'opacity': 1, |
||
6380 | 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)', |
||
6381 | 'color': '#999' |
||
6382 | }); |
||
6383 | element.css({ |
||
6384 | 'position': 'relative', |
||
6385 | 'vertical-align': 'top', |
||
6386 | 'background-color': 'transparent' |
||
6387 | }); |
||
6388 | inputsContainer.append(hintInputElem); |
||
6389 | hintInputElem.after(element); |
||
6390 | } |
||
6391 | |||
6392 | //pop-up element used to display matches |
||
6393 | var popUpEl = angular.element('<div uib-typeahead-popup></div>'); |
||
6394 | popUpEl.attr({ |
||
6395 | id: popupId, |
||
6396 | matches: 'matches', |
||
6397 | active: 'activeIdx', |
||
6398 | select: 'select(activeIdx, evt)', |
||
6399 | 'move-in-progress': 'moveInProgress', |
||
6400 | query: 'query', |
||
6401 | position: 'position', |
||
6402 | 'assign-is-open': 'assignIsOpen(isOpen)', |
||
6403 | debounce: 'debounceUpdate' |
||
6404 | }); |
||
6405 | //custom item template |
||
6406 | if (angular.isDefined(attrs.typeaheadTemplateUrl)) { |
||
6407 | popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); |
||
6408 | } |
||
6409 | |||
6410 | if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) { |
||
6411 | popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl); |
||
6412 | } |
||
6413 | |||
6414 | var resetHint = function() { |
||
6415 | if (showHint) { |
||
6416 | hintInputElem.val(''); |
||
6417 | } |
||
6418 | }; |
||
6419 | |||
6420 | var resetMatches = function() { |
||
6421 | scope.matches = []; |
||
6422 | scope.activeIdx = -1; |
||
6423 | element.attr('aria-expanded', false); |
||
6424 | resetHint(); |
||
6425 | }; |
||
6426 | |||
6427 | var getMatchId = function(index) { |
||
6428 | return popupId + '-option-' + index; |
||
6429 | }; |
||
6430 | |||
6431 | // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead. |
||
6432 | // This attribute is added or removed automatically when the `activeIdx` changes. |
||
6433 | scope.$watch('activeIdx', function(index) { |
||
6434 | if (index < 0) { |
||
6435 | element.removeAttr('aria-activedescendant'); |
||
6436 | } else { |
||
6437 | element.attr('aria-activedescendant', getMatchId(index)); |
||
6438 | } |
||
6439 | }); |
||
6440 | |||
6441 | var inputIsExactMatch = function(inputValue, index) { |
||
6442 | if (scope.matches.length > index && inputValue) { |
||
6443 | return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); |
||
6444 | } |
||
6445 | |||
6446 | return false; |
||
6447 | }; |
||
6448 | |||
6449 | var getMatchesAsync = function(inputValue, evt) { |
||
6450 | var locals = {$viewValue: inputValue}; |
||
6451 | isLoadingSetter(originalScope, true); |
||
6452 | isNoResultsSetter(originalScope, false); |
||
6453 | $q.when(parserResult.source(originalScope, locals)).then(function(matches) { |
||
6454 | //it might happen that several async queries were in progress if a user were typing fast |
||
6455 | //but we are interested only in responses that correspond to the current view value |
||
6456 | var onCurrentRequest = inputValue === modelCtrl.$viewValue; |
||
6457 | if (onCurrentRequest && hasFocus) { |
||
6458 | if (matches && matches.length > 0) { |
||
6459 | scope.activeIdx = focusFirst ? 0 : -1; |
||
6460 | isNoResultsSetter(originalScope, false); |
||
6461 | scope.matches.length = 0; |
||
6462 | |||
6463 | //transform labels |
||
6464 | for (var i = 0; i < matches.length; i++) { |
||
6465 | locals[parserResult.itemName] = matches[i]; |
||
6466 | scope.matches.push({ |
||
6467 | id: getMatchId(i), |
||
6468 | label: parserResult.viewMapper(scope, locals), |
||
6469 | model: matches[i] |
||
6470 | }); |
||
6471 | } |
||
6472 | |||
6473 | scope.query = inputValue; |
||
6474 | //position pop-up with matches - we need to re-calculate its position each time we are opening a window |
||
6475 | //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page |
||
6476 | //due to other elements being rendered |
||
6477 | recalculatePosition(); |
||
6478 | |||
6479 | element.attr('aria-expanded', true); |
||
6480 | |||
6481 | //Select the single remaining option if user input matches |
||
6482 | if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) { |
||
6483 | if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { |
||
6484 | $$debounce(function() { |
||
6485 | scope.select(0, evt); |
||
6486 | }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); |
||
6487 | } else { |
||
6488 | scope.select(0, evt); |
||
6489 | } |
||
6490 | } |
||
6491 | |||
6492 | if (showHint) { |
||
6493 | var firstLabel = scope.matches[0].label; |
||
6494 | if (angular.isString(inputValue) && |
||
6495 | inputValue.length > 0 && |
||
6496 | firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) { |
||
6497 | hintInputElem.val(inputValue + firstLabel.slice(inputValue.length)); |
||
6498 | } else { |
||
6499 | hintInputElem.val(''); |
||
6500 | } |
||
6501 | } |
||
6502 | } else { |
||
6503 | resetMatches(); |
||
6504 | isNoResultsSetter(originalScope, true); |
||
6505 | } |
||
6506 | } |
||
6507 | if (onCurrentRequest) { |
||
6508 | isLoadingSetter(originalScope, false); |
||
6509 | } |
||
6510 | }, function() { |
||
6511 | resetMatches(); |
||
6512 | isLoadingSetter(originalScope, false); |
||
6513 | isNoResultsSetter(originalScope, true); |
||
6514 | }); |
||
6515 | }; |
||
6516 | |||
6517 | // bind events only if appendToBody params exist - performance feature |
||
6518 | if (appendToBody) { |
||
6519 | angular.element($window).on('resize', fireRecalculating); |
||
6520 | $document.find('body').on('scroll', fireRecalculating); |
||
6521 | } |
||
6522 | |||
6523 | // Declare the debounced function outside recalculating for |
||
6524 | // proper debouncing |
||
6525 | var debouncedRecalculate = $$debounce(function() { |
||
6526 | // if popup is visible |
||
6527 | if (scope.matches.length) { |
||
6528 | recalculatePosition(); |
||
6529 | } |
||
6530 | |||
6531 | scope.moveInProgress = false; |
||
6532 | }, eventDebounceTime); |
||
6533 | |||
6534 | // Default progress type |
||
6535 | scope.moveInProgress = false; |
||
6536 | |||
6537 | function fireRecalculating() { |
||
6538 | if (!scope.moveInProgress) { |
||
6539 | scope.moveInProgress = true; |
||
6540 | scope.$digest(); |
||
6541 | } |
||
6542 | |||
6543 | debouncedRecalculate(); |
||
6544 | } |
||
6545 | |||
6546 | // recalculate actual position and set new values to scope |
||
6547 | // after digest loop is popup in right position |
||
6548 | function recalculatePosition() { |
||
6549 | scope.position = appendToBody ? $position.offset(element) : $position.position(element); |
||
6550 | scope.position.top += element.prop('offsetHeight'); |
||
6551 | } |
||
6552 | |||
6553 | //we need to propagate user's query so we can higlight matches |
||
6554 | scope.query = undefined; |
||
6555 | |||
6556 | //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later |
||
6557 | var timeoutPromise; |
||
6558 | |||
6559 | var scheduleSearchWithTimeout = function(inputValue) { |
||
6560 | timeoutPromise = $timeout(function() { |
||
6561 | getMatchesAsync(inputValue); |
||
6562 | }, waitTime); |
||
6563 | }; |
||
6564 | |||
6565 | var cancelPreviousTimeout = function() { |
||
6566 | if (timeoutPromise) { |
||
6567 | $timeout.cancel(timeoutPromise); |
||
6568 | } |
||
6569 | }; |
||
6570 | |||
6571 | resetMatches(); |
||
6572 | |||
6573 | scope.assignIsOpen = function (isOpen) { |
||
6574 | isOpenSetter(originalScope, isOpen); |
||
6575 | }; |
||
6576 | |||
6577 | scope.select = function(activeIdx, evt) { |
||
6578 | //called from within the $digest() cycle |
||
6579 | var locals = {}; |
||
6580 | var model, item; |
||
6581 | |||
6582 | selected = true; |
||
6583 | locals[parserResult.itemName] = item = scope.matches[activeIdx].model; |
||
6584 | model = parserResult.modelMapper(originalScope, locals); |
||
6585 | $setModelValue(originalScope, model); |
||
6586 | modelCtrl.$setValidity('editable', true); |
||
6587 | modelCtrl.$setValidity('parse', true); |
||
6588 | |||
6589 | onSelectCallback(originalScope, { |
||
6590 | $item: item, |
||
6591 | $model: model, |
||
6592 | $label: parserResult.viewMapper(originalScope, locals), |
||
6593 | $event: evt |
||
6594 | }); |
||
6595 | |||
6596 | resetMatches(); |
||
6597 | |||
6598 | //return focus to the input element if a match was selected via a mouse click event |
||
6599 | // use timeout to avoid $rootScope:inprog error |
||
6600 | if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) { |
||
6601 | $timeout(function() { element[0].focus(); }, 0, false); |
||
6602 | } |
||
6603 | }; |
||
6604 | |||
6605 | //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) |
||
6606 | element.on('keydown', function(evt) { |
||
6607 | //typeahead is open and an "interesting" key was pressed |
||
6608 | if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { |
||
6609 | return; |
||
6610 | } |
||
6611 | |||
6612 | /** |
||
6613 | * if there's nothing selected (i.e. focusFirst) and enter or tab is hit |
||
6614 | * or |
||
6615 | * shift + tab is pressed to bring focus to the previous element |
||
6616 | * then clear the results |
||
6617 | */ |
||
6618 | if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13) || evt.which === 9 && !!evt.shiftKey) { |
||
6619 | resetMatches(); |
||
6620 | scope.$digest(); |
||
6621 | return; |
||
6622 | } |
||
6623 | |||
6624 | evt.preventDefault(); |
||
6625 | var target; |
||
6626 | switch (evt.which) { |
||
6627 | case 9: |
||
6628 | case 13: |
||
6629 | scope.$apply(function () { |
||
6630 | if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { |
||
6631 | $$debounce(function() { |
||
6632 | scope.select(scope.activeIdx, evt); |
||
6633 | }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); |
||
6634 | } else { |
||
6635 | scope.select(scope.activeIdx, evt); |
||
6636 | } |
||
6637 | }); |
||
6638 | break; |
||
6639 | case 27: |
||
6640 | evt.stopPropagation(); |
||
6641 | |||
6642 | resetMatches(); |
||
6643 | originalScope.$digest(); |
||
6644 | break; |
||
6645 | case 38: |
||
6646 | scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; |
||
6647 | scope.$digest(); |
||
6648 | target = popUpEl.find('li')[scope.activeIdx]; |
||
6649 | target.parentNode.scrollTop = target.offsetTop; |
||
6650 | break; |
||
6651 | case 40: |
||
6652 | scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; |
||
6653 | scope.$digest(); |
||
6654 | target = popUpEl.find('li')[scope.activeIdx]; |
||
6655 | target.parentNode.scrollTop = target.offsetTop; |
||
6656 | break; |
||
6657 | } |
||
6658 | }); |
||
6659 | |||
6660 | element.bind('focus', function (evt) { |
||
6661 | hasFocus = true; |
||
6662 | if (minLength === 0 && !modelCtrl.$viewValue) { |
||
6663 | $timeout(function() { |
||
6664 | getMatchesAsync(modelCtrl.$viewValue, evt); |
||
6665 | }, 0); |
||
6666 | } |
||
6667 | }); |
||
6668 | |||
6669 | element.bind('blur', function(evt) { |
||
6670 | if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { |
||
6671 | selected = true; |
||
6672 | scope.$apply(function() { |
||
6673 | if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) { |
||
6674 | $$debounce(function() { |
||
6675 | scope.select(scope.activeIdx, evt); |
||
6676 | }, scope.debounceUpdate.blur); |
||
6677 | } else { |
||
6678 | scope.select(scope.activeIdx, evt); |
||
6679 | } |
||
6680 | }); |
||
6681 | } |
||
6682 | if (!isEditable && modelCtrl.$error.editable) { |
||
6683 | modelCtrl.$setViewValue(); |
||
6684 | // Reset validity as we are clearing |
||
6685 | modelCtrl.$setValidity('editable', true); |
||
6686 | modelCtrl.$setValidity('parse', true); |
||
6687 | element.val(''); |
||
6688 | } |
||
6689 | hasFocus = false; |
||
6690 | selected = false; |
||
6691 | }); |
||
6692 | |||
6693 | // Keep reference to click handler to unbind it. |
||
6694 | var dismissClickHandler = function(evt) { |
||
6695 | // Issue #3973 |
||
6696 | // Firefox treats right click as a click on document |
||
6697 | if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) { |
||
6698 | resetMatches(); |
||
6699 | if (!$rootScope.$$phase) { |
||
6700 | originalScope.$digest(); |
||
6701 | } |
||
6702 | } |
||
6703 | }; |
||
6704 | |||
6705 | $document.on('click', dismissClickHandler); |
||
6706 | |||
6707 | originalScope.$on('$destroy', function() { |
||
6708 | $document.off('click', dismissClickHandler); |
||
6709 | if (appendToBody || appendTo) { |
||
6710 | $popup.remove(); |
||
6711 | } |
||
6712 | |||
6713 | if (appendToBody) { |
||
6714 | angular.element($window).off('resize', fireRecalculating); |
||
6715 | $document.find('body').off('scroll', fireRecalculating); |
||
6716 | } |
||
6717 | // Prevent jQuery cache memory leak |
||
6718 | popUpEl.remove(); |
||
6719 | |||
6720 | if (showHint) { |
||
6721 | inputsContainer.remove(); |
||
6722 | } |
||
6723 | }); |
||
6724 | |||
6725 | var $popup = $compile(popUpEl)(scope); |
||
6726 | |||
6727 | if (appendToBody) { |
||
6728 | $document.find('body').append($popup); |
||
6729 | } else if (appendTo) { |
||
6730 | angular.element(appendTo).eq(0).append($popup); |
||
6731 | } else { |
||
6732 | element.after($popup); |
||
6733 | } |
||
6734 | |||
6735 | this.init = function(_modelCtrl, _ngModelOptions) { |
||
6736 | modelCtrl = _modelCtrl; |
||
6737 | ngModelOptions = _ngModelOptions; |
||
6738 | |||
6739 | scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope); |
||
6740 | |||
6741 | //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM |
||
6742 | //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue |
||
6743 | modelCtrl.$parsers.unshift(function(inputValue) { |
||
6744 | hasFocus = true; |
||
6745 | |||
6746 | if (minLength === 0 || inputValue && inputValue.length >= minLength) { |
||
6747 | if (waitTime > 0) { |
||
6748 | cancelPreviousTimeout(); |
||
6749 | scheduleSearchWithTimeout(inputValue); |
||
6750 | } else { |
||
6751 | getMatchesAsync(inputValue); |
||
6752 | } |
||
6753 | } else { |
||
6754 | isLoadingSetter(originalScope, false); |
||
6755 | cancelPreviousTimeout(); |
||
6756 | resetMatches(); |
||
6757 | } |
||
6758 | |||
6759 | if (isEditable) { |
||
6760 | return inputValue; |
||
6761 | } |
||
6762 | |||
6763 | if (!inputValue) { |
||
6764 | // Reset in case user had typed something previously. |
||
6765 | modelCtrl.$setValidity('editable', true); |
||
6766 | return null; |
||
6767 | } |
||
6768 | |||
6769 | modelCtrl.$setValidity('editable', false); |
||
6770 | return undefined; |
||
6771 | }); |
||
6772 | |||
6773 | modelCtrl.$formatters.push(function(modelValue) { |
||
6774 | var candidateViewValue, emptyViewValue; |
||
6775 | var locals = {}; |
||
6776 | |||
6777 | // The validity may be set to false via $parsers (see above) if |
||
6778 | // the model is restricted to selected values. If the model |
||
6779 | // is set manually it is considered to be valid. |
||
6780 | if (!isEditable) { |
||
6781 | modelCtrl.$setValidity('editable', true); |
||
6782 | } |
||
6783 | |||
6784 | if (inputFormatter) { |
||
6785 | locals.$model = modelValue; |
||
6786 | return inputFormatter(originalScope, locals); |
||
6787 | } |
||
6788 | |||
6789 | //it might happen that we don't have enough info to properly render input value |
||
6790 | //we need to check for this situation and simply return model value if we can't apply custom formatting |
||
6791 | locals[parserResult.itemName] = modelValue; |
||
6792 | candidateViewValue = parserResult.viewMapper(originalScope, locals); |
||
6793 | locals[parserResult.itemName] = undefined; |
||
6794 | emptyViewValue = parserResult.viewMapper(originalScope, locals); |
||
6795 | |||
6796 | return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; |
||
6797 | }); |
||
6798 | }; |
||
6799 | }]) |
||
6800 | |||
6801 | .directive('uibTypeahead', function() { |
||
6802 | return { |
||
6803 | controller: 'UibTypeaheadController', |
||
6804 | require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'], |
||
6805 | link: function(originalScope, element, attrs, ctrls) { |
||
6806 | ctrls[2].init(ctrls[0], ctrls[1]); |
||
6807 | } |
||
6808 | }; |
||
6809 | }) |
||
6810 | |||
6811 | View Code Duplication | .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) { |
|
6812 | return { |
||
6813 | scope: { |
||
6814 | matches: '=', |
||
6815 | query: '=', |
||
6816 | active: '=', |
||
6817 | position: '&', |
||
6818 | moveInProgress: '=', |
||
6819 | select: '&', |
||
6820 | assignIsOpen: '&', |
||
6821 | debounce: '&' |
||
6822 | }, |
||
6823 | replace: true, |
||
6824 | templateUrl: function(element, attrs) { |
||
6825 | return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html'; |
||
6826 | }, |
||
6827 | link: function(scope, element, attrs) { |
||
6828 | scope.templateUrl = attrs.templateUrl; |
||
6829 | |||
6830 | scope.isOpen = function() { |
||
6831 | var isDropdownOpen = scope.matches.length > 0; |
||
6832 | scope.assignIsOpen({ isOpen: isDropdownOpen }); |
||
6833 | return isDropdownOpen; |
||
6834 | }; |
||
6835 | |||
6836 | scope.isActive = function(matchIdx) { |
||
6837 | return scope.active === matchIdx; |
||
6838 | }; |
||
6839 | |||
6840 | scope.selectActive = function(matchIdx) { |
||
6841 | scope.active = matchIdx; |
||
6842 | }; |
||
6843 | |||
6844 | scope.selectMatch = function(activeIdx, evt) { |
||
6845 | var debounce = scope.debounce(); |
||
6846 | if (angular.isNumber(debounce) || angular.isObject(debounce)) { |
||
6847 | $$debounce(function() { |
||
6848 | scope.select({activeIdx: activeIdx, evt: evt}); |
||
6849 | }, angular.isNumber(debounce) ? debounce : debounce['default']); |
||
6850 | } else { |
||
6851 | scope.select({activeIdx: activeIdx, evt: evt}); |
||
6852 | } |
||
6853 | }; |
||
6854 | } |
||
6855 | }; |
||
6856 | }]) |
||
6857 | |||
6858 | .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) { |
||
6859 | return { |
||
6860 | scope: { |
||
6861 | index: '=', |
||
6862 | match: '=', |
||
6863 | query: '=' |
||
6864 | }, |
||
6865 | link: function(scope, element, attrs) { |
||
6866 | var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html'; |
||
6867 | $templateRequest(tplUrl).then(function(tplContent) { |
||
6868 | var tplEl = angular.element(tplContent.trim()); |
||
6869 | element.replaceWith(tplEl); |
||
6870 | $compile(tplEl)(scope); |
||
6871 | }); |
||
6872 | } |
||
6873 | }; |
||
6874 | }]) |
||
6875 | |||
6876 | View Code Duplication | .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) { |
|
6877 | var isSanitizePresent; |
||
6878 | isSanitizePresent = $injector.has('$sanitize'); |
||
6879 | |||
6880 | function escapeRegexp(queryToEscape) { |
||
6881 | // Regex: capture the whole query string and replace it with the string that will be used to match |
||
6882 | // the results, for example if the capture is "a" the result will be \a |
||
6883 | return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); |
||
6884 | } |
||
6885 | |||
6886 | function containsHtml(matchItem) { |
||
6887 | return /<.*>/g.test(matchItem); |
||
6888 | } |
||
6889 | |||
6890 | return function(matchItem, query) { |
||
6891 | if (!isSanitizePresent && containsHtml(matchItem)) { |
||
6892 | $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger |
||
6893 | } |
||
6894 | 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 |
||
6895 | if (!isSanitizePresent) { |
||
6896 | matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive |
||
6897 | } |
||
6898 | return matchItem; |
||
6899 | }; |
||
6900 | }]); |
||
6901 | |||
6902 | angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) { |
||
6903 | $templateCache.put("uib/template/accordion/accordion-group.html", |
||
6904 | "<div class=\"panel\" ng-class=\"panelClass || 'panel-default'\">\n" + |
||
6905 | " <div role=\"tab\" id=\"{{::headingId}}\" aria-selected=\"{{isOpen}}\" class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" + |
||
6906 | " <h4 class=\"panel-title\">\n" + |
||
6907 | " <a role=\"button\" data-toggle=\"collapse\" href aria-expanded=\"{{isOpen}}\" aria-controls=\"{{::panelId}}\" tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span uib-accordion-header ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" + |
||
6908 | " </h4>\n" + |
||
6909 | " </div>\n" + |
||
6910 | " <div id=\"{{::panelId}}\" aria-labelledby=\"{{::headingId}}\" aria-hidden=\"{{!isOpen}}\" role=\"tabpanel\" class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" + |
||
6911 | " <div class=\"panel-body\" ng-transclude></div>\n" + |
||
6912 | " </div>\n" + |
||
6913 | "</div>\n" + |
||
6914 | ""); |
||
6915 | }]); |
||
6916 | |||
6917 | angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) { |
||
6918 | $templateCache.put("uib/template/accordion/accordion.html", |
||
6919 | "<div role=\"tablist\" class=\"panel-group\" ng-transclude></div>"); |
||
6920 | }]); |
||
6921 | |||
6922 | angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) { |
||
6923 | $templateCache.put("uib/template/alert/alert.html", |
||
6924 | "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" + |
||
6925 | " <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" + |
||
6926 | " <span aria-hidden=\"true\">×</span>\n" + |
||
6927 | " <span class=\"sr-only\">Close</span>\n" + |
||
6928 | " </button>\n" + |
||
6929 | " <div ng-transclude></div>\n" + |
||
6930 | "</div>\n" + |
||
6931 | ""); |
||
6932 | }]); |
||
6933 | |||
6934 | angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) { |
||
6935 | $templateCache.put("uib/template/carousel/carousel.html", |
||
6936 | "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" + |
||
6937 | " <div class=\"carousel-inner\" ng-transclude></div>\n" + |
||
6938 | " <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-class=\"{ disabled: isPrevDisabled() }\" ng-show=\"slides.length > 1\">\n" + |
||
6939 | " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" + |
||
6940 | " <span class=\"sr-only\">previous</span>\n" + |
||
6941 | " </a>\n" + |
||
6942 | " <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-class=\"{ disabled: isNextDisabled() }\" ng-show=\"slides.length > 1\">\n" + |
||
6943 | " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" + |
||
6944 | " <span class=\"sr-only\">next</span>\n" + |
||
6945 | " </a>\n" + |
||
6946 | " <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" + |
||
6947 | " <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" + |
||
6948 | " <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" + |
||
6949 | " </li>\n" + |
||
6950 | " </ol>\n" + |
||
6951 | "</div>\n" + |
||
6952 | ""); |
||
6953 | }]); |
||
6954 | |||
6955 | angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) { |
||
6956 | $templateCache.put("uib/template/carousel/slide.html", |
||
6957 | "<div ng-class=\"{\n" + |
||
6958 | " 'active': active\n" + |
||
6959 | " }\" class=\"item text-center\" ng-transclude></div>\n" + |
||
6960 | ""); |
||
6961 | }]); |
||
6962 | |||
6963 | angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) { |
||
6964 | $templateCache.put("uib/template/datepicker/datepicker.html", |
||
6965 | "<div class=\"uib-datepicker\" ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" + |
||
6966 | " <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" + |
||
6967 | " <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" + |
||
6968 | " <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" + |
||
6969 | "</div>\n" + |
||
6970 | ""); |
||
6971 | }]); |
||
6972 | |||
6973 | View Code Duplication | angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) { |
|
6974 | $templateCache.put("uib/template/datepicker/day.html", |
||
6975 | "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + |
||
6976 | " <thead>\n" + |
||
6977 | " <tr>\n" + |
||
6978 | " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + |
||
6979 | " <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" + |
||
6980 | " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + |
||
6981 | " </tr>\n" + |
||
6982 | " <tr>\n" + |
||
6983 | " <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" + |
||
6984 | " <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" + |
||
6985 | " </tr>\n" + |
||
6986 | " </thead>\n" + |
||
6987 | " <tbody>\n" + |
||
6988 | " <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" + |
||
6989 | " <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" + |
||
6990 | " <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" + |
||
6991 | " id=\"{{::dt.uid}}\"\n" + |
||
6992 | " ng-class=\"::dt.customClass\">\n" + |
||
6993 | " <button type=\"button\" class=\"btn btn-default btn-sm\"\n" + |
||
6994 | " uib-is-class=\"\n" + |
||
6995 | " 'btn-info' for selectedDt,\n" + |
||
6996 | " 'active' for activeDt\n" + |
||
6997 | " on dt\"\n" + |
||
6998 | " ng-click=\"select(dt.date)\"\n" + |
||
6999 | " ng-disabled=\"::dt.disabled\"\n" + |
||
7000 | " tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + |
||
7001 | " </td>\n" + |
||
7002 | " </tr>\n" + |
||
7003 | " </tbody>\n" + |
||
7004 | "</table>\n" + |
||
7005 | ""); |
||
7006 | }]); |
||
7007 | |||
7008 | View Code Duplication | angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) { |
|
7009 | $templateCache.put("uib/template/datepicker/month.html", |
||
7010 | "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + |
||
7011 | " <thead>\n" + |
||
7012 | " <tr>\n" + |
||
7013 | " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + |
||
7014 | " <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" + |
||
7015 | " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + |
||
7016 | " </tr>\n" + |
||
7017 | " </thead>\n" + |
||
7018 | " <tbody>\n" + |
||
7019 | " <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" + |
||
7020 | " <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" + |
||
7021 | " id=\"{{::dt.uid}}\"\n" + |
||
7022 | " ng-class=\"::dt.customClass\">\n" + |
||
7023 | " <button type=\"button\" class=\"btn btn-default\"\n" + |
||
7024 | " uib-is-class=\"\n" + |
||
7025 | " 'btn-info' for selectedDt,\n" + |
||
7026 | " 'active' for activeDt\n" + |
||
7027 | " on dt\"\n" + |
||
7028 | " ng-click=\"select(dt.date)\"\n" + |
||
7029 | " ng-disabled=\"::dt.disabled\"\n" + |
||
7030 | " tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + |
||
7031 | " </td>\n" + |
||
7032 | " </tr>\n" + |
||
7033 | " </tbody>\n" + |
||
7034 | "</table>\n" + |
||
7035 | ""); |
||
7036 | }]); |
||
7037 | |||
7038 | View Code Duplication | angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) { |
|
7039 | $templateCache.put("uib/template/datepicker/year.html", |
||
7040 | "<table class=\"uib-yearpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + |
||
7041 | " <thead>\n" + |
||
7042 | " <tr>\n" + |
||
7043 | " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + |
||
7044 | " <th colspan=\"{{::columns - 2}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" + |
||
7045 | " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + |
||
7046 | " </tr>\n" + |
||
7047 | " </thead>\n" + |
||
7048 | " <tbody>\n" + |
||
7049 | " <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\">\n" + |
||
7050 | " <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" + |
||
7051 | " id=\"{{::dt.uid}}\"\n" + |
||
7052 | " ng-class=\"::dt.customClass\">\n" + |
||
7053 | " <button type=\"button\" class=\"btn btn-default\"\n" + |
||
7054 | " uib-is-class=\"\n" + |
||
7055 | " 'btn-info' for selectedDt,\n" + |
||
7056 | " 'active' for activeDt\n" + |
||
7057 | " on dt\"\n" + |
||
7058 | " ng-click=\"select(dt.date)\"\n" + |
||
7059 | " ng-disabled=\"::dt.disabled\"\n" + |
||
7060 | " tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + |
||
7061 | " </td>\n" + |
||
7062 | " </tr>\n" + |
||
7063 | " </tbody>\n" + |
||
7064 | "</table>\n" + |
||
7065 | ""); |
||
7066 | }]); |
||
7067 | |||
7068 | angular.module("uib/template/datepickerPopup/popup.html", []).run(["$templateCache", function($templateCache) { |
||
7069 | $templateCache.put("uib/template/datepickerPopup/popup.html", |
||
7070 | "<div>\n" + |
||
7071 | " <ul class=\"uib-datepicker-popup dropdown-menu uib-position-measure\" dropdown-nested ng-if=\"isOpen\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" + |
||
7072 | " <li ng-transclude></li>\n" + |
||
7073 | " <li ng-if=\"showButtonBar\" class=\"uib-button-bar\">\n" + |
||
7074 | " <span class=\"btn-group pull-left\">\n" + |
||
7075 | " <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today', $event)\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" + |
||
7076 | " <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null, $event)\">{{ getText('clear') }}</button>\n" + |
||
7077 | " </span>\n" + |
||
7078 | " <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close($event)\">{{ getText('close') }}</button>\n" + |
||
7079 | " </li>\n" + |
||
7080 | " </ul>\n" + |
||
7081 | "</div>\n" + |
||
7082 | ""); |
||
7083 | }]); |
||
7084 | |||
7085 | angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) { |
||
7086 | $templateCache.put("uib/template/modal/backdrop.html", |
||
7087 | "<div class=\"modal-backdrop\"\n" + |
||
7088 | " uib-modal-animation-class=\"fade\"\n" + |
||
7089 | " modal-in-class=\"in\"\n" + |
||
7090 | " ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" + |
||
7091 | "></div>\n" + |
||
7092 | ""); |
||
7093 | }]); |
||
7094 | |||
7095 | angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) { |
||
7096 | $templateCache.put("uib/template/modal/window.html", |
||
7097 | "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" + |
||
7098 | " uib-modal-animation-class=\"fade\"\n" + |
||
7099 | " modal-in-class=\"in\"\n" + |
||
7100 | " ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" + |
||
7101 | " <div class=\"modal-dialog {{size ? 'modal-' + size : ''}}\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" + |
||
7102 | "</div>\n" + |
||
7103 | ""); |
||
7104 | }]); |
||
7105 | |||
7106 | angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) { |
||
7107 | $templateCache.put("uib/template/pager/pager.html", |
||
7108 | "<ul class=\"pager\">\n" + |
||
7109 | " <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" + |
||
7110 | " <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" + |
||
7111 | "</ul>\n" + |
||
7112 | ""); |
||
7113 | }]); |
||
7114 | |||
7115 | angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) { |
||
7116 | $templateCache.put("uib/template/pagination/pagination.html", |
||
7117 | "<ul class=\"pagination\">\n" + |
||
7118 | " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" + |
||
7119 | " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" + |
||
7120 | " <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" + |
||
7121 | " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" + |
||
7122 | " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" + |
||
7123 | "</ul>\n" + |
||
7124 | ""); |
||
7125 | }]); |
||
7126 | |||
7127 | angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) { |
||
7128 | $templateCache.put("uib/template/tooltip/tooltip-html-popup.html", |
||
7129 | "<div class=\"tooltip\"\n" + |
||
7130 | " tooltip-animation-class=\"fade\"\n" + |
||
7131 | " uib-tooltip-classes\n" + |
||
7132 | " ng-class=\"{ in: isOpen() }\">\n" + |
||
7133 | " <div class=\"tooltip-arrow\"></div>\n" + |
||
7134 | " <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" + |
||
7135 | "</div>\n" + |
||
7136 | ""); |
||
7137 | }]); |
||
7138 | |||
7139 | angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) { |
||
7140 | $templateCache.put("uib/template/tooltip/tooltip-popup.html", |
||
7141 | "<div class=\"tooltip\"\n" + |
||
7142 | " tooltip-animation-class=\"fade\"\n" + |
||
7143 | " uib-tooltip-classes\n" + |
||
7144 | " ng-class=\"{ in: isOpen() }\">\n" + |
||
7145 | " <div class=\"tooltip-arrow\"></div>\n" + |
||
7146 | " <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" + |
||
7147 | "</div>\n" + |
||
7148 | ""); |
||
7149 | }]); |
||
7150 | |||
7151 | angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) { |
||
7152 | $templateCache.put("uib/template/tooltip/tooltip-template-popup.html", |
||
7153 | "<div class=\"tooltip\"\n" + |
||
7154 | " tooltip-animation-class=\"fade\"\n" + |
||
7155 | " uib-tooltip-classes\n" + |
||
7156 | " ng-class=\"{ in: isOpen() }\">\n" + |
||
7157 | " <div class=\"tooltip-arrow\"></div>\n" + |
||
7158 | " <div class=\"tooltip-inner\"\n" + |
||
7159 | " uib-tooltip-template-transclude=\"contentExp()\"\n" + |
||
7160 | " tooltip-template-transclude-scope=\"originScope()\"></div>\n" + |
||
7161 | "</div>\n" + |
||
7162 | ""); |
||
7163 | }]); |
||
7164 | |||
7165 | angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) { |
||
7166 | $templateCache.put("uib/template/popover/popover-html.html", |
||
7167 | "<div class=\"popover\"\n" + |
||
7168 | " tooltip-animation-class=\"fade\"\n" + |
||
7169 | " uib-tooltip-classes\n" + |
||
7170 | " ng-class=\"{ in: isOpen() }\">\n" + |
||
7171 | " <div class=\"arrow\"></div>\n" + |
||
7172 | "\n" + |
||
7173 | " <div class=\"popover-inner\">\n" + |
||
7174 | " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" + |
||
7175 | " <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" + |
||
7176 | " </div>\n" + |
||
7177 | "</div>\n" + |
||
7178 | ""); |
||
7179 | }]); |
||
7180 | |||
7181 | angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) { |
||
7182 | $templateCache.put("uib/template/popover/popover-template.html", |
||
7183 | "<div class=\"popover\"\n" + |
||
7184 | " tooltip-animation-class=\"fade\"\n" + |
||
7185 | " uib-tooltip-classes\n" + |
||
7186 | " ng-class=\"{ in: isOpen() }\">\n" + |
||
7187 | " <div class=\"arrow\"></div>\n" + |
||
7188 | "\n" + |
||
7189 | " <div class=\"popover-inner\">\n" + |
||
7190 | " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" + |
||
7191 | " <div class=\"popover-content\"\n" + |
||
7192 | " uib-tooltip-template-transclude=\"contentExp()\"\n" + |
||
7193 | " tooltip-template-transclude-scope=\"originScope()\"></div>\n" + |
||
7194 | " </div>\n" + |
||
7195 | "</div>\n" + |
||
7196 | ""); |
||
7197 | }]); |
||
7198 | |||
7199 | angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) { |
||
7200 | $templateCache.put("uib/template/popover/popover.html", |
||
7201 | "<div class=\"popover\"\n" + |
||
7202 | " tooltip-animation-class=\"fade\"\n" + |
||
7203 | " uib-tooltip-classes\n" + |
||
7204 | " ng-class=\"{ in: isOpen() }\">\n" + |
||
7205 | " <div class=\"arrow\"></div>\n" + |
||
7206 | "\n" + |
||
7207 | " <div class=\"popover-inner\">\n" + |
||
7208 | " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" + |
||
7209 | " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" + |
||
7210 | " </div>\n" + |
||
7211 | "</div>\n" + |
||
7212 | ""); |
||
7213 | }]); |
||
7214 | |||
7215 | angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) { |
||
7216 | $templateCache.put("uib/template/progressbar/bar.html", |
||
7217 | "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" + |
||
7218 | ""); |
||
7219 | }]); |
||
7220 | |||
7221 | angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) { |
||
7222 | $templateCache.put("uib/template/progressbar/progress.html", |
||
7223 | "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>"); |
||
7224 | }]); |
||
7225 | |||
7226 | angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) { |
||
7227 | $templateCache.put("uib/template/progressbar/progressbar.html", |
||
7228 | "<div class=\"progress\">\n" + |
||
7229 | " <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" + |
||
7230 | "</div>\n" + |
||
7231 | ""); |
||
7232 | }]); |
||
7233 | |||
7234 | angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) { |
||
7235 | $templateCache.put("uib/template/rating/rating.html", |
||
7236 | "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\" aria-valuetext=\"{{title}}\">\n" + |
||
7237 | " <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" + |
||
7238 | " <i ng-repeat-end ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\" ng-attr-title=\"{{r.title}}\"></i>\n" + |
||
7239 | "</span>\n" + |
||
7240 | ""); |
||
7241 | }]); |
||
7242 | |||
7243 | angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) { |
||
7244 | $templateCache.put("uib/template/tabs/tab.html", |
||
7245 | "<li ng-class=\"[{active: active, disabled: disabled}, classes]\" class=\"uib-tab nav-item\">\n" + |
||
7246 | " <a href ng-click=\"select($event)\" class=\"nav-link\" uib-tab-heading-transclude>{{heading}}</a>\n" + |
||
7247 | "</li>\n" + |
||
7248 | ""); |
||
7249 | }]); |
||
7250 | |||
7251 | angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) { |
||
7252 | $templateCache.put("uib/template/tabs/tabset.html", |
||
7253 | "<div>\n" + |
||
7254 | " <ul class=\"nav nav-{{tabset.type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" + |
||
7255 | " <div class=\"tab-content\">\n" + |
||
7256 | " <div class=\"tab-pane\"\n" + |
||
7257 | " ng-repeat=\"tab in tabset.tabs\"\n" + |
||
7258 | " ng-class=\"{active: tabset.active === tab.index}\"\n" + |
||
7259 | " uib-tab-content-transclude=\"tab\">\n" + |
||
7260 | " </div>\n" + |
||
7261 | " </div>\n" + |
||
7262 | "</div>\n" + |
||
7263 | ""); |
||
7264 | }]); |
||
7265 | |||
7266 | angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) { |
||
7267 | $templateCache.put("uib/template/timepicker/timepicker.html", |
||
7268 | "<table class=\"uib-timepicker\">\n" + |
||
7269 | " <tbody>\n" + |
||
7270 | " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" + |
||
7271 | " <td class=\"uib-increment hours\"><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" + |
||
7272 | " <td> </td>\n" + |
||
7273 | " <td class=\"uib-increment minutes\"><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" + |
||
7274 | " <td ng-show=\"showSeconds\"> </td>\n" + |
||
7275 | " <td ng-show=\"showSeconds\" class=\"uib-increment seconds\"><a ng-click=\"incrementSeconds()\" ng-class=\"{disabled: noIncrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" + |
||
7276 | " <td ng-show=\"showMeridian\"></td>\n" + |
||
7277 | " </tr>\n" + |
||
7278 | " <tr>\n" + |
||
7279 | " <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" + |
||
7280 | " <input type=\"text\" placeholder=\"HH\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementHours()\" ng-blur=\"blur()\">\n" + |
||
7281 | " </td>\n" + |
||
7282 | " <td class=\"uib-separator\">:</td>\n" + |
||
7283 | " <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" + |
||
7284 | " <input type=\"text\" placeholder=\"MM\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementMinutes()\" ng-blur=\"blur()\">\n" + |
||
7285 | " </td>\n" + |
||
7286 | " <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" + |
||
7287 | " <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" + |
||
7288 | " <input type=\"text\" placeholder=\"SS\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementSeconds()\" ng-blur=\"blur()\">\n" + |
||
7289 | " </td>\n" + |
||
7290 | " <td ng-show=\"showMeridian\" class=\"uib-time am-pm\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" + |
||
7291 | " </tr>\n" + |
||
7292 | " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" + |
||
7293 | " <td class=\"uib-decrement hours\"><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" + |
||
7294 | " <td> </td>\n" + |
||
7295 | " <td class=\"uib-decrement minutes\"><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" + |
||
7296 | " <td ng-show=\"showSeconds\"> </td>\n" + |
||
7297 | " <td ng-show=\"showSeconds\" class=\"uib-decrement seconds\"><a ng-click=\"decrementSeconds()\" ng-class=\"{disabled: noDecrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" + |
||
7298 | " <td ng-show=\"showMeridian\"></td>\n" + |
||
7299 | " </tr>\n" + |
||
7300 | " </tbody>\n" + |
||
7301 | "</table>\n" + |
||
7302 | ""); |
||
7303 | }]); |
||
7304 | |||
7305 | angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) { |
||
7306 | $templateCache.put("uib/template/typeahead/typeahead-match.html", |
||
7307 | "<a href\n" + |
||
7308 | " tabindex=\"-1\"\n" + |
||
7309 | " ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"\n" + |
||
7310 | " ng-attr-title=\"{{match.label}}\"></a>\n" + |
||
7311 | ""); |
||
7312 | }]); |
||
7313 | |||
7314 | angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) { |
||
7315 | $templateCache.put("uib/template/typeahead/typeahead-popup.html", |
||
7316 | "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" + |
||
7317 | " <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">\n" + |
||
7318 | " <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" + |
||
7319 | " </li>\n" + |
||
7320 | "</ul>\n" + |
||
7321 | ""); |
||
7322 | }]); |
||
7323 | angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibCarouselCss && 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>'); angular.$$uibCarouselCss = true; }); |
||
7324 | angular.module('ui.bootstrap.datepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-datepicker .uib-title{width:100%;}.uib-day button,.uib-month button,.uib-year button{min-width:100%;}.uib-left,.uib-right{width:100%}</style>'); angular.$$uibDatepickerCss = true; }); |
||
7325 | angular.module('ui.bootstrap.position').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibPositionCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-position-measure{display:block !important;visibility:hidden !important;position:absolute !important;top:-9999px !important;left:-9999px !important;}.uib-position-scrollbar-measure{position:absolute !important;top:-9999px !important;width:50px !important;height:50px !important;overflow:scroll !important;}.uib-position-body-scrollbar-measure{overflow:scroll !important;}</style>'); angular.$$uibPositionCss = true; }); |
||
7326 | angular.module('ui.bootstrap.datepickerPopup').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerpopupCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-datepicker-popup.dropdown-menu{display:block;float:none;margin:0;}.uib-button-bar{padding:10px 9px 2px;}</style>'); angular.$$uibDatepickerpopupCss = true; }); |
||
7327 | angular.module('ui.bootstrap.tooltip').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTooltipCss && angular.element(document).find('head').prepend('<style type="text/css">[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow,[uib-popover-popup].popover.top-left > .arrow,[uib-popover-popup].popover.top-right > .arrow,[uib-popover-popup].popover.bottom-left > .arrow,[uib-popover-popup].popover.bottom-right > .arrow,[uib-popover-popup].popover.left-top > .arrow,[uib-popover-popup].popover.left-bottom > .arrow,[uib-popover-popup].popover.right-top > .arrow,[uib-popover-popup].popover.right-bottom > .arrow,[uib-popover-html-popup].popover.top-left > .arrow,[uib-popover-html-popup].popover.top-right > .arrow,[uib-popover-html-popup].popover.bottom-left > .arrow,[uib-popover-html-popup].popover.bottom-right > .arrow,[uib-popover-html-popup].popover.left-top > .arrow,[uib-popover-html-popup].popover.left-bottom > .arrow,[uib-popover-html-popup].popover.right-top > .arrow,[uib-popover-html-popup].popover.right-bottom > .arrow,[uib-popover-template-popup].popover.top-left > .arrow,[uib-popover-template-popup].popover.top-right > .arrow,[uib-popover-template-popup].popover.bottom-left > .arrow,[uib-popover-template-popup].popover.bottom-right > .arrow,[uib-popover-template-popup].popover.left-top > .arrow,[uib-popover-template-popup].popover.left-bottom > .arrow,[uib-popover-template-popup].popover.right-top > .arrow,[uib-popover-template-popup].popover.right-bottom > .arrow{top:auto;bottom:auto;left:auto;right:auto;margin:0;}[uib-popover-popup].popover,[uib-popover-html-popup].popover,[uib-popover-template-popup].popover{display:block !important;}</style>'); angular.$$uibTooltipCss = true; }); |
||
7328 | angular.module('ui.bootstrap.timepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTimepickerCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-time input{width:50px;}</style>'); angular.$$uibTimepickerCss = true; }); |
||
7329 | angular.module('ui.bootstrap.typeahead').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTypeaheadCss && angular.element(document).find('head').prepend('<style type="text/css">[uib-typeahead-popup].dropdown-menu{display:block;}</style>'); angular.$$uibTypeaheadCss = true; }); |