GitHub Access Token became invalid

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

Code Duplication    Length = 592-592 lines in 2 locations

third-party/angularjs-modules-plugins/UI-Bootstrap/ui-bootstrap-tpls-1.3.2.js 1 location

@@ 1952-2543 (lines=592) @@
1949
 * relation to another element (this is the case for tooltips, popovers,
1950
 * typeahead suggestions etc.).
1951
 */
1952
  .factory('$uibPosition', ['$document', '$window', function($document, $window) {
1953
    /**
1954
     * Used by scrollbarWidth() function to cache scrollbar's width.
1955
     * Do not access this variable directly, use scrollbarWidth() instead.
1956
     */
1957
    var SCROLLBAR_WIDTH;
1958
    /**
1959
     * scrollbar on body and html element in IE and Edge overlay
1960
     * content and should be considered 0 width.
1961
     */
1962
    var BODY_SCROLLBAR_WIDTH;
1963
    var OVERFLOW_REGEX = {
1964
      normal: /(auto|scroll)/,
1965
      hidden: /(auto|scroll|hidden)/
1966
    };
1967
    var PLACEMENT_REGEX = {
1968
      auto: /\s?auto?\s?/i,
1969
      primary: /^(top|bottom|left|right)$/,
1970
      secondary: /^(top|bottom|left|right|center)$/,
1971
      vertical: /^(top|bottom)$/
1972
    };
1973
    var BODY_REGEX = /(HTML|BODY)/;
1974
1975
    return {
1976
1977
      /**
1978
       * Provides a raw DOM element from a jQuery/jQLite element.
1979
       *
1980
       * @param {element} elem - The element to convert.
1981
       *
1982
       * @returns {element} A HTML element.
1983
       */
1984
      getRawNode: function(elem) {
1985
        return elem.nodeName ? elem : elem[0] || elem;
1986
      },
1987
1988
      /**
1989
       * Provides a parsed number for a style property.  Strips
1990
       * units and casts invalid numbers to 0.
1991
       *
1992
       * @param {string} value - The style value to parse.
1993
       *
1994
       * @returns {number} A valid number.
1995
       */
1996
      parseStyle: function(value) {
1997
        value = parseFloat(value);
1998
        return isFinite(value) ? value : 0;
1999
      },
2000
2001
      /**
2002
       * Provides the closest positioned ancestor.
2003
       *
2004
       * @param {element} element - The element to get the offest parent for.
2005
       *
2006
       * @returns {element} The closest positioned ancestor.
2007
       */
2008
      offsetParent: function(elem) {
2009
        elem = this.getRawNode(elem);
2010
2011
        var offsetParent = elem.offsetParent || $document[0].documentElement;
2012
2013
        function isStaticPositioned(el) {
2014
          return ($window.getComputedStyle(el).position || 'static') === 'static';
2015
        }
2016
2017
        while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
2018
          offsetParent = offsetParent.offsetParent;
2019
        }
2020
2021
        return offsetParent || $document[0].documentElement;
2022
      },
2023
2024
      /**
2025
       * Provides the scrollbar width, concept from TWBS measureScrollbar()
2026
       * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
2027
       * In IE and Edge, scollbar on body and html element overlay and should
2028
       * return a width of 0.
2029
       *
2030
       * @returns {number} The width of the browser scollbar.
2031
       */
2032
      scrollbarWidth: function(isBody) {
2033
        if (isBody) {
2034
          if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) {
2035
            var bodyElem = $document.find('body');
2036
            bodyElem.addClass('uib-position-body-scrollbar-measure');
2037
            BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth;
2038
            BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0;
2039
            bodyElem.removeClass('uib-position-body-scrollbar-measure');
2040
          }
2041
          return BODY_SCROLLBAR_WIDTH;
2042
        }
2043
2044
        if (angular.isUndefined(SCROLLBAR_WIDTH)) {
2045
          var scrollElem = angular.element('<div class="uib-position-scrollbar-measure"></div>');
2046
          $document.find('body').append(scrollElem);
2047
          SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
2048
          SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
2049
          scrollElem.remove();
2050
        }
2051
2052
        return SCROLLBAR_WIDTH;
2053
      },
2054
2055
      /**
2056
       * Provides the padding required on an element to replace the scrollbar.
2057
       *
2058
       * @returns {object} An object with the following properties:
2059
       *   <ul>
2060
       *     <li>**scrollbarWidth**: the width of the scrollbar</li>
2061
       *     <li>**widthOverflow**: whether the the width is overflowing</li>
2062
       *     <li>**right**: the amount of right padding on the element needed to replace the scrollbar</li>
2063
       *     <li>**rightOriginal**: the amount of right padding currently on the element</li>
2064
       *     <li>**heightOverflow**: whether the the height is overflowing</li>
2065
       *     <li>**bottom**: the amount of bottom padding on the element needed to replace the scrollbar</li>
2066
       *     <li>**bottomOriginal**: the amount of bottom padding currently on the element</li>
2067
       *   </ul>
2068
       */
2069
      scrollbarPadding: function(elem) {
2070
        elem = this.getRawNode(elem);
2071
2072
        var elemStyle = $window.getComputedStyle(elem);
2073
        var paddingRight = this.parseStyle(elemStyle.paddingRight);
2074
        var paddingBottom = this.parseStyle(elemStyle.paddingBottom);
2075
        var scrollParent = this.scrollParent(elem, false, true);
2076
        var scrollbarWidth = this.scrollbarWidth(scrollParent, BODY_REGEX.test(scrollParent.tagName));
2077
2078
        return {
2079
          scrollbarWidth: scrollbarWidth,
2080
          widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth,
2081
          right: paddingRight + scrollbarWidth,
2082
          originalRight: paddingRight,
2083
          heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight,
2084
          bottom: paddingBottom + scrollbarWidth,
2085
          originalBottom: paddingBottom
2086
         };
2087
      },
2088
2089
      /**
2090
       * Checks to see if the element is scrollable.
2091
       *
2092
       * @param {element} elem - The element to check.
2093
       * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
2094
       *   default is false.
2095
       *
2096
       * @returns {boolean} Whether the element is scrollable.
2097
       */
2098
      isScrollable: function(elem, includeHidden) {
2099
        elem = this.getRawNode(elem);
2100
2101
        var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
2102
        var elemStyle = $window.getComputedStyle(elem);
2103
        return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX);
2104
      },
2105
2106
      /**
2107
       * Provides the closest scrollable ancestor.
2108
       * A port of the jQuery UI scrollParent method:
2109
       * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
2110
       *
2111
       * @param {element} elem - The element to find the scroll parent of.
2112
       * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
2113
       *   default is false.
2114
       * @param {boolean=} [includeSelf=false] - Should the element being passed be
2115
       * included in the scrollable llokup.
2116
       *
2117
       * @returns {element} A HTML element.
2118
       */
2119
      scrollParent: function(elem, includeHidden, includeSelf) {
2120
        elem = this.getRawNode(elem);
2121
2122
        var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
2123
        var documentEl = $document[0].documentElement;
2124
        var elemStyle = $window.getComputedStyle(elem);
2125
        if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) {
2126
          return elem;
2127
        }
2128
        var excludeStatic = elemStyle.position === 'absolute';
2129
        var scrollParent = elem.parentElement || documentEl;
2130
2131
        if (scrollParent === documentEl || elemStyle.position === 'fixed') {
2132
          return documentEl;
2133
        }
2134
2135
        while (scrollParent.parentElement && scrollParent !== documentEl) {
2136
          var spStyle = $window.getComputedStyle(scrollParent);
2137
          if (excludeStatic && spStyle.position !== 'static') {
2138
            excludeStatic = false;
2139
          }
2140
2141
          if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
2142
            break;
2143
          }
2144
          scrollParent = scrollParent.parentElement;
2145
        }
