autocomplete-base.js revision b5554435260964a5bbdcd74ab7f7d541759bd7a3
0d6d1a2d994933a68a100ec3dcdc7c7a0eeeae6cJenny Han Donnelly * Extension that provides core autocomplete logic for a text input field or
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @module autocomplete
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @submodule autocomplete-base
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Extension that provides core autocomplete logic for a text input field or
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * This extension cannot be instantiated directly, since it doesn't provide an
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * actual implementation. It provides the core logic used by the
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <code>AutoComplete</code> class, and you can mix it into a custom class as
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * follows if you'd like to create a customized autocomplete implementation:
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * YUI().use('autocomplete-base', 'base', function (Y) {
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * var MyAutoComplete = Y.Base.create('myAutocomplete', Y.Base, [Y.AutoComplete], {
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * initializer: function () {
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * this._bindInput();
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * this._syncInput();
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * },
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * destructor: function () {
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * this._unbindInput();
0d6d1a2d994933a68a100ec3dcdc7c7a0eeeae6cJenny Han Donnelly * }
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * });
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * // ... custom implementation code ...
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @class AutoCompleteBase
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Fires after the contents of the input field have been completely cleared.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @event clear
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @param {EventFacade} e Event facade with the following additional
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * properties:
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <dt>prevVal (String)</dt>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Value of the input node before it was cleared.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Fires when the contents of the input field have changed and the input
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * value meets the criteria necessary to generate an autocomplete query.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @event query
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @param {EventFacade} e Event facade with the following additional
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * properties:
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <dt>inputValue (String)</dt>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Full contents of the text input field or textarea that generated
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * the query.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <dt>query (String)</dt>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Autocomplete query. This is the string that will be used to
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * request completion results. It may or may not be the same as
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <code>inputValue</code>.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @preventable _defQueryFn
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Fires after query results are received from the DataSource. If no
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * DataSource has been set, this event will not fire.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @event results
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @param {EventFacade} e Event facade with the following additional
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * properties:
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <dt>data (Array|Object)</dt>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Raw, unfiltered result data (if available).
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <dt>query (String)</dt>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Query that generated these results.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <dt>results (Array|Object)</dt>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Normalized and filtered result data returned from the DataSource.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Fires after the input node's value changes, and before the
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <code>query</code> event.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @event valueChange
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @param {EventFacade} e Event facade with the following additional
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * properties:
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <dt>newVal (String)</dt>
0d6d1a2d994933a68a100ec3dcdc7c7a0eeeae6cJenny Han Donnelly * Value of the input node after the change.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <dt>prevVal (String)</dt>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Value of the input node prior to the change.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith// -- Public Static Properties -------------------------------------------------
b3558e978532e4905c7b41b2d0821948ac047dbbLuke Smith * Whether or not to enable the browser's built-in autocomplete
b3558e978532e4905c7b41b2d0821948ac047dbbLuke Smith * functionality for input fields.
b3558e978532e4905c7b41b2d0821948ac047dbbLuke Smith * @attribute allowBrowserAutocomplete
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @type Boolean
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @default false
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @writeonce
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * DataSource object which will be used to make queries. This can be
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * an actual DataSource instance or any other object with a
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * sendRequest() method that has the same signature as DataSource's
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * sendRequest() method.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * As an alternative to providing a DataSource, you could listen for
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <code>query</code> events and handle them any way you see fit.
d3ac39f387c773c28001a1323aa85082051965aemattsnider * Providing a DataSource or DataSource-like object is optional, but
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * will often be simpler.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @attribute dataSource
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @type DataSource|Object|null
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith return (value && isFunction(value.sendRequest)) || value === null;
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Node to monitor for changes, which will generate <code>query</code>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * events when appropriate. May be either an input field or a textarea.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @attribute inputNode
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @type Node|HTMLElement|String
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @writeonce
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Minimum number of characters that must be entered before a
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <code>query</code> event will be fired. A value of <code>0</code>
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * allows empty queries; a negative value will effectively disable all
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <code>query</code> events.
3f8c8f56d68f749e363b10328b2a5c97a5c96863Jenny Donnelly * @attribute minQueryLength
d3ac39f387c773c28001a1323aa85082051965aemattsnider * @type Number
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @default 1
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Current query, or <code>null</code> if there is no current query.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * The query might not be the same as the current value of the input
3f8c8f56d68f749e363b10328b2a5c97a5c96863Jenny Donnelly * node, both for timing reasons (due to <code>queryDelay</code>) and
0d6d1a2d994933a68a100ec3dcdc7c7a0eeeae6cJenny Han Donnelly * because when one or more <code>queryDelimiter</code> separators are
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * in use, only the last portion of the delimited input string will be
8392a541bf432ed8d6e1985b6306b83dc898768dJenny Han Donnelly * used as the query value.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @attribute query
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @type String|null
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @default null
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Number of milliseconds to delay after input before triggering a
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <code>query</code> event. If new input occurs before this delay is
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * over, the previous input event will be ignored and a new delay will
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * This can be useful both to throttle queries to a remote data source
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * and to avoid distracting the user by showing them less relevant
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * results before they've paused their typing.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @attribute queryDelay
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @type Number
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @default 150
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * DataSource request template. This can be a function that accepts a
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * query as a parameter and returns a request string, or it can be a
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * string containing the placeholder "{query}", which will be replaced
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * with the actual URI-encoded query.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * When using a string template, if it's necessary for the literal
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * string "{query}" to appear in the request, escape it with a slash:
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * "\{query}".
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * While <code>requestTemplate</code> can be set to either a function or
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * a string, it will always be returned as a function that accepts a
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * query argument and returns a string.
b9436848b51ee2b4f04b9217e74172c5a05ce276Jenny Han Donnelly * @attribute requestTemplate
b9436848b51ee2b4f04b9217e74172c5a05ce276Jenny Han Donnelly * @type Function|String
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @default encodeURIComponent
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith return function (query) {
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith // Replace {query} with the URI-encoded query, but turn
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith // \{query} into the literal string "{query}" to allow that
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith // string to appear in the request if necessary.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith replace(/(^|[^\\])((\\{2})*)\{query\}/, '$1$2' + encodeURIComponent(query)).
d3ac39f387c773c28001a1323aa85082051965aemattsnider replace(/(^|[^\\])((\\{2})*)\\(\{query\})/, '$1$2$4');
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * Array of local result filter functions. If provided, each filter
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * will be called with two arguments when results are received: the
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * query and the results received from the DataSource. Each filter is
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * expected to return a filtered or modified version of those results,
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * which will then be passed on to subsequent filters, to the
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * <code>resultHighlighter</code> function (if set), and finally to
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * subscribers to the <code>results</code> event.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * If no DataSource is set, result filters will not be called.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @attribute resultFilters
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @type Array
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @default []
8392a541bf432ed8d6e1985b6306b83dc898768dJenny Han Donnelly * Function which will be used to highlight results. If provided, this
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * function will be called with two arguments after results have been
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * received and filtered: the query and the filtered results. The
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * highlighter is expected to return a modified version of the results
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * with the query highlighted in some form.
8392a541bf432ed8d6e1985b6306b83dc898768dJenny Han Donnelly * If no DataSource is set, the highlighter will not be called.
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @attribute resultHighlighter
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith * @type Function|null
0d6d1a2d994933a68a100ec3dcdc7c7a0eeeae6cJenny Han Donnelly return isFunction(value) || value === null;
f805ad34c19740fa0c9729ce35fe59d191912f32Luke Smith// Because nobody wants to type ".yui3-autocomplete-blah" a hundred times.
_bindInput: function () {
if (!inputNode) {
this._unbindInput();
this._inputEvents = [
return value;
_syncInput: function () {
_unbindInput: function () {
_onResponse: function (e) {
var filters,
len,
if (results) {
if (highlighter) {
_onValueChange: function (e) {
var delay,
fire,
that;
that = this;
fire = function () {
if (delay) {
fire();
_defQueryFn: function (e) {
if (dataSource) {
callback: {