diff --git a/.gitignore b/.gitignore index 496ee2c..3d72576 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.DS_Store \ No newline at end of file +.DS_Store +.idea \ No newline at end of file diff --git a/bower.json b/bower.json index 05163be..f554b77 100644 --- a/bower.json +++ b/bower.json @@ -11,7 +11,7 @@ "license": "MIT", "ignore": [], "description": "Native AngularJS (Angular) directives for Bootstrap.", - "version": "2.5.0", + "version": "2.5.6", "main": ["./ui-bootstrap-tpls.js"], "dependencies": { "angular": ">=1.4.0" diff --git a/package.json b/package.json index 4ff93d6..c47968b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-ui-bootstrap", - "version": "2.5.0", + "version": "2.5.6", "description": "Bootstrap widgets for Angular", "main": "index.js", "homepage": "http://angular-ui.github.io/bootstrap/", diff --git a/ui-bootstrap-tpls.js b/ui-bootstrap-tpls.js index 6783893..957880c 100644 --- a/ui-bootstrap-tpls.js +++ b/ui-bootstrap-tpls.js @@ -2,7 +2,7 @@ * angular-ui-bootstrap * http://angular-ui.github.io/bootstrap/ - * Version: 2.5.0 - 2017-01-28 + * Version: 2.5.4 - 2018-09-20 * License: MIT */angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.tabindex","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.multiMap","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]); angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]); @@ -6739,48 +6739,80 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap }; }]) - .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) + .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser', '$scope', '$interpolate', + function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser, $scope, $interpolate) { + var minLength, isEditable; - //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; - } + var interpolateValue = function(value){ + if(value){ + return $interpolate(value)($scope); + } + return value; + }; + + var attributesToInterpolate = [ + 'typeaheadMinLength', 'typeaheadEditable', 'typeaheadWaitMs', 'typeaheadOnSelect', 'typeaheadLoading', 'typeaheadAppendTo', + 'typeaheadShowHint', 'typeaheadIsOpen', 'typeaheadSelectOnExact', 'typeaheadFocusFirst', 'typeaheadFocusOnSelect', + 'typeaheadPopupTemplateUrl', 'typeaheadTemplateUrl', 'uibTypeahead', 'ngModel', 'typeaheadSelectOnBlur' + ]; + + angular.forEach(attributesToInterpolate, function(attribute){ + attrs[attribute] = interpolateValue(attrs[attribute]); + }); + + $scope.init = function() { + + originalScope.$watch(attrs.typeaheadMinLength, function (newValue) { + var newVal = interpolateValue(newValue); + minLength = !newVal && newVal !== 0 ? 1 : newVal; + }); + + originalScope.$watch(attrs.typeaheadEditable, function (newValue) { + var newVal = interpolateValue(newValue); + isEditable = newVal !== false; + }); + }; + + 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 + minLength = originalScope.$eval(attrs.typeaheadMinLength); + if (!minLength && minLength !== 0) { + minLength = 1; + } originalScope.$watch(attrs.typeaheadMinLength, function (newVal) { minLength = !newVal && newVal !== 0 ? 1 : newVal; }); - //minimal wait time after last character typed before typeahead kicks-in + //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; + //should it restrict model values to the ones selected from the popup only? + 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 + //binding to a variable that indicates if matches are being retrieved asynchronously var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; - //a function to determine if an event should cause selection + //a function to determine if an event should cause selection var isSelectEvent = attrs.typeaheadShouldSelect ? $parse(attrs.typeaheadShouldSelect) : function(scope, vals) { - var evt = vals.$event; - return evt.which === 13 || evt.which === 9; - }; + var evt = vals.$event; + return evt.which === 13 || evt.which === 9; + }; - //a callback executed when a match is selected + //a callback executed when a match is selected var onSelectCallback = $parse(attrs.typeaheadOnSelect); - //should it select highlighted popup value when losing focus? + //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 + //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; @@ -6792,125 +6824,129 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; - //If input matches an item of the list exactly, select it automatically + //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 + //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 + //INTERNAL VARIABLES - //model setter executed upon match selection + //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.getOption('getterSetter')) { - return invokeModelSetter(scope, {$$$p: newValue}); - } + var $setModelValue = function (scope, newValue) { + if (angular.isFunction(parsedModel(originalScope)) && + ngModelOptions.getOption('getterSetter')) { + return invokeModelSetter(scope, {$$$p: newValue}); + } - return parsedModel.assign(scope, 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; + //expressions used by typeahead + var getParserResult = function () { + return typeaheadParser.parse(attrs.uibTypeahead); + }; - //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 hasFocus; - var inputsContainer, hintInputElem; - //add read-only input to show hint - if (showHint) { - inputsContainer = angular.element('
'); - inputsContainer.css('position', 'relative'); - element.after(inputsContainer); - hintInputElem = element.clone(); - hintInputElem.attr('placeholder', ''); - hintInputElem.attr('tabindex', '-1'); - 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' - }); + //Used to avoid bug in iOS webview where iOS keyboard does not fire + //mousedown & mouseup events + //Issue #3699 + var selected; - if (hintInputElem.attr('id')) { - hintInputElem.removeAttr('id'); // remove duplicate id if present. - } - inputsContainer.append(hintInputElem); - hintInputElem.after(element); - } - - //pop-up element used to display matches - var popUpEl = angular.element('
'); - 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 + //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('
'); + inputsContainer.css('position', 'relative'); + element.after(inputsContainer); + hintInputElem = element.clone(); + hintInputElem.attr('placeholder', ''); + hintInputElem.attr('tabindex', '-1'); + 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' + }); + + if (hintInputElem.attr('id')) { + hintInputElem.removeAttr('id'); // remove duplicate id if present. + } + inputsContainer.append(hintInputElem); + hintInputElem.after(element); + } + + //pop-up element used to display matches + var popUpEl = angular.element('
'); + 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 resetHint = function () { + if (showHint) { + hintInputElem.val(''); + } + }; - var resetMatches = function() { - scope.matches = []; - scope.activeIdx = -1; - element.attr('aria-expanded', false); - resetHint(); - }; + var resetMatches = function () { + scope.matches = []; + scope.activeIdx = -1; + element.attr('aria-expanded', false); + resetHint(); + }; - var getMatchId = function(index) { - return popupId + '-option-' + index; - }; + 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. @@ -6922,386 +6958,390 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap } }); - var inputIsExactMatch = function(inputValue, index) { - if (scope.matches.length > index && inputValue) { - return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); - } + var inputIsExactMatch = function (inputValue, index) { + if (scope.matches.length > index && inputValue) { + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } - return false; - }; + 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] - }); - } + var getMatchesAsync = function (inputValue, evt) { + var locals = {$viewValue: inputValue}; + isLoadingSetter(originalScope, true); + isNoResultsSetter(originalScope, false); + $q.when(getParserResult().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[getParserResult().itemName] = matches[i]; + scope.matches.push({ + id: getMatchId(i), + label: getParserResult().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(); + 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); + 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); - } - } + //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)); + 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 { - hintInputElem.val(''); + resetMatches(); + isNoResultsSetter(originalScope, true); } } - } else { + if (onCurrentRequest) { + isLoadingSetter(originalScope, false); + } + }, function () { resetMatches(); + isLoadingSetter(originalScope, false); isNoResultsSetter(originalScope, true); - } - } - if (onCurrentRequest) { - isLoadingSetter(originalScope, false); + }); + }; + + // bind events only if appendToBody params exist - performance feature + if (appendToBody) { + angular.element($window).on('resize', fireRecalculating); + $document.find('body').on('scroll', fireRecalculating); } - }, 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(); + } - // 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); - scope.moveInProgress = false; - }, eventDebounceTime); + // Default progress type + scope.moveInProgress = false; - // Default progress type - scope.moveInProgress = false; + function fireRecalculating() { + if (!scope.moveInProgress) { + scope.moveInProgress = true; + scope.$digest(); + } - function fireRecalculating() { - if (!scope.moveInProgress) { - scope.moveInProgress = true; - scope.$digest(); - } + debouncedRecalculate(); + } - 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'); + } - // 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; - //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; - //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 scheduleSearchWithTimeout = function(inputValue) { - timeoutPromise = $timeout(function() { - getMatchesAsync(inputValue); - }, waitTime); - }; + var cancelPreviousTimeout = function () { + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + }; - var cancelPreviousTimeout = function() { - if (timeoutPromise) { - $timeout.cancel(timeoutPromise); - } - }; + resetMatches(); - resetMatches(); + scope.assignIsOpen = function (isOpen) { + isOpenSetter(originalScope, isOpen); + }; - scope.assignIsOpen = function (isOpen) { - isOpenSetter(originalScope, isOpen); - }; + scope.select = function (activeIdx, evt) { + //called from within the $digest() cycle + var locals = {}; + var model, item; - 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 - }); + selected = true; + locals[getParserResult().itemName] = item = scope.matches[activeIdx].model; + model = getParserResult().modelMapper(originalScope, locals); + $setModelValue(originalScope, model); + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); - resetMatches(); + onSelectCallback(originalScope, { + $item: item, + $model: model, + $label: getParserResult().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 + //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); - } - }; + $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; - } + //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; + } - var shouldSelect = isSelectEvent(originalScope, {$event: evt}); + var shouldSelect = isSelectEvent(originalScope, {$event: evt}); - /** - * if there's nothing selected (i.e. focusFirst) and enter or tab is hit - * or - * shift + tab is pressed to bring focus to the previous element - * then clear the results - */ - if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) { - resetMatches(); - scope.$digest(); - return; - } + /** + * if there's nothing selected (i.e. focusFirst) and enter or tab is hit + * or + * shift + tab is pressed to bring focus to the previous element + * then clear the results + */ + if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) { + resetMatches(); + scope.$digest(); + return; + } - evt.preventDefault(); - var target; - switch (evt.which) { - case 27: // escape - evt.stopPropagation(); + evt.preventDefault(); + var target; + switch (evt.which) { + case 27: // escape + evt.stopPropagation(); - resetMatches(); - originalScope.$digest(); - break; - case 38: // up arrow - scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; - scope.$digest(); - target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; - target.parentNode.scrollTop = target.offsetTop; - break; - case 40: // down arrow - scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; - scope.$digest(); - target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; - target.parentNode.scrollTop = target.offsetTop; - break; - default: - if (shouldSelect) { - scope.$apply(function() { - if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { - $$debounce(function() { + resetMatches(); + originalScope.$digest(); + break; + case 38: // up arrow + scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; + target.parentNode.scrollTop = target.offsetTop; + break; + case 40: // down arrow + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; + target.parentNode.scrollTop = target.offsetTop; + break; + default: + if (shouldSelect) { + 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); + } + }); + } + } + }); + + element.on('focus', function (evt) { + hasFocus = true; + if (minLength === 0 && !modelCtrl.$viewValue) { + $timeout(function () { + getMatchesAsync(modelCtrl.$viewValue, evt); + }, 0); + } + }); + + element.on('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); - }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); + }, scope.debounceUpdate.blur); } else { scope.select(scope.activeIdx, evt); } }); } - } - }); - - element.on('focus', function (evt) { - hasFocus = true; - if (minLength === 0 && !modelCtrl.$viewValue) { - $timeout(function() { - getMatchesAsync(modelCtrl.$viewValue, evt); - }, 0); - } - }); - - element.on('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.$setViewValue(); + scope.$apply(function () { + // Reset validity as we are clearing + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + }); + element.val(''); } + hasFocus = false; + selected = false; }); - } - if (!isEditable && modelCtrl.$error.editable) { - modelCtrl.$setViewValue(); - scope.$apply(function() { - // Reset validity as we are clearing - modelCtrl.$setValidity('editable', true); - modelCtrl.$setValidity('parse', true); - }); - 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) { - originalScope.$digest(); - } - } - }; + // 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) { + originalScope.$digest(); + } + } + }; - $document.on('click', dismissClickHandler); + $document.on('click', dismissClickHandler); - originalScope.$on('$destroy', function() { - $document.off('click', dismissClickHandler); - if (appendToBody || appendTo) { - $popup.remove(); - } + 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 (appendToBody) { + angular.element($window).off('resize', fireRecalculating); + $document.find('body').off('scroll', fireRecalculating); + } + // Prevent jQuery cache memory leak + popUpEl.remove(); - if (showHint) { - inputsContainer.remove(); - } - }); + if (showHint) { + inputsContainer.remove(); + } + }); - var $popup = $compile(popUpEl)(scope); + 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); - } + 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) { - modelCtrl = _modelCtrl; - ngModelOptions = extractOptions(modelCtrl); - scope.debounceUpdate = $parse(ngModelOptions.getOption('debounce'))(originalScope); + this.init = function (_modelCtrl) { + modelCtrl = _modelCtrl; + ngModelOptions = extractOptions(modelCtrl); - //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; + scope.debounceUpdate = $parse(ngModelOptions.getOption('debounce'))(originalScope); - if (minLength === 0 || inputValue && inputValue.length >= minLength) { - if (waitTime > 0) { - cancelPreviousTimeout(); - scheduleSearchWithTimeout(inputValue); - } else { - getMatchesAsync(inputValue); - } - } else { - isLoadingSetter(originalScope, false); - cancelPreviousTimeout(); - resetMatches(); - } + //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 (isEditable) { - return inputValue; - } + if (minLength === 0 || inputValue && inputValue.length >= minLength) { + if (waitTime > 0) { + cancelPreviousTimeout(); + scheduleSearchWithTimeout(inputValue); + } else { + getMatchesAsync(inputValue); + } + } else { + isLoadingSetter(originalScope, false); + cancelPreviousTimeout(); + resetMatches(); + } - if (!inputValue) { - // Reset in case user had typed something previously. - modelCtrl.$setValidity('editable', true); - return null; - } + if (isEditable) { + return inputValue; + } - modelCtrl.$setValidity('editable', false); - return undefined; - }); + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return null; + } - modelCtrl.$formatters.push(function(modelValue) { - var candidateViewValue, emptyViewValue; - var locals = {}; + modelCtrl.$setValidity('editable', false); + return undefined; + }); - // 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); - } + modelCtrl.$formatters.push(function (modelValue) { + var candidateViewValue, emptyViewValue; + var locals = {}; - if (inputFormatter) { - locals.$model = modelValue; - return inputFormatter(originalScope, 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); + } - //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); + if (inputFormatter) { + locals.$model = modelValue; + return inputFormatter(originalScope, locals); + } - return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; - }); - }; + //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[getParserResult().itemName] = modelValue; + candidateViewValue = getParserResult().viewMapper(originalScope, locals); + locals[getParserResult().itemName] = undefined; + emptyViewValue = getParserResult().viewMapper(originalScope, locals); - function extractOptions(ngModelCtrl) { - var ngModelOptions; + return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; + }); + }; - if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing - // guarantee a value - ngModelOptions = ngModelCtrl.$options || {}; + function extractOptions(ngModelCtrl) { + var ngModelOptions; - // mimic 1.6+ api - ngModelOptions.getOption = function (key) { - return ngModelOptions[key]; - }; - } else { // in angular >=1.6 $options is always present - ngModelOptions = ngModelCtrl.$options; - } + if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing + // guarantee a value + ngModelOptions = ngModelCtrl.$options || {}; + + // mimic 1.6+ api + ngModelOptions.getOption = function (key) { + return ngModelOptions[key]; + }; + } else { // in angular >=1.6 $options is always present + ngModelOptions = ngModelCtrl.$options; + } + + return ngModelOptions; + } - return ngModelOptions; - } }]) .directive('uibTypeahead', function() { @@ -7405,7 +7445,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap }; }]); -angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) { +angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function ($templateCache) { $templateCache.put("uib/template/accordion/accordion-group.html", "
\n" + "

\n" + @@ -7418,12 +7458,12 @@ angular.module("uib/template/accordion/accordion-group.html", []).run(["$templat ""); }]); -angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) { +angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function ($templateCache) { $templateCache.put("uib/template/accordion/accordion.html", "
"); }]); -angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) { +angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function ($templateCache) { $templateCache.put("uib/template/alert/alert.html", "