2146
2147
        return scrollParent;
2148
      },
2149
2150
      /**
2151
       * Provides read-only equivalent of jQuery's position function:
2152
       * http://api.jquery.com/position/ - distance to closest positioned
2153
       * ancestor.  Does not account for margins by default like jQuery position.
2154
       *
2155
       * @param {element} elem - The element to caclulate the position on.
2156
       * @param {boolean=} [includeMargins=false] - Should margins be accounted
2157
       * for, default is false.
2158
       *
2159
       * @returns {object} An object with the following properties:
2160
       *   <ul>
2161
       *     <li>**width**: the width of the element</li>
2162
       *     <li>**height**: the height of the element</li>
2163
       *     <li>**top**: distance to top edge of offset parent</li>
2164
       *     <li>**left**: distance to left edge of offset parent</li>
2165
       *   </ul>
2166
       */
2167
      position: function(elem, includeMagins) {
2168
        elem = this.getRawNode(elem);
2169
2170
        var elemOffset = this.offset(elem);
2171
        if (includeMagins) {
2172
          var elemStyle = $window.getComputedStyle(elem);
2173
          elemOffset.top -= this.parseStyle(elemStyle.marginTop);
2174
          elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
2175
        }
2176
        var parent = this.offsetParent(elem);
2177
        var parentOffset = {top: 0, left: 0};
2178
2179
        if (parent !== $document[0].documentElement) {
2180
          parentOffset = this.offset(parent);
2181
          parentOffset.top += parent.clientTop - parent.scrollTop;
2182
          parentOffset.left += parent.clientLeft - parent.scrollLeft;
2183
        }
2184
2185
        return {
2186
          width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
2187
          height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
2188
          top: Math.round(elemOffset.top - parentOffset.top),
2189
          left: Math.round(elemOffset.left - parentOffset.left)
2190
        };
2191
      },
2192
2193
      /**
2194
       * Provides read-only equivalent of jQuery's offset function:
2195
       * http://api.jquery.com/offset/ - distance to viewport.  Does
2196
       * not account for borders, margins, or padding on the body
2197
       * element.
2198
       *
2199
       * @param {element} elem - The element to calculate the offset on.
2200
       *
2201
       * @returns {object} An object with the following properties:
2202
       *   <ul>
2203
       *     <li>**width**: the width of the element</li>
2204
       *     <li>**height**: the height of the element</li>
2205
       *     <li>**top**: distance to top edge of viewport</li>
2206
       *     <li>**right**: distance to bottom edge of viewport</li>
2207
       *   </ul>
2208
       */
2209
      offset: function(elem) {
2210
        elem = this.getRawNode(elem);
2211
2212
        var elemBCR = elem.getBoundingClientRect();
2213
        return {
2214
          width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
2215
          height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
2216
          top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
2217
          left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
2218
        };
2219
      },
