~ubuntu-branches/ubuntu/raring/maas/raring-updates

« back to all changes in this revision

Viewing changes to src/maasserver/static/jslibs/yui/3.4.1/build/autocomplete-base/autocomplete-base.js

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2012-07-03 17:42:37 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20120703174237-p8l0keuuznfg721k
Tags: 0.1+bzr709+dfsg-0ubuntu1
* New Upstream release
* debian/control:
  - Depends on python-celery, python-tempita, libjs-yui3-{full,min},
    libjs-raphael
* debian/maas.install:
  - Install apiclient, celeryconfig.py, maas-import-pxe-files, preseeds_v2.
  - Update to install various files from chroot, rather tha manually copy
    them from the source.
* debian/maas.links: symlink celeryconfig.py
* debian/maas.maas-celery.upstart: Add job.
* debian/rules:
  - Install celery upstart job.
  - Do not install jslibs as packages are now used.
  - Drop copying of maas_local_settings_sample.py as source now ships
    a maas_local_settings.py
* debian/patches:
  - 04-maas-http-fix.patch: Drop. Merged upstream.
  - 01-fix-database-settings.patch: Refreshed.
  - 99_enums_js.patch: Added until creation of enum.js / build process
    is fixed.
* debian/maas.postinst: Update bzr version to correctly handle upgrades.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
YUI 3.4.1 (build 4118)
3
 
Copyright 2011 Yahoo! Inc. All rights reserved.
4
 
Licensed under the BSD License.
5
 
http://yuilibrary.com/license/
6
 
*/
7
 
