unknown_admin 0.1.2 → 0.1.3

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