2220
2221
      /**
2222
       * Provides offset distance to the closest scrollable ancestor
2223
       * or viewport.  Accounts for border and scrollbar width.
2224
       *
2225
       * Right and bottom dimensions represent the distance to the
2226
       * respective edge of the viewport element.  If the element
2227
       * edge extends beyond the viewport, a negative value will be
2228
       * reported.
2229
       *
2230
       * @param {element} elem - The element to get the viewport offset for.
2231
       * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
2232
       * of the first scrollable element, default is false.
2233
       * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
2234
       * be accounted for, default is true.
2235
       *
2236
       * @returns {object} An object with the following properties:
2237
       *   <ul>
2238
       *     <li>**top**: distance to the top content edge of viewport element</li>
2239
       *     <li>**bottom**: distance to the bottom content edge of viewport element</li>
2240
       *     <li>**left**: distance to the left content edge of viewport element</li>
2241
       *     <li>**right**: distance to the right content edge of viewport element</li>
2242
       *   </ul>
2243
       */
2244
      viewportOffset: function(elem, useDocument, includePadding) {
2245
        elem = this.getRawNode(elem);
2246
        includePadding = includePadding !== false ? true : false;
2247
2248
        var elemBCR = elem.getBoundingClientRect();
2249
        var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
2250
2251
        var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
2252
        var offsetParentBCR = offsetParent.getBoundingClientRect();
2253
2254
        offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
2255
        offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
2256
        if (offsetParent === $document[0].documentElement) {
2257
          offsetBCR.top += $window.pageYOffset;
2258
          offsetBCR.left += $window.pageXOffset;
2259
        }
2260
        offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
2261
        offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
2262
2263
        if (includePadding) {
2264
          var offsetParentStyle = $window.getComputedStyle(offsetParent);
2265
          offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
2266
          offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
2267
          offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
2268
          offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
2269
        }
2270
2271
        return {
2272
          top: Math.round(elemBCR.top - offsetBCR.top),
2273
          bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
2274
          left: Math.round(elemBCR.left - offsetBCR.left),
2275
          right: Math.round(offsetBCR.right - elemBCR.right)
2276
        };
2277
      },
2278
2279
      /**
2280
       * Provides an array of placement values parsed from a placement string.
2281
       * Along with the 'auto' indicator, supported placement strings are:
2282
       *   <ul>
2283
       *     <li>top: element on top, horizontally centered on host element.</li>
2284
       *     <li>top-left: element on top, left edge aligned with host element left edge.</li>
2285
       *     <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
2286
       *     <li>bottom: element on bottom, horizontally centered on host element.</li>
2287
       *     <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
2288
       *     <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
2289
       *     <li>left: element on left, vertically centered on host element.</li>
2290
       *     <li>left-top: element on left, top edge aligned with host element top edge.</li>
2291
       *     <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
2292
       *     <li>right: element on right, vertically centered on host element.</li>
2293
       *     <li>right-top: element on right, top edge aligned with host element top edge.</li>
2294
       *     <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
2295
       *   </ul>
2296
       * A placement string with an 'auto' indicator is expected to be
2297
       * space separated from the placement, i.e: 'auto bottom-left'  If
2298
       * the primary and secondary placement values do not match 'top,
2299
       * bottom, left, right' then 'top' will be the primary placement and
2300
       * 'center' will be the secondary placement.  If 'auto' is passed, true
2301
       * will be returned as the 3rd value of the array.
2302
       *
2303
       * @param {string} placement - The placement string to parse.
2304
       *
2305
       * @returns {array} An array with the following values
2306
       * <ul>
2307
       *   <li>**[0]**: The primary placement.</li>
2308
       *   <li>**[1]**: The secondary placement.</li>
2309
       *   <li>**[2]**: If auto is passed: true, else undefined.</li>
2310
       * </ul>
2311
       */
2312
      parsePlacement: function(placement) {
2313
        var autoPlace = PLACEMENT_REGEX.auto.test(placement);
2314
        if (autoPlace) {
2315
          placement = placement.replace(PLACEMENT_REGEX.auto, '');
2316
        }
2317
2318
        placement = placement.split('-');
2319
2320
        placement[0] = placement[0] || 'top';
2321
        if (!PLACEMENT_REGEX.primary.test(placement[0])) {
2322
          placement[0] = 'top';
2323
        }
2324
2325
        placement[1] = placement[1] || 'center';
2326
        if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
2327
          placement[1] = 'center';
2328
        }
2329
2330
        if (autoPlace) {
2331
          placement[2] = true;
2332
        } else {
2333
          placement[2] = false;
2334
        }
2335
2336
        return placement;
2337
      },
2338
2339
      /**
2340
       * Provides coordinates for an element to be positioned relative to
2341
       * another element.  Passing 'auto' as part of the placement parameter
2342
       * will enable smart placement - where the element fits. i.e:
2343
       * 'auto left-top' will check to see if there is enough space to the left
2344
       * of the hostElem to fit the targetElem, if not place right (same for secondary
2345
       * top placement).  Available space is calculated using the viewportOffset
2346
       * function.
2347
       *
2348
       * @param {element} hostElem - The element to position against.
2349
       * @param {element} targetElem - The element to position.
2350
       * @param {string=} [placement=top] - The placement for the targetElem,
2351
       *   default is 'top'. 'center' is assumed as secondary placement for
2352
       *   'top', 'left', 'right', and 'bottom' placements.  Available placements are:
2353
       *   <ul>
2354
       *     <li>top</li>
2355
       *     <li>top-right</li>
2356
       *     <li>top-left</li>
2357
       *     <li>bottom</li>
2358
       *     <li>bottom-left</li>
2359
       *     <li>bottom-right</li>
2360
       *     <li>left</li>
2361
       *     <li>left-top</li>
2362
       *     <li>left-bottom</li>
2363
       *     <li>right</li>
2364
       *     <li>right-top</li>
2365
       *     <li>right-bottom</li>
2366
       *   </ul>
2367
       * @param {boolean=} [appendToBody=false] - Should the top and left values returned
2368
       *   be calculated from the body element, default is false.
2369
       *
2370
       * @returns {object} An object with the following properties:
2371
       *   <ul>
2372
       *     <li>**top**: Value for targetElem top.</li>
2373
       *     <li>**left**: Value for targetElem left.</li>
2374
       *     <li>**placement**: The resolved placement.</li>
2375
       *   </ul>
2376
       */
