https://samarthinstitute.edu.in/web-assets/angular/cms-ui-bootstrap-tpls-1.0.3.js?version=1772287900
1/* 2* angular-ui-bootstrap 3* http://angular-ui.github.io/bootstrap/ 4 5* Version: 1.0.3 - 2016-01-11 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.position","ui.bootstrap.datepicker","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"]); 8angular.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/popup.html","uib/template/datepicker/year.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"]); 9angular.module('ui.bootstrap.collapse', []) 10 11 .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) { 12 var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; 13 return { 14 link: function(scope, element, attrs) { 15 if (!scope.$eval(attrs.uibCollapse)) { 16 element.addClass('in') 17 .addClass('collapse') 18 .css({height: 'auto'}); 19 } 20 21 function expand() { 22 element.removeClass('collapse') 23 .addClass('collapsing') 24 .attr('aria-expanded', true) 25 .attr('aria-hidden', false); 26 27 if ($animateCss) { 28 $animateCss(element, { 29 addClass: 'in', 30 easing: 'ease', 31 to: { height: element[0].scrollHeight + 'px' } 32 }).start()['finally'](expandDone); 33 } else { 34 $animate.addClass(element, 'in', { 35 to: { height: element[0].scrollHeight + 'px' } 36 }).then(expandDone); 37 } 38 } 39 40 function expandDone() { 41 element.removeClass('collapsing') 42 .addClass('collapse') 43 .css({height: 'auto'}); 44 } 45 46 function collapse() { 47 if (!element.hasClass('collapse') && !element.hasClass('in')) { 48 return collapseDone(); 49 } 50 51 element 52 // IMPORTANT: The height must be set before adding "collapsing" class. 53 // Otherwise, the browser attempts to animate from height 0 (in 54 // collapsing class) to the given height here. 55 .css({height: element[0].scrollHeight + 'px'}) 56 // initially all panel collapse have the collapse class, this removal 57 // prevents the animation from jumping to collapsed state 58 .removeClass('collapse') 59 .addClass('collapsing') 60 .attr('aria-expanded', false) 61 .attr('aria-hidden', true); 62 63 if ($animateCss) { 64 $animateCss(element, { 65 removeClass: 'in', 66 to: {height: '0'} 67 }).start()['finally'](collapseDone); 68 } else { 69 $animate.removeClass(element, 'in', { 70 to: {height: '0'} 71 }).then(collapseDone); 72 } 73 } 74 75 function collapseDone() { 76 element.css({height: '0'}); // Required so that collapse works when animation is disabled 77 element.removeClass('collapsing') 78 .addClass('collapse'); 79 } 80 81 scope.$watch(attrs.uibCollapse, function(shouldCollapse) { 82 if (shouldCollapse) { 83 collapse(); 84 } else { 85 expand(); 86 } 87 }); 88 } 89 }; 90 }]); 91 92angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse']) 93 94.constant('uibAccordionConfig', { 95 closeOthers: true 96}) 97 98.controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) { 99 // This array keeps track of the accordion groups 100 this.groups = []; 101 102 // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to 103 this.closeOthers = function(openGroup) { 104 var closeOthers = angular.isDefined($attrs.closeOthers) ? 105 $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers; 106 if (closeOthers) { 107 angular.forEach(this.groups, function(group) { 108 if (group !== openGroup) { 109 group.isOpen = false;
vendor: 7,831 bytes, lines 110-357
110 } 111 }); 112 } 113 }; 114 115 // This is called from the accordion-group directive to add itself to the accordion 116 this.addGroup = function(groupScope) { 117 var that = this; 118 this.groups.push(groupScope); 119 120 groupScope.$on('$destroy', function(event) { 121 that.removeGroup(groupScope); 122 }); 123 }; 124 125 // This is called from the accordion-group directive when to remove itself 126 this.removeGroup = function(group) { 127 var index = this.groups.indexOf(group); 128 if (index !== -1) { 129 this.groups.splice(index, 1); 130 } 131 }; 132}]) 133 134// The accordion directive simply sets up the directive controller 135// and adds an accordion CSS class to itself element. 136.directive('uibAccordion', function() { 137 return { 138 controller: 'UibAccordionController', 139 controllerAs: 'accordion', 140 transclude: true, 141 templateUrl: function(element, attrs) { 142 return attrs.templateUrl || 'uib/template/accordion/accordion.html'; 143 } 144 }; 145}) 146 147// The accordion-group directive indicates a block of html that will expand and collapse in an accordion 148.directive('uibAccordionGroup', function() { 149 return { 150 require: '^uibAccordion', // We need this directive to be inside an accordion 151 transclude: true, // It transcludes the contents of the directive into the template 152 replace: true, // The element containing the directive will be replaced with the template 153 templateUrl: function(element, attrs) { 154 return attrs.templateUrl || 'uib/template/accordion/accordion-group.html'; 155 }, 156 scope: { 157 heading: '@', // Interpolate the heading attribute onto this scope 158 isOpen: '=?', 159 isDisabled: '=?' 160 }, 161 controller: function() { 162 this.setHeading = function(element) { 163 this.heading = element; 164 }; 165 }, 166 link: function(scope, element, attrs, accordionCtrl) { 167 accordionCtrl.addGroup(scope); 168 169 scope.openClass = attrs.openClass || 'panel-open'; 170 scope.panelClass = attrs.panelClass || 'panel-default'; 171 scope.$watch('isOpen', function(value) { 172 element.toggleClass(scope.openClass, !!value); 173 if (value) { 174 accordionCtrl.closeOthers(scope); 175 } 176 }); 177 178 scope.toggleOpen = function($event) { 179 if (!scope.isDisabled) { 180 if (!$event || $event.which === 32) { 181 scope.isOpen = !scope.isOpen; 182 } 183 } 184 }; 185 } 186 }; 187}) 188 189// Use accordion-heading below an accordion-group to provide a heading containing HTML 190.directive('uibAccordionHeading', function() { 191 return { 192 transclude: true, // Grab the contents to be used as the heading 193 template: '', // In effect remove this element! 194 replace: true, 195 require: '^uibAccordionGroup', 196 link: function(scope, element, attrs, accordionGroupCtrl, transclude) { 197 // Pass the heading to the accordion-group controller 198 // so that it can be transcluded into the right place in the template 199 // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] 200 accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); 201 } 202 }; 203}) 204 205// Use in the accordion-group template to indicate where you want the heading to be transcluded 206// You must provide the property on the accordion-group controller that will hold the transcluded element 207.directive('uibAccordionTransclude', function() { 208 return { 209 require: '^uibAccordionGroup', 210 link: function(scope, element, attrs, controller) { 211 scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) { 212 if (heading) { 213 element.find('span').html(''); 214 element.find('span').append(heading); 215 } 216 }); 217 } 218 }; 219}); 220 221angular.module('ui.bootstrap.alert', []) 222 223.controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) { 224 $scope.closeable = !!$attrs.close; 225 226 var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ? 227 $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null; 228 229 if (dismissOnTimeout) { 230 $timeout(function() { 231 $scope.close(); 232 }, parseInt(dismissOnTimeout, 10)); 233 } 234}]) 235 236.directive('uibAlert', function() { 237 return { 238 controller: 'UibAlertController', 239 controllerAs: 'alert', 240 templateUrl: function(element, attrs) { 241 return attrs.templateUrl || 'uib/template/alert/alert.html'; 242 }, 243 transclude: true, 244 replace: true, 245 scope: { 246 type: '@', 247 close: '&' 248 } 249 }; 250}); 251 252angular.module('ui.bootstrap.buttons', []) 253 254.constant('uibButtonConfig', { 255 activeClass: 'active', 256 toggleEvent: 'click' 257}) 258 259.controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) { 260 this.activeClass = buttonConfig.activeClass || 'active'; 261 this.toggleEvent = buttonConfig.toggleEvent || 'click'; 262}]) 263 264.directive('uibBtnRadio', ['$parse', function($parse) { 265 return { 266 require: ['uibBtnRadio', 'ngModel'], 267 controller: 'UibButtonsController', 268 controllerAs: 'buttons', 269 link: function(scope, element, attrs, ctrls) { 270 var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; 271 var uncheckableExpr = $parse(attrs.uibUncheckable); 272 273 element.find('input').css({display: 'none'}); 274 275 //model -> UI 276 ngModelCtrl.$render = function() { 277 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio))); 278 }; 279 280 //ui->model 281 element.on(buttonsCtrl.toggleEvent, function() { 282 if (attrs.disabled) { 283 return; 284 } 285 286 var isActive = element.hasClass(buttonsCtrl.activeClass); 287 288 if (!isActive || angular.isDefined(attrs.uncheckable)) { 289 scope.$apply(function() { 290 ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio)); 291 ngModelCtrl.$render(); 292 }); 293 } 294 }); 295 296 if (attrs.uibUncheckable) { 297 scope.$watch(uncheckableExpr, function(uncheckable) { 298 attrs.$set('uncheckable', uncheckable ? '' : null); 299 }); 300 } 301 } 302 }; 303}]) 304 305.directive('uibBtnCheckbox', function() { 306 return { 307 require: ['uibBtnCheckbox', 'ngModel'], 308 controller: 'UibButtonsController', 309 controllerAs: 'button', 310 link: function(scope, element, attrs, ctrls) { 311 var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; 312 313 element.find('input').css({display: 'none'}); 314 315 function getTrueValue() { 316 return getCheckboxValue(attrs.btnCheckboxTrue, true); 317 } 318 319 function getFalseValue() { 320 return getCheckboxValue(attrs.btnCheckboxFalse, false); 321 } 322 323 function getCheckboxValue(attribute, defaultValue) { 324 return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue; 325 } 326 327 //model -> UI 328 ngModelCtrl.$render = function() { 329 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); 330 }; 331 332 //ui->model 333 element.on(buttonsCtrl.toggleEvent, function() { 334 if (attrs.disabled) { 335 return; 336 } 337 338 scope.$apply(function() { 339 ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); 340 ngModelCtrl.$render(); 341 }); 342 }); 343 } 344 }; 345}); 346 347angular.module('ui.bootstrap.carousel', []) 348 349.controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) { 350 var self = this, 351 slides = self.slides = $scope.slides = [], 352 SLIDE_DIRECTION = 'uib-slideDirection', 353 currentIndex = -1, 354 currentInterval, isPlaying, bufferedTransitions = []; 355 self.currentSlide = null; 356 357 var destroyed = false;
358 359 self.addSlide = function(slide, element) { 360 slide.$element = element; 361 slides.push(slide); 362 //if this is the first slide or the slide is set to active, select it 363 if (slides.length === 1 || slide.active) { 364 if ($scope.$currentTransition) { 365 $scope.$currentTransition = null; 366 } 367 368 self.select(slides[slides.length - 1]); 369 if (slides.length === 1) { 370 $scope.play(); 371 } 372 } else { 373 slide.active = false; 374 } 375 }; 376 377 self.getCurrentIndex = function() { 378 if (self.currentSlide && angular.isDefined(self.currentSlide.index)) { 379 return +self.currentSlide.index; 380 } 381 return currentIndex; 382 }; 383 384 self.next = $scope.next = function() { 385 var newIndex = (self.getCurrentIndex() + 1) % slides.length; 386 387 if (newIndex === 0 && $scope.noWrap()) { 388 $scope.pause(); 389 return; 390 } 391 392 return self.select(getSlideByIndex(newIndex), 'next'); 393 }; 394 395 self.prev = $scope.prev = function() { 396 var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; 397 398 if ($scope.noWrap() && newIndex === slides.length - 1) { 399 $scope.pause(); 400 return; 401 } 402 403 return self.select(getSlideByIndex(newIndex), 'prev'); 404 }; 405 406 self.removeSlide = function(slide) { 407 if (angular.isDefined(slide.index)) { 408 slides.sort(function(a, b) { 409 return +a.index > +b.index; 410 }); 411 } 412 413 var bufferedIndex = bufferedTransitions.indexOf(slide); 414 if (bufferedIndex !== -1) { 415 bufferedTransitions.splice(bufferedIndex, 1); 416 } 417 //get the index of the slide inside the carousel 418 var index = slides.indexOf(slide); 419 slides.splice(index, 1); 420 $timeout(function() { 421 if (slides.length > 0 && slide.active) { 422 if (index >= slides.length) { 423 self.select(slides[index - 1]); 424 } else { 425 self.select(slides[index]); 426 } 427 } else if (currentIndex > index) { 428 currentIndex--; 429 } 430 }); 431 432 //clean the currentSlide when no more slide 433 if (slides.length === 0) { 434 self.currentSlide = null; 435 clearBufferedTransitions(); 436 } 437 }; 438 439 /* direction: "prev" or "next" */ 440 self.select = $scope.select = function(nextSlide, direction) { 441 var nextIndex = $scope.indexOfSlide(nextSlide); 442 //Decide direction if it's not given 443 if (direction === undefined) { 444 direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; 445 } 446 //Prevent this user-triggered transition from occurring if there is already one in progress 447 if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) { 448 goNext(nextSlide, nextIndex, direction); 449 } else if (nextSlide && nextSlide !== self.currentSlide && $scope.$currentTransition) { 450 bufferedTransitions.push(nextSlide); 451 nextSlide.active = false; 452 } 453 }; 454 455 /* Allow outside people to call indexOf on slides array */ 456 $scope.indexOfSlide = function(slide) { 457 return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide); 458 }; 459 460 $scope.isActive = function(slide) { 461 return self.currentSlide === slide; 462 }; 463 464 $scope.pause = function() { 465 if (!$scope.noPause) { 466 isPlaying = false; 467 resetTimer(); 468 } 469 }; 470 471 $scope.play = function() { 472 if (!isPlaying) { 473 isPlaying = true; 474 restartTimer(); 475 } 476 }; 477 478 $scope.$on('$destroy', function() { 479 destroyed = true; 480 resetTimer(); 481 }); 482 483 $scope.$watch('noTransition', function(noTransition) { 484 $animate.enabled($element, !noTransition); 485 }); 486 487 $scope.$watch('interval', restartTimer); 488 489 $scope.$watchCollection('slides', resetTransition); 490 491 function clearBufferedTransitions() { 492 while (bufferedTransitions.length) { 493 bufferedTransitions.shift(); 494 } 495 } 496 497 function getSlideByIndex(index) { 498 if (angular.isUndefined(slides[index].index)) { 499 return slides[index]; 500 } 501 for (var i = 0, l = slides.length; i < l; ++i) { 502 if (slides[i].index === index) { 503 return slides[i]; 504 } 505 } 506 } 507 508 function goNext(slide, index, direction) { 509 if (destroyed) { return; } 510 511 angular.extend(slide, {direction: direction, active: true}); 512 angular.extend(self.currentSlide || {}, {direction: direction, active: false}); 513 if ($animate.enabled($element) && !$scope.$currentTransition && 514 slide.$element && self.slides.length > 1) { 515 slide.$element.data(SLIDE_DIRECTION, slide.direction); 516 if (self.currentSlide && self.currentSlide.$element) { 517 self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction); 518 } 519 520 $scope.$currentTransition = true; 521 $animate.on('addClass', slide.$element, function(element, phase) { 522 if (phase === 'close') { 523 $scope.$currentTransition = null; 524 $animate.off('addClass', element); 525 if (bufferedTransitions.length) { 526 var nextSlide = bufferedTransitions.pop(); 527 var nextIndex = $scope.indexOfSlide(nextSlide); 528 var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; 529 clearBufferedTransitions(); 530 531 goNext(nextSlide, nextIndex, nextDirection); 532 } 533 } 534 }); 535 } 536 537 self.currentSlide = slide; 538 currentIndex = index; 539 540 //every time you change slides, reset the timer 541 restartTimer(); 542 } 543 544 function resetTimer() { 545 if (currentInterval) { 546 $interval.cancel(currentInterval); 547 currentInterval = null; 548 } 549 } 550 551 function resetTransition(slides) { 552 if (!slides.length) { 553 $scope.$currentTransition = null; 554 clearBufferedTransitions(); 555 } 556 } 557 558 function restartTimer() { 559 resetTimer(); 560 var interval = +$scope.interval; 561 if (!isNaN(interval) && interval > 0) { 562 currentInterval = $interval(timerFn, interval); 563 } 564 } 565 566 function timerFn() { 567 var interval = +$scope.interval; 568 if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) { 569 $scope.next(); 570 } else { 571 $scope.pause(); 572 } 573 } 574}]) 575 576.directive('uibCarousel', function() { 577 return { 578 transclude: true, 579 replace: true, 580 controller: 'UibCarouselController', 581 controllerAs: 'carousel', 582 templateUrl: function(element, attrs) { 583 return attrs.templateUrl || 'uib/template/carousel/carousel.html'; 584 }, 585 scope: { 586 interval: '=', 587 noTransition: '=', 588 noPause: '=', 589 noWrap: '&' 590 } 591 }; 592}) 593 594.directive('uibSlide', function() { 595 return { 596 require: '^uibCarousel', 597 transclude: true, 598 replace: true, 599 templateUrl: function(element, attrs) { 600 return attrs.templateUrl || 'uib/template/carousel/slide.html'; 601 }, 602 scope: { 603 active: '=?', 604 actual: '=?', 605 index: '=?' 606 }, 607 link: function (scope, element, attrs, carouselCtrl) { 608 carouselCtrl.addSlide(scope, element); 609 //when the scope is destroyed then remove the slide from the current slides array 610 scope.$on('$destroy', function() { 611 carouselCtrl.removeSlide(scope); 612 }); 613 614 scope.$watch('active', function(active) { 615 if (active) { 616 carouselCtrl.select(scope); 617 } 618 }); 619 } 620 }; 621}) 622 623.animation('.item', ['$animateCss', 624function($animateCss) { 625 var SLIDE_DIRECTION = 'uib-slideDirection'; 626 627 function removeClass(element, className, callback) { 628 element.removeClass(className); 629 if (callback) { 630 callback(); 631 } 632 } 633 634 return { 635 beforeAddClass: function(element, className, done) { 636 if (className === 'active') { 637 var stopped = false;
638 var direction = element.data(SLIDE_DIRECTION); 639 var directionClass = direction === 'next' ? 'left' : 'right'; 640 var removeClassFn = removeClass.bind(this, element, 641 directionClass + ' ' + direction, done); 642 element.addClass(direction); 643 644 $animateCss(element, {addClass: directionClass}) 645 .start() 646 .done(removeClassFn); 647 648 return function() { 649 stopped = true; 650 }; 651 } 652 done(); 653 }, 654 beforeRemoveClass: function (element, className, done) { 655 if (className === 'active') { 656 var stopped = false; 657 var direction = element.data(SLIDE_DIRECTION); 658 var directionClass = direction === 'next' ? 'left' : 'right'; 659 var removeClassFn = removeClass.bind(this, element, directionClass, done); 660 661 $animateCss(element, {addClass: directionClass}) 662 .start() 663 .done(removeClassFn); 664 665 return function() { 666 stopped = true; 667 }; 668 } 669 done(); 670 } 671 }; 672}]); 673 674angular.module('ui.bootstrap.dateparser', []) 675 676.service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) { 677 // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js 678 var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; 679 680 var localeId; 681 var formatCodeToRegex; 682 683 this.init = function() { 684 localeId = $locale.id; 685 686 this.parsers = {}; 687 688 formatCodeToRegex = [ 689 { 690 key: 'yyyy', 691 regex: '\\d{4}', 692 apply: function(value) { this.year = +value; } 693 }, 694 { 695 key: 'yy', 696 regex: '\\d{2}', 697 apply: function(value) { this.year = +value + 2000; } 698 }, 699 { 700 key: 'y', 701 regex: '\\d{1,4}', 702 apply: function(value) { this.year = +value; } 703 }, 704 { 705 key: 'M!', 706 regex: '0?[1-9]|1[0-2]', 707 apply: function(value) { this.month = value - 1; } 708 }, 709 { 710 key: 'MMMM', 711 regex: $locale.DATETIME_FORMATS.MONTH.join('|'), 712 apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); } 713 }, 714 { 715 key: 'MMM', 716 regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), 717 apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); } 718 }, 719 { 720 key: 'MM', 721 regex: '0[1-9]|1[0-2]', 722 apply: function(value) { this.month = value - 1; } 723 }, 724 { 725 key: 'M', 726 regex: '[1-9]|1[0-2]', 727 apply: function(value) { this.month = value - 1; } 728 }, 729 { 730 key: 'd!', 731 regex: '[0-2]?[0-9]{1}|3[0-1]{1}', 732 apply: function(value) { this.date = +value; } 733 }, 734 { 735 key: 'dd', 736 regex: '[0-2][0-9]{1}|3[0-1]{1}', 737 apply: function(value) { this.date = +value; } 738 }, 739 { 740 key: 'd', 741 regex: '[1-2]?[0-9]{1}|3[0-1]{1}', 742 apply: function(value) { this.date = +value; } 743 }, 744 { 745 key: 'EEEE', 746 regex: $locale.DATETIME_FORMATS.DAY.join('|') 747 }, 748 { 749 key: 'EEE', 750 regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|') 751 }, 752 { 753 key: 'HH', 754 regex: '(?:0|1)[0-9]|2[0-3]', 755 apply: function(value) { this.hours = +value; } 756 }, 757 { 758 key: 'hh', 759 regex: '0[0-9]|1[0-2]', 760 apply: function(value) { this.hours = +value; } 761 }, 762 { 763 key: 'H', 764 regex: '1?[0-9]|2[0-3]', 765 apply: function(value) { this.hours = +value; } 766 }, 767 { 768 key: 'h', 769 regex: '[0-9]|1[0-2]', 770 apply: function(value) { this.hours = +value; } 771 }, 772 { 773 key: 'mm', 774 regex: '[0-5][0-9]', 775 apply: function(value) { this.minutes = +value; } 776 }, 777 { 778 key: 'm', 779 regex: '[0-9]|[1-5][0-9]', 780 apply: function(value) { this.minutes = +value; } 781 }, 782 { 783 key: 'sss', 784 regex: '[0-9][0-9][0-9]', 785 apply: function(value) { this.milliseconds = +value; } 786 }, 787 { 788 key: 'ss', 789 regex: '[0-5][0-9]', 790 apply: function(value) { this.seconds = +value; } 791 }, 792 { 793 key: 's', 794 regex: '[0-9]|[1-5][0-9]', 795 apply: function(value) { this.seconds = +value; } 796 }, 797 { 798 key: 'a', 799 regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), 800 apply: function(value) { 801 if (this.hours === 12) { 802 this.hours = 0; 803 } 804 805 if (value === 'PM') { 806 this.hours += 12; 807 } 808 } 809 }, 810 { 811 key: 'Z', 812 regex: '[+-]\\d{4}', 813 apply: function(value) { 814 var matches = value.match(/([+-])(\d{2})(\d{2})/), 815 sign = matches[1], 816 hours = matches[2], 817 minutes = matches[3]; 818 this.hours += toInt(sign + hours); 819 this.minutes += toInt(sign + minutes); 820 } 821 }, 822 { 823 key: 'ww', 824 regex: '[0-4][0-9]|5[0-3]' 825 }, 826 { 827 key: 'w', 828 regex: '[0-9]|[1-4][0-9]|5[0-3]' 829 }, 830 { 831 key: 'GGGG', 832 regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s') 833 }, 834 { 835 key: 'GGG', 836 regex: $locale.DATETIME_FORMATS.ERAS.join('|') 837 }, 838 { 839 key: 'GG', 840 regex: $locale.DATETIME_FORMATS.ERAS.join('|') 841 }, 842 { 843 key: 'G', 844 regex: $locale.DATETIME_FORMATS.ERAS.join('|') 845 } 846 ]; 847 }; 848 849 this.init(); 850 851 function createParser(format) { 852 var map = [], regex = format.split(''); 853 854 // check for literal values 855 var quoteIndex = format.indexOf('\''); 856 if (quoteIndex > -1) { 857 var inLiteral = false;
vendor: 4,062 bytes, lines 858-995
858 format = format.split(''); 859 for (var i = quoteIndex; i < format.length; i++) { 860 if (inLiteral) { 861 if (format[i] === '\'') { 862 if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote 863 format[i+1] = '$'; 864 regex[i+1] = ''; 865 } else { // end of literal 866 regex[i] = ''; 867 inLiteral = false; 868 } 869 } 870 format[i] = '$'; 871 } else { 872 if (format[i] === '\'') { // start of literal 873 format[i] = '$'; 874 regex[i] = ''; 875 inLiteral = true; 876 } 877 } 878 } 879 880 format = format.join(''); 881 } 882 883 angular.forEach(formatCodeToRegex, function(data) { 884 var index = format.indexOf(data.key); 885 886 if (index > -1) { 887 format = format.split(''); 888 889 regex[index] = '(' + data.regex + ')'; 890 format[index] = '$'; // Custom symbol to define consumed part of format 891 for (var i = index + 1, n = index + data.key.length; i < n; i++) { 892 regex[i] = ''; 893 format[i] = '$'; 894 } 895 format = format.join(''); 896 897 map.push({ 898 index: index, 899 apply: data.apply, 900 matcher: data.regex 901 }); 902 } 903 }); 904 905 return { 906 regex: new RegExp('^' + regex.join('') + '$'), 907 map: orderByFilter(map, 'index') 908 }; 909 } 910 911 this.parse = function(input, format, baseDate) { 912 if (!angular.isString(input) || !format) { 913 return input; 914 } 915 916 format = $locale.DATETIME_FORMATS[format] || format; 917 format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&'); 918 919 if ($locale.id !== localeId) { 920 this.init(); 921 } 922 923 if (!this.parsers[format]) { 924 this.parsers[format] = createParser(format); 925 } 926 927 var parser = this.parsers[format], 928 regex = parser.regex, 929 map = parser.map, 930 results = input.match(regex), 931 tzOffset = false; 932 if (results && results.length) { 933 var fields, dt; 934 if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { 935 fields = { 936 year: baseDate.getFullYear(), 937 month: baseDate.getMonth(), 938 date: baseDate.getDate(), 939 hours: baseDate.getHours(), 940 minutes: baseDate.getMinutes(), 941 seconds: baseDate.getSeconds(), 942 milliseconds: baseDate.getMilliseconds() 943 }; 944 } else { 945 if (baseDate) { 946 $log.warn('dateparser:', 'baseDate is not a valid date'); 947 } 948 fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }; 949 } 950 951 for (var i = 1, n = results.length; i < n; i++) { 952 var mapper = map[i - 1]; 953 if (mapper.matcher === 'Z') { 954 tzOffset = true; 955 } 956 957 if (mapper.apply) { 958 mapper.apply.call(fields, results[i]); 959 } 960 } 961 962 var datesetter = tzOffset ? Date.prototype.setUTCFullYear : 963 Date.prototype.setFullYear; 964 var timesetter = tzOffset ? Date.prototype.setUTCHours : 965 Date.prototype.setHours; 966 967 if (isValid(fields.year, fields.month, fields.date)) { 968 if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) { 969 dt = new Date(baseDate); 970 datesetter.call(dt, fields.year, fields.month, fields.date); 971 timesetter.call(dt, fields.hours, fields.minutes, 972 fields.seconds, fields.milliseconds); 973 } else { 974 dt = new Date(0); 975 datesetter.call(dt, fields.year, fields.month, fields.date); 976 timesetter.call(dt, fields.hours || 0, fields.minutes || 0, 977 fields.seconds || 0, fields.milliseconds || 0); 978 } 979 } 980 981 return dt; 982 } 983 }; 984 985 // Check if date is valid for specific month (and year for February). 986 // Month: 0 = Jan, 1 = Feb, etc 987 function isValid(year, month, date) { 988 if (date < 1) { 989 return false; 990 } 991 992 if (month === 1 && date > 28) { 993 return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0); 994 } 995
996 if (month === 3 || month === 5 || month === 8 || month === 10) { 997 return date < 31; 998 } 999 1000 return true; 1001 } 1002 1003 function toInt(str) { 1004 return parseInt(str, 10); 1005 } 1006 1007 this.toTimezone = toTimezone; 1008 this.fromTimezone = fromTimezone; 1009 this.timezoneToOffset = timezoneToOffset; 1010 this.addDateMinutes = addDateMinutes; 1011 this.convertTimezoneToLocal = convertTimezoneToLocal; 1012 1013 function toTimezone(date, timezone) { 1014 return date && timezone ? convertTimezoneToLocal(date, timezone) : date; 1015 } 1016 1017 function fromTimezone(date, timezone) { 1018 return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date; 1019 } 1020 1021 //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207 1022 function timezoneToOffset(timezone, fallback) { 1023 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; 1024 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; 1025 } 1026 1027 function addDateMinutes(date, minutes) { 1028 date = new Date(date.getTime()); 1029 date.setMinutes(date.getMinutes() + minutes); 1030 return date; 1031 } 1032 1033 function convertTimezoneToLocal(date, timezone, reverse) { 1034 reverse = reverse ? -1 : 1; 1035 var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset()); 1036 return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset())); 1037 } 1038}]); 1039 1040// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to 1041// at most one element. 1042angular.module('ui.bootstrap.isClass', []) 1043.directive('uibIsClass', [ 1044 '$animate', 1045function ($animate) { 1046 // 11111111 22222222 1047 var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/; 1048 // 11111111 22222222 1049 var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/; 1050 1051 var dataPerTracked = {}; 1052 1053 return { 1054 restrict: 'A',
vendor: 6,368 bytes, lines 1055-1246
1055 compile: function (tElement, tAttrs) { 1056 var linkedScopes = []; 1057 var instances = []; 1058 var expToData = {}; 1059 var lastActivated = null; 1060 var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP); 1061 var onExp = onExpMatches[2]; 1062 var expsStr = onExpMatches[1]; 1063 var exps = expsStr.split(','); 1064 1065 return linkFn; 1066 1067 function linkFn(scope, element, attrs) { 1068 linkedScopes.push(scope); 1069 instances.push({ 1070 scope: scope, 1071 element: element 1072 }); 1073 1074 exps.forEach(function (exp, k) { 1075 addForExp(exp, scope); 1076 }); 1077 1078 scope.$on('$destroy', removeScope); 1079 } 1080 1081 function addForExp(exp, scope) { 1082 var matches = exp.match(IS_REGEXP); 1083 var clazz = scope.$eval(matches[1]); 1084 var compareWithExp = matches[2]; 1085 var data = expToData[exp]; 1086 if (!data) { 1087 var watchFn = function (compareWithVal) { 1088 var newActivated = null; 1089 instances.some(function (instance) { 1090 var thisVal = instance.scope.$eval(onExp); 1091 if (thisVal === compareWithVal) { 1092 newActivated = instance; 1093 return true; 1094 } 1095 }); 1096 if (data.lastActivated !== newActivated) { 1097 if (data.lastActivated) { 1098 $animate.removeClass(data.lastActivated.element, clazz); 1099 } 1100 if (newActivated) { 1101 $animate.addClass(newActivated.element, clazz); 1102 } 1103 data.lastActivated = newActivated; 1104 } 1105 }; 1106 expToData[exp] = data = { 1107 lastActivated: null, 1108 scope: scope, 1109 watchFn: watchFn, 1110 compareWithExp: compareWithExp, 1111 watcher: scope.$watch(compareWithExp, watchFn) 1112 }; 1113 } 1114 data.watchFn(scope.$eval(compareWithExp)); 1115 } 1116 1117 function removeScope(e) { 1118 var removedScope = e.targetScope; 1119 var index = linkedScopes.indexOf(removedScope); 1120 linkedScopes.splice(index, 1); 1121 instances.splice(index, 1); 1122 if (linkedScopes.length) { 1123 var newWatchScope = linkedScopes[0]; 1124 angular.forEach(expToData, function (data) { 1125 if (data.scope === removedScope) { 1126 data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn); 1127 data.scope = newWatchScope; 1128 } 1129 }); 1130 } 1131 else { 1132 expToData = {}; 1133 } 1134 } 1135 } 1136 }; 1137}]); 1138angular.module('ui.bootstrap.position', []) 1139 1140/** 1141 * A set of utility methods for working with the DOM. 1142 * It is meant to be used where we need to absolute-position elements in 1143 * relation to another element (this is the case for tooltips, popovers, 1144 * typeahead suggestions etc.). 1145 */ 1146 .factory('$uibPosition', ['$document', '$window', function($document, $window) { 1147 /** 1148 * Used by scrollbarWidth() function to cache scrollbar's width. 1149 * Do not access this variable directly, use scrollbarWidth() instead. 1150 */ 1151 var SCROLLBAR_WIDTH; 1152 var OVERFLOW_REGEX = { 1153 normal: /(auto|scroll)/, 1154 hidden: /(auto|scroll|hidden)/ 1155 }; 1156 var PLACEMENT_REGEX = { 1157 auto: /\s?auto?\s?/i, 1158 primary: /^(top|bottom|left|right)$/, 1159 secondary: /^(top|bottom|left|right|center)$/, 1160 vertical: /^(top|bottom)$/ 1161 }; 1162 1163 return { 1164 1165 /** 1166 * Provides a raw DOM element from a jQuery/jQLite element. 1167 * 1168 * @param {element} elem - The element to convert. 1169 * 1170 * @returns {element} A HTML element. 1171 */ 1172 getRawNode: function(elem) { 1173 return elem[0] || elem; 1174 }, 1175 1176 /** 1177 * Provides a parsed number for a style property. Strips 1178 * units and casts invalid numbers to 0. 1179 * 1180 * @param {string} value - The style value to parse. 1181 * 1182 * @returns {number} A valid number. 1183 */ 1184 parseStyle: function(value) { 1185 value = parseFloat(value); 1186 return isFinite(value) ? value : 0; 1187 }, 1188 1189 /** 1190 * Provides the closest positioned ancestor. 1191 * 1192 * @param {element} element - The element to get the offest parent for. 1193 * 1194 * @returns {element} The closest positioned ancestor. 1195 */ 1196 offsetParent: function(elem) { 1197 elem = this.getRawNode(elem); 1198 1199 var offsetParent = elem.offsetParent || $document[0].documentElement; 1200 1201 function isStaticPositioned(el) { 1202 return ($window.getComputedStyle(el).position || 'static') === 'static'; 1203 } 1204 1205 while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) { 1206 offsetParent = offsetParent.offsetParent; 1207 } 1208 1209 return offsetParent || $document[0].documentElement; 1210 }, 1211 1212 /** 1213 * Provides the scrollbar width, concept from TWBS measureScrollbar() 1214 * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js 1215 * 1216 * @returns {number} The width of the browser scollbar. 1217 */ 1218 scrollbarWidth: function() { 1219 if (angular.isUndefined(SCROLLBAR_WIDTH)) { 1220 var scrollElem = angular.element('<div style="position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;"></div>'); 1221 $document.find('body').append(scrollElem); 1222 SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth; 1223 SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0; 1224 scrollElem.remove(); 1225 } 1226 1227 return SCROLLBAR_WIDTH; 1228 }, 1229 1230 /** 1231 * Provides the closest scrollable ancestor. 1232 * A port of the jQuery UI scrollParent method: 1233 * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js 1234 * 1235 * @param {element} elem - The element to find the scroll parent of. 1236 * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, 1237 * default is false. 1238 * 1239 * @returns {element} A HTML element. 1240 */ 1241 scrollParent: function(elem, includeHidden) { 1242 elem = this.getRawNode(elem); 1243 1244 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; 1245 var documentEl = $document[0].documentElement; 1246 var elemStyle = $window.getComputedStyle(elem);
vendor: 12,075 bytes, lines 1246-1510
1246 1247 var excludeStatic = elemStyle.position === 'absolute'; 1248 var scrollParent = elem.parentElement || documentEl; 1249 1250 if (scrollParent === documentEl || elemStyle.position === 'fixed') { 1251 return documentEl; 1252 } 1253 1254 while (scrollParent.parentElement && scrollParent !== documentEl) { 1255 var spStyle = $window.getComputedStyle(scrollParent); 1256 if (excludeStatic && spStyle.position !== 'static') { 1257 excludeStatic = false; 1258 } 1259 1260 if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) { 1261 break; 1262 } 1263 scrollParent = scrollParent.parentElement; 1264 } 1265 1266 return scrollParent; 1267 }, 1268 1269 /** 1270 * Provides read-only equivalent of jQuery's position function: 1271 * http://api.jquery.com/position/ - distance to closest positioned 1272 * ancestor. Does not account for margins by default like jQuery position. 1273 * 1274 * @param {element} elem - The element to caclulate the position on. 1275 * @param {boolean=} [includeMargins=false] - Should margins be accounted 1276 * for, default is false. 1277 * 1278 * @returns {object} An object with the following properties: 1279 * <ul> 1280 * <li>**width**: the width of the element</li> 1281 * <li>**height**: the height of the element</li> 1282 * <li>**top**: distance to top edge of offset parent</li> 1283 * <li>**left**: distance to left edge of offset parent</li> 1284 * </ul> 1285 */ 1286 position: function(elem, includeMagins) { 1287 elem = this.getRawNode(elem); 1288 1289 var elemOffset = this.offset(elem); 1290 if (includeMagins) { 1291 var elemStyle = $window.getComputedStyle(elem); 1292 elemOffset.top -= this.parseStyle(elemStyle.marginTop); 1293 elemOffset.left -= this.parseStyle(elemStyle.marginLeft); 1294 } 1295 var parent = this.offsetParent(elem); 1296 var parentOffset = {top: 0, left: 0}; 1297 1298 if (parent !== $document[0].documentElement) { 1299 parentOffset = this.offset(parent); 1300 parentOffset.top += parent.clientTop - parent.scrollTop; 1301 parentOffset.left += parent.clientLeft - parent.scrollLeft; 1302 } 1303 1304 return { 1305 width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth), 1306 height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight), 1307 top: Math.round(elemOffset.top - parentOffset.top), 1308 left: Math.round(elemOffset.left - parentOffset.left) 1309 }; 1310 }, 1311 1312 /** 1313 * Provides read-only equivalent of jQuery's offset function: 1314 * http://api.jquery.com/offset/ - distance to viewport. Does 1315 * not account for borders, margins, or padding on the body 1316 * element. 1317 * 1318 * @param {element} elem - The element to calculate the offset on. 1319 * 1320 * @returns {object} An object with the following properties: 1321 * <ul> 1322 * <li>**width**: the width of the element</li> 1323 * <li>**height**: the height of the element</li> 1324 * <li>**top**: distance to top edge of viewport</li> 1325 * <li>**right**: distance to bottom edge of viewport</li> 1326 * </ul> 1327 */ 1328 offset: function(elem) { 1329 elem = this.getRawNode(elem); 1330 1331 var elemBCR = elem.getBoundingClientRect(); 1332 return { 1333 width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth), 1334 height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight), 1335 top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)), 1336 left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)) 1337 }; 1338 }, 1339 1340 /** 1341 * Provides offset distance to the closest scrollable ancestor 1342 * or viewport. Accounts for border and scrollbar width. 1343 * 1344 * Right and bottom dimensions represent the distance to the 1345 * respective edge of the viewport element. If the element 1346 * edge extends beyond the viewport, a negative value will be 1347 * reported. 1348 * 1349 * @param {element} elem - The element to get the viewport offset for. 1350 * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead 1351 * of the first scrollable element, default is false. 1352 * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element 1353 * be accounted for, default is true. 1354 * 1355 * @returns {object} An object with the following properties: 1356 * <ul> 1357 * <li>**top**: distance to the top content edge of viewport element</li> 1358 * <li>**bottom**: distance to the bottom content edge of viewport element</li> 1359 * <li>**left**: distance to the left content edge of viewport element</li> 1360 * <li>**right**: distance to the right content edge of viewport element</li> 1361 * </ul> 1362 */ 1363 viewportOffset: function(elem, useDocument, includePadding) { 1364 elem = this.getRawNode(elem); 1365 includePadding = includePadding !== false ? true : false; 1366 1367 var elemBCR = elem.getBoundingClientRect(); 1368 var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0}; 1369 1370 var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem); 1371 var offsetParentBCR = offsetParent.getBoundingClientRect(); 1372 1373 offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop; 1374 offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft; 1375 if (offsetParent === $document[0].documentElement) { 1376 offsetBCR.top += $window.pageYOffset; 1377 offsetBCR.left += $window.pageXOffset; 1378 } 1379 offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight; 1380 offsetBCR.right = offsetBCR.left + offsetParent.clientWidth; 1381 1382 if (includePadding) { 1383 var offsetParentStyle = $window.getComputedStyle(offsetParent); 1384 offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop); 1385 offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom); 1386 offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft); 1387 offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight); 1388 } 1389 1390 return { 1391 top: Math.round(elemBCR.top - offsetBCR.top), 1392 bottom: Math.round(offsetBCR.bottom - elemBCR.bottom), 1393 left: Math.round(elemBCR.left - offsetBCR.left), 1394 right: Math.round(offsetBCR.right - elemBCR.right) 1395 }; 1396 }, 1397 1398 /** 1399 * Provides an array of placement values parsed from a placement string. 1400 * Along with the 'auto' indicator, supported placement strings are: 1401 * <ul> 1402 * <li>top: element on top, horizontally centered on host element.</li> 1403 * <li>top-left: element on top, left edge aligned with host element left edge.</li> 1404 * <li>top-right: element on top, lerightft edge aligned with host element right edge.</li> 1405 * <li>bottom: element on bottom, horizontally centered on host element.</li> 1406 * <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li> 1407 * <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li> 1408 * <li>left: element on left, vertically centered on host element.</li> 1409 * <li>left-top: element on left, top edge aligned with host element top edge.</li> 1410 * <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li> 1411 * <li>right: element on right, vertically centered on host element.</li> 1412 * <li>right-top: element on right, top edge aligned with host element top edge.</li> 1413 * <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li> 1414 * </ul> 1415 * A placement string with an 'auto' indicator is expected to be 1416 * space separated from the placement, i.e: 'auto bottom-left' If 1417 * the primary and secondary placement values do not match 'top, 1418 * bottom, left, right' then 'top' will be the primary placement and 1419 * 'center' will be the secondary placement. If 'auto' is passed, true 1420 * will be returned as the 3rd value of the array. 1421 * 1422 * @param {string} placement - The placement string to parse. 1423 * 1424 * @returns {array} An array with the following values 1425 * <ul> 1426 * <li>**[0]**: The primary placement.</li> 1427 * <li>**[1]**: The secondary placement.</li> 1428 * <li>**[2]**: If auto is passed: true, else undefined.</li> 1429 * </ul> 1430 */ 1431 parsePlacement: function(placement) { 1432 var autoPlace = PLACEMENT_REGEX.auto.test(placement); 1433 if (autoPlace) { 1434 placement = placement.replace(PLACEMENT_REGEX.auto, ''); 1435 } 1436 1437 placement = placement.split('-'); 1438 1439 placement[0] = placement[0] || 'top'; 1440 if (!PLACEMENT_REGEX.primary.test(placement[0])) { 1441 placement[0] = 'top'; 1442 } 1443 1444 placement[1] = placement[1] || 'center'; 1445 if (!PLACEMENT_REGEX.secondary.test(placement[1])) { 1446 placement[1] = 'center'; 1447 } 1448 1449 if (autoPlace) { 1450 placement[2] = true; 1451 } else { 1452 placement[2] = false; 1453 } 1454 1455 return placement; 1456 }, 1457 1458 /** 1459 * Provides coordinates for an element to be positioned relative to 1460 * another element. Passing 'auto' as part of the placement parameter 1461 * will enable smart placement - where the element fits. i.e: 1462 * 'auto left-top' will check to see if there is enough space to the left 1463 * of the hostElem to fit the targetElem, if not place right (same for secondary 1464 * top placement). Available space is calculated using the viewportOffset 1465 * function. 1466 * 1467 * @param {element} hostElem - The element to position against. 1468 * @param {element} targetElem - The element to position. 1469 * @param {string=} [placement=top] - The placement for the targetElem, 1470 * default is 'top'. 'center' is assumed as secondary placement for 1471 * 'top', 'left', 'right', and 'bottom' placements. Available placements are: 1472 * <ul> 1473 * <li>top</li> 1474 * <li>top-right</li> 1475 * <li>top-left</li> 1476 * <li>bottom</li> 1477 * <li>bottom-left</li> 1478 * <li>bottom-right</li> 1479 * <li>left</li> 1480 * <li>left-top</li> 1481 * <li>left-bottom</li> 1482 * <li>right</li> 1483 * <li>right-top</li> 1484 * <li>right-bottom</li> 1485 * </ul> 1486 * @param {boolean=} [appendToBody=false] - Should the top and left values returned 1487 * be calculated from the body element, default is false. 1488 * 1489 * @returns {object} An object with the following properties: 1490 * <ul> 1491 * <li>**top**: Value for targetElem top.</li> 1492 * <li>**left**: Value for targetElem left.</li> 1493 * <li>**placement**: The resolved placement.</li> 1494 * </ul> 1495 */ 1496 positionElements: function(hostElem, targetElem, placement, appendToBody) { 1497 hostElem = this.getRawNode(hostElem); 1498 targetElem = this.getRawNode(targetElem); 1499 1500 // need to read from prop to support tests. 1501 var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth'); 1502 var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight'); 1503 1504 placement = this.parsePlacement(placement); 1505 1506 var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem); 1507 var targetElemPos = {top: 0, left: 0, placement: ''}; 1508 1509 if (placement[2]) { 1510 var viewportOffset = this.viewportOffset(hostElem
vendor: 5,008 bytes, lines 1510-1608
1510); 1511 1512 var targetElemStyle = $window.getComputedStyle(targetElem); 1513 var adjustedSize = { 1514 width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))), 1515 height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom))) 1516 }; 1517 1518 placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' : 1519 placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' : 1520 placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' : 1521 placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' : 1522 placement[0]; 1523 1524 placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' : 1525 placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' : 1526 placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' : 1527 placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' : 1528 placement[1]; 1529 1530 if (placement[1] === 'center') { 1531 if (PLACEMENT_REGEX.vertical.test(placement[0])) { 1532 var xOverflow = hostElemPos.width / 2 - targetWidth / 2; 1533 if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) { 1534 placement[1] = 'left'; 1535 } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) { 1536 placement[1] = 'right'; 1537 } 1538 } else { 1539 var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2; 1540 if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) { 1541 placement[1] = 'top'; 1542 } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) { 1543 placement[1] = 'bottom'; 1544 } 1545 } 1546 } 1547 } 1548 1549 switch (placement[0]) { 1550 case 'top': 1551 targetElemPos.top = hostElemPos.top - targetHeight; 1552 break; 1553 case 'bottom': 1554 targetElemPos.top = hostElemPos.top + hostElemPos.height; 1555 break; 1556 case 'left': 1557 targetElemPos.left = hostElemPos.left - targetWidth; 1558 break; 1559 case 'right': 1560 targetElemPos.left = hostElemPos.left + hostElemPos.width; 1561 break; 1562 } 1563 1564 switch (placement[1]) { 1565 case 'top': 1566 targetElemPos.top = hostElemPos.top; 1567 break; 1568 case 'bottom': 1569 targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight; 1570 break; 1571 case 'left': 1572 targetElemPos.left = hostElemPos.left; 1573 break; 1574 case 'right': 1575 targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth; 1576 break; 1577 case 'center': 1578 if (PLACEMENT_REGEX.vertical.test(placement[0])) { 1579 targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2; 1580 } else { 1581 targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2; 1582 } 1583 break; 1584 } 1585 1586 targetElemPos.top = Math.round(targetElemPos.top); 1587 targetElemPos.left = Math.round(targetElemPos.left); 1588 targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1]; 1589 1590 return targetElemPos; 1591 }, 1592 1593 /** 1594 * Provides a way for positioning tooltip & dropdown 1595 * arrows when using placement options beyond the standard 1596 * left, right, top, or bottom. 1597 * 1598 * @param {element} elem - The tooltip/dropdown element. 1599 * @param {string} placement - The placement for the elem. 1600 */ 1601 positionArrow: function(elem, placement) { 1602 elem = this.getRawNode(elem); 1603 1604 var isTooltip = true; 1605 1606 var innerElem = elem.querySelector('.tooltip-inner'); 1607 if (!innerElem) { 1608 isTooltip = false;
1609 innerElem = elem.querySelector('.popover-inner'); 1610 } 1611 if (!innerElem) { 1612 return; 1613 } 1614 1615 var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow'); 1616 if (!arrowElem) { 1617 return; 1618 } 1619 1620 placement = this.parsePlacement(placement); 1621 if (placement[1] === 'center') { 1622 // no adjustment necessary - just reset styles 1623 angular.element(arrowElem).css({top: '', bottom: '', right: '', left: '', margin: ''}); 1624 return; 1625 } 1626 1627 var borderProp = 'border-' + placement[0] + '-width'; 1628 var borderWidth = $window.getComputedStyle(arrowElem)[borderProp]; 1629 1630 var borderRadiusProp = 'border-'; 1631 if (PLACEMENT_REGEX.vertical.test(placement[0])) { 1632 borderRadiusProp += placement[0] + '-' + placement[1]; 1633 } else { 1634 borderRadiusProp += placement[1] + '-' + placement[0]; 1635 } 1636 borderRadiusProp += '-radius'; 1637 var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp]; 1638 1639 var arrowCss = { 1640 top: 'auto', 1641 bottom: 'auto', 1642 left: 'auto', 1643 right: 'auto', 1644 margin: 0 1645 }; 1646 1647 switch (placement[0]) { 1648 case 'top': 1649 arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth; 1650 break; 1651 case 'bottom': 1652 arrowCss.top = isTooltip ? '0' : '-' + borderWidth; 1653 break; 1654 case 'left': 1655 arrowCss.right = isTooltip ? '0' : '-' + borderWidth; 1656 break; 1657 case 'right': 1658 arrowCss.left = isTooltip ? '0' : '-' + borderWidth; 1659 break; 1660 } 1661 1662 arrowCss[placement[1]] = borderRadius; 1663 1664 angular.element(arrowElem).css(arrowCss); 1665 } 1666 }; 1667 }]); 1668 1669angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass', 'ui.bootstrap.position']) 1670 1671.value('$datepickerSuppressError', false) 1672 1673.constant('uibDatepickerConfig', { 1674 formatDay: 'dd', 1675 formatMonth: 'MMMM', 1676 formatYear: 'yyyy', 1677 formatDayHeader: 'EEE', 1678 formatDayTitle: 'MMMM yyyy', 1679 formatMonthTitle: 'yyyy', 1680 datepickerMode: 'day', 1681 minMode: 'day', 1682 maxMode: 'year', 1683 showWeeks: true, 1684 startingDay: 0, 1685 yearRows: 4, 1686 yearColumns: 5, 1687 minDate: null, 1688 maxDate: null, 1689 shortcutPropagation: false, 1690 ngModelOptions: {} 1691}) 1692 1693.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', 'uibDateParser', 1694 function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, dateParser) { 1695 var self = this, 1696 ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl; 1697 ngModelOptions = {}; 1698 1699 // Modes chain 1700 this.modes = ['day', 'month', 'year']; 1701 1702 // Interpolated configuration attributes 1703 angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle'], function(key) { 1704 self[key] = angular.isDefined($attrs[key]) ? $interpolate($attrs[key])($scope.$parent) : datepickerConfig[key]; 1705 }); 1706 1707 // Evaled configuration attributes 1708 angular.forEach(['showWeeks', 'startingDay', 'yearRows', 'yearColumns', 'shortcutPropagation'], function(key) { 1709 self[key] = angular.isDefined($attrs[key]) ? $scope.$parent.$eval($attrs[key]) : datepickerConfig[key]; 1710 }); 1711 1712 // Watchable date attributes 1713 angular.forEach(['minDate', 'maxDate'], function(key) { 1714 if ($attrs[key]) { 1715 $scope.$parent.$watch($attrs[key], function(value) { 1716 self[key] = value ? angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')) : null; 1717 self.refreshView(); 1718 }); 1719 } else { 1720 self[key] = datepickerConfig[key] ? dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : null; 1721 } 1722 }); 1723 1724 angular.forEach(['minMode', 'maxMode'], function(key) { 1725 if ($attrs[key]) { 1726 $scope.$parent.$watch($attrs[key], function(value) { 1727 self[key] = $scope[key] = angular.isDefined(value) ? value : $attrs[key]; 1728 if (key === 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key]) || 1729 key === 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key])) { 1730 $scope.datepickerMode = self[key]; 1731 } 1732 }); 1733 } else { 1734 self[key] = $scope[key] = datepickerConfig[key] || null; 1735 } 1736 }); 1737 1738 $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode; 1739 $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); 1740 1741 if (angular.isDefined($attrs.initDate)) { 1742 this.activeDate = dateParser.fromTimezone($scope.$parent.$eval($attrs.initDate), ngModelOptions.timezone) || new Date(); 1743 $scope.$parent.$watch($attrs.initDate, function(initDate) { 1744 if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invali
1744d)) { 1745 self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone); 1746 self.refreshView(); 1747 } 1748 }); 1749 } else { 1750 this.activeDate = new Date(); 1751 } 1752 1753 $scope.disabled = angular.isDefined($attrs.disabled) || false; 1754 if (angular.isDefined($attrs.ngDisabled)) { 1755 $scope.$parent.$watch($attrs.ngDisabled, function(disabled) { 1756 $scope.disabled = disabled; 1757 self.refreshView(); 1758 }); 1759 } 1760 1761 $scope.isActive = function(dateObject) { 1762 if (self.compare(dateObject.date, self.activeDate) === 0) { 1763 $scope.activeDateId = dateObject.uid; 1764 return true; 1765 } 1766 return false; 1767 }; 1768 1769 this.init = function(ngModelCtrl_) { 1770 ngModelCtrl = ngModelCtrl_; 1771 ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions; 1772 1773 if (ngModelCtrl.$modelValue) { 1774 this.activeDate = ngModelCtrl.$modelValue; 1775 } 1776 1777 ngModelCtrl.$render = function() { 1778 self.render(); 1779 }; 1780 }; 1781 1782 this.render = function() { 1783 if (ngModelCtrl.$viewValue) { 1784 var date = new Date(ngModelCtrl.$viewValue), 1785 isValid = !isNaN(date); 1786 1787 if (isValid) { 1788 this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone); 1789 } else if (!$datepickerSuppressError) { 1790 $log.error('Datepicker directive: "ng-model" value must be a Date object'); 1791 } 1792 } 1793 this.refreshView(); 1794 }; 1795 1796 this.refreshView = function() { 1797 if (this.element) { 1798 $scope.selectedDt = null; 1799 this._refreshView(); 1800 if ($scope.activeDt) { 1801 $scope.activeDateId = $scope.activeDt.uid; 1802 } 1803 1804 var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; 1805 date = dateParser.fromTimezone(date, ngModelOptions.timezone); 1806 ngModelCtrl.$setValidity('dateDisabled', !date || 1807 this.element && !this.isDisabled(date)); 1808 } 1809 }; 1810 1811 this.createDateObject = function(date, format) { 1812 var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; 1813 model = dateParser.fromTimezone(model, ngModelOptions.timezone); 1814 var dt = { 1815 date: date, 1816 label: dateFilter(date, format.replace(/d!/, 'dd')).replace(/M!/, 'MM'), 1817 selected: model && this.compare(date, model) === 0, 1818 disabled: this.isDisabled(date), 1819 current: this.compare(date, new Date()) === 0, 1820 customClass: this.customClass(date) || null 1821 }; 1822 1823 if (model && this.compare(date, model) === 0) { 1824 $scope.selectedDt = dt; 1825 } 1826 1827 if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) { 1828 $scope.activeDt = dt; 1829 } 1830 1831 return dt; 1832 }; 1833 1834 this.isDisabled = function(date) { 1835 return $scope.disabled || 1836 this.minDate && this.compare(date, this.minDate) < 0 || 1837 this.maxDate && this.compare(date, this.maxDate) > 0 || 1838 $attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode}); 1839 }; 1840 1841 this.customClass = function(date) { 1842 return $scope.customClass({date: date, mode: $scope.datepickerMode}); 1843 }; 1844 1845 // Split array into smaller arrays 1846 this.split = function(arr, size) { 1847 var arrays = []; 1848 while (arr.length > 0) { 1849 arrays.push(arr.splice(0, size)); 1850 } 1851 return arrays; 1852 }; 1853 1854 $scope.select = function(date) { 1855 if ($scope.datepickerMode === self.minMode) { 1856 var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0); 1857 dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); 1858 dt = dateParser.toTimezone(dt, ngModelOptions.timezone); 1859 ngModelCtrl.$setViewValue(dt); 1860 ngModelCtrl.$render(); 1861 } else { 1862 self.activeDate = date; 1863 $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1]; 1864 } 1865 }; 1866 1867 $scope.move = function(direction) { 1868 var year = self.activeDate.getFullYear() + direction * (self.step.years || 0), 1869 month = self.activeDate.getMonth() + direction * (self.step.months || 0); 1870 self.activeDate.setFullYear(year, month, 1); 1871 self.refreshView(); 1872 }; 1873 1874 $scope.toggleMode = function(direction) { 1875 direction = direction || 1; 1876 1877 if ($scope.datepickerMode === self.maxMode && direction === 1 || 1878 $scope.datepickerMode === self.minMode && direction === -1) { 1879 return; 1880 } 1881 1882 $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction]; 1883 }; 1884 1885 // Key event mapper 1886 $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; 1887 1888 var focusElement = function() { 1889 self.element[0].focus(); 1890 }; 1891 1892 // Listen for focus requests from popup directive 1893 $scope.$on('uib:datepicker.focus', focusElement); 1894 1895 $scope.keydown = function(evt) { 1896 var key = $scope.keys[evt.which]; 1897 1898 if (!key || evt.shiftKey || evt.altKey || $scope.disabled) { 1899 return; 1900 } 1901 1902 evt.preventDefault(); 1903 if (!self.shortcutPropagation) { 1904 evt.stopPropagation(); 1905 } 1906 1907 if (key === 'enter' || key === 'space') { 1908 if (self.isDisabled(self.activeDate)) { 1909 return; // do nothing 1910 } 1911 $scope.select(self.activeDate); 1912 } else if (evt.ctrlKey && (key === 'up' || key === 'down')) { 1913 $scope.toggleMode(key === 'up' ? 1 : -1); 1914 } else { 1915 self.handleKeyDown(key, evt); 1916 self.refreshView(); 1917 } 1918 }; 1919}]) 1920 1921.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { 1922 var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 1923 1924 this.step = { months: 1 }; 1925 this.element = $element; 1926 function getDaysInMonth(year, month) { 1927 return month === 1 && year % 4 === 0 && 1928 (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month]; 1929 } 1930 1931 this.init = function(ctrl) { 1932 angular.extend(ctrl, this); 1933 scope.showWeeks = ctrl.showWeeks; 1934 ctrl.refreshView(); 1935 }; 1936 1937 this.getDates = function(startDate, n) { 1938 var dates = new Array(n), current = new Date(startDate), i = 0, date; 1939 while (i < n) { 1940 date = new Date(current); 1941 dates[i++] = date; 1942 current.setDate(current.getDate() + 1); 1943 } 1944 return dates; 1945 }; 1946 1947 this._refreshView = function() { 1948 var year = this.activeDate.getFullYear(),
vendor: 9,362 bytes, lines 1949-2246
1949 month = this.activeDate.getMonth(), 1950 firstDayOfMonth = new Date(this.activeDate); 1951 1952 firstDayOfMonth.setFullYear(year, month, 1); 1953 1954 var difference = this.startingDay - firstDayOfMonth.getDay(), 1955 numDisplayedFromPreviousMonth = difference > 0 ? 1956 7 - difference : - difference, 1957 firstDate = new Date(firstDayOfMonth); 1958 1959 if (numDisplayedFromPreviousMonth > 0) { 1960 firstDate.setDate(-numDisplayedFromPreviousMonth + 1); 1961 } 1962 1963 // 42 is the number of days on a six-week calendar 1964 var days = this.getDates(firstDate, 42); 1965 for (var i = 0; i < 42; i ++) { 1966 days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), { 1967 secondary: days[i].getMonth() !== month, 1968 uid: scope.uniqueId + '-' + i 1969 }); 1970 } 1971 1972 scope.labels = new Array(7); 1973 for (var j = 0; j < 7; j++) { 1974 scope.labels[j] = { 1975 abbr: dateFilter(days[j].date, this.formatDayHeader), 1976 full: dateFilter(days[j].date, 'EEEE') 1977 }; 1978 } 1979 1980 scope.title = dateFilter(this.activeDate, this.formatDayTitle); 1981 scope.rows = this.split(days, 7); 1982 1983 if (scope.showWeeks) { 1984 scope.weekNumbers = []; 1985 var thursdayIndex = (4 + 7 - this.startingDay) % 7, 1986 numWeeks = scope.rows.length; 1987 for (var curWeek = 0; curWeek < numWeeks; curWeek++) { 1988 scope.weekNumbers.push( 1989 getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date)); 1990 } 1991 } 1992 }; 1993 1994 this.compare = function(date1, date2) { 1995 var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()); 1996 var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); 1997 _date1.setFullYear(date1.getFullYear()); 1998 _date2.setFullYear(date2.getFullYear()); 1999 return _date1 - _date2; 2000 }; 2001 2002 function getISO8601WeekNumber(date) { 2003 var checkDate = new Date(date); 2004 checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday 2005 var time = checkDate.getTime(); 2006 checkDate.setMonth(0); // Compare with Jan 1 2007 checkDate.setDate(1); 2008 return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; 2009 } 2010 2011 this.handleKeyDown = function(key, evt) { 2012 var date = this.activeDate.getDate(); 2013 2014 if (key === 'left') { 2015 date = date - 1; 2016 } else if (key === 'up') { 2017 date = date - 7; 2018 } else if (key === 'right') { 2019 date = date + 1; 2020 } else if (key === 'down') { 2021 date = date + 7; 2022 } else if (key === 'pageup' || key === 'pagedown') { 2023 var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1); 2024 this.activeDate.setMonth(month, 1); 2025 date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date); 2026 } else if (key === 'home') { 2027 date = 1; 2028 } else if (key === 'end') { 2029 date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()); 2030 } 2031 this.activeDate.setDate(date); 2032 }; 2033}]) 2034 2035.controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { 2036 this.step = { years: 1 }; 2037 this.element = $element; 2038 2039 this.init = function(ctrl) { 2040 angular.extend(ctrl, this); 2041 ctrl.refreshView(); 2042 }; 2043 2044 this._refreshView = function() { 2045 var months = new Array(12), 2046 year = this.activeDate.getFullYear(), 2047 date; 2048 2049 for (var i = 0; i < 12; i++) { 2050 date = new Date(this.activeDate); 2051 date.setFullYear(year, i, 1); 2052 months[i] = angular.extend(this.createDateObject(date, this.formatMonth), { 2053 uid: scope.uniqueId + '-' + i 2054 }); 2055 } 2056 2057 scope.title = dateFilter(this.activeDate, this.formatMonthTitle); 2058 scope.rows = this.split(months, 3); 2059 }; 2060 2061 this.compare = function(date1, date2) { 2062 var _date1 = new Date(date1.getFullYear(), date1.getMonth()); 2063 var _date2 = new Date(date2.getFullYear(), date2.getMonth()); 2064 _date1.setFullYear(date1.getFullYear()); 2065 _date2.setFullYear(date2.getFullYear()); 2066 return _date1 - _date2; 2067 }; 2068 2069 this.handleKeyDown = function(key, evt) { 2070 var date = this.activeDate.getMonth(); 2071 2072 if (key === 'left') { 2073 date = date - 1; 2074 } else if (key === 'up') { 2075 date = date - 3; 2076 } else if (key === 'right') { 2077 date = date + 1; 2078 } else if (key === 'down') { 2079 date = date + 3; 2080 } else if (key === 'pageup' || key === 'pagedown') { 2081 var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1); 2082 this.activeDate.setFullYear(year); 2083 } else if (key === 'home') { 2084 date = 0; 2085 } else if (key === 'end') { 2086 date = 11; 2087 } 2088 this.activeDate.setMonth(date); 2089 }; 2090}]) 2091 2092.controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { 2093 var columns, range; 2094 this.element = $element; 2095 2096 function getStartingYear(year) { 2097 return parseInt((year - 1) / range, 10) * range + 1; 2098 } 2099 2100 this.yearpickerInit = function() { 2101 columns = this.yearColumns; 2102 range = this.yearRows * columns; 2103 this.step = { years: range }; 2104 }; 2105 2106 this._refreshView = function() { 2107 var years = new Array(range), date; 2108 2109 for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) { 2110 date = new Date(this.activeDate); 2111 date.setFullYear(start + i, 0, 1); 2112 years[i] = angular.extend(this.createDateObject(date, this.formatYear), { 2113 uid: scope.uniqueId + '-' + i 2114 }); 2115 } 2116 2117 scope.title = [years[0].label, years[range - 1].label].join(' - '); 2118 scope.rows = this.split(years, columns); 2119 scope.columns = columns; 2120 }; 2121 2122 this.compare = function(date1, date2) { 2123 return date1.getFullYear() - date2.getFullYear(); 2124 }; 2125 2126 this.handleKeyDown = function(key, evt) { 2127 var date = this.activeDate.getFullYear(); 2128 2129 if (key === 'left') { 2130 date = date - 1; 2131 } else if (key === 'up') { 2132 date = date - columns; 2133 } else if (key === 'right') { 2134 date = date + 1; 2135 } else if (key === 'down') { 2136 date = date + columns; 2137 } else if (key === 'pageup' || key === 'pagedown') { 2138 date += (key === 'pageup' ? - 1 : 1) * range; 2139 } else if (key === 'home') { 2140 date = getStartingYear(this.activeDate.getFullYear()); 2141 } else if (key === 'end') { 2142 date = getStartingYear(this.activeDate.getFullYear()) + range - 1; 2143 } 2144 this.activeDate.setFullYear(date); 2145 }; 2146}]) 2147 2148.directive('uibDatepicker', function() { 2149 return { 2150 replace: true, 2151 templateUrl: function(element, attrs) { 2152 return attrs.templateUrl || 'uib/template/datepicker/datepicker.html'; 2153 }, 2154 scope: { 2155 datepickerMode: '=?', 2156 dateDisabled: '&', 2157 customClass: '&', 2158 shortcutPropagation: '&?' 2159 }, 2160 require: ['uibDatepicker', '^ngModel'], 2161 controller: 'UibDatepickerController', 2162 controllerAs: 'datepicker', 2163 link: function(scope, element, attrs, ctrls) { 2164 var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; 2165 2166 datepickerCtrl.init(ngModelCtrl); 2167 } 2168 }; 2169}) 2170 2171.directive('uibDaypicker', function() { 2172 return { 2173 replace: true, 2174 templateUrl: function(element, attrs) { 2175 return attrs.templateUrl || 'uib/template/datepicker/day.html'; 2176 }, 2177 require: ['^uibDatepicker', 'uibDaypicker'], 2178 controller: 'UibDaypickerController', 2179 link: function(scope, element, attrs, ctrls) { 2180 var datepickerCtrl = ctrls[0], 2181 daypickerCtrl = ctrls[1]; 2182 2183 daypickerCtrl.init(datepickerCtrl); 2184 } 2185 }; 2186}) 2187 2188.directive('uibMonthpicker', function() { 2189 return { 2190 replace: true, 2191 templateUrl: function(element, attrs) { 2192 return attrs.templateUrl || 'uib/template/datepicker/month.html'; 2193 }, 2194 require: ['^uibDatepicker', 'uibMonthpicker'], 2195 controller: 'UibMonthpickerController', 2196 link: function(scope, element, attrs, ctrls) { 2197 var datepickerCtrl = ctrls[0], 2198 monthpickerCtrl = ctrls[1]; 2199 2200 monthpickerCtrl.init(datepickerCtrl); 2201 } 2202 }; 2203}) 2204 2205.directive('uibYearpicker', function() { 2206 return { 2207 replace: true, 2208 templateUrl: function(element, attrs) { 2209 return attrs.templateUrl || 'uib/template/datepicker/year.html'; 2210 }, 2211 require: ['^uibDatepicker', 'uibYearpicker'], 2212 controller: 'UibYearpickerController', 2213 link: function(scope, element, attrs, ctrls) { 2214 var ctrl = ctrls[0]; 2215 angular.extend(ctrl, ctrls[1]); 2216 ctrl.yearpickerInit(); 2217 2218 ctrl.refreshView(); 2219 } 2220 }; 2221}) 2222 2223.constant('uibDatepickerPopupConfig', { 2224 datepickerPopup: 'yyyy-MM-dd', 2225 datepickerPopupTemplateUrl: 'uib/template/datepicker/popup.html', 2226 datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html', 2227 html5Types: { 2228 date: 'yyyy-MM-dd', 2229 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss', 2230 'month': 'yyyy-MM' 2231 }, 2232 currentText: 'Today', 2233 clearText: 'Clear', 2234 closeText: 'Done', 2235 closeOnDateSelection: true, 2236 appendToBody: false, 2237 showButtonBar: true, 2238 onOpenFocus: true, 2239 altInputFormats: [] 2240}) 2241 2242.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', 2243function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig) { 2244 var self = this; 2245 var cache = {}, 2246 isHtml5DateInput = false;
2247 var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus, 2248 datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, 2249 ngModel, ngModelOptions, $popup, altInputFormats; 2250 2251 scope.watchData = {}; 2252 2253 this.init = function(_ngModel_) { 2254 ngModel = _ngModel_; 2255 ngModelOptions = _ngModel_.$options || datepickerConfig.ngModelOptions; 2256 closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection; 2257 appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody; 2258 onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus; 2259 datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl; 2260 datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl; 2261 altInputFormats = angular.isDefined(attrs.altInputFormats) ? scope.$parent.$eval(attrs.altInputFormats) : datepickerPopupConfig.altInputFormats; 2262 2263 scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar; 2264 2265 if (datepickerPopupConfig.html5Types[attrs.type]) { 2266 dateFormat = datepickerPopupConfig.html5Types[attrs.type]; 2267 isHtml5DateInput = true; 2268 } else { 2269 dateFormat = attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup; 2270 attrs.$observe('uibDatepickerPopup', function(value, oldValue) { 2271 var newDateFormat = value || datepickerPopupConfig.datepickerPopup; 2272 // Invalidate the $modelValue to ensure that formatters re-run 2273 // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764 2274 if (newDateFormat !== dateFormat) { 2275 dateFormat = newDateFormat; 2276 ngModel.$modelValue = null; 2277 2278 if (!dateFormat) { 2279 throw new Error('uibDatepickerPopup must have a date format specified.'); 2280 } 2281 } 2282 }); 2283 } 2284 2285 if (!dateFormat) { 2286 throw new Error('uibDatepickerPopup must have a date format specified.'); 2287 } 2288 2289 if (isHtml5DateInput && attrs.uibDatepickerPopup) { 2290 throw new Error('HTML5 date input types do not support custom formats.'); 2291 } 2292 2293 // popup element used to display calendar 2294 popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>'); 2295 scope.ngModelOptions = angular.copy(ngModelOptions); 2296 scope.ngModelOptions.timezone = null; 2297 popupEl.attr({ 2298 'ng-model': 'date', 2299 'ng-model-options': 'ngModelOptions', 2300 'ng-change': 'dateSelection(date)', 2301 'template-url': datepickerPopupTemplateUrl 2302 }); 2303 2304 // datepicker element 2305 datepickerEl = angular.element(popupEl.children()[0]); 2306 datepickerEl.attr('template-url', datepickerTemplateUrl); 2307 2308 if (isHtml5DateInput) { 2309 if (attrs.type === 'month') { 2310 datepickerEl.attr('datepicker-mode', '"month"'); 2311 datepickerEl.attr('min-mode', 'month'); 2312 } 2313 } 2314 2315 if (attrs.datepickerOptions) { 2316 var options = scope.$parent.$eval(attrs.datepickerOptions); 2317 if (options && options.initDate) { 2318 scope.initDate = dateParser.fromTimezone(options.initDate, ngModelOptions.timezone); 2319 datepickerEl.attr('init-date', 'initDate'); 2320 delete options.initDate; 2321 } 2322 angular.forEach(options, function(value, option) { 2323 datepickerEl.attr(cameltoDash(option), value); 2324 }); 2325 } 2326 2327 angular.forEach(['minMode', 'maxMode'], function(key) { 2328 if (attrs[key]) { 2329 scope.$parent.$watch(function() { return attrs[key]; }, function(value) { 2330 scope.watchData[key] = value; 2331 }); 2332 datepickerEl.attr(cameltoDash(key), 'watchData.' + key); 2333 } 2334 }); 2335 2336 angular.forEach(['datepickerMode', 'shortcutPropagation'], function(key) { 2337 if (attrs[key]) { 2338 var getAttribute = $parse(attrs[key]); 2339 var propConfig = { 2340 get: function() { 2341 return getAttribute(scope.$parent); 2342 } 2343 }; 2344 2345 datepickerEl.attr(cameltoDash(key), 'watchData.' + key); 2346 2347 // Propagate changes from datepicker to outside 2348 if (key === 'datepickerMode') { 2349 var setAttribute = getAttribute.assign; 2350 propConfig.set = function(v) { 2351 setAttribute(scope.$parent, v); 2352 }; 2353 } 2354 2355 Object.defineProperty(scope.watchData, key, propConfig); 2356 } 2357 }); 2358 2359 angular.forEach(['minDate', 'maxDate', 'initDate'], function(key) { 2360 if (attrs[key]) { 2361 var getAttribute = $parse(attrs[key]); 2362 2363 scope.$parent.$watch(getAttribute, function(value) { 2364 if (key === 'minDate' || key === 'maxDate') { 2365 cache[key] = angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')); 2366 } 2367 2368 scope.watchData[key] = cache[key] || dateParser.fromTimezone(new Date(value), ngModelOptions.timezone); 2369 }); 2370 2371 datepickerEl.attr(cameltoDash(key), 'watchData.' + key); 2372 } 2373 }); 2374 2375 if (attrs.dateDisabled) { 2376 datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })'); 2377 } 2378 2379 angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', 'showWeeks', 'startingDay', 'yearRows', 'yearColumns'], function(key) { 2380 if (angular.isDefined(attrs[key])) { 2381 datepickerEl.attr(cameltoDash(key), attrs[key]); 2382 } 2383 }); 2384 2385 if (attrs.customClass) { 2386 datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })'); 2387 } 2388 2389 if (!isHtml5DateInput) { 2390 // Internal API to maintain the correct ng-invalid-[key] class 2391 ngModel.$$parserName = 'date'; 2392 ngModel.$validators.date = validator; 2393 ngModel.$parsers.unshift(parseDate); 2394 ngModel.$formatters.push(function(value) { 2395 if (ngModel.$isEmpty(value)) { 2396 scope.date = value; 2397 return value; 2398 } 2399 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone); 2400 dateFormat = dateFormat.replace(/M!/, 'MM') 2401 .replace(/d!/, 'dd'); 2402 2403 return dateFilter(scope.date, dateFormat); 2404 }); 2405 } else { 2406 ngModel.$formatters.push(function(value) { 2407 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone); 2408 return value; 2409 }); 2410 } 2411 2412 // Detect changes in the view from the text box 2413 ngModel.$viewChangeListeners.push(function() { 2414 scope.date = parseDateString(ngModel.$viewValue); 2415 }); 2416 2417 element.bind('keydown', inputKeydownBind); 2418 2419 $popup = $compile(popupEl)(scope); 2420 // Prevent jQuery cache memory leak (template is now redundant after linking) 2421 popupEl.remove(); 2422 2423 if (appendToBody) { 2424 $document.find('body').append($popup); 2425 } else { 2426 element.after($popup); 2427 } 2428 2429 scope.$on('$destroy', function() { 2430 if (scope.isOpen === true) { 2431 if (!$rootScope.$$phase) { 2432 scope.$apply(function() { 2433 scope.isOpen = false;
2434 }); 2435 } 2436 } 2437 2438 $popup.remove(); 2439 element.unbind('keydown', inputKeydownBind); 2440 $document.unbind('click', documentClickBind); 2441 }); 2442 }; 2443 2444 scope.getText = function(key) { 2445 return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text']; 2446 }; 2447 2448 scope.isDisabled = function(date) { 2449 if (date === 'today') { 2450 date = new Date(); 2451 } 2452 2453 return scope.watchData.minDate && scope.compare(date, cache.minDate) < 0 || 2454 scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0; 2455 }; 2456 2457 scope.compare = function(date1, date2) { 2458 return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); 2459 }; 2460 2461 // Inner change 2462 scope.dateSelection = function(dt) { 2463 if (angular.isDefined(dt)) { 2464 scope.date = dt; 2465 } 2466 var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function 2467 element.val(date); 2468 ngModel.$setViewValue(date); 2469 2470 if (closeOnDateSelection) { 2471 scope.isOpen = false; 2472 element[0].focus(); 2473 } 2474 }; 2475 2476 scope.keydown = function(evt) { 2477 if (evt.which === 27) { 2478 evt.stopPropagation(); 2479 scope.isOpen = false; 2480 element[0].focus(); 2481 } 2482 }; 2483 2484 scope.select = function(date) { 2485 if (date === 'today') { 2486 var today = new Date(); 2487 if (angular.isDate(scope.date)) { 2488 date = new Date(scope.date); 2489 date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); 2490 } else { 2491 date = new Date(today.setHours(0, 0, 0, 0)); 2492 } 2493 } 2494 scope.dateSelection(date); 2495 }; 2496 2497 scope.close = function() { 2498 scope.isOpen = false; 2499 element[0].focus(); 2500 }; 2501 2502 scope.disabled = angular.isDefined(attrs.disabled) || false; 2503 if (attrs.ngDisabled) { 2504 scope.$parent.$watch($parse(attrs.ngDisabled), function(disabled) { 2505 scope.disabled = disabled; 2506 }); 2507 } 2508 2509 scope.$watch('isOpen', function(value) { 2510 if (value) { 2511 if (!scope.disabled) { 2512 scope.position = appendToBody ? $position.offset(element) : $position.position(element); 2513 scope.position.top = scope.position.top + element.prop('offsetHeight'); 2514 2515 $timeout(function() { 2516 if (onOpenFocus) { 2517 scope.$broadcast('uib:datepicker.focus'); 2518 } 2519 $document.bind('click', documentClickBind); 2520 }, 0, false); 2521 } else { 2522 scope.isOpen = false; 2523 } 2524 } else { 2525 $document.unbind('click', documentClickBind); 2526 } 2527 }); 2528 2529 function cameltoDash(string) { 2530 return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); }); 2531 } 2532 2533 function parseDateString(viewValue) { 2534 var date = dateParser.parse(viewValue, dateFormat, scope.date); 2535 if (isNaN(date)) { 2536 for (var i = 0; i < altInputFormats.length; i++) { 2537 date = dateParser.parse(viewValue, altInputFormats[i], scope.date); 2538 if (!isNaN(date)) { 2539 return date; 2540 } 2541 } 2542 } 2543 return date; 2544 } 2545 2546 function parseDate(viewValue) { 2547 if (angular.isNumber(viewValue)) { 2548 // presumably timestamp to date object 2549 viewValue = new Date(viewValue); 2550 } 2551 2552 if (!viewValue) { 2553 return null; 2554 } 2555 2556 if (angular.isDate(viewValue) && !isNaN(viewValue)) { 2557 return viewValue; 2558 } 2559 2560 if (angular.isString(viewValue)) { 2561 var date = parseDateString(viewValue); 2562 if (!isNaN(date)) { 2563 return dateParser.toTimezone(date, ngModelOptions.timezone); 2564 } 2565 } 2566 2567 return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined; 2568 } 2569 2570 function validator(modelValue, viewValue) { 2571 var value = modelValue || viewValue; 2572 2573 if (!attrs.ngRequired && !value) { 2574 return true; 2575 } 2576 2577 if (angular.isNumber(value)) { 2578 value = new Date(value); 2579 } 2580 2581 if (!value) { 2582 return true; 2583 } 2584 2585 if (angular.isDate(value) && !isNaN(value)) { 2586 return true; 2587 } 2588 2589 if (angular.isString(value)) { 2590 return !isNaN(parseDateString(viewValue)); 2591 } 2592 2593 return false; 2594 } 2595 2596 function documentClickBind(event) { 2597 if (!scope.isOpen && scope.disabled) { 2598 return; 2599 } 2600 2601 var popup = $popup[0]; 2602 var dpContainsTarget = element[0].contains(event.target); 2603 // The popup node may not be an element node 2604 // In some browsers (IE) only element nodes have the 'contains' function 2605 var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target); 2606 if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) { 2607 scope.$apply(function() { 2608 scope.isOpen = false;
2609 }); 2610 } 2611 } 2612 2613 function inputKeydownBind(evt) { 2614 if (evt.which === 27 && scope.isOpen) { 2615 evt.preventDefault(); 2616 evt.stopPropagation(); 2617 scope.$apply(function() { 2618 scope.isOpen = false; 2619 }); 2620 element[0].focus(); 2621 } else if (evt.which === 40 && !scope.isOpen) { 2622 evt.preventDefault(); 2623 evt.stopPropagation(); 2624 scope.$apply(function() { 2625 scope.isOpen = true; 2626 }); 2627 } 2628 } 2629}]) 2630 2631.directive('uibDatepickerPopup', function() { 2632 return { 2633 require: ['ngModel', 'uibDatepickerPopup'], 2634 controller: 'UibDatepickerPopupController', 2635 scope: { 2636 isOpen: '=?', 2637 currentText: '@', 2638 clearText: '@', 2639 closeText: '@', 2640 dateDisabled: '&', 2641 customClass: '&' 2642 }, 2643 link: function(scope, element, attrs, ctrls) { 2644 var ngModel = ctrls[0], 2645 ctrl = ctrls[1]; 2646 2647 ctrl.init(ngModel); 2648 } 2649 }; 2650}) 2651 2652.directive('uibDatepickerPopupWrap', function() { 2653 return { 2654 replace: true, 2655 transclude: true, 2656 templateUrl: function(element, attrs) { 2657 return attrs.templateUrl || 'uib/template/datepicker/popup.html'; 2658 } 2659 }; 2660}); 2661 2662angular.module('ui.bootstrap.debounce', []) 2663/** 2664 * A helper, internal service that debounces a function 2665 */ 2666 .factory('$$debounce', ['$timeout', function($timeout) { 2667 return function(callback, debounceTime) { 2668 var timeoutPromise; 2669 2670 return function() { 2671 var self = this; 2672 var args = Array.prototype.slice.call(arguments); 2673 if (timeoutPromise) { 2674 $timeout.cancel(timeoutPromise); 2675 } 2676 2677 timeoutPromise = $timeout(function() { 2678 callback.apply(self, args); 2679 }, debounceTime); 2680 }; 2681 }; 2682 }]); 2683 2684angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position']) 2685 2686.constant('uibDropdownConfig', { 2687 appendToOpenClass: 'uib-dropdown-open', 2688 openClass: 'open' 2689}) 2690 2691.service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) { 2692 var openScope = null; 2693 2694 this.open = function(dropdownScope) { 2695 if (!openScope) { 2696 $document.on('click', closeDropdown); 2697 $document.on('keydown', keybindFilter); 2698 } 2699 2700 if (openScope && openScope !== dropdownScope) { 2701 openScope.isOpen = false; 2702 } 2703 2704 openScope = dropdownScope; 2705 }; 2706 2707 this.close = function(dropdownScope) { 2708 if (openScope === dropdownScope) { 2709 openScope = null; 2710 $document.off('click', closeDropdown); 2711 $document.off('keydown', keybindFilter); 2712 } 2713 }; 2714 2715 var closeDropdown = function(evt) { 2716 // This method may still be called during the same mouse event that 2717 // unbound this event handler. So check openScope before proceeding. 2718 if (!openScope) { return; } 2719 2720 if (evt && openScope.getAutoClose() === 'disabled') { return; } 2721 2722 if (evt && evt.which === 3) { return; } 2723 2724 var toggleElement = openScope.getToggleElement(); 2725 if (evt && toggleElement && toggleElement[0].contains(evt.target)) { 2726 return; 2727 } 2728 2729 var dropdownElement = openScope.getDropdownElement(); 2730 if (evt && openScope.getAutoClose() === 'outsideClick' && 2731 dropdownElement && dropdownElement[0].contains(evt.target)) { 2732 return; 2733 } 2734 2735 openScope.isOpen = false; 2736 2737 if (!$rootScope.$$phase) { 2738 openScope.$apply(); 2739 } 2740 }; 2741 2742 var keybindFilter = function(evt) { 2743 if (evt.which === 27) { 2744 openScope.focusToggleElement(); 2745 closeDropdown(); 2746 } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) { 2747 evt.preventDefault(); 2748 evt.stopPropagation(); 2749 openScope.focusDropdownEntry(evt.which); 2750 } 2751 }; 2752}]) 2753 2754.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) { 2755 var self = this, 2756 scope = $scope.$new(), // create a child scope so we are not polluting original one 2757 templateScope, 2758 appendToOpenClass = dropdownConfig.appendToOpenClass, 2759 openClass = dropdownConfig.openClass, 2760 getIsOpen, 2761 setIsOpen = angular.noop, 2762 toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, 2763 appendToBody = false, 2764 appendTo = null, 2765 keynavEnabled = false, 2766 selectedOption = null, 2767 body = $document.find('body'); 2768 2769 $element.addClass('dropdown'); 2770 2771 this.init = function() { 2772 if ($attrs.isOpen) { 2773 getIsOpen = $parse($attrs.isOpen); 2774 setIsOpen = getIsOpen.assign; 2775 2776 $scope.$watch(getIsOpen, function(value) { 2777 scope.isOpen = !!value; 2778 }); 2779 } 2780 2781 if (angular.isDefined($attrs.dropdownAppendTo)) { 2782 var appendToEl = $parse($attrs.dropdownAppendTo)(scope); 2783 if (appendToEl) { 2784 appendTo = angular.element(appendToEl); 2785 } 2786 } 2787 2788 appendToBody = angular.isDefined($attrs.dropdownAppendToBody); 2789 keynavEnabled = angular.isDefined($attrs.keyboardNav); 2790 2791 if (appendToBody && !appendTo) { 2792 appendTo = body; 2793 } 2794 2795 if (appendTo && self.dropdownMenu) { 2796 appendTo.append(self.dropdownMenu); 2797 $element.on('$destroy', function handleDestroyEvent() { 2798 self.dropdownMenu.remove(); 2799 }); 2800 } 2801 }; 2802 2803 this.toggle = function(open) { 2804 return scope.isOpen = arguments.length ? !!open : !scope.isOpen; 2805 }; 2806
2807 // Allow other directives to watch status 2808 this.isOpen = function() { 2809 return scope.isOpen; 2810 }; 2811 2812 scope.getToggleElement = function() { 2813 return self.toggleElement; 2814 }; 2815 2816 scope.getAutoClose = function() { 2817 return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' 2818 }; 2819 2820 scope.getElement = function() { 2821 return $element; 2822 }; 2823 2824 scope.isKeynavEnabled = function() { 2825 return keynavEnabled; 2826 }; 2827 2828 scope.focusDropdownEntry = function(keyCode) { 2829 var elems = self.dropdownMenu ? //If append to body is used. 2830 angular.element(self.dropdownMenu).find('a') : 2831 $element.find('ul').eq(0).find('a'); 2832 2833 switch (keyCode) { 2834 case 40: { 2835 if (!angular.isNumber(self.selectedOption)) { 2836 self.selectedOption = 0; 2837 } else { 2838 self.selectedOption = self.selectedOption === elems.length - 1 ? 2839 self.selectedOption : 2840 self.selectedOption + 1; 2841 } 2842 break; 2843 } 2844 case 38: { 2845 if (!angular.isNumber(self.selectedOption)) { 2846 self.selectedOption = elems.length - 1; 2847 } else { 2848 self.selectedOption = self.selectedOption === 0 ? 2849 0 : self.selectedOption - 1; 2850 } 2851 break; 2852 } 2853 } 2854 elems[self.selectedOption].focus(); 2855 }; 2856 2857 scope.getDropdownElement = function() { 2858 return self.dropdownMenu; 2859 }; 2860 2861 scope.focusToggleElement = function() { 2862 if (self.toggleElement) { 2863 self.toggleElement[0].focus(); 2864 } 2865 }; 2866 2867 scope.$watch('isOpen', function(isOpen, wasOpen) { 2868 if (appendTo && self.dropdownMenu) { 2869 var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true), 2870 css, 2871 rightalign; 2872 2873 css = { 2874 top: pos.top + 'px', 2875 display: isOpen ? 'block' : 'none' 2876 }; 2877 2878 rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); 2879 if (!rightalign) { 2880 css.left = pos.left + 'px'; 2881 css.right = 'auto'; 2882 } else { 2883 css.left = 'auto'; 2884 css.right = window.innerWidth - 2885 (pos.left + $element.prop('offsetWidth')) + 'px'; 2886 } 2887 2888 // Need to adjust our positioning to be relative to the appendTo container 2889 // if it's not the body element 2890 if (!appendToBody) { 2891 var appendOffset = $position.offset(appendTo); 2892 2893 css.top = pos.top - appendOffset.top + 'px'; 2894 2895 if (!rightalign) { 2896 css.left = pos.left - appendOffset.left + 'px'; 2897 } else { 2898 css.right = window.innerWidth - 2899 (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px'; 2900 } 2901 } 2902 2903 self.dropdownMenu.css(css); 2904 } 2905 2906 var openContainer = appendTo ? appendTo : $element; 2907 2908 $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() { 2909 if (angular.isDefined(isOpen) && isOpen !== wasOpen) { 2910 toggleInvoker($scope, { open: !!isOpen }); 2911 } 2912 }); 2913 2914 if (isOpen) { 2915 if (self.dropdownMenuTemplateUrl) { 2916 $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) { 2917 templateScope = scope.$new(); 2918 $compile(tplContent.trim())(templateScope, function(dropdownElement) { 2919 var newEl = dropdownElement; 2920 self.dropdownMenu.replaceWith(newEl); 2921 self.dropdownMenu = newEl; 2922 }); 2923 }); 2924 } 2925 2926 scope.focusToggleElement(); 2927 uibDropdownService.open(scope); 2928 } else { 2929 if (self.dropdownMenuTemplateUrl) { 2930 if (templateScope) { 2931 templateScope.$destroy(); 2932 } 2933 var newEl = angular.element('<ul class="dropdown-menu"></ul>'); 2934 self.dropdownMenu.replaceWith(newEl); 2935 self.dropdownMenu = newEl; 2936 } 2937 2938 uibDropdownService.close(scope); 2939 self.selectedOption = null; 2940 } 2941 2942 if (angular.isFunction(setIsOpen)) { 2943 setIsOpen($scope, isOpen); 2944 } 2945 }); 2946 2947 $scope.$on('$locationChangeSuccess', function() { 2948 if (scope.getAutoClose() !== 'disabled') { 2949 scope.isOpen = false; 2950 } 2951 }); 2952}]) 2953 2954.directive('uibDropdown', function() { 2955 return { 2956 controller: 'UibDropdownController', 2957 link: function(scope, element, attrs, dropdownCtrl) { 2958 dropdownCtrl.init(); 2959 } 2960 }; 2961}) 2962 2963.directive('uibDropdownMenu', function() { 2964 return { 2965 restrict: 'A', 2966 require: '?^uibDropdown', 2967 link: function(scope, element, attrs, dropdownCtrl) { 2968 if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) { 2969 return; 2970 } 2971 2972 element.addClass('dropdown-menu'); 2973 2974 var tplUrl = attrs.templateUrl; 2975 if (tplUrl) { 2976 dropdownCtrl.dropdownMenuTemplateUrl = tplUrl; 2977 } 2978 2979 if (!dropdownCtrl.dropdownMenu) { 2980 dropdownCtrl.dropdownMenu = element; 2981 } 2982 } 2983 }; 2984}) 2985 2986.directive('uibDropdownToggle', function() { 2987 return { 2988 require: '?^uibDropdown', 2989 link: function(scope, element, attrs, dropdownCtrl) { 2990 if (!dropdownCtrl) { 2991 return; 2992 } 2993 2994 element.addClass('dropdown-toggle'); 2995 2996 dropdownCtrl.toggleElement = element; 2997 2998 var toggleDropdown = function(event) { 2999 event.preventDefault(); 3000 3001 if (!element.hasClass('disabled') && !attrs.disabled) { 3002 scope.$apply(function() {
vendor: 4,837 bytes, lines 3003-3186
3003 dropdownCtrl.toggle(); 3004 }); 3005 } 3006 }; 3007 3008 element.bind('click', toggleDropdown); 3009 3010 // WAI-ARIA 3011 element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); 3012 scope.$watch(dropdownCtrl.isOpen, function(isOpen) { 3013 element.attr('aria-expanded', !!isOpen); 3014 }); 3015 3016 scope.$on('$destroy', function() { 3017 element.unbind('click', toggleDropdown); 3018 }); 3019 } 3020 }; 3021}); 3022 3023angular.module('ui.bootstrap.stackedMap', []) 3024/** 3025 * A helper, internal data structure that acts as a map but also allows getting / removing 3026 * elements in the LIFO order 3027 */ 3028 .factory('$$stackedMap', function() { 3029 return { 3030 createNew: function() { 3031 var stack = []; 3032 3033 return { 3034 add: function(key, value) { 3035 stack.push({ 3036 key: key, 3037 value: value 3038 }); 3039 }, 3040 get: function(key) { 3041 for (var i = 0; i < stack.length; i++) { 3042 if (key === stack[i].key) { 3043 return stack[i]; 3044 } 3045 } 3046 }, 3047 keys: function() { 3048 var keys = []; 3049 for (var i = 0; i < stack.length; i++) { 3050 keys.push(stack[i].key); 3051 } 3052 return keys; 3053 }, 3054 top: function() { 3055 return stack[stack.length - 1]; 3056 }, 3057 remove: function(key) { 3058 var idx = -1; 3059 for (var i = 0; i < stack.length; i++) { 3060 if (key === stack[i].key) { 3061 idx = i; 3062 break; 3063 } 3064 } 3065 return stack.splice(idx, 1)[0]; 3066 }, 3067 removeTop: function() { 3068 return stack.splice(stack.length - 1, 1)[0]; 3069 }, 3070 length: function() { 3071 return stack.length; 3072 } 3073 }; 3074 } 3075 }; 3076 }); 3077angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) 3078/** 3079 * A helper, internal data structure that stores all references attached to key 3080 */ 3081 .factory('$$multiMap', function() { 3082 return { 3083 createNew: function() { 3084 var map = {}; 3085 3086 return { 3087 entries: function() { 3088 return Object.keys(map).map(function(key) { 3089 return { 3090 key: key, 3091 value: map[key] 3092 }; 3093 }); 3094 }, 3095 get: function(key) { 3096 return map[key]; 3097 }, 3098 hasKey: function(key) { 3099 return !!map[key]; 3100 }, 3101 keys: function() { 3102 return Object.keys(map); 3103 }, 3104 put: function(key, value) { 3105 if (!map[key]) { 3106 map[key] = []; 3107 } 3108 3109 map[key].push(value); 3110 }, 3111 remove: function(key, value) { 3112 var values = map[key]; 3113 3114 if (!values) { 3115 return; 3116 } 3117 3118 var idx = values.indexOf(value); 3119 3120 if (idx !== -1) { 3121 values.splice(idx, 1); 3122 } 3123 3124 if (!values.length) { 3125 delete map[key]; 3126 } 3127 } 3128 }; 3129 } 3130 }; 3131 }) 3132 3133/** 3134 * Pluggable resolve mechanism for the modal resolve resolution 3135 * Supports UI Router's $resolve service 3136 */ 3137 .provider('$uibResolve', function() { 3138 var resolve = this; 3139 this.resolver = null; 3140 3141 this.setResolver = function(resolver) { 3142 this.resolver = resolver; 3143 }; 3144 3145 this.$get = ['$injector', '$q', function($injector, $q) { 3146 var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null; 3147 return { 3148 resolve: function(invocables, locals, parent, self) { 3149 if (resolver) { 3150 return resolver.resolve(invocables, locals, parent, self); 3151 } 3152 3153 var promises = []; 3154 3155 angular.forEach(invocables, function(value) { 3156 if (angular.isFunction(value) || angular.isArray(value)) { 3157 promises.push($q.resolve($injector.invoke(value))); 3158 } else if (angular.isString(value)) { 3159 promises.push($q.resolve($injector.get(value))); 3160 } else { 3161 promises.push($q.resolve(value)); 3162 } 3163 }); 3164 3165 return $q.all(promises).then(function(resolves) { 3166 var resolveObj = {}; 3167 var resolveIter = 0; 3168 angular.forEach(invocables, function(value, key) { 3169 resolveObj[key] = resolves[resolveIter++]; 3170 }); 3171 3172 return resolveObj; 3173 }); 3174 } 3175 }; 3176 }]; 3177 }) 3178 3179/** 3180 * A helper directive for the $modal service. It creates a backdrop element. 3181 */ 3182 .directive('uibModalBackdrop', ['$animateCss', '$injector', '$uibModalStack', 3183 function($animateCss, $injector, $modalStack) { 3184 return { 3185 replace: true, 3186 templateUrl: 'uib/template/modal/backdrop.html',
3187 compile: function(tElement, tAttrs) { 3188 tElement.addClass(tAttrs.backdropClass); 3189 return linkFn; 3190 } 3191 }; 3192 3193 function linkFn(scope, element, attrs) { 3194 if (attrs.modalInClass) { 3195 $animateCss(element, { 3196 addClass: attrs.modalInClass 3197 }).start(); 3198 3199 scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { 3200 var done = setIsAsync(); 3201 if (scope.modalOptions.animation) { 3202 $animateCss(element, { 3203 removeClass: attrs.modalInClass 3204 }).start().then(done); 3205 } else { 3206 done(); 3207 } 3208 }); 3209 } 3210 } 3211 }]) 3212 3213 .directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', '$document', 3214 function($modalStack, $q, $animate, $animateCss, $document) { 3215 return { 3216 scope: { 3217 index: '@' 3218 }, 3219 replace: true, 3220 transclude: true, 3221 templateUrl: function(tElement, tAttrs) { 3222 return tAttrs.templateUrl || 'uib/template/modal/window.html'; 3223 }, 3224 link: function(scope, element, attrs) { 3225 element.addClass(attrs.windowClass || ''); 3226 element.addClass(attrs.windowTopClass || ''); 3227 scope.size = attrs.size; 3228 3229 scope.close = function(evt) { 3230 var modal = $modalStack.getTop(); 3231 if (modal && modal.value.backdrop && 3232 modal.value.backdrop !== 'static' && 3233 evt.target === evt.currentTarget) { 3234 evt.preventDefault(); 3235 evt.stopPropagation(); 3236 $modalStack.dismiss(modal.key, 'backdrop click'); 3237 } 3238 }; 3239 3240 // moved from template to fix issue #2280 3241 element.on('click', scope.close); 3242 3243 // This property is only added to the scope for the purpose of detecting when this directive is rendered. 3244 // We can detect that by using this property in the template associated with this directive and then use 3245 // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. 3246 scope.$isRendered = true; 3247 3248 // Deferred object that will be resolved when this modal is render. 3249 var modalRenderDeferObj = $q.defer(); 3250 // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. 3251 // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. 3252 attrs.$observe('modalRender', function(value) { 3253 if (value === 'true') { 3254 modalRenderDeferObj.resolve(); 3255 } 3256 }); 3257 3258 modalRenderDeferObj.promise.then(function() { 3259 var animationPromise = null; 3260 3261 if (attrs.modalInClass) { 3262 animationPromise = $animateCss(element, { 3263 addClass: attrs.modalInClass 3264 }).start(); 3265 3266 scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { 3267 var done = setIsAsync(); 3268 if ($animateCss) { 3269 $animateCss(element, { 3270 removeClass: attrs.modalInClass 3271 }).start().then(done); 3272 } else { 3273 $animate.removeClass(element, attrs.modalInClass).then(done); 3274 } 3275 }); 3276 } 3277 3278 3279 $q.when(animationPromise).then(function() { 3280 /** 3281 * If something within the freshly-opened modal already has focus (perhaps via a 3282 * directive that causes focus). then no need to try and focus anything. 3283 */ 3284 if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) { 3285 var inputWithAutofocus = element[0].querySelector('[autofocus]'); 3286 /** 3287 * Auto-focusing of a freshly-opened modal element causes any child elements 3288 * with the autofocus attribute to lose focus. This is an issue on touch 3289 * based devices which will show and then hide the onscreen keyboard. 3290 * Attempts to refocus the autofocus element via JavaScript will not reopen 3291 * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus 3292 * the modal element if the modal does not contain an autofocus element. 3293 */ 3294 if (inputWithAutofocus) { 3295 inputWithAutofocus.focus(); 3296 } else { 3297 element[0].focus(); 3298 } 3299 } 3300 }); 3301 3302 // Notify {@link $modalStack} that modal is rendered. 3303 var modal = $modalStack.getTop(); 3304 if (modal) { 3305 $modalStack.modalRendered(modal.key); 3306 } 3307 }); 3308 } 3309 }; 3310 }]) 3311 3312 .directive('uibModalAnimationClass', function() { 3313 return {
3314 compile: function(tElement, tAttrs) { 3315 if (tAttrs.modalAnimation) { 3316 tElement.addClass(tAttrs.uibModalAnimationClass); 3317 } 3318 } 3319 }; 3320 }) 3321 3322 .directive('uibModalTransclude', function() { 3323 return { 3324 link: function(scope, element, attrs, controller, transclude) { 3325 transclude(scope.$parent, function(clone) { 3326 element.empty(); 3327 element.append(clone); 3328 }); 3329 } 3330 }; 3331 }) 3332 3333 .factory('$uibModalStack', ['$animate', '$animateCss', '$document', 3334 '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', 3335 function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap) { 3336 var OPENED_MODAL_CLASS = 'modal-open'; 3337 3338 var backdropDomEl, backdropScope; 3339 var openedWindows = $$stackedMap.createNew(); 3340 var openedClasses = $$multiMap.createNew(); 3341 var $modalStack = { 3342 NOW_CLOSING_EVENT: 'modal.stack.now-closing' 3343 }; 3344 3345 //Modal focus behavior 3346 var focusableElementList; 3347 var focusIndex = 0; 3348 var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' + 3349 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' + 3350 'iframe, object, embed, *[tabindex], *[contenteditable=true]'; 3351 3352 function backdropIndex() { 3353 var topBackdropIndex = -1; 3354 var opened = openedWindows.keys(); 3355 for (var i = 0; i < opened.length; i++) { 3356 if (openedWindows.get(opened[i]).value.backdrop) { 3357 topBackdropIndex = i; 3358 } 3359 } 3360 return topBackdropIndex; 3361 } 3362 3363 $rootScope.$watch(backdropIndex, function(newBackdropIndex) { 3364 if (backdropScope) { 3365 backdropScope.index = newBackdropIndex; 3366 } 3367 }); 3368 3369 function removeModalWindow(modalInstance, elementToReceiveFocus) { 3370 var modalWindow = openedWindows.get(modalInstance).value; 3371 var appendToElement = modalWindow.appendTo; 3372 3373 //clean up the stack 3374 openedWindows.remove(modalInstance); 3375 3376 removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { 3377 var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; 3378 openedClasses.remove(modalBodyClass, modalInstance); 3379 appendToElement.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass)); 3380 toggleTopWindowClass(true); 3381 }); 3382 checkRemoveBackdrop(); 3383 3384 //move focus to specified element if available, or else to body 3385 if (elementToReceiveFocus && elementToReceiveFocus.focus) { 3386 elementToReceiveFocus.focus(); 3387 } else if (appendToElement.focus) { 3388 appendToElement.focus(); 3389 } 3390 } 3391 3392 // Add or remove "windowTopClass" from the top window in the stack 3393 function toggleTopWindowClass(toggleSwitch) { 3394 var modalWindow; 3395 3396 if (openedWindows.length() > 0) { 3397 modalWindow = openedWindows.top().value; 3398 modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch); 3399 } 3400 } 3401 3402 function checkRemoveBackdrop() { 3403 //remove backdrop if no longer needed 3404 if (backdropDomEl && backdropIndex() === -1) { 3405 var backdropScopeRef = backdropScope; 3406 removeAfterAnimate(backdropDomEl, backdropScope, function() { 3407 backdropScopeRef = null; 3408 }); 3409 backdropDomEl = undefined; 3410 backdropScope = undefined; 3411 } 3412 } 3413 3414 function removeAfterAnimate(domEl, scope, done, closedDeferred) { 3415 var asyncDeferred; 3416 var asyncPromise = null; 3417 var setIsAsync = function() { 3418 if (!asyncDeferred) { 3419 asyncDeferred = $q.defer(); 3420 asyncPromise = asyncDeferred.promise; 3421 } 3422 3423 return function asyncDone() { 3424 asyncDeferred.resolve(); 3425 }; 3426 }; 3427 scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync); 3428 3429 // Note that it's intentional that asyncPromise might be null. 3430 // That's when setIsAsync has not been called during the 3431 // NOW_CLOSING_EVENT broadcast. 3432 return $q.when(asyncPromise).then(afterAnimating); 3433 3434 function afterAnimating() { 3435 if (afterAnimating.done) { 3436 return; 3437 } 3438 afterAnimating.done = true; 3439 3440 $animateCss(domEl, { 3441 event: 'leave' 3442 }).start().then(function() { 3443 domEl.remove(); 3444 if (closedDeferred) { 3445 closedDeferred.resolve(); 3446 } 3447 }); 3448 3449 scope.$destroy(); 3450 if (done) { 3451 done(); 3452 } 3453 } 3454 } 3455 3456 $document.on('keydown', keydownListener); 3457 3458 $rootScope.$on('$destroy', function() { 3459 $document.off('keydown', keydownListener); 3460 }); 3461 3462 function keydownListener(evt) { 3463 if (evt.isDefaultPrevented()) { 3464 return evt; 3465 } 3466 3467 var modal = openedWindows.top(); 3468 if (modal) { 3469 switch (evt.which) { 3470 case 27: { 3471 if (modal.value.keyboard) { 3472 evt.preventDefault(); 3473 $rootScope.$apply(function() { 3474 $modalStack.dismiss(modal.key, 'escape key press'); 3475 }); 3476 } 3477 break; 3478 } 3479 case 9: { 3480 $modalStack.loadFocusElementList(modal); 3481 var focusChanged = false;
3482 if (evt.shiftKey) { 3483 if ($modalStack.isFocusInFirstItem(evt)) { 3484 focusChanged = $modalStack.focusLastFocusableElement(); 3485 } 3486 } else { 3487 if ($modalStack.isFocusInLastItem(evt)) { 3488 focusChanged = $modalStack.focusFirstFocusableElement(); 3489 } 3490 } 3491 3492 if (focusChanged) { 3493 evt.preventDefault(); 3494 evt.stopPropagation(); 3495 } 3496 break; 3497 } 3498 } 3499 } 3500 } 3501 3502 $modalStack.open = function(modalInstance, modal) { 3503 var modalOpener = $document[0].activeElement, 3504 modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS; 3505 3506 toggleTopWindowClass(false); 3507 3508 openedWindows.add(modalInstance, { 3509 deferred: modal.deferred, 3510 renderDeferred: modal.renderDeferred, 3511 closedDeferred: modal.closedDeferred, 3512 modalScope: modal.scope, 3513 backdrop: modal.backdrop, 3514 keyboard: modal.keyboard, 3515 openedClass: modal.openedClass, 3516 windowTopClass: modal.windowTopClass, 3517 animation: modal.animation, 3518 appendTo: modal.appendTo 3519 }); 3520 3521 openedClasses.put(modalBodyClass, modalInstance); 3522 3523 var appendToElement = modal.appendTo, 3524 currBackdropIndex = backdropIndex(); 3525 3526 if (!appendToElement.length) { 3527 throw new Error('appendTo element not found. Make sure that the element passed is in DOM.'); 3528 } 3529 3530 if (currBackdropIndex >= 0 && !backdropDomEl) { 3531 backdropScope = $rootScope.$new(true); 3532 backdropScope.modalOptions = modal; 3533 backdropScope.index = currBackdropIndex; 3534 backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>'); 3535 backdropDomEl.attr('backdrop-class', modal.backdropClass); 3536 if (modal.animation) { 3537 backdropDomEl.attr('modal-animation', 'true'); 3538 } 3539 $compile(backdropDomEl)(backdropScope); 3540 $animate.enter(backdropDomEl, appendToElement); 3541 } 3542 3543 var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>'); 3544 angularDomEl.attr({ 3545 'template-url': modal.windowTemplateUrl, 3546 'window-class': modal.windowClass, 3547 'window-top-class': modal.windowTopClass, 3548 'size': modal.size, 3549 'index': openedWindows.length() - 1, 3550 'animate': 'animate' 3551 }).html(modal.content); 3552 if (modal.animation) { 3553 angularDomEl.attr('modal-animation', 'true'); 3554 } 3555 3556 $animate.enter(angularDomEl, appendToElement) 3557 .then(function() { 3558 $compile(angularDomEl)(modal.scope); 3559 $animate.addClass(appendToElement, modalBodyClass); 3560 }); 3561 3562 openedWindows.top().value.modalDomEl = angularDomEl; 3563 openedWindows.top().value.modalOpener = modalOpener; 3564 3565 $modalStack.clearFocusListCache(); 3566 }; 3567 3568 function broadcastClosing(modalWindow, resultOrReason, closing) { 3569 return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented; 3570 } 3571 3572 $modalStack.close = function(modalInstance, result) { 3573 var modalWindow = openedWindows.get(modalInstance); 3574 if (modalWindow && broadcastClosing(modalWindow, result, true)) { 3575 modalWindow.value.modalScope.$$uibDestructionScheduled = true; 3576 modalWindow.value.deferred.resolve(result); 3577 removeModalWindow(modalInstance, modalWindow.value.modalOpener); 3578 return true; 3579 } 3580 return !modalWindow; 3581 }; 3582 3583 $modalStack.dismiss = function(modalInstance, reason) { 3584 var modalWindow = openedWindows.get(modalInstance); 3585 if (modalWindow && broadcastClosing(modalWindow, reason, false)) { 3586 modalWindow.value.modalScope.$$uibDestructionScheduled = true; 3587 modalWindow.value.deferred.reject(reason); 3588 removeModalWindow(modalInstance, modalWindow.value.modalOpener); 3589 return true; 3590 } 3591 return !modalWindow; 3592 }; 3593 3594 $modalStack.dismissAll = function(reason) { 3595 var topModal = this.getTop(); 3596 while (topModal && this.dismiss(topModal.key, reason)) { 3597 topModal = this.getTop(); 3598 } 3599 }; 3600 3601 $modalStack.getTop = function() { 3602 return openedWindows.top(); 3603 }; 3604 3605 $modalStack.modalRendered = function(modalInstance) { 3606 var modalWindow = openedWindows.get(modalInstance); 3607 if (modalWindow) { 3608 modalWindow.value.renderDeferred.resolve(); 3609 } 3610 }; 3611 3612 $modalStack.focusFirstFocusableElement = function() { 3613 if (focusableElementList.length > 0) { 3614 focusableElementList[0].focus(); 3615 return true; 3616 } 3617 return false;
3618 }; 3619 $modalStack.focusLastFocusableElement = function() { 3620 if (focusableElementList.length > 0) { 3621 focusableElementList[focusableElementList.length - 1].focus(); 3622 return true; 3623 } 3624 return false; 3625 }; 3626 3627 $modalStack.isFocusInFirstItem = function(evt) { 3628 if (focusableElementList.length > 0) { 3629 return (evt.target || evt.srcElement) === focusableElementList[0]; 3630 } 3631 return false; 3632 }; 3633 3634 $modalStack.isFocusInLastItem = function(evt) { 3635 if (focusableElementList.length > 0) { 3636 return (evt.target || evt.srcElement) === focusableElementList[focusableElementList.length - 1]; 3637 } 3638 return false; 3639 }; 3640 3641 $modalStack.clearFocusListCache = function() { 3642 focusableElementList = []; 3643 focusIndex = 0; 3644 }; 3645 3646 $modalStack.loadFocusElementList = function(modalWindow) { 3647 if (focusableElementList === undefined || !focusableElementList.length) { 3648 if (modalWindow) { 3649 var modalDomE1 = modalWindow.value.modalDomEl; 3650 if (modalDomE1 && modalDomE1.length) { 3651 focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector); 3652 } 3653 } 3654 } 3655 }; 3656 3657 return $modalStack; 3658 }]) 3659 3660 .provider('$uibModal', function() { 3661 var $modalProvider = { 3662 options: { 3663 animation: true, 3664 backdrop: true, //can also be false or 'static' 3665 keyboard: true 3666 }, 3667 $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack', 3668 function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) { 3669 var $modal = {}; 3670 3671 function getTemplatePromise(options) { 3672 return options.template ? $q.when(options.template) : 3673 $templateRequest(angular.isFunction(options.templateUrl) ? 3674 options.templateUrl() : options.templateUrl); 3675 } 3676 3677 var promiseChain = null; 3678 $modal.getPromiseChain = function() { 3679 return promiseChain; 3680 }; 3681 3682 $modal.open = function(modalOptions) { 3683 var modalResultDeferred = $q.defer(); 3684 var modalOpenedDeferred = $q.defer(); 3685 var modalClosedDeferred = $q.defer(); 3686 var modalRenderDeferred = $q.defer(); 3687 3688 //prepare an instance of a modal to be injected into controllers and returned to a caller 3689 var modalInstance = { 3690 result: modalResultDeferred.promise, 3691 opened: modalOpenedDeferred.promise, 3692 closed: modalClosedDeferred.promise, 3693 rendered: modalRenderDeferred.promise, 3694 close: function (result) { 3695 return $modalStack.close(modalInstance, result); 3696 }, 3697 dismiss: function (reason) { 3698 return $modalStack.dismiss(modalInstance, reason); 3699 } 3700 }; 3701 3702 //merge and clean up options 3703 modalOptions = angular.extend({}, $modalProvider.options, modalOptions); 3704 modalOptions.resolve = modalOptions.resolve || {}; 3705 modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0); 3706 3707 //verify options 3708 if (!modalOptions.template && !modalOptions.templateUrl) { 3709 throw new Error('One of template or templateUrl options is required.'); 3710 } 3711 3712 var templateAndResolvePromise = 3713 $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]); 3714 3715 function resolveWithTemplate() { 3716 return templateAndResolvePromise; 3717 } 3718 3719 // Wait for the resolution of the existing promise chain. 3720 // Then switch to our own combined promise dependency (regardless of how the previous modal fared). 3721 // Then add to $modalStack and resolve opened. 3722 // Finally clean up the chain variable if no subsequent modal has overwritten it. 3723 var samePromise; 3724 samePromise = promiseChain = $q.all([promiseChain]) 3725 .then(resolveWithTemplate, resolveWithTemplate) 3726 .then(function resolveSuccess(tplAndVars) { 3727 var providedScope = modalOptions.scope || $rootScope; 3728 3729 var modalScope = providedScope.$new(); 3730 modalScope.$close = modalInstance.close; 3731 modalScope.$dismiss = modalInstance.dismiss; 3732 3733 modalScope.$on('$destroy', function() { 3734 if (!modalScope.$$uibDestructionScheduled) { 3735 modalScope.$dismiss('$uibUnscheduledDestruction'); 3736 } 3737 }); 3738 3739 var ctrlInstance, ctrlLocals = {}; 3740 3741 //controllers 3742 if (modalOptions.controller) { 3743 ctrlLocals.$scope = modalScope; 3744 ctrlLocals.$uibModalInstance = modalInstance; 3745 angular.forEach(tplAndVars[1], function(value, key) { 3746 ctrlLocals[key] = value; 3747 }); 3748 3749 ctrlInstance = $controller(modalOptions.controller, ctrlLocals); 3750 if (modalOptions.controllerAs) { 3751 if (modalOptions.bindToController) { 3752 ctrlInstance.$close = modalScope.$close; 3753 ctrlInstance.$dismiss = modalScope.$dismiss; 3754 angular.extend(ctrlInstance, providedScope); 3755 } 3756 3757 modalScope[modalOptions.controllerAs] = ctrlInstance; 3758 } 3759 } 3760 3761 $modalStack.open(modalInstance, { 3762 scope: modalScope, 3763 deferred: modalResultDeferred, 3764 renderDeferred: modalRenderDeferred, 3765 closedDeferred: modalClosedDeferred, 3766 content: tplAndVars[0], 3767 animation: modalOptions.animation, 3768 backdrop: modalOptions.backdrop, 3769 keyboard: modalOptions.keyboard, 3770 backdropClass: modalOptions.backdropClass, 3771 windowTopClass: modalOptions.windowTopClass, 3772 windowClass: modalOptions.windowClass, 3773 windowTemplateUrl: modalOptions.windowTemplateUrl, 3774 size: modalOptions.size, 3775 openedClass: modalOptions.openedClass, 3776 appendTo: modalOptions.appendTo 3777 }); 3778 modalOpenedDeferred.resolve(true); 3779 3780 }, function resolveError(reason) { 3781 modalOpenedDeferred.reject(reason); 3782 modalResultDeferred.reject(reason); 3783 })['finally'](function() { 3784 if (promiseChain === samePromise) { 3785 promiseChain = null; 3786 } 3787 }); 3788 3789 return modalInstance; 3790 }; 3791 3792 return $modal; 3793 } 3794 ] 3795 }; 3796 3797 return $modalProvider; 3798 }); 3799 3800angular.module('ui.bootstrap.paging', []) 3801/** 3802 * Helper internal service for generating common controller code between the 3803 * pager and pagination components 3804 */ 3805.factory('uibPaging', ['$parse', function($parse) { 3806 return { 3807 create: function(ctrl, $scope, $attrs) { 3808 ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; 3809 ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl 3810 3811 ctrl.init = function(ngModelCtrl, config) { 3812 ctrl.ngModelCtrl = ngModelCtrl; 3813 ctrl.config = config; 3814 3815 ngModelCtrl.$render = function() { 3816 ctrl.render(); 3817 }; 3818 3819 if ($attrs.itemsPerPage) { 3820 $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) { 3821 ctrl.itemsPerPage = parseInt(value, 10); 3822 $scope.totalPages = ctrl.calculateTotalPages(); 3823 ctrl.updatePage(); 3824 }); 3825 } else { 3826 ctrl.itemsPerPage = config.itemsPerPage; 3827 } 3828 3829 $scope.$watch('totalItems', function(newTotal, oldTotal) {
vendor: 13,335 bytes, lines 3830-4227
3830 if (angular.isDefined(newTotal) || newTotal !== oldTotal) { 3831 $scope.totalPages = ctrl.calculateTotalPages(); 3832 ctrl.updatePage(); 3833 } 3834 }); 3835 }; 3836 3837 ctrl.calculateTotalPages = function() { 3838 var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage); 3839 return Math.max(totalPages || 0, 1); 3840 }; 3841 3842 ctrl.render = function() { 3843 $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1; 3844 }; 3845 3846 $scope.selectPage = function(page, evt) { 3847 if (evt) { 3848 evt.preventDefault(); 3849 } 3850 3851 var clickAllowed = !$scope.ngDisabled || !evt; 3852 if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) { 3853 if (evt && evt.target) { 3854 evt.target.blur(); 3855 } 3856 ctrl.ngModelCtrl.$setViewValue(page); 3857 ctrl.ngModelCtrl.$render(); 3858 } 3859 }; 3860 3861 $scope.getText = function(key) { 3862 return $scope[key + 'Text'] || ctrl.config[key + 'Text']; 3863 }; 3864 3865 $scope.noPrevious = function() { 3866 return $scope.page === 1; 3867 }; 3868 3869 $scope.noNext = function() { 3870 return $scope.page === $scope.totalPages; 3871 }; 3872 3873 ctrl.updatePage = function() { 3874 ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable 3875 3876 if ($scope.page > $scope.totalPages) { 3877 $scope.selectPage($scope.totalPages); 3878 } else { 3879 ctrl.ngModelCtrl.$render(); 3880 } 3881 }; 3882 } 3883 }; 3884}]); 3885 3886angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging']) 3887 3888.controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) { 3889 $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align; 3890 3891 uibPaging.create(this, $scope, $attrs); 3892}]) 3893 3894.constant('uibPagerConfig', { 3895 itemsPerPage: 10, 3896 previousText: '« Previous', 3897 nextText: 'Next »', 3898 align: true 3899}) 3900 3901.directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) { 3902 return { 3903 scope: { 3904 totalItems: '=', 3905 previousText: '@', 3906 nextText: '@', 3907 ngDisabled: '=' 3908 }, 3909 require: ['uibPager', '?ngModel'], 3910 controller: 'UibPagerController', 3911 controllerAs: 'pager', 3912 templateUrl: function(element, attrs) { 3913 return attrs.templateUrl || 'uib/template/pager/pager.html'; 3914 }, 3915 replace: true, 3916 link: function(scope, element, attrs, ctrls) { 3917 var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; 3918 3919 if (!ngModelCtrl) { 3920 return; // do nothing if no ng-model 3921 } 3922 3923 paginationCtrl.init(ngModelCtrl, uibPagerConfig); 3924 } 3925 }; 3926}]); 3927 3928angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging']) 3929.controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) { 3930 var ctrl = this; 3931 // Setup configuration parameters 3932 var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize, 3933 rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate, 3934 forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses, 3935 boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers; 3936 $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks; 3937 $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks; 3938 3939 uibPaging.create(this, $scope, $attrs); 3940 3941 if ($attrs.maxSize) { 3942 $scope.$parent.$watch($parse($attrs.maxSize), function(value) { 3943 maxSize = parseInt(value, 10); 3944 ctrl.render(); 3945 }); 3946 } 3947 3948 // Create page object used in template 3949 function makePage(number, text, isActive) { 3950 return { 3951 number: number, 3952 text: text, 3953 active: isActive 3954 }; 3955 } 3956 3957 function getPages(currentPage, totalPages) { 3958 var pages = []; 3959 3960 // Default page limits 3961 var startPage = 1, endPage = totalPages; 3962 var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages; 3963 3964 // recompute if maxSize 3965 if (isMaxSized) { 3966 if (rotate) { 3967 // Current page is displayed in the middle of the visible ones 3968 startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1); 3969 endPage = startPage + maxSize - 1; 3970 3971 // Adjust if limit is exceeded 3972 if (endPage > totalPages) { 3973 endPage = totalPages; 3974 startPage = endPage - maxSize + 1; 3975 } 3976 } else { 3977 // Visible pages are paginated with maxSize 3978 startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1; 3979 3980 // Adjust last page if limit is exceeded 3981 endPage = Math.min(startPage + maxSize - 1, totalPages); 3982 } 3983 } 3984 3985 // Add page number links 3986 for (var number = startPage; number <= endPage; number++) { 3987 var page = makePage(number, number, number === currentPage); 3988 pages.push(page); 3989 } 3990 3991 // Add links to move between page sets 3992 if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) { 3993 if (startPage > 1) { 3994 if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning 3995 var previousPageSet = makePage(startPage - 1, '...', false); 3996 pages.unshift(previousPageSet); 3997 } 3998 if (boundaryLinkNumbers) { 3999 if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential 4000 var secondPageLink = makePage(2, '2', false); 4001 pages.unshift(secondPageLink); 4002 } 4003 //add the first page 4004 var firstPageLink = makePage(1, '1', false); 4005 pages.unshift(firstPageLink); 4006 } 4007 } 4008 4009 if (endPage < totalPages) { 4010 if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end 4011 var nextPageSet = makePage(endPage + 1, '...', false); 4012 pages.push(nextPageSet); 4013 } 4014 if (boundaryLinkNumbers) { 4015 if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential 4016 var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false); 4017 pages.push(secondToLastPageLink); 4018 } 4019 //add the last page 4020 var lastPageLink = makePage(totalPages, totalPages, false); 4021 pages.push(lastPageLink); 4022 } 4023 } 4024 } 4025 return pages; 4026 } 4027 4028 var originalRender = this.render; 4029 this.render = function() { 4030 originalRender(); 4031 if ($scope.page > 0 && $scope.page <= $scope.totalPages) { 4032 $scope.pages = getPages($scope.page, $scope.totalPages); 4033 } 4034 }; 4035}]) 4036 4037.constant('uibPaginationConfig', { 4038 itemsPerPage: 10, 4039 boundaryLinks: false, 4040 boundaryLinkNumbers: false, 4041 directionLinks: true, 4042 firstText: 'First', 4043 previousText: 'Previous', 4044 nextText: 'Next', 4045 lastText: 'Last', 4046 rotate: true, 4047 forceEllipses: false 4048}) 4049 4050.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) { 4051 return { 4052 scope: { 4053 totalItems: '=', 4054 firstText: '@', 4055 previousText: '@', 4056 nextText: '@', 4057 lastText: '@', 4058 ngDisabled:'=' 4059 }, 4060 require: ['uibPagination', '?ngModel'], 4061 controller: 'UibPaginationController', 4062 controllerAs: 'pagination', 4063 templateUrl: function(element, attrs) { 4064 return attrs.templateUrl || 'uib/template/pagination/pagination.html'; 4065 }, 4066 replace: true, 4067 link: function(scope, element, attrs, ctrls) { 4068 var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; 4069 4070 if (!ngModelCtrl) { 4071 return; // do nothing if no ng-model 4072 } 4073 4074 paginationCtrl.init(ngModelCtrl, uibPaginationConfig); 4075 } 4076 }; 4077}]); 4078 4079/** 4080 * The following features are still outstanding: animation as a 4081 * function, placement as a function, inside, support for more triggers than 4082 * just mouse enter/leave, html tooltips, and selector delegation. 4083 */ 4084angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap']) 4085 4086/** 4087 * The $tooltip service creates tooltip- and popover-like directives as well as 4088 * houses global options for them. 4089 */ 4090.provider('$uibTooltip', function() { 4091 // The default options tooltip and popover. 4092 var defaultOptions = { 4093 placement: 'top', 4094 placementClassPrefix: '', 4095 animation: true, 4096 popupDelay: 0, 4097 popupCloseDelay: 0, 4098 useContentExp: false 4099 }; 4100 4101 // Default hide triggers for each show trigger 4102 var triggerMap = { 4103 'mouseenter': 'mouseleave', 4104 'click': 'click', 4105 'outsideClick': 'outsideClick', 4106 'focus': 'blur', 4107 'none': '' 4108 }; 4109 4110 // The options specified to the provider globally. 4111 var globalOptions = {}; 4112 4113 /** 4114 * `options({})` allows global configuration of all tooltips in the 4115 * application. 4116 * 4117 * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { 4118 * // place tooltips left instead of top by default 4119 * $tooltipProvider.options( { placement: 'left' } ); 4120 * }); 4121 */ 4122 this.options = function(value) { 4123 angular.extend(globalOptions, value); 4124 }; 4125 4126 /** 4127 * This allows you to extend the set of trigger mappings available. E.g.: 4128 * 4129 * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ); 4130 */ 4131 this.setTriggers = function setTriggers(triggers) { 4132 angular.extend(triggerMap, triggers); 4133 }; 4134 4135 /** 4136 * This is a helper function for translating camel-case to snake_case. 4137 */ 4138 function snake_case(name) { 4139 var regexp = /[A-Z]/g; 4140 var separator = '-'; 4141 return name.replace(regexp, function(letter, pos) { 4142 return (pos ? separator : '') + letter.toLowerCase(); 4143 }); 4144 } 4145 4146 /** 4147 * Returns the actual instance of the $tooltip service. 4148 * TODO support multiple triggers 4149 */ 4150 this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { 4151 var openedTooltips = $$stackedMap.createNew(); 4152 $document.on('keypress', keypressListener); 4153 4154 $rootScope.$on('$destroy', function() { 4155 $document.off('keypress', keypressListener); 4156 }); 4157 4158 function keypressListener(e) { 4159 if (e.which === 27) { 4160 var last = openedTooltips.top(); 4161 if (last) { 4162 last.value.close(); 4163 openedTooltips.removeTop(); 4164 last = null; 4165 } 4166 } 4167 } 4168 4169 return function $tooltip(ttType, prefix, defaultTriggerShow, options) { 4170 options = angular.extend({}, defaultOptions, globalOptions, options); 4171 4172 /** 4173 * Returns an object of show and hide triggers. 4174 * 4175 * If a trigger is supplied, 4176 * it is used to show the tooltip; otherwise, it will use the `trigger` 4177 * option passed to the `$tooltipProvider.options` method; else it will 4178 * default to the trigger supplied to this directive factory. 4179 * 4180 * The hide trigger is based on the show trigger. If the `trigger` option 4181 * was passed to the `$tooltipProvider.options` method, it will use the 4182 * mapped trigger from `triggerMap` or the passed trigger if the map is 4183 * undefined; otherwise, it uses the `triggerMap` value of the show 4184 * trigger; else it will just use the show trigger. 4185 */ 4186 function getTriggers(trigger) { 4187 var show = (trigger || options.trigger || defaultTriggerShow).split(' '); 4188 var hide = show.map(function(trigger) { 4189 return triggerMap[trigger] || trigger; 4190 }); 4191 return { 4192 show: show, 4193 hide: hide 4194 }; 4195 } 4196 4197 var directiveName = snake_case(ttType); 4198 4199 var startSym = $interpolate.startSymbol(); 4200 var endSym = $interpolate.endSymbol(); 4201 var template = 4202 '<div '+ directiveName + '-popup '+ 4203 'title="' + startSym + 'title' + endSym + '" '+ 4204 (options.useContentExp ? 4205 'content-exp="contentExp()" ' : 4206 'content="' + startSym + 'content' + endSym + '" ') + 4207 'placement="' + startSym + 'placement' + endSym + '" '+ 4208 'popup-class="' + startSym + 'popupClass' + endSym + '" '+ 4209 'animation="animation" ' + 4210 'is-open="isOpen"' + 4211 'origin-scope="origScope" ' + 4212 'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' + 4213 '>' + 4214 '</div>'; 4215 4216 return { 4217 compile: function(tElem, tAttrs) { 4218 var tooltipLinker = $compile(template); 4219 4220 return function link(scope, element, attrs, tooltipCtrl) { 4221 var tooltip; 4222 var tooltipLinkedScope; 4223 var transitionTimeout; 4224 var showTimeout; 4225 var hideTimeout; 4226 var positionTimeout; 4227 var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
4228 var triggers = getTriggers(undefined); 4229 var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); 4230 var ttScope = scope.$new(true); 4231 var repositionScheduled = false; 4232 var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; 4233 var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; 4234 var observers = []; 4235 4236 var positionTooltip = function() { 4237 // check if tooltip exists and is not empty 4238 if (!tooltip || !tooltip.html()) { return; } 4239 4240 if (!positionTimeout) { 4241 positionTimeout = $timeout(function() { 4242 // Reset the positioning. 4243 tooltip.css({ top: 0, left: 0 }); 4244 4245 // Now set the calculated positioning. 4246 var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); 4247 tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px', visibility: 'visible' }); 4248 4249 // If the placement class is prefixed, still need 4250 // to remove the TWBS standard class. 4251 if (options.placementClassPrefix) { 4252 tooltip.removeClass('top bottom left right'); 4253 } 4254 4255 tooltip.removeClass( 4256 options.placementClassPrefix + 'top ' + 4257 options.placementClassPrefix + 'top-left ' + 4258 options.placementClassPrefix + 'top-right ' + 4259 options.placementClassPrefix + 'bottom ' + 4260 options.placementClassPrefix + 'bottom-left ' + 4261 options.placementClassPrefix + 'bottom-right ' + 4262 options.placementClassPrefix + 'left ' + 4263 options.placementClassPrefix + 'left-top ' + 4264 options.placementClassPrefix + 'left-bottom ' + 4265 options.placementClassPrefix + 'right ' + 4266 options.placementClassPrefix + 'right-top ' + 4267 options.placementClassPrefix + 'right-bottom'); 4268 4269 var placement = ttPosition.placement.split('-'); 4270 tooltip.addClass(placement[0], options.placementClassPrefix + ttPosition.placement); 4271 $position.positionArrow(tooltip, ttPosition.placement); 4272 4273 positionTimeout = null; 4274 }, 0, false); 4275 } 4276 }; 4277 4278 // Set up the correct scope to allow transclusion later 4279 ttScope.origScope = scope; 4280 4281 // By default, the tooltip is not open. 4282 // TODO add ability to start tooltip opened 4283 ttScope.isOpen = false; 4284 openedTooltips.add(ttScope, { 4285 close: hide 4286 }); 4287 4288 function toggleTooltipBind() { 4289 if (!ttScope.isOpen) { 4290 showTooltipBind(); 4291 } else { 4292 hideTooltipBind(); 4293 } 4294 } 4295 4296 // Show the tooltip with delay if specified, otherwise show it immediately 4297 function showTooltipBind() { 4298 if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { 4299 return; 4300 } 4301 4302 cancelHide(); 4303 prepareTooltip(); 4304 4305 if (ttScope.popupDelay) { 4306 // Do nothing if the tooltip was already scheduled to pop-up. 4307 // This happens if show is triggered multiple times before any hide is triggered. 4308 if (!showTimeout) { 4309 showTimeout = $timeout(show, ttScope.popupDelay, false); 4310 } 4311 } else { 4312 show(); 4313 } 4314 } 4315 4316 function hideTooltipBind() { 4317 cancelShow(); 4318 4319 if (ttScope.popupCloseDelay) { 4320 if (!hideTimeout) { 4321 hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); 4322 } 4323 } else { 4324 hide(); 4325 } 4326 } 4327 4328 // Show the tooltip popup element. 4329 function show() { 4330 cancelShow(); 4331 cancelHide(); 4332 4333 // Don't show empty tooltips. 4334 if (!ttScope.content) { 4335 return angular.noop; 4336 } 4337 4338 createTooltip(); 4339 4340 // And show the tooltip. 4341 ttScope.$evalAsync(function() { 4342 ttScope.isOpen = true; 4343 assignIsOpen(true); 4344 positionTooltip(); 4345 }); 4346 } 4347 4348 function cancelShow() { 4349 if (showTimeout) { 4350 $timeout.cancel(showTimeout); 4351 showTimeout = null; 4352 } 4353 4354 if (positionTimeout) { 4355 $timeout.cancel(positionTimeout); 4356 positionTimeout = null; 4357 } 4358 } 4359 4360 // Hide the tooltip popup element. 4361 function hide() { 4362 if (!ttScope) { 4363 return; 4364 } 4365 4366 // First things first: we don't show it anymore. 4367 ttScope.$evalAsync(function() { 4368 ttScope.isOpen = false;
vendor: 4,461 bytes, lines 4369-4499
4369 assignIsOpen(false); 4370 // And now we remove it from the DOM. However, if we have animation, we 4371 // need to wait for it to expire beforehand. 4372 // FIXME: this is a placeholder for a port of the transitions library. 4373 // The fade transition in TWBS is 150ms. 4374 if (ttScope.animation) { 4375 if (!transitionTimeout) { 4376 transitionTimeout = $timeout(removeTooltip, 150, false); 4377 } 4378 } else { 4379 removeTooltip(); 4380 } 4381 }); 4382 } 4383 4384 function cancelHide() { 4385 if (hideTimeout) { 4386 $timeout.cancel(hideTimeout); 4387 hideTimeout = null; 4388 } 4389 if (transitionTimeout) { 4390 $timeout.cancel(transitionTimeout); 4391 transitionTimeout = null; 4392 } 4393 } 4394 4395 function createTooltip() { 4396 // There can only be one tooltip element per directive shown at once. 4397 if (tooltip) { 4398 return; 4399 } 4400 4401 tooltipLinkedScope = ttScope.$new(); 4402 tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { 4403 if (appendToBody) { 4404 $document.find('body').append(tooltip); 4405 } else { 4406 element.after(tooltip); 4407 } 4408 }); 4409 4410 prepObservers(); 4411 } 4412 4413 function removeTooltip() { 4414 cancelShow(); 4415 cancelHide(); 4416 unregisterObservers(); 4417 4418 if (tooltip) { 4419 tooltip.remove(); 4420 tooltip = null; 4421 } 4422 if (tooltipLinkedScope) { 4423 tooltipLinkedScope.$destroy(); 4424 tooltipLinkedScope = null; 4425 } 4426 } 4427 4428 /** 4429 * Set the initial scope values. Once 4430 * the tooltip is created, the observers 4431 * will be added to keep things in sync. 4432 */ 4433 function prepareTooltip() { 4434 ttScope.title = attrs[prefix + 'Title']; 4435 if (contentParse) { 4436 ttScope.content = contentParse(scope); 4437 } else { 4438 ttScope.content = attrs[ttType]; 4439 } 4440 4441 ttScope.popupClass = attrs[prefix + 'Class']; 4442 ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; 4443 4444 var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); 4445 var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); 4446 ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; 4447 ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; 4448 } 4449 4450 function assignIsOpen(isOpen) { 4451 if (isOpenParse && angular.isFunction(isOpenParse.assign)) { 4452 isOpenParse.assign(scope, isOpen); 4453 } 4454 } 4455 4456 ttScope.contentExp = function() { 4457 return ttScope.content; 4458 }; 4459 4460 /** 4461 * Observe the relevant attributes. 4462 */ 4463 attrs.$observe('disabled', function(val) { 4464 if (val) { 4465 cancelShow(); 4466 } 4467 4468 if (val && ttScope.isOpen) { 4469 hide(); 4470 } 4471 }); 4472 4473 if (isOpenParse) { 4474 scope.$watch(isOpenParse, function(val) { 4475 if (ttScope && !val === ttScope.isOpen) { 4476 toggleTooltipBind(); 4477 } 4478 }); 4479 } 4480 4481 function prepObservers() { 4482 observers.length = 0; 4483 4484 if (contentParse) { 4485 observers.push( 4486 scope.$watch(contentParse, function(val) { 4487 ttScope.content = val; 4488 if (!val && ttScope.isOpen) { 4489 hide(); 4490 } 4491 }) 4492 ); 4493 4494 observers.push( 4495 tooltipLinkedScope.$watch(function() { 4496 if (!repositionScheduled) { 4497 repositionScheduled = true; 4498 tooltipLinkedScope.$$postDigest(function() { 4499 repositionScheduled = false;
4500 if (ttScope && ttScope.isOpen) { 4501 positionTooltip(); 4502 } 4503 }); 4504 } 4505 }) 4506 ); 4507 } else { 4508 observers.push( 4509 attrs.$observe(ttType, function(val) { 4510 ttScope.content = val; 4511 if (!val && ttScope.isOpen) { 4512 hide(); 4513 } else { 4514 positionTooltip(); 4515 } 4516 }) 4517 ); 4518 } 4519 4520 observers.push( 4521 attrs.$observe(prefix + 'Title', function(val) { 4522 ttScope.title = val; 4523 if (ttScope.isOpen) { 4524 positionTooltip(); 4525 } 4526 }) 4527 ); 4528 4529 observers.push( 4530 attrs.$observe(prefix + 'Placement', function(val) { 4531 ttScope.placement = val ? val : options.placement; 4532 if (ttScope.isOpen) { 4533 positionTooltip(); 4534 } 4535 }) 4536 ); 4537 } 4538 4539 function unregisterObservers() { 4540 if (observers.length) { 4541 angular.forEach(observers, function(observer) { 4542 observer(); 4543 }); 4544 observers.length = 0; 4545 } 4546 } 4547 4548 // hide tooltips/popovers for outsideClick trigger 4549 function bodyHideTooltipBind(e) { 4550 if (!ttScope || !ttScope.isOpen || !tooltip) { 4551 return; 4552 } 4553 // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked 4554 if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { 4555 hideTooltipBind(); 4556 } 4557 } 4558 4559 var unregisterTriggers = function() { 4560 triggers.show.forEach(function(trigger) { 4561 if (trigger === 'outsideClick') { 4562 element.off('click', toggleTooltipBind); 4563 } else { 4564 element.off(trigger, showTooltipBind); 4565 element.off(trigger, toggleTooltipBind); 4566 } 4567 }); 4568 triggers.hide.forEach(function(trigger) { 4569 if (trigger === 'outsideClick') { 4570 $document.off('click', bodyHideTooltipBind); 4571 } else { 4572 element.off(trigger, hideTooltipBind); 4573 } 4574 }); 4575 }; 4576 4577 function prepTriggers() { 4578 var val = attrs[prefix + 'Trigger']; 4579 unregisterTriggers(); 4580 4581 triggers = getTriggers(val); 4582 4583 if (triggers.show !== 'none') { 4584 triggers.show.forEach(function(trigger, idx) { 4585 if (trigger === 'outsideClick') { 4586 element.on('click', toggleTooltipBind); 4587 $document.on('click', bodyHideTooltipBind); 4588 } else if (trigger === triggers.hide[idx]) { 4589 element.on(trigger, toggleTooltipBind); 4590 } else if (trigger) { 4591 element.on(trigger, showTooltipBind); 4592 element.on(triggers.hide[idx], hideTooltipBind); 4593 } 4594 4595 element.on('keypress', function(e) { 4596 if (e.which === 27) { 4597 hideTooltipBind(); 4598 } 4599 }); 4600 }); 4601 } 4602 } 4603 4604 prepTriggers(); 4605 4606 var animation = scope.$eval(attrs[prefix + 'Animation']); 4607 ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; 4608 4609 var appendToBodyVal; 4610 var appendKey = prefix + 'AppendToBody'; 4611 if (appendKey in attrs && attrs[appendKey] === undefined) { 4612 appendToBodyVal = true; 4613 } else { 4614 appendToBodyVal = scope.$eval(attrs[appendKey]); 4615 } 4616 4617 appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; 4618 4619 // if a tooltip is attached to <body> we need to remove it on 4620 // location change as its parent scope will probably not be destroyed 4621 // by the change. 4622 if (appendToBody) { 4623 scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() { 4624 if (ttScope.isOpen) { 4625 hide(); 4626 } 4627 }); 4628 } 4629 4630 // Make sure tooltip is destroyed and removed. 4631 scope.$on('$destroy', function onDestroyTooltip() { 4632 unregisterTriggers(); 4633 removeTooltip(); 4634 openedTooltips.remove(ttScope); 4635 ttScope = null; 4636 }); 4637 }; 4638 } 4639 }; 4640 }; 4641 }]; 4642}) 4643 4644// This is mostly ngInclude code but with a custom scope 4645.directive('uibTooltipTemplateTransclude', [ 4646 '$animate', '$sce', '$compile', '$templateRequest', 4647function ($animate, $sce, $compile, $templateRequest) { 4648 return { 4649 link: function(scope, elem, attrs) { 4650 var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); 4651 4652 var changeCounter = 0, 4653 currentScope, 4654 previousElement, 4655 currentElement; 4656 4657 var cleanupLastIncludeContent = function() { 4658 if (previousElement) { 4659 previousElement.remove(); 4660 previousElement = null; 4661 } 4662 4663 if (currentScope) { 4664 currentScope.$destroy(); 4665 currentScope = null; 4666 } 4667 4668 if (currentElement) { 4669 $animate.leave(currentElement).then(function() { 4670 previousElement = null; 4671 }); 4672 previousElement = currentElement; 4673 currentElement = null; 4674 } 4675 }; 4676 4677 scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) { 4678 var thisChangeId = ++changeCounter; 4679 4680 if (src) { 4681 //set the 2nd param to true to ignore the template request error so that the inner 4682 //contents and scope can be cleaned up. 4683 $templateRequest(src, true).then(function(response) { 4684 if (thisChangeId !== changeCounter) { return; } 4685 var newScope = origScope.$new(); 4686 var template = response; 4687 4688 var clone = $compile(template)(newScope, function(clone) { 4689 cleanupLastIncludeContent(); 4690 $animate.enter(clone, elem); 4691 }); 4692 4693 currentScope = newScope; 4694 currentElement = clone; 4695 4696 currentScope.$emit('$includeContentLoaded', src); 4697 }, function() { 4698 if (thisChangeId === changeCounter) { 4699 cleanupLastIncludeContent();
4700 scope.$emit('$includeContentError', src); 4701 } 4702 }); 4703 scope.$emit('$includeContentRequested', src); 4704 } else { 4705 cleanupLastIncludeContent(); 4706 } 4707 }); 4708 4709 scope.$on('$destroy', cleanupLastIncludeContent); 4710 } 4711 }; 4712}]) 4713 4714/** 4715 * Note that it's intentional that these classes are *not* applied through $animate. 4716 * They must not be animated as they're expected to be present on the tooltip on 4717 * initialization. 4718 */ 4719.directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) { 4720 return { 4721 restrict: 'A', 4722 link: function(scope, element, attrs) { 4723 // need to set the primary position so the 4724 // arrow has space during position measure. 4725 // tooltip.positionTooltip() 4726 if (scope.placement) { 4727 // // There are no top-left etc... classes 4728 // // in TWBS, so we need the primary position. 4729 var position = $uibPosition.parsePlacement(scope.placement); 4730 element.addClass(position[0]); 4731 } else { 4732 element.addClass('top'); 4733 } 4734 4735 if (scope.popupClass) { 4736 element.addClass(scope.popupClass); 4737 } 4738 4739 if (scope.animation()) { 4740 element.addClass(attrs.tooltipAnimationClass); 4741 } 4742 } 4743 }; 4744}]) 4745 4746.directive('uibTooltipPopup', function() { 4747 return { 4748 replace: true, 4749 scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, 4750 templateUrl: 'uib/template/tooltip/tooltip-popup.html' 4751 }; 4752}) 4753 4754.directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) { 4755 return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter'); 4756}]) 4757 4758.directive('uibTooltipTemplatePopup', function() { 4759 return { 4760 replace: true, 4761 scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', 4762 originScope: '&' }, 4763 templateUrl: 'uib/template/tooltip/tooltip-template-popup.html' 4764 }; 4765}) 4766 4767.directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) { 4768 return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', { 4769 useContentExp: true 4770 }); 4771}]) 4772 4773.directive('uibTooltipHtmlPopup', function() { 4774 return { 4775 replace: true, 4776 scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, 4777 templateUrl: 'uib/template/tooltip/tooltip-html-popup.html' 4778 }; 4779}) 4780 4781.directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) { 4782 return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', { 4783 useContentExp: true 4784 }); 4785}]); 4786 4787/** 4788 * The following features are still outstanding: popup delay, animation as a 4789 * function, placement as a function, inside, support for more triggers than 4790 * just mouse enter/leave, and selector delegatation. 4791 */ 4792angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip']) 4793 4794.directive('uibPopoverTemplatePopup', function() { 4795 return { 4796 replace: true, 4797 scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', 4798 originScope: '&' }, 4799 templateUrl: 'uib/template/popover/popover-template.html' 4800 }; 4801}) 4802 4803.directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) { 4804 return $uibTooltip('uibPopoverTemplate', 'popover', 'click', { 4805 useContentExp: true 4806 }); 4807}]) 4808 4809.directive('uibPopoverHtmlPopup', function() { 4810 return { 4811 replace: true, 4812 scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, 4813 templateUrl: 'uib/template/popover/popover-html.html' 4814 }; 4815}) 4816 4817.directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) { 4818 return $uibTooltip('uibPopoverHtml', 'popover', 'click', { 4819 useContentExp: true 4820 }); 4821}]) 4822 4823.directive('uibPopoverPopup', function() { 4824 return { 4825 replace: true, 4826 scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, 4827 templateUrl: 'uib/template/popover/popover.html' 4828 }; 4829}) 4830 4831.directive('uibPopover', ['$uibTooltip', function($uibTooltip) { 4832 return $uibTooltip('uibPopover', 'popover', 'click'); 4833}]); 4834 4835angular.module('ui.bootstrap.progressbar', []) 4836 4837.constant('uibProgressConfig', { 4838 animate: true, 4839 max: 100 4840}) 4841 4842.controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) { 4843 var self = this, 4844 animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate; 4845 4846 this.bars = []; 4847 $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max; 4848 4849 this.addBar = function(bar, element, attrs) { 4850 if (!animate) { 4851 element.css({'transition': 'none'}); 4852 } 4853 4854 this.bars.push(bar); 4855 4856 bar.max = $scope.max; 4857 bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar'; 4858 4859 bar.$watch('value', function(value) { 4860 bar.recalculatePercentage(); 4861 }); 4862 4863 bar.recalculatePercentage = function() { 4864 var totalPercentage = self.bars.reduce(function(total, bar) { 4865 bar.percent = +(100 * bar.value / bar.max).toFixed(2); 4866 return total + bar.percent; 4867 }, 0); 4868 4869 if (totalPercentage > 100) { 4870 bar.percent -= totalPercentage - 100; 4871 } 4872 }; 4873 4874 bar.$on('$destroy', function() { 4875 element = null; 4876 self.removeBar(bar); 4877 }); 4878 }; 4879 4880 this.removeBar = function(bar) { 4881 this.bars.splice(this.bars.indexOf(bar), 1);
4882 this.bars.forEach(function (bar) { 4883 bar.recalculatePercentage(); 4884 }); 4885 }; 4886 4887 $scope.$watch('max', function(max) { 4888 self.bars.forEach(function(bar) { 4889 bar.max = $scope.max; 4890 bar.recalculatePercentage(); 4891 }); 4892 }); 4893}]) 4894 4895.directive('uibProgress', function() { 4896 return { 4897 replace: true, 4898 transclude: true, 4899 controller: 'UibProgressController', 4900 require: 'uibProgress', 4901 scope: { 4902 max: '=?' 4903 }, 4904 templateUrl: 'uib/template/progressbar/progress.html' 4905 }; 4906}) 4907 4908.directive('uibBar', function() { 4909 return { 4910 replace: true, 4911 transclude: true, 4912 require: '^uibProgress', 4913 scope: { 4914 value: '=', 4915 type: '@' 4916 }, 4917 templateUrl: 'uib/template/progressbar/bar.html', 4918 link: function(scope, element, attrs, progressCtrl) { 4919 progressCtrl.addBar(scope, element, attrs); 4920 } 4921 }; 4922}) 4923 4924.directive('uibProgressbar', function() { 4925 return { 4926 replace: true, 4927 transclude: true, 4928 controller: 'UibProgressController', 4929 scope: { 4930 value: '=', 4931 max: '=?', 4932 type: '@' 4933 }, 4934 templateUrl: 'uib/template/progressbar/progressbar.html', 4935 link: function(scope, element, attrs, progressCtrl) { 4936 progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); 4937 } 4938 }; 4939}); 4940 4941angular.module('ui.bootstrap.rating', []) 4942 4943.constant('uibRatingConfig', { 4944 max: 5, 4945 stateOn: null, 4946 stateOff: null, 4947 titles : ['one', 'two', 'three', 'four', 'five'] 4948}) 4949 4950.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) { 4951 var ngModelCtrl = { $setViewValue: angular.noop }; 4952 4953 this.init = function(ngModelCtrl_) { 4954 ngModelCtrl = ngModelCtrl_; 4955 ngModelCtrl.$render = this.render; 4956 4957 ngModelCtrl.$formatters.push(function(value) { 4958 if (angular.isNumber(value) && value << 0 !== value) { 4959 value = Math.round(value); 4960 } 4961 4962 return value; 4963 }); 4964 4965 this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn; 4966 this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff; 4967 var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ; 4968 this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ? 4969 tmpTitles : ratingConfig.titles; 4970 4971 var ratingStates = angular.isDefined($attrs.ratingStates) ? 4972 $scope.$parent.$eval($attrs.ratingStates) : 4973 new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max); 4974 $scope.range = this.buildTemplateObjects(ratingStates); 4975 }; 4976 4977 this.buildTemplateObjects = function(states) { 4978 for (var i = 0, n = states.length; i < n; i++) { 4979 states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]); 4980 } 4981 return states; 4982 }; 4983 4984 this.getTitle = function(index) { 4985 if (index >= this.titles.length) { 4986 return index + 1; 4987 } 4988 4989 return this.titles[index]; 4990 }; 4991 4992 $scope.rate = function(value) { 4993 if (!$scope.readonly && value >= 0 && value <= $scope.range.length) { 4994 ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value); 4995 ngModelCtrl.$render(); 4996 } 4997 }; 4998 4999 $scope.enter = function(value) { 5000 if (!$scope.readonly) { 5001 $scope.value = value; 5002 } 5003 $scope.onHover({value: value}); 5004 }; 5005 5006 $scope.reset = function() { 5007 $scope.value = ngModelCtrl.$viewValue; 5008 $scope.onLeave(); 5009 }; 5010 5011 $scope.onKeydown = function(evt) { 5012 if (/(37|38|39|40)/.test(evt.which)) { 5013 evt.preventDefault(); 5014 evt.stopPropagation(); 5015 $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1)); 5016 } 5017 }; 5018 5019 this.render = function() { 5020 $scope.value = ngModelCtrl.$viewValue; 5021 }; 5022}]) 5023 5024.directive('uibRating', function() { 5025 return { 5026 require: ['uibRating', 'ngModel'], 5027 scope: { 5028 readonly: '=?', 5029 onHover: '&', 5030 onLeave: '&' 5031 }, 5032 controller: 'UibRatingController', 5033 templateUrl: 'uib/template/rating/rating.html', 5034 replace: true, 5035 link: function(scope, element, attrs, ctrls) { 5036 var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; 5037 ratingCtrl.init(ngModelCtrl); 5038 } 5039 }; 5040}); 5041 5042angular.module('ui.bootstrap.tabs', []) 5043 5044.controller('UibTabsetController', ['$scope', function ($scope) { 5045 var ctrl = this, 5046 tabs = ctrl.tabs = $scope.tabs = []; 5047 5048 ctrl.select = function(selectedTab) { 5049 angular.forEach(tabs, function(tab) { 5050 if (tab.active && tab !== selectedTab) { 5051 tab.active = false;
5052 tab.onDeselect(); 5053 selectedTab.selectCalled = false; 5054 } 5055 }); 5056 selectedTab.active = true; 5057 // only call select if it has not already been called 5058 if (!selectedTab.selectCalled) { 5059 selectedTab.onSelect(); 5060 selectedTab.selectCalled = true; 5061 } 5062 }; 5063 5064 ctrl.addTab = function addTab(tab) { 5065 tabs.push(tab); 5066 // we can't run the select function on the first tab 5067 // since that would select it twice 5068 if (tabs.length === 1 && tab.active !== false) { 5069 tab.active = true; 5070 } else if (tab.active) { 5071 ctrl.select(tab); 5072 } else { 5073 tab.active = false; 5074 } 5075 }; 5076 5077 ctrl.removeTab = function removeTab(tab) { 5078 var index = tabs.indexOf(tab); 5079 //Select a new tab if the tab to be removed is selected and not destroyed 5080 if (tab.active && tabs.length > 1 && !destroyed) { 5081 //If this is the last tab, select the previous tab. else, the next tab. 5082 var newActiveIndex = index === tabs.length - 1 ? index - 1 : index + 1; 5083 ctrl.select(tabs[newActiveIndex]); 5084 } 5085 tabs.splice(index, 1); 5086 }; 5087 5088 var destroyed; 5089 $scope.$on('$destroy', function() { 5090 destroyed = true; 5091 }); 5092}]) 5093 5094.directive('uibTabset', function() { 5095 return { 5096 transclude: true, 5097 replace: true, 5098 scope: { 5099 type: '@' 5100 }, 5101 controller: 'UibTabsetController', 5102 templateUrl: 'uib/template/tabs/tabset.html', 5103 link: function(scope, element, attrs) { 5104 scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false; 5105 scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false; 5106 } 5107 }; 5108}) 5109 5110.directive('uibTab', ['$parse', function($parse) { 5111 return { 5112 require: '^uibTabset', 5113 replace: true, 5114 templateUrl: 'uib/template/tabs/tab.html', 5115 transclude: true, 5116 scope: { 5117 active: '=?', 5118 heading: '@', 5119 onSelect: '&select', //This callback is called in contentHeadingTransclude 5120 //once it inserts the tab's content into the dom 5121 onDeselect: '&deselect' 5122 }, 5123 controller: function() { 5124 //Empty controller so other directives can require being 'under' a tab 5125 }, 5126 controllerAs: 'tab', 5127 link: function(scope, elm, attrs, tabsetCtrl, transclude) { 5128 scope.$watch('active', function(active) { 5129 if (active) { 5130 tabsetCtrl.select(scope); 5131 } 5132 }); 5133 5134 scope.disabled = false; 5135 if (attrs.disable) { 5136 scope.$parent.$watch($parse(attrs.disable), function(value) { 5137 scope.disabled = !! value; 5138 }); 5139 } 5140 5141 scope.select = function() { 5142 if (!scope.disabled) { 5143 scope.active = true; 5144 } 5145 }; 5146 5147 tabsetCtrl.addTab(scope); 5148 scope.$on('$destroy', function() { 5149 tabsetCtrl.removeTab(scope); 5150 }); 5151 5152 //We need to transclude later, once the content container is ready. 5153 //when this link happens, we're inside a tab heading. 5154 scope.$transcludeFn = transclude; 5155 } 5156 }; 5157}]) 5158 5159.directive('uibTabHeadingTransclude', function() { 5160 return { 5161 restrict: 'A', 5162 require: '^uibTab', 5163 link: function(scope, elm) { 5164 scope.$watch('headingElement', function updateHeadingElement(heading) { 5165 if (heading) { 5166 elm.html(''); 5167 elm.append(heading); 5168 } 5169 }); 5170 } 5171 }; 5172}) 5173 5174.directive('uibTabContentTransclude', function() { 5175 return { 5176 restrict: 'A', 5177 require: '^uibTabset', 5178 link: function(scope, elm, attrs) { 5179 var tab = scope.$eval(attrs.uibTabContentTransclude); 5180 5181 //Now our tab is ready to be transcluded: both the tab heading area 5182 //and the tab content area are loaded. Transclude 'em both. 5183 tab.$transcludeFn(tab.$parent, function(contents) { 5184 angular.forEach(contents, function(node) { 5185 if (isTabHeading(node)) { 5186 //Let tabHeadingTransclude know. 5187 tab.headingElement = node; 5188 } else { 5189 elm.append(node); 5190 } 5191 }); 5192 }); 5193 } 5194 }; 5195 5196 function isTabHeading(node) { 5197 return node.tagName && ( 5198 node.hasAttribute('uib-tab-heading') || 5199 node.hasAttribute('data-uib-tab-heading') || 5200 node.hasAttribute('x-uib-tab-heading') || 5201 node.tagName.toLowerCase() === 'uib-tab-heading' || 5202 node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
5203 node.tagName.toLowerCase() === 'x-uib-tab-heading' 5204 ); 5205 } 5206}); 5207 5208angular.module('ui.bootstrap.timepicker', []) 5209 5210.constant('uibTimepickerConfig', { 5211 hourStep: 1, 5212 minuteStep: 1, 5213 secondStep: 1, 5214 showMeridian: true, 5215 showSeconds: false, 5216 meridians: null, 5217 readonlyInput: false, 5218 mousewheel: true, 5219 arrowkeys: true, 5220 showSpinners: true, 5221 templateUrl: 'uib/template/timepicker/timepicker.html' 5222}) 5223 5224.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) { 5225 var selected = new Date(), 5226 ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl 5227 meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS; 5228 5229 $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0; 5230 $element.removeAttr('tabindex'); 5231 5232 this.init = function(ngModelCtrl_, inputs) { 5233 ngModelCtrl = ngModelCtrl_; 5234 ngModelCtrl.$render = this.render; 5235 5236 ngModelCtrl.$formatters.unshift(function(modelValue) { 5237 return modelValue ? new Date(modelValue) : null; 5238 }); 5239 5240 var hoursInputEl = inputs.eq(0), 5241 minutesInputEl = inputs.eq(1), 5242 secondsInputEl = inputs.eq(2); 5243 5244 var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel; 5245 5246 if (mousewheel) { 5247 this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl); 5248 } 5249 5250 var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys; 5251 if (arrowkeys) { 5252 this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl); 5253 } 5254 5255 $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput; 5256 this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl); 5257 }; 5258 5259 var hourStep = timepickerConfig.hourStep; 5260 if ($attrs.hourStep) { 5261 $scope.$parent.$watch($parse($attrs.hourStep), function(value) { 5262 hourStep = +value; 5263 }); 5264 } 5265 5266 var minuteStep = timepickerConfig.minuteStep; 5267 if ($attrs.minuteStep) { 5268 $scope.$parent.$watch($parse($attrs.minuteStep), function(value) { 5269 minuteStep = +value; 5270 }); 5271 } 5272 5273 var min; 5274 $scope.$parent.$watch($parse($attrs.min), function(value) { 5275 var dt = new Date(value); 5276 min = isNaN(dt) ? undefined : dt; 5277 }); 5278 5279 var max; 5280 $scope.$parent.$watch($parse($attrs.max), function(value) { 5281 var dt = new Date(value); 5282 max = isNaN(dt) ? undefined : dt; 5283 }); 5284 5285 var disabled = false; 5286 if ($attrs.ngDisabled) { 5287 $scope.$parent.$watch($parse($attrs.ngDisabled), function(value) { 5288 disabled = value; 5289 }); 5290 } 5291 5292 $scope.noIncrementHours = function() { 5293 var incrementedSelected = addMinutes(selected, hourStep * 60); 5294 return disabled || incrementedSelected > max || 5295 incrementedSelected < selected && incrementedSelected < min; 5296 }; 5297 5298 $scope.noDecrementHours = function() { 5299 var decrementedSelected = addMinutes(selected, -hourStep * 60); 5300 return disabled || decrementedSelected < min || 5301 decrementedSelected > selected && decrementedSelected > max; 5302 }; 5303 5304 $scope.noIncrementMinutes = function() { 5305 var incrementedSelected = addMinutes(selected, minuteStep); 5306 return disabled || incrementedSelected > max || 5307 incrementedSelected < selected && incrementedSelected < min; 5308 }; 5309 5310 $scope.noDecrementMinutes = function() { 5311 var decrementedSelected = addMinutes(selected, -minuteStep); 5312 return disabled || decrementedSelected < min || 5313 decrementedSelected > selected && decrementedSelected > max; 5314 }; 5315 5316 $scope.noIncrementSeconds = function() { 5317 var incrementedSelected = addSeconds(selected, secondStep); 5318 return disabled || incrementedSelected > max || 5319 incrementedSelected < selected && incrementedSelected < min; 5320 }; 5321 5322 $scope.noDecrementSeconds = function() { 5323 var decrementedSelected = addSeconds(selected, -secondStep); 5324 return disabled || decrementedSelected < min || 5325 decrementedSelected > selected && decrementedSelected > max; 5326 }; 5327 5328 $scope.noToggleMeridian = function() { 5329 if (selected.getHours() < 12) { 5330 return disabled || addMinutes(selected, 12 * 60) > max; 5331 } 5332 5333 return disabled || addMinutes(selected, -12 * 60) < min; 5334 }; 5335 5336 var secondStep = timepickerConfig.secondStep; 5337 if ($attrs.secondStep) { 5338 $scope.$parent.$watch($parse($attrs.secondStep), function(value) { 5339 secondStep = +value; 5340 }); 5341 } 5342 5343 $scope.showSeconds = timepickerConfig.showSeconds; 5344 if ($attrs.showSeconds) { 5345 $scope.$parent.$watch($parse($attrs.showSeconds), function(value) { 5346 $scope.showSeconds = !!value; 5347 }); 5348 } 5349 5350 // 12H / 24H mode 5351 $scope.showMeridian = timepickerConfig.showMeridian; 5352 if ($attrs.showMeridian) { 5353 $scope.$parent.$watch($parse($attrs.showMeridian), function(value) { 5354 $scope.showMeridian = !!value; 5355 5356 if (ngModelCtrl.$error.time) { 5357 // Evaluate from template 5358 var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate(); 5359 if (angular.isDefined(hours) && angular.isDefined(minutes)) { 5360 selected.setHours(hours); 5361 refresh(); 5362 } 5363 } else { 5364 updateTemplate(); 5365 } 5366 }); 5367 } 5368
vendor: 25,005 bytes, lines 5369-6146
5369 // Get $scope.hours in 24H mode if valid 5370 function getHoursFromTemplate() { 5371 var hours = +$scope.hours; 5372 var valid = $scope.showMeridian ? hours > 0 && hours < 13 : 5373 hours >= 0 && hours < 24; 5374 if (!valid) { 5375 return undefined; 5376 } 5377 5378 if ($scope.showMeridian) { 5379 if (hours === 12) { 5380 hours = 0; 5381 } 5382 if ($scope.meridian === meridians[1]) { 5383 hours = hours + 12; 5384 } 5385 } 5386 return hours; 5387 } 5388 5389 function getMinutesFromTemplate() { 5390 var minutes = +$scope.minutes; 5391 return minutes >= 0 && minutes < 60 ? minutes : undefined; 5392 } 5393 5394 function getSecondsFromTemplate() { 5395 var seconds = +$scope.seconds; 5396 return seconds >= 0 && seconds < 60 ? seconds : undefined; 5397 } 5398 5399 function pad(value) { 5400 if (value === null) { 5401 return ''; 5402 } 5403 5404 return angular.isDefined(value) && value.toString().length < 2 ? 5405 '0' + value : value.toString(); 5406 } 5407 5408 // Respond on mousewheel spin 5409 this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { 5410 var isScrollingUp = function(e) { 5411 if (e.originalEvent) { 5412 e = e.originalEvent; 5413 } 5414 //pick correct delta variable depending on event 5415 var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY; 5416 return e.detail || delta > 0; 5417 }; 5418 5419 hoursInputEl.bind('mousewheel wheel', function(e) { 5420 if (!disabled) { 5421 $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours()); 5422 } 5423 e.preventDefault(); 5424 }); 5425 5426 minutesInputEl.bind('mousewheel wheel', function(e) { 5427 if (!disabled) { 5428 $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes()); 5429 } 5430 e.preventDefault(); 5431 }); 5432 5433 secondsInputEl.bind('mousewheel wheel', function(e) { 5434 if (!disabled) { 5435 $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds()); 5436 } 5437 e.preventDefault(); 5438 }); 5439 }; 5440 5441 // Respond on up/down arrowkeys 5442 this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { 5443 hoursInputEl.bind('keydown', function(e) { 5444 if (!disabled) { 5445 if (e.which === 38) { // up 5446 e.preventDefault(); 5447 $scope.incrementHours(); 5448 $scope.$apply(); 5449 } else if (e.which === 40) { // down 5450 e.preventDefault(); 5451 $scope.decrementHours(); 5452 $scope.$apply(); 5453 } 5454 } 5455 }); 5456 5457 minutesInputEl.bind('keydown', function(e) { 5458 if (!disabled) { 5459 if (e.which === 38) { // up 5460 e.preventDefault(); 5461 $scope.incrementMinutes(); 5462 $scope.$apply(); 5463 } else if (e.which === 40) { // down 5464 e.preventDefault(); 5465 $scope.decrementMinutes(); 5466 $scope.$apply(); 5467 } 5468 } 5469 }); 5470 5471 secondsInputEl.bind('keydown', function(e) { 5472 if (!disabled) { 5473 if (e.which === 38) { // up 5474 e.preventDefault(); 5475 $scope.incrementSeconds(); 5476 $scope.$apply(); 5477 } else if (e.which === 40) { // down 5478 e.preventDefault(); 5479 $scope.decrementSeconds(); 5480 $scope.$apply(); 5481 } 5482 } 5483 }); 5484 }; 5485 5486 this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { 5487 if ($scope.readonlyInput) { 5488 $scope.updateHours = angular.noop; 5489 $scope.updateMinutes = angular.noop; 5490 $scope.updateSeconds = angular.noop; 5491 return; 5492 } 5493 5494 var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) { 5495 ngModelCtrl.$setViewValue(null); 5496 ngModelCtrl.$setValidity('time', false); 5497 if (angular.isDefined(invalidHours)) { 5498 $scope.invalidHours = invalidHours; 5499 } 5500 5501 if (angular.isDefined(invalidMinutes)) { 5502 $scope.invalidMinutes = invalidMinutes; 5503 } 5504 5505 if (angular.isDefined(invalidSeconds)) { 5506 $scope.invalidSeconds = invalidSeconds; 5507 } 5508 }; 5509 5510 $scope.updateHours = function() { 5511 var hours = getHoursFromTemplate(), 5512 minutes = getMinutesFromTemplate(); 5513 5514 ngModelCtrl.$setDirty(); 5515 5516 if (angular.isDefined(hours) && angular.isDefined(minutes)) { 5517 selected.setHours(hours); 5518 selected.setMinutes(minutes); 5519 if (selected < min || selected > max) { 5520 invalidate(true); 5521 } else { 5522 refresh('h'); 5523 } 5524 } else { 5525 invalidate(true); 5526 } 5527 }; 5528 5529 hoursInputEl.bind('blur', function(e) { 5530 ngModelCtrl.$setTouched(); 5531 if ($scope.hours === null || $scope.hours === '') { 5532 invalidate(true); 5533 } else if (!$scope.invalidHours && $scope.hours < 10) { 5534 $scope.$apply(function() { 5535 $scope.hours = pad($scope.hours); 5536 }); 5537 } 5538 }); 5539 5540 $scope.updateMinutes = function() { 5541 var minutes = getMinutesFromTemplate(), 5542 hours = getHoursFromTemplate(); 5543 5544 ngModelCtrl.$setDirty(); 5545 5546 if (angular.isDefined(minutes) && angular.isDefined(hours)) { 5547 selected.setHours(hours); 5548 selected.setMinutes(minutes); 5549 if (selected < min || selected > max) { 5550 invalidate(undefined, true); 5551 } else { 5552 refresh('m'); 5553 } 5554 } else { 5555 invalidate(undefined, true); 5556 } 5557 }; 5558 5559 minutesInputEl.bind('blur', function(e) { 5560 ngModelCtrl.$setTouched(); 5561 if ($scope.minutes === null) { 5562 invalidate(undefined, true); 5563 } else if (!$scope.invalidMinutes && $scope.minutes < 10) { 5564 $scope.$apply(function() { 5565 $scope.minutes = pad($scope.minutes); 5566 }); 5567 } 5568 }); 5569 5570 $scope.updateSeconds = function() { 5571 var seconds = getSecondsFromTemplate(); 5572 5573 ngModelCtrl.$setDirty(); 5574 5575 if (angular.isDefined(seconds)) { 5576 selected.setSeconds(seconds); 5577 refresh('s'); 5578 } else { 5579 invalidate(undefined, undefined, true); 5580 } 5581 }; 5582 5583 secondsInputEl.bind('blur', function(e) { 5584 if (!$scope.invalidSeconds && $scope.seconds < 10) { 5585 $scope.$apply( function() { 5586 $scope.seconds = pad($scope.seconds); 5587 }); 5588 } 5589 }); 5590 5591 }; 5592 5593 this.render = function() { 5594 var date = ngModelCtrl.$viewValue; 5595 5596 if (isNaN(date)) { 5597 ngModelCtrl.$setValidity('time', false); 5598 $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.'); 5599 } else { 5600 if (date) { 5601 selected = date; 5602 } 5603 5604 if (selected < min || selected > max) { 5605 ngModelCtrl.$setValidity('time', false); 5606 $scope.invalidHours = true; 5607 $scope.invalidMinutes = true; 5608 } else { 5609 makeValid(); 5610 } 5611 updateTemplate(); 5612 } 5613 }; 5614 5615 // Call internally when we know that model is valid. 5616 function refresh(keyboardChange) { 5617 makeValid(); 5618 ngModelCtrl.$setViewValue(new Date(selected)); 5619 updateTemplate(keyboardChange); 5620 } 5621 5622 function makeValid() { 5623 ngModelCtrl.$setValidity('time', true); 5624 $scope.invalidHours = false; 5625 $scope.invalidMinutes = false; 5626 $scope.invalidSeconds = false; 5627 } 5628 5629 function updateTemplate(keyboardChange) { 5630 if (!ngModelCtrl.$modelValue) { 5631 $scope.hours = null; 5632 $scope.minutes = null; 5633 $scope.seconds = null; 5634 $scope.meridian = meridians[0]; 5635 } else { 5636 var hours = selected.getHours(), 5637 minutes = selected.getMinutes(), 5638 seconds = selected.getSeconds(); 5639 5640 if ($scope.showMeridian) { 5641 hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system 5642 } 5643 5644 $scope.hours = keyboardChange === 'h' ? hours : pad(hours); 5645 if (keyboardChange !== 'm') { 5646 $scope.minutes = pad(minutes); 5647 } 5648 $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; 5649 5650 if (keyboardChange !== 's') { 5651 $scope.seconds = pad(seconds); 5652 } 5653 $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; 5654 } 5655 } 5656 5657 function addSecondsToSelected(seconds) { 5658 selected = addSeconds(selected, seconds); 5659 refresh(); 5660 } 5661 5662 function addMinutes(selected, minutes) { 5663 return addSeconds(selected, minutes*60); 5664 } 5665 5666 function addSeconds(date, seconds) { 5667 var dt = new Date(date.getTime() + seconds * 1000); 5668 var newDate = new Date(date); 5669 newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds()); 5670 return newDate; 5671 } 5672 5673 $scope.showSpinners = angular.isDefined($attrs.showSpinners) ? 5674 $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; 5675 5676 $scope.incrementHours = function() { 5677 if (!$scope.noIncrementHours()) { 5678 addSecondsToSelected(hourStep * 60 * 60); 5679 } 5680 }; 5681 5682 $scope.decrementHours = function() { 5683 if (!$scope.noDecrementHours()) { 5684 addSecondsToSelected(-hourStep * 60 * 60); 5685 } 5686 }; 5687 5688 $scope.incrementMinutes = function() { 5689 if (!$scope.noIncrementMinutes()) { 5690 addSecondsToSelected(minuteStep * 60); 5691 } 5692 }; 5693 5694 $scope.decrementMinutes = function() { 5695 if (!$scope.noDecrementMinutes()) { 5696 addSecondsToSelected(-minuteStep * 60); 5697 } 5698 }; 5699 5700 $scope.incrementSeconds = function() { 5701 if (!$scope.noIncrementSeconds()) { 5702 addSecondsToSelected(secondStep); 5703 } 5704 }; 5705 5706 $scope.decrementSeconds = function() { 5707 if (!$scope.noDecrementSeconds()) { 5708 addSecondsToSelected(-secondStep); 5709 } 5710 }; 5711 5712 $scope.toggleMeridian = function() { 5713 var minutes = getMinutesFromTemplate(), 5714 hours = getHoursFromTemplate(); 5715 5716 if (!$scope.noToggleMeridian()) { 5717 if (angular.isDefined(minutes) && angular.isDefined(hours)) { 5718 addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60)); 5719 } else { 5720 $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0]; 5721 } 5722 } 5723 }; 5724 5725 $scope.blur = function() { 5726 ngModelCtrl.$setTouched(); 5727 }; 5728}]) 5729 5730.directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) { 5731 return { 5732 require: ['uibTimepicker', '?^ngModel'], 5733 controller: 'UibTimepickerController', 5734 controllerAs: 'timepicker', 5735 replace: true, 5736 scope: {}, 5737 templateUrl: function(element, attrs) { 5738 return attrs.templateUrl || uibTimepickerConfig.templateUrl; 5739 }, 5740 link: function(scope, element, attrs, ctrls) { 5741 var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; 5742 5743 if (ngModelCtrl) { 5744 timepickerCtrl.init(ngModelCtrl, element.find('input')); 5745 } 5746 } 5747 }; 5748}]); 5749 5750angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position']) 5751 5752/** 5753 * A helper service that can parse typeahead's syntax (string provided by users) 5754 * Extracted to a separate service for ease of unit testing 5755 */ 5756 .factory('uibTypeaheadParser', ['$parse', function($parse) { 5757 // 00000111000000000000022200000000000000003333333333333330000000000044000 5758 var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/; 5759 return { 5760 parse: function(input) { 5761 var match = input.match(TYPEAHEAD_REGEXP); 5762 if (!match) { 5763 throw new Error( 5764 'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' + 5765 ' but got "' + input + '".'); 5766 } 5767 5768 return { 5769 itemName: match[3], 5770 source: $parse(match[4]), 5771 viewMapper: $parse(match[2] || match[1]), 5772 modelMapper: $parse(match[1]) 5773 }; 5774 } 5775 }; 5776 }]) 5777 5778 .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser', 5779 function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) { 5780 var HOT_KEYS = [9, 13, 27, 38, 40]; 5781 var eventDebounceTime = 200; 5782 var modelCtrl, ngModelOptions; 5783 //SUPPORTED ATTRIBUTES (OPTIONS) 5784 5785 //minimal no of characters that needs to be entered before typeahead kicks-in 5786 var minLength = originalScope.$eval(attrs.typeaheadMinLength); 5787 if (!minLength && minLength !== 0) { 5788 minLength = 1; 5789 } 5790 5791 //minimal wait time after last character typed before typeahead kicks-in 5792 var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; 5793 5794 //should it restrict model values to the ones selected from the popup only? 5795 var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; 5796 originalScope.$watch(attrs.typeaheadEditable, function (newVal) { 5797 isEditable = newVal !== false; 5798 }); 5799 5800 //binding to a variable that indicates if matches are being retrieved asynchronously 5801 var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; 5802 5803 //a callback executed when a match is selected 5804 var onSelectCallback = $parse(attrs.typeaheadOnSelect); 5805 5806 //should it select highlighted popup value when losing focus? 5807 var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false; 5808 5809 //binding to a variable that indicates if there were no results after the query is completed 5810 var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop; 5811 5812 var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; 5813 5814 var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; 5815 5816 var appendTo = attrs.typeaheadAppendTo ? 5817 originalScope.$eval(attrs.typeaheadAppendTo) : null; 5818 5819 var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; 5820 5821 //If input matches an item of the list exactly, select it automatically 5822 var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; 5823 5824 //binding to a variable that indicates if dropdown is open 5825 var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop; 5826 5827 var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false; 5828 5829 //INTERNAL VARIABLES 5830 5831 //model setter executed upon match selection 5832 var parsedModel = $parse(attrs.ngModel); 5833 var invokeModelSetter = $parse(attrs.ngModel + '($$$p)'); 5834 var $setModelValue = function(scope, newValue) { 5835 if (angular.isFunction(parsedModel(originalScope)) && 5836 ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) { 5837 return invokeModelSetter(scope, {$$$p: newValue}); 5838 } 5839 5840 return parsedModel.assign(scope, newValue); 5841 }; 5842 5843 //expressions used by typeahead 5844 var parserResult = typeaheadParser.parse(attrs.uibTypeahead); 5845 5846 var hasFocus; 5847 5848 //Used to avoid bug in iOS webview where iOS keyboard does not fire 5849 //mousedown & mouseup events 5850 //Issue #3699 5851 var selected; 5852 5853 //create a child scope for the typeahead directive so we are not polluting original scope 5854 //with typeahead-specific data (matches, query etc.) 5855 var scope = originalScope.$new(); 5856 var offDestroy = originalScope.$on('$destroy', function() { 5857 scope.$destroy(); 5858 }); 5859 scope.$on('$destroy', offDestroy); 5860 5861 // WAI-ARIA 5862 var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000); 5863 element.attr({ 5864 'aria-autocomplete': 'list', 5865 'aria-expanded': false, 5866 'aria-owns': popupId 5867 }); 5868 5869 var inputsContainer, hintInputElem; 5870 //add read-only input to show hint 5871 if (showHint) { 5872 inputsContainer = angular.element('<div></div>'); 5873 inputsContainer.css('position', 'relative'); 5874 element.after(inputsContainer); 5875 hintInputElem = element.clone(); 5876 hintInputElem.attr('placeholder', ''); 5877 hintInputElem.val(''); 5878 hintInputElem.css({ 5879 'position': 'absolute', 5880 'top': '0px', 5881 'left': '0px', 5882 'border-color': 'transparent', 5883 'box-shadow': 'none', 5884 'opacity': 1, 5885 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)', 5886 'color': '#999' 5887 }); 5888 element.css({ 5889 'position': 'relative', 5890 'vertical-align': 'top', 5891 'background-color': 'transparent' 5892 }); 5893 inputsContainer.append(hintInputElem); 5894 hintInputElem.after(element); 5895 } 5896 5897 //pop-up element used to display matches 5898 var popUpEl = angular.element('<div uib-typeahead-popup></div>'); 5899 popUpEl.attr({ 5900 id: popupId, 5901 matches: 'matches', 5902 active: 'activeIdx', 5903 select: 'select(activeIdx, evt)', 5904 'move-in-progress': 'moveInProgress', 5905 query: 'query', 5906 position: 'position', 5907 'assign-is-open': 'assignIsOpen(isOpen)', 5908 debounce: 'debounceUpdate' 5909 }); 5910 //custom item template 5911 if (angular.isDefined(attrs.typeaheadTemplateUrl)) { 5912 popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); 5913 } 5914 5915 if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) { 5916 popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl); 5917 } 5918 5919 var resetHint = function() { 5920 if (showHint) { 5921 hintInputElem.val(''); 5922 } 5923 }; 5924 5925 var resetMatches = function() { 5926 scope.matches = []; 5927 scope.activeIdx = -1; 5928 element.attr('aria-expanded', false); 5929 resetHint(); 5930 }; 5931 5932 var getMatchId = function(index) { 5933 return popupId + '-option-' + index; 5934 }; 5935 5936 // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead. 5937 // This attribute is added or removed automatically when the `activeIdx` changes. 5938 scope.$watch('activeIdx', function(index) { 5939 if (index < 0) { 5940 element.removeAttr('aria-activedescendant'); 5941 } else { 5942 element.attr('aria-activedescendant', getMatchId(index)); 5943 } 5944 }); 5945 5946 var inputIsExactMatch = function(inputValue, index) { 5947 if (scope.matches.length > index && inputValue) { 5948 return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); 5949 } 5950 5951 return false; 5952 }; 5953 5954 var getMatchesAsync = function(inputValue, evt) { 5955 var locals = {$viewValue: inputValue}; 5956 isLoadingSetter(originalScope, true); 5957 isNoResultsSetter(originalScope, false); 5958 $q.when(parserResult.source(originalScope, locals)).then(function(matches) { 5959 //it might happen that several async queries were in progress if a user were typing fast 5960 //but we are interested only in responses that correspond to the current view value 5961 var onCurrentRequest = inputValue === modelCtrl.$viewValue; 5962 if (onCurrentRequest && hasFocus) { 5963 if (matches && matches.length > 0) { 5964 scope.activeIdx = focusFirst ? 0 : -1; 5965 isNoResultsSetter(originalScope, false); 5966 scope.matches.length = 0; 5967 5968 //transform labels 5969 for (var i = 0; i < matches.length; i++) { 5970 locals[parserResult.itemName] = matches[i]; 5971 scope.matches.push({ 5972 id: getMatchId(i), 5973 label: parserResult.viewMapper(scope, locals), 5974 model: matches[i] 5975 }); 5976 } 5977 5978 scope.query = inputValue; 5979 //position pop-up with matches - we need to re-calculate its position each time we are opening a window 5980 //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page 5981 //due to other elements being rendered 5982 recalculatePosition(); 5983 5984 element.attr('aria-expanded', true); 5985 5986 //Select the single remaining option if user input matches 5987 if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) { 5988 if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { 5989 $$debounce(function() { 5990 scope.select(0, evt); 5991 }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); 5992 } else { 5993 scope.select(0, evt); 5994 } 5995 } 5996 5997 if (showHint) { 5998 var firstLabel = scope.matches[0].label; 5999 if (inputValue.length > 0 && firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) { 6000 hintInputElem.val(inputValue + firstLabel.slice(inputValue.length)); 6001 } 6002 else { 6003 hintInputElem.val(''); 6004 } 6005 } 6006 } else { 6007 resetMatches(); 6008 isNoResultsSetter(originalScope, true); 6009 } 6010 } 6011 if (onCurrentRequest) { 6012 isLoadingSetter(originalScope, false); 6013 } 6014 }, function() { 6015 resetMatches(); 6016 isLoadingSetter(originalScope, false); 6017 isNoResultsSetter(originalScope, true); 6018 }); 6019 }; 6020 6021 // bind events only if appendToBody params exist - performance feature 6022 if (appendToBody) { 6023 angular.element($window).on('resize', fireRecalculating); 6024 $document.find('body').on('scroll', fireRecalculating); 6025 } 6026 6027 // Declare the debounced function outside recalculating for 6028 // proper debouncing 6029 var debouncedRecalculate = $$debounce(function() { 6030 // if popup is visible 6031 if (scope.matches.length) { 6032 recalculatePosition(); 6033 } 6034 6035 scope.moveInProgress = false; 6036 }, eventDebounceTime); 6037 6038 // Default progress type 6039 scope.moveInProgress = false; 6040 6041 function fireRecalculating() { 6042 if (!scope.moveInProgress) { 6043 scope.moveInProgress = true; 6044 scope.$digest(); 6045 } 6046 6047 debouncedRecalculate(); 6048 } 6049 6050 // recalculate actual position and set new values to scope 6051 // after digest loop is popup in right position 6052 function recalculatePosition() { 6053 scope.position = appendToBody ? $position.offset(element) : $position.position(element); 6054 scope.position.top += element.prop('offsetHeight'); 6055 } 6056 6057 //we need to propagate user's query so we can higlight matches 6058 scope.query = undefined; 6059 6060 //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later 6061 var timeoutPromise; 6062 6063 var scheduleSearchWithTimeout = function(inputValue) { 6064 timeoutPromise = $timeout(function() { 6065 getMatchesAsync(inputValue); 6066 }, waitTime); 6067 }; 6068 6069 var cancelPreviousTimeout = function() { 6070 if (timeoutPromise) { 6071 $timeout.cancel(timeoutPromise); 6072 } 6073 }; 6074 6075 resetMatches(); 6076 6077 scope.assignIsOpen = function (isOpen) { 6078 isOpenSetter(originalScope, isOpen); 6079 }; 6080 6081 scope.select = function(activeIdx, evt) { 6082 //called from within the $digest() cycle 6083 var locals = {}; 6084 var model, item; 6085 6086 selected = true; 6087 locals[parserResult.itemName] = item = scope.matches[activeIdx].model; 6088 model = parserResult.modelMapper(originalScope, locals); 6089 $setModelValue(originalScope, model); 6090 modelCtrl.$setValidity('editable', true); 6091 modelCtrl.$setValidity('parse', true); 6092 6093 onSelectCallback(originalScope, { 6094 $item: item, 6095 $model: model, 6096 $label: parserResult.viewMapper(originalScope, locals), 6097 $event: evt 6098 }); 6099 6100 resetMatches(); 6101 6102 //return focus to the input element if a match was selected via a mouse click event 6103 // use timeout to avoid $rootScope:inprog error 6104 if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) { 6105 $timeout(function() { element[0].focus(); }, 0, false); 6106 } 6107 }; 6108 6109 //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) 6110 element.on('keydown', function(evt) { 6111 //typeahead is open and an "interesting" key was pressed 6112 if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { 6113 return; 6114 } 6115 6116 // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results 6117 if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) { 6118 resetMatches(); 6119 scope.$digest(); 6120 return; 6121 } 6122 6123 evt.preventDefault(); 6124 var target; 6125 switch (evt.which) { 6126 case 9: 6127 case 13: 6128 scope.$apply(function () { 6129 if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { 6130 $$debounce(function() { 6131 scope.select(scope.activeIdx, evt); 6132 }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); 6133 } else { 6134 scope.select(scope.activeIdx, evt); 6135 } 6136 }); 6137 break; 6138 case 27: 6139 evt.stopPropagation(); 6140 6141 resetMatches(); 6142 scope.$digest(); 6143 break; 6144 case 38: 6145 scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; 6146 scope.$digest();
6147 target = popUpEl.find('li')[scope.activeIdx]; 6148 target.parentNode.scrollTop = target.offsetTop; 6149 break; 6150 case 40: 6151 scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; 6152 scope.$digest(); 6153 target = popUpEl.find('li')[scope.activeIdx]; 6154 target.parentNode.scrollTop = target.offsetTop; 6155 break; 6156 } 6157 }); 6158 6159 element.bind('focus', function (evt) { 6160 hasFocus = true; 6161 if (minLength === 0 && !modelCtrl.$viewValue) { 6162 $timeout(function() { 6163 getMatchesAsync(modelCtrl.$viewValue, evt); 6164 }, 0); 6165 } 6166 }); 6167 6168 element.bind('blur', function(evt) { 6169 if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { 6170 selected = true; 6171 scope.$apply(function() { 6172 if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) { 6173 $$debounce(function() { 6174 scope.select(scope.activeIdx, evt); 6175 }, scope.debounceUpdate.blur); 6176 } else { 6177 scope.select(scope.activeIdx, evt); 6178 } 6179 }); 6180 } 6181 if (!isEditable && modelCtrl.$error.editable) { 6182 modelCtrl.$viewValue = ''; 6183 element.val(''); 6184 } 6185 hasFocus = false; 6186 selected = false; 6187 }); 6188 6189 // Keep reference to click handler to unbind it. 6190 var dismissClickHandler = function(evt) { 6191 // Issue #3973 6192 // Firefox treats right click as a click on document 6193 if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) { 6194 resetMatches(); 6195 if (!$rootScope.$$phase) { 6196 scope.$digest(); 6197 } 6198 } 6199 }; 6200 6201 $document.on('click', dismissClickHandler); 6202 6203 originalScope.$on('$destroy', function() { 6204 $document.off('click', dismissClickHandler); 6205 if (appendToBody || appendTo) { 6206 $popup.remove(); 6207 } 6208 6209 if (appendToBody) { 6210 angular.element($window).off('resize', fireRecalculating); 6211 $document.find('body').off('scroll', fireRecalculating); 6212 } 6213 // Prevent jQuery cache memory leak 6214 popUpEl.remove(); 6215 6216 if (showHint) { 6217 inputsContainer.remove(); 6218 } 6219 }); 6220 6221 var $popup = $compile(popUpEl)(scope); 6222 6223 if (appendToBody) { 6224 $document.find('body').append($popup); 6225 } else if (appendTo) { 6226 angular.element(appendTo).eq(0).append($popup); 6227 } else { 6228 element.after($popup); 6229 } 6230 6231 this.init = function(_modelCtrl, _ngModelOptions) { 6232 modelCtrl = _modelCtrl; 6233 ngModelOptions = _ngModelOptions; 6234 6235 scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope); 6236 6237 //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM 6238 //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue 6239 modelCtrl.$parsers.unshift(function(inputValue) { 6240 hasFocus = true; 6241 6242 if (minLength === 0 || inputValue && inputValue.length >= minLength) { 6243 if (waitTime > 0) { 6244 cancelPreviousTimeout(); 6245 scheduleSearchWithTimeout(inputValue); 6246 } else { 6247 getMatchesAsync(inputValue); 6248 } 6249 } else { 6250 isLoadingSetter(originalScope, false); 6251 cancelPreviousTimeout(); 6252 resetMatches(); 6253 } 6254 6255 if (isEditable) { 6256 return inputValue; 6257 } 6258 6259 if (!inputValue) { 6260 // Reset in case user had typed something previously. 6261 modelCtrl.$setValidity('editable', true); 6262 return null; 6263 } 6264 6265 modelCtrl.$setValidity('editable', false); 6266 return undefined; 6267 }); 6268 6269 modelCtrl.$formatters.push(function(modelValue) { 6270 var candidateViewValue, emptyViewValue; 6271 var locals = {}; 6272 6273 // The validity may be set to false via $parsers (see above) if 6274 // the model is restricted to selected values. If the model 6275 // is set manually it is considered to be valid. 6276 if (!isEditable) { 6277 modelCtrl.$setValidity('editable', true); 6278 } 6279 6280 if (inputFormatter) { 6281 locals.$model = modelValue; 6282 return inputFormatter(originalScope, locals); 6283 } 6284 6285 //it might happen that we don't have enough info to properly render input value 6286 //we need to check for this situation and simply return model value if we can't apply custom formatting 6287 locals[parserResult.itemName] = modelValue; 6288 candidateViewValue = parserResult.viewMapper(originalScope, locals); 6289 locals[parserResult.itemName] = undefined; 6290 emptyViewValue = parserResult.viewMapper(originalScope, locals); 6291 6292 return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; 6293 }); 6294 }; 6295 }]) 6296 6297 .directive('uibTypeahead', function() { 6298 return { 6299 controller: 'UibTypeaheadController', 6300 require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'], 6301 link: function(originalScope, element, attrs, ctrls) { 6302 ctrls[2].init(ctrls[0], ctrls[1]); 6303 } 6304 }; 6305 }) 6306 6307 .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) { 6308 return { 6309 scope: { 6310 matches: '=', 6311 query: '=', 6312 active: '=', 6313 position: '&', 6314 moveInProgress: '=', 6315 select: '&', 6316 assignIsOpen: '&', 6317 debounce: '&' 6318 }, 6319 replace: true, 6320 templateUrl: function(element, attrs) { 6321 return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html'; 6322 }, 6323 link: function(scope, element, attrs) { 6324 scope.templateUrl = attrs.templateUrl; 6325 6326 scope.isOpen = function() { 6327 var isDropdownOpen = scope.matches.length > 0; 6328 scope.assignIsOpen({ isOpen: isDropdownOpen }); 6329 return isDropdownOpen; 6330 }; 6331 6332 scope.isActive = function(matchIdx) { 6333 return scope.active === matchIdx; 6334 }; 6335 6336 scope.selectActive = function(matchIdx) {
vendor: 5,139 bytes, lines 6337-6443
6337 scope.active = matchIdx; 6338 }; 6339 6340 scope.selectMatch = function(activeIdx, evt) { 6341 var debounce = scope.debounce(); 6342 if (angular.isNumber(debounce) || angular.isObject(debounce)) { 6343 $$debounce(function() { 6344 scope.select({activeIdx: activeIdx, evt: evt}); 6345 }, angular.isNumber(debounce) ? debounce : debounce['default']); 6346 } else { 6347 scope.select({activeIdx: activeIdx, evt: evt}); 6348 } 6349 }; 6350 } 6351 }; 6352 }]) 6353 6354 .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) { 6355 return { 6356 scope: { 6357 index: '=', 6358 match: '=', 6359 query: '=' 6360 }, 6361 link: function(scope, element, attrs) { 6362 var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html'; 6363 $templateRequest(tplUrl).then(function(tplContent) { 6364 var tplEl = angular.element(tplContent.trim()); 6365 element.replaceWith(tplEl); 6366 $compile(tplEl)(scope); 6367 }); 6368 } 6369 }; 6370 }]) 6371 6372 .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) { 6373 var isSanitizePresent; 6374 isSanitizePresent = $injector.has('$sanitize'); 6375 6376 function escapeRegexp(queryToEscape) { 6377 // Regex: capture the whole query string and replace it with the string that will be used to match 6378 // the results, for example if the capture is "a" the result will be \a 6379 return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); 6380 } 6381 6382 function containsHtml(matchItem) { 6383 return /<.*>/g.test(matchItem); 6384 } 6385 6386 return function(matchItem, query) { 6387 if (!isSanitizePresent && containsHtml(matchItem)) { 6388 $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger 6389 } 6390 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 6391 if (!isSanitizePresent) { 6392 matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive 6393 } 6394 return matchItem; 6395 }; 6396 }]); 6397 6398angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) { 6399 $templateCache.put("uib/template/accordion/accordion-group.html", 6400 "<div class=\"panel\" ng-class=\"panelClass || 'panel-default'\">\n" + 6401 " <div class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" + 6402 " <h4 class=\"panel-title\">\n" + 6403 " <div tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></div>\n" + 6404 " </h4>\n" + 6405 " </div>\n" + 6406 " <div class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" + 6407 " <div class=\"panel-body\" ng-transclude></div>\n" + 6408 " </div>\n" + 6409 "</div>\n" + 6410 ""); 6411}]); 6412 6413angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) { 6414 $templateCache.put("uib/template/accordion/accordion.html", 6415 "<div class=\"panel-group\" ng-transclude></div>"); 6416}]); 6417 6418angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) { 6419 $templateCache.put("uib/template/alert/alert.html", 6420 "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" + 6421 " <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" + 6422 " <span aria-hidden=\"true\">×</span>\n" + 6423 " <span class=\"sr-only\">Close</span>\n" + 6424 " </button>\n" + 6425 " <div ng-transclude></div>\n" + 6426 "</div>\n" + 6427 ""); 6428}]); 6429 6430angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) { 6431 $templateCache.put("uib/template/carousel/carousel.html", 6432 "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" + 6433 " <div class=\"carousel-inner\" ng-transclude></div>\n" + 6434 " <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\">\n" + 6435 " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" + 6436 " <span class=\"sr-only\">previous</span>\n" + 6437 " </a>\n" + 6438 " <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\">\n" + 6439 " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" + 6440 " <span class=\"sr-only\">next</span>\n" + 6441 " </a>\n" + 6442 " <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" + 6443 " <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" +
6444 " <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" + 6445 " </li>\n" + 6446 " </ol>\n" + 6447 "</div>"); 6448}]); 6449 6450angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) { 6451 $templateCache.put("uib/template/carousel/slide.html", 6452 "<div ng-class=\"{\n" + 6453 " 'active': active\n" + 6454 " }\" class=\"item text-center\" ng-transclude></div>\n" + 6455 ""); 6456}]); 6457 6458angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) { 6459 $templateCache.put("uib/template/datepicker/datepicker.html", 6460 "<div class=\"uib-datepicker\" ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" + 6461 " <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" + 6462 " <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" + 6463 " <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" + 6464 "</div>"); 6465}]); 6466 6467angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) { 6468 $templateCache.put("uib/template/datepicker/day.html", 6469 "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + 6470 " <thead>\n" + 6471 " <tr>\n" + 6472 " <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" + 6473 " <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\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + 6474 " <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" + 6475 " </tr>\n" + 6476 " <tr>\n" + 6477 " <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" + 6478 " <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" + 6479 " </tr>\n" + 6480 " </thead>\n" + 6481 " <tbody>\n" + 6482 " <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" + 6483 " <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" + 6484 " <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" + 6485 " id=\"{{::dt.uid}}\"\n" + 6486 " ng-class=\"::dt.customClass\">\n" + 6487 " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\"\n" + 6488 " uib-is-class=\"\n" + 6489 " 'btn-info' for selectedDt,\n" + 6490 " 'active' for activeDt\n" + 6491 " on dt\"\n" + 6492 " ng-click=\"select(dt.date)\"\n" + 6493 " ng-disabled=\"::dt.disabled\"\n" + 6494 " tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + 6495 " </td>\n" + 6496 " </tr>\n" + 6497 " </tbody>\n" + 6498 "</table>\n" + 6499 ""); 6500}]); 6501 6502angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) { 6503 $templateCache.put("uib/template/datepicker/month.html", 6504 "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + 6505 " <thead>\n" + 6506 " <tr>\n" + 6507 " <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" + 6508 " <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 === max
6508Mode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + 6509 " <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" + 6510 " </tr>\n" + 6511 " </thead>\n" + 6512 " <tbody>\n" + 6513 " <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" + 6514 " <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" + 6515 " id=\"{{::dt.uid}}\"\n" + 6516 " ng-class=\"::dt.customClass\">\n" + 6517 " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" + 6518 " uib-is-class=\"\n" + 6519 " 'btn-info' for selectedDt,\n" + 6520 " 'active' for activeDt\n" + 6521 " on dt\"\n" + 6522 " ng-click=\"select(dt.date)\"\n" + 6523 " ng-disabled=\"::dt.disabled\"\n" + 6524 " tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + 6525 " </td>\n" + 6526 " </tr>\n" + 6527 " </tbody>\n" + 6528 "</table>\n" + 6529 ""); 6530}]); 6531 6532angular.module("uib/template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) { 6533 $templateCache.put("uib/template/datepicker/popup.html", 6534 "<ul class=\"uib-datepicker-popup dropdown-menu\" dropdown-nested ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" + 6535 " <li ng-transclude></li>\n" + 6536 " <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\" class=\"uib-button-bar\">\n" + 6537 " <span class=\"btn-group pull-left\">\n" + 6538 " <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" + 6539 " <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" + 6540 " </span>\n" + 6541 " <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close()\">{{ getText('close') }}</button>\n" + 6542 " </li>\n" + 6543 "</ul>\n" + 6544 ""); 6545}]); 6546 6547angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) { 6548 $templateCache.put("uib/template/datepicker/year.html", 6549 "<table class=\"uib-yearpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + 6550 " <thead>\n" + 6551 " <tr>\n" + 6552 " <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" + 6553 " <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\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + 6554 " <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" + 6555 " </tr>\n" + 6556 " </thead>\n" + 6557 " <tbody>\n" + 6558 " <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\">\n" + 6559 " <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" + 6560 " id=\"{{::dt.uid}}\"\n" + 6561 " ng-class=\"::dt.customClass\">\n" + 6562 " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" + 6563 " uib-is-class=\"\n" + 6564 " 'btn-info' for selectedDt,\n" + 6565 " 'active' for activeDt\n" + 6566 " on dt\"\n" + 6567 " ng-click=\"select(dt.date)\"\n" + 6568 " ng-disabled=\"::dt.disabled\"\n" + 6569 " tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + 6570 " </td>\n" + 6571 " </tr>\n" + 6572 " </tbody>\n" + 6573 "</table>\n" + 6574 ""); 6575}]); 6576 6577angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) { 6578 $templateCache.put("uib/template/modal/backdrop.html", 6579 "<div class=\"modal-backdrop\"\n" + 6580 " uib-modal-animation-class=\"fade\"\n" + 6581 " modal-in-class=\"in\"\n" +
6582 " ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" + 6583 "></div>\n" + 6584 ""); 6585}]); 6586 6587angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) { 6588 $templateCache.put("uib/template/modal/window.html", 6589 "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" + 6590 " uib-modal-animation-class=\"fade\"\n" + 6591 " modal-in-class=\"in\"\n" + 6592 " ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" + 6593 " <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" + 6594 "</div>\n" + 6595 ""); 6596}]); 6597 6598angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) { 6599 $templateCache.put("uib/template/pager/pager.html", 6600 "<ul class=\"pager\">\n" + 6601 " <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" + 6602 " <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" + 6603 "</ul>\n" + 6604 ""); 6605}]); 6606 6607angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) { 6608 $templateCache.put("uib/template/pagination/pagination.html", 6609 "<ul class=\"pagination\">\n" + 6610 " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" + 6611 " <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" + 6612 " <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" + 6613 " <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" + 6614 " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" + 6615 "</ul>\n" + 6616 ""); 6617}]); 6618 6619angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) { 6620 $templateCache.put("uib/template/tooltip/tooltip-html-popup.html", 6621 "<div class=\"tooltip\"\n" + 6622 " tooltip-animation-class=\"fade\"\n" + 6623 " uib-tooltip-classes\n" + 6624 " ng-class=\"{ in: isOpen() }\">\n" + 6625 " <div class=\"tooltip-arrow\"></div>\n" + 6626 " <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" + 6627 "</div>\n" + 6628 ""); 6629}]); 6630 6631angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) { 6632 $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html", 6633 "<div class=\"tooltip\"\n" + 6634 " tooltip-animation-class=\"fade\"\n" + 6635 " tooltip-classes\n" + 6636 " ng-class=\"{ in: isOpen() }\">\n" + 6637 " <div class=\"tooltip-arrow\"></div>\n" + 6638 " <div class=\"tooltip-inner\" bind-html-unsafe=\"content\"></div>\n" + 6639 "</div>\n" + 6640 ""); 6641}]); 6642 6643angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) { 6644 $templateCache.put("uib/template/tooltip/tooltip-popup.html", 6645 "<div class=\"tooltip\"\n" + 6646 " tooltip-animation-class=\"fade\"\n" + 6647 " uib-tooltip-classes\n" + 6648 " ng-class=\"{ in: isOpen() }\">\n" + 6649 " <div class=\"tooltip-arrow\"></div>\n" + 6650 " <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" + 6651 "</div>\n" + 6652 ""); 6653}]); 6654 6655angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) { 6656 $templateCache.put("uib/template/tooltip/tooltip-template-popup.html", 6657 "<div class=\"tooltip\"\n" + 6658 " tooltip-animation-class=\"fade\"\n" +
6659 " uib-tooltip-classes\n" + 6660 " ng-class=\"{ in: isOpen() }\">\n" + 6661 " <div class=\"tooltip-arrow\"></div>\n" + 6662 " <div class=\"tooltip-inner\"\n" + 6663 " uib-tooltip-template-transclude=\"contentExp()\"\n" + 6664 " tooltip-template-transclude-scope=\"originScope()\"></div>\n" + 6665 "</div>\n" + 6666 ""); 6667}]); 6668 6669angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) { 6670 $templateCache.put("uib/template/popover/popover-html.html", 6671 "<div class=\"popover\"\n" + 6672 " tooltip-animation-class=\"fade\"\n" + 6673 " uib-tooltip-classes\n" + 6674 " ng-class=\"{ in: isOpen() }\">\n" + 6675 " <div class=\"arrow\"></div>\n" + 6676 "\n" + 6677 " <div class=\"popover-inner\">\n" + 6678 " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" + 6679 " <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" + 6680 " </div>\n" + 6681 "</div>\n" + 6682 ""); 6683}]); 6684 6685angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) { 6686 $templateCache.put("uib/template/popover/popover-template.html", 6687 "<div class=\"popover\"\n" + 6688 " tooltip-animation-class=\"fade\"\n" + 6689 " uib-tooltip-classes\n" + 6690 " ng-class=\"{ in: isOpen() }\">\n" + 6691 " <div class=\"arrow\"></div>\n" + 6692 "\n" + 6693 " <div class=\"popover-inner\">\n" + 6694 " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" + 6695 " <div class=\"popover-content\"\n" + 6696 " uib-tooltip-template-transclude=\"contentExp()\"\n" + 6697 " tooltip-template-transclude-scope=\"originScope()\"></div>\n" + 6698 " </div>\n" + 6699 "</div>\n" + 6700 ""); 6701}]); 6702 6703angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) { 6704 $templateCache.put("uib/template/popover/popover.html", 6705 "<div class=\"popover\"\n" + 6706 " tooltip-animation-class=\"fade\"\n" + 6707 " uib-tooltip-classes\n" + 6708 " ng-class=\"{ in: isOpen() }\">\n" + 6709 " <div class=\"arrow\"></div>\n" + 6710 "\n" + 6711 " <div class=\"popover-inner\">\n" + 6712 " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" + 6713 " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" + 6714 " </div>\n" + 6715 "</div>\n" + 6716 ""); 6717}]); 6718 6719angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) { 6720 $templateCache.put("uib/template/progressbar/bar.html", 6721 "<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" + 6722 ""); 6723}]); 6724 6725angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) { 6726 $templateCache.put("uib/template/progressbar/progress.html", 6727 "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>"); 6728}]); 6729 6730angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) { 6731 $templateCache.put("uib/template/progressbar/progressbar.html", 6732 "<div class=\"progress\">\n" + 6733 " <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" + 6734 "</div>\n" + 6735 ""); 6736}]); 6737 6738angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) { 6739 $templateCache.put("uib/template/rating/rating.html", 6740 "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" + 6741 " <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" + 6742 " <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}}\" aria-valuetext=\"{{r.title}}\"></i>\n" + 6743 "</span>\n" + 6744 ""); 6745}]); 6746 6747angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) { 6748 $templateCache.put("uib/template/tabs/tab.html", 6749 "<li ng-class=\"{active: active, disabled: disabled}\" class=\"uib-tab\">\n" + 6750 " <div ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</div>\n" + 6751 "</li>\n" + 6752 ""); 6753}]); 6754 6755angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) { 6756 $templateCache.put("uib/template/tabs/tabset.html", 6757 "<div>\n" + 6758 " <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" + 6759 " <div class=\"tab-content\">\n" + 6760 " <div class=\"tab-pane\" \n" + 6761 " ng-repeat=\"tab in tabs\" \n" + 6762 " ng-class=\"{active: tab.active}\"\n" + 6763 " uib-tab-content-transclude=\"tab\">\n" + 6764 " </div>\n" + 6765 " </div>\n" + 6766 "</div>\n" + 6767 ""); 6768}]); 6769 6770angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) { 6771 $templateCache.put("uib/template/timepicker/timepicker.html", 6772 "<table class=\"uib-timepicker\">\n" + 6773 " <tbody>\n" + 6774 " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" + 6775 " <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" + 6776 " <td> </td>\n" + 6777 " <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" + 6778 " <td ng-show=\"showSeconds\"> </td>\n" + 6779 " <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" + 6780 " <td ng-show=\"showMeridian\"></td>\n" + 6781 " </tr>\n" + 6782 " <tr>\n" + 6783 " <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" + 6784 " <input style=\"width:50px;\" 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" + 6785 " </td>\n" + 6786 " <td class=\"uib-separator\">:</td>\n" + 6787 " <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" + 6788 " <input style=\"width:50px;\" 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" + 6789 " </td>\n" + 6790 " <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" + 6791 " <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" + 6792 " <input style=\"width:50px;\" type=\"text\" 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" + 6793 " </td>\n" + 6794 " <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" + 6795 " </tr>\n" + 6796 " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" + 6797 " <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" + 6798 " <td> </td>\n" + 6799 " <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" + 6800 " <td ng-show=\"showSeconds\"> </td>\n" + 6801 " <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" + 6802 " <td ng-show=\"showMeridian\"></td>\n" + 6803 " </tr>\n" + 6804 " </tbody>\n" + 6805 "</table>\n" + 6806 ""); 6807}]); 6808 6809angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) { 6810 $templateCache.put("uib/template/typeahead/typeahead-match.html", 6811 "<a href tabindex=\"-1\" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"></a>\n" + 6812 ""); 6813}]); 6814 6815angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) { 6816 $templateCache.put("uib/template/typeahead/typeahead-popup.html", 6817 "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" + 6818 " <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" + 6819 " <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" + 6820 " </li>\n" + 6821 "</ul>\n" + 6822 ""); 6823}]); 6824angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp().noInlineStyle && angular.element(document).find('head').prepend('<style type="text/css">
6824.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>'); }); 6825angular.module('ui.bootstrap.tabs').run(function() {!angular.$$csp().noInlineStyle && angular.element(document).find('head').prepend('<style type="text/css">.uib-tab > div{position:relative;display:block;padding:10px 15px;outline:0;color:#337ab7;}.uib-tab > div:focus,.uib-tab > div:hover{background-color:#eee;color:#23527c;}.uib-tab.disabled > div{color:#777;}.uib-tab.disabled > div:focus,.uib-tab.disabled > div:hover{color:#777;cursor:not-allowed;background-color:transparent;}.nav-tabs > .uib-tab > div{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0;}.nav-tabs > .uib-tab > div:hover{border-color:#eee #eee #ddd;}.nav-tabs > .uib-tab.active > div,.nav-tabs > .uib-tab.active > div:focus,.nav-tabs > .uib-tab.active > div:hover{color:#555;cursor:default;background-color:#fff;border-color:#ddd #ddd transparent #ddd;}.nav-pills > .uib-tab > div{border-radius:4px;}.nav-pills > .uib-tab.active > div,.nav-pills > .uib-tab.active > div:focus,.nav-pills > .uib-tab.active > div:hover{color:#fff;background-color:#337ab7;}</style>'); });
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.