diff -r 322d0feea350 -r 89ef5ed3c48b src/cm/media/js/lib/yui/yui_3.10.3/build/autocomplete-base/autocomplete-base-debug.js --- /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 filters 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 `` or a `