active_admin_tokeninput 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a185b6d32121eef50d5943d37dbd38391fd599ff
4
+ data.tar.gz: 13fad77e729f70a0fdcf19b787413f501af071f9
5
+ SHA512:
6
+ metadata.gz: 12252b74d2d4a3f913c54c278f122d82fac6a6944614a06c32c3f1d2efa5c1e5a436cb57125c4dad3fa5551edc14336c243f57076153ea7ed353495e79908acb
7
+ data.tar.gz: 1493a370e2f1e90c1c24dd0006e06a8264b38ab9d532f9110481a5c3221ed1a82b320d2948a9dc5e53dd9e63ac7183fa25665fc9cff03e914d4522a62e1ccbaa
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 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.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = ActiveAdminTaginput
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ActiveAdminTokeninput'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
@@ -0,0 +1,9 @@
1
+ require "active_admin_tokeninput/engine"
2
+ require "active_admin_tokeninput/version"
3
+ require "active_admin_tokeninput/dsl"
4
+
5
+ require "activeadmin"
6
+ module ActiveAdminTokeninput
7
+ end
8
+
9
+ ::ActiveAdmin::DSL.send(:include, ActiveAdminTokeninput::DSL)
@@ -0,0 +1,12 @@
1
+ module ActiveAdminTokeninput
2
+ module DSL
3
+ def tag_autocomplete(options={})
4
+
5
+
6
+ collection_action :autocomplete, :method => :get do
7
+ collection = scoped_collection.where("name ILIKE ?", "%#{params[:q]}%").order('lower(name) ASC').limit(10)
8
+ render :json => collection.as_json(:only => [:id, :name]), root: false
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module ActiveAdminTokeninput
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ActiveAdminTokeninput
4
+
5
+ initializer 'active_admin.tokeninput', :group => :all do |app|
6
+
7
+ vendor_path = File.expand_path("../../vendor/assets", __FILE__)
8
+ app.config.assets.paths.push(vendor_path.to_s)
9
+ app.config.assets.precompile += %w( active_admin_tokeninput/active_admin_tokeninput.js )
10
+
11
+ ActiveAdmin.application.tap do |config|
12
+ config.register_javascript 'active_admin_tokeninput/active_admin_tokeninput'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveAdminTokeninput
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :active_admin_taginput do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,1086 @@
1
+ /*
2
+ * jQuery Plugin: Tokenizing Autocomplete Text Entry
3
+ * Version 1.6.2
4
+ *
5
+ * Copyright (c) 2009 James Smith (http://loopj.com)
6
+ * Licensed jointly under the GPL and MIT licenses,
7
+ * choose which one suits your project best!
8
+ *
9
+ * This is a modified version of the original and
10
+ * posts tokens as an array instead of a comma
11
+ * seperated list.
12
+ *
13
+ * Can be found at https://github.com/Papercloud/jquery-tokeninput
14
+ *
15
+ */
16
+ (function ($) {
17
+ // Default settings
18
+ var DEFAULT_SETTINGS = {
19
+ // Search settings
20
+ method: "GET",
21
+ queryParam: "q",
22
+ searchDelay: 300,
23
+ minChars: 1,
24
+ propertyToSearch: "name",
25
+ jsonContainer: null,
26
+ contentType: "json",
27
+
28
+ // Prepopulation settings
29
+ prePopulate: null,
30
+ processPrePopulate: false,
31
+
32
+ // Display settings
33
+ hintText: "Type in a search term",
34
+ noResultsText: "No results",
35
+ searchingText: "Searching...",
36
+ deleteText: "&times;",
37
+ animateDropdown: true,
38
+ placeholder: null,
39
+ theme: null,
40
+ zindex: 999,
41
+ resultsLimit: null,
42
+ parentInput: null,// custom variables
43
+ fieldName: null,// custom variables
44
+
45
+
46
+ enableHTML: false,
47
+
48
+ resultsFormatter: function(item) {
49
+ var string = item[this.propertyToSearch];
50
+ return "<li>" + (this.enableHTML ? string : _escapeHTML(string)) + "</li>";
51
+ },
52
+
53
+ tokenFormatter: function(item) {
54
+ var string = item[this.propertyToSearch];
55
+ return "<li><p>" + (this.enableHTML ? string : _escapeHTML(string)) + "</p></li>";
56
+ },
57
+
58
+ // Tokenization settings
59
+ tokenLimit: null,
60
+ tokenDelimiter: ",",
61
+ preventDuplicates: false,
62
+ tokenValue: "id",
63
+
64
+ // Behavioral settings
65
+ allowFreeTagging: false,
66
+ allowTabOut: false,
67
+
68
+ // Callbacks
69
+ onResult: null,
70
+ onCachedResult: null,
71
+ onAdd: null,
72
+ onFreeTaggingAdd: null,
73
+ onDelete: null,
74
+ onReady: null,
75
+
76
+ // Other settings
77
+ idPrefix: "token-input-",
78
+
79
+ // Keep track if the input is currently in disabled mode
80
+ disabled: false
81
+ };
82
+
83
+ // Default classes to use when theming
84
+ var DEFAULT_CLASSES = {
85
+ tokenList: "token-input-list",
86
+ token: "token-input-token",
87
+ tokenReadOnly: "token-input-token-readonly",
88
+ tokenDelete: "token-input-delete-token",
89
+ selectedToken: "token-input-selected-token",
90
+ highlightedToken: "token-input-highlighted-token",
91
+ dropdown: "token-input-dropdown",
92
+ dropdownItem: "token-input-dropdown-item",
93
+ dropdownItem2: "token-input-dropdown-item2",
94
+ selectedDropdownItem: "token-input-selected-dropdown-item",
95
+ inputToken: "token-input-input-token",
96
+ focused: "token-input-focused",
97
+ disabled: "token-input-disabled"
98
+ };
99
+
100
+ // Input box position "enum"
101
+ var POSITION = {
102
+ BEFORE: 0,
103
+ AFTER: 1,
104
+ END: 2
105
+ };
106
+
107
+ // Keys "enum"
108
+ var KEY = {
109
+ BACKSPACE: 8,
110
+ TAB: 9,
111
+ ENTER: 13,
112
+ ESCAPE: 27,
113
+ SPACE: 32,
114
+ PAGE_UP: 33,
115
+ PAGE_DOWN: 34,
116
+ END: 35,
117
+ HOME: 36,
118
+ LEFT: 37,
119
+ UP: 38,
120
+ RIGHT: 39,
121
+ DOWN: 40,
122
+ NUMPAD_ENTER: 108,
123
+ COMMA: 188
124
+ };
125
+
126
+ var HTML_ESCAPES = {
127
+ '&': '&amp;',
128
+ '<': '&lt;',
129
+ '>': '&gt;',
130
+ '"': '&quot;',
131
+ "'": '&#x27;',
132
+ '/': '&#x2F;'
133
+ };
134
+
135
+ var HTML_ESCAPE_CHARS = /[&<>"'\/]/g;
136
+
137
+ function coerceToString(val) {
138
+ return String((val === null || val === undefined) ? '' : val);
139
+ }
140
+
141
+ function _escapeHTML(text) {
142
+ return coerceToString(text).replace(HTML_ESCAPE_CHARS, function(match) {
143
+ return HTML_ESCAPES[match];
144
+ });
145
+ }
146
+
147
+ // Additional public (exposed) methods
148
+ var methods = {
149
+ init: function(url_or_data_or_function, options) {
150
+ var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
151
+
152
+ return this.each(function () {
153
+ $(this).data("settings", settings);
154
+ $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
155
+ });
156
+ },
157
+ clear: function() {
158
+ this.data("tokenInputObject").clear();
159
+ return this;
160
+ },
161
+ add: function(item) {
162
+ this.data("tokenInputObject").add(item);
163
+ return this;
164
+ },
165
+ remove: function(item) {
166
+ this.data("tokenInputObject").remove(item);
167
+ return this;
168
+ },
169
+ get: function() {
170
+ return this.data("tokenInputObject").getTokens();
171
+ },
172
+ toggleDisabled: function(disable) {
173
+ this.data("tokenInputObject").toggleDisabled(disable);
174
+ return this;
175
+ },
176
+ setOptions: function(options){
177
+ $(this).data("settings", $.extend({}, $(this).data("settings"), options || {}));
178
+ return this;
179
+ },
180
+ destroy: function () {
181
+ if(this.data("tokenInputObject")){
182
+ this.data("tokenInputObject").clear();
183
+ var tmpInput = this;
184
+ var closest = this.parent();
185
+ closest.empty();
186
+ tmpInput.show();
187
+ closest.append(tmpInput);
188
+ return tmpInput;
189
+ }
190
+ }
191
+ };
192
+
193
+ // Expose the .tokenInput function to jQuery as a plugin
194
+ $.fn.tokenInput = function (method) {
195
+ // Method calling and initialization logic
196
+ if(methods[method]) {
197
+ return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
198
+ } else {
199
+ return methods.init.apply(this, arguments);
200
+ }
201
+ };
202
+
203
+
204
+
205
+ // TokenList class for each input
206
+ $.TokenList = function (input, url_or_data, settings) {
207
+ //
208
+ // Initialization
209
+ //
210
+
211
+ // Configure the data source
212
+ if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") {
213
+ // Set the url to query against
214
+ $(input).data("settings").url = url_or_data;
215
+
216
+ // If the URL is a function, evaluate it here to do our initalization work
217
+ var url = computeURL();
218
+
219
+ // Make a smart guess about cross-domain if it wasn't explicitly specified
220
+ if($(input).data("settings").crossDomain === undefined && typeof url === "string") {
221
+ if(url.indexOf("://") === -1) {
222
+ $(input).data("settings").crossDomain = false;
223
+ } else {
224
+ $(input).data("settings").crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
225
+ }
226
+ }
227
+ } else if(typeof(url_or_data) === "object") {
228
+ // Set the local data to search through
229
+ $(input).data("settings").local_data = url_or_data;
230
+ }
231
+
232
+ // Build class names
233
+ if($(input).data("settings").classes) {
234
+ // Use custom class names
235
+ $(input).data("settings").classes = $.extend({}, DEFAULT_CLASSES, $(input).data("settings").classes);
236
+ } else if($(input).data("settings").theme) {
237
+ // Use theme-suffixed default class names
238
+ $(input).data("settings").classes = {};
239
+ $.each(DEFAULT_CLASSES, function(key, value) {
240
+ $(input).data("settings").classes[key] = value + "-" + $(input).data("settings").theme;
241
+ });
242
+ } else {
243
+ $(input).data("settings").classes = DEFAULT_CLASSES;
244
+ }
245
+
246
+
247
+ // Save the tokens
248
+ var saved_tokens = [];
249
+
250
+ // Keep track of the number of tokens in the list
251
+ var token_count = 0;
252
+
253
+ // Basic cache to save on db hits
254
+ var cache = new $.TokenList.Cache();
255
+
256
+ // Keep track of the timeout, old vals
257
+ var timeout;
258
+ var input_val;
259
+
260
+ // Create a new text input an attach keyup events
261
+ var input_box = $("<input type=\"text\" autocomplete=\"off\" autocapitalize=\"off\">")
262
+ .css({
263
+ outline: "none"
264
+ })
265
+ .attr("id", $(input).data("settings").idPrefix + input.id)
266
+ .focus(function () {
267
+ if ($(input).data("settings").disabled) {
268
+ return false;
269
+ } else
270
+ if ($(input).data("settings").tokenLimit === null || $(input).data("settings").tokenLimit !== token_count) {
271
+ show_dropdown_hint();
272
+ }
273
+ token_list.addClass($(input).data("settings").classes.focused);
274
+ })
275
+ .blur(function () {
276
+ hide_dropdown();
277
+
278
+ if ($(input).data("settings").allowFreeTagging) {
279
+ add_freetagging_tokens();
280
+ }
281
+
282
+ $(this).val("");
283
+ token_list.removeClass($(input).data("settings").classes.focused);
284
+ })
285
+ .bind("keyup keydown blur update", resize_input)
286
+ .keydown(function (event) {
287
+ var previous_token;
288
+ var next_token;
289
+
290
+ switch(event.keyCode) {
291
+ case KEY.LEFT:
292
+ case KEY.RIGHT:
293
+ case KEY.UP:
294
+ case KEY.DOWN:
295
+ if(!$(this).val()) {
296
+ previous_token = input_token.prev();
297
+ next_token = input_token.next();
298
+
299
+ if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
300
+ // Check if there is a previous/next token and it is selected
301
+ if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
302
+ deselect_token($(selected_token), POSITION.BEFORE);
303
+ } else {
304
+ deselect_token($(selected_token), POSITION.AFTER);
305
+ }
306
+ } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
307
+ // We are moving left, select the previous token if it exists
308
+ select_token($(previous_token.get(0)));
309
+ } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
310
+ // We are moving right, select the next token if it exists
311
+ select_token($(next_token.get(0)));
312
+ }
313
+ } else {
314
+ var dropdown_item = null;
315
+
316
+ if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
317
+ dropdown_item = $(selected_dropdown_item).next();
318
+ } else {
319
+ dropdown_item = $(selected_dropdown_item).prev();
320
+ }
321
+
322
+ if(dropdown_item.length) {
323
+ select_dropdown_item(dropdown_item);
324
+ }
325
+ }
326
+ return false;
327
+ break;
328
+
329
+ case KEY.BACKSPACE:
330
+ previous_token = input_token.prev();
331
+
332
+ if(!$(this).val().length) {
333
+ if(selected_token) {
334
+ delete_token($(selected_token));
335
+ hidden_input.change();
336
+ } else if(previous_token.length) {
337
+ select_token($(previous_token.get(0)));
338
+ }
339
+
340
+ return false;
341
+ } else if($(this).val().length === 1) {
342
+ hide_dropdown();
343
+ } else {
344
+ // set a timeout just long enough to let this function finish.
345
+ setTimeout(function(){do_search();}, 5);
346
+ }
347
+ break;
348
+
349
+ case KEY.TAB:
350
+ case KEY.ENTER:
351
+ case KEY.NUMPAD_ENTER:
352
+ case KEY.COMMA:
353
+ if(selected_dropdown_item) {
354
+ add_token($(selected_dropdown_item).data("tokeninput"));
355
+ hidden_input.change();
356
+ } else {
357
+ if ($(input).data("settings").allowFreeTagging) {
358
+ if($(input).data("settings").allowTabOut && $(this).val() === "") {
359
+ return true;
360
+ } else {
361
+ add_freetagging_tokens();
362
+ }
363
+ } else {
364
+ $(this).val("");
365
+ if($(input).data("settings").allowTabOut) {
366
+ return true;
367
+ }
368
+ }
369
+ event.stopPropagation();
370
+ event.preventDefault();
371
+ }
372
+ return false;
373
+
374
+ case KEY.ESCAPE:
375
+ hide_dropdown();
376
+ return true;
377
+
378
+ default:
379
+ if(String.fromCharCode(event.which)) {
380
+ // set a timeout just long enough to let this function finish.
381
+ setTimeout(function(){do_search();}, 5);
382
+ }
383
+ break;
384
+ }
385
+ });
386
+
387
+ // Keep reference for placeholder
388
+ if (settings.placeholder)
389
+ input_box.attr("placeholder", settings.placeholder)
390
+
391
+ // Keep a reference to the original input box
392
+ var hidden_input = $(input)
393
+ .hide()
394
+ .val("")
395
+ .focus(function () {
396
+ focus_with_timeout(input_box);
397
+ })
398
+ .blur(function () {
399
+ input_box.blur();
400
+ //return the object to this can be referenced in the callback functions.
401
+ return hidden_input;
402
+ });
403
+
404
+ // Keep a reference to the selected token and dropdown item
405
+ var selected_token = null;
406
+ var selected_token_index = 0;
407
+ var selected_dropdown_item = null;
408
+
409
+ // The list to store the token items in
410
+ var token_list = $("<ul />")
411
+ .addClass($(input).data("settings").classes.tokenList)
412
+ .click(function (event) {
413
+ var li = $(event.target).closest("li");
414
+ if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
415
+ toggle_select_token(li);
416
+ } else {
417
+ // Deselect selected token
418
+ if(selected_token) {
419
+ deselect_token($(selected_token), POSITION.END);
420
+ }
421
+
422
+ // Focus input box
423
+ focus_with_timeout(input_box);
424
+ }
425
+ })
426
+ .mouseover(function (event) {
427
+ var li = $(event.target).closest("li");
428
+ if(li && selected_token !== this) {
429
+ li.addClass($(input).data("settings").classes.highlightedToken);
430
+ }
431
+ })
432
+ .mouseout(function (event) {
433
+ var li = $(event.target).closest("li");
434
+ if(li && selected_token !== this) {
435
+ li.removeClass($(input).data("settings").classes.highlightedToken);
436
+ }
437
+ })
438
+ .insertBefore(hidden_input);
439
+
440
+ // The token holding the input box
441
+ var input_token = $("<li />")
442
+ .addClass($(input).data("settings").classes.inputToken)
443
+ .appendTo(token_list)
444
+ .append(input_box);
445
+
446
+ // The list to store the dropdown items in
447
+ var dropdown = $("<div>")
448
+ .addClass($(input).data("settings").classes.dropdown)
449
+ .appendTo("body")
450
+ .hide();
451
+
452
+ // Magic element to help us resize the text input
453
+ var input_resizer = $("<tester/>")
454
+ .insertAfter(input_box)
455
+ .css({
456
+ position: "absolute",
457
+ top: -9999,
458
+ left: -9999,
459
+ width: "auto",
460
+ fontSize: input_box.css("fontSize"),
461
+ fontFamily: input_box.css("fontFamily"),
462
+ fontWeight: input_box.css("fontWeight"),
463
+ letterSpacing: input_box.css("letterSpacing"),
464
+ whiteSpace: "nowrap"
465
+ });
466
+
467
+ // Pre-populate list if items exist
468
+ hidden_input.val("");
469
+ var li_data = $(input).data("settings").prePopulate || hidden_input.data("pre");
470
+ if($(input).data("settings").processPrePopulate && $.isFunction($(input).data("settings").onResult)) {
471
+ li_data = $(input).data("settings").onResult.call(hidden_input, li_data);
472
+ }
473
+ if(li_data && li_data.length) {
474
+ $.each(li_data, function (index, value) {
475
+ insert_token(value);
476
+ checkTokenLimit();
477
+ addHiddenField(value);
478
+ input_box.attr("placeholder", null)
479
+ });
480
+ }
481
+
482
+ // Check if widget should initialize as disabled
483
+ if ($(input).data("settings").disabled) {
484
+ toggleDisabled(true);
485
+ }
486
+
487
+ // Initialization is done
488
+ if($.isFunction($(input).data("settings").onReady)) {
489
+ $(input).data("settings").onReady.call();
490
+ }
491
+
492
+ //
493
+ // Public functions
494
+ //
495
+
496
+ this.clear = function() {
497
+ token_list.children("li").each(function() {
498
+ if ($(this).children("input").length === 0) {
499
+ delete_token($(this));
500
+ }
501
+ });
502
+ };
503
+
504
+ this.add = function(item) {
505
+ add_token(item);
506
+ };
507
+
508
+ this.remove = function(item) {
509
+ token_list.children("li").each(function() {
510
+ if ($(this).children("input").length === 0) {
511
+ var currToken = $(this).data("tokeninput");
512
+ var match = true;
513
+ for (var prop in item) {
514
+ if (item[prop] !== currToken[prop]) {
515
+ match = false;
516
+ break;
517
+ }
518
+ }
519
+ if (match) {
520
+ delete_token($(this));
521
+ }
522
+ }
523
+ });
524
+ };
525
+
526
+ this.getTokens = function() {
527
+ return saved_tokens;
528
+ };
529
+
530
+ this.toggleDisabled = function(disable) {
531
+ toggleDisabled(disable);
532
+ };
533
+
534
+ // Resize input to maximum width so the placeholder can be seen
535
+ resize_input();
536
+
537
+ //
538
+ // Private functions
539
+ //
540
+
541
+ function escapeHTML(text) {
542
+ return $(input).data("settings").enableHTML ? text : _escapeHTML(text);
543
+ }
544
+
545
+ // Toggles the widget between enabled and disabled state, or according
546
+ // to the [disable] parameter.
547
+ function toggleDisabled(disable) {
548
+ if (typeof disable === 'boolean') {
549
+ $(input).data("settings").disabled = disable
550
+ } else {
551
+ $(input).data("settings").disabled = !$(input).data("settings").disabled;
552
+ }
553
+ input_box.attr('disabled', $(input).data("settings").disabled);
554
+ token_list.toggleClass($(input).data("settings").classes.disabled, $(input).data("settings").disabled);
555
+ // if there is any token selected we deselect it
556
+ if(selected_token) {
557
+ deselect_token($(selected_token), POSITION.END);
558
+ }
559
+ hidden_input.attr('disabled', $(input).data("settings").disabled);
560
+ }
561
+
562
+ function checkTokenLimit() {
563
+ if($(input).data("settings").tokenLimit !== null && token_count >= $(input).data("settings").tokenLimit) {
564
+ input_box.hide();
565
+ hide_dropdown();
566
+ return;
567
+ }
568
+ }
569
+
570
+ function resize_input() {
571
+ if(input_val === (input_val = input_box.val())) {return;}
572
+
573
+ // Get width left on the current line
574
+ var width_left = token_list.width() - input_box.offset().left - token_list.offset().left;
575
+ // Enter new content into resizer and resize input accordingly
576
+ input_resizer.html(_escapeHTML(input_val));
577
+ // Get maximum width, minimum the size of input and maximum the widget's width
578
+ input_box.width(Math.min(token_list.width(),
579
+ Math.max(width_left, input_resizer.width() + 30)));
580
+ }
581
+
582
+ function is_printable_character(keycode) {
583
+ return ((keycode >= 48 && keycode <= 90) || // 0-1a-z
584
+ (keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * .
585
+ (keycode >= 186 && keycode <= 192) || // ; = , - . / ^
586
+ (keycode >= 219 && keycode <= 222)); // ( \ ) '
587
+ }
588
+
589
+ function add_freetagging_tokens() {
590
+ var value = $.trim(input_box.val());
591
+ var tokens = value.split($(input).data("settings").tokenDelimiter);
592
+ $.each(tokens, function(i, token) {
593
+ if (!token) {
594
+ return;
595
+ }
596
+
597
+ if ($.isFunction($(input).data("settings").onFreeTaggingAdd)) {
598
+ token = $(input).data("settings").onFreeTaggingAdd.call(hidden_input, token);
599
+ }
600
+ var object = {};
601
+ object[$(input).data("settings").tokenValue] = object[$(input).data("settings").propertyToSearch] = token;
602
+ add_token(object);
603
+ });
604
+ }
605
+
606
+ //added these functions so that posted as array instead of string
607
+ function addHiddenField(item){
608
+ $('<input>').attr({
609
+ type: 'hidden',
610
+ id: "item_" + item.id,
611
+ name: escapeHTML($(input).data("settings").fieldName),
612
+ value: item.id
613
+ }).appendTo(escapeHTML($(input).data("settings").parentInput));
614
+ }
615
+ function removeHiddenField(item){
616
+ $('#item_' + item.id).remove();
617
+ }
618
+ // custom functions end here
619
+
620
+
621
+ // Inner function to a token to the list
622
+ function insert_token(item) {
623
+ var $this_token = $($(input).data("settings").tokenFormatter(item));
624
+ var readonly = item.readonly === true ? true : false;
625
+
626
+ if(readonly) $this_token.addClass($(input).data("settings").classes.tokenReadOnly);
627
+
628
+ $this_token.addClass($(input).data("settings").classes.token).insertBefore(input_token);
629
+
630
+ // The 'delete token' button
631
+ if(!readonly) {
632
+ $("<span>" + $(input).data("settings").deleteText + "</span>")
633
+ .addClass($(input).data("settings").classes.tokenDelete)
634
+ .appendTo($this_token)
635
+ .click(function () {
636
+ if (!$(input).data("settings").disabled) {
637
+ delete_token($(this).parent());
638
+ hidden_input.change();
639
+ return false;
640
+ }
641
+ });
642
+ }
643
+
644
+ // Store data on the token
645
+ var token_data = item;
646
+ $.data($this_token.get(0), "tokeninput", item);
647
+
648
+ // Save this token for duplicate checking
649
+ saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
650
+ selected_token_index++;
651
+
652
+ // Update the hidden input
653
+ update_hidden_input(saved_tokens, hidden_input);
654
+
655
+ token_count += 1;
656
+
657
+ // Check the token limit
658
+ if($(input).data("settings").tokenLimit !== null && token_count >= $(input).data("settings").tokenLimit) {
659
+ input_box.hide();
660
+ hide_dropdown();
661
+ }
662
+
663
+ return $this_token;
664
+ }
665
+
666
+ // Add a token to the token list based on user input
667
+ function add_token (item) {
668
+ var callback = $(input).data("settings").onAdd;
669
+
670
+
671
+ // See if the token already exists and select it if we don't want duplicates
672
+ if(token_count > 0 && $(input).data("settings").preventDuplicates) {
673
+ var found_existing_token = null;
674
+ token_list.children().each(function () {
675
+ var existing_token = $(this);
676
+ var existing_data = $.data(existing_token.get(0), "tokeninput");
677
+ if(existing_data && existing_data[settings.tokenValue] === item[settings.tokenValue]) {
678
+ found_existing_token = existing_token;
679
+ return false;
680
+ }
681
+ });
682
+
683
+ if(found_existing_token) {
684
+ select_token(found_existing_token);
685
+ input_token.insertAfter(found_existing_token);
686
+ focus_with_timeout(input_box);
687
+ return;
688
+ }
689
+ }
690
+
691
+ // Squeeze input_box so we force no unnecessary line break
692
+ input_box.width(0);
693
+
694
+ // Insert the new tokens
695
+ if($(input).data("settings").tokenLimit == null || token_count < $(input).data("settings").tokenLimit) {
696
+ insert_token(item);
697
+ // Remove the placeholder so it's not seen after you've added a token
698
+ input_box.attr("placeholder", null)
699
+ checkTokenLimit();
700
+ }
701
+
702
+ // Clear input box
703
+ input_box.val("");
704
+
705
+ // Don't show the help dropdown, they've got the idea
706
+ hide_dropdown();
707
+
708
+
709
+ addHiddenField(item); //custom function call
710
+
711
+ // Execute the onAdd callback if defined
712
+ if($.isFunction(callback)) {
713
+ callback.call(hidden_input,item);
714
+ }
715
+ }
716
+
717
+ // Select a token in the token list
718
+ function select_token (token) {
719
+ if (!$(input).data("settings").disabled) {
720
+ token.addClass($(input).data("settings").classes.selectedToken);
721
+ selected_token = token.get(0);
722
+
723
+ // Hide input box
724
+ input_box.val("");
725
+
726
+ // Hide dropdown if it is visible (eg if we clicked to select token)
727
+ hide_dropdown();
728
+ }
729
+ }
730
+
731
+ // Deselect a token in the token list
732
+ function deselect_token (token, position) {
733
+ token.removeClass($(input).data("settings").classes.selectedToken);
734
+ selected_token = null;
735
+
736
+ if(position === POSITION.BEFORE) {
737
+ input_token.insertBefore(token);
738
+ selected_token_index--;
739
+ } else if(position === POSITION.AFTER) {
740
+ input_token.insertAfter(token);
741
+ selected_token_index++;
742
+ } else {
743
+ input_token.appendTo(token_list);
744
+ selected_token_index = token_count;
745
+ }
746
+
747
+ // Show the input box and give it focus again
748
+ focus_with_timeout(input_box);
749
+ }
750
+
751
+ // Toggle selection of a token in the token list
752
+ function toggle_select_token(token) {
753
+ var previous_selected_token = selected_token;
754
+
755
+ if(selected_token) {
756
+ deselect_token($(selected_token), POSITION.END);
757
+ }
758
+
759
+ if(previous_selected_token === token.get(0)) {
760
+ deselect_token(token, POSITION.END);
761
+ } else {
762
+ select_token(token);
763
+ }
764
+ }
765
+
766
+ // Delete a token from the token list
767
+ function delete_token (token) {
768
+ // Remove the id from the saved list
769
+ var token_data = $.data(token.get(0), "tokeninput");
770
+ var callback = $(input).data("settings").onDelete;
771
+
772
+ var index = token.prevAll().length;
773
+ if(index > selected_token_index) index--;
774
+
775
+ // Delete the token
776
+ token.remove();
777
+ removeHiddenField(token_data); //custom class call
778
+
779
+ selected_token = null;
780
+
781
+ // Show the input box and give it focus again
782
+ focus_with_timeout(input_box);
783
+
784
+ // Remove this token from the saved list
785
+ saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
786
+ if (saved_tokens.length == 0) {
787
+ input_box.attr("placeholder", settings.placeholder)
788
+ }
789
+ if(index < selected_token_index) selected_token_index--;
790
+
791
+ // Update the hidden input
792
+ update_hidden_input(saved_tokens, hidden_input);
793
+
794
+ token_count -= 1;
795
+
796
+ if($(input).data("settings").tokenLimit !== null) {
797
+ input_box
798
+ .show()
799
+ .val("");
800
+ focus_with_timeout(input_box);
801
+ }
802
+
803
+ // Execute the onDelete callback if defined
804
+ if($.isFunction(callback)) {
805
+ callback.call(hidden_input,token_data);
806
+ }
807
+ }
808
+
809
+ // Update the hidden input box value
810
+ function update_hidden_input(saved_tokens, hidden_input) {
811
+ var token_values = $.map(saved_tokens, function (el) {
812
+ if(typeof $(input).data("settings").tokenValue == 'function')
813
+ return $(input).data("settings").tokenValue.call(this, el);
814
+
815
+ return el[$(input).data("settings").tokenValue];
816
+ });
817
+ hidden_input.val(token_values.join($(input).data("settings").tokenDelimiter));
818
+ }
819
+
820
+ // Hide and clear the results dropdown
821
+ function hide_dropdown () {
822
+ dropdown.hide().empty();
823
+ selected_dropdown_item = null;
824
+ }
825
+
826
+ function show_dropdown() {
827
+ dropdown
828
+ .css({
829
+ position: "absolute",
830
+ top: token_list.offset().top + token_list.outerHeight(),
831
+ left: token_list.offset().left,
832
+ width: token_list.width(),
833
+ 'z-index': $(input).data("settings").zindex
834
+ })
835
+ .show();
836
+ }
837
+
838
+ function show_dropdown_searching () {
839
+ if($(input).data("settings").searchingText) {
840
+ dropdown.html("<p>" + escapeHTML($(input).data("settings").searchingText) + "</p>");
841
+ show_dropdown();
842
+ }
843
+ }
844
+
845
+ function show_dropdown_hint () {
846
+ if($(input).data("settings").hintText) {
847
+ dropdown.html("<p>" + escapeHTML($(input).data("settings").hintText) + "</p>");
848
+ show_dropdown();
849
+ }
850
+ }
851
+
852
+ var regexp_special_chars = new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g');
853
+ function regexp_escape(term) {
854
+ return term.replace(regexp_special_chars, '\\$&');
855
+ }
856
+
857
+ // Highlight the query part of the search term
858
+ function highlight_term(value, term) {
859
+ return value.replace(
860
+ new RegExp(
861
+ "(?![^&;]+;)(?!<[^<>]*)(" + regexp_escape(term) + ")(?![^<>]*>)(?![^&;]+;)",
862
+ "gi"
863
+ ), function(match, p1) {
864
+ return "<b>" + escapeHTML(p1) + "</b>";
865
+ }
866
+ );
867
+ }
868
+
869
+ function find_value_and_highlight_term(template, value, term) {
870
+ return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + regexp_escape(value) + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
871
+ }
872
+
873
+ // Populate the results dropdown with some results
874
+ function populate_dropdown (query, results) {
875
+ if(results && results.length) {
876
+ dropdown.empty();
877
+ var dropdown_ul = $("<ul>")
878
+ .appendTo(dropdown)
879
+ .mouseover(function (event) {
880
+ select_dropdown_item($(event.target).closest("li"));
881
+ })
882
+ .mousedown(function (event) {
883
+ add_token($(event.target).closest("li").data("tokeninput"));
884
+ hidden_input.change();
885
+ return false;
886
+ })
887
+ .hide();
888
+
889
+ if ($(input).data("settings").resultsLimit && results.length > $(input).data("settings").resultsLimit) {
890
+ results = results.slice(0, $(input).data("settings").resultsLimit);
891
+ }
892
+
893
+ $.each(results, function(index, value) {
894
+ var this_li = $(input).data("settings").resultsFormatter(value);
895
+
896
+ this_li = find_value_and_highlight_term(this_li ,value[$(input).data("settings").propertyToSearch], query);
897
+
898
+ this_li = $(this_li).appendTo(dropdown_ul);
899
+
900
+ if(index % 2) {
901
+ this_li.addClass($(input).data("settings").classes.dropdownItem);
902
+ } else {
903
+ this_li.addClass($(input).data("settings").classes.dropdownItem2);
904
+ }
905
+
906
+ if(index === 0) {
907
+ select_dropdown_item(this_li);
908
+ }
909
+
910
+ $.data(this_li.get(0), "tokeninput", value);
911
+ });
912
+
913
+ show_dropdown();
914
+
915
+ if($(input).data("settings").animateDropdown) {
916
+ dropdown_ul.slideDown("fast");
917
+ } else {
918
+ dropdown_ul.show();
919
+ }
920
+ } else {
921
+ if($(input).data("settings").noResultsText) {
922
+ dropdown.html("<p>" + escapeHTML($(input).data("settings").noResultsText) + "</p>");
923
+ show_dropdown();
924
+ }
925
+ }
926
+ }
927
+
928
+ // Highlight an item in the results dropdown
929
+ function select_dropdown_item (item) {
930
+ if(item) {
931
+ if(selected_dropdown_item) {
932
+ deselect_dropdown_item($(selected_dropdown_item));
933
+ }
934
+
935
+ item.addClass($(input).data("settings").classes.selectedDropdownItem);
936
+ selected_dropdown_item = item.get(0);
937
+ }
938
+ }
939
+
940
+ // Remove highlighting from an item in the results dropdown
941
+ function deselect_dropdown_item (item) {
942
+ item.removeClass($(input).data("settings").classes.selectedDropdownItem);
943
+ selected_dropdown_item = null;
944
+ }
945
+
946
+ // Do a search and show the "searching" dropdown if the input is longer
947
+ // than $(input).data("settings").minChars
948
+ function do_search() {
949
+ var query = input_box.val();
950
+
951
+ if(query && query.length) {
952
+ if(selected_token) {
953
+ deselect_token($(selected_token), POSITION.AFTER);
954
+ }
955
+
956
+ if(query.length >= $(input).data("settings").minChars) {
957
+ show_dropdown_searching();
958
+ clearTimeout(timeout);
959
+
960
+ timeout = setTimeout(function(){
961
+ run_search(query);
962
+ }, $(input).data("settings").searchDelay);
963
+ } else {
964
+ hide_dropdown();
965
+ }
966
+ }
967
+ }
968
+
969
+ // Do the actual search
970
+ function run_search(query) {
971
+ var cache_key = query + computeURL();
972
+ var cached_results = cache.get(cache_key);
973
+ if(cached_results) {
974
+ if ($.isFunction($(input).data("settings").onCachedResult)) {
975
+ cached_results = $(input).data("settings").onCachedResult.call(hidden_input, cached_results);
976
+ }
977
+ populate_dropdown(query, cached_results);
978
+ } else {
979
+ // Are we doing an ajax search or local data search?
980
+ if($(input).data("settings").url) {
981
+ var url = computeURL();
982
+ // Extract exisiting get params
983
+ var ajax_params = {};
984
+ ajax_params.data = {};
985
+ if(url.indexOf("?") > -1) {
986
+ var parts = url.split("?");
987
+ ajax_params.url = parts[0];
988
+
989
+ var param_array = parts[1].split("&");
990
+ $.each(param_array, function (index, value) {
991
+ var kv = value.split("=");
992
+ ajax_params.data[kv[0]] = kv[1];
993
+ });
994
+ } else {
995
+ ajax_params.url = url;
996
+ }
997
+
998
+ // Prepare the request
999
+ ajax_params.data[$(input).data("settings").queryParam] = query;
1000
+ ajax_params.type = $(input).data("settings").method;
1001
+ ajax_params.dataType = $(input).data("settings").contentType;
1002
+ if($(input).data("settings").crossDomain) {
1003
+ ajax_params.dataType = "jsonp";
1004
+ }
1005
+
1006
+ // Attach the success callback
1007
+ ajax_params.success = function(results) {
1008
+ cache.add(cache_key, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results);
1009
+ if($.isFunction($(input).data("settings").onResult)) {
1010
+ results = $(input).data("settings").onResult.call(hidden_input, results);
1011
+ }
1012
+
1013
+ // only populate the dropdown if the results are associated with the active search query
1014
+ if(input_box.val() === query) {
1015
+ populate_dropdown(query, $(input).data("settings").jsonContainer ? results[$(input).data("settings").jsonContainer] : results);
1016
+ }
1017
+ };
1018
+
1019
+ // Make the request
1020
+ $.ajax(ajax_params);
1021
+ } else if($(input).data("settings").local_data) {
1022
+ // Do the search through local data
1023
+ var results = $.grep($(input).data("settings").local_data, function (row) {
1024
+ return row[$(input).data("settings").propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
1025
+ });
1026
+
1027
+ cache.add(cache_key, results);
1028
+ if($.isFunction($(input).data("settings").onResult)) {
1029
+ results = $(input).data("settings").onResult.call(hidden_input, results);
1030
+ }
1031
+ populate_dropdown(query, results);
1032
+ }
1033
+ }
1034
+ }
1035
+
1036
+ // compute the dynamic URL
1037
+ function computeURL() {
1038
+ var url = $(input).data("settings").url;
1039
+ if(typeof $(input).data("settings").url == 'function') {
1040
+ url = $(input).data("settings").url.call($(input).data("settings"));
1041
+ }
1042
+ return url;
1043
+ }
1044
+
1045
+ // Bring browser focus to the specified object.
1046
+ // Use of setTimeout is to get around an IE bug.
1047
+ // (See, e.g., http://stackoverflow.com/questions/2600186/focus-doesnt-work-in-ie)
1048
+ //
1049
+ // obj: a jQuery object to focus()
1050
+ function focus_with_timeout(obj) {
1051
+ setTimeout(function() { obj.focus(); }, 50);
1052
+ }
1053
+
1054
+ };
1055
+
1056
+ // Really basic cache for the results
1057
+ $.TokenList.Cache = function (options) {
1058
+ var settings = $.extend({
1059
+ max_size: 500
1060
+ }, options);
1061
+
1062
+ var data = {};
1063
+ var size = 0;
1064
+
1065
+ var flush = function () {
1066
+ data = {};
1067
+ size = 0;
1068
+ };
1069
+
1070
+ this.add = function (query, results) {
1071
+ if(size > settings.max_size) {
1072
+ flush();
1073
+ }
1074
+
1075
+ if(!data[query]) {
1076
+ size += 1;
1077
+ }
1078
+
1079
+ data[query] = results;
1080
+ };
1081
+
1082
+ this.get = function (query) {
1083
+ return data[query];
1084
+ };
1085
+ };
1086
+ }(jQuery));
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_admin_tokeninput
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - William Porter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-11 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.1
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.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: pg
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description:
42
+ email:
43
+ - willports@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - vendor/assets/javascripts/jquery.pl.tokeninput.js
49
+ - lib/active_admin_tokeninput/dsl.rb
50
+ - lib/active_admin_tokeninput/engine.rb
51
+ - lib/active_admin_tokeninput/version.rb
52
+ - lib/active_admin_tokeninput.rb
53
+ - lib/tasks/active_admin_tokeninput_tasks.rake
54
+ - MIT-LICENSE
55
+ - Rakefile
56
+ - README.rdoc
57
+ homepage: http://papercloud.com.au
58
+ licenses: []
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.1.11
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Turn fields to a token autocomplete in active_admin
80
+ test_files: []