--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cm/media/js/lib/yui/yui_3.10.3/build/autocomplete-base/autocomplete-base-debug.js Tue Jul 16 14:29:46 2013 +0200
@@ -0,0 +1,1564 @@
+/*
+YUI 3.10.3 (build 2fb5187)
+Copyright 2013 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+http://yuilibrary.com/license/
+*/
+
+YUI.add('autocomplete-base', function (Y, NAME) {
+
+/**
+Provides automatic input completion or suggestions for text input fields and
+textareas.
+
+@module autocomplete
+@main autocomplete
+@since 3.3.0
+**/
+
+/**
+`Y.Base` extension that provides core autocomplete logic (but no UI
+implementation) for a text input field or textarea. Must be mixed into a
+`Y.Base`-derived class to be useful.
+
+@module autocomplete
+@submodule autocomplete-base
+**/
+
+/**
+Extension that provides core autocomplete logic (but no UI implementation) for a
+text input field or textarea.
+
+The `AutoCompleteBase` class provides events and attributes that abstract away
+core autocomplete logic and configuration, but does not provide a widget
+implementation or suggestion UI. For a prepackaged autocomplete widget, see
+`AutoCompleteList`.
+
+This extension cannot be instantiated directly, since it doesn't provide an
+actual implementation. It's intended to be mixed into a `Y.Base`-based class or
+widget.
+
+`Y.Widget`-based example:
+
+ YUI().use('autocomplete-base', 'widget', function (Y) {
+ var MyAC = Y.Base.create('myAC', Y.Widget, [Y.AutoCompleteBase], {
+ // Custom prototype methods and properties.
+ }, {
+ // Custom static methods and properties.
+ });
+
+ // Custom implementation code.
+ });
+
+`Y.Base`-based example:
+
+ YUI().use('autocomplete-base', function (Y) {
+ var MyAC = Y.Base.create('myAC', Y.Base, [Y.AutoCompleteBase], {
+ initializer: function () {
+ this._bindUIACBase();
+ this._syncUIACBase();
+ },
+
+ // Custom prototype methods and properties.
+ }, {
+ // Custom static methods and properties.
+ });
+
+ // Custom implementation code.
+ });
+
+@class AutoCompleteBase
+**/
+
+var Escape = Y.Escape,
+ Lang = Y.Lang,
+ YArray = Y.Array,
+ YObject = Y.Object,
+
+ isFunction = Lang.isFunction,
+ isString = Lang.isString,
+ trim = Lang.trim,
+
+ INVALID_VALUE = Y.Attribute.INVALID_VALUE,
+
+ _FUNCTION_VALIDATOR = '_functionValidator',
+ _SOURCE_SUCCESS = '_sourceSuccess',
+
+ ALLOW_BROWSER_AC = 'allowBrowserAutocomplete',
+ INPUT_NODE = 'inputNode',
+ QUERY = 'query',
+ QUERY_DELIMITER = 'queryDelimiter',
+ REQUEST_TEMPLATE = 'requestTemplate',
+ RESULTS = 'results',
+ RESULT_LIST_LOCATOR = 'resultListLocator',
+ VALUE = 'value',
+ VALUE_CHANGE = 'valueChange',
+
+ EVT_CLEAR = 'clear',
+ EVT_QUERY = QUERY,
+ EVT_RESULTS = RESULTS;
+
+function AutoCompleteBase() {}
+
+AutoCompleteBase.prototype = {
+ // -- Lifecycle Methods ----------------------------------------------------
+ initializer: function () {
+ // AOP bindings.
+ Y.before(this._bindUIACBase, this, 'bindUI');
+ Y.before(this._syncUIACBase, this, 'syncUI');
+
+ // -- Public Events ----------------------------------------------------
+
+ /**
+ Fires after the query has been completely cleared or no longer meets the
+ minimum query length requirement.
+
+ @event clear
+ @param {String} prevVal Value of the query before it was cleared.
+ @param {String} src Source of the event.
+ @preventable _defClearFn
+ **/
+ this.publish(EVT_CLEAR, {
+ defaultFn: this._defClearFn
+ });
+
+ /**
+ Fires when the contents of the input field have changed and the input
+ value meets the criteria necessary to generate an autocomplete query.
+
+ @event query
+ @param {String} inputValue Full contents of the text input field or
+ textarea that generated the query.
+ @param {String} query AutoComplete query. This is the string that will
+ be used to request completion results. It may or may not be the same
+ as `inputValue`.
+ @param {String} src Source of the event.
+ @preventable _defQueryFn
+ **/
+ this.publish(EVT_QUERY, {
+ defaultFn: this._defQueryFn
+ });
+
+ /**
+ Fires after query results are received from the source. If no source has
+ been set, this event will not fire.
+
+ @event results
+ @param {Array|Object} data Raw, unfiltered result data (if available).
+ @param {String} query Query that generated these results.
+ @param {Object[]} results Array of filtered, formatted, and highlighted
+ results. Each item in the array is an object with the following
+ properties:
+
+ @param {Node|HTMLElement|String} results.display Formatted result
+ HTML suitable for display to the user. If no custom formatter is
+ set, this will be an HTML-escaped version of the string in the
+ `text` property.
+ @param {String} [results.highlighted] Highlighted (but not
+ formatted) result text. This property will only be set if a
+ highlighter is in use.
+ @param {Any} results.raw Raw, unformatted result in whatever form it
+ was provided by the source.
+ @param {String} results.text Plain text version of the result,
+ suitable for being inserted into the value of a text input field
+ or textarea when the result is selected by a user. This value is
+ not HTML-escaped and should not be inserted into the page using
+ `innerHTML` or `Node#setContent()`.
+
+ @preventable _defResultsFn
+ **/
+ this.publish(EVT_RESULTS, {
+ defaultFn: this._defResultsFn
+ });
+ },
+
+ destructor: function () {
+ this._acBaseEvents && this._acBaseEvents.detach();
+
+ delete this._acBaseEvents;
+ delete this._cache;
+ delete this._inputNode;
+ delete this._rawSource;
+ },
+
+ // -- Public Prototype Methods ---------------------------------------------
+
+ /**
+ Clears the result cache.
+
+ @method clearCache
+ @chainable
+ @since 3.5.0
+ **/
+ clearCache: function () {
+ this._cache && (this._cache = {});
+ return this;
+ },
+
+ /**
+ Sends a request to the configured source. If no source is configured, this
+ method won't do anything.
+
+ Usually there's no reason to call this method manually; it will be called
+ automatically when user input causes a `query` event to be fired. The only
+ time you'll need to call this method manually is if you want to force a
+ request to be sent when no user input has occurred.
+
+ @method sendRequest
+ @param {String} [query] Query to send. If specified, the `query` attribute
+ will be set to this query. If not specified, the current value of the
+ `query` attribute will be used.
+ @param {Function} [requestTemplate] Request template function. If not
+ specified, the current value of the `requestTemplate` attribute will be
+ used.
+ @chainable
+ **/
+ sendRequest: function (query, requestTemplate) {
+ var request,
+ source = this.get('source');
+
+ if (query || query === '') {
+ this._set(QUERY, query);
+ } else {
+ query = this.get(QUERY) || '';
+ }
+
+ if (source) {
+ if (!requestTemplate) {
+ requestTemplate = this.get(REQUEST_TEMPLATE);
+ }
+
+ request = requestTemplate ?
+ requestTemplate.call(this, query) : query;
+
+ Y.log('sendRequest: ' + request, 'info', 'autocomplete-base');
+
+ source.sendRequest({
+ query : query,
+ request: request,
+
+ callback: {
+ success: Y.bind(this._onResponse, this, query)
+ }
+ });
+ }
+
+ return this;
+ },
+
+ // -- Protected Lifecycle Methods ------------------------------------------
+
+ /**
+ Attaches event listeners and behaviors.
+
+ @method _bindUIACBase
+ @protected
+ **/
+ _bindUIACBase: function () {
+ var inputNode = this.get(INPUT_NODE),
+ tokenInput = inputNode && inputNode.tokenInput;
+
+ // If the inputNode has a node-tokeninput plugin attached, bind to the
+ // plugin's inputNode instead.
+ if (tokenInput) {
+ inputNode = tokenInput.get(INPUT_NODE);
+ this._set('tokenInput', tokenInput);
+ }
+
+ if (!inputNode) {
+ Y.error('No inputNode specified.');
+ return;
+ }
+
+ this._inputNode = inputNode;
+
+ this._acBaseEvents = new Y.EventHandle([
+ // This is the valueChange event on the inputNode, provided by the
+ // event-valuechange module, not our own valueChange.
+ inputNode.on(VALUE_CHANGE, this._onInputValueChange, this),
+ inputNode.on('blur', this._onInputBlur, this),
+
+ this.after(ALLOW_BROWSER_AC + 'Change', this._syncBrowserAutocomplete),
+ this.after('sourceTypeChange', this._afterSourceTypeChange),
+ this.after(VALUE_CHANGE, this._afterValueChange)
+ ]);
+ },
+
+ /**
+ Synchronizes the UI state of the `inputNode`.
+
+ @method _syncUIACBase
+ @protected
+ **/
+ _syncUIACBase: function () {
+ this._syncBrowserAutocomplete();
+ this.set(VALUE, this.get(INPUT_NODE).get(VALUE));
+ },
+
+ // -- Protected Prototype Methods ------------------------------------------
+
+ /**
+ Creates a DataSource-like object that simply returns the specified array as
+ a response. See the `source` attribute for more details.
+
+ @method _createArraySource
+ @param {Array} source
+ @return {Object} DataSource-like object.
+ @protected
+ **/
+ _createArraySource: function (source) {
+ var that = this;
+
+ return {
+ type: 'array',
+ sendRequest: function (request) {
+ that[_SOURCE_SUCCESS](source.concat(), request);
+ }
+ };
+ },
+
+ /**
+ Creates a DataSource-like object that passes the query to a custom-defined
+ function, which is expected to call the provided callback with an array of
+ results. See the `source` attribute for more details.
+
+ @method _createFunctionSource
+ @param {Function} source Function that accepts a query and a callback as
+ parameters, and calls the callback with an array of results.
+ @return {Object} DataSource-like object.
+ @protected
+ **/
+ _createFunctionSource: function (source) {
+ var that = this;
+
+ return {
+ type: 'function',
+ sendRequest: function (request) {
+ var value;
+
+ function afterResults(results) {
+ that[_SOURCE_SUCCESS](results || [], request);
+ }
+
+ // Allow both synchronous and asynchronous functions. If we get
+ // a truthy return value, assume the function is synchronous.
+ if ((value = source(request.query, afterResults))) {
+ afterResults(value);
+ }
+ }
+ };
+ },
+
+ /**
+ Creates a DataSource-like object that looks up queries as properties on the
+ specified object, and returns the found value (if any) as a response. See
+ the `source` attribute for more details.
+
+ @method _createObjectSource
+ @param {Object} source
+ @return {Object} DataSource-like object.
+ @protected
+ **/
+ _createObjectSource: function (source) {
+ var that = this;
+
+ return {
+ type: 'object',
+ sendRequest: function (request) {
+ var query = request.query;
+
+ that[_SOURCE_SUCCESS](
+ YObject.owns(source, query) ? source[query] : [],
+ request
+ );
+ }
+ };
+ },
+
+ /**
+ Returns `true` if _value_ is either a function or `null`.
+
+ @method _functionValidator
+ @param {Function|null} value Value to validate.
+ @protected
+ **/
+ _functionValidator: function (value) {
+ return value === null || isFunction(value);
+ },
+
+ /**
+ Faster and safer alternative to `Y.Object.getValue()`. Doesn't bother
+ casting the path to an array (since we already know it's an array) and
+ doesn't throw an error if a value in the middle of the object hierarchy is
+ neither `undefined` nor an object.
+
+ @method _getObjectValue
+ @param {Object} obj
+ @param {Array} path
+ @return {Any} Located value, or `undefined` if the value was
+ not found at the specified path.
+ @protected
+ **/
+ _getObjectValue: function (obj, path) {
+ if (!obj) {
+ return;
+ }
+
+ for (var i = 0, len = path.length; obj && i < len; i++) {
+ obj = obj[path[i]];
+ }
+
+ return obj;
+ },
+
+ /**
+ Parses result responses, performs filtering and highlighting, and fires the
+ `results` event.
+
+ @method _parseResponse
+ @param {String} query Query that generated these results.
+ @param {Object} response Response containing results.
+ @param {Object} data Raw response data.
+ @protected
+ **/
+ _parseResponse: function (query, response, data) {
+ var facade = {
+ data : data,
+ query : query,
+ results: []
+ },
+
+ listLocator = this.get(RESULT_LIST_LOCATOR),
+ results = [],
+ unfiltered = response && response.results,
+
+ filters,
+ formatted,
+ formatter,
+ highlighted,
+ highlighter,
+ i,
+ len,
+ maxResults,
+ result,
+ text,
+ textLocator;
+
+ if (unfiltered && listLocator) {
+ unfiltered = listLocator.call(this, unfiltered);
+ }
+
+ if (unfiltered && unfiltered.length) {
+ filters = this.get('resultFilters');
+ textLocator = this.get('resultTextLocator');
+
+ // Create a lightweight result object for each result to make them
+ // easier to work with. The various properties on the object
+ // represent different formats of the result, and will be populated
+ // as we go.
+ for (i = 0, len = unfiltered.length; i < len; ++i) {
+ result = unfiltered[i];
+
+ text = textLocator ?
+ textLocator.call(this, result) :
+ result.toString();
+
+ results.push({
+ display: Escape.html(text),
+ raw : result,
+ text : text
+ });
+ }
+
+ // Run the results through all configured result filters. Each
+ // filter returns an array of (potentially fewer) result objects,
+ // which is then passed to the next filter, and so on.
+ for (i = 0, len = filters.length; i < len; ++i) {
+ results = filters[i].call(this, query, results.concat());
+
+ if (!results) {
+ Y.log("Filter didn't return anything.", 'warn', 'autocomplete-base');
+ return;
+ }
+
+ if (!results.length) {
+ break;
+ }
+ }
+
+ if (results.length) {
+ formatter = this.get('resultFormatter');
+ highlighter = this.get('resultHighlighter');
+ maxResults = this.get('maxResults');
+
+ // If maxResults is set and greater than 0, limit the number of
+ // results.
+ if (maxResults && maxResults > 0 &&
+ results.length > maxResults) {
+ results.length = maxResults;
+ }
+
+ // Run the results through the configured highlighter (if any).
+ // The highlighter returns an array of highlighted strings (not
+ // an array of result objects), and these strings are then added
+ // to each result object.
+ if (highlighter) {
+ highlighted = highlighter.call(this, query,
+ results.concat());
+
+ if (!highlighted) {
+ Y.log("Highlighter didn't return anything.", 'warn', 'autocomplete-base');
+ return;
+ }
+
+ for (i = 0, len = highlighted.length; i < len; ++i) {
+ result = results[i];
+ result.highlighted = highlighted[i];
+ result.display = result.highlighted;
+ }
+ }
+
+ // Run the results through the configured formatter (if any) to
+ // produce the final formatted results. The formatter returns an
+ // array of strings or Node instances (not an array of result
+ // objects), and these strings/Nodes are then added to each
+ // result object.
+ if (formatter) {
+ formatted = formatter.call(this, query, results.concat());
+
+ if (!formatted) {
+ Y.log("Formatter didn't return anything.", 'warn', 'autocomplete-base');
+ return;
+ }
+
+ for (i = 0, len = formatted.length; i < len; ++i) {
+ results[i].display = formatted[i];
+ }
+ }
+ }
+ }
+
+ facade.results = results;
+ this.fire(EVT_RESULTS, facade);
+ },
+
+ /**
+ Returns the query portion of the specified input value, or `null` if there
+ is no suitable query within the input value.
+
+ If a query delimiter is defined, the query will be the last delimited part
+ of of the string.
+
+ @method _parseValue
+ @param {String} value Input value from which to extract the query.
+ @return {String|null} query
+ @protected
+ **/
+ _parseValue: function (value) {
+ var delim = this.get(QUERY_DELIMITER);
+
+ if (delim) {
+ value = value.split(delim);
+ value = value[value.length - 1];
+ }
+
+ return Lang.trimLeft(value);
+ },
+
+ /**
+ Setter for the `enableCache` attribute.
+
+ @method _setEnableCache
+ @param {Boolean} value
+ @protected
+ @since 3.5.0
+ **/
+ _setEnableCache: function (value) {
+ // When `this._cache` is an object, result sources will store cached
+ // results in it. When it's falsy, they won't. This way result sources
+ // don't need to get the value of the `enableCache` attribute on every
+ // request, which would be sloooow.
+ this._cache = value ? {} : null;
+ Y.log('Cache ' + (value ? 'enabled' : 'disabled'), 'debug', 'autocomplete-base');
+ },
+
+ /**
+ Setter for locator attributes.
+
+ @method _setLocator
+ @param {Function|String|null} locator
+ @return {Function|null}
+ @protected
+ **/
+ _setLocator: function (locator) {
+ if (this[_FUNCTION_VALIDATOR](locator)) {
+ return locator;
+ }
+
+ var that = this;
+
+ locator = locator.toString().split('.');
+
+ return function (result) {
+ return result && that._getObjectValue(result, locator);
+ };
+ },
+
+ /**
+ Setter for the `requestTemplate` attribute.
+
+ @method _setRequestTemplate
+ @param {Function|String|null} template
+ @return {Function|null}
+ @protected
+ **/
+ _setRequestTemplate: function (template) {
+ if (this[_FUNCTION_VALIDATOR](template)) {
+ return template;
+ }
+
+ template = template.toString();
+
+ return function (query) {
+ return Lang.sub(template, {query: encodeURIComponent(query)});
+ };
+ },
+
+ /**
+ Setter for the `resultFilters` attribute.
+
+ @method _setResultFilters
+ @param {Array|Function|String|null} filters `null`, a filter
+ function, an array of filter functions, or a string or array of strings
+ representing the names of methods on `Y.AutoCompleteFilters`.
+ @return {Function[]} Array of filter functions (empty if <i>filters</i> is
+ `null`).
+ @protected
+ **/
+ _setResultFilters: function (filters) {
+ var acFilters, getFilterFunction;
+
+ if (filters === null) {
+ return [];
+ }
+
+ acFilters = Y.AutoCompleteFilters;
+
+ getFilterFunction = function (filter) {
+ if (isFunction(filter)) {
+ return filter;
+ }
+
+ if (isString(filter) && acFilters &&
+ isFunction(acFilters[filter])) {
+ return acFilters[filter];
+ }
+
+ return false;
+ };
+
+ if (Lang.isArray(filters)) {
+ filters = YArray.map(filters, getFilterFunction);
+ return YArray.every(filters, function (f) { return !!f; }) ?
+ filters : INVALID_VALUE;
+ } else {
+ filters = getFilterFunction(filters);
+ return filters ? [filters] : INVALID_VALUE;
+ }
+ },
+
+ /**
+ Setter for the `resultHighlighter` attribute.
+
+ @method _setResultHighlighter
+ @param {Function|String|null} highlighter `null`, a highlighter function, or
+ a string representing the name of a method on
+ `Y.AutoCompleteHighlighters`.
+ @return {Function|null}
+ @protected
+ **/
+ _setResultHighlighter: function (highlighter) {
+ var acHighlighters;
+
+ if (this[_FUNCTION_VALIDATOR](highlighter)) {
+ return highlighter;
+ }
+
+ acHighlighters = Y.AutoCompleteHighlighters;
+
+ if (isString(highlighter) && acHighlighters &&
+ isFunction(acHighlighters[highlighter])) {
+ return acHighlighters[highlighter];
+ }
+
+ return INVALID_VALUE;
+ },
+
+ /**
+ Setter for the `source` attribute. Returns a DataSource or a DataSource-like
+ object depending on the type of _source_ and/or the value of the
+ `sourceType` attribute.
+
+ @method _setSource
+ @param {Any} source AutoComplete source. See the `source` attribute for
+ details.
+ @return {DataSource|Object}
+ @protected
+ **/
+ _setSource: function (source) {
+ var sourceType = this.get('sourceType') || Lang.type(source),
+ sourceSetter;
+
+ if ((source && isFunction(source.sendRequest))
+ || source === null
+ || sourceType === 'datasource') {
+
+ // Quacks like a DataSource instance (or null). Make it so!
+ this._rawSource = source;
+ return source;
+ }
+
+ // See if there's a registered setter for this source type.
+ if ((sourceSetter = AutoCompleteBase.SOURCE_TYPES[sourceType])) {
+ this._rawSource = source;
+ return Lang.isString(sourceSetter) ?
+ this[sourceSetter](source) : sourceSetter(source);
+ }
+
+ Y.error("Unsupported source type '" + sourceType + "'. Maybe autocomplete-sources isn't loaded?");
+ return INVALID_VALUE;
+ },
+
+ /**
+ Shared success callback for non-DataSource sources.
+
+ @method _sourceSuccess
+ @param {Any} data Response data.
+ @param {Object} request Request object.
+ @protected
+ **/
+ _sourceSuccess: function (data, request) {
+ request.callback.success({
+ data: data,
+ response: {results: data}
+ });
+ },
+
+ /**
+ Synchronizes the UI state of the `allowBrowserAutocomplete` attribute.
+
+ @method _syncBrowserAutocomplete
+ @protected
+ **/
+ _syncBrowserAutocomplete: function () {
+ var inputNode = this.get(INPUT_NODE);
+
+ if (inputNode.get('nodeName').toLowerCase() === 'input') {
+ inputNode.setAttribute('autocomplete',
+ this.get(ALLOW_BROWSER_AC) ? 'on' : 'off');
+ }
+ },
+
+ /**
+ Updates the query portion of the `value` attribute.
+
+ If a query delimiter is defined, the last delimited portion of the input
+ value will be replaced with the specified _value_.
+
+ @method _updateValue
+ @param {String} newVal New value.
+ @protected
+ **/
+ _updateValue: function (newVal) {
+ var delim = this.get(QUERY_DELIMITER),
+ insertDelim,
+ len,
+ prevVal;
+
+ newVal = Lang.trimLeft(newVal);
+
+ if (delim) {
+ insertDelim = trim(delim); // so we don't double up on spaces
+ prevVal = YArray.map(trim(this.get(VALUE)).split(delim), trim);
+ len = prevVal.length;
+
+ if (len > 1) {
+ prevVal[len - 1] = newVal;
+ newVal = prevVal.join(insertDelim + ' ');
+ }
+
+ newVal = newVal + insertDelim + ' ';
+ }
+
+ this.set(VALUE, newVal);
+ },
+
+ // -- Protected Event Handlers ---------------------------------------------
+
+ /**
+ Updates the current `source` based on the new `sourceType` to ensure that
+ the two attributes don't get out of sync when they're changed separately.
+
+ @method _afterSourceTypeChange
+ @param {EventFacade} e
+ @protected
+ **/
+ _afterSourceTypeChange: function (e) {
+ if (this._rawSource) {
+ this.set('source', this._rawSource);
+ }
+ },
+
+ /**
+ Handles change events for the `value` attribute.
+
+ @method _afterValueChange
+ @param {EventFacade} e
+ @protected
+ **/
+ _afterValueChange: function (e) {
+ var newVal = e.newVal,
+ self = this,
+ uiChange = e.src === AutoCompleteBase.UI_SRC,
+ delay, fire, minQueryLength, query;
+
+ // Update the UI if the value was changed programmatically.
+ if (!uiChange) {
+ self._inputNode.set(VALUE, newVal);
+ }
+
+ Y.log('valueChange: new: "' + newVal + '"; old: "' + e.prevVal + '"', 'info', 'autocomplete-base');
+
+ minQueryLength = self.get('minQueryLength');
+ query = self._parseValue(newVal) || '';
+
+ if (minQueryLength >= 0 && query.length >= minQueryLength) {
+ // Only query on changes that originate from the UI.
+ if (uiChange) {
+ delay = self.get('queryDelay');
+
+ fire = function () {
+ self.fire(EVT_QUERY, {
+ inputValue: newVal,
+ query : query,
+ src : e.src
+ });
+ };
+
+ if (delay) {
+ clearTimeout(self._delay);
+ self._delay = setTimeout(fire, delay);
+ } else {
+ fire();
+ }
+ } else {
+ // For programmatic value changes, just update the query
+ // attribute without sending a query.
+ self._set(QUERY, query);
+ }
+ } else {
+ clearTimeout(self._delay);
+
+ self.fire(EVT_CLEAR, {
+ prevVal: e.prevVal ? self._parseValue(e.prevVal) : null,
+ src : e.src
+ });
+ }
+ },
+
+ /**
+ Handles `blur` events on the input node.
+
+ @method _onInputBlur
+ @param {EventFacade} e
+ @protected
+ **/
+ _onInputBlur: function (e) {
+ var delim = this.get(QUERY_DELIMITER),
+ delimPos,
+ newVal,
+ value;
+
+ // If a query delimiter is set and the input's value contains one or
+ // more trailing delimiters, strip them.
+ if (delim && !this.get('allowTrailingDelimiter')) {
+ delim = Lang.trimRight(delim);
+ value = newVal = this._inputNode.get(VALUE);
+
+ if (delim) {
+ while ((newVal = Lang.trimRight(newVal)) &&
+ (delimPos = newVal.length - delim.length) &&
+ newVal.lastIndexOf(delim) === delimPos) {
+
+ newVal = newVal.substring(0, delimPos);
+ }
+ } else {
+ // Delimiter is one or more space characters, so just trim the
+ // value.
+ newVal = Lang.trimRight(newVal);
+ }
+
+ if (newVal !== value) {
+ this.set(VALUE, newVal);
+ }
+ }
+ },
+
+ /**
+ Handles `valueChange` events on the input node and fires a `query` event
+ when the input value meets the configured criteria.
+
+ @method _onInputValueChange
+ @param {EventFacade} e
+ @protected
+ **/
+ _onInputValueChange: function (e) {
+ var newVal = e.newVal;
+
+ // Don't query if the internal value is the same as the new value
+ // reported by valueChange.
+ if (newVal !== this.get(VALUE)) {
+ this.set(VALUE, newVal, {src: AutoCompleteBase.UI_SRC});
+ }
+ },
+
+ /**
+ Handles source responses and fires the `results` event.
+
+ @method _onResponse
+ @param {EventFacade} e
+ @protected
+ **/
+ _onResponse: function (query, e) {
+ // Ignore stale responses that aren't for the current query.
+ if (query === (this.get(QUERY) || '')) {
+ this._parseResponse(query || '', e.response, e.data);
+ }
+ },
+
+ // -- Protected Default Event Handlers -------------------------------------
+
+ /**
+ Default `clear` event handler. Sets the `results` attribute to an empty
+ array and `query` to null.
+
+ @method _defClearFn
+ @protected
+ **/
+ _defClearFn: function () {
+ this._set(QUERY, null);
+ this._set(RESULTS, []);
+ },
+
+ /**
+ Default `query` event handler. Sets the `query` attribute and sends a
+ request to the source if one is configured.
+
+ @method _defQueryFn
+ @param {EventFacade} e
+ @protected
+ **/
+ _defQueryFn: function (e) {
+ Y.log('query: "' + e.query + '"; inputValue: "' + e.inputValue + '"', 'info', 'autocomplete-base');
+ this.sendRequest(e.query); // sendRequest will set the 'query' attribute
+ },
+
+ /**
+ Default `results` event handler. Sets the `results` attribute to the latest
+ results.
+
+ @method _defResultsFn
+ @param {EventFacade} e
+ @protected
+ **/
+ _defResultsFn: function (e) {
+ Y.log('results: ' + Y.dump(e.results), 'info', 'autocomplete-base');
+ this._set(RESULTS, e[RESULTS]);
+ }
+};
+
+AutoCompleteBase.ATTRS = {
+ /**
+ Whether or not to enable the browser's built-in autocomplete functionality
+ for input fields.
+
+ @attribute allowBrowserAutocomplete
+ @type Boolean
+ @default false
+ **/
+ allowBrowserAutocomplete: {
+ value: false
+ },
+
+ /**
+ When a `queryDelimiter` is set, trailing delimiters will automatically be
+ stripped from the input value by default when the input node loses focus.
+ Set this to `true` to allow trailing delimiters.
+
+ @attribute allowTrailingDelimiter
+ @type Boolean
+ @default false
+ **/
+ allowTrailingDelimiter: {
+ value: false
+ },
+
+ /**
+ Whether or not to enable in-memory caching in result sources that support
+ it.
+
+ @attribute enableCache
+ @type Boolean
+ @default true
+ @since 3.5.0
+ **/
+ enableCache: {
+ lazyAdd: false, // we need the setter to run on init
+ setter: '_setEnableCache',
+ value: true
+ },
+
+ /**
+ Node to monitor for changes, which will generate `query` events when
+ appropriate. May be either an `<input>` or a `<textarea>`.
+
+ @attribute inputNode
+ @type Node|HTMLElement|String
+ @initOnly
+ **/
+ inputNode: {
+ setter: Y.one,
+ writeOnce: 'initOnly'
+ },
+
+ /**
+ Maximum number of results to return. A value of `0` or less will allow an
+ unlimited number of results.
+
+ @attribute maxResults
+ @type Number
+ @default 0
+ **/
+ maxResults: {
+ value: 0
+ },
+
+ /**
+ Minimum number of characters that must be entered before a `query` event
+ will be fired. A value of `0` allows empty queries; a negative value will
+ effectively disable all `query` events.
+
+ @attribute minQueryLength
+ @type Number
+ @default 1
+ **/
+ minQueryLength: {
+ value: 1
+ },
+
+ /**
+ Current query, or `null` if there is no current query.
+
+ The query might not be the same as the current value of the input node, both
+ for timing reasons (due to `queryDelay`) and because when one or more
+ `queryDelimiter` separators are in use, only the last portion of the
+ delimited input string will be used as the query value.
+
+ @attribute query
+ @type String|null
+ @default null
+ @readonly
+ **/
+ query: {
+ readOnly: true,
+ value: null
+ },
+
+ /**
+ Number of milliseconds to delay after input before triggering a `query`
+ event. If new input occurs before this delay is over, the previous input
+ event will be ignored and a new delay will begin.
+
+ This can be useful both to throttle queries to a remote data source and to
+ avoid distracting the user by showing them less relevant results before
+ they've paused their typing.
+
+ @attribute queryDelay
+ @type Number
+ @default 100
+ **/
+ queryDelay: {
+ value: 100
+ },
+
+ /**
+ Query delimiter string. When a delimiter is configured, the input value
+ will be split on the delimiter, and only the last portion will be used in
+ autocomplete queries and updated when the `query` attribute is
+ modified.
+
+ @attribute queryDelimiter
+ @type String|null
+ @default null
+ **/
+ queryDelimiter: {
+ value: null
+ },
+
+ /**
+ Source request template. This can be a function that accepts a query as a
+ parameter and returns a request string, or it can be a string containing the
+ placeholder "{query}", which will be replaced with the actual URI-encoded
+ query. In either case, the resulting string will be appended to the request
+ URL when the `source` attribute is set to a remote DataSource, JSONP URL, or
+ XHR URL (it will not be appended to YQL URLs).
+
+ While `requestTemplate` may be set to either a function or a string, it will
+ always be returned as a function that accepts a query argument and returns a
+ string.
+
+ @attribute requestTemplate
+ @type Function|String|null
+ @default null
+ **/
+ requestTemplate: {
+ setter: '_setRequestTemplate',
+ value: null
+ },
+
+ /**
+ Array of local result filter functions. If provided, each filter will be
+ called with two arguments when results are received: the query and an array
+ of result objects. See the documentation for the `results` event for a list
+ of the properties available on each result object.
+
+ Each filter is expected to return a filtered or modified version of the
+ results array, which will then be passed on to subsequent filters, then the
+ `resultHighlighter` function (if set), then the `resultFormatter` function
+ (if set), and finally to subscribers to the `results` event.
+
+ If no `source` is set, result filters will not be called.
+
+ Prepackaged result filters provided by the autocomplete-filters and
+ autocomplete-filters-accentfold modules can be used by specifying the filter
+ name as a string, such as `'phraseMatch'` (assuming the necessary filters
+ module is loaded).
+
+ @attribute resultFilters
+ @type Array
+ @default []
+ **/
+ resultFilters: {
+ setter: '_setResultFilters',
+ value: []
+ },
+
+ /**
+ Function which will be used to format results. If provided, this function
+ will be called with two arguments after results have been received and
+ filtered: the query and an array of result objects. The formatter is
+ expected to return an array of HTML strings or Node instances containing the
+ desired HTML for each result.
+
+ See the documentation for the `results` event for a list of the properties
+ available on each result object.
+
+ If no `source` is set, the formatter will not be called.
+
+ @attribute resultFormatter
+ @type Function|null
+ **/
+ resultFormatter: {
+ validator: _FUNCTION_VALIDATOR,
+ value: null
+ },
+
+ /**
+ Function which will be used to highlight results. If provided, this function
+ will be called with two arguments after results have been received and
+ filtered: the query and an array of filtered result objects. The highlighter
+ is expected to return an array of highlighted result text in the form of
+ HTML strings.
+
+ See the documentation for the `results` event for a list of the properties
+ available on each result object.
+
+ If no `source` is set, the highlighter will not be called.
+
+ @attribute resultHighlighter
+ @type Function|null
+ **/
+ resultHighlighter: {
+ setter: '_setResultHighlighter',
+ value: null
+ },
+
+ /**
+ Locator that should be used to extract an array of results from a non-array
+ response.
+
+ By default, no locator is applied, and all responses are assumed to be
+ arrays by default. If all responses are already arrays, you don't need to
+ define a locator.
+
+ The locator may be either a function (which will receive the raw response as
+ an argument and must return an array) or a string representing an object
+ path, such as "foo.bar.baz" (which would return the value of
+ `result.foo.bar.baz` if the response is an object).
+
+ While `resultListLocator` may be set to either a function or a string, it
+ will always be returned as a function that accepts a response argument and
+ returns an array.
+
+ @attribute resultListLocator
+ @type Function|String|null
+ **/
+ resultListLocator: {
+ setter: '_setLocator',
+ value: null
+ },
+
+ /**
+ Current results, or an empty array if there are no results.
+
+ @attribute results
+ @type Array
+ @default []
+ @readonly
+ **/
+ results: {
+ readOnly: true,
+ value: []
+ },
+
+ /**
+ Locator that should be used to extract a plain text string from a non-string
+ result item. The resulting text value will typically be the value that ends
+ up being inserted into an input field or textarea when the user of an
+ autocomplete implementation selects a result.
+
+ By default, no locator is applied, and all results are assumed to be plain
+ text strings. If all results are already plain text strings, you don't need
+ to define a locator.
+
+ The locator may be either a function (which will receive the raw result as
+ an argument and must return a string) or a string representing an object
+ path, such as "foo.bar.baz" (which would return the value of
+ `result.foo.bar.baz` if the result is an object).
+
+ While `resultTextLocator` may be set to either a function or a string, it
+ will always be returned as a function that accepts a result argument and
+ returns a string.
+
+ @attribute resultTextLocator
+ @type Function|String|null
+ **/
+ resultTextLocator: {
+ setter: '_setLocator',
+ value: null
+ },
+
+ /**
+ Source for autocomplete results. The following source types are supported:
+
+ <dl>
+ <dt>Array</dt>
+ <dd>
+ <p>
+ The full array will be provided to any configured filters for each
+ query. This is an easy way to create a fully client-side autocomplete
+ implementation.
+ </p>
+
+ <p>
+ Example: `['first result', 'second result', 'etc']`
+ </p>
+ </dd>
+
+ <dt>DataSource</dt>
+ <dd>
+ A `DataSource` instance or other object that provides a DataSource-like
+ `sendRequest` method. See the `DataSource` documentation for details.
+ </dd>
+
+ <dt>Function</dt>
+ <dd>
+ <p>
+ A function source will be called with the current query and a
+ callback function as parameters, and should either return an array of
+ results (for synchronous operation) or return nothing and pass an
+ array of results to the provided callback (for asynchronous
+ operation).
+ </p>
+
+ <p>
+ Example (synchronous):
+ </p>
+
+ <pre>
+ function (query) {
+ return ['foo', 'bar'];
+ }
+ </pre>
+
+ <p>
+ Example (async):
+ </p>
+
+ <pre>
+ function (query, callback) {
+ callback(['foo', 'bar']);
+ }
+ </pre>
+ </dd>
+
+ <dt>Object</dt>
+ <dd>
+ <p>
+ An object will be treated as a query hashmap. If a property on the
+ object matches the current query, the value of that property will be
+ used as the response.
+ </p>
+
+ <p>
+ The response is assumed to be an array of results by default. If the
+ response is not an array, provide a `resultListLocator` to
+ process the response and return an array.
+ </p>
+
+ <p>
+ Example: `{foo: ['foo result 1', 'foo result 2'], bar: ['bar result']}`
+ </p>
+ </dd>
+ </dl>
+
+ If the optional `autocomplete-sources` module is loaded, then
+ the following additional source types will be supported as well:
+
+ <dl>
+ <dt><select> Node</dt>
+ <dd>
+ You may provide a YUI Node instance wrapping a <select>
+ element, and the options in the list will be used as results. You
+ will also need to specify a `resultTextLocator` of 'text'
+ or 'value', depending on what you want to use as the text of the
+ result.
+
+ Each result will be an object with the following properties:
+
+ <dl>
+ <dt>html (String)</dt>
+ <dd>
+ <p>HTML content of the <option> element.</p>
+ </dd>
+
+ <dt>index (Number)</dt>
+ <dd>
+ <p>Index of the <option> element in the list.</p>
+ </dd>
+
+ <dt>node (Y.Node)</dt>
+ <dd>
+ <p>Node instance referring to the original <option> element.</p>
+ </dd>
+
+ <dt>selected (Boolean)</dt>
+ <dd>
+ <p>Whether or not this item is currently selected in the
+ <select> list.</p>
+ </dd>
+
+ <dt>text (String)</dt>
+ <dd>
+ <p>Text content of the <option> element.</p>
+ </dd>
+
+ <dt>value (String)</dt>
+ <dd>
+ <p>Value of the <option> element.</p>
+ </dd>
+ </dl>
+ </dd>
+
+ <dt>String (JSONP URL)</dt>
+ <dd>
+ <p>
+ If a URL with a `{callback}` placeholder is provided, it will be used to
+ make a JSONP request. The `{query}` placeholder will be replaced with
+ the current query, and the `{callback}` placeholder will be replaced
+ with an internally-generated JSONP callback name. Both placeholders must
+ appear in the URL, or the request will fail. An optional `{maxResults}`
+ placeholder may also be provided, and will be replaced with the value of
+ the maxResults attribute (or 1000 if the maxResults attribute is 0 or
+ less).
+ </p>
+
+ <p>
+ The response is assumed to be an array of results by default. If the
+ response is not an array, provide a `resultListLocator` to process the
+ response and return an array.
+ </p>
+
+ <p>
+ <strong>The `jsonp` module must be loaded in order for
+ JSONP URL sources to work.</strong> If the `jsonp` module
+ is not already loaded, it will be loaded on demand if possible.
+ </p>
+
+ <p>
+ Example: `'http://example.com/search?q={query}&callback={callback}'`
+ </p>
+ </dd>
+
+ <dt>String (XHR URL)</dt>
+ <dd>
+ <p>
+ If a URL without a `{callback}` placeholder is provided, it will be used
+ to make a same-origin XHR request. The `{query}` placeholder will be
+ replaced with the current query. An optional `{maxResults}` placeholder
+ may also be provided, and will be replaced with the value of the
+ maxResults attribute (or 1000 if the maxResults attribute is 0 or less).
+ </p>
+
+ <p>
+ The response is assumed to be a JSON array of results by default. If the
+ response is a JSON object and not an array, provide a
+ `resultListLocator` to process the response and return an array. If the
+ response is in some form other than JSON, you will need to use a custom
+ DataSource instance as the source.
+ </p>
+
+ <p>
+ <strong>The `io-base` and `json-parse` modules
+ must be loaded in order for XHR URL sources to work.</strong> If
+ these modules are not already loaded, they will be loaded on demand
+ if possible.
+ </p>
+
+ <p>
+ Example: `'http://example.com/search?q={query}'`
+ </p>
+ </dd>
+
+ <dt>String (YQL query)</dt>
+ <dd>
+ <p>
+ If a YQL query is provided, it will be used to make a YQL request. The
+ `{query}` placeholder will be replaced with the current autocomplete
+ query. This placeholder must appear in the YQL query, or the request
+ will fail. An optional `{maxResults}` placeholder may also be provided,
+ and will be replaced with the value of the maxResults attribute (or 1000
+ if the maxResults attribute is 0 or less).
+ </p>
+
+ <p>
+ <strong>The `yql` module must be loaded in order for YQL
+ sources to work.</strong> If the `yql` module is not
+ already loaded, it will be loaded on demand if possible.
+ </p>
+
+ <p>
+ Example: `'select * from search.suggest where query="{query}"'`
+ </p>
+ </dd>
+ </dl>
+
+ As an alternative to providing a source, you could simply listen for `query`
+ events and handle them any way you see fit. Providing a source is optional,
+ but will usually be simpler.
+
+ @attribute source
+ @type Array|DataSource|Function|Node|Object|String|null
+ **/
+ source: {
+ setter: '_setSource',
+ value: null
+ },
+
+ /**
+ May be used to force a specific source type, overriding the automatic source
+ type detection. It should almost never be necessary to do this, but as they
+ taught us in the Boy Scouts, one should always be prepared, so it's here if
+ you need it. Be warned that if you set this attribute and something breaks,
+ it's your own fault.
+
+ Supported `sourceType` values are: 'array', 'datasource', 'function', and
+ 'object'.
+
+ If the `autocomplete-sources` module is loaded, the following additional
+ source types are supported: 'io', 'jsonp', 'select', 'string', 'yql'
+
+ @attribute sourceType
+ @type String
+ **/
+ sourceType: {
+ value: null
+ },
+
+ /**
+ If the `inputNode` specified at instantiation time has a `node-tokeninput`
+ plugin attached to it, this attribute will be a reference to the
+ `Y.Plugin.TokenInput` instance.
+
+ @attribute tokenInput
+ @type Plugin.TokenInput
+ @readonly
+ **/
+ tokenInput: {
+ readOnly: true
+ },
+
+ /**
+ Current value of the input node.
+
+ @attribute value
+ @type String
+ @default ''
+ **/
+ value: {
+ // Why duplicate this._inputNode.get('value')? Because we need a
+ // reliable way to track the source of value changes. We want to perform
+ // completion when the user changes the value, but not when we change
+ // the value.
+ value: ''
+ }
+};
+
+// This tells Y.Base.create() to copy these static properties to any class
+// AutoCompleteBase is mixed into.
+AutoCompleteBase._buildCfg = {
+ aggregates: ['SOURCE_TYPES'],
+ statics : ['UI_SRC']
+};
+
+/**
+Mapping of built-in source types to their setter functions. DataSource instances
+and DataSource-like objects are handled natively, so are not mapped here.
+
+@property SOURCE_TYPES
+@type {Object}
+@static
+**/
+AutoCompleteBase.SOURCE_TYPES = {
+ array : '_createArraySource',
+ 'function': '_createFunctionSource',
+ object : '_createObjectSource'
+};
+
+AutoCompleteBase.UI_SRC = (Y.Widget && Y.Widget.UI_SRC) || 'ui';
+
+Y.AutoCompleteBase = AutoCompleteBase;
+
+
+}, '3.10.3', {
+ "optional": [
+ "autocomplete-sources"
+ ],
+ "requires": [
+ "array-extras",
+ "base-build",
+ "escape",
+ "event-valuechange",
+ "node-base"
+ ]
+});