PageSourceSearch

https://xn--80akjfhmgn4l.market/vendors/ui-bootstrap-custom/ui-bootstrap-custom-tpls-2.5.0.js

js xn--80akjfhmgn4l.market collected 2026-10-09 21:28:50 UTC 61,477 bytes, 1,515 lines download raw bytes

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.