GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (3063)

UI-Bootstrap/ui-bootstrap-tpls-1.3.2.js (18 issues)

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
The function getSlideByIndex does not seem to be used and can be removed.
Loading history...
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
The variable stopped seems to be never used. Consider removing it.
Loading history...
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
The call to bind does not seem necessary since the function removeClass declared on line 696 does not use this.
Loading history...
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
The variable stopped seems to be never used. Consider removing it.
Loading history...
719
        };
720
      }
721
      done();
722
    },
723
    beforeRemoveClass: function (element, className, done) {
724
      if (className === 'active') {
725
        var stopped = false;
0 ignored issues
show
The variable stopped seems to be never used. Consider removing it.
Loading history...
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
The call to bind does not seem necessary since the function removeClass declared on line 696 does not use this.
Loading history...
729
730
        $animateCss(element, {addClass: directionClass})
731
          .start()
732
          .done(removeClassFn);
733
734
        return function() {
735
          stopped = true;
0 ignored issues
show
The variable stopped seems to be never used. Consider removing it.
Loading history...
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
The variable dataPerTracked seems to be never used. Consider removing it.
Loading history...
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
The variable optionsUsed seems to be never used. Consider removing it.
Loading history...
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
Coding Style Best Practice introduced by
Using the Array constructor is generally discouraged. Consider using an array literal instead.
Loading history...
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
The variable cache seems to be never used. Consider removing it.
Loading history...
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
The function cameltoDash does not seem to be used and can be removed.
Loading history...
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
The variable backdropScopeRef seems to be never used. Consider removing it.
Loading history...
3770
          removeAfterAnimate(backdropDomEl, backdropScope, function() {
3771
            backdropScopeRef = null;
0 ignored issues
show
The variable backdropScopeRef seems to be never used. Consider removing it.
Loading history...
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
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
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
The assignment to last seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
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
Coding Style Best Practice introduced by
Using the Array constructor is generally discouraged. Consider using an array literal instead.
Loading history...
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
The variable index seems to not be initialized for all possible execution paths. Are you sure select handles undefined variables?
Loading history...
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>&nbsp;</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\">&nbsp;</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>&nbsp;</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\">&nbsp;</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; });