2377
      positionElements: function(hostElem, targetElem, placement, appendToBody) {
2378
        hostElem = this.getRawNode(hostElem);
2379
        targetElem = this.getRawNode(targetElem);
2380
2381
        // need to read from prop to support tests.
2382
        var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
2383
        var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
2384
2385
        placement = this.parsePlacement(placement);
2386
2387
        var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
2388
        var targetElemPos = {top: 0, left: 0, placement: ''};
2389
2390
        if (placement[2]) {
2391
          var viewportOffset = this.viewportOffset(hostElem, appendToBody);
2392
2393
          var targetElemStyle = $window.getComputedStyle(targetElem);
2394
          var adjustedSize = {
2395
            width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
2396
            height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
2397
          };
2398
2399
          placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
2400
                         placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
2401
                         placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
2402
                         placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
2403
                         placement[0];
2404
2405
          placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
2406
                         placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
2407
                         placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
2408
                         placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
2409
                         placement[1];
2410
2411
          if (placement[1] === 'center') {
2412
            if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2413
              var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
2414
              if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
2415
                placement[1] = 'left';
2416
              } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
2417
                placement[1] = 'right';
2418
              }
2419
            } else {
2420
              var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
2421
              if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
2422
                placement[1] = 'top';
2423
              } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
2424
                placement[1] = 'bottom';
2425
              }
2426
            }
2427
          }
2428
        }
2429
2430
        switch (placement[0]) {
2431
          case 'top':
2432
            targetElemPos.top = hostElemPos.top - targetHeight;
2433
            break;
2434
          case 'bottom':
2435
            targetElemPos.top = hostElemPos.top + hostElemPos.height;
2436
            break;
2437
          case 'left':
2438
            targetElemPos.left = hostElemPos.left - targetWidth;
2439
            break;
2440
          case 'right':
2441
            targetElemPos.left = hostElemPos.left + hostElemPos.width;
2442
            break;
2443
        }
2444
2445
        switch (placement[1]) {
2446
          case 'top':
2447
            targetElemPos.top = hostElemPos.top;
2448
            break;
2449
          case 'bottom':
2450
            targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
2451
            break;
2452
          case 'left':
2453
            targetElemPos.left = hostElemPos.left;
2454
            break;
2455
          case 'right':
2456
            targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
2457
            break;
2458
          case 'center':
2459
            if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2460
              targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
2461
            } else {
2462
              targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
2463
            }
2464
            break;
2465
        }
2466
2467
        targetElemPos.top = Math.round(targetElemPos.top);
2468
        targetElemPos.left = Math.round(targetElemPos.left);
2469
        targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
2470
2471
        return targetElemPos;
2472
      },
2473
2474
      /**
2475
      * Provides a way for positioning tooltip & dropdown
2476
      * arrows when using placement options beyond the standard
2477
      * left, right, top, or bottom.
2478
      *
2479
      * @param {element} elem - The tooltip/dropdown element.
2480
      * @param {string} placement - The placement for the elem.
2481
      */
2482
      positionArrow: function(elem, placement) {
2483
        elem = this.getRawNode(elem);
2484
2485
        var innerElem = elem.querySelector('.tooltip-inner, .popover-inner');
2486
        if (!innerElem) {
2487
          return;
2488
        }
2489
2490
        var isTooltip = angular.element(innerElem).hasClass('tooltip-inner');
2491
2492
        var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
2493
        if (!arrowElem) {
2494
          return;
2495
        }
2496
2497
        var arrowCss = {
2498
          top: '',
2499
          bottom: '',
2500
          left: '',
2501
          right: ''
2502
        };
2503
2504
        placement = this.parsePlacement(placement);
2505
        if (placement[1] === 'center') {
2506
          // no adjustment necessary - just reset styles
2507
          angular.element(arrowElem).css(arrowCss);
2508
          return;
2509
        }
2510
2511
        var borderProp = 'border-' + placement[0] + '-width';
2512
        var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
2513
2514
        var borderRadiusProp = 'border-';
2515
        if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2516
          borderRadiusProp += placement[0] + '-' + placement[1];
2517
        } else {
2518
          borderRadiusProp += placement[1] + '-' + placement[0];
2519
        }
2520
        borderRadiusProp += '-radius';
2521
        var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
2522
2523
        switch (placement[0]) {
2524
          case 'top':
2525
            arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
2526
            break;
2527
          case 'bottom':
2528
            arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
2529
            break;
2530
          case 'left':
2531
            arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
2532
            break;
2533
          case 'right':
2534
            arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
2535
            break;
2536
        }
2537
2538
        arrowCss[placement[1]] = borderRadius;
2539
2540
        angular.element(arrowElem).css(arrowCss);
2541
      }
2542
    };
2543
  }]);
2544
2545
angular.module('ui.bootstrap.datepickerPopup', ['ui.bootstrap.datepicker', 'ui.bootstrap.position'])
2546

