text: /*
   * angular-ui-bootstrap
   * http://angular-ui.github.io/bootstrap/
  
   * Version: 1.1.2 - 2016-02-01
   * License: MIT
   */angular.module("ui.bootstrap", ["ui.bootstrap.tpls","ui.bootstrap.pagination","ui.bootstrap.paging","ui.bootstrap.typeahead","ui.bootstrap.debounce","ui.bootstrap.position"]);
  angular.module("ui.bootstrap.tpls", ["uib/template/pagination/pagination.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]);
  angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
  .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
    var ctrl = this;
    // Setup configuration parameters
    var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
      rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
      forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
      boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers;
    $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
    $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
  
    uibPaging.create(this, $scope, $attrs);
  
    if ($attrs.maxSize) {
      ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) {
        maxSize = parseInt(value, 10);
        ctrl.render();
      }));
    }
  
    // Create page object used in template
    function makePage(number, text, isActive) {
      return {
        number: number,
        text: text,
        active: isActive
      };
    }
  
    function getPages(currentPage, totalPages) {
      var pages = [];
  
      // Default page limits
      var startPage = 1, endPage = totalPages;
      var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
  
      // recompute if maxSize
      if (isMaxSized) {
        if (rotate) {
          // Current page is displayed in the middle of the visible ones
          startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
          endPage = startPage + maxSize - 1;
  
          // Adjust if limit is exceeded
          if (endPage > totalPages) {
            endPage = totalPages;
            startPage = endPage - maxSize + 1;
          }
        } else {
          // Visible pages are paginated with maxSize
          startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
  
          // Adjust last page if limit is exceeded
          endPage = Math.min(startPage + maxSize - 1, totalPages);
        }
      }
  
      // Add page number links
      for (var number = startPage; number <= endPage; number++) {
        var page = makePage(number, number, number === currentPage);
        pages.push(page);
      }
  
      // Add links to move between page sets
      if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
        if (startPage > 1) {
          if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
          var previousPageSet = makePage(startPage - 1, '...', false);
          pages.unshift(previousPageSet);
        }
          if (boundaryLinkNumbers) {
            if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
              var secondPageLink = makePage(2, '2', false);
              pages.unshift(secondPageLink);
            }
            //add the first page
            var firstPageLink = makePage(1, '1', false);
            pages.unshift(firstPageLink);
          }
        }
  
        if (endPage < totalPages) {
          if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
          var nextPageSet = makePage(endPage + 1, '...', false);
          pages.push(nextPageSet);
        }
          if (boundaryLinkNumbers) {
            if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
              var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
              pages.push(secondToLastPageLink);
            }
            //add the last page
            var lastPageLink = makePage(totalPages, totalPages, false);
            pages.push(lastPageLink);
          }
        }
      }
      return pages;
    }
  
    var originalRender = this.render;
    this.render = function() {
      originalRender();
      if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
        $scope.pages = getPages($scope.page, $scope.totalPages);
      }
    };
  }])
  
  .constant('uibPaginationConfig', {
    itemsPerPage: 10,
    boundaryLinks: false,
    boundaryLinkNumbers: false,
    directionLinks: true,
    firstText: 'First',
    previousText: 'Previous',
    nextText: 'Next',
    lastText: 'Last',
    rotate: true,
    forceEllipses: false
  })
  
  .directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) {
    return {
      scope: {
        totalItems: '=',
        firstText: '@',
        previousText: '@',
        nextText: '@',
        lastText: '@',
        ngDisabled:'='
      },
      require: ['uibPagination', '?ngModel'],
      controller: 'UibPaginationController',
      controllerAs: 'pagination',
      templateUrl: function(element, attrs) {
        return attrs.templateUrl || 'uib/template/pagination/pagination.html';
      },
      replace: true,
      link: function(scope, element, attrs, ctrls) {
        var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
  
        if (!ngModelCtrl) {
           return; // do nothing if no ng-model
        }
  
        paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
      }
    };
  }]);
  
  angular.module('ui.bootstrap.paging', [])
  /**
   * Helper internal service for generating common controller code between the
   * pager and pagination components
   */
  .factory('uibPaging', ['$parse', function($parse) {
    return {
      create: function(ctrl, $scope, $attrs) {
        ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
        ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl
        ctrl._watchers = [];
  
        ctrl.init = function(ngModelCtrl, config) {
          ctrl.ngModelCtrl = ngModelCtrl;
          ctrl.config = config;
  
          ngModelCtrl.$render = function() {
            ctrl.render();
          };
  
          if ($attrs.itemsPerPage) {
            ctrl._watchers.push($scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
              ctrl.itemsPerPage = parseInt(value, 10);
              $scope.totalPages = ctrl.calculateTotalPages();
              ctrl.updatePage();
            }));
          } else {
            ctrl.itemsPerPage = config.itemsPerPage;
          }
  
          $scope.$watch('totalItems', function(newTotal, oldTotal) {
            if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
              $scope.totalPages = ctrl.calculateTotalPages();
              ctrl.updatePage();
            }
          });
        };
  
        ctrl.calculateTotalPages = function() {
          var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
          return Math.max(totalPages || 0, 1);
        };
  
        ctrl.render = function() {
          $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
        };
  
        $scope.selectPage = function(page, evt) {
          if (evt) {
            evt.preventDefault();
          }
  
          var clickAllowed = !$scope.ngDisabled || !evt;
          if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
            if (evt && evt.target) {
              evt.target.blur();
            }
            ctrl.ngModelCtrl.$setViewValue(page);
            ctrl.ngModelCtrl.$render();
          }
        };
  
        $scope.getText = function(key) {
          return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
        };
  
        $scope.noPrevious = function() {
          return $scope.page === 1;
        };
  
        $scope.noNext = function() {
          return $scope.page === $scope.totalPages;
        };
  
        ctrl.updatePage = function() {
          ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
  
          if ($scope.page > $scope.totalPages) {
            $scope.selectPage($scope.totalPages);
          } else {
            ctrl.ngModelCtrl.$render();
          }
        };
  
        $scope.$on('$destroy', function() {
          while (ctrl._watchers.length) {
            ctrl._watchers.shift()();
          }
        });
      }
    };
  }]);
  
  angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])
  
  /**
   * A helper service that can parse typeahead's syntax (string provided by users)
   * Extracted to a separate service for ease of unit testing
   */
    .factory('uibTypeaheadParser', ['$parse', function($parse) {
      //                      00000111000000000000022200000000000000003333333333333330000000000044000
      var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
      return {
        parse: function(input) {
          var match = input.match(TYPEAHEAD_REGEXP);
          if (!match) {
            throw new Error(
              'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
                ' but got "' + input + '".');
          }
  
          return {
            itemName: match[3],
            source: $parse(match[4]),
            viewMapper: $parse(match[2] || match[1]),
            modelMapper: $parse(match[1])
          };
        }
      };
    }])
  
    .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
      function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
      var HOT_KEYS = [9, 13, 27, 38, 40];
      var eventDebounceTime = 200;
      var modelCtrl, ngModelOptions;
      //SUPPORTED ATTRIBUTES (OPTIONS)
  
      //minimal no of characters that needs to be entered before typeahead kicks-in
      var minLength = originalScope.$eval(attrs.typeaheadMinLength);
      if (!minLength && minLength !== 0) {
        minLength = 1;
      }
  
      //minimal wait time after last character typed before typeahead kicks-in
      var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
  
      //should it restrict model values to the ones selected from the popup only?
      var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
      originalScope.$watch(attrs.typeaheadEditable, function (newVal) {
        isEditable = newVal !== false;
      });
  
      //binding to a variable that indicates if matches are being retrieved asynchronously
      var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
  
      //a callback executed when a match is selected
      var onSelectCallback = $parse(attrs.typeaheadOnSelect);
  
      //should it select highlighted popup value when losing focus?
      var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
  
      //binding to a variable that indicates if there were no results after the query is completed
      var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
  
      var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
  
      var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
  
      var appendTo = attrs.typeaheadAppendTo ?
        originalScope.$eval(attrs.typeaheadAppendTo) : null;
  
      var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
  
      //If input matches an item of the list exactly, select it automatically
      var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
  
      //binding to a variable that indicates if dropdown is open
      var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop;
  
      var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false;
  
      //INTERNAL VARIABLES
  
      //model setter executed upon match selection
      var parsedModel = $parse(attrs.ngModel);
      var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
      var $setModelValue = function(scope, newValue) {
        if (angular.isFunction(parsedModel(originalScope)) &&
          ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
          return invokeModelSetter(scope, {$$$p: newValue});
        }
  
        return parsedModel.assign(scope, newValue);
      };
  
      //expressions used by typeahead
      var parserResult = typeaheadParser.parse(attrs.uibTypeahead);
  
      var hasFocus;
  
      //Used to avoid bug in iOS webview where iOS keyboard does not fire
      //mousedown & mouseup events
      //Issue #3699
      var selected;
  
      //create a child scope for the typeahead directive so we are not polluting original scope
      //with typeahead-specific data (matches, query etc.)
      var scope = originalScope.$new();
      var offDestroy = originalScope.$on('$destroy', function() {
        scope.$destroy();
      });
      scope.$on('$destroy', offDestroy);
  
      // WAI-ARIA
      var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
      element.attr({
        'aria-autocomplete': 'list',
        'aria-expanded': false,
        'aria-owns': popupId
      });
  
      var inputsContainer, hintInputElem;
      //add read-only input to show hint
      if (showHint) {
        inputsContainer = angular.element('<div></div>');
        inputsContainer.css('position', 'relative');
        element.after(inputsContainer);
        hintInputElem = element.clone();
        hintInputElem.attr('placeholder', '');
        hintInputElem.val('');
        hintInputElem.css({
          'position': 'absolute',
          'top': '0px',
          'left': '0px',
          'border-color': 'transparent',
          'box-shadow': 'none',
          'opacity': 1,
          'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)',
          'color': '#999'
        });
        element.css({
          'position': 'relative',
          'vertical-align': 'top',
          'background-color': 'transparent'
        });
        inputsContainer.append(hintInputElem);
        hintInputElem.after(element);
      }
  
      //pop-up element used to display matches
      var popUpEl = angular.element('<div uib-typeahead-popup></div>');
      popUpEl.attr({
        id: popupId,
        matches: 'matches',
        active: 'activeIdx',
        select: 'select(activeIdx, evt)',
        'move-in-progress': 'moveInProgress',
        query: 'query',
        position: 'position',
        'assign-is-open': 'assignIsOpen(isOpen)',
        debounce: 'debounceUpdate'
      });
      //custom item template
      if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
        popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
      }
  
      if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
        popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
      }
  
      var resetHint = function() {
        if (showHint) {
          hintInputElem.val('');
        }
      };
  
      var resetMatches = function() {
        scope.matches = [];
        scope.activeIdx = -1;
        element.attr('aria-expanded', false);
        resetHint();
      };
  
      var getMatchId = function(index) {
        return popupId + '-option-' + index;
      };
  
      // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
      // This attribute is added or removed automatically when the `activeIdx` changes.
      scope.$watch('activeIdx', function(index) {
        if (index < 0) {
          element.removeAttr('aria-activedescendant');
        } else {
          element.attr('aria-activedescendant', getMatchId(index));
        }
      });
  
      var inputIsExactMatch = function(inputValue, index) {
        if (scope.matches.length > index && inputValue) {
          return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
        }
  
        return false;
      };
  
      var getMatchesAsync = function(inputValue, evt) {
        var locals = {$viewValue: inputValue};
        isLoadingSetter(originalScope, true);
        isNoResultsSetter(originalScope, false);
        $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
          //it might happen that several async queries were in progress if a user were typing fast
          //but we are interested only in responses that correspond to the current view value
          var onCurrentRequest = inputValue === modelCtrl.$viewValue;
          if (onCurrentRequest && hasFocus) {
            if (matches && matches.length > 0) {
              scope.activeIdx = focusFirst ? 0 : -1;
              isNoResultsSetter(originalScope, false);
              scope.matches.length = 0;
  
              //transform labels
              for (var i = 0; i < matches.length; i++) {
                locals[parserResult.itemName] = matches[i];
                scope.matches.push({
                  id: getMatchId(i),
                  label: parserResult.viewMapper(scope, locals),
                  model: matches[i]
                });
              }
  
              scope.query = inputValue;
              //position pop-up with matches - we need to re-calculate its position each time we are opening a window
              //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
              //due to other elements being rendered
              recalculatePosition();
  
              element.attr('aria-expanded', true);
  
              //Select the single remaining option if user input matches
              if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
                if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
                  $$debounce(function() {
                    scope.select(0, evt);
                  }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
                } else {
                  scope.select(0, evt);
                }
              }
  
              if (showHint) {
                var firstLabel = scope.matches[0].label;
                if (angular.isString(inputValue) &&
                  inputValue.length > 0 &&
                  firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
                  hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
                } else {
                  hintInputElem.val('');
                }
              }
            } else {
              resetMatches();
              isNoResultsSetter(originalScope, true);
            }
          }
          if (onCurrentRequest) {
            isLoadingSetter(originalScope, false);
          }
        }, function() {
          resetMatches();
          isLoadingSetter(originalScope, false);
          isNoResultsSetter(originalScope, true);
        });
      };
  
      // bind events only if appendToBody params exist - performance feature
      if (appendToBody) {
        angular.element($window).on('resize', fireRecalculating);
        $document.find('body').on('scroll', fireRecalculating);
      }
  
      // Declare the debounced function outside recalculating for
      // proper debouncing
      var debouncedRecalculate = $$debounce(function() {
        // if popup is visible
        if (scope.matches.length) {
          recalculatePosition();
        }
  
        scope.moveInProgress = false;
      }, eventDebounceTime);
  
      // Default progress type
      scope.moveInProgress = false;
  
      function fireRecalculating() {
        if (!scope.moveInProgress) {
          scope.moveInProgress = true;
          scope.$digest();
        }
  
        debouncedRecalculate();
      }
  
      // recalculate actual position and set new values to scope
      // after digest loop is popup in right position
      function recalculatePosition() {
        scope.position = appendToBody ? $position.offset(element) : $position.position(element);
        scope.position.top += element.prop('offsetHeight');
      }
  
      //we need to propagate user's query so we can higlight matches
      scope.query = undefined;
  
      //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
      var timeoutPromise;
  
      var scheduleSearchWithTimeout = function(inputValue) {
        timeoutPromise = $timeout(function() {
          getMatchesAsync(inputValue);
        }, waitTime);
      };
  
      var cancelPreviousTimeout = function() {
        if (timeoutPromise) {
          $timeout.cancel(timeoutPromise);
        }
      };
  
      resetMatches();
  
      scope.assignIsOpen = function (isOpen) {
        isOpenSetter(originalScope, isOpen);
      };
  
      scope.select = function(activeIdx, evt) {
        //called from within the $digest() cycle
        var locals = {};
        var model, item;
  
        selected = true;
        locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
        model = parserResult.modelMapper(originalScope, locals);
        $setModelValue(originalScope, model);
        modelCtrl.$setValidity('editable', true);
        modelCtrl.$setValidity('parse', true);
  
        onSelectCallback(originalScope, {
          $item: item,
          $model: model,
          $label: parserResult.viewMapper(originalScope, locals),
          $event: evt
        });
  
        resetMatches();
  
        //return focus to the input element if a match was selected via a mouse click event
        // use timeout to avoid $rootScope:inprog error
        if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
          $timeout(function() { element[0].focus(); }, 0, false);
        }
      };
  
      //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
      element.on('keydown', function(evt) {
        //typeahead is open and an "interesting" key was pressed
        if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
          return;
        }
  
        // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
        if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
          resetMatches();
          scope.$digest();
          return;
        }
  
        evt.preventDefault();
        var target;
        switch (evt.which) {
          case 9:
          case 13:
            scope.$apply(function () {
              if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
                $$debounce(function() {
                  scope.select(scope.activeIdx, evt);
                }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
              } else {
                scope.select(scope.activeIdx, evt);
              }
            });
            break;
          case 27:
            evt.stopPropagation();
  
            resetMatches();
            scope.$digest();
            break;
          case 38:
            scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
            scope.$digest();
            target = popUpEl.find('li')[scope.activeIdx];
            target.parentNode.scrollTop = target.offsetTop;
            break;
          case 40:
            scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
            scope.$digest();
            target = popUpEl.find('li')[scope.activeIdx];
            target.parentNode.scrollTop = target.offsetTop;
            break;
        }
      });
  
      element.bind('focus', function (evt) {
        hasFocus = true;
        if (minLength === 0 && !modelCtrl.$viewValue) {
          $timeout(function() {
            getMatchesAsync(modelCtrl.$viewValue, evt);
          }, 0);
        }
      });
  
      element.bind('blur', function(evt) {
        if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
          selected = true;
          scope.$apply(function() {
            if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
              $$debounce(function() {
                scope.select(scope.activeIdx, evt);
              }, scope.debounceUpdate.blur);
            } else {
              scope.select(scope.activeIdx, evt);
            }
          });
        }
        if (!isEditable && modelCtrl.$error.editable) {
          modelCtrl.$viewValue = '';
          element.val('');
        }
        hasFocus = false;
        selected = false;
      });
  
      // Keep reference to click handler to unbind it.
      var dismissClickHandler = function(evt) {
        // Issue #3973
        // Firefox treats right click as a click on document
        if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
          resetMatches();
          if (!$rootScope.$$phase) {
            scope.$digest();
          }
        }
      };
  
      $document.on('click', dismissClickHandler);
  
      originalScope.$on('$destroy', function() {
        $document.off('click', dismissClickHandler);
        if (appendToBody || appendTo) {
          $popup.remove();
        }
  
        if (appendToBody) {
          angular.element($window).off('resize', fireRecalculating);
          $document.find('body').off('scroll', fireRecalculating);
        }
        // Prevent jQuery cache memory leak
        popUpEl.remove();
  
        if (showHint) {
            inputsContainer.remove();
        }
      });
  
      var $popup = $compile(popUpEl)(scope);
  
      if (appendToBody) {
        $document.find('body').append($popup);
      } else if (appendTo) {
        angular.element(appendTo).eq(0).append($popup);
      } else {
        element.after($popup);
      }
  
      this.init = function(_modelCtrl, _ngModelOptions) {
        modelCtrl = _modelCtrl;
        ngModelOptions = _ngModelOptions;
  
        scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
  
        //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
        //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
        modelCtrl.$parsers.unshift(function(inputValue) {
          hasFocus = true;
  
          if (minLength === 0 || inputValue && inputValue.length >= minLength) {
            if (waitTime > 0) {
              cancelPreviousTimeout();
              scheduleSearchWithTimeout(inputValue);
            } else {
              getMatchesAsync(inputValue);
            }
          } else {
            isLoadingSetter(originalScope, false);
            cancelPreviousTimeout();
            resetMatches();
          }
  
          if (isEditable) {
            return inputValue;
          }
  
          if (!inputValue) {
            // Reset in case user had typed something previously.
            modelCtrl.$setValidity('editable', true);
            return null;
          }
  
          modelCtrl.$setValidity('editable', false);
          return undefined;
        });
  
        modelCtrl.$formatters.push(function(modelValue) {
          var candidateViewValue, emptyViewValue;
          var locals = {};
  
          // The validity may be set to false via $parsers (see above) if
          // the model is restricted to selected values. If the model
          // is set manually it is considered to be valid.
          if (!isEditable) {
            modelCtrl.$setValidity('editable', true);
          }
  
          if (inputFormatter) {
            locals.$model = modelValue;
            return inputFormatter(originalScope, locals);
          }
  
          //it might happen that we don't have enough info to properly render input value
          //we need to check for this situation and simply return model value if we can't apply custom formatting
          locals[parserResult.itemName] = modelValue;
          candidateViewValue = parserResult.viewMapper(originalScope, locals);
          locals[parserResult.itemName] = undefined;
          emptyViewValue = parserResult.viewMapper(originalScope, locals);
  
          return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
        });
      };
    }])
  
    .directive('uibTypeahead', function() {
      return {
        controller: 'UibTypeaheadController',
        require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
        link: function(originalScope, element, attrs, ctrls) {
          ctrls[2].init(ctrls[0], ctrls[1]);
        }
      };
    })
  
    .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) {
      return {
        scope: {
          matches: '=',
          query: '=',
          active: '=',
          position: '&',
          moveInProgress: '=',
          select: '&',
          assignIsOpen: '&',
          debounce: '&'
        },
        replace: true,
        templateUrl: function(element, attrs) {
          return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
        },
        link: function(scope, element, attrs) {
          scope.templateUrl = attrs.templateUrl;
  
          scope.isOpen = function() {
            var isDropdownOpen = scope.matches.length > 0;
            scope.assignIsOpen({ isOpen: isDropdownOpen });
            return isDropdownOpen;
          };
  
          scope.isActive = function(matchIdx) {
            return scope.active === matchIdx;
          };
  
          scope.selectActive = function(matchIdx) {
            scope.active = matchIdx;
          };
  
          scope.selectMatch = function(activeIdx, evt) {
            var debounce = scope.debounce();
            if (angular.isNumber(debounce) || angular.isObject(debounce)) {
              $$debounce(function() {
                scope.select({activeIdx: activeIdx, evt: evt});
              }, angular.isNumber(debounce) ? debounce : debounce['default']);
            } else {
              scope.select({activeIdx: activeIdx, evt: evt});
            }
          };
        }
      };
    }])
  
    .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
      return {
        scope: {
          index: '=',
          match: '=',
          query: '='
        },
        link: function(scope, element, attrs) {
          var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
          $templateRequest(tplUrl).then(function(tplContent) {
            var tplEl = angular.element(tplContent.trim());
            element.replaceWith(tplEl);
            $compile(tplEl)(scope);
          });
        }
      };
    }])
  
    .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
      var isSanitizePresent;
      isSanitizePresent = $injector.has('$sanitize');
  
      function escapeRegexp(queryToEscape) {
        // Regex: capture the whole query string and replace it with the string that will be used to match
        // the results, for example if the capture is "a" the result will be \a
        return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
      }
  
      function containsHtml(matchItem) {
        return /<.*>/g.test(matchItem);
      }
  
      return function(matchItem, query) {
        if (!isSanitizePresent && containsHtml(matchItem)) {
          $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
        }
        matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
        if (!isSanitizePresent) {
          matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
        }
        return matchItem;
      };
    }]);
  
  angular.module('ui.bootstrap.debounce', [])
  /**
   * A helper, internal service that debounces a function
   */
    .factory('$$debounce', ['$timeout', function($timeout) {
      return function(callback, debounceTime) {
        var timeoutPromise;
  
        return function() {
          var self = this;
          var args = Array.prototype.slice.call(arguments);
          if (timeoutPromise) {
            $timeout.cancel(timeoutPromise);
          }
  
          timeoutPromise = $timeout(function() {
            callback.apply(self, args);
          }, debounceTime);
        };
      };
    }]);
  
  angular.module('ui.bootstrap.position', [])
  
  /**
   * A set of utility methods for working with the DOM.
   * It is meant to be used where we need to absolute-position elements in
   * relation to another element (this is the case for tooltips, popovers,
   * typeahead suggestions etc.).
   */
    .factory('$uibPosition', ['$document', '$window', function($document, $window) {
      /**
       * Used by scrollbarWidth() function to cache scrollbar's width.
       * Do not access this variable directly, use scrollbarWidth() instead.
       */
      var SCROLLBAR_WIDTH;
      var OVERFLOW_REGEX = {
        normal: /(auto|scroll)/,
        hidden: /(auto|scroll|hidden)/
      };
      var PLACEMENT_REGEX = {
        auto: /\s?auto?\s?/i,
        primary: /^(top|bottom|left|right)$/,
        secondary: /^(top|bottom|left|right|center)$/,
        vertical: /^(top|bottom)$/
      };
  
      return {
  
        /**
         * Provides a raw DOM element from a jQuery/jQLite element.
         *
         * @param {element} elem - The element to convert.
         *
         * @returns {element} A HTML element.
         */
        getRawNode: function(elem) {
          return elem[0] || elem;
        },
  
        /**
         * Provides a parsed number for a style property.  Strips
         * units and casts invalid numbers to 0.
         *
         * @param {string} value - The style value to parse.
         *
         * @returns {number} A valid number.
         */
        parseStyle: function(value) {
          value = parseFloat(value);
          return isFinite(value) ? value : 0;
        },
  
        /**
         * Provides the closest positioned ancestor.
         *
         * @param {element} element - The element to get the offest parent for.
         *
         * @returns {element} The closest positioned ancestor.
         */
        offsetParent: function(elem) {
          elem = this.getRawNode(elem);
  
          var offsetParent = elem.offsetParent || $document[0].documentElement;
  
          function isStaticPositioned(el) {
            return ($window.getComputedStyle(el).position || 'static') === 'static';
          }
  
          while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
            offsetParent = offsetParent.offsetParent;
          }
  
          return offsetParent || $document[0].documentElement;
        },
  
        /**
         * Provides the scrollbar width, concept from TWBS measureScrollbar()
         * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
         *
         * @returns {number} The width of the browser scollbar.
         */
        scrollbarWidth: function() {
          if (angular.isUndefined(SCROLLBAR_WIDTH)) {
            var scrollElem = angular.element('<div style="position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;"></div>');
            $document.find('body').append(scrollElem);
            SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
            SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
            scrollElem.remove();
          }
  
          return SCROLLBAR_WIDTH;
        },
  
        /**
         * Provides the closest scrollable ancestor.
         * A port of the jQuery UI scrollParent method:
         * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
         *
         * @param {element} elem - The element to find the scroll parent of.
         * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
         *   default is false.
         *
         * @returns {element} A HTML element.
         */
        scrollParent: function(elem, includeHidden) {
          elem = this.getRawNode(elem);
  
          var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
          var documentEl = $document[0].documentElement;
          var elemStyle = $window.getComputedStyle(elem);
          var excludeStatic = elemStyle.position === 'absolute';
          var scrollParent = elem.parentElement || documentEl;
  
          if (scrollParent === documentEl || elemStyle.position === 'fixed') {
            return documentEl;
          }
  
          while (scrollParent.parentElement && scrollParent !== documentEl) {
            var spStyle = $window.getComputedStyle(scrollParent);
            if (excludeStatic && spStyle.position !== 'static') {
              excludeStatic = false;
            }
  
            if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
              break;
            }
            scrollParent = scrollParent.parentElement;
          }
  
          return scrollParent;
        },
  
        /**
         * Provides read-only equivalent of jQuery's position function:
         * http://api.jquery.com/position/ - distance to closest positioned
         * ancestor.  Does not account for margins by default like jQuery position.
         *
         * @param {element} elem - The element to caclulate the position on.
         * @param {boolean=} [includeMargins=false] - Should margins be accounted
         * for, default is false.
         *
         * @returns {object} An object with the following properties:
         *   <ul>
         *     <li>**width**: the width of the element</li>
         *     <li>**height**: the height of the element</li>
         *     <li>**top**: distance to top edge of offset parent</li>
         *     <li>**left**: distance to left edge of offset parent</li>
         *   </ul>
         */
        position: function(elem, includeMagins) {
          elem = this.getRawNode(elem);
  
          var elemOffset = this.offset(elem);
          if (includeMagins) {
            var elemStyle = $window.getComputedStyle(elem);
            elemOffset.top -= this.parseStyle(elemStyle.marginTop);
            elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
          }
          var parent = this.offsetParent(elem);
          var parentOffset = {top: 0, left: 0};
  
          if (parent !== $document[0].documentElement) {
            parentOffset = this.offset(parent);
            parentOffset.top += parent.clientTop - parent.scrollTop;
            parentOffset.left += parent.clientLeft - parent.scrollLeft;
          }
  
          return {
            width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
            height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
            top: Math.round(elemOffset.top - parentOffset.top),
            left: Math.round(elemOffset.left - parentOffset.left)
          };
        },
  
        /**
         * Provides read-only equivalent of jQuery's offset function:
         * http://api.jquery.com/offset/ - distance to viewport.  Does
         * not account for borders, margins, or padding on the body
         * element.
         *
         * @param {element} elem - The element to calculate the offset on.
         *
         * @returns {object} An object with the following properties:
         *   <ul>
         *     <li>**width**: the width of the element</li>
         *     <li>**height**: the height of the element</li>
         *     <li>**top**: distance to top edge of viewport</li>
         *     <li>**right**: distance to bottom edge of viewport</li>
         *   </ul>
         */
        offset: function(elem) {
          elem = this.getRawNode(elem);
  
          var elemBCR = elem.getBoundingClientRect();
          return {
            width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
            height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
            top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
            left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
          };
        },
  
        /**
         * Provides offset distance to the closest scrollable ancestor
         * or viewport.  Accounts for border and scrollbar width.
         *
         * Right and bottom dimensions represent the distance to the
         * respective edge of the viewport element.  If the element
         * edge extends beyond the viewport, a negative value will be
         * reported.
         *
         * @param {element} elem - The element to get the viewport offset for.
         * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
         * of the first scrollable element, default is false.
         * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
         * be accounted for, default is true.
         *
         * @returns {object} An object with the following properties:
         *   <ul>
         *     <li>**top**: distance to the top content edge of viewport element</li>
         *     <li>**bottom**: distance to the bottom content edge of viewport element</li>
         *     <li>**left**: distance to the left content edge of viewport element</li>
         *     <li>**right**: distance to the right content edge of viewport element</li>
         *   </ul>
         */
        viewportOffset: function(elem, useDocument, includePadding) {
          elem = this.getRawNode(elem);
          includePadding = includePadding !== false ? true : false;
  
          var elemBCR = elem.getBoundingClientRect();
          var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
  
          var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
          var offsetParentBCR = offsetParent.getBoundingClientRect();
  
          offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
          offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
          if (offsetParent === $document[0].documentElement) {
            offsetBCR.top += $window.pageYOffset;
            offsetBCR.left += $window.pageXOffset;
          }
          offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
          offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
  
          if (includePadding) {
            var offsetParentStyle = $window.getComputedStyle(offsetParent);
            offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
            offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
            offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
            offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
          }
  
          return {
            top: Math.round(elemBCR.top - offsetBCR.top),
            bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
            left: Math.round(elemBCR.left - offsetBCR.left),
            right: Math.round(offsetBCR.right - elemBCR.right)
          };
        },
  
        /**
         * Provides an array of placement values parsed from a placement string.
         * Along with the 'auto' indicator, supported placement strings are:
         *   <ul>
         *     <li>top: element on top, horizontally centered on host element.</li>
         *     <li>top-left: element on top, left edge aligned with host element left edge.</li>
         *     <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
         *     <li>bottom: element on bottom, horizontally centered on host element.</li>
         *     <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
         *     <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
         *     <li>left: element on left, vertically centered on host element.</li>
         *     <li>left-top: element on left, top edge aligned with host element top edge.</li>
         *     <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
         *     <li>right: element on right, vertically centered on host element.</li>
         *     <li>right-top: element on right, top edge aligned with host element top edge.</li>
         *     <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
         *   </ul>
         * A placement string with an 'auto' indicator is expected to be
         * space separated from the placement, i.e: 'auto bottom-left'  If
         * the primary and secondary placement values do not match 'top,
         * bottom, left, right' then 'top' will be the primary placement and
         * 'center' will be the secondary placement.  If 'auto' is passed, true
         * will be returned as the 3rd value of the array.
         *
         * @param {string} placement - The placement string to parse.
         *
         * @returns {array} An array with the following values
         * <ul>
         *   <li>**[0]**: The primary placement.</li>
         *   <li>**[1]**: The secondary placement.</li>
         *   <li>**[2]**: If auto is passed: true, else undefined.</li>
         * </ul>
         */
        parsePlacement: function(placement) {
          var autoPlace = PLACEMENT_REGEX.auto.test(placement);
          if (autoPlace) {
            placement = placement.replace(PLACEMENT_REGEX.auto, '');
          }
  
          placement = placement.split('-');
  
          placement[0] = placement[0] || 'top';
          if (!PLACEMENT_REGEX.primary.test(placement[0])) {
            placement[0] = 'top';
          }
  
          placement[1] = placement[1] || 'center';
          if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
            placement[1] = 'center';
          }
  
          if (autoPlace) {
            placement[2] = true;
          } else {
            placement[2] = false;
          }
  
          return placement;
        },
  
        /**
         * Provides coordinates for an element to be positioned relative to
         * another element.  Passing 'auto' as part of the placement parameter
         * will enable smart placement - where the element fits. i.e:
         * 'auto left-top' will check to see if there is enough space to the left
         * of the hostElem to fit the targetElem, if not place right (same for secondary
         * top placement).  Available space is calculated using the viewportOffset
         * function.
         *
         * @param {element} hostElem - The element to position against.
         * @param {element} targetElem - The element to position.
         * @param {string=} [placement=top] - The placement for the targetElem,
         *   default is 'top'. 'center' is assumed as secondary placement for
         *   'top', 'left', 'right', and 'bottom' placements.  Available placements are:
         *   <ul>
         *     <li>top</li>
         *     <li>top-right</li>
         *     <li>top-left</li>
         *     <li>bottom</li>
         *     <li>bottom-left</li>
         *     <li>bottom-right</li>
         *     <li>left</li>
         *     <li>left-top</li>
         *     <li>left-bottom</li>
         *     <li>right</li>
         *     <li>right-top</li>
         *     <li>right-bottom</li>
         *   </ul>
         * @param {boolean=} [appendToBody=false] - Should the top and left values returned
         *   be calculated from the body element, default is false.
         *
         * @returns {object} An object with the following properties:
         *   <ul>
         *     <li>**top**: Value for targetElem top.</li>
         *     <li>**left**: Value for targetElem left.</li>
         *     <li>**placement**: The resolved placement.</li>
         *   </ul>
         */
        positionElements: function(hostElem, targetElem, placement, appendToBody) {
          hostElem = this.getRawNode(hostElem);
          targetElem = this.getRawNode(targetElem);
  
          // need to read from prop to support tests.
          var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
          var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
  
          placement = this.parsePlacement(placement);
  
          var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
          var targetElemPos = {top: 0, left: 0, placement: ''};
  
          if (placement[2]) {
            var viewportOffset = this.viewportOffset(hostElem);
  
            var targetElemStyle = $window.getComputedStyle(targetElem);
            var adjustedSize = {
              width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
              height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
            };
  
            placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
                           placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
                           placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
                           placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
                           placement[0];
  
            placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
                           placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
                           placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
                           placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
                           placement[1];
  
            if (placement[1] === 'center') {
              if (PLACEMENT_REGEX.vertical.test(placement[0])) {
                var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
                if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
                  placement[1] = 'left';
                } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
                  placement[1] = 'right';
                }
              } else {
                var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
                if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
                  placement[1] = 'top';
                } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
                  placement[1] = 'bottom';
                }
              }
            }
          }
  
          switch (placement[0]) {
            case 'top':
              targetElemPos.top = hostElemPos.top - targetHeight;
              break;
            case 'bottom':
              targetElemPos.top = hostElemPos.top + hostElemPos.height;
              break;
            case 'left':
              targetElemPos.left = hostElemPos.left - targetWidth;
              break;
            case 'right':
              targetElemPos.left = hostElemPos.left + hostElemPos.width;
              break;
          }
  
          switch (placement[1]) {
            case 'top':
              targetElemPos.top = hostElemPos.top;
              break;
            case 'bottom':
              targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
              break;
            case 'left':
              targetElemPos.left = hostElemPos.left;
              break;
            case 'right':
              targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
              break;
            case 'center':
              if (PLACEMENT_REGEX.vertical.test(placement[0])) {
                targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
              } else {
                targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
              }
              break;
          }
  
          targetElemPos.top = Math.round(targetElemPos.top);
          targetElemPos.left = Math.round(targetElemPos.left);
          targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
  
          return targetElemPos;
        },
  
        /**
        * Provides a way for positioning tooltip & dropdown
        * arrows when using placement options beyond the standard
        * left, right, top, or bottom.
        *
        * @param {element} elem - The tooltip/dropdown element.
        * @param {string} placement - The placement for the elem.
        */
        positionArrow: function(elem, placement) {
          elem = this.getRawNode(elem);
  
          var innerElem = elem.querySelector('.tooltip-inner, .popover-inner');
          if (!innerElem) {
            return;
          }
  
          var isTooltip = angular.element(innerElem).hasClass('tooltip-inner');
  
          var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
          if (!arrowElem) {
            return;
          }
  
          placement = this.parsePlacement(placement);
          if (placement[1] === 'center') {
            // no adjustment necessary - just reset styles
            angular.element(arrowElem).css({top: '', bottom: '', right: '', left: '', margin: ''});
            return;
          }
  
          var borderProp = 'border-' + placement[0] + '-width';
          var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
  
          var borderRadiusProp = 'border-';
          if (PLACEMENT_REGEX.vertical.test(placement[0])) {
            borderRadiusProp += placement[0] + '-' + placement[1];
          } else {
            borderRadiusProp += placement[1] + '-' + placement[0];
          }
          borderRadiusProp += '-radius';
          var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
  
          var arrowCss = {
            top: 'auto',
            bottom: 'auto',
            left: 'auto',
            right: 'auto',
            margin: 0
          };
  
          switch (placement[0]) {
            case 'top':
              arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
              break;
            case 'bottom':
              arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
              break;
            case 'left':
              arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
              break;
            case 'right':
              arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
              break;
          }
  
          arrowCss[placement[1]] = borderRadius;
  
          angular.element(arrowElem).css(arrowCss);
        }
      };
    }]);
  
  angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("uib/template/pagination/pagination.html",
      "<ul class=\"pagination\">\n" +
      "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" +
      "  <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
      "  <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" +
      "  <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
      "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" +
      "</ul>\n" +
      "");
  }]);
  
  angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("uib/template/typeahead/typeahead-match.html",
      "<a href\n" +
      "   tabindex=\"-1\"\n" +
      "   ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"\n" +
      "   ng-attr-title=\"{{match.label}}\"></a>\n" +
      "");
  }]);
  
  angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("uib/template/typeahead/typeahead-popup.html",
      "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
      "    <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">\n" +
      "        <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
      "    </li>\n" +
      "</ul>\n" +
      "");
  }]);
  angular.module('ui.bootstrap.typeahead').run(function() {!angular.$$csp().noInlineStyle && angular.element(document).find('head').prepend('<style type="text/css">[uib-typeahead-popup].dropdown-menu{display:block;}</style>'); });