active_admin_tokeninput 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []