vendor: 13,417 bytes, lines 1-367
1/* 2 * angular-ui-bootstrap 3 * http://angular-ui.github.io/bootstrap/ 4 5 * Version: 2.5.0 - 2017-01-28 6 * License: MIT 7 */angular.module("ui.bootstrap", ["ui.bootstrap.tpls","ui.bootstrap.tooltip","ui.bootstrap.position","ui.bootstrap.stackedMap","ui.bootstrap.popover"]); 8angular.module("ui.bootstrap.tpls", ["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"]); 9/** 10 * The following features are still outstanding: animation as a 11 * function, placement as a function, inside, support for more triggers than 12 * just mouse enter/leave, html tooltips, and selector delegation. 13 */ 14angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap']) 15 16/** 17 * The $tooltip service creates tooltip- and popover-like directives as well as 18 * houses global options for them. 19 */ 20.provider('$uibTooltip', function() { 21 // The default options tooltip and popover. 22 var defaultOptions = { 23 placement: 'top', 24 placementClassPrefix: '', 25 animation: true, 26 popupDelay: 0, 27 popupCloseDelay: 0, 28 useContentExp: false 29 }; 30 31 // Default hide triggers for each show trigger 32 var triggerMap = { 33 'mouseenter': 'mouseleave', 34 'click': 'click', 35 'outsideClick': 'outsideClick', 36 'focus': 'blur', 37 'none': '' 38 }; 39 40 // The options specified to the provider globally. 41 var globalOptions = {}; 42 43 /** 44 * `options({})` allows global configuration of all tooltips in the 45 * application. 46 * 47 * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { 48 * // place tooltips left instead of top by default 49 * $tooltipProvider.options( { placement: 'left' } ); 50 * }); 51 */ 52 this.options = function(value) { 53 angular.extend(globalOptions, value); 54 }; 55 56 /** 57 * This allows you to extend the set of trigger mappings available. E.g.: 58 * 59 * $tooltipProvider.setTriggers( { 'openTrigger': 'closeTrigger' } ); 60 */ 61 this.setTriggers = function setTriggers(triggers) { 62 angular.extend(triggerMap, triggers); 63 }; 64 65 /** 66 * This is a helper function for translating camel-case to snake_case. 67 */ 68 function snake_case(name) { 69 var regexp = /[A-Z]/g; 70 var separator = '-'; 71 return name.replace(regexp, function(letter, pos) { 72 return (pos ? separator : '') + letter.toLowerCase(); 73 }); 74 } 75 76 /** 77 * Returns the actual instance of the $tooltip service. 78 * TODO support multiple triggers 79 */ 80 this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { 81 var openedTooltips = $$stackedMap.createNew(); 82 $document.on('keyup', keypressListener); 83 84 $rootScope.$on('$destroy', function() { 85 $document.off('keyup', keypressListener); 86 }); 87 88 function keypressListener(e) { 89 if (e.which === 27) { 90 var last = openedTooltips.top(); 91 if (last) { 92 last.value.close(); 93 last = null; 94 } 95 } 96 } 97 98 return function $tooltip(ttType, prefix, defaultTriggerShow, options) { 99 options = angular.extend({}, defaultOptions, globalOptions, options); 100 101 /** 102 * Returns an object of show and hide triggers. 103 * 104 * If a trigger is supplied, 105 * it is used to show the tooltip; otherwise, it will use the `trigger` 106 * option passed to the `$tooltipProvider.options` method; else it will 107 * default to the trigger supplied to this directive factory. 108 * 109 * The hide trigger is based on the show trigger. If the `trigger` option 110 * was passed to the `$tooltipProvider.options` method, it will use the 111 * mapped trigger from `triggerMap` or the passed trigger if the map is 112 * undefined; otherwise, it uses the `triggerMap` value of the show 113 * trigger; else it will just use the show trigger. 114 */ 115 function getTriggers(trigger) { 116 var show = (trigger || options.trigger || defaultTriggerShow).split(' '); 117 var hide = show.map(function(trigger) { 118 return triggerMap[trigger] || trigger; 119 }); 120 return { 121 show: show, 122 hide: hide 123 }; 124 } 125 126 var directiveName = snake_case(ttType); 127 128 var startSym = $interpolate.startSymbol(); 129 var endSym = $interpolate.endSymbol(); 130 var template = 131 '<div '+ directiveName + '-popup ' + 132 'uib-title="' + startSym + 'title' + endSym + '" ' + 133 (options.useContentExp ? 134 'content-exp="contentExp()" ' : 135 'content="' + startSym + 'content' + endSym + '" ') + 136 'origin-scope="origScope" ' + 137 'class="uib-position-measure ' + prefix + '" ' + 138 'tooltip-animation-class="fade"' + 139 'uib-tooltip-classes ' + 140 'ng-class="{ in: isOpen }" ' + 141 '>' + 142 '</div>'; 143 144 return { 145 compile: function(tElem, tAttrs) { 146 var tooltipLinker = $compile(template); 147 148 return function link(scope, element, attrs, tooltipCtrl) { 149 var tooltip; 150 var tooltipLinkedScope; 151 var transitionTimeout; 152 var showTimeout; 153 var hideTimeout; 154 var positionTimeout; 155 var adjustmentTimeout; 156 var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false; 157 var triggers = getTriggers(undefined); 158 var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); 159 var ttScope = scope.$new(true); 160 var repositionScheduled = false; 161 var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; 162 var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; 163 var observers = []; 164 var lastPlacement; 165 166 var positionTooltip = function() { 167 // check if tooltip exists and is not empty 168 if (!tooltip || !tooltip.html()) { return; } 169 170 if (!positionTimeout) { 171 positionTimeout = $timeout(function() { 172 var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); 173 var initialHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight'); 174 var elementPos = appendToBody ? $position.offset(element) : $position.position(element); 175 tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' }); 176 var placementClasses = ttPosition.placement.split('-'); 177 178 if (!tooltip.hasClass(placementClasses[0])) { 179 tooltip.removeClass(lastPlacement.split('-')[0]); 180 tooltip.addClass(placementClasses[0]); 181 } 182 183 if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) { 184 tooltip.removeClass(options.placementClassPrefix + lastPlacement); 185 tooltip.addClass(options.placementClassPrefix + ttPosition.placement); 186 } 187 188 adjustmentTimeout = $timeout(function() { 189 var currentHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight'); 190 var adjustment = $position.adjustTop(placementClasses, elementPos, initialHeight, currentHeight); 191 if (adjustment) { 192 tooltip.css(adjustment); 193 } 194 adjustmentTimeout = null; 195 }, 0, false); 196 197 // first time through tt element will have the 198 // uib-position-measure class or if the placement 199 // has changed we need to position the arrow. 200 if (tooltip.hasClass('uib-position-measure')) { 201 $position.positionArrow(tooltip, ttPosition.placement); 202 tooltip.removeClass('uib-position-measure'); 203 } else if (lastPlacement !== ttPosition.placement) { 204 $position.positionArrow(tooltip, ttPosition.placement); 205 } 206 lastPlacement = ttPosition.placement; 207 208 positionTimeout = null; 209 }, 0, false); 210 } 211 }; 212 213 // Set up the correct scope to allow transclusion later 214 ttScope.origScope = scope; 215 216 // By default, the tooltip is not open. 217 // TODO add ability to start tooltip opened 218 ttScope.isOpen = false; 219 220 function toggleTooltipBind() { 221 if (!ttScope.isOpen) { 222 showTooltipBind(); 223 } else { 224 hideTooltipBind(); 225 } 226 } 227 228 // Show the tooltip with delay if specified, otherwise show it immediately 229 function showTooltipBind() { 230 if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { 231 return; 232 } 233 234 cancelHide(); 235 prepareTooltip(); 236 237 if (ttScope.popupDelay) { 238 // Do nothing if the tooltip was already scheduled to pop-up. 239 // This happens if show is triggered multiple times before any hide is triggered. 240 if (!showTimeout) { 241 showTimeout = $timeout(show, ttScope.popupDelay, false); 242 } 243 } else { 244 show(); 245 } 246 } 247 248 function hideTooltipBind() { 249 cancelShow(); 250 251 if (ttScope.popupCloseDelay) { 252 if (!hideTimeout) { 253 hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); 254 } 255 } else { 256 hide(); 257 } 258 } 259 260 // Show the tooltip popup element. 261 function show() { 262 cancelShow(); 263 cancelHide(); 264 265 // Don't show empty tooltips. 266 if (!ttScope.content) { 267 return angular.noop; 268 } 269 270 createTooltip(); 271 272 // And show the tooltip. 273 ttScope.$evalAsync(function() { 274 ttScope.isOpen = true; 275 assignIsOpen(true); 276 positionTooltip(); 277 }); 278 } 279 280 function cancelShow() { 281 if (showTimeout) { 282 $timeout.cancel(showTimeout); 283 showTimeout = null; 284 } 285 286 if (positionTimeout) { 287 $timeout.cancel(positionTimeout); 288 positionTimeout = null; 289 } 290 } 291 292 // Hide the tooltip popup element. 293 function hide() { 294 if (!ttScope) { 295 return; 296 } 297 298 // First things first: we don't show it anymore. 299 ttScope.$evalAsync(function() { 300 if (ttScope) { 301 ttScope.isOpen = false; 302 assignIsOpen(false); 303 // And now we remove it from the DOM. However, if we have animation, we 304 // need to wait for it to expire beforehand. 305 // FIXME: this is a placeholder for a port of the transitions library. 306 // The fade transition in TWBS is 150ms. 307 if (ttScope.animation) { 308 if (!transitionTimeout) { 309 transitionTimeout = $timeout(removeTooltip, 150, false); 310 } 311 } else { 312 removeTooltip(); 313 } 314 } 315 }); 316 } 317 318 function cancelHide() { 319 if (hideTimeout) { 320 $timeout.cancel(hideTimeout); 321 hideTimeout = null; 322 } 323 324 if (transitionTimeout) { 325 $timeout.cancel(transitionTimeout); 326 transitionTimeout = null; 327 } 328 } 329 330 function createTooltip() { 331 // There can only be one tooltip element per directive shown at once. 332 if (tooltip) { 333 return; 334 } 335 336 tooltipLinkedScope = ttScope.$new(); 337 tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { 338 if (appendToBody) { 339 $document.find('body').append(tooltip); 340 } else { 341 element.after(tooltip); 342 } 343 }); 344 345 openedTooltips.add(ttScope, { 346 close: hide 347 }); 348 349 prepObservers(); 350 } 351 352 function removeTooltip() { 353 cancelShow(); 354 cancelHide(); 355 unregisterObservers(); 356 357 if (tooltip) { 358 tooltip.remove(); 359 360 tooltip = null; 361 if (adjustmentTimeout) { 362 $timeout.cancel(adjustmentTimeout); 363 } 364 } 365 366 openedTooltips.remove(ttScope); 367
vendor: 12,086 bytes, lines 367-734
367 368 if (tooltipLinkedScope) { 369 tooltipLinkedScope.$destroy(); 370 tooltipLinkedScope = null; 371 } 372 } 373 374 /** 375 * Set the initial scope values. Once 376 * the tooltip is created, the observers 377 * will be added to keep things in sync. 378 */ 379 function prepareTooltip() { 380 ttScope.title = attrs[prefix + 'Title']; 381 if (contentParse) { 382 ttScope.content = contentParse(scope); 383 } else { 384 ttScope.content = attrs[ttType]; 385 } 386 387 ttScope.popupClass = attrs[prefix + 'Class']; 388 ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; 389 var placement = $position.parsePlacement(ttScope.placement); 390 lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0]; 391 392 var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); 393 var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); 394 ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; 395 ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; 396 } 397 398 function assignIsOpen(isOpen) { 399 if (isOpenParse && angular.isFunction(isOpenParse.assign)) { 400 isOpenParse.assign(scope, isOpen); 401 } 402 } 403 404 ttScope.contentExp = function() { 405 return ttScope.content; 406 }; 407 408 /** 409 * Observe the relevant attributes. 410 */ 411 attrs.$observe('disabled', function(val) { 412 if (val) { 413 cancelShow(); 414 } 415 416 if (val && ttScope.isOpen) { 417 hide(); 418 } 419 }); 420 421 if (isOpenParse) { 422 scope.$watch(isOpenParse, function(val) { 423 if (ttScope && !val === ttScope.isOpen) { 424 toggleTooltipBind(); 425 } 426 }); 427 } 428 429 function prepObservers() { 430 observers.length = 0; 431 432 if (contentParse) { 433 observers.push( 434 scope.$watch(contentParse, function(val) { 435 ttScope.content = val; 436 if (!val && ttScope.isOpen) { 437 hide(); 438 } 439 }) 440 ); 441 442 observers.push( 443 tooltipLinkedScope.$watch(function() { 444 if (!repositionScheduled) { 445 repositionScheduled = true; 446 tooltipLinkedScope.$$postDigest(function() { 447 repositionScheduled = false; 448 if (ttScope && ttScope.isOpen) { 449 positionTooltip(); 450 } 451 }); 452 } 453 }) 454 ); 455 } else { 456 observers.push( 457 attrs.$observe(ttType, function(val) { 458 ttScope.content = val; 459 if (!val && ttScope.isOpen) { 460 hide(); 461 } else { 462 positionTooltip(); 463 } 464 }) 465 ); 466 } 467 468 observers.push( 469 attrs.$observe(prefix + 'Title', function(val) { 470 ttScope.title = val; 471 if (ttScope.isOpen) { 472 positionTooltip(); 473 } 474 }) 475 ); 476 477 observers.push( 478 attrs.$observe(prefix + 'Placement', function(val) { 479 ttScope.placement = val ? val : options.placement; 480 if (ttScope.isOpen) { 481 positionTooltip(); 482 } 483 }) 484 ); 485 } 486 487 function unregisterObservers() { 488 if (observers.length) { 489 angular.forEach(observers, function(observer) { 490 observer(); 491 }); 492 observers.length = 0; 493 } 494 } 495 496 // hide tooltips/popovers for outsideClick trigger 497 function bodyHideTooltipBind(e) { 498 if (!ttScope || !ttScope.isOpen || !tooltip) { 499 return; 500 } 501 // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked 502 if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { 503 hideTooltipBind(); 504 } 505 } 506 507 // KeyboardEvent handler to hide the tooltip on Escape key press 508 function hideOnEscapeKey(e) { 509 if (e.which === 27) { 510 hideTooltipBind(); 511 } 512 } 513 514 var unregisterTriggers = function() { 515 triggers.show.forEach(function(trigger) { 516 if (trigger === 'outsideClick') { 517 element.off('click', toggleTooltipBind); 518 } else { 519 element.off(trigger, showTooltipBind); 520 element.off(trigger, toggleTooltipBind); 521 } 522 element.off('keypress', hideOnEscapeKey); 523 }); 524 triggers.hide.forEach(function(trigger) { 525 if (trigger === 'outsideClick') { 526 $document.off('click', bodyHideTooltipBind); 527 } else { 528 element.off(trigger, hideTooltipBind); 529 } 530 }); 531 }; 532 533 function prepTriggers() { 534 var showTriggers = [], hideTriggers = []; 535 var val = scope.$eval(attrs[prefix + 'Trigger']); 536 unregisterTriggers(); 537 538 if (angular.isObject(val)) { 539 Object.keys(val).forEach(function(key) { 540 showTriggers.push(key); 541 hideTriggers.push(val[key]); 542 }); 543 triggers = { 544 show: showTriggers, 545 hide: hideTriggers 546 }; 547 } else { 548 triggers = getTriggers(val); 549 } 550 551 if (triggers.show !== 'none') { 552 triggers.show.forEach(function(trigger, idx) { 553 if (trigger === 'outsideClick') { 554 element.on('click', toggleTooltipBind); 555 $document.on('click', bodyHideTooltipBind); 556 } else if (trigger === triggers.hide[idx]) { 557 element.on(trigger, toggleTooltipBind); 558 } else if (trigger) { 559 element.on(trigger, showTooltipBind); 560 element.on(triggers.hide[idx], hideTooltipBind); 561 } 562 element.on('keypress', hideOnEscapeKey); 563 }); 564 } 565 } 566 567 prepTriggers(); 568 569 var animation = scope.$eval(attrs[prefix + 'Animation']); 570 ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; 571 572 var appendToBodyVal; 573 var appendKey = prefix + 'AppendToBody'; 574 if (appendKey in attrs && attrs[appendKey] === undefined) { 575 appendToBodyVal = true; 576 } else { 577 appendToBodyVal = scope.$eval(attrs[appendKey]); 578 } 579 580 appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; 581 582 // Make sure tooltip is destroyed and removed. 583 scope.$on('$destroy', function onDestroyTooltip() { 584 unregisterTriggers(); 585 removeTooltip(); 586 ttScope = null; 587 }); 588 }; 589 } 590 }; 591 }; 592 }]; 593}) 594 595// This is mostly ngInclude code but with a custom scope 596.directive('uibTooltipTemplateTransclude', [ 597 '$animate', '$sce', '$compile', '$templateRequest', 598function ($animate, $sce, $compile, $templateRequest) { 599 return { 600 link: function(scope, elem, attrs) { 601 var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); 602 603 var changeCounter = 0, 604 currentScope, 605 previousElement, 606 currentElement; 607 608 var cleanupLastIncludeContent = function() { 609 if (previousElement) { 610 previousElement.remove(); 611 previousElement = null; 612 } 613 614 if (currentScope) { 615 currentScope.$destroy(); 616 currentScope = null; 617 } 618 619 if (currentElement) { 620 $animate.leave(currentElement).then(function() { 621 previousElement = null; 622 }); 623 previousElement = currentElement; 624 currentElement = null; 625 } 626 }; 627 628 scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) { 629 var thisChangeId = ++changeCounter; 630 631 if (src) { 632 //set the 2nd param to true to ignore the template request error so that the inner 633 //contents and scope can be cleaned up. 634 $templateRequest(src, true).then(function(response) { 635 if (thisChangeId !== changeCounter) { return; } 636 var newScope = origScope.$new(); 637 var template = response; 638 639 var clone = $compile(template)(newScope, function(clone) { 640 cleanupLastIncludeContent(); 641 $animate.enter(clone, elem); 642 }); 643 644 currentScope = newScope; 645 currentElement = clone; 646 647 currentScope.$emit('$includeContentLoaded', src); 648 }, function() { 649 if (thisChangeId === changeCounter) { 650 cleanupLastIncludeContent(); 651 scope.$emit('$includeContentError', src); 652 } 653 }); 654 scope.$emit('$includeContentRequested', src); 655 } else { 656 cleanupLastIncludeContent(); 657 } 658 }); 659 660 scope.$on('$destroy', cleanupLastIncludeContent); 661 } 662 }; 663}]) 664 665/** 666 * Note that it's intentional that these classes are *not* applied through $animate. 667 * They must not be animated as they're expected to be present on the tooltip on 668 * initialization. 669 */ 670.directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) { 671 return { 672 restrict: 'A', 673 link: function(scope, element, attrs) { 674 // need to set the primary position so the 675 // arrow has space during position measure. 676 // tooltip.positionTooltip() 677 if (scope.placement) { 678 // // There are no top-left etc... classes 679 // // in TWBS, so we need the primary position. 680 var position = $uibPosition.parsePlacement(scope.placement); 681 element.addClass(position[0]); 682 } 683 684 if (scope.popupClass) { 685 element.addClass(scope.popupClass); 686 } 687 688 if (scope.animation) { 689 element.addClass(attrs.tooltipAnimationClass); 690 } 691 } 692 }; 693}]) 694 695.directive('uibTooltipPopup', function() { 696 return { 697 restrict: 'A', 698 scope: { content: '@' }, 699 templateUrl: 'uib/template/tooltip/tooltip-popup.html' 700 }; 701}) 702 703.directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) { 704 return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter'); 705}]) 706 707.directive('uibTooltipTemplatePopup', function() { 708 return { 709 restrict: 'A', 710 scope: { contentExp: '&', originScope: '&' }, 711 templateUrl: 'uib/template/tooltip/tooltip-template-popup.html' 712 }; 713}) 714 715.directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) { 716 return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', { 717 useContentExp: true 718 }); 719}]) 720 721.directive('uibTooltipHtmlPopup', function() { 722 return { 723 restrict: 'A', 724 scope: { contentExp: '&' }, 725 templateUrl: 'uib/template/tooltip/tooltip-html-popup.html' 726 }; 727}) 728 729.directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) { 730 return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', { 731 useContentExp: true 732 }); 733}]); 734
vendor: 26,980 bytes, lines 735-1356
735angular.module('ui.bootstrap.position', []) 736 737/** 738 * A set of utility methods for working with the DOM. 739 * It is meant to be used where we need to absolute-position elements in 740 * relation to another element (this is the case for tooltips, popovers, 741 * typeahead suggestions etc.). 742 */ 743 .factory('$uibPosition', ['$document', '$window', function($document, $window) { 744 /** 745 * Used by scrollbarWidth() function to cache scrollbar's width. 746 * Do not access this variable directly, use scrollbarWidth() instead. 747 */ 748 var SCROLLBAR_WIDTH; 749 /** 750 * scrollbar on body and html element in IE and Edge overlay 751 * content and should be considered 0 width. 752 */ 753 var BODY_SCROLLBAR_WIDTH; 754 var OVERFLOW_REGEX = { 755 normal: /(auto|scroll)/, 756 hidden: /(auto|scroll|hidden)/ 757 }; 758 var PLACEMENT_REGEX = { 759 auto: /\s?auto?\s?/i, 760 primary: /^(top|bottom|left|right)$/, 761 secondary: /^(top|bottom|left|right|center)$/, 762 vertical: /^(top|bottom)$/ 763 }; 764 var BODY_REGEX = /(HTML|BODY)/; 765 766 return { 767 768 /** 769 * Provides a raw DOM element from a jQuery/jQLite element. 770 * 771 * @param {element} elem - The element to convert. 772 * 773 * @returns {element} A HTML element. 774 */ 775 getRawNode: function(elem) { 776 return elem.nodeName ? elem : elem[0] || elem; 777 }, 778 779 /** 780 * Provides a parsed number for a style property. Strips 781 * units and casts invalid numbers to 0. 782 * 783 * @param {string} value - The style value to parse. 784 * 785 * @returns {number} A valid number. 786 */ 787 parseStyle: function(value) { 788 value = parseFloat(value); 789 return isFinite(value) ? value : 0; 790 }, 791 792 /** 793 * Provides the closest positioned ancestor. 794 * 795 * @param {element} element - The element to get the offest parent for. 796 * 797 * @returns {element} The closest positioned ancestor. 798 */ 799 offsetParent: function(elem) { 800 elem = this.getRawNode(elem); 801 802 var offsetParent = elem.offsetParent || $document[0].documentElement; 803 804 function isStaticPositioned(el) { 805 return ($window.getComputedStyle(el).position || 'static') === 'static'; 806 } 807 808 while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) { 809 offsetParent = offsetParent.offsetParent; 810 } 811 812 return offsetParent || $document[0].documentElement; 813 }, 814 815 /** 816 * Provides the scrollbar width, concept from TWBS measureScrollbar() 817 * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js 818 * In IE and Edge, scollbar on body and html element overlay and should 819 * return a width of 0. 820 * 821 * @returns {number} The width of the browser scollbar. 822 */ 823 scrollbarWidth: function(isBody) { 824 if (isBody) { 825 if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) { 826 var bodyElem = $document.find('body'); 827 bodyElem.addClass('uib-position-body-scrollbar-measure'); 828 BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth; 829 BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0; 830 bodyElem.removeClass('uib-position-body-scrollbar-measure'); 831 } 832 return BODY_SCROLLBAR_WIDTH; 833 } 834 835 if (angular.isUndefined(SCROLLBAR_WIDTH)) { 836 var scrollElem = angular.element('<div class="uib-position-scrollbar-measure"></div>'); 837 $document.find('body').append(scrollElem); 838 SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth; 839 SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0; 840 scrollElem.remove(); 841 } 842 843 return SCROLLBAR_WIDTH; 844 }, 845 846 /** 847 * Provides the padding required on an element to replace the scrollbar. 848 * 849 * @returns {object} An object with the following properties: 850 * <ul> 851 * <li>**scrollbarWidth**: the width of the scrollbar</li> 852 * <li>**widthOverflow**: whether the the width is overflowing</li> 853 * <li>**right**: the amount of right padding on the element needed to replace the scrollbar</li> 854 * <li>**rightOriginal**: the amount of right padding currently on the element</li> 855 * <li>**heightOverflow**: whether the the height is overflowing</li> 856 * <li>**bottom**: the amount of bottom padding on the element needed to replace the scrollbar</li> 857 * <li>**bottomOriginal**: the amount of bottom padding currently on the element</li> 858 * </ul> 859 */ 860 scrollbarPadding: function(elem) { 861 elem = this.getRawNode(elem); 862 863 var elemStyle = $window.getComputedStyle(elem); 864 var paddingRight = this.parseStyle(elemStyle.paddingRight); 865 var paddingBottom = this.parseStyle(elemStyle.paddingBottom); 866 var scrollParent = this.scrollParent(elem, false, true); 867 var scrollbarWidth = this.scrollbarWidth(BODY_REGEX.test(scrollParent.tagName)); 868 869 return { 870 scrollbarWidth: scrollbarWidth, 871 widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth, 872 right: paddingRight + scrollbarWidth, 873 originalRight: paddingRight, 874 heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight, 875 bottom: paddingBottom + scrollbarWidth, 876 originalBottom: paddingBottom 877 }; 878 }, 879 880 /** 881 * Checks to see if the element is scrollable. 882 * 883 * @param {element} elem - The element to check. 884 * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, 885 * default is false. 886 * 887 * @returns {boolean} Whether the element is scrollable. 888 */ 889 isScrollable: function(elem, includeHidden) { 890 elem = this.getRawNode(elem); 891 892 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; 893 var elemStyle = $window.getComputedStyle(elem); 894 return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX); 895 }, 896 897 /** 898 * Provides the closest scrollable ancestor. 899 * A port of the jQuery UI scrollParent method: 900 * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js 901 * 902 * @param {element} elem - The element to find the scroll parent of. 903 * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, 904 * default is false. 905 * @param {boolean=} [includeSelf=false] - Should the element being passed be 906 * included in the scrollable llokup. 907 * 908 * @returns {element} A HTML element. 909 */ 910 scrollParent: function(elem, includeHidden, includeSelf) { 911 elem = this.getRawNode(elem); 912 913 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; 914 var documentEl = $document[0].documentElement; 915 var elemStyle = $window.getComputedStyle(elem); 916 if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) { 917 return elem; 918 } 919 var excludeStatic = elemStyle.position === 'absolute'; 920 var scrollParent = elem.parentElement || documentEl; 921 922 if (scrollParent === documentEl || elemStyle.position === 'fixed') { 923 return documentEl; 924 } 925 926 while (scrollParent.parentElement && scrollParent !== documentEl) { 927 var spStyle = $window.getComputedStyle(scrollParent); 928 if (excludeStatic && spStyle.position !== 'static') { 929 excludeStatic = false; 930 } 931 932 if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) { 933 break; 934 } 935 scrollParent = scrollParent.parentElement; 936 } 937 938 return scrollParent; 939 }, 940 941 /** 942 * Provides read-only equivalent of jQuery's position function: 943 * http://api.jquery.com/position/ - distance to closest positioned 944 * ancestor. Does not account for margins by default like jQuery position. 945 * 946 * @param {element} elem - The element to caclulate the position on. 947 * @param {boolean=} [includeMargins=false] - Should margins be accounted 948 * for, default is false. 949 * 950 * @returns {object} An object with the following properties: 951 * <ul> 952 * <li>**width**: the width of the element</li> 953 * <li>**height**: the height of the element</li> 954 * <li>**top**: distance to top edge of offset parent</li> 955 * <li>**left**: distance to left edge of offset parent</li> 956 * </ul> 957 */ 958 position: function(elem, includeMagins) { 959 elem = this.getRawNode(elem); 960 961 var elemOffset = this.offset(elem); 962 if (includeMagins) { 963 var elemStyle = $window.getComputedStyle(elem); 964 elemOffset.top -= this.parseStyle(elemStyle.marginTop); 965 elemOffset.left -= this.parseStyle(elemStyle.marginLeft); 966 } 967 var parent = this.offsetParent(elem); 968 var parentOffset = {top: 0, left: 0}; 969 970 if (parent !== $document[0].documentElement) { 971 parentOffset = this.offset(parent); 972 parentOffset.top += parent.clientTop - parent.scrollTop; 973 parentOffset.left += parent.clientLeft - parent.scrollLeft; 974 } 975 976 return { 977 width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth), 978 height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight), 979 top: Math.round(elemOffset.top - parentOffset.top), 980 left: Math.round(elemOffset.left - parentOffset.left) 981 }; 982 }, 983 984 /** 985 * Provides read-only equivalent of jQuery's offset function: 986 * http://api.jquery.com/offset/ - distance to viewport. Does 987 * not account for borders, margins, or padding on the body 988 * element. 989 * 990 * @param {element} elem - The element to calculate the offset on. 991 * 992 * @returns {object} An object with the following properties: 993 * <ul> 994 * <li>**width**: the width of the element</li> 995 * <li>**height**: the height of the element</li> 996 * <li>**top**: distance to top edge of viewport</li> 997 * <li>**right**: distance to bottom edge of viewport</li> 998 * </ul> 999 */ 1000 offset: function(elem) { 1001 elem = this.getRawNode(elem); 1002 1003 var elemBCR = elem.getBoundingClientRect(); 1004 return { 1005 width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth), 1006 height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight), 1007 top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)), 1008 left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)) 1009 }; 1010 }, 1011 1012 /** 1013 * Provides offset distance to the closest scrollable ancestor 1014 * or viewport. Accounts for border and scrollbar width. 1015 * 1016 * Right and bottom dimensions represent the distance to the 1017 * respective edge of the viewport element. If the element 1018 * edge extends beyond the viewport, a negative value will be 1019 * reported. 1020 * 1021 * @param {element} elem - The element to get the viewport offset for. 1022 * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead 1023 * of the first scrollable element, default is false. 1024 * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element 1025 * be accounted for, default is true. 1026 * 1027 * @returns {object} An object with the following properties: 1028 * <ul> 1029 * <li>**top**: distance to the top content edge of viewport element</li> 1030 * <li>**bottom**: distance to the bottom content edge of viewport element</li> 1031 * <li>**left**: distance to the left content edge of viewport element</li> 1032 * <li>**right**: distance to the right content edge of viewport element</li> 1033 * </ul> 1034 */ 1035 viewportOffset: function(elem, useDocument, includePadding) { 1036 elem = this.getRawNode(elem); 1037 includePadding = includePadding !== false ? true : false; 1038 1039 var elemBCR = elem.getBoundingClientRect(); 1040 var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0}; 1041 1042 var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem); 1043 var offsetParentBCR = offsetParent.getBoundingClientRect(); 1044 1045 offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop; 1046 offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft; 1047 if (offsetParent === $document[0].documentElement) { 1048 offsetBCR.top += $window.pageYOffset; 1049 offsetBCR.left += $window.pageXOffset; 1050 } 1051 offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight; 1052 offsetBCR.right = offsetBCR.left + offsetParent.clientWidth; 1053 1054 if (includePadding) { 1055 var offsetParentStyle = $window.getComputedStyle(offsetParent); 1056 offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop); 1057 offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom); 1058 offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft); 1059 offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight); 1060 } 1061 1062 return { 1063 top: Math.round(elemBCR.top - offsetBCR.top), 1064 bottom: Math.round(offsetBCR.bottom - elemBCR.bottom), 1065 left: Math.round(elemBCR.left - offsetBCR.left), 1066 right: Math.round(offsetBCR.right - elemBCR.right) 1067 }; 1068 }, 1069 1070 /** 1071 * Provides an array of placement values parsed from a placement string. 1072 * Along with the 'auto' indicator, supported placement strings are: 1073 * <ul> 1074 * <li>top: element on top, horizontally centered on host element.</li> 1075 * <li>top-left: element on top, left edge aligned with host element left edge.</li> 1076 * <li>top-right: element on top, lerightft edge aligned with host element right edge.</li> 1077 * <li>bottom: element on bottom, horizontally centered on host element.</li> 1078 * <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li> 1079 * <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li> 1080 * <li>left: element on left, vertically centered on host element.</li> 1081 * <li>left-top: element on left, top edge aligned with host element top edge.</li> 1082 * <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li> 1083 * <li>right: element on right, vertically centered on host element.</li> 1084 * <li>right-top: element on right, top edge aligned with host element top edge.</li> 1085 * <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li> 1086 * </ul> 1087 * A placement string with an 'auto' indicator is expected to be 1088 * space separated from the placement, i.e: 'auto bottom-left' If 1089 * the primary and secondary placement values do not match 'top, 1090 * bottom, left, right' then 'top' will be the primary placement and 1091 * 'center' will be the secondary placement. If 'auto' is passed, true 1092 * will be returned as the 3rd value of the array. 1093 * 1094 * @param {string} placement - The placement string to parse. 1095 * 1096 * @returns {array} An array with the following values 1097 * <ul> 1098 * <li>**[0]**: The primary placement.</li> 1099 * <li>**[1]**: The secondary placement.</li> 1100 * <li>**[2]**: If auto is passed: true, else undefined.</li> 1101 * </ul> 1102 */ 1103 parsePlacement: function(placement) { 1104 var autoPlace = PLACEMENT_REGEX.auto.test(placement); 1105 if (autoPlace) { 1106 placement = placement.replace(PLACEMENT_REGEX.auto, ''); 1107 } 1108 1109 placement = placement.split('-'); 1110 1111 placement[0] = placement[0] || 'top'; 1112 if (!PLACEMENT_REGEX.primary.test(placement[0])) { 1113 placement[0] = 'top'; 1114 } 1115 1116 placement[1] = placement[1] || 'center'; 1117 if (!PLACEMENT_REGEX.secondary.test(placement[1])) { 1118 placement[1] = 'center'; 1119 } 1120 1121 if (autoPlace) { 1122 placement[2] = true; 1123 } else { 1124 placement[2] = false; 1125 } 1126 1127 return placement; 1128 }, 1129 1130 /** 1131 * Provides coordinates for an element to be positioned relative to 1132 * another element. Passing 'auto' as part of the placement parameter 1133 * will enable smart placement - where the element fits. i.e: 1134 * 'auto left-top' will check to see if there is enough space to the left 1135 * of the hostElem to fit the targetElem, if not place right (same for secondary 1136 * top placement). Available space is calculated using the viewportOffset 1137 * function. 1138 * 1139 * @param {element} hostElem - The element to position against. 1140 * @param {element} targetElem - The element to position. 1141 * @param {string=} [placement=top] - The placement for the targetElem, 1142 * default is 'top'. 'center' is assumed as secondary placement for 1143 * 'top', 'left', 'right', and 'bottom' placements. Available placements are: 1144 * <ul> 1145 * <li>top</li> 1146 * <li>top-right</li> 1147 * <li>top-left</li> 1148 * <li>bottom</li> 1149 * <li>bottom-left</li> 1150 * <li>bottom-right</li> 1151 * <li>left</li> 1152 * <li>left-top</li> 1153 * <li>left-bottom</li> 1154 * <li>right</li> 1155 * <li>right-top</li> 1156 * <li>right-bottom</li> 1157 * </ul> 1158 * @param {boolean=} [appendToBody=false] - Should the top and left values returned 1159 * be calculated from the body element, default is false. 1160 * 1161 * @returns {object} An object with the following properties: 1162 * <ul> 1163 * <li>**top**: Value for targetElem top.</li> 1164 * <li>**left**: Value for targetElem left.</li> 1165 * <li>**placement**: The resolved placement.</li> 1166 * </ul> 1167 */ 1168 positionElements: function(hostElem, targetElem, placement, appendToBody) { 1169 hostElem = this.getRawNode(hostElem); 1170 targetElem = this.getRawNode(targetElem); 1171 1172 // need to read from prop to support tests. 1173 var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth'); 1174 var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight'); 1175 1176 placement = this.parsePlacement(placement); 1177 1178 var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem); 1179 var targetElemPos = {top: 0, left: 0, placement: ''}; 1180 1181 if (placement[2]) { 1182 var viewportOffset = this.viewportOffset(hostElem, appendToBody); 1183 1184 var targetElemStyle = $window.getComputedStyle(targetElem); 1185 var adjustedSize = { 1186 width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))), 1187 height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom))) 1188 }; 1189 1190 placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' : 1191 placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' : 1192 placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' : 1193 placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' : 1194 placement[0]; 1195 1196 placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' : 1197 placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' : 1198 placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' : 1199 placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' : 1200 placement[1]; 1201 1202 if (placement[1] === 'center') { 1203 if (PLACEMENT_REGEX.vertical.test(placement[0])) { 1204 var xOverflow = hostElemPos.width / 2 - targetWidth / 2; 1205 if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) { 1206 placement[1] = 'left'; 1207 } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) { 1208 placement[1] = 'right'; 1209 } 1210 } else { 1211 var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2; 1212 if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) { 1213 placement[1] = 'top'; 1214 } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) { 1215 placement[1] = 'bottom'; 1216 } 1217 } 1218 } 1219 } 1220 1221 switch (placement[0]) { 1222 case 'top': 1223 targetElemPos.top = hostElemPos.top - targetHeight; 1224 break; 1225 case 'bottom': 1226 targetElemPos.top = hostElemPos.top + hostElemPos.height; 1227 break; 1228 case 'left': 1229 targetElemPos.left = hostElemPos.left - targetWidth; 1230 break; 1231 case 'right': 1232 targetElemPos.left = hostElemPos.left + hostElemPos.width; 1233 break; 1234 } 1235 1236 switch (placement[1]) { 1237 case 'top': 1238 targetElemPos.top = hostElemPos.top; 1239 break; 1240 case 'bottom': 1241 targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight; 1242 break; 1243 case 'left': 1244 targetElemPos.left = hostElemPos.left; 1245 break; 1246 case 'right': 1247 targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth; 1248 break; 1249 case 'center': 1250 if (PLACEMENT_REGEX.vertical.test(placement[0])) { 1251 targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2; 1252 } else { 1253 targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2; 1254 } 1255 break; 1256 } 1257 1258 targetElemPos.top = Math.round(targetElemPos.top); 1259 targetElemPos.left = Math.round(targetElemPos.left); 1260 targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1]; 1261 1262 return targetElemPos; 1263 }, 1264 1265 /** 1266 * Provides a way to adjust the top positioning after first 1267 * render to correctly align element to top after content 1268 * rendering causes resized element height 1269 * 1270 * @param {array} placementClasses - The array of strings of classes 1271 * element should have. 1272 * @param {object} containerPosition - The object with container 1273 * position information 1274 * @param {number} initialHeight - The initial height for the elem. 1275 * @param {number} currentHeight - The current height for the elem. 1276 */ 1277 adjustTop: function(placementClasses, containerPosition, initialHeight, currentHeight) { 1278 if (placementClasses.indexOf('top') !== -1 && initialHeight !== currentHeight) { 1279 return { 1280 top: containerPosition.top - currentHeight + 'px' 1281 }; 1282 } 1283 }, 1284 1285 /** 1286 * Provides a way for positioning tooltip & dropdown 1287 * arrows when using placement options beyond the standard 1288 * left, right, top, or bottom. 1289 * 1290 * @param {element} elem - The tooltip/dropdown element. 1291 * @param {string} placement - The placement for the elem. 1292 */ 1293 positionArrow: function(elem, placement) { 1294 elem = this.getRawNode(elem); 1295 1296 var innerElem = elem.querySelector('.tooltip-inner, .popover-inner'); 1297 if (!innerElem) { 1298 return; 1299 } 1300 1301 var isTooltip = angular.element(innerElem).hasClass('tooltip-inner'); 1302 1303 var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow'); 1304 if (!arrowElem) { 1305 return; 1306 } 1307 1308 var arrowCss = { 1309 top: '', 1310 bottom: '', 1311 left: '', 1312 right: '' 1313 }; 1314 1315 placement = this.parsePlacement(placement); 1316 if (placement[1] === 'center') { 1317 // no adjustment necessary - just reset styles 1318 angular.element(arrowElem).css(arrowCss); 1319 return; 1320 } 1321 1322 var borderProp = 'border-' + placement[0] + '-width'; 1323 var borderWidth = $window.getComputedStyle(arrowElem)[borderProp]; 1324 1325 var borderRadiusProp = 'border-'; 1326 if (PLACEMENT_REGEX.vertical.test(placement[0])) { 1327 borderRadiusProp += placement[0] + '-' + placement[1]; 1328 } else { 1329 borderRadiusProp += placement[1] + '-' + placement[0]; 1330 } 1331 borderRadiusProp += '-radius'; 1332 var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp]; 1333 1334 switch (placement[0]) { 1335 case 'top': 1336 arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth; 1337 break; 1338 case 'bottom': 1339 arrowCss.top = isTooltip ? '0' : '-' + borderWidth; 1340 break; 1341 case 'left': 1342 arrowCss.right = isTooltip ? '0' : '-' + borderWidth; 1343 break; 1344 case 'right': 1345 arrowCss.left = isTooltip ? '0' : '-' + borderWidth; 1346 break; 1347 } 1348 1349 arrowCss[placement[1]] = borderRadius; 1350 1351 angular.element(arrowElem).css(arrowCss); 1352 } 1353 }; 1354 }]); 1355 1356angular.module('ui.bootstrap.
vendor: 8,994 bytes, lines 1356-1515
1356stackedMap', []) 1357/** 1358 * A helper, internal data structure that acts as a map but also allows getting / removing 1359 * elements in the LIFO order 1360 */ 1361 .factory('$$stackedMap', function() { 1362 return { 1363 createNew: function() { 1364 var stack = []; 1365 1366 return { 1367 add: function(key, value) { 1368 stack.push({ 1369 key: key, 1370 value: value 1371 }); 1372 }, 1373 get: function(key) { 1374 for (var i = 0; i < stack.length; i++) { 1375 if (key === stack[i].key) { 1376 return stack[i]; 1377 } 1378 } 1379 }, 1380 keys: function() { 1381 var keys = []; 1382 for (var i = 0; i < stack.length; i++) { 1383 keys.push(stack[i].key); 1384 } 1385 return keys; 1386 }, 1387 top: function() { 1388 return stack[stack.length - 1]; 1389 }, 1390 remove: function(key) { 1391 var idx = -1; 1392 for (var i = 0; i < stack.length; i++) { 1393 if (key === stack[i].key) { 1394 idx = i; 1395 break; 1396 } 1397 } 1398 return stack.splice(idx, 1)[0]; 1399 }, 1400 removeTop: function() { 1401 return stack.pop(); 1402 }, 1403 length: function() { 1404 return stack.length; 1405 } 1406 }; 1407 } 1408 }; 1409 }); 1410/** 1411 * The following features are still outstanding: popup delay, animation as a 1412 * function, placement as a function, inside, support for more triggers than 1413 * just mouse enter/leave, and selector delegatation. 1414 */ 1415angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip']) 1416 1417.directive('uibPopoverTemplatePopup', function() { 1418 return { 1419 restrict: 'A', 1420 scope: { uibTitle: '@', contentExp: '&', originScope: '&' }, 1421 templateUrl: 'uib/template/popover/popover-template.html' 1422 }; 1423}) 1424 1425.directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) { 1426 return $uibTooltip('uibPopoverTemplate', 'popover', 'click', { 1427 useContentExp: true 1428 }); 1429}]) 1430 1431.directive('uibPopoverHtmlPopup', function() { 1432 return { 1433 restrict: 'A', 1434 scope: { contentExp: '&', uibTitle: '@' }, 1435 templateUrl: 'uib/template/popover/popover-html.html' 1436 }; 1437}) 1438 1439.directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) { 1440 return $uibTooltip('uibPopoverHtml', 'popover', 'click', { 1441 useContentExp: true 1442 }); 1443}]) 1444 1445.directive('uibPopoverPopup', function() { 1446 return { 1447 restrict: 'A', 1448 scope: { uibTitle: '@', content: '@' }, 1449 templateUrl: 'uib/template/popover/popover.html' 1450 }; 1451}) 1452 1453.directive('uibPopover', ['$uibTooltip', function($uibTooltip) { 1454 return $uibTooltip('uibPopover', 'popover', 'click'); 1455}]); 1456 1457angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) { 1458 $templateCache.put("uib/template/tooltip/tooltip-html-popup.html", 1459 "<div class=\"tooltip-arrow\"></div>\n" + 1460 "<div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" + 1461 ""); 1462}]); 1463 1464angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) { 1465 $templateCache.put("uib/template/tooltip/tooltip-popup.html", 1466 "<div class=\"tooltip-arrow\"></div>\n" + 1467 "<div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" + 1468 ""); 1469}]); 1470 1471angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) { 1472 $templateCache.put("uib/template/tooltip/tooltip-template-popup.html", 1473 "<div class=\"tooltip-arrow\"></div>\n" + 1474 "<div class=\"tooltip-inner\"\n" + 1475 " uib-tooltip-template-transclude=\"contentExp()\"\n" + 1476 " tooltip-template-transclude-scope=\"originScope()\"></div>\n" + 1477 ""); 1478}]); 1479 1480angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) { 1481 $templateCache.put("uib/template/popover/popover-html.html", 1482 "<div class=\"arrow\"></div>\n" + 1483 "\n" + 1484 "<div class=\"popover-inner\">\n" + 1485 " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" + 1486 " <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" + 1487 "</div>\n" + 1488 ""); 1489}]); 1490 1491angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) { 1492 $templateCache.put("uib/template/popover/popover-template.html", 1493 "<div class=\"arrow\"></div>\n" + 1494 "\n" + 1495 "<div class=\"popover-inner\">\n" + 1496 " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" + 1497 " <div class=\"popover-content\"\n" + 1498 " uib-tooltip-template-transclude=\"contentExp()\"\n" + 1499 " tooltip-template-transclude-scope=\"originScope()\"></div>\n" + 1500 "</div>\n" + 1501 ""); 1502}]); 1503 1504angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) { 1505 $templateCache.put("uib/template/popover/popover.html", 1506 "<div class=\"arrow\"></div>\n" + 1507 "\n" + 1508 "<div class=\"popover-inner\">\n" + 1509 " <h3 class=\"popover-title\" ng-bind=\"uibTitle\" ng-if=\"uibTitle\"></h3>\n" + 1510 " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" + 1511 "</div>\n" + 1512 ""); 1513}]); 1514angular.module('ui.bootstrap.tooltip').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTooltipCss && angular.element(document).find('head').prepend('<style type="text/css">[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow,[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow,[uib-popover-popup].popover.top-left > .arrow,[uib-popover-popup].popover.top-right > .arrow,[uib-popover-popup].popover.bottom-left > .arrow,[uib-popover-popup].popover.bottom-right > .arrow,[uib-popover-popup].popover.left-top > .arrow,[uib-popover-popup].popover.left-bottom > .arrow,[uib-popover-popup].popover.right-top > .arrow,[uib-popover-popup].popover.right-bottom > .arrow,[uib-popover-html-popup].popover.top-left > .arrow,[uib-popover-html-popup].popover.top-right > .arrow,[uib-popover-html-popup].popover.bottom-left > .arrow,[uib-popover-html-popup].popover.bottom-right > .arrow,[uib-popover-html-popup].popover.left-top > .arrow,[uib-popover-html-popup].popover.left-bottom > .arrow,[uib-popover-html-popup].popover.right-top > .arrow,[uib-popover-html-popup].popover.right-bottom > .arrow,[uib-popover-template-popup].popover.top-left > .arrow,[uib-popover-template-popup].popover.top-right > .arrow,[uib-popover-template-popup].popover.bottom-left > .arrow,[uib-popover-template-popup].popover.bottom-right > .arrow,[uib-popover-template-popup].popover.left-top > .arrow,[uib-popover-template-popup].popover.left-bottom > .arrow,[uib-popover-template-popup].popover.right-top > .arrow,[uib-popover-template-popup].popover.right-bottom > .arrow{top:auto;bottom:auto;left:auto;right:auto;margin:0;}[uib-popover-popup].popover,[uib-popover-html-popup].popover,[uib-popover-template-popup].popover{display:block !important;}</style>'); angular.$$uibTooltipCss = true; }); 1515angular.module('ui.bootstrap.position').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibPositionCss && angular.element(document).find('head').prepend('<style type="text/css">.uib-position-measure{display:block !important;visibility:hidden !important;position:absolute !important;top:-9999px !important;left:-9999px !important;}.uib-position-scrollbar-measure{position:absolute !important;top:-9999px !important;width:50px !important;height:50px !important;overflow:scroll !important;}.uib-position-body-scrollbar-measure{overflow:scroll !important;}</style>'); angular.$$uibPositionCss = true; });
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.