jquery-textcomplete-rails 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +72 -0
  6. data/Rakefile +1 -0
  7. data/jquery-textcomplete-rails.gemspec +29 -0
  8. data/lib/jquery/textcomplete/rails.rb +10 -0
  9. data/lib/jquery/textcomplete/rails/version.rb +7 -0
  10. data/spec/dummy/.gitignore +16 -0
  11. data/spec/dummy/.rspec +1 -0
  12. data/spec/dummy/Gemfile +51 -0
  13. data/spec/dummy/README.rdoc +28 -0
  14. data/spec/dummy/Rakefile +6 -0
  15. data/spec/dummy/app/assets/images/.keep +0 -0
  16. data/spec/dummy/app/assets/javascripts/application.js.coffee +17 -0
  17. data/spec/dummy/app/assets/stylesheets/application.css.scss +14 -0
  18. data/spec/dummy/app/controllers/application_controller.rb +8 -0
  19. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  20. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  21. data/spec/dummy/app/mailers/.keep +0 -0
  22. data/spec/dummy/app/models/.keep +0 -0
  23. data/spec/dummy/app/models/concerns/.keep +0 -0
  24. data/spec/dummy/app/views/application/index.html.erb +0 -0
  25. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  26. data/spec/dummy/bin/bundle +3 -0
  27. data/spec/dummy/bin/rails +4 -0
  28. data/spec/dummy/bin/rake +4 -0
  29. data/spec/dummy/config.ru +4 -0
  30. data/spec/dummy/config/application.rb +28 -0
  31. data/spec/dummy/config/boot.rb +4 -0
  32. data/spec/dummy/config/database.yml +25 -0
  33. data/spec/dummy/config/environment.rb +5 -0
  34. data/spec/dummy/config/environments/development.rb +29 -0
  35. data/spec/dummy/config/environments/production.rb +80 -0
  36. data/spec/dummy/config/environments/test.rb +36 -0
  37. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  38. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  39. data/spec/dummy/config/initializers/inflections.rb +16 -0
  40. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  41. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  42. data/spec/dummy/config/initializers/session_store.rb +3 -0
  43. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  44. data/spec/dummy/config/locales/en.yml +23 -0
  45. data/spec/dummy/config/routes.rb +56 -0
  46. data/spec/dummy/db/seeds.rb +7 -0
  47. data/spec/dummy/lib/assets/.keep +0 -0
  48. data/spec/dummy/lib/tasks/.keep +0 -0
  49. data/spec/dummy/log/.keep +0 -0
  50. data/spec/dummy/public/404.html +58 -0
  51. data/spec/dummy/public/422.html +58 -0
  52. data/spec/dummy/public/500.html +57 -0
  53. data/spec/dummy/public/favicon.ico +0 -0
  54. data/spec/dummy/public/robots.txt +5 -0
  55. data/spec/dummy/spec/controllers/application_controller_spec.rb +13 -0
  56. data/spec/dummy/spec/spec_helper.rb +41 -0
  57. data/spec/dummy/vendor/assets/javascripts/.keep +0 -0
  58. data/spec/dummy/vendor/assets/stylesheets/.keep +0 -0
  59. data/vendor/assets/javascripts/jquery-textcomplete-rails.js.coffee +1 -0
  60. data/vendor/assets/javascripts/jquery-textcomplete-rails/base.js +553 -0
  61. data/vendor/assets/stylesheets/jquery-textcomplete-rails.css.scss +1 -0
  62. data/vendor/assets/stylesheets/jquery-textcomplete-rails/base.css.scss +19 -0
  63. metadata +238 -0
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style>
6
+ body {
7
+ background-color: #EFEFEF;
8
+ color: #2E2F30;
9
+ text-align: center;
10
+ font-family: arial, sans-serif;
11
+ }
12
+
13
+ div.dialog {
14
+ width: 25em;
15
+ margin: 4em auto 0 auto;
16
+ border: 1px solid #CCC;
17
+ border-right-color: #999;
18
+ border-left-color: #999;
19
+ border-bottom-color: #BBB;
20
+ border-top: #B00100 solid 4px;
21
+ border-top-left-radius: 9px;
22
+ border-top-right-radius: 9px;
23
+ background-color: white;
24
+ padding: 7px 4em 0 4em;
25
+ }
26
+
27
+ h1 {
28
+ font-size: 100%;
29
+ color: #730E15;
30
+ line-height: 1.5em;
31
+ }
32
+
33
+ body > p {
34
+ width: 33em;
35
+ margin: 0 auto 1em;
36
+ padding: 1em 0;
37
+ background-color: #F7F7F7;
38
+ border: 1px solid #CCC;
39
+ border-right-color: #999;
40
+ border-bottom-color: #999;
41
+ border-bottom-left-radius: 4px;
42
+ border-bottom-right-radius: 4px;
43
+ border-top-color: #DADADA;
44
+ color: #666;
45
+ box-shadow:0 3px 8px rgba(50, 50, 50, 0.17);
46
+ }
47
+ </style>
48
+ </head>
49
+
50
+ <body>
51
+ <!-- This file lives in public/500.html -->
52
+ <div class="dialog">
53
+ <h1>We're sorry, but something went wrong.</h1>
54
+ </div>
55
+ <p>If you are the application owner check the logs for more information.</p>
56
+ </body>
57
+ </html>
File without changes
@@ -0,0 +1,5 @@
1
+ # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2
+ #
3
+ # To ban all spiders from the entire site uncomment the next two lines:
4
+ # User-agent: *
5
+ # Disallow: /
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApplicationController do
4
+ render_views
5
+
6
+ describe 'GET index' do
7
+ it 'successfully renders application index' do
8
+ get :index
9
+
10
+ expect(response).to be_success
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,41 @@
1
+ # This file is copied to spec/ when you run 'rails generate rspec:install'
2
+ ENV["RAILS_ENV"] ||= 'test'
3
+ require File.expand_path("../../config/environment", __FILE__)
4
+ require 'rspec/rails'
5
+
6
+ # Requires supporting ruby files with custom matchers and macros, etc, in
7
+ # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
8
+ # run as spec files by default. This means that files in spec/support that end
9
+ # in _spec.rb will both be required and run as specs, causing the specs to be
10
+ # run twice. It is recommended that you do not name files matching this glob to
11
+ # end with _spec.rb. You can configure this pattern with with the --pattern
12
+ # option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
13
+ Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
14
+
15
+ # Checks for pending migrations before tests are run.
16
+ # If you are not using ActiveRecord, you can remove this line.
17
+ ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
18
+
19
+ RSpec.configure do |config|
20
+ # ## Mock Framework
21
+ #
22
+ # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
23
+ #
24
+ # config.mock_with :mocha
25
+ # config.mock_with :flexmock
26
+ # config.mock_with :rr
27
+
28
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
29
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
30
+
31
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
32
+ # examples within a transaction, remove the following line or assign false
33
+ # instead of true.
34
+ config.use_transactional_fixtures = true
35
+
36
+ # Run specs in random order to surface order dependencies. If you find an
37
+ # order dependency and want to debug it, you can fix the order by providing
38
+ # the seed, which is printed after each run.
39
+ # --seed 1234
40
+ config.order = "random"
41
+ end
File without changes
File without changes
@@ -0,0 +1 @@
1
+ #= require_tree ./jquery-textcomplete-rails
@@ -0,0 +1,553 @@
1
+ /*!
2
+ * jQuery.textcomplete.js
3
+ *
4
+ * Repositiory: https://github.com/yuku-t/jquery-textcomplete
5
+ * License: MIT
6
+ * Author: Yuku Takahashi
7
+ */
8
+
9
+ ;(function ($) {
10
+
11
+ 'use strict';
12
+
13
+ /**
14
+ * Exclusive execution control utility.
15
+ */
16
+ var lock = function (func) {
17
+ var free, locked;
18
+ free = function () { locked = false; };
19
+ return function () {
20
+ var args;
21
+ if (locked) return;
22
+ locked = true;
23
+ args = toArray(arguments);
24
+ args.unshift(free);
25
+ func.apply(this, args);
26
+ };
27
+ };
28
+
29
+ /**
30
+ * Convert arguments into a real array.
31
+ */
32
+ var toArray = function (args) {
33
+ var result;
34
+ result = Array.prototype.slice.call(args);
35
+ return result;
36
+ };
37
+
38
+ /**
39
+ * Get the styles of any element from property names.
40
+ */
41
+ var getStyles = (function () {
42
+ var color;
43
+ color = $('<div></div>').css(['color']).color;
44
+ if (typeof color !== 'undefined') {
45
+ return function ($el, properties) {
46
+ return $el.css(properties);
47
+ };
48
+ } else { // for jQuery 1.8 or below
49
+ return function ($el, properties) {
50
+ var styles;
51
+ styles = {};
52
+ $.each(properties, function (i, property) {
53
+ styles[property] = $el.css(property);
54
+ });
55
+ return styles;
56
+ };
57
+ }
58
+ })();
59
+
60
+ /**
61
+ * Default template function.
62
+ */
63
+ var identity = function (obj) { return obj; };
64
+
65
+ /**
66
+ * Memoize a search function.
67
+ */
68
+ var memoize = function (func) {
69
+ var memo = {};
70
+ return function (term, callback) {
71
+ if (memo[term]) {
72
+ callback(memo[term]);
73
+ } else {
74
+ func.call(this, term, function (data) {
75
+ memo[term] = (memo[term] || []).concat(data);
76
+ callback.apply(null, arguments);
77
+ });
78
+ }
79
+ };
80
+ };
81
+
82
+ /**
83
+ * Determine if the array contains a given value.
84
+ */
85
+ var include = function (array, value) {
86
+ var i, l;
87
+ if (array.indexOf) return array.indexOf(value) != -1;
88
+ for (i = 0, l = array.length; i < l; i++) {
89
+ if (array[i] === value) return true;
90
+ }
91
+ return false;
92
+ };
93
+
94
+ /**
95
+ * Textarea manager class.
96
+ */
97
+ var Completer = (function () {
98
+ var html, css, $baseWrapper, $baseList, _id;
99
+
100
+ html = {
101
+ wrapper: '<div class="textcomplete-wrapper"></div>',
102
+ list: '<ul class="dropdown-menu"></ul>'
103
+ };
104
+ css = {
105
+ wrapper: {
106
+ position: 'relative'
107
+ },
108
+ list: {
109
+ position: 'absolute',
110
+ top: 0,
111
+ left: 0,
112
+ zIndex: '100',
113
+ display: 'none'
114
+ }
115
+ };
116
+ $baseWrapper = $(html.wrapper).css(css.wrapper);
117
+ $baseList = $(html.list).css(css.list);
118
+ _id = 0;
119
+
120
+ function Completer($el) {
121
+ var focus;
122
+ this.el = $el.get(0); // textarea element
123
+ focus = this.el === document.activeElement;
124
+ // Cannot wrap $el at initialize method lazily due to Firefox's behavior.
125
+ this.$el = wrapElement($el); // Focus is lost
126
+ this.id = 'textComplete' + _id++;
127
+ this.strategies = [];
128
+ if (focus) {
129
+ this.initialize();
130
+ this.$el.focus();
131
+ } else {
132
+ this.$el.one('focus.textComplete', $.proxy(this.initialize, this));
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Completer's public methods
138
+ */
139
+ $.extend(Completer.prototype, {
140
+
141
+ /**
142
+ * Prepare ListView and bind events.
143
+ */
144
+ initialize: function () {
145
+ var $list, globalEvents;
146
+ $list = $baseList.clone();
147
+ this.listView = new ListView($list, this);
148
+ this.$el
149
+ .before($list)
150
+ .on({
151
+ 'keyup.textComplete': $.proxy(this.onKeyup, this),
152
+ 'keydown.textComplete': $.proxy(this.listView.onKeydown,
153
+ this.listView)
154
+ });
155
+ globalEvents = {};
156
+ globalEvents['click.' + this.id] = $.proxy(this.onClickDocument, this);
157
+ globalEvents['keyup.' + this.id] = $.proxy(this.onKeyupDocument, this);
158
+ $(document).on(globalEvents);
159
+ },
160
+
161
+ /**
162
+ * Register strategies to the completer.
163
+ */
164
+ register: function (strategies) {
165
+ this.strategies = this.strategies.concat(strategies);
166
+ },
167
+
168
+ /**
169
+ * Show autocomplete list next to the caret.
170
+ */
171
+ renderList: function (data) {
172
+ if (this.clearAtNext) {
173
+ this.listView.clear();
174
+ this.clearAtNext = false;
175
+ }
176
+ if (data.length) {
177
+ if (!this.listView.shown) {
178
+ this.listView
179
+ .setPosition(this.getCaretPosition())
180
+ .clear()
181
+ .activate();
182
+ this.listView.strategy = this.strategy;
183
+ }
184
+ data = data.slice(0, this.strategy.maxCount);
185
+ this.listView.render(data);
186
+ }
187
+
188
+ if (!this.listView.data.length && this.listView.shown) {
189
+ this.listView.deactivate();
190
+ }
191
+ },
192
+
193
+ searchCallbackFactory: function (free) {
194
+ var self = this;
195
+ return function (data, keep) {
196
+ self.renderList(data);
197
+ if (!keep) {
198
+ // This is the last callback for this search.
199
+ free();
200
+ self.clearAtNext = true;
201
+ }
202
+ };
203
+ },
204
+
205
+ /**
206
+ * Keyup event handler.
207
+ */
208
+ onKeyup: function (e) {
209
+ var searchQuery, term;
210
+ if (this.skipSearch(e)) { return; }
211
+
212
+ searchQuery = this.extractSearchQuery(this.getTextFromHeadToCaret());
213
+ if (searchQuery.length) {
214
+ term = searchQuery[1];
215
+ if (this.term === term) return; // Ignore shift-key or something.
216
+ this.term = term;
217
+ this.search(searchQuery);
218
+ } else {
219
+ this.term = null;
220
+ this.listView.deactivate();
221
+ }
222
+ },
223
+
224
+ /**
225
+ * Suppress searching if it returns true.
226
+ */
227
+ skipSearch: function (e) {
228
+ if (this.skipNextKeyup) {
229
+ this.skipNextKeyup = false;
230
+ return true;
231
+ }
232
+ switch (e.keyCode) {
233
+ case 40:
234
+ case 38:
235
+ return true;
236
+ }
237
+ },
238
+
239
+ onSelect: function (value) {
240
+ var pre, post, newSubStr;
241
+ pre = this.getTextFromHeadToCaret();
242
+ post = this.el.value.substring(this.el.selectionEnd);
243
+
244
+ newSubStr = this.strategy.replace(value);
245
+ if ($.isArray(newSubStr)) {
246
+ post = newSubStr[1] + post;
247
+ newSubStr = newSubStr[0];
248
+ }
249
+ pre = pre.replace(this.strategy.match, newSubStr);
250
+ this.$el.val(pre + post)
251
+ .trigger('change')
252
+ .trigger('textComplete:select', value);
253
+ this.el.focus();
254
+ this.el.selectionStart = this.el.selectionEnd = pre.length;
255
+ this.skipNextKeyup = true;
256
+ },
257
+
258
+ /**
259
+ * Global click event handler.
260
+ */
261
+ onClickDocument: function (e) {
262
+ if (e.originalEvent && !e.originalEvent.keepTextCompleteDropdown) {
263
+ this.listView.deactivate();
264
+ }
265
+ },
266
+
267
+ /**
268
+ * Global keyup event handler.
269
+ */
270
+ onKeyupDocument: function (e) {
271
+ if (this.listView.shown && e.keyCode === 27) { // ESC
272
+ this.listView.deactivate();
273
+ this.$el.focus();
274
+ }
275
+ },
276
+
277
+ /**
278
+ * Remove all event handlers and the wrapper element.
279
+ */
280
+ destroy: function () {
281
+ var $wrapper;
282
+ this.$el.off('.textComplete');
283
+ $(document).off('.' + this.id);
284
+ if (this.listView) { this.listView.destroy(); }
285
+ $wrapper = this.$el.parent();
286
+ $wrapper.after(this.$el).remove();
287
+ this.$el.data('textComplete', void 0);
288
+ this.$el = null;
289
+ },
290
+
291
+ // Helper methods
292
+ // ==============
293
+
294
+ /**
295
+ * Returns caret's relative coordinates from textarea's left top corner.
296
+ */
297
+ getCaretPosition: function () {
298
+ // Browser native API does not provide the way to know the position of
299
+ // caret in pixels, so that here we use a kind of hack to accomplish
300
+ // the aim. First of all it puts a div element and completely copies
301
+ // the textarea's style to the element, then it inserts the text and a
302
+ // span element into the textarea.
303
+ // Consequently, the span element's position is the thing what we want.
304
+
305
+ if (this.el.selectionEnd === 0) return;
306
+ var properties, css, $div, $span, position, dir;
307
+
308
+ dir = this.$el.attr('dir') || this.$el.css('direction');
309
+ properties = ['border-width', 'font-family', 'font-size', 'font-style',
310
+ 'font-variant', 'font-weight', 'height', 'letter-spacing',
311
+ 'word-spacing', 'line-height', 'text-decoration', 'text-align',
312
+ 'width', 'padding-top', 'padding-right', 'padding-bottom',
313
+ 'padding-left', 'margin-top', 'margin-right', 'margin-bottom',
314
+ 'margin-left'
315
+ ];
316
+ css = $.extend({
317
+ position: 'absolute',
318
+ overflow: 'auto',
319
+ 'white-space': 'pre-wrap',
320
+ top: 0,
321
+ left: -9999,
322
+ direction: dir
323
+ }, getStyles(this.$el, properties));
324
+
325
+ $div = $('<div></div>').css(css).text(this.getTextFromHeadToCaret());
326
+ $span = $('<span></span>').text('.').appendTo($div);
327
+ this.$el.before($div);
328
+ position = $span.position();
329
+ position.top += $span.height() - this.$el.scrollTop();
330
+ if (dir === 'rtl') { position.left -= this.listView.$el.width(); }
331
+ $div.remove();
332
+ return position;
333
+ },
334
+
335
+ getTextFromHeadToCaret: function () {
336
+ var text, selectionEnd, range;
337
+ selectionEnd = this.el.selectionEnd;
338
+ if (typeof selectionEnd === 'number') {
339
+ text = this.el.value.substring(0, selectionEnd);
340
+ } else if (document.selection) {
341
+ range = this.el.createTextRange();
342
+ range.moveStart('character', 0);
343
+ range.moveEnd('textedit');
344
+ text = range.text;
345
+ }
346
+ return text;
347
+ },
348
+
349
+ /**
350
+ * Parse the value of textarea and extract search query.
351
+ */
352
+ extractSearchQuery: function (text) {
353
+ // If a search query found, it returns used strategy and the query
354
+ // term. If the caret is currently in a code block or search query does
355
+ // not found, it returns an empty array.
356
+
357
+ var i, l, strategy, match;
358
+ for (i = 0, l = this.strategies.length; i < l; i++) {
359
+ strategy = this.strategies[i];
360
+ match = text.match(strategy.match);
361
+ if (match) { return [strategy, match[strategy.index]]; }
362
+ }
363
+ return [];
364
+ },
365
+
366
+ search: lock(function (free, searchQuery) {
367
+ var term;
368
+ this.strategy = searchQuery[0];
369
+ term = searchQuery[1];
370
+ this.strategy.search(term, this.searchCallbackFactory(free));
371
+ })
372
+ });
373
+
374
+ /**
375
+ * Completer's private functions
376
+ */
377
+ var wrapElement = function ($el) {
378
+ return $el.wrap($baseWrapper.clone().css('display', $el.css('display')));
379
+ };
380
+
381
+ return Completer;
382
+ })();
383
+
384
+ /**
385
+ * Dropdown menu manager class.
386
+ */
387
+ var ListView = (function () {
388
+
389
+ function ListView($el, completer) {
390
+ this.data = [];
391
+ this.$el = $el;
392
+ this.index = 0;
393
+ this.completer = completer;
394
+
395
+ this.$el.on('click.textComplete', 'li.textcomplete-item',
396
+ $.proxy(this.onClick, this));
397
+ }
398
+
399
+ $.extend(ListView.prototype, {
400
+ shown: false,
401
+
402
+ render: function (data) {
403
+ var html, i, l, index, val;
404
+
405
+ html = '';
406
+ for (i = 0, l = data.length; i < l; i++) {
407
+ val = data[i];
408
+ if (include(this.data, val)) continue;
409
+ index = this.data.length;
410
+ this.data.push(val);
411
+ html += '<li class="textcomplete-item" data-index="' + index + '"><a>';
412
+ html += this.strategy.template(val);
413
+ html += '</a></li>';
414
+ if (this.data.length === this.strategy.maxCount) break;
415
+ }
416
+ this.$el.append(html);
417
+ if (!this.data.length) {
418
+ this.deactivate();
419
+ } else {
420
+ this.activateIndexedItem();
421
+ }
422
+ },
423
+
424
+ clear: function () {
425
+ this.data = [];
426
+ this.$el.html('');
427
+ this.index = 0;
428
+ return this;
429
+ },
430
+
431
+ activateIndexedItem: function () {
432
+ this.$el.find('.active').removeClass('active');
433
+ this.getActiveItem().addClass('active');
434
+ },
435
+
436
+ getActiveItem: function () {
437
+ return $(this.$el.children().get(this.index));
438
+ },
439
+
440
+ activate: function () {
441
+ if (!this.shown) {
442
+ this.$el.show();
443
+ this.completer.$el.trigger('textComplete:show');
444
+ this.shown = true;
445
+ }
446
+ return this;
447
+ },
448
+
449
+ deactivate: function () {
450
+ if (this.shown) {
451
+ this.$el.hide();
452
+ this.completer.$el.trigger('textComplete:hide');
453
+ this.shown = false;
454
+ this.data = this.index = null;
455
+ }
456
+ return this;
457
+ },
458
+
459
+ setPosition: function (position) {
460
+ this.$el.css(position);
461
+ return this;
462
+ },
463
+
464
+ select: function (index) {
465
+ var self = this;
466
+ this.completer.onSelect(this.data[index]);
467
+ // Deactive at next tick to allow other event handlers to know whether
468
+ // the dropdown has been shown or not.
469
+ setTimeout(function () { self.deactivate(); }, 0);
470
+ },
471
+
472
+ onKeydown: function (e) {
473
+ if (!this.shown) return;
474
+ if (e.keyCode === 38) { // UP
475
+ e.preventDefault();
476
+ if (this.index === 0) {
477
+ this.index = this.data.length-1;
478
+ } else {
479
+ this.index -= 1;
480
+ }
481
+ this.activateIndexedItem();
482
+ } else if (e.keyCode === 40) { // DOWN
483
+ e.preventDefault();
484
+ if (this.index === this.data.length - 1) {
485
+ this.index = 0;
486
+ } else {
487
+ this.index += 1;
488
+ }
489
+ this.activateIndexedItem();
490
+ } else if (e.keyCode === 13 || e.keyCode === 9) { // ENTER or TAB
491
+ e.preventDefault();
492
+ this.select(parseInt(this.getActiveItem().data('index'), 10));
493
+ }
494
+ },
495
+
496
+ onClick: function (e) {
497
+ var $e = $(e.target);
498
+ e.originalEvent.keepTextCompleteDropdown = true;
499
+ if (!$e.hasClass('textcomplete-item')) {
500
+ $e = $e.parents('li.textcomplete-item');
501
+ }
502
+ this.select(parseInt($e.data('index'), 10));
503
+ },
504
+
505
+ destroy: function () {
506
+ this.deactivate();
507
+ this.$el.off('click.textComplete').remove();
508
+ this.$el = null;
509
+ }
510
+ });
511
+
512
+ return ListView;
513
+ })();
514
+
515
+ $.fn.textcomplete = function (strategies) {
516
+ var i, l, strategy, dataKey;
517
+
518
+ dataKey = 'textComplete';
519
+
520
+ if (strategies === 'destroy') {
521
+ return this.each(function () {
522
+ var completer = $(this).data(dataKey);
523
+ if (completer) { completer.destroy(); }
524
+ });
525
+ }
526
+
527
+ for (i = 0, l = strategies.length; i < l; i++) {
528
+ strategy = strategies[i];
529
+ if (!strategy.template) {
530
+ strategy.template = identity;
531
+ }
532
+ if (strategy.index == null) {
533
+ strategy.index = 2;
534
+ }
535
+ if (strategy.cache) {
536
+ strategy.search = memoize(strategy.search);
537
+ }
538
+ strategy.maxCount || (strategy.maxCount = 10);
539
+ }
540
+
541
+ return this.each(function () {
542
+ var $this, completer;
543
+ $this = $(this);
544
+ completer = $this.data(dataKey);
545
+ if (!completer) {
546
+ completer = new Completer($this);
547
+ $this.data(dataKey, completer);
548
+ }
549
+ completer.register(strategies);
550
+ });
551
+ };
552
+
553
+ })(window.jQuery || window.Zepto);