autocomplete-base.js revision e58e329a62c99b6967a791762117392e5af609de
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Provides automatic input completion or suggestions for text input fields and
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * textareas.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @module autocomplete
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @main autocomplete
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @since 3.3.0
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>Y.Base</code> extension that provides core autocomplete logic (but no
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * UI implementation) for a text input field or textarea. Must be mixed into a
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>Y.Base</code>-derived class to be useful.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @submodule autocomplete-base
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Extension that provides core autocomplete logic (but no UI implementation)
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * for a text input field or textarea.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * The <code>AutoCompleteBase</code> class provides events and attributes that
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * abstract away core autocomplete logic and configuration, but does not provide
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * a widget implementation or suggestion UI. For a prepackaged autocomplete
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * widget, see <code>AutoCompleteList</code>.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * This extension cannot be instantiated directly, since it doesn't provide an
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * actual implementation. It's intended to be mixed into a
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>Y.Base</code>-based class or widget.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>Y.Widget</code>-based example:
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * YUI().use('autocomplete-base', 'widget', function (Y) {
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * var MyAC = Y.Base.create('myAC', Y.Widget, [Y.AutoCompleteBase], {
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * // Custom prototype methods and properties.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * }, {
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * // Custom static methods and properties.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * });
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * // Custom implementation code.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>Y.Base</code>-based example:
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * YUI().use('autocomplete-base', function (Y) {
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * var MyAC = Y.Base.create('myAC', Y.Base, [Y.AutoCompleteBase], {
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * initializer: function () {
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * this._bindUIACBase();
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * this._syncUIACBase();
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * },
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * // Custom prototype methods and properties.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * }, {
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * // Custom static methods and properties.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * });
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * // Custom implementation code.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @class AutoCompleteBase
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk // AOP bindings.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk Y.before(this._destructorACBase, this, 'destructor');
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk // -- Public Events --------------------------------------------------------
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Fires after the query has been completely cleared or no longer meets the
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * minimum query length requirement.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @event clear
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @param {EventFacade} e Event facade with the following additional
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * properties:
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>prevVal (String)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Value of the query before it was cleared.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @preventable _defClearFn
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Fires when the contents of the input field have changed and the input
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * value meets the criteria necessary to generate an autocomplete query.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @event query
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @param {EventFacade} e Event facade with the following additional
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * properties:
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>inputValue (String)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Full contents of the text input field or textarea that generated
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * the query.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>query (String)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Autocomplete query. This is the string that will be used to
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * request completion results. It may or may not be the same as
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>inputValue</code>.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @preventable _defQueryFn
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Fires after query results are received from the <code>source</code>. If
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * no source has been set, this event will not fire.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @event results
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @param {EventFacade} e Event facade with the following additional
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * properties:
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>data (Array|Object)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Raw, unfiltered result data (if available).
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>query (String)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Query that generated these results.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>results (Array)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Array of filtered, formatted, and highlighted results. Each item in
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * the array is an object with the following properties:
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>display (Node|HTMLElement|String)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Formatted result HTML suitable for display to the user. If no
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * custom formatter is set, this will be an HTML-escaped version of
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * the string in the <code>text</code> property.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>highlighted (String)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Highlighted (but not formatted) result text. This property will
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * only be set if a highlighter is in use.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>raw (mixed)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Raw, unformatted result in whatever form it was provided by the
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>source</code>.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <dt>text (String)</dt>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Plain text version of the result, suitable for being inserted
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * into the value of a text input field or textarea when the result
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * is selected by a user. This value is not HTML-escaped and should
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * not be inserted into the page using innerHTML.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @preventable _defResultsFn
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk// -- Public Static Properties -------------------------------------------------
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Whether or not to enable the browser's built-in autocomplete
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * functionality for input fields.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute allowBrowserAutocomplete
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @type Boolean
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @default false
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * When a <code>queryDelimiter</code> is set, trailing delimiters will
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * automatically be stripped from the input value by default when the
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * input node loses focus. Set this to <code>true</code> to allow trailing
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * delimiters.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute allowTrailingDelimiter
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @type Boolean
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @default false
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Whether or not to enable in-memory caching in result sources that support
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute enableCache
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @type Boolean
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @default true
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @since 3.5.0
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk lazyAdd: false, // we need the setter to run on init
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Node to monitor for changes, which will generate <code>query</code>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * events when appropriate. May be either an input field or a textarea.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute inputNode
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @type Node|HTMLElement|String
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @writeonce
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Maximum number of results to return. A value of <code>0</code> or less
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * will allow an unlimited number of results.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute maxResults
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @type Number
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @default 0
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Minimum number of characters that must be entered before a
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>query</code> event will be fired. A value of <code>0</code>
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * allows empty queries; a negative value will effectively disable all
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>query</code> events.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute minQueryLength
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @type Number
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @default 1
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Current query, or <code>null</code> if there is no current query.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * The query might not be the same as the current value of the input
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * node, both for timing reasons (due to <code>queryDelay</code>) and
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * because when one or more <code>queryDelimiter</code> separators are
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * in use, only the last portion of the delimited input string will be
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * used as the query value.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute query
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @type String|null
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @default null
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Number of milliseconds to delay after input before triggering a
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * <code>query</code> event. If new input occurs before this delay is
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * over, the previous input event will be ignored and a new delay will
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * This can be useful both to throttle queries to a remote data source
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * and to avoid distracting the user by showing them less relevant
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * results before they've paused their typing.
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute queryDelay
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @type Number
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @default 100
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * Query delimiter string. When a delimiter is configured, the input value
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * will be split on the delimiter, and only the last portion will be used in
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * autocomplete queries and updated when the <code>query</code> attribute is
c1350cf5bc50458ba79cc93ff9e0e5fe3f1aeeb0jeff.schenk * @attribute queryDelimiter
value: null
value: null
value: []
results: {
readOnly: true,
value: []
* <i>Example:</i> <code>'http://example.com/search?q={query}&callback={callback}'</code>
* <i>Example:</i> <code>'http://example.com/search?q={query}'</code>
source: {
sourceType: {
value: null
tokenInput: {
readOnly: true
value: {
clearCache: function () {
var request,
if (source) {
if (!requestTemplate) {
callback: {
_bindUIACBase: function () {
if (tokenInput) {
if (!inputNode) {
_destructorACBase: function () {
_syncUIACBase: function () {
this._syncBrowserAutocomplete();
var that = this;
var that = this;
var value;
var that = this;
if (!obj) {
return obj;
var facade = {
results: []
results = [],
len,
text,
if (!results) {
if (highlighter) {
if (!highlighted) {
if (formatter) {
if (!formatted) {
if (delim) {
return locator;
var that = this;
return function (result) {
return template;
return function (query) {
if (filters === null) {
return filter;
var acHighlighters;
return highlighter;
return INVALID_VALUE;
|| source === null
return source;
return INVALID_VALUE;
_syncBrowserAutocomplete: function () {
len,
if (delim) {
_afterSourceTypeChange: function (e) {
if (this._rawSource) {
_afterValueChange: function (e) {
var delay,
fire,
that;
that = this;
fire = function () {
if (delay) {
fire();
_onInputBlur: function (e) {
if (delim) {
_onInputValueChange: function (e) {
_defClearFn: function () {
_defQueryFn: function (e) {
_defResultsFn: function (e) {