diff options
author | 2017-03-08 11:54:56 +0530 | |
---|---|---|
committer | 2017-03-08 11:54:56 +0530 | |
commit | 80b299e8c4f290e3b16f35eea922cac989b6e767 (patch) | |
tree | b99086734507728ae096349cca4d0258335dbf4f /vnfmarket/common/thirdparty/angular-material/modules/closure/autocomplete/autocomplete.js | |
parent | 5fa92b4eb456b5f4959c39578a3c6e3555c4ae7d (diff) |
VnfMarket place refactor
IssueId : CLIENT-4
Change-Id: Ia1b076400f9c3bedf9db714099a608ece392aa59
Signed-off-by: seshukm <seshu.kumar.m@huawei.com>
Diffstat (limited to 'vnfmarket/common/thirdparty/angular-material/modules/closure/autocomplete/autocomplete.js')
-rw-r--r-- | vnfmarket/common/thirdparty/angular-material/modules/closure/autocomplete/autocomplete.js | 1698 |
1 files changed, 0 insertions, 1698 deletions
diff --git a/vnfmarket/common/thirdparty/angular-material/modules/closure/autocomplete/autocomplete.js b/vnfmarket/common/thirdparty/angular-material/modules/closure/autocomplete/autocomplete.js deleted file mode 100644 index c72c4d81..00000000 --- a/vnfmarket/common/thirdparty/angular-material/modules/closure/autocomplete/autocomplete.js +++ /dev/null @@ -1,1698 +0,0 @@ -/*! - * Angular Material Design - * https://github.com/angular/material - * @license MIT - * v1.1.3 - */ -goog.provide('ngmaterial.components.autocomplete'); -goog.require('ngmaterial.components.icon'); -goog.require('ngmaterial.components.virtualRepeat'); -goog.require('ngmaterial.core'); -/** - * @ngdoc module - * @name material.components.autocomplete - */ -/* - * @see js folder for autocomplete implementation - */ -angular.module('material.components.autocomplete', [ - 'material.core', - 'material.components.icon', - 'material.components.virtualRepeat' -]); - - -MdAutocompleteCtrl['$inject'] = ["$scope", "$element", "$mdUtil", "$mdConstant", "$mdTheming", "$window", "$animate", "$rootElement", "$attrs", "$q", "$log", "$mdLiveAnnouncer"];angular - .module('material.components.autocomplete') - .controller('MdAutocompleteCtrl', MdAutocompleteCtrl); - -var ITEM_HEIGHT = 48, - MAX_ITEMS = 5, - MENU_PADDING = 8, - INPUT_PADDING = 2; // Padding provided by `md-input-container` - -function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming, $window, - $animate, $rootElement, $attrs, $q, $log, $mdLiveAnnouncer) { - - // Internal Variables. - var ctrl = this, - itemParts = $scope.itemsExpr.split(/ in /i), - itemExpr = itemParts[ 1 ], - elements = null, - cache = {}, - noBlur = false, - selectedItemWatchers = [], - hasFocus = false, - fetchesInProgress = 0, - enableWrapScroll = null, - inputModelCtrl = null, - debouncedOnResize = $mdUtil.debounce(onWindowResize); - - // Public Exported Variables with handlers - defineProperty('hidden', handleHiddenChange, true); - - // Public Exported Variables - ctrl.scope = $scope; - ctrl.parent = $scope.$parent; - ctrl.itemName = itemParts[ 0 ]; - ctrl.matches = []; - ctrl.loading = false; - ctrl.hidden = true; - ctrl.index = null; - ctrl.id = $mdUtil.nextUid(); - ctrl.isDisabled = null; - ctrl.isRequired = null; - ctrl.isReadonly = null; - ctrl.hasNotFound = false; - - // Public Exported Methods - ctrl.keydown = keydown; - ctrl.blur = blur; - ctrl.focus = focus; - ctrl.clear = clearValue; - ctrl.select = select; - ctrl.listEnter = onListEnter; - ctrl.listLeave = onListLeave; - ctrl.mouseUp = onMouseup; - ctrl.getCurrentDisplayValue = getCurrentDisplayValue; - ctrl.registerSelectedItemWatcher = registerSelectedItemWatcher; - ctrl.unregisterSelectedItemWatcher = unregisterSelectedItemWatcher; - ctrl.notFoundVisible = notFoundVisible; - ctrl.loadingIsVisible = loadingIsVisible; - ctrl.positionDropdown = positionDropdown; - - /** - * Report types to be used for the $mdLiveAnnouncer - * @enum {number} Unique flag id. - */ - var ReportType = { - Count: 1, - Selected: 2 - }; - - return init(); - - //-- initialization methods - - /** - * Initialize the controller, setup watchers, gather elements - */ - function init () { - - $mdUtil.initOptionalProperties($scope, $attrs, { - searchText: '', - selectedItem: null, - clearButton: false - }); - - $mdTheming($element); - configureWatchers(); - $mdUtil.nextTick(function () { - - gatherElements(); - moveDropdown(); - - // Forward all focus events to the input element when autofocus is enabled - if ($scope.autofocus) { - $element.on('focus', focusInputElement); - } - }); - } - - function updateModelValidators() { - if (!$scope.requireMatch || !inputModelCtrl) return; - - inputModelCtrl.$setValidity('md-require-match', !!$scope.selectedItem || !$scope.searchText); - } - - /** - * Calculates the dropdown's position and applies the new styles to the menu element - * @returns {*} - */ - function positionDropdown () { - if (!elements) { - return $mdUtil.nextTick(positionDropdown, false, $scope); - } - - var dropdownHeight = ($scope.dropdownItems || MAX_ITEMS) * ITEM_HEIGHT; - - var hrect = elements.wrap.getBoundingClientRect(), - vrect = elements.snap.getBoundingClientRect(), - root = elements.root.getBoundingClientRect(), - top = vrect.bottom - root.top, - bot = root.bottom - vrect.top, - left = hrect.left - root.left, - width = hrect.width, - offset = getVerticalOffset(), - position = $scope.dropdownPosition, - styles; - - // Automatically determine dropdown placement based on available space in viewport. - if (!position) { - position = (top > bot && root.height - hrect.bottom - MENU_PADDING < dropdownHeight) ? 'top' : 'bottom'; - } - // Adjust the width to account for the padding provided by `md-input-container` - if ($attrs.mdFloatingLabel) { - left += INPUT_PADDING; - width -= INPUT_PADDING * 2; - } - styles = { - left: left + 'px', - minWidth: width + 'px', - maxWidth: Math.max(hrect.right - root.left, root.right - hrect.left) - MENU_PADDING + 'px' - }; - - if (position === 'top') { - styles.top = 'auto'; - styles.bottom = bot + 'px'; - styles.maxHeight = Math.min(dropdownHeight, hrect.top - root.top - MENU_PADDING) + 'px'; - } else { - var bottomSpace = root.bottom - hrect.bottom - MENU_PADDING + $mdUtil.getViewportTop(); - - styles.top = (top - offset) + 'px'; - styles.bottom = 'auto'; - styles.maxHeight = Math.min(dropdownHeight, bottomSpace) + 'px'; - } - - elements.$.scrollContainer.css(styles); - $mdUtil.nextTick(correctHorizontalAlignment, false); - - /** - * Calculates the vertical offset for floating label examples to account for ngMessages - * @returns {number} - */ - function getVerticalOffset () { - var offset = 0; - var inputContainer = $element.find('md-input-container'); - if (inputContainer.length) { - var input = inputContainer.find('input'); - offset = inputContainer.prop('offsetHeight'); - offset -= input.prop('offsetTop'); - offset -= input.prop('offsetHeight'); - // add in the height left up top for the floating label text - offset += inputContainer.prop('offsetTop'); - } - return offset; - } - - /** - * Makes sure that the menu doesn't go off of the screen on either side. - */ - function correctHorizontalAlignment () { - var dropdown = elements.scrollContainer.getBoundingClientRect(), - styles = {}; - if (dropdown.right > root.right - MENU_PADDING) { - styles.left = (hrect.right - dropdown.width) + 'px'; - } - elements.$.scrollContainer.css(styles); - } - } - - /** - * Moves the dropdown menu to the body tag in order to avoid z-index and overflow issues. - */ - function moveDropdown () { - if (!elements.$.root.length) return; - $mdTheming(elements.$.scrollContainer); - elements.$.scrollContainer.detach(); - elements.$.root.append(elements.$.scrollContainer); - if ($animate.pin) $animate.pin(elements.$.scrollContainer, $rootElement); - } - - /** - * Sends focus to the input element. - */ - function focusInputElement () { - elements.input.focus(); - } - - /** - * Sets up any watchers used by autocomplete - */ - function configureWatchers () { - var wait = parseInt($scope.delay, 10) || 0; - - $attrs.$observe('disabled', function (value) { ctrl.isDisabled = $mdUtil.parseAttributeBoolean(value, false); }); - $attrs.$observe('required', function (value) { ctrl.isRequired = $mdUtil.parseAttributeBoolean(value, false); }); - $attrs.$observe('readonly', function (value) { ctrl.isReadonly = $mdUtil.parseAttributeBoolean(value, false); }); - - $scope.$watch('searchText', wait ? $mdUtil.debounce(handleSearchText, wait) : handleSearchText); - $scope.$watch('selectedItem', selectedItemChange); - - angular.element($window).on('resize', debouncedOnResize); - - $scope.$on('$destroy', cleanup); - } - - /** - * Removes any events or leftover elements created by this controller - */ - function cleanup () { - if (!ctrl.hidden) { - $mdUtil.enableScrolling(); - } - - angular.element($window).off('resize', debouncedOnResize); - - if ( elements ){ - var items = ['ul', 'scroller', 'scrollContainer', 'input']; - angular.forEach(items, function(key){ - elements.$[key].remove(); - }); - } - } - - /** - * Event handler to be called whenever the window resizes. - */ - function onWindowResize() { - if (!ctrl.hidden) { - positionDropdown(); - } - } - - /** - * Gathers all of the elements needed for this controller - */ - function gatherElements () { - - var snapWrap = gatherSnapWrap(); - - elements = { - main: $element[0], - scrollContainer: $element[0].querySelector('.md-virtual-repeat-container'), - scroller: $element[0].querySelector('.md-virtual-repeat-scroller'), - ul: $element.find('ul')[0], - input: $element.find('input')[0], - wrap: snapWrap.wrap, - snap: snapWrap.snap, - root: document.body - }; - - elements.li = elements.ul.getElementsByTagName('li'); - elements.$ = getAngularElements(elements); - - inputModelCtrl = elements.$.input.controller('ngModel'); - } - - /** - * Gathers the snap and wrap elements - * - */ - function gatherSnapWrap() { - var element; - var value; - for (element = $element; element.length; element = element.parent()) { - value = element.attr('md-autocomplete-snap'); - if (angular.isDefined(value)) break; - } - - if (element.length) { - return { - snap: element[0], - wrap: (value.toLowerCase() === 'width') ? element[0] : $element.find('md-autocomplete-wrap')[0] - }; - } - - var wrap = $element.find('md-autocomplete-wrap')[0]; - return { - snap: wrap, - wrap: wrap - }; - } - - /** - * Gathers angular-wrapped versions of each element - * @param elements - * @returns {{}} - */ - function getAngularElements (elements) { - var obj = {}; - for (var key in elements) { - if (elements.hasOwnProperty(key)) obj[ key ] = angular.element(elements[ key ]); - } - return obj; - } - - //-- event/change handlers - - /** - * Handles changes to the `hidden` property. - * @param hidden - * @param oldHidden - */ - function handleHiddenChange (hidden, oldHidden) { - if (!hidden && oldHidden) { - positionDropdown(); - - // Report in polite mode, because the screenreader should finish the default description of - // the input. element. - reportMessages(true, ReportType.Count | ReportType.Selected); - - if (elements) { - $mdUtil.disableScrollAround(elements.ul); - enableWrapScroll = disableElementScrollEvents(angular.element(elements.wrap)); - } - } else if (hidden && !oldHidden) { - $mdUtil.enableScrolling(); - - if (enableWrapScroll) { - enableWrapScroll(); - enableWrapScroll = null; - } - } - } - - /** - * Disables scrolling for a specific element - */ - function disableElementScrollEvents(element) { - - function preventDefault(e) { - e.preventDefault(); - } - - element.on('wheel', preventDefault); - element.on('touchmove', preventDefault); - - return function() { - element.off('wheel', preventDefault); - element.off('touchmove', preventDefault); - }; - } - - /** - * When the user mouses over the dropdown menu, ignore blur events. - */ - function onListEnter () { - noBlur = true; - } - - /** - * When the user's mouse leaves the menu, blur events may hide the menu again. - */ - function onListLeave () { - if (!hasFocus && !ctrl.hidden) elements.input.focus(); - noBlur = false; - ctrl.hidden = shouldHide(); - } - - /** - * When the mouse button is released, send focus back to the input field. - */ - function onMouseup () { - elements.input.focus(); - } - - /** - * Handles changes to the selected item. - * @param selectedItem - * @param previousSelectedItem - */ - function selectedItemChange (selectedItem, previousSelectedItem) { - - updateModelValidators(); - - if (selectedItem) { - getDisplayValue(selectedItem).then(function (val) { - $scope.searchText = val; - handleSelectedItemChange(selectedItem, previousSelectedItem); - }); - } else if (previousSelectedItem && $scope.searchText) { - getDisplayValue(previousSelectedItem).then(function(displayValue) { - // Clear the searchText, when the selectedItem is set to null. - // Do not clear the searchText, when the searchText isn't matching with the previous - // selected item. - if (angular.isString($scope.searchText) - && displayValue.toString().toLowerCase() === $scope.searchText.toLowerCase()) { - $scope.searchText = ''; - } - }); - } - - if (selectedItem !== previousSelectedItem) announceItemChange(); - } - - /** - * Use the user-defined expression to announce changes each time a new item is selected - */ - function announceItemChange () { - angular.isFunction($scope.itemChange) && $scope.itemChange(getItemAsNameVal($scope.selectedItem)); - } - - /** - * Use the user-defined expression to announce changes each time the search text is changed - */ - function announceTextChange () { - angular.isFunction($scope.textChange) && $scope.textChange(); - } - - /** - * Calls any external watchers listening for the selected item. Used in conjunction with - * `registerSelectedItemWatcher`. - * @param selectedItem - * @param previousSelectedItem - */ - function handleSelectedItemChange (selectedItem, previousSelectedItem) { - selectedItemWatchers.forEach(function (watcher) { watcher(selectedItem, previousSelectedItem); }); - } - - /** - * Register a function to be called when the selected item changes. - * @param cb - */ - function registerSelectedItemWatcher (cb) { - if (selectedItemWatchers.indexOf(cb) == -1) { - selectedItemWatchers.push(cb); - } - } - - /** - * Unregister a function previously registered for selected item changes. - * @param cb - */ - function unregisterSelectedItemWatcher (cb) { - var i = selectedItemWatchers.indexOf(cb); - if (i != -1) { - selectedItemWatchers.splice(i, 1); - } - } - - /** - * Handles changes to the searchText property. - * @param searchText - * @param previousSearchText - */ - function handleSearchText (searchText, previousSearchText) { - ctrl.index = getDefaultIndex(); - - // do nothing on init - if (searchText === previousSearchText) return; - - updateModelValidators(); - - getDisplayValue($scope.selectedItem).then(function (val) { - // clear selected item if search text no longer matches it - if (searchText !== val) { - $scope.selectedItem = null; - - - // trigger change event if available - if (searchText !== previousSearchText) announceTextChange(); - - // cancel results if search text is not long enough - if (!isMinLengthMet()) { - ctrl.matches = []; - - setLoading(false); - reportMessages(false, ReportType.Count); - - } else { - handleQuery(); - } - } - }); - - } - - /** - * Handles input blur event, determines if the dropdown should hide. - */ - function blur($event) { - hasFocus = false; - - if (!noBlur) { - ctrl.hidden = shouldHide(); - evalAttr('ngBlur', { $event: $event }); - } - } - - /** - * Force blur on input element - * @param forceBlur - */ - function doBlur(forceBlur) { - if (forceBlur) { - noBlur = false; - hasFocus = false; - } - elements.input.blur(); - } - - /** - * Handles input focus event, determines if the dropdown should show. - */ - function focus($event) { - hasFocus = true; - - if (isSearchable() && isMinLengthMet()) { - handleQuery(); - } - - ctrl.hidden = shouldHide(); - - evalAttr('ngFocus', { $event: $event }); - } - - /** - * Handles keyboard input. - * @param event - */ - function keydown (event) { - switch (event.keyCode) { - case $mdConstant.KEY_CODE.DOWN_ARROW: - if (ctrl.loading) return; - event.stopPropagation(); - event.preventDefault(); - ctrl.index = Math.min(ctrl.index + 1, ctrl.matches.length - 1); - updateScroll(); - reportMessages(false, ReportType.Selected); - break; - case $mdConstant.KEY_CODE.UP_ARROW: - if (ctrl.loading) return; - event.stopPropagation(); - event.preventDefault(); - ctrl.index = ctrl.index < 0 ? ctrl.matches.length - 1 : Math.max(0, ctrl.index - 1); - updateScroll(); - reportMessages(false, ReportType.Selected); - break; - case $mdConstant.KEY_CODE.TAB: - // If we hit tab, assume that we've left the list so it will close - onListLeave(); - - if (ctrl.hidden || ctrl.loading || ctrl.index < 0 || ctrl.matches.length < 1) return; - select(ctrl.index); - break; - case $mdConstant.KEY_CODE.ENTER: - if (ctrl.hidden || ctrl.loading || ctrl.index < 0 || ctrl.matches.length < 1) return; - if (hasSelection()) return; - event.stopPropagation(); - event.preventDefault(); - select(ctrl.index); - break; - case $mdConstant.KEY_CODE.ESCAPE: - event.preventDefault(); // Prevent browser from always clearing input - if (!shouldProcessEscape()) return; - event.stopPropagation(); - - clearSelectedItem(); - if ($scope.searchText && hasEscapeOption('clear')) { - clearSearchText(); - } - - // Manually hide (needed for mdNotFound support) - ctrl.hidden = true; - - if (hasEscapeOption('blur')) { - // Force the component to blur if they hit escape - doBlur(true); - } - - break; - default: - } - } - - //-- getters - - /** - * Returns the minimum length needed to display the dropdown. - * @returns {*} - */ - function getMinLength () { - return angular.isNumber($scope.minLength) ? $scope.minLength : 1; - } - - /** - * Returns the display value for an item. - * @param item - * @returns {*} - */ - function getDisplayValue (item) { - return $q.when(getItemText(item) || item).then(function(itemText) { - if (itemText && !angular.isString(itemText)) { - $log.warn('md-autocomplete: Could not resolve display value to a string. ' + - 'Please check the `md-item-text` attribute.'); - } - - return itemText; - }); - - /** - * Getter function to invoke user-defined expression (in the directive) - * to convert your object to a single string. - */ - function getItemText (item) { - return (item && $scope.itemText) ? $scope.itemText(getItemAsNameVal(item)) : null; - } - } - - /** - * Returns the locals object for compiling item templates. - * @param item - * @returns {{}} - */ - function getItemAsNameVal (item) { - if (!item) return undefined; - - var locals = {}; - if (ctrl.itemName) locals[ ctrl.itemName ] = item; - - return locals; - } - - /** - * Returns the default index based on whether or not autoselect is enabled. - * @returns {number} - */ - function getDefaultIndex () { - return $scope.autoselect ? 0 : -1; - } - - /** - * Sets the loading parameter and updates the hidden state. - * @param value {boolean} Whether or not the component is currently loading. - */ - function setLoading(value) { - if (ctrl.loading != value) { - ctrl.loading = value; - } - - // Always refresh the hidden variable as something else might have changed - ctrl.hidden = shouldHide(); - } - - /** - * Determines if the menu should be hidden. - * @returns {boolean} - */ - function shouldHide () { - if (!isSearchable()) return true; // Hide when not able to query - else return !shouldShow(); // Hide when the dropdown is not able to show. - } - - /** - * Determines whether the autocomplete is able to query within the current state. - * @returns {boolean} - */ - function isSearchable() { - if (ctrl.loading && !hasMatches()) return false; // No query when query is in progress. - else if (hasSelection()) return false; // No query if there is already a selection - else if (!hasFocus) return false; // No query if the input does not have focus - return true; - } - - /** - * Determines if the escape keydown should be processed - * @returns {boolean} - */ - function shouldProcessEscape() { - return hasEscapeOption('blur') || !ctrl.hidden || ctrl.loading || hasEscapeOption('clear') && $scope.searchText; - } - - /** - * Determines if an escape option is set - * @returns {boolean} - */ - function hasEscapeOption(option) { - return !$scope.escapeOptions || $scope.escapeOptions.toLowerCase().indexOf(option) !== -1; - } - - /** - * Determines if the menu should be shown. - * @returns {boolean} - */ - function shouldShow() { - return (isMinLengthMet() && hasMatches()) || notFoundVisible(); - } - - /** - * Returns true if the search text has matches. - * @returns {boolean} - */ - function hasMatches() { - return ctrl.matches.length ? true : false; - } - - /** - * Returns true if the autocomplete has a valid selection. - * @returns {boolean} - */ - function hasSelection() { - return ctrl.scope.selectedItem ? true : false; - } - - /** - * Returns true if the loading indicator is, or should be, visible. - * @returns {boolean} - */ - function loadingIsVisible() { - return ctrl.loading && !hasSelection(); - } - - /** - * Returns the display value of the current item. - * @returns {*} - */ - function getCurrentDisplayValue () { - return getDisplayValue(ctrl.matches[ ctrl.index ]); - } - - /** - * Determines if the minimum length is met by the search text. - * @returns {*} - */ - function isMinLengthMet () { - return ($scope.searchText || '').length >= getMinLength(); - } - - //-- actions - - /** - * Defines a public property with a handler and a default value. - * @param key - * @param handler - * @param value - */ - function defineProperty (key, handler, value) { - Object.defineProperty(ctrl, key, { - get: function () { return value; }, - set: function (newValue) { - var oldValue = value; - value = newValue; - handler(newValue, oldValue); - } - }); - } - - /** - * Selects the item at the given index. - * @param index - */ - function select (index) { - //-- force form to update state for validation - $mdUtil.nextTick(function () { - getDisplayValue(ctrl.matches[ index ]).then(function (val) { - var ngModel = elements.$.input.controller('ngModel'); - ngModel.$setViewValue(val); - ngModel.$render(); - }).finally(function () { - $scope.selectedItem = ctrl.matches[ index ]; - setLoading(false); - }); - }, false); - } - - /** - * Clears the searchText value and selected item. - */ - function clearValue () { - clearSelectedItem(); - clearSearchText(); - } - - /** - * Clears the selected item - */ - function clearSelectedItem () { - // Reset our variables - ctrl.index = 0; - ctrl.matches = []; - } - - /** - * Clears the searchText value - */ - function clearSearchText () { - // Set the loading to true so we don't see flashes of content. - // The flashing will only occur when an async request is running. - // So the loading process will stop when the results had been retrieved. - setLoading(true); - - $scope.searchText = ''; - - // Normally, triggering the change / input event is unnecessary, because the browser detects it properly. - // But some browsers are not detecting it properly, which means that we have to trigger the event. - // Using the `input` is not working properly, because for example IE11 is not supporting the `input` event. - // The `change` event is a good alternative and is supported by all supported browsers. - var eventObj = document.createEvent('CustomEvent'); - eventObj.initCustomEvent('change', true, true, { value: '' }); - elements.input.dispatchEvent(eventObj); - - // For some reason, firing the above event resets the value of $scope.searchText if - // $scope.searchText has a space character at the end, so we blank it one more time and then - // focus. - elements.input.blur(); - $scope.searchText = ''; - elements.input.focus(); - } - - /** - * Fetches the results for the provided search text. - * @param searchText - */ - function fetchResults (searchText) { - var items = $scope.$parent.$eval(itemExpr), - term = searchText.toLowerCase(), - isList = angular.isArray(items), - isPromise = !!items.then; // Every promise should contain a `then` property - - if (isList) onResultsRetrieved(items); - else if (isPromise) handleAsyncResults(items); - - function handleAsyncResults(items) { - if ( !items ) return; - - items = $q.when(items); - fetchesInProgress++; - setLoading(true); - - $mdUtil.nextTick(function () { - items - .then(onResultsRetrieved) - .finally(function(){ - if (--fetchesInProgress === 0) { - setLoading(false); - } - }); - },true, $scope); - } - - function onResultsRetrieved(matches) { - cache[term] = matches; - - // Just cache the results if the request is now outdated. - // The request becomes outdated, when the new searchText has changed during the result fetching. - if ((searchText || '') !== ($scope.searchText || '')) { - return; - } - - handleResults(matches); - } - } - - - /** - * Reports given message types to supported screenreaders. - * @param {boolean} isPolite Whether the announcement should be polite. - * @param {!number} types Message flags to be reported to the screenreader. - */ - function reportMessages(isPolite, types) { - - var politeness = isPolite ? 'polite' : 'assertive'; - var messages = []; - - if (types & ReportType.Selected && ctrl.index !== -1) { - messages.push(getCurrentDisplayValue()); - } - - if (types & ReportType.Count) { - messages.push($q.resolve(getCountMessage())); - } - - $q.all(messages).then(function(data) { - $mdLiveAnnouncer.announce(data.join(' '), politeness); - }); - - } - - /** - * Returns the ARIA message for how many results match the current query. - * @returns {*} - */ - function getCountMessage () { - switch (ctrl.matches.length) { - case 0: - return 'There are no matches available.'; - case 1: - return 'There is 1 match available.'; - default: - return 'There are ' + ctrl.matches.length + ' matches available.'; - } - } - - /** - * Makes sure that the focused element is within view. - */ - function updateScroll () { - if (!elements.li[0]) return; - var height = elements.li[0].offsetHeight, - top = height * ctrl.index, - bot = top + height, - hgt = elements.scroller.clientHeight, - scrollTop = elements.scroller.scrollTop; - if (top < scrollTop) { - scrollTo(top); - } else if (bot > scrollTop + hgt) { - scrollTo(bot - hgt); - } - } - - function isPromiseFetching() { - return fetchesInProgress !== 0; - } - - function scrollTo (offset) { - elements.$.scrollContainer.controller('mdVirtualRepeatContainer').scrollTo(offset); - } - - function notFoundVisible () { - var textLength = (ctrl.scope.searchText || '').length; - - return ctrl.hasNotFound && !hasMatches() && (!ctrl.loading || isPromiseFetching()) && textLength >= getMinLength() && (hasFocus || noBlur) && !hasSelection(); - } - - /** - * Starts the query to gather the results for the current searchText. Attempts to return cached - * results first, then forwards the process to `fetchResults` if necessary. - */ - function handleQuery () { - var searchText = $scope.searchText || ''; - var term = searchText.toLowerCase(); - - // If caching is enabled and the current searchText is stored in the cache - if (!$scope.noCache && cache[term]) { - // The results should be handled as same as a normal un-cached request does. - handleResults(cache[term]); - } else { - fetchResults(searchText); - } - - ctrl.hidden = shouldHide(); - } - - /** - * Handles the retrieved results by showing them in the autocompletes dropdown. - * @param results Retrieved results - */ - function handleResults(results) { - ctrl.matches = results; - ctrl.hidden = shouldHide(); - - // If loading is in progress, then we'll end the progress. This is needed for example, - // when the `clear` button was clicked, because there we always show the loading process, to prevent flashing. - if (ctrl.loading) setLoading(false); - - if ($scope.selectOnMatch) selectItemOnMatch(); - - positionDropdown(); - reportMessages(true, ReportType.Count); - } - - /** - * If there is only one matching item and the search text matches its display value exactly, - * automatically select that item. Note: This function is only called if the user uses the - * `md-select-on-match` flag. - */ - function selectItemOnMatch () { - var searchText = $scope.searchText, - matches = ctrl.matches, - item = matches[ 0 ]; - if (matches.length === 1) getDisplayValue(item).then(function (displayValue) { - var isMatching = searchText == displayValue; - if ($scope.matchInsensitive && !isMatching) { - isMatching = searchText.toLowerCase() == displayValue.toLowerCase(); - } - - if (isMatching) select(0); - }); - } - - /** - * Evaluates an attribute expression against the parent scope. - * @param {String} attr Name of the attribute to be evaluated. - * @param {Object?} locals Properties to be injected into the evaluation context. - */ - function evalAttr(attr, locals) { - if ($attrs[attr]) { - $scope.$parent.$eval($attrs[attr], locals || {}); - } - } - -} - - -MdAutocomplete['$inject'] = ["$$mdSvgRegistry"];angular - .module('material.components.autocomplete') - .directive('mdAutocomplete', MdAutocomplete); - -/** - * @ngdoc directive - * @name mdAutocomplete - * @module material.components.autocomplete - * - * @description - * `<md-autocomplete>` is a special input component with a drop-down of all possible matches to a - * custom query. This component allows you to provide real-time suggestions as the user types - * in the input area. - * - * To start, you will need to specify the required parameters and provide a template for your - * results. The content inside `md-autocomplete` will be treated as a template. - * - * In more complex cases, you may want to include other content such as a message to display when - * no matches were found. You can do this by wrapping your template in `md-item-template` and - * adding a tag for `md-not-found`. An example of this is shown below. - * - * To reset the displayed value you must clear both values for `md-search-text` and `md-selected-item`. - * - * ### Validation - * - * You can use `ng-messages` to include validation the same way that you would normally validate; - * however, if you want to replicate a standard input with a floating label, you will have to - * do the following: - * - * - Make sure that your template is wrapped in `md-item-template` - * - Add your `ng-messages` code inside of `md-autocomplete` - * - Add your validation properties to `md-autocomplete` (ie. `required`) - * - Add a `name` to `md-autocomplete` (to be used on the generated `input`) - * - * There is an example below of how this should look. - * - * ### Snapping Drop-Down - * - * You can cause the autocomplete drop-down to snap to an ancestor element by applying the - * `md-autocomplete-snap` attribute to that element. You can also snap to the width of - * the `md-autocomplete-snap` element by setting the attribute's value to `width` - * (ie. `md-autocomplete-snap="width"`). - * - * ### Notes - * - * **Autocomplete Dropdown Items Rendering** - * - * The `md-autocomplete` uses the the <a ng-href="api/directive/mdVirtualRepeatContainer">VirtualRepeat</a> - * directive for displaying the results inside of the dropdown.<br/> - * - * > When encountering issues regarding the item template please take a look at the - * <a ng-href="api/directive/mdVirtualRepeatContainer">VirtualRepeatContainer</a> documentation. - * - * **Autocomplete inside of a Virtual Repeat** - * - * When using the `md-autocomplete` directive inside of a - * <a ng-href="api/directive/mdVirtualRepeatContainer">VirtualRepeatContainer</a> the dropdown items might - * not update properly, because caching of the results is enabled by default. - * - * The autocomplete will then show invalid dropdown items, because the VirtualRepeat only updates the - * scope bindings, rather than re-creating the `md-autocomplete` and the previous cached results will be used. - * - * > To avoid such problems ensure that the autocomplete does not cache any results. - * - * <hljs lang="html"> - * <md-autocomplete - * md-no-cache="true" - * md-selected-item="selectedItem" - * md-items="item in items" - * md-search-text="searchText" - * md-item-text="item.display"> - * <span>{{ item.display }}</span> - * </md-autocomplete> - * </hljs> - * - * - * - * @param {expression} md-items An expression in the format of `item in results` to iterate over - * matches for your search.<br/><br/> - * The `results` expression can be also a function, which returns the results synchronously - * or asynchronously (per Promise) - * @param {expression=} md-selected-item-change An expression to be run each time a new item is - * selected - * @param {expression=} md-search-text-change An expression to be run each time the search text - * updates - * @param {expression=} md-search-text A model to bind the search query text to - * @param {object=} md-selected-item A model to bind the selected item to - * @param {expression=} md-item-text An expression that will convert your object to a single string. - * @param {string=} placeholder Placeholder text that will be forwarded to the input. - * @param {boolean=} md-no-cache Disables the internal caching that happens in autocomplete - * @param {boolean=} ng-disabled Determines whether or not to disable the input field - * @param {boolean=} md-require-match When set to true, the autocomplete will add a validator, - * which will evaluate to false, when no item is currently selected. - * @param {number=} md-min-length Specifies the minimum length of text before autocomplete will - * make suggestions - * @param {number=} md-delay Specifies the amount of time (in milliseconds) to wait before looking - * for results - * @param {boolean=} md-clear-button Whether the clear button for the autocomplete input should show up or not. - * @param {boolean=} md-autofocus If true, the autocomplete will be automatically focused when a `$mdDialog`, - * `$mdBottomsheet` or `$mdSidenav`, which contains the autocomplete, is opening. <br/><br/> - * Also the autocomplete will immediately focus the input element. - * @param {boolean=} md-no-asterisk When present, asterisk will not be appended to the floating label - * @param {boolean=} md-autoselect If set to true, the first item will be automatically selected - * in the dropdown upon open. - * @param {string=} md-menu-class This will be applied to the dropdown menu for styling - * @param {string=} md-floating-label This will add a floating label to autocomplete and wrap it in - * `md-input-container` - * @param {string=} md-input-name The name attribute given to the input element to be used with - * FormController - * @param {string=} md-select-on-focus When present the inputs text will be automatically selected - * on focus. - * @param {string=} md-input-id An ID to be added to the input element - * @param {number=} md-input-minlength The minimum length for the input's value for validation - * @param {number=} md-input-maxlength The maximum length for the input's value for validation - * @param {boolean=} md-select-on-match When set, autocomplete will automatically select exact - * the item if the search text is an exact match. <br/><br/> - * Exact match means that there is only one match showing up. - * @param {boolean=} md-match-case-insensitive When set and using `md-select-on-match`, autocomplete - * will select on case-insensitive match - * @param {string=} md-escape-options Override escape key logic. Default is `blur clear`.<br/> - * Options: `blur | clear`, `none` - * @param {string=} md-dropdown-items Specifies the maximum amount of items to be shown in - * the dropdown.<br/><br/> - * When the dropdown doesn't fit into the viewport, the dropdown will shrink - * as less as possible. - * @param {string=} md-dropdown-position Overrides the default dropdown position. Options: `top`, `bottom`. - * @param {string=} ng-trim If set to false, the search text will be not trimmed automatically. - * Defaults to true. - * @param {string=} ng-pattern Adds the pattern validator to the ngModel of the search text. - * [ngPattern Directive](https://docs.angularjs.org/api/ng/directive/ngPattern) - * - * @usage - * ### Basic Example - * <hljs lang="html"> - * <md-autocomplete - * md-selected-item="selectedItem" - * md-search-text="searchText" - * md-items="item in getMatches(searchText)" - * md-item-text="item.display"> - * <span md-highlight-text="searchText">{{item.display}}</span> - * </md-autocomplete> - * </hljs> - * - * ### Example with "not found" message - * <hljs lang="html"> - * <md-autocomplete - * md-selected-item="selectedItem" - * md-search-text="searchText" - * md-items="item in getMatches(searchText)" - * md-item-text="item.display"> - * <md-item-template> - * <span md-highlight-text="searchText">{{item.display}}</span> - * </md-item-template> - * <md-not-found> - * No matches found. - * </md-not-found> - * </md-autocomplete> - * </hljs> - * - * In this example, our code utilizes `md-item-template` and `md-not-found` to specify the - * different parts that make up our component. - * - * ### Clear button for the input - * By default, for floating label autocomplete's the clear button is not showing up - * ([See specs](https://material.google.com/components/text-fields.html#text-fields-auto-complete-text-field)) - * - * Nevertheless, developers are able to explicitly toggle the clear button for all types of autocomplete's. - * - * <hljs lang="html"> - * <md-autocomplete ... md-clear-button="true"></md-autocomplete> - * <md-autocomplete ... md-clear-button="false"></md-autocomplete> - * </hljs> - * - * ### Example with validation - * <hljs lang="html"> - * <form name="autocompleteForm"> - * <md-autocomplete - * required - * md-input-name="autocomplete" - * md-selected-item="selectedItem" - * md-search-text="searchText" - * md-items="item in getMatches(searchText)" - * md-item-text="item.display"> - * <md-item-template> - * <span md-highlight-text="searchText">{{item.display}}</span> - * </md-item-template> - * <div ng-messages="autocompleteForm.autocomplete.$error"> - * <div ng-message="required">This field is required</div> - * </div> - * </md-autocomplete> - * </form> - * </hljs> - * - * In this example, our code utilizes `md-item-template` and `ng-messages` to specify - * input validation for the field. - * - * ### Asynchronous Results - * The autocomplete items expression also supports promises, which will resolve with the query results. - * - * <hljs lang="js"> - * function AppController($scope, $http) { - * $scope.query = function(searchText) { - * return $http - * .get(BACKEND_URL + '/items/' + searchText) - * .then(function(data) { - * // Map the response object to the data object. - * return data; - * }); - * }; - * } - * </hljs> - * - * <hljs lang="html"> - * <md-autocomplete - * md-selected-item="selectedItem" - * md-search-text="searchText" - * md-items="item in query(searchText)"> - * <md-item-template> - * <span md-highlight-text="searchText">{{item}}</span> - * </md-item-template> - * </md-autocomplete> - * </hljs> - * - */ - -function MdAutocomplete ($$mdSvgRegistry) { - - return { - controller: 'MdAutocompleteCtrl', - controllerAs: '$mdAutocompleteCtrl', - scope: { - inputName: '@mdInputName', - inputMinlength: '@mdInputMinlength', - inputMaxlength: '@mdInputMaxlength', - searchText: '=?mdSearchText', - selectedItem: '=?mdSelectedItem', - itemsExpr: '@mdItems', - itemText: '&mdItemText', - placeholder: '@placeholder', - noCache: '=?mdNoCache', - requireMatch: '=?mdRequireMatch', - selectOnMatch: '=?mdSelectOnMatch', - matchInsensitive: '=?mdMatchCaseInsensitive', - itemChange: '&?mdSelectedItemChange', - textChange: '&?mdSearchTextChange', - minLength: '=?mdMinLength', - delay: '=?mdDelay', - autofocus: '=?mdAutofocus', - floatingLabel: '@?mdFloatingLabel', - autoselect: '=?mdAutoselect', - menuClass: '@?mdMenuClass', - inputId: '@?mdInputId', - escapeOptions: '@?mdEscapeOptions', - dropdownItems: '=?mdDropdownItems', - dropdownPosition: '@?mdDropdownPosition', - clearButton: '=?mdClearButton' - }, - compile: function(tElement, tAttrs) { - var attributes = ['md-select-on-focus', 'md-no-asterisk', 'ng-trim', 'ng-pattern']; - var input = tElement.find('input'); - - attributes.forEach(function(attribute) { - var attrValue = tAttrs[tAttrs.$normalize(attribute)]; - - if (attrValue !== null) { - input.attr(attribute, attrValue); - } - }); - - return function(scope, element, attrs, ctrl) { - // Retrieve the state of using a md-not-found template by using our attribute, which will - // be added to the element in the template function. - ctrl.hasNotFound = !!element.attr('md-has-not-found'); - - // By default the inset autocomplete should show the clear button when not explicitly overwritten. - if (!angular.isDefined(attrs.mdClearButton) && !scope.floatingLabel) { - scope.clearButton = true; - } - } - }, - template: function (element, attr) { - var noItemsTemplate = getNoItemsTemplate(), - itemTemplate = getItemTemplate(), - leftover = element.html(), - tabindex = attr.tabindex; - - // Set our attribute for the link function above which runs later. - // We will set an attribute, because otherwise the stored variables will be trashed when - // removing the element is hidden while retrieving the template. For example when using ngIf. - if (noItemsTemplate) element.attr('md-has-not-found', true); - - // Always set our tabindex of the autocomplete directive to -1, because our input - // will hold the actual tabindex. - element.attr('tabindex', '-1'); - - return '\ - <md-autocomplete-wrap\ - ng-class="{ \'md-whiteframe-z1\': !floatingLabel, \ - \'md-menu-showing\': !$mdAutocompleteCtrl.hidden, \ - \'md-show-clear-button\': !!clearButton }">\ - ' + getInputElement() + '\ - ' + getClearButton() + '\ - <md-progress-linear\ - class="' + (attr.mdFloatingLabel ? 'md-inline' : '') + '"\ - ng-if="$mdAutocompleteCtrl.loadingIsVisible()"\ - md-mode="indeterminate"></md-progress-linear>\ - <md-virtual-repeat-container\ - md-auto-shrink\ - md-auto-shrink-min="1"\ - ng-mouseenter="$mdAutocompleteCtrl.listEnter()"\ - ng-mouseleave="$mdAutocompleteCtrl.listLeave()"\ - ng-mouseup="$mdAutocompleteCtrl.mouseUp()"\ - ng-hide="$mdAutocompleteCtrl.hidden"\ - class="md-autocomplete-suggestions-container md-whiteframe-z1"\ - ng-class="{ \'md-not-found\': $mdAutocompleteCtrl.notFoundVisible() }"\ - role="presentation">\ - <ul class="md-autocomplete-suggestions"\ - ng-class="::menuClass"\ - id="ul-{{$mdAutocompleteCtrl.id}}">\ - <li md-virtual-repeat="item in $mdAutocompleteCtrl.matches"\ - ng-class="{ selected: $index === $mdAutocompleteCtrl.index }"\ - ng-click="$mdAutocompleteCtrl.select($index)"\ - md-extra-name="$mdAutocompleteCtrl.itemName">\ - ' + itemTemplate + '\ - </li>' + noItemsTemplate + '\ - </ul>\ - </md-virtual-repeat-container>\ - </md-autocomplete-wrap>'; - - function getItemTemplate() { - var templateTag = element.find('md-item-template').detach(), - html = templateTag.length ? templateTag.html() : element.html(); - if (!templateTag.length) element.empty(); - return '<md-autocomplete-parent-scope md-autocomplete-replace>' + html + '</md-autocomplete-parent-scope>'; - } - - function getNoItemsTemplate() { - var templateTag = element.find('md-not-found').detach(), - template = templateTag.length ? templateTag.html() : ''; - return template - ? '<li ng-if="$mdAutocompleteCtrl.notFoundVisible()"\ - md-autocomplete-parent-scope>' + template + '</li>' - : ''; - - } - - function getInputElement () { - if (attr.mdFloatingLabel) { - return '\ - <md-input-container ng-if="floatingLabel">\ - <label>{{floatingLabel}}</label>\ - <input type="search"\ - ' + (tabindex != null ? 'tabindex="' + tabindex + '"' : '') + '\ - id="{{ inputId || \'fl-input-\' + $mdAutocompleteCtrl.id }}"\ - name="{{inputName}}"\ - autocomplete="off"\ - ng-required="$mdAutocompleteCtrl.isRequired"\ - ng-readonly="$mdAutocompleteCtrl.isReadonly"\ - ng-minlength="inputMinlength"\ - ng-maxlength="inputMaxlength"\ - ng-disabled="$mdAutocompleteCtrl.isDisabled"\ - ng-model="$mdAutocompleteCtrl.scope.searchText"\ - ng-model-options="{ allowInvalid: true }"\ - ng-keydown="$mdAutocompleteCtrl.keydown($event)"\ - ng-blur="$mdAutocompleteCtrl.blur($event)"\ - ng-focus="$mdAutocompleteCtrl.focus($event)"\ - aria-owns="ul-{{$mdAutocompleteCtrl.id}}"\ - aria-label="{{floatingLabel}}"\ - aria-autocomplete="list"\ - role="combobox"\ - aria-haspopup="true"\ - aria-activedescendant=""\ - aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"/>\ - <div md-autocomplete-parent-scope md-autocomplete-replace>' + leftover + '</div>\ - </md-input-container>'; - } else { - return '\ - <input type="search"\ - ' + (tabindex != null ? 'tabindex="' + tabindex + '"' : '') + '\ - id="{{ inputId || \'input-\' + $mdAutocompleteCtrl.id }}"\ - name="{{inputName}}"\ - ng-if="!floatingLabel"\ - autocomplete="off"\ - ng-required="$mdAutocompleteCtrl.isRequired"\ - ng-disabled="$mdAutocompleteCtrl.isDisabled"\ - ng-readonly="$mdAutocompleteCtrl.isReadonly"\ - ng-minlength="inputMinlength"\ - ng-maxlength="inputMaxlength"\ - ng-model="$mdAutocompleteCtrl.scope.searchText"\ - ng-keydown="$mdAutocompleteCtrl.keydown($event)"\ - ng-blur="$mdAutocompleteCtrl.blur($event)"\ - ng-focus="$mdAutocompleteCtrl.focus($event)"\ - placeholder="{{placeholder}}"\ - aria-owns="ul-{{$mdAutocompleteCtrl.id}}"\ - aria-label="{{placeholder}}"\ - aria-autocomplete="list"\ - role="combobox"\ - aria-haspopup="true"\ - aria-activedescendant=""\ - aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"/>'; - } - } - - function getClearButton() { - return '' + - '<button ' + - 'type="button" ' + - 'aria-label="Clear Input" ' + - 'tabindex="-1" ' + - 'ng-if="clearButton && $mdAutocompleteCtrl.scope.searchText && !$mdAutocompleteCtrl.isDisabled" ' + - 'ng-click="$mdAutocompleteCtrl.clear($event)">' + - '<md-icon md-svg-src="' + $$mdSvgRegistry.mdClose + '"></md-icon>' + - '</button>'; - } - } - }; -} - - -MdAutocompleteItemScopeDirective['$inject'] = ["$compile", "$mdUtil"];angular - .module('material.components.autocomplete') - .directive('mdAutocompleteParentScope', MdAutocompleteItemScopeDirective); - -function MdAutocompleteItemScopeDirective($compile, $mdUtil) { - return { - restrict: 'AE', - compile: compile, - terminal: true, - transclude: 'element' - }; - - function compile(tElement, tAttr, transclude) { - return function postLink(scope, element, attr) { - var ctrl = scope.$mdAutocompleteCtrl; - var newScope = ctrl.parent.$new(); - var itemName = ctrl.itemName; - - // Watch for changes to our scope's variables and copy them to the new scope - watchVariable('$index', '$index'); - watchVariable('item', itemName); - - // Ensure that $digest calls on our scope trigger $digest on newScope. - connectScopes(); - - // Link the element against newScope. - transclude(newScope, function(clone) { - element.after(clone); - }); - - /** - * Creates a watcher for variables that are copied from the parent scope - * @param variable - * @param alias - */ - function watchVariable(variable, alias) { - newScope[alias] = scope[variable]; - - scope.$watch(variable, function(value) { - $mdUtil.nextTick(function() { - newScope[alias] = value; - }); - }); - } - - /** - * Creates watchers on scope and newScope that ensure that for any - * $digest of scope, newScope is also $digested. - */ - function connectScopes() { - var scopeDigesting = false; - var newScopeDigesting = false; - - scope.$watch(function() { - if (newScopeDigesting || scopeDigesting) { - return; - } - - scopeDigesting = true; - scope.$$postDigest(function() { - if (!newScopeDigesting) { - newScope.$digest(); - } - - scopeDigesting = newScopeDigesting = false; - }); - }); - - newScope.$watch(function() { - newScopeDigesting = true; - }); - } - }; - } -} - -MdHighlightCtrl['$inject'] = ["$scope", "$element", "$attrs"];angular - .module('material.components.autocomplete') - .controller('MdHighlightCtrl', MdHighlightCtrl); - -function MdHighlightCtrl ($scope, $element, $attrs) { - this.$scope = $scope; - this.$element = $element; - this.$attrs = $attrs; - - // Cache the Regex to avoid rebuilding each time. - this.regex = null; -} - -MdHighlightCtrl.prototype.init = function(unsafeTermFn, unsafeContentFn) { - - this.flags = this.$attrs.mdHighlightFlags || ''; - - this.unregisterFn = this.$scope.$watch(function($scope) { - return { - term: unsafeTermFn($scope), - contentText: unsafeContentFn($scope) - }; - }.bind(this), this.onRender.bind(this), true); - - this.$element.on('$destroy', this.unregisterFn); -}; - -/** - * Triggered once a new change has been recognized and the highlighted - * text needs to be updated. - */ -MdHighlightCtrl.prototype.onRender = function(state, prevState) { - - var contentText = state.contentText; - - /* Update the regex if it's outdated, because we don't want to rebuilt it constantly. */ - if (this.regex === null || state.term !== prevState.term) { - this.regex = this.createRegex(state.term, this.flags); - } - - /* If a term is available apply the regex to the content */ - if (state.term) { - this.applyRegex(contentText); - } else { - this.$element.text(contentText); - } - -}; - -/** - * Decomposes the specified text into different tokens (whether match or not). - * Breaking down the string guarantees proper XSS protection due to the native browser - * escaping of unsafe text. - */ -MdHighlightCtrl.prototype.applyRegex = function(text) { - var tokens = this.resolveTokens(text); - - this.$element.empty(); - - tokens.forEach(function (token) { - - if (token.isMatch) { - var tokenEl = angular.element('<span class="highlight">').text(token.text); - - this.$element.append(tokenEl); - } else { - this.$element.append(document.createTextNode(token)); - } - - }.bind(this)); - -}; - - /** - * Decomposes the specified text into different tokens by running the regex against the text. - */ -MdHighlightCtrl.prototype.resolveTokens = function(string) { - var tokens = []; - var lastIndex = 0; - - // Use replace here, because it supports global and single regular expressions at same time. - string.replace(this.regex, function(match, index) { - appendToken(lastIndex, index); - - tokens.push({ - text: match, - isMatch: true - }); - - lastIndex = index + match.length; - }); - - // Append the missing text as a token. - appendToken(lastIndex); - - return tokens; - - function appendToken(from, to) { - var targetText = string.slice(from, to); - targetText && tokens.push(targetText); - } -}; - -/** Creates a regex for the specified text with the given flags. */ -MdHighlightCtrl.prototype.createRegex = function(term, flags) { - var startFlag = '', endFlag = ''; - var regexTerm = this.sanitizeRegex(term); - - if (flags.indexOf('^') >= 0) startFlag = '^'; - if (flags.indexOf('$') >= 0) endFlag = '$'; - - return new RegExp(startFlag + regexTerm + endFlag, flags.replace(/[$\^]/g, '')); -}; - -/** Sanitizes a regex by removing all common RegExp identifiers */ -MdHighlightCtrl.prototype.sanitizeRegex = function(term) { - return term && term.toString().replace(/[\\\^\$\*\+\?\.\(\)\|\{}\[\]]/g, '\\$&'); -}; - - -MdHighlight['$inject'] = ["$interpolate", "$parse"];angular - .module('material.components.autocomplete') - .directive('mdHighlightText', MdHighlight); - -/** - * @ngdoc directive - * @name mdHighlightText - * @module material.components.autocomplete - * - * @description - * The `md-highlight-text` directive allows you to specify text that should be highlighted within - * an element. Highlighted text will be wrapped in `<span class="highlight"></span>` which can - * be styled through CSS. Please note that child elements may not be used with this directive. - * - * @param {string} md-highlight-text A model to be searched for - * @param {string=} md-highlight-flags A list of flags (loosely based on JavaScript RexExp flags). - * #### **Supported flags**: - * - `g`: Find all matches within the provided text - * - `i`: Ignore case when searching for matches - * - `$`: Only match if the text ends with the search term - * - `^`: Only match if the text begins with the search term - * - * @usage - * <hljs lang="html"> - * <input placeholder="Enter a search term..." ng-model="searchTerm" type="text" /> - * <ul> - * <li ng-repeat="result in results" md-highlight-text="searchTerm"> - * {{result.text}} - * </li> - * </ul> - * </hljs> - */ - -function MdHighlight ($interpolate, $parse) { - return { - terminal: true, - controller: 'MdHighlightCtrl', - compile: function mdHighlightCompile(tElement, tAttr) { - var termExpr = $parse(tAttr.mdHighlightText); - var unsafeContentExpr = $interpolate(tElement.html()); - - return function mdHighlightLink(scope, element, attr, ctrl) { - ctrl.init(termExpr, unsafeContentExpr); - }; - } - }; -} - -ngmaterial.components.autocomplete = angular.module("material.components.autocomplete");
\ No newline at end of file |