third-party/angularjs-modules-plugins/UI-Bootstrap/ui-bootstrap-1.3.2.js 1 location

@@ 1951-2542 (lines=592) @@
1948
 * relation to another element (this is the case for tooltips, popovers,
1949
 * typeahead suggestions etc.).
1950
 */
1951
  .factory('$uibPosition', ['$document', '$window', function($document, $window) {
1952
    /**
1953
     * Used by scrollbarWidth() function to cache scrollbar's width.
1954
     * Do not access this variable directly, use scrollbarWidth() instead.
1955
     */
1956
    var SCROLLBAR_WIDTH;
1957
    /**
1958
     * scrollbar on body and html element in IE and Edge overlay
1959
     * content and should be considered 0 width.
1960
     */
1961
    var BODY_SCROLLBAR_WIDTH;
1962
    var OVERFLOW_REGEX = {
1963
      normal: /(auto|scroll)/,
1964
      hidden: /(auto|scroll|hidden)/
1965
    };
1966
    var PLACEMENT_REGEX = {
1967
      auto: /\s?auto?\s?/i,
1968
      primary: /^(top|bottom|left|right)$/,
1969
      secondary: /^(top|bottom|left|right|center)$/,
1970
      vertical: /^(top|bottom)$/
1971
    };
1972
    var BODY_REGEX = /(HTML|BODY)/;
1973
1974
    return {
1975
1976
      /**
1977
       * Provides a raw DOM element from a jQuery/jQLite element.
1978
       *
1979
       * @param {element} elem - The element to convert.
1980
       *
1981
       * @returns {element} A HTML element.
1982
       */
1983
      getRawNode: function(elem) {
1984
        return elem.nodeName ? elem : elem[0] || elem;
1985
      },
1986
1987
      /**
1988
       * Provides a parsed number for a style property.  Strips
1989
       * units and casts invalid numbers to 0.
1990
       *
1991
       * @param {string} value - The style value to parse.
1992
       *
1993
       * @returns {number} A valid number.
1994
       */
1995
      parseStyle: function(value) {
1996
        value = parseFloat(value);
1997
        return isFinite(value) ? value : 0;
1998
      },
1999
2000
      /**
2001
       * Provides the closest positioned ancestor.
2002
       *
2003
       * @param {element} element - The element to get the offest parent for.
2004
       *
2005
       * @returns {element} The closest positioned ancestor.
2006
       */
2007
      offsetParent: function(elem) {
2008
        elem = this.getRawNode(elem);
2009
2010
        var offsetParent = elem.offsetParent || $document[0].documentElement;
2011
2012
        function isStaticPositioned(el) {
2013
          return ($window.getComputedStyle(el).position || 'static') === 'static';
2014
        }
2015
2016
        while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
2017
          offsetParent = offsetParent.offsetParent;
2018
        }
2019
2020
        return offsetParent || $document[0].documentElement;
2021
      },
2022
2023
      /**
2024
       * Provides the scrollbar width, concept from TWBS measureScrollbar()
2025
       * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
2026
       * In IE and Edge, scollbar on body and html element overlay and should
2027
       * return a width of 0.
2028
       *
2029
       * @returns {number} The width of the browser scollbar.
2030
       */
2031
      scrollbarWidth: function(isBody) {
2032
        if (isBody) {
2033
          if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) {
2034
            var bodyElem = $document.find('body');
2035
            bodyElem.addClass('uib-position-body-scrollbar-measure');
2036
            BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth;
2037
            BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0;
2038
            bodyElem.removeClass('uib-position-body-scrollbar-measure');
2039
          }
2040
          return BODY_SCROLLBAR_WIDTH;
2041
        }
2042
2043
        if (angular.isUndefined(SCROLLBAR_WIDTH)) {
2044
          var scrollElem = angular.element('<div class="uib-position-scrollbar-measure"></div>');
2045
          $document.find('body').append(scrollElem);
2046
          SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
2047
          SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
2048
          scrollElem.remove();
2049
        }
2050
2051
        return SCROLLBAR_WIDTH;
2052
      },
2053
2054
      /**
2055
       * Provides the padding required on an element to replace the scrollbar.
2056
       *
2057
       * @returns {object} An object with the following properties:
2058
       *   <ul>
2059
       *     <li>**scrollbarWidth**: the width of the scrollbar</li>
2060
       *     <li>**widthOverflow**: whether the the width is overflowing</li>
2061
       *     <li>**right**: the amount of right padding on the element needed to replace the scrollbar</li>
2062
       *     <li>**rightOriginal**: the amount of right padding currently on the element</li>
2063
       *     <li>**heightOverflow**: whether the the height is overflowing</li>
2064
       *     <li>**bottom**: the amount of bottom padding on the element needed to replace the scrollbar</li>
2065
       *     <li>**bottomOriginal**: the amount of bottom padding currently on the element</li>
2066
       *   </ul>
2067
       */
2068
      scrollbarPadding: function(elem) {
2069
        elem = this.getRawNode(elem);
2070
2071
        var elemStyle = $window.getComputedStyle(elem);
2072
        var paddingRight = this.parseStyle(elemStyle.paddingRight);
2073
        var paddingBottom = this.parseStyle(elemStyle.paddingBottom);
2074
        var scrollParent = this.scrollParent(elem, false, true);
2075
        var scrollbarWidth = this.scrollbarWidth(scrollParent, BODY_REGEX.test(scrollParent.tagName));
2076
2077
        return {
2078
          scrollbarWidth: scrollbarWidth,
2079
          widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth,
2080
          right: paddingRight + scrollbarWidth,
2081
          originalRight: paddingRight,
2082
          heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight,
2083
          bottom: paddingBottom + scrollbarWidth,
2084
          originalBottom: paddingBottom
2085
         };
2086
      },
2087
2088
      /**
2089
       * Checks to see if the element is scrollable.
2090
       *
2091
       * @param {element} elem - The element to check.
2092
       * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
2093
       *   default is false.
2094
       *
2095
       * @returns {boolean} Whether the element is scrollable.
2096
       */
2097
      isScrollable: function(elem, includeHidden) {
2098
        elem = this.getRawNode(elem);
2099
2100
        var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
2101
        var elemStyle = $window.getComputedStyle(elem);
2102
        return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX);
2103
      },
2104
2105
      /**
2106
       * Provides the closest scrollable ancestor.
2107
       * A port of the jQuery UI scrollParent method:
2108
       * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
2109
       *
2110
       * @param {element} elem - The element to find the scroll parent of.
2111
       * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
2112
       *   default is false.
2113
       * @param {boolean=} [includeSelf=false] - Should the element being passed be
2114
       * included in the scrollable llokup.
2115
       *
2116
       * @returns {element} A HTML element.
2117
       */
2118
      scrollParent: function(elem, includeHidden, includeSelf) {
2119
        elem = this.getRawNode(elem);
2120
2121
        var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
2122
        var documentEl = $document[0].documentElement;
2123
        var elemStyle = $window.getComputedStyle(elem);
2124
        if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) {
2125
          return elem;
2126
        }
2127
        var excludeStatic = elemStyle.position === 'absolute';
2128
        var scrollParent = elem.parentElement || documentEl;
2129
2130
        if (scrollParent === documentEl || elemStyle.position === 'fixed') {
2131
          return documentEl;
2132
        }
2133
2134
        while (scrollParent.parentElement && scrollParent !== documentEl) {
2135
          var spStyle = $window.getComputedStyle(scrollParent);
2136
          if (excludeStatic && spStyle.position !== 'static') {
2137
            excludeStatic = false;
2138
          }
2139
2140
          if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
2141
            break;
2142
          }
2143
          scrollParent = scrollParent.parentElement;
2144
        }
