Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* __PrimeFaces AutoComplete Widget__
*
* AutoComplete provides live suggestions while the user is entering text
*
* @typedef {"blank" | "current"} PrimeFaces.widget.AutoComplete.DropdownMode Specifies the behavior of the dropdown
* button.
* - `blank`: Sends an empty string.
* - `current`: Send the input value.
*
* @typedef {"keyup" | "enter"} PrimeFaces.widget.AutoComplete.QueryEvent Event to initiate the autocomplete search.
* - `enter`: Starts the search for suggestion items when the enter key is pressed.
* - `keyup`: Starts the search for suggestion items as soon as a key is released.
*
* @typedef {"server" | "client" | "hybrid"} PrimeFaces.widget.AutoComplete.QueryMode Specifies whether filter requests
* are evaluated by the client's browser or whether they are sent to the server.
*
* @typedef PrimeFaces.widget.AutoComplete.OnChangeCallback Client side callback to invoke when value changes.
* @param {JQuery} PrimeFaces.widget.AutoComplete.OnChangeCallback.input (Input) element on which the change occurred.
*
* @prop {boolean} active Whether the autocomplete is active.
* @prop {Record} [cache] The cache for the results of an autocomplete search.
* @prop {number} [cacheTimeout] The set-interval timer ID for the cache timeout.
* @prop {boolean} [checkMatchedItem] Whether the click event is fired on the selected items when a `blur` occurs.
* @prop {number} [colspan] Column span count for the options in the overlay panel with the available completion items.
* @prop {string} [currentGroup] Current option group when creating the options in the overlay with the available
* completion items.
* @prop {string} currentInputValue Current value in the input field where the user can search for completion items.
* @prop {string[]} [currentItems] Currently selected items, when `forceSelection` is enabled.
* @prop {string} [currentText] Text currently entered in the input field.
* @prop {JQuery} dropdown The DOM element for the container with the dropdown suggestions.
* @prop {PrimeFaces.UnbindCallback} [hideOverlayHandler] Unbind callback for the hide overlay handler.
* @prop {JQuery} input The DOM element for the input element.
* @prop {boolean} isDynamicLoaded If dynamic loading is enabled, whether the content was loaded already.
* @prop {boolean} isTabPressed Whether the tab key is currently pressed.
* @prop {JQuery} hinput The DOM element for the hidden input with the selected value.
* @prop {JQuery} inputContainer When multiple mode is enabled that allows multiple items to be selected: The DOM
* element of the container with the input element used to enter text and search for an item to select.
* @prop {boolean} [isSearchWithDropdown] Whether to use a drop down menu when searching for completion options.
* @prop {JQuery} [items] The DOM elements for the suggestion items.
* @prop {JQuery} itemtip The DOM element for the tooltip of a suggestion item.
* @prop {boolean} [itemClick] Whether an item was clicked.
* @prop {JQuery} [itemContainer] The DOM element for the container with the suggestion items.
* @prop {boolean} [itemSelectedWithEnter] Whether an item was selected via the enter key.
* @prop {JQuery} [multiItemContainer] The DOM element for the container with multiple selection items.
* @prop {JQuery} panel The DOM element for the overlay panel with the suggestion items.
* @prop {string} panelId The client ID of the overlay panel with the suggestion items.
* @prop {string} placeholder Placeholder shown in the input field when no text is entered.
* @prop {boolean} [preventInputChangeEvent] Whether to suppress the change event when the input's value changes.
* @prop {string} [previousText] Text previously entered in the input field.
* @prop {boolean} [querying] Whether an AJAX request for the autocompletion items is currently in progress.
* @prop {PrimeFaces.UnbindCallback} [resizeHandler] Unbind callback for the resize handler.
* @prop {PrimeFaces.UnbindCallback} [scrollHandler] Unbind callback for the scroll handler.
* @prop {number} requestId Tracking number to make sure search requests match up in query mode
* @prop {JQuery} status The DOM element for the autocomplete status ARIA element.
* @prop {boolean} suppressInput Whether key input events should be ignored currently.
* @prop {number} [timeout] Timeout ID for the timer used to clear the autocompletion cache in regular
* intervals.
* @prop {boolean} touchToDropdownButton Whether a touch is made on the dropdown button.
* @prop {PrimeFaces.CssTransitionHandler | null} [transition] Handler for CSS transitions used by this widget.
* @prop {string} wrapperStartTag The starting HTML with the wrapper element of the suggestions box.
* @prop {string} wrapperEndTag The finishing HTML with the wrapper element of the suggestions box.
*
* @interface {PrimeFaces.widget.AutoCompleteCfg} cfg The configuration for the {@link AutoComplete| AutoComplete widget}.
* You can access this configuration via {@link PrimeFaces.widget.BaseWidget.cfg|BaseWidget.cfg}. Please note that this
* configuration is usually meant to be read-only and should not be modified.
* @extends {PrimeFaces.widget.BaseWidgetCfg} cfg
*
* @prop {boolean} cfg.active Whether autocompletion search is initially active.
* @prop {string} cfg.appendTo ID of the container to which the suggestion box is appended.
* @prop {string} cfg.atPos Defines which position on the target element to align the positioned element against.
* @prop {boolean} cfg.autoHighlight Highlights the first suggested item automatically.
* @prop {boolean} cfg.autoSelection Defines if auto selection of items that are equal to the typed input is enabled. If
* `true`, an item that is equal to the typed input is selected.
* @prop {boolean} cfg.cache When enabled autocomplete caches the searched result list.
* @prop {number} cfg.cacheTimeout Timeout in milliseconds value for cached results.
* @prop {number} cfg.delay The delay in milliseconds before an autocomplete search is triggered.
* @prop {PrimeFaces.widget.AutoComplete.DropdownMode} cfg.dropdownMode Specifies the behavior of the dropdown button.
* @prop {boolean} cfg.dynamic Defines if dynamic loading is enabled for the element's panel. If the value is `true`,
* the overlay is not rendered on page load to improve performance.
* @prop {string} cfg.emptyMessage Text to display when there is no data to display.
* @prop {boolean} cfg.escape Whether the text of the suggestion items is escaped for HTML.
* @prop {boolean} cfg.hasFooter Whether a footer facet is present.
* @prop {boolean} cfg.forceSelection Whether one of the available suggestion items is forced to be preselected.
* @prop {boolean} cfg.grouping Whether suggestion items are grouped.
* @prop {boolean} cfg.itemtip Whether a tooltip is shown for the suggestion items.
* @prop {string} cfg.itemtipAtPosition Position of item corner relative to item tip.
* @prop {string} cfg.itemtipMyPosition Position of itemtip corner relative to item.
* @prop {number} cfg.minLength Minimum length before an autocomplete search is triggered.
* @prop {boolean} cfg.multiple When `true`, enables multiple selection.
* @prop {string} cfg.myPos Defines which position on the element being positioned to align with the target element.
* @prop {PrimeFaces.widget.AutoComplete.OnChangeCallback} cfg.onChange Client side callback to invoke when value
* changes.
* @prop {PrimeFaces.widget.AutoComplete.QueryEvent} cfg.queryEvent Event to initiate the the autocomplete search.
* @prop {PrimeFaces.widget.AutoComplete.QueryMode} cfg.queryMode Specifies query mode, whether autocomplete contacts
* the server.
* @prop {string} cfg.resultsMessage Hint text for screen readers to provide information about the search results.
* @prop {number} cfg.selectLimit Limits the number of simultaneously selected items. Default is unlimited.
* @prop {number} cfg.scrollHeight Height of the container with the suggestion items.
* @prop {boolean} cfg.unique Ensures uniqueness of the selected items.
* @prop {string} cfg.completeEndpoint REST endpoint for fetching autocomplete suggestions. Takes precedence over the
* bean command specified via `completeMethod` on the component.
* @prop {string} cfg.moreText The text shown in the panel when the number of suggestions is greater than `maxResults`.
*/
PrimeFaces.widget.AutoComplete = PrimeFaces.widget.BaseWidget.extend({
/**
* @override
* @inheritdoc
* @param {PrimeFaces.PartialWidgetCfg} cfg
*/
init: function(cfg) {
this._super(cfg);
this.panelId = this.jqId + '_panel';
this.input = $(this.jqId + '_input');
this.hinput = $(this.jqId + '_hinput');
this.panel = this.jq.children(this.panelId);
this.dropdown = this.jq.children('.ui-button');
this.active = true;
this.cfg.pojo = this.hinput.length == 1;
this.cfg.minLength = this.cfg.minLength != undefined ? this.cfg.minLength : 1;
this.cfg.cache = this.cfg.cache || false;
this.cfg.ariaEmptyMessage = this.cfg.emptyMessage || 'No search results are available.';
this.cfg.dropdownMode = this.cfg.dropdownMode || 'blank';
this.cfg.autoHighlight = (this.cfg.autoHighlight === undefined) ? true : this.cfg.autoHighlight;
this.cfg.appendTo = PrimeFaces.utils.resolveAppendTo(this, this.jq, this.panel);
this.cfg.myPos = this.cfg.myPos || 'left top';
this.cfg.atPos = this.cfg.atPos || 'left bottom';
this.cfg.active = (this.cfg.active === false) ? false : true;
this.cfg.dynamic = this.cfg.dynamic === true ? true : false;
this.cfg.autoSelection = this.cfg.autoSelection === false ? false : true;
this.cfg.escape = this.cfg.escape === false ? false : true;
this.cfg.hasFooter = this.cfg.hasFooter === true ? true : false;
this.cfg.forceSelection = this.cfg.forceSelection === true ? true : false;
this.suppressInput = true;
this.touchToDropdownButton = false;
this.isTabPressed = false;
this.isDynamicLoaded = false;
this.currentInputValue = '';
if (this.cfg.cache) {
this.initCache();
}
if (this.cfg.queryMode !== 'server') {
this.fetchItems();
}
//pfs metadata
this.input.data(PrimeFaces.CLIENT_ID_DATA, this.id);
this.hinput.data(PrimeFaces.CLIENT_ID_DATA, this.id);
this.placeholder = this.input.attr('placeholder');
if (this.cfg.multiple) {
this.setupMultipleMode();
this.multiItemContainer.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
if (this.cfg.selectLimit >= 0 && this.multiItemContainer.children('li.ui-autocomplete-token').length === this.cfg.selectLimit) {
this.input.hide();
this.disableDropdown();
}
}
else {
//visuals
PrimeFaces.skinInput(this.input);
this.input.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
this.dropdown.data('primefaces-overlay-target', true).find('*').data('primefaces-overlay-target', true);
}
//core events
this.bindStaticEvents();
//client Behaviors
if (this.cfg.behaviors) {
PrimeFaces.attachBehaviors(this.input, this.cfg.behaviors);
}
//force selection
if (this.cfg.forceSelection) {
this.setupForceSelection();
}
//Panel management
if (this.panel.length) {
this.appendPanel();
this.transition = PrimeFaces.utils.registerCSSTransition(this.panel, 'ui-connected-overlay');
}
//itemtip
if (this.cfg.itemtip) {
this.itemtip = $('').appendTo(document.body);
this.cfg.itemtipMyPosition = this.cfg.itemtipMyPosition || 'left top';
this.cfg.itemtipAtPosition = this.cfg.itemtipAtPosition || 'right bottom';
this.cfg.checkForScrollbar = (this.cfg.itemtipAtPosition.indexOf('right') !== -1);
}
//aria
this.input.attr('aria-autocomplete', 'list');
this.jq.append('');
this.status = this.jq.children('.ui-autocomplete-status');
},
/**
* @override
* @inheritdoc
* @param {PrimeFaces.PartialWidgetCfg} cfg
*/
refresh: function(cfg) {
this._super(cfg);
},
/**
* Appends the overlay panel to the DOM.
* @private
*/
appendPanel: function() {
PrimeFaces.utils.registerDynamicOverlay(this, this.panel, this.id + '_panel');
},
/**
* Initializes the cache that stores the retrieved suggestions for a search term.
* @private
*/
initCache: function() {
this.cache = {};
var $this = this;
this.cacheTimeout = setInterval(function() {
$this.clearCache();
}, this.cfg.cacheTimeout);
},
/**
* Clears the cache with the results of an autocomplete search.
* @private
*/
clearCache: function() {
this.cache = {};
},
/**
* Binds events for multiple selection mode.
* @private
*/
setupMultipleMode: function() {
var $this = this;
this.multiItemContainer = this.jq.children('ul');
this.inputContainer = this.multiItemContainer.children('.ui-autocomplete-input-token');
this.multiItemContainer.on("mouseenter", function() {
$(this).addClass('ui-state-hover');
}).on("mouseleave", function() {
$(this).removeClass('ui-state-hover');
}).on("click", function() {
$this.input.trigger('focus');
});
//delegate events to container
this.input.on("focus", function() {
$this.multiItemContainer.addClass('ui-state-focus');
}).on("blur", function(e) {
$this.multiItemContainer.removeClass('ui-state-focus');
});
var closeSelector = '> li.ui-autocomplete-token > .ui-autocomplete-token-icon';
this.multiItemContainer.off('click', closeSelector).on('click', closeSelector, null, function(event) {
if ($this.multiItemContainer.children('li.ui-autocomplete-token').length === $this.cfg.selectLimit) {
$this.input.css('display', 'inline');
$this.enableDropdown();
}
$this.removeItem($(this).parent());
});
},
/**
* Sets up all global event listeners for the overlay.
* @private
*/
bindStaticEvents: function() {
var $this = this;
this.bindKeyEvents();
this.bindDropdownEvents();
if (PrimeFaces.env.browser.mobile) {
this.dropdown.on('touchstart', function() {
$this.touchToDropdownButton = true;
});
}
},
/**
* Sets up all panel event listeners
* @private
*/
bindPanelEvents: function() {
var $this = this;
this.hideOverlayHandler = PrimeFaces.utils.registerHideOverlayHandler(this, 'mousedown.' + this.id + '_hide', this.panel,
function() { return $this.itemtip; },
function(e, eventTarget) {
if (!($this.panel.is(eventTarget) || $this.panel.has(eventTarget).length > 0)) {
$this.hide();
}
});
this.resizeHandler = PrimeFaces.utils.registerResizeHandler(this, 'resize.' + this.id + '_hide', this.panel, function() {
$this.handleViewportChange();
});
this.scrollHandler = PrimeFaces.utils.registerConnectedOverlayScrollHandler(this, 'scroll.' + this.id + '_hide', this.jq, function() {
$this.handleViewportChange();
});
},
/**
* Fired when the browser viewport is resized or scrolled. In Mobile environment we don't want to hider the overlay
* we want to re-align it. This is because on some mobile browser the popup may force the browser to trigger a
* resize immediately and close the overlay. See GitHub #7075.
* @private
*/
handleViewportChange: function() {
if (PrimeFaces.env.mobile || PrimeFaces.hideOverlaysOnViewportChange === false) {
this.alignPanel();
} else {
this.hide();
}
},
/**
* Unbind all panel event listeners
* @private
*/
unbindPanelEvents: function() {
if (this.hideOverlayHandler) {
this.hideOverlayHandler.unbind();
}
if (this.resizeHandler) {
this.resizeHandler.unbind();
}
if (this.scrollHandler) {
this.scrollHandler.unbind();
}
},
/**
* Sets up all event listeners for the dropdown menu.
* @private
*/
bindDropdownEvents: function() {
var $this = this;
PrimeFaces.skinButton(this.dropdown);
this.dropdown.on("mouseup", function() {
if ($this.active) {
$this.searchWithDropdown();
$this.input.trigger('focus');
}
}).on("keyup", function(e) {
if (PrimeFaces.utils.isActionKey(e)) {
$this.searchWithDropdown();
$this.input.trigger('focus');
e.preventDefault();
e.stopPropagation();
}
});
},
/**
* Disables the dropdown menu.
* @private
*/
disableDropdown: function() {
if (this.dropdown.length) {
this.dropdown.off().prop('disabled', true).addClass('ui-state-disabled');
}
},
/**
* Enables the dropdown menu.
* @private
*/
enableDropdown: function() {
if (this.dropdown.length && this.dropdown.prop('disabled')) {
this.bindDropdownEvents();
this.dropdown.prop('disabled', false).removeClass('ui-state-disabled');
}
},
/**
* Sets up all keyboard related event listeners.
* @private
*/
bindKeyEvents: function() {
var $this = this;
// GitHub #6711 use DOM if non-CSP and JQ event if CSP
var originalOnchange = this.input.prop('onchange');
if (!originalOnchange && this.input[0]) {
var events = $._data(this.input[0], "events");
if (events.change) {
originalOnchange = events.change[0].handler;
}
}
this.cfg.onChange = originalOnchange;
if (originalOnchange) {
this.input.prop('onchange', null).off('change');
}
if (this.cfg.queryEvent !== 'enter') {
this.input.on('input propertychange', function(e) {
$this.processKeyEvent(e);
});
}
this.input.on('keyup.autoComplete', function(e) {
var key = e.key;
if ($this.cfg.queryEvent === 'enter' && (key === 'Enter')) {
if ($this.itemSelectedWithEnter)
$this.itemSelectedWithEnter = false;
else
$this.search($this.input.val());
}
if ($this.panel.is(':visible')) {
if (key === 'Escape') {
$this.hide();
}
else if (key === 'ArrowUp' || key === 'ArrowDown') {
var highlightedItem = $this.items.filter('.ui-state-highlight');
if (highlightedItem.length) {
$this.displayAriaStatus(highlightedItem.data('item-label'));
$this.changeAriaValue(highlightedItem[0]);
}
}
}
$this.checkMatchedItem = true;
$this.isTabPressed = false;
}).on('keydown.autoComplete', function(e) {
$this.suppressInput = false;
if ($this.panel.is(':visible')) {
var highlightedItem = $this.items.filter('.ui-state-highlight');
switch (e.key) {
case 'ArrowUp':
var prev = highlightedItem.length == 0 ? $this.items.eq(0) : highlightedItem.prevAll('.ui-autocomplete-item:first');
if (prev.length == 1) {
$this.highlightItem(highlightedItem, false);
$this.highlightItem(prev, true);
if ($this.cfg.scrollHeight) {
PrimeFaces.scrollInView($this.panel, prev);
}
if ($this.cfg.itemtip) {
$this.showItemtip(prev);
}
}
e.preventDefault();
break;
case 'ArrowDown':
var next = highlightedItem.length == 0 ? $this.items.eq(0) : highlightedItem.nextAll('.ui-autocomplete-item:first');
if (next.length == 1) {
$this.highlightItem(highlightedItem, false);
$this.highlightItem(next, true);
if ($this.cfg.scrollHeight) {
PrimeFaces.scrollInView($this.panel, next);
}
if ($this.cfg.itemtip) {
$this.showItemtip(next);
}
}
e.preventDefault();
break;
case 'Enter':
if ($this.timeout) {
$this.deleteTimeout();
}
if (highlightedItem.length > 0) {
$this.preventInputChangeEvent = true;
highlightedItem.trigger("click");
$this.itemSelectedWithEnter = true;
} else {
$this.hide();
}
e.preventDefault();
e.stopPropagation();
break;
case 'Alt':
break;
case 'Tab':
if (highlightedItem.length && $this.cfg.autoSelection) {
highlightedItem.trigger('click');
} else {
$this.hide();
if ($this.timeout) {
$this.deleteTimeout();
}
}
$this.isTabPressed = true;
break;
}
}
else {
switch (e.key) {
case 'Tab':
if ($this.timeout) {
$this.deleteTimeout();
}
$this.isTabPressed = true;
break;
case 'Enter':
var itemValue = $(this).val();
var valid = true;
if ($this.cfg.queryEvent === 'enter' || ($this.timeout > 0) || $this.querying) {
e.preventDefault();
}
if ($this.cfg.queryEvent !== 'enter') {
valid = $this.isValid(itemValue, true);
if (!$this.cfg.forceSelection) {
valid = true;
}
}
if ($this.cfg.multiple && itemValue) {
if (valid) {
$this.addItem(itemValue);
}
e.preventDefault();
e.stopPropagation();
}
break;
case 'Backspace':
if ($this.cfg.multiple && !$this.input.val().length) {
if (e.metaKey || e.ctrlKey || e.shiftKey) {
$this.removeAllItems();
} else {
$this.removeItem($(this).parent().prev());
}
e.preventDefault();
}
break;
};
}
}).on('paste.autoComplete', function() {
$this.suppressInput = false;
$this.checkMatchedItem = true;
}).on('change.autoComplete', function(e) {
var value = e.currentTarget.value,
valid = $this.isValid(value, true);
if ($this.cfg.forceSelection && $this.currentInputValue === '' && !valid) {
$this.preventInputChangeEvent = true;
}
if ($this.cfg.onChange && !$this.preventInputChangeEvent) {
$this.cfg.onChange.call(this);
}
$this.currentInputValue = $this.cfg.forceSelection && !valid ? '' : value;
$this.preventInputChangeEvent = false;
});
},
/**
* Sets up all event listeners for mouse and click events.
* @private
*/
bindDynamicEvents: function() {
var $this = this;
//visuals and click handler for items
this.items.off('click.autocomplete mousedown.autocomplete mouseover.autocomplete')
.on('mouseover.autocomplete', function() {
var item = $(this);
if (!item.hasClass('ui-state-highlight')) {
$this.items.filter('.ui-state-highlight').removeClass('ui-state-highlight').attr('aria-selected', false);
$this.highlightItem(item, true);
if ($this.cfg.itemtip) {
$this.showItemtip(item);
}
}
})
.on('click.autocomplete', function(event) {
var item = $(this),
isMoreText = item.hasClass('ui-autocomplete-moretext');
if (isMoreText) {
$this.input.trigger('focus');
$this.invokeMoreTextBehavior();
}
else {
$this.addItem(item);
}
$this.hide();
})
.on('mousedown.autocomplete', function() {
$this.preventInputChangeEvent = true;
$this.checkMatchedItem = false;
});
this.panel.on('click.emptyMessage', function() {
if (!this.children) {
return;
}
var item = $(this.children[0]),
isEmptyMessage = item.hasClass('ui-autocomplete-empty-message');
if (isEmptyMessage) {
$this.invokeEmptyMessageBehavior();
}
});
if (PrimeFaces.env.browser.mobile) {
this.items.on('touchstart.autocomplete', function() {
if (!$this.touchToDropdownButton) {
$this.itemClick = true;
}
});
}
},
/**
* Callback for when a key event occurred.
* @private
* @param {JQuery.TriggeredEvent} e Key event that occurred.
*/
processKeyEvent: function(e) {
var $this = this;
if ($this.suppressInput) {
e.preventDefault();
return;
}
// for touch event on mobile
if (PrimeFaces.env.browser.mobile) {
$this.touchToDropdownButton = false;
if ($this.itemClick) {
$this.itemClick = false;
return;
}
}
var value = $this.input.val();
if ($this.cfg.pojo && !$this.cfg.multiple) {
$this.hinput.val(value);
}
if (!value.length) {
$this.hide();
$this.deleteTimeout();
}
if (value.length >= $this.cfg.minLength) {
if ($this.timeout) {
$this.deleteTimeout();
}
var delay = $this.cfg.delay;
if (delay && delay > 0) {
$this.timeout = setTimeout(function() {
$this.timeout = null;
$this.search(value);
}, delay);
}
else {
$this.search(value);
}
}
else if (value.length === 0) {
if ($this.timeout) {
$this.deleteTimeout();
}
$this.fireClearEvent();
}
},
/**
* Shows the tooltip for the given suggestion item.
* @private
* @param {JQuery} item Item with a tooltip.
*/
showItemtip: function(item) {
if (item.hasClass('ui-autocomplete-moretext')) {
this.itemtip.hide();
}
else {
var content;
if (item.is('li')) {
content = item.next('.ui-autocomplete-itemtip-content');
} else {
if (item.children('td:last').hasClass('ui-autocomplete-itemtip-content')) {
content = item.children('td:last');
} else {
this.itemtip.hide();
return;
}
}
this.itemtip.html(content.html())
.css({
'left': '',
'top': '',
'z-index': PrimeFaces.nextZindex(),
'width': content.outerWidth() + 'px'
})
.position({
my: this.cfg.itemtipMyPosition
, at: this.cfg.itemtipAtPosition
, of: item
});
//scrollbar offset
if (this.cfg.checkForScrollbar) {
if (this.panel.innerHeight() < this.panel.children('.ui-autocomplete-items').outerHeight(true)) {
var panelOffset = this.panel.offset();
this.itemtip.css('left', (panelOffset.left + this.panel.outerWidth()) + 'px');
}
}
this.itemtip.show();
}
},
/**
* Performs the search for the available suggestion items.
* @private
* @param {string} query Keyword for the search.
*/
showSuggestions: function(query) {
this.items = this.panel.find('.ui-autocomplete-item');
this.items.attr('role', 'option');
if (this.cfg.grouping) {
this.groupItems();
}
this.bindDynamicEvents();
var $this = this,
hidden = this.panel.is(':hidden');
if (hidden) {
this.show();
}
else {
this.alignPanel();
}
// #8717 always clear list before trying to fill it
if (this.cfg.forceSelection) {
this.currentItems = [];
}
if (this.items.length > 0) {
var firstItem = this.items.eq(0);
//highlight first item
if (this.cfg.autoHighlight && firstItem.length) {
this.highlightItem(firstItem, true);
this.changeAriaValue(firstItem[0]);
}
//highlight query string
if (query.length > 0) {
var cleanedQuery = query.trim().replaceAll(/(\s+)/g, ' ');
if (cleanedQuery.length > 0) {
var queryParts = cleanedQuery.split(' ');
for (var i = 0; i < queryParts.length; i++) {
queryParts[i] = PrimeFaces.escapeRegExp(queryParts[i]);
}
var re = new RegExp('(' + queryParts.join('|') + ')', 'gi');
var isCustomContent = this.panel.children().is('table');
var queryResults = isCustomContent ? this.panel.children().find('span') : this.items;
queryResults.filter(':not(.ui-autocomplete-moretext)').each(function() {
var item = $(this);
var escape = $this.cfg.escape;
var text = escape ? item.html() : item.text();
var escaped_re = escape ? /${PrimeFaces.escapeHTML(text)}/g : /${text}/g;
text = text.replace(escaped_re, '$&');
item.html(text.replace(re, '$&'));
});
}
}
if (this.cfg.forceSelection) {
this.items.each(function(i, item) {
$this.currentItems.push($(item).attr('data-item-label'));
});
}
//show itemtip if defined
if (this.cfg.autoHighlight && this.cfg.itemtip && firstItem.length === 1) {
this.showItemtip(firstItem);
}
this.displayAriaStatus(this.items.length + this.cfg.resultsMessage);
}
else {
if (this.cfg.emptyMessage) {
var emptyText = '
';
$this.inputContainer.before(itemDisplayMarkup);
$this.multiItemContainer.children('.ui-helper-hidden').fadeIn();
$this.input.val('');
$this.input.removeAttr('placeholder');
$this.hinput.append('');
if ($this.multiItemContainer.children('li.ui-autocomplete-token').length >= $this.cfg.selectLimit) {
$this.input.css('display', 'none').trigger("blur");
$this.disableDropdown();
}
$this.invokeItemSelectBehavior(itemValue);
}
} else {
$this.input.val(item.attr('data-item-label'));
this.currentText = $this.input.val();
this.previousText = $this.input.val();
if ($this.cfg.pojo) {
$this.hinput.val(itemValue);
}
$this.invokeItemSelectBehavior(itemValue);
}
if ($this.cfg.onChange) {
$this.cfg.onChange.call(this);
}
if (!$this.isTabPressed) {
$this.input.trigger('focus');
}
},
/**
* Removes the given suggestion item.
* @param {JQuery | string} item Suggestion item to remove.
*/
removeItem: function(item) {
var $this = this,
itemValue = '';
if ($this.input.hasClass('ui-state-disabled') || $this.input.attr("readonly")) {
return;
}
if (typeof item === 'string' || item instanceof String) {
itemValue = item;
}
else {
itemValue = item.attr('data-token-value');
}
var foundItem = this.multiItemContainer.children("li.ui-autocomplete-token[data-token-value='" + $.escapeSelector(itemValue) + "']");
if (!foundItem.length) {
return;
}
var itemIndex = foundItem.index();
if (!itemValue || itemIndex === -1) {
return;
}
//remove from options
this.hinput.children('option').eq(itemIndex).remove();
//remove from items
foundItem.fadeOut('fast', function() {
var token = $(this);
token.remove();
$this.invokeItemUnselectBehavior(itemValue);
});
// if empty return placeholder
if (this.placeholder && this.hinput.children('option').length === 0) {
this.input.attr('placeholder', this.placeholder);
}
},
/**
* Removes all items if in multiple mode.
*/
removeAllItems: function() {
var $this = this;
if (this.cfg.multiple && !this.input.val().length) {
this.multiItemContainer.find('.ui-autocomplete-token').each(function(index) {
$this.removeItem($(this));
});
}
},
/**
* Sets up the event listener for the blur event to force a selection, when that feature is enabled.
* @private
*/
setupForceSelection: function() {
this.currentItems = [this.input.val()];
var $this = this;
this.input.on('blur', function(e) {
// #5731: do not fire clear event if selecting item
var isNotPanel = e.relatedTarget == null || PrimeFaces.escapeClientId(e.relatedTarget.id) !== $this.panelId,
value = $(this).val(),
valid = $this.isValid(value, isNotPanel);
if ($this.cfg.autoSelection && valid && $this.checkMatchedItem && $this.items && !$this.isTabPressed && !$this.itemSelectedWithEnter && isNotPanel) {
var selectedItem = $this.items.filter('[data-item-label="' + $.escapeSelector(value) + '"]');
if (selectedItem.length) {
selectedItem.trigger("click");
}
}
$this.checkMatchedItem = false;
});
},
/**
* Disables the component.
*/
disable: function() {
this.jq.addClass("ui-state-disabled");
PrimeFaces.utils.disableInputWidget(this.input);
if (this.dropdown.length) {
this.disableDropdown();
}
},
/**
* Enables the component.
*/
enable: function() {
this.jq.removeClass("ui-state-disabled");
PrimeFaces.utils.enableInputWidget(this.input);
if (this.dropdown.length) {
this.enableDropdown();
}
},
/**
* Hides suggested items menu.
*/
close: function() {
this.hide();
},
/**
* Deactivates search behavior.
*/
deactivate: function() {
this.active = false;
},
/**
* Activates search behavior.
*/
activate: function() {
this.active = true;
},
/**
* Aligns (positions) the overlay panel that shows the found suggestions.
*/
alignPanel: function() {
var panelWidth = null;
if (this.cfg.multiple) {
panelWidth = this.multiItemContainer.outerWidth();
}
else {
if (this.panel.is(':visible')) {
panelWidth = this.panel.children('.ui-autocomplete-items').outerWidth();
}
else {
this.panel.css({ 'visibility': 'hidden', 'display': 'block' });
panelWidth = this.panel.children('.ui-autocomplete-items').outerWidth();
this.panel.css({ 'visibility': 'visible', 'display': 'none' });
}
var inputWidth = this.input.outerWidth();
if (panelWidth < inputWidth) {
panelWidth = inputWidth;
}
}
if (this.cfg.scrollHeight) {
var heightConstraint = this.panel.is(':hidden') ? this.panel.height() : this.panel.children().height();
if (heightConstraint > this.cfg.scrollHeight)
this.panel.height(this.cfg.scrollHeight);
else
this.panel.css('height', 'auto');
}
this.panel.css({
'left': '',
'top': '',
'width': panelWidth + 'px',
'z-index': PrimeFaces.nextZindex(),
'transform-origin': 'center top'
});
if (this.panel.parent().is(this.jq)) {
this.panel.css({
left: '0px',
top: this.jq.innerHeight() + 'px',
'transform-origin': 'center top'
});
}
else {
this.panel.position({
my: this.cfg.myPos
, at: this.cfg.atPos
, of: this.cfg.multiple ? this.jq : this.input
, collision: 'flipfit'
, using: function(pos, directions) {
$(this).css('transform-origin', 'center ' + directions.vertical).css(pos);
}
});
}
},
/**
* Adds the given text to the ARIA status label element.
* @private
* @param {string} text Label text to display.
*/
displayAriaStatus: function(text) {
this.status.html('
' + PrimeFaces.escapeHTML(text) + '
');
},
/**
* Adjusts the value of the aria attributes for the given selectable option.
* @private
* @param {Element} item An option for which to set the aria attributes.
*/
changeAriaValue: function (item) {
if (item) {
this.input.attr('aria-activedescendant', item.id);
}
},
/**
* Adjusts the highlighting and aria attributes for the given selectable option.
* @private
* @param {Element} item An option for which to set the aria attributes.
* @param {boolean} highlight Flag to indicate to highlight or not
*/
highlightItem: function (item, highlight) {
if (highlight) {
item.addClass('ui-state-highlight');
}
else {
item.removeClass('ui-state-highlight');
}
item.attr('aria-selected', highlight);
},
/**
* Takes the available suggestion items and groups them.
* @private
*/
groupItems: function() {
var $this = this;
if (this.items.length) {
this.itemContainer = this.panel.children('.ui-autocomplete-items');
var firstItem = this.items.eq(0);
if (!firstItem.hasClass('ui-autocomplete-moretext')) {
this.currentGroup = firstItem.data('item-group');
var currentGroupTooltip = firstItem.data('item-group-tooltip');
firstItem.before(this.getGroupItem($this.currentGroup, $this.itemContainer, currentGroupTooltip));
}
this.items.filter(':not(.ui-autocomplete-moretext)').each(function(i) {
var item = $this.items.eq(i),
itemGroup = item.data('item-group'),
itemGroupTooltip = item.data('item-group-tooltip');
if ($this.currentGroup !== itemGroup) {
$this.currentGroup = itemGroup;
item.before($this.getGroupItem(itemGroup, $this.itemContainer, itemGroupTooltip));
}
});
}
},
/**
* Creates the grouped suggestion item for the given parameters.
* @private
* @param {string} group A group where to look for the item.
* @param {JQuery} container Container element of the group.
* @param {string} tooltip Optional tooltip for the group item.
* @return {JQuery} The newly created group item.
*/
getGroupItem: function(group, container, tooltip) {
var element = null;
if (container.is('.ui-autocomplete-table')) {
if (!this.colspan) {
this.colspan = this.items.eq(0).children('td').length;
}
element = $('
' + group + '
');
}
else {
element = $('
' + group + '
');
}
if (element) {
element.attr('title', tooltip);
}
return element;
},
/**
* Clears the set-timeout timer for the autocomplete search.
* @private
*/
deleteTimeout: function() {
clearTimeout(this.timeout);
this.timeout = null;
},
/**
* Triggers the behavior for when the input was cleared.
* @private
*/
fireClearEvent: function() {
this.callBehavior('clear');
this.previousText = this.currentText;
this.currentText = '';
},
/**
* Checks whether the given value is part of the available suggestion items.
* @param {string} value A value to check.
* @param {boolean} [shouldFireClearEvent] `true` if clear event should be fired.
* @return {boolean | undefined} Whether the given value matches a value in the list of available suggestion items;
* or `undefined` if {@link AutoCompleteCfg.forceSelection} is set to `false`.
*/
isValid: function(value, shouldFireClearEvent) {
if (!this.cfg.forceSelection) {
return;
}
var valid = false;
for (var i = 0; i < this.currentItems.length; i++) {
var strippedItem = this.currentItems[i];
if (strippedItem) {
strippedItem = strippedItem.replace(/\r?\n/g, '');
}
if (strippedItem === value) {
valid = true;
break;
}
}
if (!valid) {
this.input.val('');
if (!this.cfg.multiple) {
this.hinput.val('');
}
shouldFireClearEvent = shouldFireClearEvent && (!this.cfg.multiple && this.currentText);
if (shouldFireClearEvent) {
this.fireClearEvent();
}
}
return valid;
},
/**
* Fetches the suggestion items for the current query from the server.
* @private
*/
fetchItems: function() {
var $this = this;
var options = {
source: this.id,
process: this.id,
update: this.id,
formId: this.getParentFormId(),
global: false,
params: [{ name: this.id + '_clientCache', value: true }],
onsuccess: function(responseXML, status, xhr) {
PrimeFaces.ajax.Response.handle(responseXML, status, xhr, {
widget: $this,
handle: function(content) {
$this.setCache($(content));
}
});
return true;
}
};
PrimeFaces.ajax.Request.handle(options);
},
/**
* Adds the suggestions items in the given wrapper to the local cache of suggestion items.
* @private
* @param {JQuery} wrapper Wrapper element with the suggestions fetched from the server.
*/
setCache: function(wrapper) {
var $this = this,
items = wrapper.find('.ui-autocomplete-item'),
prevKey = null;
if (!this.wrapperStartTag || !this.wrapperEndTag) {
this.findWrapperTag(wrapper);
}
for (var i = 0; i < items.length; i++) {
var item = items.eq(i),
key = item.data('item-key');
this.cache[key] = (this.cache[key] || this.wrapperStartTag) + item.get(0).outerHTML;
if ((prevKey !== null && prevKey !== key) || (i === items.length - 1)) {
this.cache[prevKey] += $this.wrapperEndTag;
}
prevKey = key;
}
},
/**
* Finds and sets the wrapper HTML snippets on this instance.
* @private
* @param {JQuery} wrapper Wrapper element with the suggestions fetched from the server.
*/
findWrapperTag: function(wrapper) {
if (wrapper.is('ul')) {
this.wrapperStartTag = '