YUI.add('autocomplete-base', function(Y) {
8
 
 
9
 
/**
10
 
 * Provides automatic input completion or suggestions for text input fields and
11
 
 * textareas.
12
 
 *
13
 
 * @module autocomplete
14
 
 * @main autocomplete
15
 
 * @since 3.3.0
16
 
 */
17
 
 
18
 
/**
19
 
 * <code>Y.Base</code> extension that provides core autocomplete logic (but no
20
 
 * UI implementation) for a text input field or textarea. Must be mixed into a
21
 
 * <code>Y.Base</code>-derived class to be useful.
22
 
 *
23
 
 * @submodule autocomplete-base
24
 
 */
25
 
 
26
 
/**
27
 
 * <p>
28
 
 * Extension that provides core autocomplete logic (but no UI implementation)
29
 
 * for a text input field or textarea.
30
 
 * </p>
31
 
 *
32
 
 * <p>
33
 
 * The <code>AutoCompleteBase</code> class provides events and attributes that
34
 
 * abstract away core autocomplete logic and configuration, but does not provide
35
 
 * a widget implementation or suggestion UI. For a prepackaged autocomplete
36
 
 * widget, see <code>AutoCompleteList</code>.
37
 
 * </p>
38
 
 *
39
 
 * <p>
40
 
 * This extension cannot be instantiated directly, since it doesn't provide an
41
 
 * actual implementation. It's intended to be mixed into a
42
 
 * <code>Y.Base</code>-based class or widget.
43
 
 * </p>
44
 
 *
45
 
 * <p>
46
 
 * <code>Y.Widget</code>-based example:
47
 
 * </p>
48
 
 *
49
 
 * <pre>
50
 
 * YUI().use('autocomplete-base', 'widget', function (Y) {
51
 
 * &nbsp;&nbsp;var MyAC = Y.Base.create('myAC', Y.Widget, [Y.AutoCompleteBase], {
52
 
 * &nbsp;&nbsp;&nbsp;&nbsp;// Custom prototype methods and properties.
53
 
 * &nbsp;&nbsp;}, {
54
 
 * &nbsp;&nbsp;&nbsp;&nbsp;// Custom static methods and properties.
55
 
 * &nbsp;&nbsp;});
56
 
 * &nbsp;
57
 
 * &nbsp;&nbsp;// Custom implementation code.
58
 
 * });
59
 
 * </pre>
60
 
 *
61
 
 * <p>
62
 
 * <code>Y.Base</code>-based example:
63
 
 * </p>
64
 
 *
65
 
 * <pre>
66
 
 * YUI().use('autocomplete-base', function (Y) {
67
 
 * &nbsp;&nbsp;var MyAC = Y.Base.create('myAC', Y.Base, [Y.AutoCompleteBase], {
68
 
 * &nbsp;&nbsp;&nbsp;&nbsp;initializer: function () {
69
 
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this._bindUIACBase();
70
 
 * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this._syncUIACBase();
71
 
 * &nbsp;&nbsp;&nbsp;&nbsp;},
72
 
 * &nbsp;
73
 
 * &nbsp;&nbsp;&nbsp;&nbsp;// Custom prototype methods and properties.
74
 
 * &nbsp;&nbsp;}, {
75
 
 * &nbsp;&nbsp;&nbsp;&nbsp;// Custom static methods and properties.
76
 
 * &nbsp;&nbsp;});
77
 
 * &nbsp;
78
 
 * &nbsp;&nbsp;// Custom implementation code.
79
 
 * });
80
 
 * </pre>
81
 
 *
82
 
 * @class AutoCompleteBase
83
 
 */
84
 
 
85
 
var Escape  = Y.Escape,
86
 
    Lang    = Y.Lang,
87
 
    YArray  = Y.Array,
88
 
    YObject = Y.Object,
89
 
 
90
 
    isFunction = Lang.isFunction,
91
 
    isString   = Lang.isString,
92
 
    trim       = Lang.trim,
93
 
 
94
 
    INVALID_VALUE = Y.Attribute.INVALID_VALUE,
95
 
 
96
 
    _FUNCTION_VALIDATOR = '_functionValidator',
97
 
    _SOURCE_SUCCESS     = '_sourceSuccess',
98
 
 
99
 
    ALLOW_BROWSER_AC    = 'allowBrowserAutocomplete',
100
 
    INPUT_NODE          = 'inputNode',
101
 
    QUERY               = 'query',
102
 
    QUERY_DELIMITER     = 'queryDelimiter',
103
 
    REQUEST_TEMPLATE    = 'requestTemplate',
104
 
    RESULTS             = 'results',
105
 
    RESULT_LIST_LOCATOR = 'resultListLocator',
106
 
    VALUE               = 'value',
107
 
    VALUE_CHANGE        = 'valueChange',
108
 
 
109
 
    EVT_CLEAR   = 'clear',
110
 
    EVT_QUERY   = QUERY,
111
 
    EVT_RESULTS = RESULTS;
112
 
 
113
 
function AutoCompleteBase() {
114
 
    // AOP bindings.
115
 
    Y.before(this._bindUIACBase, this, 'bindUI');
116
 
    Y.before(this._destructorACBase, this, 'destructor');
117
 
    Y.before(this._syncUIACBase, this, 'syncUI');
118
 
 
119
 
    // -- Public Events --------------------------------------------------------
120
 
 
121
 
    /**
122
 
     * Fires after the query has been completely cleared or no longer meets the
123
 
     * minimum query length requirement.
124
 
     *
125
 
     * @event clear
126
 
     * @param {EventFacade} e Event facade with the following additional
127
 
     *   properties:
128
 
     *
129
 
     * <dl>
130
 
     *   <dt>prevVal (String)</dt>
131
 
     *   <dd>
132
 
     *     Value of the query before it was cleared.
133
 
     *   </dd>
134
 
     * </dl>
135
 
     *
136
 
     * @preventable _defClearFn
137
 
     */
138
 
    this.publish(EVT_CLEAR, {
139
 
        defaultFn: this._defClearFn
140
 
    });
141
 
 
142
 
    /**
143
 
     * Fires when the contents of the input field have changed and the input
144
 
     * value meets the criteria necessary to generate an autocomplete query.
145
 
     *
146
 
     * @event query
147
 
     * @param {EventFacade} e Event facade with the following additional
148
 
     *   properties:
149
 
     *
150
 
     * <dl>
151
 
     *   <dt>inputValue (String)</dt>
152
 
     *   <dd>
153
 
     *     Full contents of the text input field or textarea that generated
154
 
     *     the query.
155
 
     *   </dd>
156
 
     *
157
 
     *   <dt>query (String)</dt>
158
 
     *   <dd>
159
 
     *     Autocomplete query. This is the string that will be used to
160
 
     *     request completion results. It may or may not be the same as
161
 
     *     <code>inputValue</code>.
162
 
     *   </dd>
163
 
     * </dl>
164
 
     *
165
 
     * @preventable _defQueryFn
166
 
     */
167
 
    this.publish(EVT_QUERY, {
168
 
        defaultFn: this._defQueryFn
169
 
    });
170
 
 
171
 
    /**
172
 
     * Fires after query results are received from the <code>source</code>. If
173
 
     * no source has been set, this event will not fire.
174
 
     *
175
 
     * @event results
176
 
     * @param {EventFacade} e Event facade with the following additional
177
 
     *   properties:
178
 
     *
179
 
     * <dl>
180
 
     *   <dt>data (Array|Object)</dt>
181
 
     *   <dd>
182
 
     *     Raw, unfiltered result data (if available).
183
 
     *   </dd>
184
 
     *
185
 
     *   <dt>query (String)</dt>
186
 
     *   <dd>
187
 
     *     Query that generated these results.
188
 
     *   </dd>
189
 
     *
190
 
     *   <dt>results (Array)</dt>
191
 
     *   <dd>
192
 
     *     Array of filtered, formatted, and highlighted results. Each item in
193
 
     *     the array is an object with the following properties:
194
 
     *
195
 
     *     <dl>
196
 
     *       <dt>display (Node|HTMLElement|String)</dt>
197
 
     *       <dd>
198
 
     *         Formatted result HTML suitable for display to the user. If no
199
 
     *         custom formatter is set, this will be an HTML-escaped version of
200
 
     *         the string in the <code>text</code> property.
201
 
     *       </dd>
202
 
     *
203
 
     *       <dt>highlighted (String)</dt>
204
 
     *       <dd>
205
 
     *         Highlighted (but not formatted) result text. This property will
206
 
     *         only be set if a highlighter is in use.
207
 
     *       </dd>
208
 
     *
209
 
     *       <dt>raw (mixed)</dt>
210
 
     *       <dd>
211
 
     *         Raw, unformatted result in whatever form it was provided by the
212
 
     *         <code>source</code>.
213
 
     *       </dd>
214
 
     *
215
 
     *       <dt>text (String)</dt>
216
 
     *       <dd>
217
 
     *         Plain text version of the result, suitable for being inserted
218
 
     *         into the value of a text input field or textarea when the result
219
 
     *         is selected by a user. This value is not HTML-escaped and should
220
 
     *         not be inserted into the page using innerHTML.
221
 
     *       </dd>
222
 
     *     </dl>
223
 
     *   </dd>
224
 
     * </dl>
225
 
     *
226
 
     * @preventable _defResultsFn
227
 
     */
228
 
    this.publish(EVT_RESULTS, {
229
 
        defaultFn: this._defResultsFn
230
 
    });
231
 
}
232
 
 
233
 
// -- Public Static Properties -------------------------------------------------
234
 
AutoCompleteBase.ATTRS = {
235
 
    /**
236
 
     * Whether or not to enable the browser's built-in autocomplete
237
 
     * functionality for input fields.
238
 
     *
239
 
     * @attribute allowBrowserAutocomplete
240
 
     * @type Boolean
241
 
     * @default false
242
 
     */
243
 
    allowBrowserAutocomplete: {
244
 
        value: false
245
 
    },
246
 
 
247
 
    /**
248
 
     * When a <code>queryDelimiter</code> is set, trailing delimiters will
249
 
     * automatically be stripped from the input value by default when the
250
 
     * input node loses focus. Set this to <code>true</code> to allow trailing
251
 
     * delimiters.
252
 
     *
253
 
     * @attribute allowTrailingDelimiter
254
 
     * @type Boolean
255
 
     * @default false
256
 
     */
257
 
    allowTrailingDelimiter: {
258
 
        value: false
259
 
    },
260
 
 
261
 
    /**
262
 
     * Node to monitor for changes, which will generate <code>query</code>
263
 
     * events when appropriate. May be either an input field or a textarea.
264
 
     *
265
 
     * @attribute inputNode
266
 
     * @type Node|HTMLElement|String
267
 
     * @writeonce
268
 
     */
269
 
    inputNode: {
270
 
        setter: Y.one,
271
 
        writeOnce: 'initOnly'
272
 
    },
273
 
 
274
 
    /**
275
 
     * Maximum number of results to return. A value of <code>0</code> or less
276
 
     * will allow an unlimited number of results.
277
 
     *
278
 
     * @attribute maxResults
279
 
     * @type Number
280
 
     * @default 0
281
 
     */
282
 
    maxResults: {
283
 
        value: 0
284
 
    },
285
 
 
286
 
    /**
287
 
     * Minimum number of characters that must be entered before a
288
 
     * <code>query</code> event will be fired. A value of <code>0</code>
289
 
     * allows empty queries; a negative value will effectively disable all
290
 
     * <code>query</code> events.
291
 
     *
292
 
     * @attribute minQueryLength
293
 
     * @type Number
294
 
     * @default 1
295
 
     */
296
 
    minQueryLength: {
297
 
        value: 1
298
 
    },
299
 
 
300
 
    /**
301
 
     * <p>
302
 
     * Current query, or <code>null</code> if there is no current query.
303
 
     * </p>
304
 
     *
305
 
     * <p>
306
 
     * The query might not be the same as the current value of the input
307
 
     * node, both for timing reasons (due to <code>queryDelay</code>) and
308
 
     * because when one or more <code>queryDelimiter</code> separators are
309
 
     * in use, only the last portion of the delimited input string will be
310
 
     * used as the query value.
311
 
     * </p>
312
 
     *
313
 
     * @attribute query
314
 
     * @type String|null
315
 
     * @default null
316
 
     * @readonly
317
 
     */
318
 
    query: {
319
 
        readOnly: true,
320
 
        value: null
321
 
    },
322
 
 
323
 
    /**
324
 
     * <p>
325
 
     * Number of milliseconds to delay after input before triggering a
326
 
     * <code>query</code> event. If new input occurs before this delay is
327
 
     * over, the previous input event will be ignored and a new delay will
328
 
     * begin.
329
 
     * </p>
330
 
     *
331
 
     * <p>
332
 
     * This can be useful both to throttle queries to a remote data source
333
 
     * and to avoid distracting the user by showing them less relevant
334
 
     * results before they've paused their typing.
335
 
     * </p>
336
 
     *
337
 
     * @attribute queryDelay
338
 
     * @type Number
339
 
     * @default 100
340
 
     */
341
 
    queryDelay: {
342
 
        value: 100
343
 
    },
344
 
 
345
 
    /**
346
 
     * Query delimiter string. When a delimiter is configured, the input value
347
 
     * will be split on the delimiter, and only the last portion will be used in
348
 
     * autocomplete queries and updated when the <code>query</code> attribute is
349
 
     * modified.
350
 
     *
351
 
     * @attribute queryDelimiter
352
 
     * @type String|null
353
 
     * @default null
354
 
     */
355
 
    queryDelimiter: {
356
 
        value: null
357
 
    },
358
 
 
359
 
    /**
360
 
     * <p>
361
 
     * Source request template. This can be a function that accepts a query as a
362
 
     * parameter and returns a request string, or it can be a string containing
363
 
     * the placeholder "{query}", which will be replaced with the actual
364
 
     * URI-encoded query. In either case, the resulting string will be appended
365
 
     * to the request URL when the <code>source</code> attribute is set to a
366
 
     * remote DataSource, JSONP URL, or XHR URL (it will not be appended to YQL
367
 
     * URLs).
368
 
     * </p>
369
 
     *
370
 
     * <p>
371
 
     * While <code>requestTemplate</code> may be set to either a function or
372
 
     * a string, it will always be returned as a function that accepts a
373
 
     * query argument and returns a string.
374
 
     * </p>
375
 
     *
376
 
     * @attribute requestTemplate
377
 
     * @type Function|String|null
378
 
     * @default null
379
 
     */
380
 
    requestTemplate: {
381
 
        setter: '_setRequestTemplate',
382
 
        value: null
383
 
    },
384
 
 
385
 
    /**
386
 
     * <p>
387
 
     * Array of local result filter functions. If provided, each filter
388
 
     * will be called with two arguments when results are received: the query
389
 
     * and an array of result objects. See the documentation for the
390
 
     * <code>results</code> event for a list of the properties available on each
391
 
     * result object.
392
 
     * </p>
393
 
     *
394
 
     * <p>
395
 
     * Each filter is expected to return a filtered or modified version of the
396
 
     * results array, which will then be passed on to subsequent filters, then
397
 
     * the <code>resultHighlighter</code> function (if set), then the
398
 
     * <code>resultFormatter</code> function (if set), and finally to
399
 
     * subscribers to the <code>results</code> event.
400
 
     * </p>
401
 
     *
402
 
     * <p>
403
 
     * If no <code>source</code> is set, result filters will not be called.
404
 
     * </p>
405
 
     *
406
 
     * <p>
407
 
     * Prepackaged result filters provided by the autocomplete-filters and
408
 
     * autocomplete-filters-accentfold modules can be used by specifying the
409
 
     * filter name as a string, such as <code>'phraseMatch'</code> (assuming
410
 
     * the necessary filters module is loaded).
411
 
     * </p>
412
 
     *
413
 
     * @attribute resultFilters
414
 
     * @type Array
415
 
     * @default []
416
 
     */
417
 
    resultFilters: {
418
 
        setter: '_setResultFilters',
419
 
        value: []
420
 
    },
421
 
 
422
 
    /**
423
 
     * <p>
424
 
     * Function which will be used to format results. If provided, this function
425
 
     * will be called with two arguments after results have been received and
426
 
     * filtered: the query and an array of result objects. The formatter is
427
 
     * expected to return an array of HTML strings or Node instances containing
428
 
     * the desired HTML for each result.
429
 
     * </p>
430
 
     *
431
 
     * <p>
432
 
     * See the documentation for the <code>results</code> event for a list of
433
 
     * the properties available on each result object.
434
 
     * </p>
435
 
     *
436
 
     * <p>
437
 
     * If no <code>source</code> is set, the formatter will not be called.
438
 
     * </p>
439
 
     *
440
 
     * @attribute resultFormatter
441
 
     * @type Function|null
442
 
     */
443
 
    resultFormatter: {
444
 
        validator: _FUNCTION_VALIDATOR
445
 
    },
446
 
 
447
 
    /**
448
 
     * <p>
449
 
     * Function which will be used to highlight results. If provided, this
450
 
     * function will be called with two arguments after results have been
451
 
     * received and filtered: the query and an array of filtered result objects.
452
 
     * The highlighter is expected to return an array of highlighted result
453
 
     * text in the form of HTML strings.
454
 
     * </p>
455
 
     *
456
 
     * <p>
457
 
     * See the documentation for the <code>results</code> event for a list of
458
 
     * the properties available on each result object.
459
 
     * </p>
460
 
     *
461
 
     * <p>
462
 
     * If no <code>source</code> is set, the highlighter will not be called.
463
 
     * </p>
464
 
     *
465
 
     * @attribute resultHighlighter
466
 
     * @type Function|null
467
 
     */
468
 
    resultHighlighter: {
469
 
        setter: '_setResultHighlighter'
470
 
    },
471
 
 
472
 
    /**
473
 
     * <p>
474
 
     * Locator that should be used to extract an array of results from a
475
 
     * non-array response.
476
 
     * </p>
477
 
     *
478
 
     * <p>
479
 
     * By default, no locator is applied, and all responses are assumed to be
480
 
     * arrays by default. If all responses are already arrays, you don't need to
481
 
     * define a locator.
482
 
     * </p>
483
 
     *
484
 
     * <p>
485
 
     * The locator may be either a function (which will receive the raw response
486
 
     * as an argument and must return an array) or a string representing an
487
 
     * object path, such as "foo.bar.baz" (which would return the value of
488
 
     * <code>result.foo.bar.baz</code> if the response is an object).
489
 
     * </p>
490
 
     *
491
 
     * <p>
492
 
     * While <code>resultListLocator</code> may be set to either a function or a
493
 
     * string, it will always be returned as a function that accepts a response
494
 
     * argument and returns an array.
495
 
     * </p>
496
 
     *
497
 
     * @attribute resultListLocator
498
 
     * @type Function|String|null
499
 
     */
500
 
    resultListLocator: {
501
 
        setter: '_setLocator'
502
 
    },
503
 
 
504
 
    /**
505
 
     * Current results, or an empty array if there are no results.
506
 
     *
507
 
     * @attribute results
508
 
     * @type Array
509
 
     * @default []
510
 
     * @readonly
511
 
     */
512
 
    results: {
513
 
        readOnly: true,
514
 
        value: []
515
 
    },
516
 
 
517
 
    /**
518
 
     * <p>
519
 
     * Locator that should be used to extract a plain text string from a
520
 
     * non-string result item. The resulting text value will typically be the
521
 
     * value that ends up being inserted into an input field or textarea when
522
 
     * the user of an autocomplete implementation selects a result.
523
 
     * </p>
524
 
     *
525
 
     * <p>
526
 
     * By default, no locator is applied, and all results are assumed to be
527
 
     * plain text strings. If all results are already plain text strings, you
528
 
     * don't need to define a locator.
529
 
     * </p>
530
 
     *
531
 
     * <p>
532
 
     * The locator may be either a function (which will receive the raw result
533
 
     * as an argument and must return a string) or a string representing an
534
 
     * object path, such as "foo.bar.baz" (which would return the value of
535
 
     * <code>result.foo.bar.baz</code> if the result is an object).
536
 
     * </p>
537
 
     *
538
 
     * <p>
539
 
     * While <code>resultTextLocator</code> may be set to either a function or a
540
 
     * string, it will always be returned as a function that accepts a result
541
 
     * argument and returns a string.
542
 
     * </p>
543
 
     *
544
 
     * @attribute resultTextLocator
545
 
     * @type Function|String|null
546
 
     */
547
 
    resultTextLocator: {
548
 
        setter: '_setLocator'
549
 
    },
550
 
 
551
 
    /**
552
 
     * <p>
553
 
     * Source for autocomplete results. The following source types are
554
 
     * supported:
555
 
     * </p>
556
 
     *
557
 
     * <dl>
558
 
     *   <dt>Array</dt>
559
 
     *   <dd>
560
 
     *     <p>
561
 
     *     <i>Example:</i> <code>['first result', 'second result', 'etc']</code>
562
 
     *     </p>
563
 
     *
564
 
     *     <p>
565
 
     *     The full array will be provided to any configured filters for each
566
 
     *     query. This is an easy way to create a fully client-side autocomplete
567
 
     *     implementation.
568
 
     *     </p>
569
 
     *   </dd>
570
 
     *
571
 
     *   <dt>DataSource</dt>
572
 
     *   <dd>
573
 
     *     <p>
574
 
     *     A <code>DataSource</code> instance or other object that provides a
575
 
     *     DataSource-like <code>sendRequest</code> method. See the
576
 
     *     <code>DataSource</code> documentation for details.
577
 
     *     </p>
578
 
     *   </dd>
579
 
     *
580
 
     *   <dt>Function</dt>
581
 
     *   <dd>
582
 
     *     <p>
583
 
     *     <i>Example (synchronous):</i> <code>function (query) { return ['foo', 'bar']; }</code><br>
584
 
           <i>Example (async):</i> <code>function (query, callback) { callback(['foo', 'bar']); }</code>
585
 
     *     </p>
586
 
     *
587
 
     *     <p>
588
 
     *     A function source will be called with the current query and a
589
 
     *     callback function as parameters, and should either return an array of
590
 
     *     results (for synchronous operation) or return nothing and pass an
591
 
     *     array of results to the provided callback (for asynchronous
592
 
     *     operation).
593
 
     *     </p>
594
 
     *   </dd>
595
 
     *
596
 
     *   <dt>Object</dt>
597
 
     *   <dd>
598
 
     *     <p>
599
 
     *     <i>Example:</i> <code>{foo: ['foo result 1', 'foo result 2'], bar: ['bar result']}</code>
600
 
     *     </p>
601
 
     *
602
 
     *     <p>
603
 
     *     An object will be treated as a query hashmap. If a property on the
604
 
     *     object matches the current query, the value of that property will be
605
 
     *     used as the response.
606
 
     *     </p>
607
 
     *
608
 
     *     <p>
609
 
     *     The response is assumed to be an array of results by default. If the
610
 
     *     response is not an array, provide a <code>resultListLocator</code> to
611
 
     *     process the response and return an array.
612
 
     *     </p>
613
 
     *   </dd>
614
 
     * </dl>
615
 
     *
616
 
     * <p>
617
 
     * If the optional <code>autocomplete-sources</code> module is loaded, then
618
 
     * the following additional source types will be supported as well:
619
 
     * </p>
620
 
     *
621
 
     * <dl>
622
 
     *   <dt>&lt;select&gt; Node</dt>
623
 
     *   <dd>
624
 
     *     <p>
625
 
     *     You may provide a YUI Node instance wrapping a &lt;select&gt;
626
 
     *     element, and the options in the list will be used as results. You
627
 
     *     will also need to specify a <code>resultTextLocator</code> of 'text'
628
 
     *     or 'value', depending on what you want to use as the text of the
629
 
     *     result.
630
 
     *     </p>
631
 
     *
632
 
     *     <p>
633
 
     *     Each result will be an object with the following properties:
634
 
     *     </p>
635
 
     *
636
 
     *     <dl>
637
 
     *       <dt>html (String)</dt>
638
 
     *       <dd>
639
 
     *         <p>HTML content of the &lt;option&gt; element.</p>
640
 
     *       </dd>
641
 
     *
642
 
     *       <dt>index (Number)</dt>
643
 
     *       <dd>
644
 
     *         <p>Index of the &lt;option&gt; element in the list.</p>
645
 
     *       </dd>
646
 
     *
647
 
     *       <dt>node (Y.Node)</dt>
648
 
     *       <dd>
649
 
     *         <p>Node instance referring to the original &lt;option&gt; element.</p>
650
 
     *       </dd>
651
 
     *
652
 
     *       <dt>selected (Boolean)</dt>
653
 
     *       <dd>
654
 
     *         <p>Whether or not this item is currently selected in the
655
 
     *         &lt;select&gt; list.</p>
656
 
     *       </dd>
657
 
     *
658
 
     *       <dt>text (String)</dt>
659
 
     *       <dd>
660
 
     *         <p>Text content of the &lt;option&gt; element.</p>
661
 
     *       </dd>
662
 
     *
663
 
     *       <dt>value (String)</dt>
664
 
     *       <dd>
665
 
     *         <p>Value of the &lt;option&gt; element.</p>
666
 
     *       </dd>
667
 
     *     </dl>
668
 
     *   </dd>
669
 
     *
670
 
     *   <dt>String (JSONP URL)</dt>
671
 
     *   <dd>
672
 
     *     <p>
673
 
     *     <i>Example:</i> <code>'http://example.com/search?q={query}&callback={callback}'</code>
674
 
     *     </p>
675
 
     *
676
 
     *     <p>
677
 
     *     If a URL with a <code>{callback}</code> placeholder is provided, it
678
 
     *     will be used to make a JSONP request. The <code>{query}</code>
679
 
     *     placeholder will be replaced with the current query, and the
680
 
     *     <code>{callback}</code> placeholder will be replaced with an
681
 
     *     internally-generated JSONP callback name. Both placeholders must
682
 
     *     appear in the URL, or the request will fail. An optional
683
 
     *     <code>{maxResults}</code> placeholder may also be provided, and will
684
 
     *     be replaced with the value of the maxResults attribute (or 1000 if
685
 
     *     the maxResults attribute is 0 or less).
686
 
     *     </p>
687
 
     *
688
 
     *     <p>
689
 
     *     The response is assumed to be an array of results by default. If the
690
 
     *     response is not an array, provide a <code>resultListLocator</code> to
691
 
     *     process the response and return an array.
692
 
     *     </p>
693
 
     *
694
 
     *     <p>
695
 
     *     <strong>The <code>jsonp</code> module must be loaded in order for
696
 
     *     JSONP URL sources to work.</strong> If the <code>jsonp</code> module
697
 
     *     is not already loaded, it will be loaded on demand if possible.
698
 
     *     </p>
699
 
     *   </dd>
700
 
     *
701
 
     *   <dt>String (XHR URL)</dt>
702
 
     *   <dd>
703
 
     *     <p>
704
 
     *     <i>Example:</i> <code>'http://example.com/search?q={query}'</code>
705
 
     *     </p>
706
 
     *
707
 
     *     <p>
708
 
     *     If a URL without a <code>{callback}</code> placeholder is provided,
709
 
     *     it will be used to make a same-origin XHR request. The
710
 
     *     <code>{query}</code> placeholder will be replaced with the current
711
 
     *     query. An optional <code>{maxResults}</code> placeholder may also be
712
 
     *     provided, and will be replaced with the value of the maxResults
713
 
     *     attribute (or 1000 if the maxResults attribute is 0 or less).
714
 
     *     </p>
715
 
     *
716
 
     *     <p>
717
 
     *     The response is assumed to be a JSON array of results by default. If
718
 
     *     the response is a JSON object and not an array, provide a
719
 
     *     <code>resultListLocator</code> to process the response and return an
720
 
     *     array. If the response is in some form other than JSON, you will
721
 
     *     need to use a custom DataSource instance as the source.
722
 
     *     </p>
723
 
     *
724
 
     *     <p>
725
 
     *     <strong>The <code>io-base</code> and <code>json-parse</code> modules
726
 
     *     must be loaded in order for XHR URL sources to work.</strong> If
727
 
     *     these modules are not already loaded, they will be loaded on demand
728
 
     *     if possible.
729
 
     *     </p>
730
 
     *   </dd>
731
 
     *
732
 
     *   <dt>String (YQL query)</dt>
733
 
     *   <dd>
734
 
     *     <p>
735
 
     *     <i>Example:</i> <code>'select * from search.suggest where query="{query}"'</code>
736
 
     *     </p>
737
 
     *
738
 
     *     <p>
739
 
     *     If a YQL query is provided, it will be used to make a YQL request.
740
 
     *     The <code>{query}</code> placeholder will be replaced with the
741
 
     *     current autocomplete query. This placeholder must appear in the YQL
742
 
     *     query, or the request will fail. An optional
743
 
     *     <code>{maxResults}</code> placeholder may also be provided, and will
744
 
     *     be replaced with the value of the maxResults attribute (or 1000 if
745
 
     *     the maxResults attribute is 0 or less).
746
 
     *     </p>
747
 
     *
748
 
     *     <p>
749
 
     *     <strong>The <code>yql</code> module must be loaded in order for YQL
750
 
     *     sources to work.</strong> If the <code>yql</code> module is not
751
 
     *     already loaded, it will be loaded on demand if possible.
752
 
     *     </p>
753
 
     *   </dd>
754
 
     * </dl>
755
 
     *
756
 
     * <p>
757
 
     * As an alternative to providing a source, you could simply listen for
758
 
     * <code>query</code> events and handle them any way you see fit. Providing
759
 
     * a source is optional, but will usually be simpler.
760
 
     * </p>
761
 
     *
762
 
     * @attribute source
763
 
     * @type Array|DataSource|Function|Node|Object|String|null
764
 
     */
765
 
    source: {
766
 
        setter: '_setSource'
767
 
    },
768
 
 
769
 
    /**
770
 
     * <p>
771
 
     * May be used to force a specific source type, overriding the automatic
772
 
     * source type detection. It should almost never be necessary to do this,
773
 
     * but as they taught us in the Boy Scouts, one should always be prepared,
774
 
     * so it's here if you need it. Be warned that if you set this attribute and
775
 
     * something breaks, it's your own fault.
776
 
     * </p>
777
 
     *
778
 
     * <p>
779
 
     * Supported <code>sourceType</code> values are: 'array', 'datasource',
780
 
     * 'function', and 'object'.
781
 
     * </p>
782
 
     *
783
 
     * <p>
784
 
     * If the <code>autocomplete-sources</code> module is loaded, the following
785
 
     * additional source types are supported: 'io', 'jsonp', 'select',
786
 
     * 'string', 'yql'
787
 
     * </p>
788
 
     *
789
 
     * @attribute sourceType
790
 
     * @type String
791
 
     */
792
 
    sourceType: {
793
 
        value: null
794
 
    },
795
 
 
796
 
    /**
797
 
     * If the <code>inputNode</code> specified at instantiation time has a
798
 
     * <code>node-tokeninput</code> plugin attached to it, this attribute will
799
 
     * be a reference to the <code>Y.Plugin.TokenInput</code> instance.
800
 
     *
801
 
     * @attribute tokenInput
802
 
     * @type Plugin.TokenInput
803
 
     * @readonly
804
 
     */
805
 
    tokenInput: {
806
 
        readOnly: true
807
 
    },
808
 
 
809
 
    /**
810
 
     * Current value of the input node.
811
 
     *
812
 
     * @attribute value
813
 
     * @type String
814
 
     * @default ''
815
 
     */
816
 
    value: {
817
 
        // Why duplicate this._inputNode.get('value')? Because we need a
818
 
        // reliable way to track the source of value changes. We want to perform
819
 
        // completion when the user changes the value, but not when we change
820
 
        // the value.
821
 
        value: ''
822
 
    }
823
 
};
824
 
 
825
 
AutoCompleteBase.CSS_PREFIX = 'ac';
826
 
AutoCompleteBase.UI_SRC = (Y.Widget && Y.Widget.UI_SRC) || 'ui';
827
 
 
828
 
/**
829
 
 * Mapping of built-in source types to their setter functions. DataSource
830
 
 * instances and DataSource-like objects are handled natively, so are not
831
 
 * mapped here.
832
 
 *
833
 
 * @property SOURCE_TYPES
834
 
 * @type {Object}
835
 
 * @static
836
 
 */
837
 
AutoCompleteBase.SOURCE_TYPES = {
838
 
    array     : '_createArraySource',
839
 
    'function': '_createFunctionSource',
840
 
    object    : '_createObjectSource'
841
 
};
842
 
 
843
 
AutoCompleteBase.prototype = {
844
 
    // -- Public Prototype Methods ---------------------------------------------
845
 
 
846
 
    /**
847
 
     * <p>
848
 
     * Sends a request to the configured source. If no source is configured,
849
 
     * this method won't do anything.
850
 
     * </p>
851
 
     *
852
 
     * <p>
853
 
     * Usually there's no reason to call this method manually; it will be
854
 
     * called automatically when user input causes a <code>query</code> event to
855
 
     * be fired. The only time you'll need to call this method manually is if
856
 
     * you want to force a request to be sent when no user input has occurred.
857
 
     * </p>
858
 
     *
859
 
     * @method sendRequest
860
 
     * @param {String} query (optional) Query to send. If specified, the
861
 
     *   <code>query</code> attribute will be set to this query. If not
862
 
     *   specified, the current value of the <code>query</code> attribute will
863
 
     *   be used.
864
 
     * @param {Function} requestTemplate (optional) Request template function.
865
 
     *   If not specified, the current value of the <code>requestTemplate</code>
866
 
     *   attribute will be used.
867
 
     * @chainable
868
 
     */
869
 
    sendRequest: function (query, requestTemplate) {
870
 
        var request,
871
 
            source = this.get('source');
872
 
 
873
 
        if (query || query === '') {
874
 
            this._set(QUERY, query);
875
 
        } else {
876
 
            query = this.get(QUERY) || '';
877
 
        }
878
 
 
879
 
        if (source) {
880
 
            if (!requestTemplate) {
881
 
                requestTemplate = this.get(REQUEST_TEMPLATE);
882
 
            }
883
 
 
884
 
            request = requestTemplate ?
885
 
                requestTemplate.call(this, query) : query;
886
 
 
887
 
 
888
 
            source.sendRequest({
889
 
                query  : query,
890
 
                request: request,
891
 
 
892
 
                callback: {
893
 
                    success: Y.bind(this._onResponse, this, query)
894
 
                }
895
 
            });
896
 
        }
897
 
 
898
 
        return this;
899
 
    },
900
 
 
901
 
    // -- Protected Lifecycle Methods ------------------------------------------
902
 
 
903
 
    /**
904
 
     * Attaches event listeners and behaviors.
905
 
     *
906
 
     * @method _bindUIACBase
907
 
     * @protected
908
 
     */
909
 
    _bindUIACBase: function () {
910
 
        var inputNode  = this.get(INPUT_NODE),
911
 
            tokenInput = inputNode && inputNode.tokenInput;
912
 
 
913
 
        // If the inputNode has a node-tokeninput plugin attached, bind to the
914
 
        // plugin's inputNode instead.
915
 
        if (tokenInput) {
916
 
            inputNode = tokenInput.get(INPUT_NODE);
917
 
            this._set('tokenInput', tokenInput);
918
 
        }
919
 
 
920
 
        if (!inputNode) {
921
 
            Y.error('No inputNode specified.');
922
 
            return;
923
 
        }
924
 
 
925
 
        this._inputNode = inputNode;
926
 
 
927
 
        this._acBaseEvents = new Y.EventHandle([
928
 
            // This is the valueChange event on the inputNode, provided by the
929
 
            // event-valuechange module, not our own valueChange.
930
 
            inputNode.on(VALUE_CHANGE, this._onInputValueChange, this),
931
 
            inputNode.on('blur', this._onInputBlur, this),
932
 
 
933
 
            this.after(ALLOW_BROWSER_AC + 'Change', this._syncBrowserAutocomplete),
934
 
            this.after('sourceTypeChange', this._afterSourceTypeChange),
935
 
            this.after(VALUE_CHANGE, this._afterValueChange)
936
 
        ]);
937
 
    },
938
 
 
939
 
    /**
940
 
     * Detaches AutoCompleteBase event listeners.
941
 
     *
942
 
     * @method _destructorACBase
943
 
     * @protected
944
 
     */
945
 
    _destructorACBase: function () {
946
 
        this._acBaseEvents.detach();
947
 
    },
948
 
 
949
 
    /**
950
 
     * Synchronizes the UI state of the <code>inputNode</code>.
951
 
     *
952
 
     * @method _syncUIACBase
953
 
     * @protected
954
 
     */
955
 
    _syncUIACBase: function () {
956
 
        this._syncBrowserAutocomplete();
957
 
        this.set(VALUE, this.get(INPUT_NODE).get(VALUE));
958
 
    },
959
 
 
960
 
    // -- Protected Prototype Methods ------------------------------------------
961
 
 
962
 
    /**
963
 
     * Creates a DataSource-like object that simply returns the specified array
964
 
     * as a response. See the <code>source</code> attribute for more details.
965
 
     *
966
 
     * @method _createArraySource
967
 
     * @param {Array} source
968
 
     * @return {Object} DataSource-like object.
969
 
     * @protected
970
 
     */
971
 
    _createArraySource: function (source) {
972
 
        var that = this;
973
 
 
974
 
        return {
975
 
            type: 'array',
976
 
            sendRequest: function (request) {
977
 
                that[_SOURCE_SUCCESS](source.concat(), request);
978
 
            }
979
 
        };
980
 
    },
981
 
 
982
 
    /**
983
 
     * Creates a DataSource-like object that passes the query to a
984
 
     * custom-defined function, which is expected to call the provided callback
985
 
     * with an array of results. See the <code>source</code> attribute for more
986
 
     * details.
987
 
     *
988
 
     * @method _createFunctionSource
989
 
     * @param {Function} source Function that accepts a query and a callback as
990
 
     *   parameters, and calls the callback with an array of results.
991
 
     * @return {Object} DataSource-like object.
992
 
     * @protected
993
 
     */
994
 
    _createFunctionSource: function (source) {
995
 
        var that = this;
996
 
 
997
 
        return {
998
 
            type: 'function',
999
 
            sendRequest: function (request) {
1000
 
                var value;
1001
 
 
1002
 
                function afterResults(results) {
1003
 
                    that[_SOURCE_SUCCESS](results || [], request);
1004
 
                }
1005
 
 
1006
 
                // Allow both synchronous and asynchronous functions. If we get
1007
 
                // a truthy return value, assume the function is synchronous.
1008
 
                if ((value = source(request.query, afterResults))) {
1009
 
                    afterResults(value);
1010
 
                }
1011
 
            }
1012
 
        };
1013
 
    },
1014
 
 
1015
 
    /**
1016
 
     * Creates a DataSource-like object that looks up queries as properties on
1017
 
     * the specified object, and returns the found value (if any) as a response.
1018
 
     * See the <code>source</code> attribute for more details.
1019
 
     *
1020
 
     * @method _createObjectSource
1021
 
     * @param {Object} source
1022
 
     * @return {Object} DataSource-like object.
1023
 
     * @protected
1024
 
     */
1025
 
    _createObjectSource: function (source) {
1026
 
        var that = this;
1027
 
 
1028
 
        return {
1029
 
            type: 'object',
1030
 
            sendRequest: function (request) {
1031
 
                var query = request.query;
1032
 
 
1033
 
                that[_SOURCE_SUCCESS](
1034
 
                    YObject.owns(source, query) ? source[query] : [],
1035
 
                    request
1036
 
                );
1037
 
            }
1038
 
        };
1039
 
    },
1040
 
 
1041
 
    /**
1042
 
     * Returns <code>true</code> if <i>value</i> is either a function or
1043
 
     * <code>null</code>.
1044
 
     *
1045
 
     * @method _functionValidator
1046
 
     * @param {Function|null} value Value to validate.
1047
 
     * @protected
1048
 
     */
1049
 
    _functionValidator: function (value) {
1050
 
        return value === null || isFunction(value);
1051
 
    },
1052
 
 
1053
 
    /**
1054
 
     * Faster and safer alternative to Y.Object.getValue(). Doesn't bother
1055
 
     * casting the path to an array (since we already know it's an array) and
1056
 
     * doesn't throw an error if a value in the middle of the object hierarchy
1057
 
     * is neither <code>undefined</code> nor an object.
1058
 
     *
1059
 
     * @method _getObjectValue
1060
 
     * @param {Object} obj
1061
 
     * @param {Array} path
1062
 
     * @return {mixed} Located value, or <code>undefined</code> if the value was
1063
 
     *   not found at the specified path.
1064
 
     * @protected
1065
 
     */
1066
 
    _getObjectValue: function (obj, path) {
1067
 
        if (!obj) {
1068
 
            return;
1069
 
        }
1070
 
 
1071
 
        for (var i = 0, len = path.length; obj && i < len; i++) {
1072
 
            obj = obj[path[i]];
1073
 
        }
1074
 
 
1075
 
        return obj;
1076
 
    },
1077
 
 
1078
 
    /**
1079
 
     * Parses result responses, performs filtering and highlighting, and fires
1080
 
     * the <code>results</code> event.
1081
 
     *
1082
 
     * @method _parseResponse
1083
 
     * @param {String} query Query that generated these results.
1084
 
     * @param {Object} response Response containing results.
1085
 
     * @param {Object} data Raw response data.
1086
 
     * @protected
1087
 
     */
1088
 
    _parseResponse: function (query, response, data) {
1089
 
        var facade = {
1090
 
                data   : data,
1091
 
                query  : query,
1092
 
                results: []
1093
 
            },
1094
 
 
1095
 
            listLocator = this.get(RESULT_LIST_LOCATOR),
1096
 
            results     = [],
1097
 
            unfiltered  = response && response.results,
1098
 
 
1099
 
            filters,
1100
 
            formatted,
1101
 
            formatter,
1102
 
            highlighted,
1103
 
            highlighter,
1104
 
            i,
1105
 
            len,
1106
 
            maxResults,
1107
 
            result,
1108
 
            text,
1109
 
            textLocator;
1110
 
 
1111
 
        if (unfiltered && listLocator) {
1112
 
            unfiltered = listLocator.call(this, unfiltered);
1113
 
        }
1114
 
 
1115
 
        if (unfiltered && unfiltered.length) {
1116
 
            filters     = this.get('resultFilters');
1117
 
            textLocator = this.get('resultTextLocator');
1118
 
 
1119
 
            // Create a lightweight result object for each result to make them
1120
 
            // easier to work with. The various properties on the object
1121
 
            // represent different formats of the result, and will be populated
1122
 
            // as we go.
1123
 
            for (i = 0, len = unfiltered.length; i < len; ++i) {
1124
 
                result = unfiltered[i];
1125
 
 
1126
 
                text = textLocator ?
1127
 
                        textLocator.call(this, result) :
1128
 
                        result.toString();
1129
 
 
1130
 
                results.push({
1131
 
                    display: Escape.html(text),
1132
 
                    raw    : result,
1133
 
                    text   : text
1134
 
                });
1135
 
            }
1136
 
 
1137
 
            // Run the results through all configured result filters. Each
1138
 
            // filter returns an array of (potentially fewer) result objects,
1139
 
            // which is then passed to the next filter, and so on.
1140
 
            for (i = 0, len = filters.length; i < len; ++i) {
1141
 
                results = filters[i].call(this, query, results.concat());
1142
 
 
1143
 
                if (!results) {
1144
 
                    return;
1145
 
                }
1146
 
 
1147
 
                if (!results.length) {
1148
 
                    break;
1149
 
                }
1150
 
            }
1151
 
 
1152
 
            if (results.length) {
1153
 
                formatter   = this.get('resultFormatter');
1154
 
                highlighter = this.get('resultHighlighter');
1155
 
                maxResults  = this.get('maxResults');
1156
 
 
1157
 
                // If maxResults is set and greater than 0, limit the number of
1158
 
                // results.
1159
 
                if (maxResults && maxResults > 0 &&
1160
 
                        results.length > maxResults) {
1161
 
                    results.length = maxResults;
1162
 
                }
1163
 
 
1164
 
                // Run the results through the configured highlighter (if any).
1165
 
                // The highlighter returns an array of highlighted strings (not
1166
 
                // an array of result objects), and these strings are then added
1167
 
                // to each result object.
1168
 
                if (highlighter) {
1169
 
                    highlighted = highlighter.call(this, query,
1170
 
                            results.concat());
1171
 
 
1172
 
                    if (!highlighted) {
1173
 
                        return;
1174
 
                    }
1175
 
 
1176
 
                    for (i = 0, len = highlighted.length; i < len; ++i) {
1177
 
                        result = results[i];
1178
 
                        result.highlighted = highlighted[i];
1179
 
                        result.display     = result.highlighted;
1180
 
                    }
1181
 
                }
1182
 
 
1183
 
                // Run the results through the configured formatter (if any) to
1184
 
                // produce the final formatted results. The formatter returns an
1185
 
                // array of strings or Node instances (not an array of result
1186
 
                // objects), and these strings/Nodes are then added to each
1187
 
                // result object.
1188
 
                if (formatter) {
1189
 
                    formatted = formatter.call(this, query, results.concat());
1190
 
 
1191
 
                    if (!formatted) {
1192
 
                        return;
1193
 
                    }
1194
 
 
1195
 
                    for (i = 0, len = formatted.length; i < len; ++i) {
1196
 
                        results[i].display = formatted[i];
1197
 
                    }
1198
 
                }
1199
 
            }
1200
 
        }
1201
 
 
1202
 
        facade.results = results;
1203
 
        this.fire(EVT_RESULTS, facade);
1204
 
    },
1205
 
 
1206
 
    /**
1207
 
     * <p>
1208
 
     * Returns the query portion of the specified input value, or
1209
 
     * <code>null</code> if there is no suitable query within the input value.
1210
 
     * </p>
1211
 
     *
1212
 
     * <p>
1213
 
     * If a query delimiter is defined, the query will be the last delimited
1214
 
     * part of of the string.
1215
 
     * </p>
1216
 
     *
1217
 
     * @method _parseValue
1218
 
     * @param {String} value Input value from which to extract the query.
1219
 
     * @return {String|null} query
1220
 
     * @protected
1221
 
     */
1222
 
    _parseValue: function (value) {
1223
 
        var delim = this.get(QUERY_DELIMITER);
1224
 
 
1225
 
        if (delim) {
1226
 
            value = value.split(delim);
1227
 
            value = value[value.length - 1];
1228
 
        }
1229
 
 
1230
 
        return Lang.trimLeft(value);
1231
 
    },
1232
 
 
1233
 
    /**
1234
 
     * Setter for locator attributes.
1235
 
     *
1236
 
     * @method _setLocator
1237
 
     * @param {Function|String|null} locator
1238
 
     * @return {Function|null}
1239
 
     * @protected
1240
 
     */
1241
 
    _setLocator: function (locator) {
1242
 
        if (this[_FUNCTION_VALIDATOR](locator)) {
1243
 
            return locator;
1244
 
        }
1245
 
 
1246
 
        var that = this;
1247
 
 
1248
 
        locator = locator.toString().split('.');
1249
 
 
1250
 
        return function (result) {
1251
 
            return result && that._getObjectValue(result, locator);
1252
 
        };
1253
 
    },
1254
 
 
1255
 
    /**
1256
 
     * Setter for the <code>requestTemplate</code> attribute.
1257
 
     *
1258
 
     * @method _setRequestTemplate
1259
 
     * @param {Function|String|null} template
1260
 
     * @return {Function|null}
1261
 
     * @protected
1262
 
     */
1263
 
    _setRequestTemplate: function (template) {
1264
 
        if (this[_FUNCTION_VALIDATOR](template)) {
1265
 
            return template;
1266
 
        }
1267
 
 
1268
 
        template = template.toString();
1269
 
 
1270
 
        return function (query) {
1271
 
            return Lang.sub(template, {query: encodeURIComponent(query)});
1272
 
        };
1273
 
    },
1274
 
 
1275
 
    /**
1276
 
     * Setter for the <code>resultFilters</code> attribute.
1277
 
     *
1278
 
     * @method _setResultFilters
1279
 
     * @param {Array|Function|String|null} filters <code>null</code>, a filter
1280
 
     *   function, an array of filter functions, or a string or array of strings
1281
 
     *   representing the names of methods on
1282
 
     *   <code>Y.AutoCompleteFilters</code>.
1283
 
     * @return {Array} Array of filter functions (empty if <i>filters</i> is
1284
 
     *   <code>null</code>).
1285
 
     * @protected
1286
 
     */
1287
 
    _setResultFilters: function (filters) {
1288
 
        var acFilters, getFilterFunction;
1289
 
 
1290
 
        if (filters === null) {
1291
 
            return [];
1292
 
        }
1293
 
 
1294
 
        acFilters = Y.AutoCompleteFilters;
1295
 
 
1296
 
        getFilterFunction = function (filter) {
1297
 
            if (isFunction(filter)) {
1298
 
                return filter;
1299
 
            }
1300
 
 
1301
 
            if (isString(filter) && acFilters &&
1302
 
                    isFunction(acFilters[filter])) {
1303
 
                return acFilters[filter];
1304
 
            }
1305
 
 
1306
 
            return false;
1307
 
        };
1308
 
 
1309
 
        if (Lang.isArray(filters)) {
1310
 
            filters = YArray.map(filters, getFilterFunction);
1311
 
            return YArray.every(filters, function (f) { return !!f; }) ?
1312
 
                    filters : INVALID_VALUE;
1313
 
        } else {
1314
 
            filters = getFilterFunction(filters);
1315
 
            return filters ? [filters] : INVALID_VALUE;
1316
 
        }
1317
 
    },
1318
 
 
1319
 
    /**
1320
 
     * Setter for the <code>resultHighlighter</code> attribute.
1321
 
     *
1322
 
     * @method _setResultHighlighter
1323
 
     * @param {Function|String|null} highlighter <code>null</code>, a
1324
 
     *   highlighter function, or a string representing the name of a method on
1325
 
     *   <code>Y.AutoCompleteHighlighters</code>.
1326
 
     * @return {Function|null}
1327
 
     * @protected
1328
 
     */
1329
 
    _setResultHighlighter: function (highlighter) {
1330
 
        var acHighlighters;
1331
 
 
1332
 
        if (this._functionValidator(highlighter)) {
1333
 
            return highlighter;
1334
 
        }
1335
 
 
1336
 
        acHighlighters = Y.AutoCompleteHighlighters;
1337
 
 
1338
 
        if (isString(highlighter) && acHighlighters &&
1339
 
                isFunction(acHighlighters[highlighter])) {
1340
 
            return acHighlighters[highlighter];
1341
 
        }
1342
 
 
1343
 
        return INVALID_VALUE;
1344
 
    },
1345
 
 
1346
 
    /**
1347
 
     * Setter for the <code>source</code> attribute. Returns a DataSource or
1348
 
     * a DataSource-like object depending on the type of <i>source</i> and/or
1349
 
     * the value of the <code>sourceType</code> attribute.
1350
 
     *
1351
 
     * @method _setSource
1352
 
     * @param {mixed} source AutoComplete source. See the <code>source</code>
1353
 
     *   attribute for details.
1354
 
     * @return {DataSource|Object}
1355
 
     * @protected
1356
 
     */
1357
 
    _setSource: function (source) {
1358
 
        var sourceType = this.get('sourceType') || Lang.type(source),
1359
 
            sourceSetter;
1360
 
 
1361
 
        if ((source && isFunction(source.sendRequest))
1362
 
                || source === null
1363
 
                || sourceType === 'datasource') {
1364
 
 
1365
 
            // Quacks like a DataSource instance (or null). Make it so!
1366
 
            this._rawSource = source;
1367
 
            return source;
1368
 
        }
1369
 
 
1370
 
        // See if there's a registered setter for this source type.
1371
 
        if ((sourceSetter = AutoCompleteBase.SOURCE_TYPES[sourceType])) {
1372
 
            this._rawSource = source;
1373
 
            return Lang.isString(sourceSetter) ?
1374
 
                    this[sourceSetter](source) : sourceSetter(source);
1375
 
        }
1376
 
 
1377
 
        Y.error("Unsupported source type '" + sourceType + "'. Maybe autocomplete-sources isn't loaded?");
1378
 
        return INVALID_VALUE;
1379
 
    },
1380
 
 
1381
 
    /**
1382
 
     * Shared success callback for non-DataSource sources.
1383
 
     *
1384
 
     * @method _sourceSuccess
1385
 
     * @param {mixed} data Response data.
1386
 
     * @param {Object} request Request object.
1387
 
     * @protected
1388
 
     */
1389
 
    _sourceSuccess: function (data, request) {
1390
 
        request.callback.success({
1391
 
            data: data,
1392
 
            response: {results: data}
1393
 
        });
1394
 
    },
1395
 
 
1396
 
    /**
1397
 
     * Synchronizes the UI state of the <code>allowBrowserAutocomplete</code>
1398
 
     * attribute.
1399
 
     *
1400
 
     * @method _syncBrowserAutocomplete
1401
 
     * @protected
1402
 
     */
1403
 
    _syncBrowserAutocomplete: function () {
1404
 
        var inputNode = this.get(INPUT_NODE);
1405
 
 
1406
 
        if (inputNode.get('nodeName').toLowerCase() === 'input') {
1407
 
            inputNode.setAttribute('autocomplete',
1408
 
                    this.get(ALLOW_BROWSER_AC) ? 'on' : 'off');
1409
 
        }
1410
 
    },
1411
 
 
1412
 
    /**
1413
 
     * <p>
1414
 
     * Updates the query portion of the <code>value</code> attribute.
1415
 
     * </p>
1416
 
     *
1417
 
     * <p>
1418
 
     * If a query delimiter is defined, the last delimited portion of the input
1419
 
     * value will be replaced with the specified <i>value</i>.
1420
 
     * </p>
1421
 
     *
1422
 
     * @method _updateValue
1423
 
     * @param {String} newVal New value.
1424
 
     * @protected
1425
 
     */
1426
 
    _updateValue: function (newVal) {
1427
 
        var delim = this.get(QUERY_DELIMITER),
1428
 
            insertDelim,
1429
 
            len,
1430
 
            prevVal;
1431
 
 
1432
 
        newVal = Lang.trimLeft(newVal);
1433
 
 
1434
 
        if (delim) {
1435
 
            insertDelim = trim(delim); // so we don't double up on spaces
1436
 
            prevVal     = YArray.map(trim(this.get(VALUE)).split(delim), trim);
1437
 
            len         = prevVal.length;
1438
 
 
1439
 
            if (len > 1) {
1440
 
                prevVal[len - 1] = newVal;
1441
 
                newVal = prevVal.join(insertDelim + ' ');
1442
 
            }
1443
 
 
1444
 
            newVal = newVal + insertDelim + ' ';
1445
 
        }
1446
 
 
1447
 
        this.set(VALUE, newVal);
1448
 
    },
1449
 
 
1450
 
    // -- Protected Event Handlers ---------------------------------------------
1451
 
 
1452
 
    /**
1453
 
     * Updates the current <code>source</code> based on the new
1454
 
     * <code>sourceType</code> to ensure that the two attributes don't get out
1455
 
     * of sync when they're changed separately.
1456
 
     *
1457
 
     * @method _afterSourceTypeChange
1458
 
     * @param {EventFacade} e
1459
 
     * @protected
1460
 
     */
1461
 
    _afterSourceTypeChange: function (e) {
1462
 
        if (this._rawSource) {
1463
 
            this.set('source', this._rawSource);
1464
 
        }
1465
 
    },
1466
 
 
1467
 
    /**
1468
 
     * Handles change events for the <code>value</code> attribute.
1469
 
     *
1470
 
     * @method _afterValueChange
1471
 
     * @param {EventFacade} e
1472
 
     * @protected
1473
 
     */
1474
 
    _afterValueChange: function (e) {
1475
 
        var delay,
1476
 
            fire,
1477
 
            minQueryLength,
1478
 
            newVal = e.newVal,
1479
 
            query,
1480
 
            that;
1481
 
 
1482
 
        // Don't query on value changes that didn't come from the user.
1483
 
        if (e.src !== AutoCompleteBase.UI_SRC) {
1484
 
            this._inputNode.set(VALUE, newVal);
1485
 
            return;
1486
 
        }
1487
 
 
1488
 
 
1489
 
        minQueryLength = this.get('minQueryLength');
1490
 
        query          = this._parseValue(newVal) || '';
1491
 
 
1492
 
        if (minQueryLength >= 0 && query.length >= minQueryLength) {
1493
 
            delay = this.get('queryDelay');
1494
 
            that  = this;
1495
 
 
1496
 
            fire = function () {
1497
 
                that.fire(EVT_QUERY, {
1498
 
                    inputValue: newVal,
1499
 
                    query     : query
1500
 
                });
1501
 
            };
1502
 
 
1503
 
            if (delay) {
1504
 
                clearTimeout(this._delay);
1505
 
                this._delay = setTimeout(fire, delay);
1506
 
            } else {
1507
 
                fire();
1508
 
            }
1509
 
        } else {
1510
 
            clearTimeout(this._delay);
1511
 
 
1512
 
            this.fire(EVT_CLEAR, {
1513
 
                prevVal: e.prevVal ? this._parseValue(e.prevVal) : null
1514
 
            });
1515
 
        }
1516
 
    },
1517
 
 
1518
 
    /**
1519
 
     * Handles <code>blur</code> events on the input node.
1520
 
     *
1521
 
     * @method _onInputBlur
1522
 
     * @param {EventFacade} e
1523
 
     * @protected
1524
 
     */
1525
 
    _onInputBlur: function (e) {
1526
 
        var delim = this.get(QUERY_DELIMITER),
1527
 
            delimPos,
1528
 
            newVal,
1529
 
            value;
1530
 
 
1531
 
        // If a query delimiter is set and the input's value contains one or
1532
 
        // more trailing delimiters, strip them.
1533
 
        if (delim && !this.get('allowTrailingDelimiter')) {
1534
 
            delim = Lang.trimRight(delim);
1535
 
            value = newVal = this._inputNode.get(VALUE);
1536
 
 
1537
 
            if (delim) {
1538
 
                while ((newVal = Lang.trimRight(newVal)) &&
1539
 
                        (delimPos = newVal.length - delim.length) &&
1540
 
                        newVal.lastIndexOf(delim) === delimPos) {
1541
 
 
1542
 
                    newVal = newVal.substring(0, delimPos);
1543
 
                }
1544
 
            } else {
1545
 
                // Delimiter is one or more space characters, so just trim the
1546
 
                // value.
1547
 
                newVal = Lang.trimRight(newVal);
1548
 
            }
1549
 
 
1550
 
            if (newVal !== value) {
1551
 
                this.set(VALUE, newVal);
1552
 
            }
1553
 
        }
1554
 
    },
1555
 
 
1556
 
    /**
1557
 
     * Handles <code>valueChange</code> events on the input node and fires a
1558
 
     * <code>query</code> event when the input value meets the configured
1559
 
     * criteria.
1560
 
     *
1561
 
     * @method _onInputValueChange
1562
 
     * @param {EventFacade} e
1563
 
     * @protected
1564
 
     */
1565
 
    _onInputValueChange: function (e) {
1566
 
        var newVal = e.newVal;
1567
 
 
1568
 
        // Don't query if the internal value is the same as the new value
1569
 
        // reported by valueChange.
1570
 
        if (newVal === this.get(VALUE)) {
1571
 
            return;
1572
 
        }
1573
 
 
1574
 
        this.set(VALUE, newVal, {src: AutoCompleteBase.UI_SRC});
1575
 
    },
1576
 
 
1577
 
    /**
1578
 
     * Handles source responses and fires the <code>results</code> event.
1579
 
     *
1580
 
     * @method _onResponse
1581
 
     * @param {EventFacade} e
1582
 
     * @protected
1583
 
     */
1584
 
    _onResponse: function (query, e) {
1585
 
        // Ignore stale responses that aren't for the current query.
1586
 
        if (query === (this.get(QUERY) || '')) {
1587
 
            this._parseResponse(query || '', e.response, e.data);
1588
 
        }
1589
 
    },
1590
 
 
1591
 
    // -- Protected Default Event Handlers -------------------------------------
1592
 
 
1593
 
    /**
1594
 
     * Default <code>clear</code> event handler. Sets the <code>results</code>
1595
 
     * property to an empty array and <code>query</code> to null.
1596
 
     *
1597
 
     * @method _defClearFn
1598
 
     * @protected
1599
 
     */
1600
 
    _defClearFn: function () {
1601
 
        this._set(QUERY, null);
1602
 
        this._set(RESULTS, []);
1603
 
    },
1604
 
 
1605
 
    /**
1606
 
     * Default <code>query</code> event handler. Sets the <code>query</code>
1607
 
     * property and sends a request to the source if one is configured.
1608
 
     *
1609
 
     * @method _defQueryFn
1610
 
     * @param {EventFacade} e
1611
 
     * @protected
1612
 
     */
1613
 
    _defQueryFn: function (e) {
1614
 
        var query = e.query;
1615
 
 
1616
 
        this.sendRequest(query); // sendRequest will set the 'query' attribute
1617
 
    },
1618
 
 
1619
 
    /**
1620
 
     * Default <code>results</code> event handler. Sets the <code>results</code>
1621
 
     * property to the latest results.
1622
 
     *
1623
 
     * @method _defResultsFn
1624
 
     * @param {EventFacade} e
1625
 
     * @protected
1626
 
     */
1627
 
    _defResultsFn: function (e) {
1628
 
        this._set(RESULTS, e[RESULTS]);
1629
 
    }
1630
 
};
1631
 
 
1632
 
Y.AutoCompleteBase = AutoCompleteBase;
1633
 
 
1634
 
 
1635
 
}, '3.4.1' ,{optional:['autocomplete-sources'], requires:['array-extras', 'base-build', 'escape', 'event-valuechange', 'node-base']});