2145
2146
        return scrollParent;
2147
      },
2148
2149
      /**
2150
       * Provides read-only equivalent of jQuery's position function:
2151
       * http://api.jquery.com/position/ - distance to closest positioned
2152
       * ancestor.  Does not account for margins by default like jQuery position.
2153
       *
2154
       * @param {element} elem - The element to caclulate the position on.
2155
       * @param {boolean=} [includeMargins=false] - Should margins be accounted
2156
       * for, default is false.
2157
       *
2158
       * @returns {object} An object with the following properties:
2159
       *   <ul>
2160
       *     <li>**width**: the width of the element</li>
2161
       *     <li>**height**: the height of the element</li>
2162
       *     <li>**top**: distance to top edge of offset parent</li>
2163
       *     <li>**left**: distance to left edge of offset parent</li>
2164
       *   </ul>
2165
       */
2166
      position: function(elem, includeMagins) {
2167
        elem = this.getRawNode(elem);
2168
2169
        var elemOffset = this.offset(elem);
2170
        if (includeMagins) {
2171
          var elemStyle = $window.getComputedStyle(elem);
2172
          elemOffset.top -= this.parseStyle(elemStyle.marginTop);
2173
          elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
2174
        }
2175
        var parent = this.offsetParent(elem);
2176
        var parentOffset = {top: 0, left: 0};
2177
2178
        if (parent !== $document[0].documentElement) {
2179
          parentOffset = this.offset(parent);
2180
          parentOffset.top += parent.clientTop - parent.scrollTop;
2181
          parentOffset.left += parent.clientLeft - parent.scrollLeft;
2182
        }
2183
2184
        return {
2185
          width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
2186
          height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
2187
          top: Math.round(elemOffset.top - parentOffset.top),
2188
          left: Math.round(elemOffset.left - parentOffset.left)
2189
        };
2190
      },
2191
2192
      /**
2193
       * Provides read-only equivalent of jQuery's offset function:
2194
       * http://api.jquery.com/offset/ - distance to viewport.  Does
2195
       * not account for borders, margins, or padding on the body
2196
       * element.
2197
       *
2198
       * @param {element} elem - The element to calculate the offset on.
2199
       *
2200
       * @returns {object} An object with the following properties:
2201
       *   <ul>
2202
       *     <li>**width**: the width of the element</li>
2203
       *     <li>**height**: the height of the element</li>
2204
       *     <li>**top**: distance to top edge of viewport</li>
2205
       *     <li>**right**: distance to bottom edge of viewport</li>
2206
       *   </ul>
2207
       */
2208
      offset: function(elem) {
2209
        elem = this.getRawNode(elem);
2210
2211
        var elemBCR = elem.getBoundingClientRect();
2212
        return {
2213
          width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
2214
          height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
2215
          top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
2216
          left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
2217
        };
2218
      },
