jquery_autocomplete_rails 1.2.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3833df3df7c20da6aceb3dcad2eff1d8ca5226f0
4
+ data.tar.gz: 243d6a801c0abce2c59f9b14c4baafb3b2c33fa8
5
+ SHA512:
6
+ metadata.gz: a76033b2af9341fc04377b3d697cbc6a5d7db3e91001edd692ecb986a640d6648796fe78360655a3cf369619bba70006a92340f872f94ac11e1cdc300ae875d5
7
+ data.tar.gz: 9f3e7aaa39de637f16a676f8020dbe3c0c6590726c26e0ce2c5915f6abe0a3e6495089936716be4458c8303daed96d631df3cbc267b56630078e21323e5a9d8d
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ require 'jquery_autocomplete_rails/version'
2
+
3
+ module JqueryAutocompleteRails
4
+ module Rails
5
+ class Engine < ::Rails::Engine
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module JqueryAutocompleteRails
2
+ VERSION = "1.2.9"
3
+ end
@@ -0,0 +1,829 @@
1
+ /**
2
+ * Ajax Autocomplete for jQuery, version 1.2.9
3
+ * (c) 2013 Tomas Kirda
4
+ *
5
+ * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
6
+ * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
7
+ *
8
+ */
9
+
10
+ /*jslint browser: true, white: true, plusplus: true */
11
+ /*global define, window, document, jQuery */
12
+
13
+ // Expose plugin as an AMD module if AMD loader is present:
14
+ (function (factory) {
15
+ 'use strict';
16
+ if (typeof define === 'function' && define.amd) {
17
+ // AMD. Register as an anonymous module.
18
+ define(['jquery'], factory);
19
+ } else {
20
+ // Browser globals
21
+ factory(jQuery);
22
+ }
23
+ }(function ($) {
24
+ 'use strict';
25
+
26
+ var
27
+ utils = (function () {
28
+ return {
29
+ escapeRegExChars: function (value) {
30
+ return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
31
+ },
32
+ createNode: function (containerClass) {
33
+ var div = document.createElement('div');
34
+ div.className = containerClass;
35
+ div.style.position = 'absolute';
36
+ div.style.display = 'none';
37
+ return div;
38
+ }
39
+ };
40
+ }()),
41
+
42
+ keys = {
43
+ ESC: 27,
44
+ TAB: 9,
45
+ RETURN: 13,
46
+ LEFT: 37,
47
+ UP: 38,
48
+ RIGHT: 39,
49
+ DOWN: 40
50
+ };
51
+
52
+ function Autocomplete(el, options) {
53
+ var noop = function () { },
54
+ that = this,
55
+ defaults = {
56
+ autoSelectFirst: false,
57
+ appendTo: 'body',
58
+ serviceUrl: null,
59
+ lookup: null,
60
+ onSelect: null,
61
+ width: 'auto',
62
+ minChars: 1,
63
+ maxHeight: 300,
64
+ deferRequestBy: 0,
65
+ params: {},
66
+ formatResult: Autocomplete.formatResult,
67
+ delimiter: null,
68
+ zIndex: 9999,
69
+ type: 'GET',
70
+ noCache: false,
71
+ onSearchStart: noop,
72
+ onSearchComplete: noop,
73
+ onSearchError: noop,
74
+ containerClass: 'autocomplete-suggestions',
75
+ tabDisabled: false,
76
+ dataType: 'text',
77
+ currentRequest: null,
78
+ triggerSelectOnValidInput: true,
79
+ preventBadQueries: true,
80
+ lookupFilter: function (suggestion, originalQuery, queryLowerCase) {
81
+ return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
82
+ },
83
+ paramName: 'query',
84
+ transformResult: function (response) {
85
+ return typeof response === 'string' ? $.parseJSON(response) : response;
86
+ }
87
+ };
88
+
89
+ // Shared variables:
90
+ that.element = el;
91
+ that.el = $(el);
92
+ that.suggestions = [];
93
+ that.badQueries = [];
94
+ that.selectedIndex = -1;
95
+ that.currentValue = that.element.value;
96
+ that.intervalId = 0;
97
+ that.cachedResponse = {};
98
+ that.onChangeInterval = null;
99
+ that.onChange = null;
100
+ that.isLocal = false;
101
+ that.suggestionsContainer = null;
102
+ that.options = $.extend({}, defaults, options);
103
+ that.classes = {
104
+ selected: 'autocomplete-selected',
105
+ suggestion: 'autocomplete-suggestion'
106
+ };
107
+ that.hint = null;
108
+ that.hintValue = '';
109
+ that.selection = null;
110
+
111
+ // Initialize and set options:
112
+ that.initialize();
113
+ that.setOptions(options);
114
+ }
115
+
116
+ Autocomplete.utils = utils;
117
+
118
+ $.Autocomplete = Autocomplete;
119
+
120
+ Autocomplete.formatResult = function (suggestion, currentValue) {
121
+ var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
122
+
123
+ return suggestion.value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
124
+ };
125
+
126
+ Autocomplete.prototype = {
127
+
128
+ killerFn: null,
129
+
130
+ initialize: function () {
131
+ var that = this,
132
+ suggestionSelector = '.' + that.classes.suggestion,
133
+ selected = that.classes.selected,
134
+ options = that.options,
135
+ container;
136
+
137
+ // Remove autocomplete attribute to prevent native suggestions:
138
+ that.element.setAttribute('autocomplete', 'off');
139
+
140
+ that.killerFn = function (e) {
141
+ if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
142
+ that.killSuggestions();
143
+ that.disableKillerFn();
144
+ }
145
+ };
146
+
147
+ that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
148
+
149
+ container = $(that.suggestionsContainer);
150
+
151
+ container.appendTo(options.appendTo);
152
+
153
+ // Only set width if it was provided:
154
+ if (options.width !== 'auto') {
155
+ container.width(options.width);
156
+ }
157
+
158
+ // Listen for mouse over event on suggestions list:
159
+ container.on('mouseover.autocomplete', suggestionSelector, function () {
160
+ that.activate($(this).data('index'));
161
+ });
162
+
163
+ // Deselect active element when mouse leaves suggestions container:
164
+ container.on('mouseout.autocomplete', function () {
165
+ that.selectedIndex = -1;
166
+ container.children('.' + selected).removeClass(selected);
167
+ });
168
+
169
+ // Listen for click event on suggestions list:
170
+ container.on('click.autocomplete', suggestionSelector, function () {
171
+ that.select($(this).data('index'));
172
+ });
173
+
174
+ that.fixPosition();
175
+
176
+ that.fixPositionCapture = function () {
177
+ if (that.visible) {
178
+ that.fixPosition();
179
+ }
180
+ };
181
+
182
+ $(window).on('resize.autocomplete', that.fixPositionCapture);
183
+
184
+ that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
185
+ that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
186
+ that.el.on('blur.autocomplete', function () { that.onBlur(); });
187
+ that.el.on('focus.autocomplete', function () { that.onFocus(); });
188
+ that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
189
+ },
190
+
191
+ onFocus: function () {
192
+ var that = this;
193
+ that.fixPosition();
194
+ if (that.options.minChars <= that.el.val().length) {
195
+ that.onValueChange();
196
+ }
197
+ },
198
+
199
+ onBlur: function () {
200
+ this.enableKillerFn();
201
+ },
202
+
203
+ setOptions: function (suppliedOptions) {
204
+ var that = this,
205
+ options = that.options;
206
+
207
+ $.extend(options, suppliedOptions);
208
+
209
+ that.isLocal = $.isArray(options.lookup);
210
+
211
+ if (that.isLocal) {
212
+ options.lookup = that.verifySuggestionsFormat(options.lookup);
213
+ }
214
+
215
+ // Adjust height, width and z-index:
216
+ $(that.suggestionsContainer).css({
217
+ 'max-height': options.maxHeight + 'px',
218
+ 'width': options.width + 'px',
219
+ 'z-index': options.zIndex
220
+ });
221
+ },
222
+
223
+ clearCache: function () {
224
+ this.cachedResponse = {};
225
+ this.badQueries = [];
226
+ },
227
+
228
+ clear: function () {
229
+ this.clearCache();
230
+ this.currentValue = '';
231
+ this.suggestions = [];
232
+ },
233
+
234
+ disable: function () {
235
+ var that = this;
236
+ that.disabled = true;
237
+ if (that.currentRequest) {
238
+ that.currentRequest.abort();
239
+ }
240
+ },
241
+
242
+ enable: function () {
243
+ this.disabled = false;
244
+ },
245
+
246
+ fixPosition: function () {
247
+ var that = this,
248
+ offset,
249
+ styles;
250
+
251
+ // Don't adjsut position if custom container has been specified:
252
+ if (that.options.appendTo !== 'body') {
253
+ return;
254
+ }
255
+
256
+ offset = that.el.offset();
257
+
258
+ styles = {
259
+ top: (offset.top + that.el.outerHeight()) + 'px',
260
+ left: offset.left + 'px'
261
+ };
262
+
263
+ if (that.options.width === 'auto') {
264
+ styles.width = (that.el.outerWidth() - 2) + 'px';
265
+ }
266
+
267
+ $(that.suggestionsContainer).css(styles);
268
+ },
269
+
270
+ enableKillerFn: function () {
271
+ var that = this;
272
+ $(document).on('click.autocomplete', that.killerFn);
273
+ },
274
+
275
+ disableKillerFn: function () {
276
+ var that = this;
277
+ $(document).off('click.autocomplete', that.killerFn);
278
+ },
279
+
280
+ killSuggestions: function () {
281
+ var that = this;
282
+ that.stopKillSuggestions();
283
+ that.intervalId = window.setInterval(function () {
284
+ that.hide();
285
+ that.stopKillSuggestions();
286
+ }, 50);
287
+ },
288
+
289
+ stopKillSuggestions: function () {
290
+ window.clearInterval(this.intervalId);
291
+ },
292
+
293
+ isCursorAtEnd: function () {
294
+ var that = this,
295
+ valLength = that.el.val().length,
296
+ selectionStart = that.element.selectionStart,
297
+ range;
298
+
299
+ if (typeof selectionStart === 'number') {
300
+ return selectionStart === valLength;
301
+ }
302
+ if (document.selection) {
303
+ range = document.selection.createRange();
304
+ range.moveStart('character', -valLength);
305
+ return valLength === range.text.length;
306
+ }
307
+ return true;
308
+ },
309
+
310
+ onKeyPress: function (e) {
311
+ var that = this;
312
+
313
+ // If suggestions are hidden and user presses arrow down, display suggestions:
314
+ if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
315
+ that.suggest();
316
+ return;
317
+ }
318
+
319
+ if (that.disabled || !that.visible) {
320
+ return;
321
+ }
322
+
323
+ switch (e.which) {
324
+ case keys.ESC:
325
+ that.el.val(that.currentValue);
326
+ that.hide();
327
+ break;
328
+ case keys.RIGHT:
329
+ if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
330
+ that.selectHint();
331
+ break;
332
+ }
333
+ return;
334
+ case keys.TAB:
335
+ if (that.hint && that.options.onHint) {
336
+ that.selectHint();
337
+ return;
338
+ }
339
+ // Fall through to RETURN
340
+ case keys.RETURN:
341
+ if (that.selectedIndex === -1) {
342
+ that.hide();
343
+ return;
344
+ }
345
+ that.select(that.selectedIndex);
346
+ if (e.which === keys.TAB && that.options.tabDisabled === false) {
347
+ return;
348
+ }
349
+ break;
350
+ case keys.UP:
351
+ that.moveUp();
352
+ break;
353
+ case keys.DOWN:
354
+ that.moveDown();
355
+ break;
356
+ default:
357
+ return;
358
+ }
359
+
360
+ // Cancel event if function did not return:
361
+ e.stopImmediatePropagation();
362
+ e.preventDefault();
363
+ },
364
+
365
+ onKeyUp: function (e) {
366
+ var that = this;
367
+
368
+ if (that.disabled) {
369
+ return;
370
+ }
371
+
372
+ switch (e.which) {
373
+ case keys.UP:
374
+ case keys.DOWN:
375
+ return;
376
+ }
377
+
378
+ clearInterval(that.onChangeInterval);
379
+
380
+ if (that.currentValue !== that.el.val()) {
381
+ that.findBestHint();
382
+ if (that.options.deferRequestBy > 0) {
383
+ // Defer lookup in case when value changes very quickly:
384
+ that.onChangeInterval = setInterval(function () {
385
+ that.onValueChange();
386
+ }, that.options.deferRequestBy);
387
+ } else {
388
+ that.onValueChange();
389
+ }
390
+ }
391
+ },
392
+
393
+ onValueChange: function () {
394
+ var that = this,
395
+ options = that.options,
396
+ value = that.el.val(),
397
+ query = that.getQuery(value),
398
+ index;
399
+
400
+ if (that.selection) {
401
+ that.selection = null;
402
+ (options.onInvalidateSelection || $.noop).call(that.element);
403
+ }
404
+
405
+ clearInterval(that.onChangeInterval);
406
+ that.currentValue = value;
407
+ that.selectedIndex = -1;
408
+
409
+ // Check existing suggestion for the match before proceeding:
410
+ if (options.triggerSelectOnValidInput) {
411
+ index = that.findSuggestionIndex(query);
412
+ if (index !== -1) {
413
+ that.select(index);
414
+ return;
415
+ }
416
+ }
417
+
418
+ if (query.length < options.minChars) {
419
+ that.hide();
420
+ } else {
421
+ that.getSuggestions(query);
422
+ }
423
+ },
424
+
425
+ findSuggestionIndex: function (query) {
426
+ var that = this,
427
+ index = -1,
428
+ queryLowerCase = query.toLowerCase();
429
+
430
+ $.each(that.suggestions, function (i, suggestion) {
431
+ if (suggestion.value.toLowerCase() === queryLowerCase) {
432
+ index = i;
433
+ return false;
434
+ }
435
+ });
436
+
437
+ return index;
438
+ },
439
+
440
+ getQuery: function (value) {
441
+ var delimiter = this.options.delimiter,
442
+ parts;
443
+
444
+ if (!delimiter) {
445
+ return value;
446
+ }
447
+ parts = value.split(delimiter);
448
+ return $.trim(parts[parts.length - 1]);
449
+ },
450
+
451
+ getSuggestionsLocal: function (query) {
452
+ var that = this,
453
+ options = that.options,
454
+ queryLowerCase = query.toLowerCase(),
455
+ filter = options.lookupFilter,
456
+ limit = parseInt(options.lookupLimit, 10),
457
+ data;
458
+
459
+ data = {
460
+ suggestions: $.grep(options.lookup, function (suggestion) {
461
+ return filter(suggestion, query, queryLowerCase);
462
+ })
463
+ };
464
+
465
+ if (limit && data.suggestions.length > limit) {
466
+ data.suggestions = data.suggestions.slice(0, limit);
467
+ }
468
+
469
+ return data;
470
+ },
471
+
472
+ getSuggestions: function (q) {
473
+ var response,
474
+ that = this,
475
+ options = that.options,
476
+ serviceUrl = options.serviceUrl,
477
+ params,
478
+ cacheKey;
479
+
480
+ options.params[options.paramName] = q;
481
+ params = options.ignoreParams ? null : options.params;
482
+
483
+ if (that.isLocal) {
484
+ response = that.getSuggestionsLocal(q);
485
+ } else {
486
+ if ($.isFunction(serviceUrl)) {
487
+ serviceUrl = serviceUrl.call(that.element, q);
488
+ }
489
+ cacheKey = serviceUrl + '?' + $.param(params || {});
490
+ response = that.cachedResponse[cacheKey];
491
+ }
492
+
493
+ if (response && $.isArray(response.suggestions)) {
494
+ that.suggestions = response.suggestions;
495
+ that.suggest();
496
+ } else if (!that.isBadQuery(q)) {
497
+ if (options.onSearchStart.call(that.element, options.params) === false) {
498
+ return;
499
+ }
500
+ if (that.currentRequest) {
501
+ that.currentRequest.abort();
502
+ }
503
+ that.currentRequest = $.ajax({
504
+ url: serviceUrl,
505
+ data: params,
506
+ type: options.type,
507
+ dataType: options.dataType
508
+ }).done(function (data) {
509
+ var result;
510
+ that.currentRequest = null;
511
+ result = options.transformResult(data);
512
+ that.processResponse(result, q, cacheKey);
513
+ options.onSearchComplete.call(that.element, q, result.suggestions);
514
+ }).fail(function (jqXHR, textStatus, errorThrown) {
515
+ options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown);
516
+ });
517
+ }
518
+ },
519
+
520
+ isBadQuery: function (q) {
521
+ if (!this.options.preventBadQueries){
522
+ return false;
523
+ }
524
+
525
+ var badQueries = this.badQueries,
526
+ i = badQueries.length;
527
+
528
+ while (i--) {
529
+ if (q.indexOf(badQueries[i]) === 0) {
530
+ return true;
531
+ }
532
+ }
533
+
534
+ return false;
535
+ },
536
+
537
+ hide: function () {
538
+ var that = this;
539
+ that.visible = false;
540
+ that.selectedIndex = -1;
541
+ $(that.suggestionsContainer).hide();
542
+ that.signalHint(null);
543
+ },
544
+
545
+ suggest: function () {
546
+ if (this.suggestions.length === 0) {
547
+ this.hide();
548
+ return;
549
+ }
550
+
551
+ var that = this,
552
+ options = that.options,
553
+ formatResult = options.formatResult,
554
+ value = that.getQuery(that.currentValue),
555
+ className = that.classes.suggestion,
556
+ classSelected = that.classes.selected,
557
+ container = $(that.suggestionsContainer),
558
+ beforeRender = options.beforeRender,
559
+ html = '',
560
+ index,
561
+ width;
562
+
563
+ if (options.triggerSelectOnValidInput) {
564
+ index = that.findSuggestionIndex(value);
565
+ if (index !== -1) {
566
+ that.select(index);
567
+ return;
568
+ }
569
+ }
570
+
571
+ // Build suggestions inner HTML:
572
+ $.each(that.suggestions, function (i, suggestion) {
573
+ html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>';
574
+ });
575
+
576
+ // If width is auto, adjust width before displaying suggestions,
577
+ // because if instance was created before input had width, it will be zero.
578
+ // Also it adjusts if input width has changed.
579
+ // -2px to account for suggestions border.
580
+ if (options.width === 'auto') {
581
+ width = that.el.outerWidth() - 2;
582
+ container.width(width > 0 ? width : 300);
583
+ }
584
+
585
+ container.html(html);
586
+
587
+ // Select first value by default:
588
+ if (options.autoSelectFirst) {
589
+ that.selectedIndex = 0;
590
+ container.children().first().addClass(classSelected);
591
+ }
592
+
593
+ if ($.isFunction(beforeRender)) {
594
+ beforeRender.call(that.element, container);
595
+ }
596
+
597
+ container.show();
598
+ that.visible = true;
599
+
600
+ that.findBestHint();
601
+ },
602
+
603
+ findBestHint: function () {
604
+ var that = this,
605
+ value = that.el.val().toLowerCase(),
606
+ bestMatch = null;
607
+
608
+ if (!value) {
609
+ return;
610
+ }
611
+
612
+ $.each(that.suggestions, function (i, suggestion) {
613
+ var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
614
+ if (foundMatch) {
615
+ bestMatch = suggestion;
616
+ }
617
+ return !foundMatch;
618
+ });
619
+
620
+ that.signalHint(bestMatch);
621
+ },
622
+
623
+ signalHint: function (suggestion) {
624
+ var hintValue = '',
625
+ that = this;
626
+ if (suggestion) {
627
+ hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
628
+ }
629
+ if (that.hintValue !== hintValue) {
630
+ that.hintValue = hintValue;
631
+ that.hint = suggestion;
632
+ (this.options.onHint || $.noop)(hintValue);
633
+ }
634
+ },
635
+
636
+ verifySuggestionsFormat: function (suggestions) {
637
+ // If suggestions is string array, convert them to supported format:
638
+ if (suggestions.length && typeof suggestions[0] === 'string') {
639
+ return $.map(suggestions, function (value) {
640
+ return { value: value, data: null };
641
+ });
642
+ }
643
+
644
+ return suggestions;
645
+ },
646
+
647
+ processResponse: function (result, originalQuery, cacheKey) {
648
+ var that = this,
649
+ options = that.options;
650
+
651
+ result.suggestions = that.verifySuggestionsFormat(result.suggestions);
652
+
653
+ // Cache results if cache is not disabled:
654
+ if (!options.noCache) {
655
+ that.cachedResponse[cacheKey] = result;
656
+ if (options.preventBadQueries && result.suggestions.length === 0) {
657
+ that.badQueries.push(originalQuery);
658
+ }
659
+ }
660
+
661
+ // Return if originalQuery is not matching current query:
662
+ if (originalQuery !== that.getQuery(that.currentValue)) {
663
+ return;
664
+ }
665
+
666
+ that.suggestions = result.suggestions;
667
+ that.suggest();
668
+ },
669
+
670
+ activate: function (index) {
671
+ var that = this,
672
+ activeItem,
673
+ selected = that.classes.selected,
674
+ container = $(that.suggestionsContainer),
675
+ children = container.children();
676
+
677
+ container.children('.' + selected).removeClass(selected);
678
+
679
+ that.selectedIndex = index;
680
+
681
+ if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
682
+ activeItem = children.get(that.selectedIndex);
683
+ $(activeItem).addClass(selected);
684
+ return activeItem;
685
+ }
686
+
687
+ return null;
688
+ },
689
+
690
+ selectHint: function () {
691
+ var that = this,
692
+ i = $.inArray(that.hint, that.suggestions);
693
+
694
+ that.select(i);
695
+ },
696
+
697
+ select: function (i) {
698
+ var that = this;
699
+ that.hide();
700
+ that.onSelect(i);
701
+ },
702
+
703
+ moveUp: function () {
704
+ var that = this;
705
+
706
+ if (that.selectedIndex === -1) {
707
+ return;
708
+ }
709
+
710
+ if (that.selectedIndex === 0) {
711
+ $(that.suggestionsContainer).children().first().removeClass(that.classes.selected);
712
+ that.selectedIndex = -1;
713
+ that.el.val(that.currentValue);
714
+ that.findBestHint();
715
+ return;
716
+ }
717
+
718
+ that.adjustScroll(that.selectedIndex - 1);
719
+ },
720
+
721
+ moveDown: function () {
722
+ var that = this;
723
+
724
+ if (that.selectedIndex === (that.suggestions.length - 1)) {
725
+ return;
726
+ }
727
+
728
+ that.adjustScroll(that.selectedIndex + 1);
729
+ },
730
+
731
+ adjustScroll: function (index) {
732
+ var that = this,
733
+ activeItem = that.activate(index),
734
+ offsetTop,
735
+ upperBound,
736
+ lowerBound,
737
+ heightDelta = 25;
738
+
739
+ if (!activeItem) {
740
+ return;
741
+ }
742
+
743
+ offsetTop = activeItem.offsetTop;
744
+ upperBound = $(that.suggestionsContainer).scrollTop();
745
+ lowerBound = upperBound + that.options.maxHeight - heightDelta;
746
+
747
+ if (offsetTop < upperBound) {
748
+ $(that.suggestionsContainer).scrollTop(offsetTop);
749
+ } else if (offsetTop > lowerBound) {
750
+ $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
751
+ }
752
+
753
+ that.el.val(that.getValue(that.suggestions[index].value));
754
+ that.signalHint(null);
755
+ },
756
+
757
+ onSelect: function (index) {
758
+ var that = this,
759
+ onSelectCallback = that.options.onSelect,
760
+ suggestion = that.suggestions[index];
761
+
762
+ that.currentValue = that.getValue(suggestion.value);
763
+ that.el.val(that.currentValue);
764
+ that.signalHint(null);
765
+ that.suggestions = [];
766
+ that.selection = suggestion;
767
+
768
+ if ($.isFunction(onSelectCallback)) {
769
+ onSelectCallback.call(that.element, suggestion);
770
+ }
771
+ },
772
+
773
+ getValue: function (value) {
774
+ var that = this,
775
+ delimiter = that.options.delimiter,
776
+ currentValue,
777
+ parts;
778
+
779
+ if (!delimiter) {
780
+ return value;
781
+ }
782
+
783
+ currentValue = that.currentValue;
784
+ parts = currentValue.split(delimiter);
785
+
786
+ if (parts.length === 1) {
787
+ return value;
788
+ }
789
+
790
+ return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
791
+ },
792
+
793
+ dispose: function () {
794
+ var that = this;
795
+ that.el.off('.autocomplete').removeData('autocomplete');
796
+ that.disableKillerFn();
797
+ $(window).off('resize.autocomplete', that.fixPositionCapture);
798
+ $(that.suggestionsContainer).remove();
799
+ }
800
+ };
801
+
802
+ // Create chainable jQuery plugin:
803
+ $.fn.autocomplete = function (options, args) {
804
+ var dataKey = 'autocomplete';
805
+ // If function invoked without argument return
806
+ // instance of the first matched element:
807
+ if (arguments.length === 0) {
808
+ return this.first().data(dataKey);
809
+ }
810
+
811
+ return this.each(function () {
812
+ var inputElement = $(this),
813
+ instance = inputElement.data(dataKey);
814
+
815
+ if (typeof options === 'string') {
816
+ if (instance && typeof instance[options] === 'function') {
817
+ instance[options](args);
818
+ }
819
+ } else {
820
+ // If instance already exists, destroy it:
821
+ if (instance && instance.dispose) {
822
+ instance.dispose();
823
+ }
824
+ instance = new Autocomplete(this, options);
825
+ inputElement.data(dataKey, instance);
826
+ }
827
+ });
828
+ };
829
+ }));
@@ -0,0 +1,9 @@
1
+ body { font-family: sans-serif; font-size: 14px; line-height: 1.6em; margin: 0; padding: 0; }
2
+ .container { width: 800px; margin: 0 auto; }
3
+
4
+ .autocomplete-suggestions { border: 1px solid #999; background: #FFF; cursor: default; overflow: auto; -webkit-box-shadow: 1px 4px 3px rgba(50, 50, 50, 0.64); -moz-box-shadow: 1px 4px 3px rgba(50, 50, 50, 0.64); box-shadow: 1px 4px 3px rgba(50, 50, 50, 0.64); }
5
+ .autocomplete-suggestion { padding: 2px 5px; white-space: nowrap; overflow: hidden; }
6
+ .autocomplete-selected { background: #F0F0F0; }
7
+ .autocomplete-suggestions strong { font-weight: normal; color: #3399FF; }
8
+
9
+ input { font-size: 28px; padding: 10px; border: 1px solid #CCC; display: block; margin: 20px 0; }
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jquery_autocomplete_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.9
5
+ platform: ruby
6
+ authors:
7
+ - John Kamenik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: jquery-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.4
41
+ description: Wraps jQuery-Autocomplete in rails asset wrappers.
42
+ email:
43
+ - jkamenik@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - vendor/assets/javascripts/jquery.autocomplete.js
49
+ - vendor/assets/stylesheets/jquery.autocomplete.css
50
+ - lib/jquery_autocomplete_rails/version.rb
51
+ - lib/jquery_autocomplete_rails.rb
52
+ - MIT-LICENSE
53
+ homepage: https://github.com/jkamenik/jquery_autocomplete_rails
54
+ licenses: []
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 2.1.11
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: Provides jQuery Autocomplete assets.
76
+ test_files: []