unknown_admin 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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