2219
2220
      /**
2221
       * Provides offset distance to the closest scrollable ancestor
2222
       * or viewport.  Accounts for border and scrollbar width.
2223
       *
2224
       * Right and bottom dimensions represent the distance to the
2225
       * respective edge of the viewport element.  If the element
2226
       * edge extends beyond the viewport, a negative value will be
2227
       * reported.
2228
       *
2229
       * @param {element} elem - The element to get the viewport offset for.
2230
       * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
2231
       * of the first scrollable element, default is false.
2232
       * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
2233
       * be accounted for, default is true.
2234
       *
2235
       * @returns {object} An object with the following properties:
2236
       *   <ul>
2237
       *     <li>**top**: distance to the top content edge of viewport element</li>
2238
       *     <li>**bottom**: distance to the bottom content edge of viewport element</li>
2239
       *     <li>**left**: distance to the left content edge of viewport element</li>
2240
       *     <li>**right**: distance to the right content edge of viewport element</li>
2241
       *   </ul>
2242
       */
2243
      viewportOffset: function(elem, useDocument, includePadding) {
2244
        elem = this.getRawNode(elem);
2245
        includePadding = includePadding !== false ? true : false;
2246
2247
        var elemBCR = elem.getBoundingClientRect();
2248
        var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
2249
2250
        var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
2251
        var offsetParentBCR = offsetParent.getBoundingClientRect();
2252
2253
        offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
2254
        offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
2255
        if (offsetParent === $document[0].documentElement) {
2256
          offsetBCR.top += $window.pageYOffset;
2257
          offsetBCR.left += $window.pageXOffset;
2258
        }
2259
        offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
2260
        offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
2261
2262
        if (includePadding) {
2263
          var offsetParentStyle = $window.getComputedStyle(offsetParent);
2264
          offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
2265
          offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
2266
          offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
2267
          offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
2268
        }
2269
2270
        return {
2271
          top: Math.round(elemBCR.top - offsetBCR.top),
2272
          bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
2273
          left: Math.round(elemBCR.left - offsetBCR.left),
2274
          right: Math.round(offsetBCR.right - elemBCR.right)
2275
        };
2276
      },
2277
2278
      /**
2279
       * Provides an array of placement values parsed from a placement string.
2280
       * Along with the 'auto' indicator, supported placement strings are:
2281
       *   <ul>
2282
       *     <li>top: element on top, horizontally centered on host element.</li>
2283
       *     <li>top-left: element on top, left edge aligned with host element left edge.</li>
2284
       *     <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
2285
       *     <li>bottom: element on bottom, horizontally centered on host element.</li>
2286
       *     <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
2287
       *     <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
2288
       *     <li>left: element on left, vertically centered on host element.</li>
2289
       *     <li>left-top: element on left, top edge aligned with host element top edge.</li>
2290
       *     <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
2291
       *     <li>right: element on right, vertically centered on host element.</li>
2292
       *     <li>right-top: element on right, top edge aligned with host element top edge.</li>
2293
       *     <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
2294
       *   </ul>
2295
       * A placement string with an 'auto' indicator is expected to be
2296
       * space separated from the placement, i.e: 'auto bottom-left'  If
2297
       * the primary and secondary placement values do not match 'top,
2298
       * bottom, left, right' then 'top' will be the primary placement and
2299
       * 'center' will be the secondary placement.  If 'auto' is passed, true
2300
       * will be returned as the 3rd value of the array.
2301
       *
2302
       * @param {string} placement - The placement string to parse.
2303
       *
2304
       * @returns {array} An array with the following values
2305
       * <ul>
2306
       *   <li>**[0]**: The primary placement.</li>
2307
       *   <li>**[1]**: The secondary placement.</li>
2308
       *   <li>**[2]**: If auto is passed: true, else undefined.</li>
2309
       * </ul>
2310
       */
2311
      parsePlacement: function(placement) {
2312
        var autoPlace = PLACEMENT_REGEX.auto.test(placement);
2313
        if (autoPlace) {
2314
          placement = placement.replace(PLACEMENT_REGEX.auto, '');
2315
        }
2316
2317
        placement = placement.split('-');
2318
2319
        placement[0] = placement[0] || 'top';
2320
        if (!PLACEMENT_REGEX.primary.test(placement[0])) {
2321
          placement[0] = 'top';
2322
        }
2323
2324
        placement[1] = placement[1] || 'center';
2325
        if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
2326
          placement[1] = 'center';
2327
        }
2328
2329
        if (autoPlace) {
2330
          placement[2] = true;
2331
        } else {
2332
          placement[2] = false;
2333
        }
2334
2335
        return placement;
2336
      },
2337
2338
      /**
2339
       * Provides coordinates for an element to be positioned relative to
2340
       * another element.  Passing 'auto' as part of the placement parameter
2341
       * will enable smart placement - where the element fits. i.e:
2342
       * 'auto left-top' will check to see if there is enough space to the left
2343
       * of the hostElem to fit the targetElem, if not place right (same for secondary
2344
       * top placement).  Available space is calculated using the viewportOffset
2345
       * function.
2346
       *
2347
       * @param {element} hostElem - The element to position against.
2348
       * @param {element} targetElem - The element to position.
2349
       * @param {string=} [placement=top] - The placement for the targetElem,
2350
       *   default is 'top'. 'center' is assumed as secondary placement for
2351
       *   'top', 'left', 'right', and 'bottom' placements.  Available placements are:
2352
       *   <ul>
2353
       *     <li>top</li>
2354
       *     <li>top-right</li>
2355
       *     <li>top-left</li>
2356
       *     <li>bottom</li>
2357
       *     <li>bottom-left</li>
2358
       *     <li>bottom-right</li>
2359
       *     <li>left</li>
2360
       *     <li>left-top</li>
2361
       *     <li>left-bottom</li>
2362
       *     <li>right</li>
2363
       *     <li>right-top</li>
2364
       *     <li>right-bottom</li>
2365
       *   </ul>
2366
       * @param {boolean=} [appendToBody=false] - Should the top and left values returned
2367
       *   be calculated from the body element, default is false.
2368
       *
2369
       * @returns {object} An object with the following properties:
2370
       *   <ul>
2371
       *     <li>**top**: Value for targetElem top.</li>
2372
       *     <li>**left**: Value for targetElem left.</li>
2373
       *     <li>**placement**: The resolved placement.</li>
2374
       *   </ul>
2375
       */
2376
      positionElements: function(hostElem, targetElem, placement, appendToBody) {
2377
        hostElem = this.getRawNode(hostElem);
2378
        targetElem = this.getRawNode(targetElem);
2379
2380
        // need to read from prop to support tests.
2381
        var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
2382
        var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
2383
2384
        placement = this.parsePlacement(placement);
2385
2386
        var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
2387
        var targetElemPos = {top: 0, left: 0, placement: ''};
2388
2389
        if (placement[2]) {
2390
          var viewportOffset = this.viewportOffset(hostElem, appendToBody);
2391
2392
          var targetElemStyle = $window.getComputedStyle(targetElem);
2393
          var adjustedSize = {
2394
            width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
2395
            height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
2396
          };
2397
2398
          placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
2399
                         placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
2400
                         placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
2401
                         placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
2402
                         placement[0];
2403
2404
          placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
2405
                         placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
2406
                         placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
2407
                         placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
2408
                         placement[1];
2409
2410
          if (placement[1] === 'center') {
2411
            if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2412
              var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
2413
              if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
2414
                placement[1] = 'left';
2415
              } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
2416
                placement[1] = 'right';
2417
              }
2418
            } else {
2419
              var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
2420
              if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
2421
                placement[1] = 'top';
2422
              } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
2423
                placement[1] = 'bottom';
2424
              }
2425
            }
2426
          }
2427
        }
2428
2429
        switch (placement[0]) {
2430
          case 'top':
2431
            targetElemPos.top = hostElemPos.top - targetHeight;
2432
            break;
2433
          case 'bottom':
2434
            targetElemPos.top = hostElemPos.top + hostElemPos.height;
2435
            break;
2436
          case 'left':
2437
            targetElemPos.left = hostElemPos.left - targetWidth;
2438
            break;
2439
          case 'right':
2440
            targetElemPos.left = hostElemPos.left + hostElemPos.width;
2441
            break;
2442
        }
2443
2444
        switch (placement[1]) {
2445
          case 'top':
2446
            targetElemPos.top = hostElemPos.top;
2447
            break;
2448
          case 'bottom':
2449
            targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
2450
            break;
2451
          case 'left':
2452
            targetElemPos.left = hostElemPos.left;
2453
            break;
2454
          case 'right':
2455
            targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
2456
            break;
2457
          case 'center':
2458
            if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2459
              targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
2460
            } else {
2461
              targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
2462
            }
2463
            break;
2464
        }
2465
2466
        targetElemPos.top = Math.round(targetElemPos.top);
2467
        targetElemPos.left = Math.round(targetElemPos.left);
2468
        targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
2469
2470
        return targetElemPos;
2471
      },
2472
2473
      /**
2474
      * Provides a way for positioning tooltip & dropdown
2475
      * arrows when using placement options beyond the standard
2476
      * left, right, top, or bottom.
2477
      *
2478
      * @param {element} elem - The tooltip/dropdown element.
2479
      * @param {string} placement - The placement for the elem.
2480
      */
2481
      positionArrow: function(elem, placement) {
2482
        elem = this.getRawNode(elem);
2483
2484
        var innerElem = elem.querySelector('.tooltip-inner, .popover-inner');
2485
        if (!innerElem) {
2486
          return;
2487
        }
2488
2489
        var isTooltip = angular.element(innerElem).hasClass('tooltip-inner');
2490
2491
        var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
2492
        if (!arrowElem) {
2493
          return;
2494
        }
2495
2496
        var arrowCss = {
2497
          top: '',
2498
          bottom: '',
2499
          left: '',
2500
          right: ''
2501
        };
2502
2503
        placement = this.parsePlacement(placement);
2504
        if (placement[1] === 'center') {
2505
          // no adjustment necessary - just reset styles
2506
          angular.element(arrowElem).css(arrowCss);
2507
          return;
2508
        }
2509
2510
        var borderProp = 'border-' + placement[0] + '-width';
2511
        var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
2512
2513
        var borderRadiusProp = 'border-';
2514
        if (PLACEMENT_REGEX.vertical.test(placement[0])) {
2515
          borderRadiusProp += placement[0] + '-' + placement[1];
2516
        } else {
2517
          borderRadiusProp += placement[1] + '-' + placement[0];
2518
        }
2519
        borderRadiusProp += '-radius';
2520
        var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
2521
2522
        switch (placement[0]) {
2523
          case 'top':
2524
            arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
2525
            break;
2526
          case 'bottom':
2527
            arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
2528
            break;
2529
          case 'left':
2530
            arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
2531
            break;
2532
          case 'right':
2533
            arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
2534
            break;
2535
        }
2536
2537
        arrowCss[placement[1]] = borderRadius;
2538
2539
        angular.element(arrowElem).css(arrowCss);
2540
      }
2541
    };
2542
  }]);
2543
2544
angular.module('ui.bootstrap.datepickerPopup', ['ui.bootstrap.datepicker', 'ui.bootstrap.position'])
2545