typus 3.1.0.rc18 → 3.1.0.rc19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/.gitignore +14 -11
  2. data/.travis.yml +12 -0
  3. data/CHANGELOG +253 -0
  4. data/Gemfile +69 -0
  5. data/Guardfile +11 -0
  6. data/README.md +14 -26
  7. data/Rakefile +24 -100
  8. data/app/assets/javascripts/typus/application.js +8 -2
  9. data/app/assets/javascripts/typus/custom.js +2 -0
  10. data/app/assets/javascripts/typus/jquery.application.js +14 -0
  11. data/app/assets/stylesheets/typus/application.css +18 -10
  12. data/app/assets/stylesheets/typus/content.css +0 -1
  13. data/app/assets/stylesheets/typus/custom.css +11 -0
  14. data/app/assets/stylesheets/typus/errors.css +1 -1
  15. data/app/assets/stylesheets/typus/forms.css +17 -5
  16. data/app/assets/stylesheets/typus/hacks.css +4 -0
  17. data/app/assets/stylesheets/typus/header.css +8 -2
  18. data/app/assets/stylesheets/typus/pagination.css +3 -3
  19. data/app/assets/stylesheets/typus/sidebar.css +4 -0
  20. data/app/assets/stylesheets/typus/tables.css +0 -1
  21. data/app/controllers/admin/account_controller.rb +1 -2
  22. data/app/controllers/admin/base_controller.rb +5 -2
  23. data/app/controllers/admin/dashboard_controller.rb +5 -0
  24. data/app/controllers/admin/resource_controller.rb +1 -2
  25. data/app/controllers/admin/resources_controller.rb +36 -51
  26. data/app/controllers/admin/session_controller.rb +2 -3
  27. data/app/helpers/admin/base_helper.rb +2 -5
  28. data/app/helpers/admin/resources/data_types/belongs_to_helper.rb +35 -27
  29. data/app/helpers/admin/resources/data_types/dragonfly_helper.rb +16 -3
  30. data/app/helpers/admin/resources/data_types/has_and_belongs_to_many_helper.rb +45 -13
  31. data/app/helpers/admin/resources/data_types/has_many_helper.rb +27 -19
  32. data/app/helpers/admin/resources/data_types/has_one_helper.rb +24 -11
  33. data/app/helpers/admin/resources/data_types/paperclip_helper.rb +1 -1
  34. data/app/helpers/admin/resources/data_types/string_helper.rb +1 -1
  35. data/app/helpers/admin/resources/data_types/text_helper.rb +1 -1
  36. data/app/helpers/admin/resources/data_types/transversal_helper.rb +1 -1
  37. data/app/helpers/admin/resources/data_types/tree_helper.rb +1 -1
  38. data/app/helpers/admin/resources/display_helper.rb +10 -9
  39. data/app/helpers/admin/resources/filters_helper.rb +3 -10
  40. data/app/helpers/admin/resources/form_helper.rb +12 -2
  41. data/app/helpers/admin/resources/list_helper.rb +13 -5
  42. data/app/helpers/admin/resources/relationships_helper.rb +0 -21
  43. data/app/helpers/admin/resources_helper.rb +3 -3
  44. data/app/mailers/admin/mailer.rb +9 -4
  45. data/app/views/admin/base/user_guide.html.erb +1 -1
  46. data/app/views/admin/dashboard/{show.html.erb → index.html.erb} +0 -0
  47. data/app/views/admin/dashboard/widgets/_applications.html.erb +32 -10
  48. data/app/views/admin/mailer/reset_password_instructions.html.erb +9 -0
  49. data/app/views/admin/mailer/reset_password_instructions.text.erb +9 -0
  50. data/app/views/admin/resources/edit.html.erb +3 -13
  51. data/app/views/admin/resources/index.html.erb +11 -4
  52. data/app/views/admin/resources/new.html.erb +2 -2
  53. data/app/views/admin/resources/show.html.erb +1 -1
  54. data/app/views/admin/shared/_head.html.erb +2 -2
  55. data/app/views/admin/templates/_belongs_to.html.erb +6 -9
  56. data/app/views/admin/templates/_dragonfly.html.erb +1 -1
  57. data/app/views/admin/templates/_dragonfly_form_preview.html.erb +21 -0
  58. data/app/views/admin/templates/_dragonfly_preview.html.erb +1 -1
  59. data/app/views/admin/templates/_has_and_belongs_to_many.html.erb +15 -0
  60. data/app/views/admin/templates/{_has_n.html.erb → _has_many.html.erb} +0 -0
  61. data/app/views/admin/templates/_password.html.erb +1 -1
  62. data/app/views/admin/templates/_selector.html.erb +1 -11
  63. data/app/views/admin/templates/_string.html.erb +2 -20
  64. data/app/views/admin/templates/_string_with_preview.html.erb +2 -3
  65. data/app/views/admin/templates/_text.html.erb +1 -8
  66. data/app/views/admin/templates/_text_with_ckeditor.html.erb +1 -1
  67. data/app/views/admin/templates/_text_with_ckeditor_and_assets.html.erb +12 -9
  68. data/app/views/helpers/admin/base/_apps.html.erb +12 -4
  69. data/app/views/helpers/admin/base/_login_info.html.erb +1 -1
  70. data/app/views/helpers/admin/resources/_filters.html.erb +2 -1
  71. data/app/views/helpers/admin/resources/_sidebar.html.erb +25 -41
  72. data/app/views/layouts/admin/base.html.erb +1 -1
  73. data/config/locales/typus.ca.yml +1 -1
  74. data/config/locales/typus.de.yml +40 -35
  75. data/config/locales/typus.el.yml +1 -1
  76. data/config/locales/typus.es.yml +1 -1
  77. data/config/locales/typus.fr.yml +1 -1
  78. data/config/locales/typus.hu.yml +1 -1
  79. data/config/locales/typus.it.yml +1 -1
  80. data/config/locales/typus.locale.models.yml.template +17 -0
  81. data/config/locales/typus.locale.yml.template +80 -0
  82. data/config/locales/typus.pt-BR.yml +1 -1
  83. data/config/locales/typus.pt-PT.yml +2 -2
  84. data/config/locales/typus.ru.yml +1 -1
  85. data/config/locales/typus.zh-CN.yml +1 -1
  86. data/config/routes.rb +5 -1
  87. data/lib/generators/templates/config/initializers/typus.rb +9 -0
  88. data/lib/generators/templates/config/typus/README +0 -6
  89. data/lib/support/active_record.rb +2 -10
  90. data/lib/support/string.rb +6 -1
  91. data/lib/typus.rb +49 -10
  92. data/lib/typus/authentication/base.rb +0 -4
  93. data/lib/typus/authentication/none_with_role.rb +15 -0
  94. data/lib/typus/configuration.rb +3 -7
  95. data/lib/typus/controller/ancestry.rb +1 -1
  96. data/lib/typus/controller/bulk.rb +6 -2
  97. data/lib/typus/controller/format.rb +38 -25
  98. data/lib/typus/orm/active_record/class_methods.rb +0 -1
  99. data/lib/typus/orm/active_record/search.rb +6 -2
  100. data/lib/typus/orm/base.rb +4 -3
  101. data/lib/typus/orm/mongoid.rb +4 -0
  102. data/lib/typus/orm/{mongo → mongoid}/class_methods.rb +4 -3
  103. data/lib/typus/resources.rb +9 -0
  104. data/lib/typus/version.rb +1 -1
  105. data/typus.gemspec +13 -13
  106. data/vendor/assets/chosen/chosen-sprite.png +0 -0
  107. data/vendor/assets/chosen/chosen.css +334 -0
  108. data/vendor/assets/chosen/chosen.jquery.min.js +10 -0
  109. data/vendor/assets/fancybox/jquery.fancybox-1.3.4.css +17 -17
  110. metadata +57 -131
  111. data/.gitmodules +0 -6
  112. data/app/assets/javascripts/typus.js +0 -8
  113. data/app/assets/stylesheets/typus.css +0 -20
  114. data/app/assets/stylesheets/typus/actions.css +0 -11
  115. data/app/views/admin/mailer/reset_password_link.text.erb +0 -9
  116. data/app/views/admin/templates/_belongs_to_with_autocomplete.html.erb +0 -26
  117. data/lib/typus/controller/associations.rb +0 -146
  118. data/lib/typus/controller/autocomplete.rb +0 -16
  119. data/vendor/assets/fancybox/jquery.easing-1.3.pack.js +0 -72
  120. data/vendor/assets/fancybox/jquery.fancybox-1.3.4.js +0 -1156
  121. data/vendor/assets/fancybox/jquery.mousewheel-3.0.4.pack.js +0 -14
  122. data/vendor/assets/formalize/css/_formalize.sass +0 -332
  123. data/vendor/assets/formalize/css/demo.css +0 -47
  124. data/vendor/assets/formalize/css/reset.css +0 -202
  125. data/vendor/assets/formalize/css/text.css +0 -81
  126. data/vendor/assets/formalize/js/dojo.formalize.js +0 -166
  127. data/vendor/assets/formalize/js/dojo.formalize.min.js +0 -1
  128. data/vendor/assets/formalize/js/extjs.formalize.js +0 -163
  129. data/vendor/assets/formalize/js/extjs.formalize.min.js +0 -1
  130. data/vendor/assets/formalize/js/jquery.formalize.js +0 -150
  131. data/vendor/assets/formalize/js/mootools.formalize.js +0 -155
  132. data/vendor/assets/formalize/js/mootools.formalize.min.js +0 -1
  133. data/vendor/assets/formalize/js/prototype.formalize.js +0 -163
  134. data/vendor/assets/formalize/js/prototype.formalize.min.js +0 -1
  135. data/vendor/assets/formalize/js/yui.formalize.js +0 -152
  136. data/vendor/assets/formalize/js/yui.formalize.min.js +0 -1
  137. data/vendor/assets/jquery-tokeninput/jquery.tokeninput.js +0 -736
  138. data/vendor/assets/jquery-tokeninput/token-input.css +0 -120
@@ -1 +0,0 @@
1
- YUI.add("formalize",function(a){var b="placeholder"in document.createElement("input"),c="autofocus"in document.createElement("input"),d=parseInt(a.UA.ie,10)===6,e=parseInt(a.UA.ie,10)===7;a.formalize={go:function(){for(var b in a.formalize.init)a.formalize.init[b]()},init:{full_input_size:function(){!!e&&!!a.all("textarea, input.input_full")&&a.all("textarea, input.input_full").each(function(b){var c=a.Node.create('<span class="input_full_wrap"></span>');c.append(b.replace(c))})},ie6_skin_inputs:function(){if(!!d&&!!a.all("input, select, textarea")){var b=/button|submit|reset/,c=/date|datetime|datetime-local|email|month|number|password|range|search|tel|text|time|url|week/;a.all("input").each(function(a){a.getAttribute("type").match(b)?(a.addClass("ie6_button"),a.disabled&&a.addClass("ie6_button_disabled")):a.getAttribute("type").match(c)&&(a.addClass("ie6_input"),a.disabled&&a.addClass("ie6_input_disabled"))}),a.all("textarea, select").each(function(a){a.disabled&&a.addClass("ie6_input_disabled")})}},autofocus:function(){!c&&!!a.one("[autofocus]")&&a.one("[autofocus]").focus()},placeholder:function(){!b&&!!a.one("[placeholder]")&&(a.formalize.misc.add_placeholder(),a.all("[placeholder]").each(function(b){function e(){(!b.get("value")||b.get("value")===c)&&b.set("value",c).addClass("placeholder_text")}var c=b.getAttribute("placeholder"),d=b.ancestor("form");b.on("focus",function(){b.get("value")===c&&b.set("value","").removeClass("placeholder_text")}),b.on("blur",function(){a.formalize.misc.add_placeholder()}),d.on("submit",function(){b.get("value")===c&&b.set("value","").removeClass("placeholder_text")}),d.on("reset",function(){setTimeout(a.formalize.misc.add_placeholder,50)})}))}},misc:{add_placeholder:function(){!b&&!!a.one("[placeholder]")&&a.all("[placeholder]").each(function(a){var b=a.getAttribute("placeholder");(!a.get("value")||a.get("value")===b)&&a.set("value",b).addClass("placeholder_text")})}}}},"1.1",{requires:["yui","dom","event"]})
@@ -1,736 +0,0 @@
1
- /*
2
- * jQuery Plugin: Tokenizing Autocomplete Text Entry
3
- * Version 1.4.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
- */
10
-
11
- (function ($) {
12
- // Default settings
13
- var DEFAULT_SETTINGS = {
14
- hintText: "Type in a search term",
15
- noResultsText: "No results",
16
- searchingText: "Searching...",
17
- deleteText: "&times;",
18
- searchDelay: 300,
19
- minChars: 1,
20
- tokenLimit: null,
21
- jsonContainer: null,
22
- method: "GET",
23
- contentType: "json",
24
- queryParam: "q",
25
- tokenDelimiter: ",",
26
- preventDuplicates: false,
27
- prePopulate: null,
28
- processPrePopulate: false,
29
- animateDropdown: true,
30
- onResult: null,
31
- onAdd: null,
32
- onDelete: null
33
- };
34
-
35
- // Default classes to use when theming
36
- var DEFAULT_CLASSES = {
37
- tokenList: "token-input-list",
38
- token: "token-input-token",
39
- tokenDelete: "token-input-delete-token",
40
- selectedToken: "token-input-selected-token",
41
- highlightedToken: "token-input-highlighted-token",
42
- dropdown: "token-input-dropdown",
43
- dropdownItem: "token-input-dropdown-item",
44
- dropdownItem2: "token-input-dropdown-item2",
45
- selectedDropdownItem: "token-input-selected-dropdown-item",
46
- inputToken: "token-input-input-token"
47
- };
48
-
49
- // Input box position "enum"
50
- var POSITION = {
51
- BEFORE: 0,
52
- AFTER: 1,
53
- END: 2
54
- };
55
-
56
- // Keys "enum"
57
- var KEY = {
58
- BACKSPACE: 8,
59
- TAB: 9,
60
- ENTER: 13,
61
- ESCAPE: 27,
62
- SPACE: 32,
63
- PAGE_UP: 33,
64
- PAGE_DOWN: 34,
65
- END: 35,
66
- HOME: 36,
67
- LEFT: 37,
68
- UP: 38,
69
- RIGHT: 39,
70
- DOWN: 40,
71
- NUMPAD_ENTER: 108,
72
- COMMA: 188
73
- };
74
-
75
-
76
- // Expose the .tokenInput function to jQuery as a plugin
77
- $.fn.tokenInput = function (url_or_data, options) {
78
- var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
79
-
80
- return this.each(function () {
81
- new $.TokenList(this, url_or_data, settings);
82
- });
83
- };
84
-
85
-
86
- // TokenList class for each input
87
- $.TokenList = function (input, url_or_data, settings) {
88
- //
89
- // Initialization
90
- //
91
-
92
- // Configure the data source
93
- if(typeof(url_or_data) === "string") {
94
- // Set the url to query against
95
- settings.url = url_or_data;
96
-
97
- // Make a smart guess about cross-domain if it wasn't explicitly specified
98
- if(settings.crossDomain === undefined) {
99
- if(settings.url.indexOf("://") === -1) {
100
- settings.crossDomain = false;
101
- } else {
102
- settings.crossDomain = (location.href.split(/\/+/g)[1] !== settings.url.split(/\/+/g)[1]);
103
- }
104
- }
105
- } else if(typeof(url_or_data) === "object") {
106
- // Set the local data to search through
107
- settings.local_data = url_or_data;
108
- }
109
-
110
- // Build class names
111
- if(settings.classes) {
112
- // Use custom class names
113
- settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
114
- } else if(settings.theme) {
115
- // Use theme-suffixed default class names
116
- settings.classes = {};
117
- $.each(DEFAULT_CLASSES, function(key, value) {
118
- settings.classes[key] = value + "-" + settings.theme;
119
- });
120
- } else {
121
- settings.classes = DEFAULT_CLASSES;
122
- }
123
-
124
-
125
- // Save the tokens
126
- var saved_tokens = [];
127
-
128
- // Keep track of the number of tokens in the list
129
- var token_count = 0;
130
-
131
- // Basic cache to save on db hits
132
- var cache = new $.TokenList.Cache();
133
-
134
- // Keep track of the timeout, old vals
135
- var timeout;
136
- var input_val;
137
-
138
- // Create a new text input an attach keyup events
139
- var input_box = $("<input type=\"text\" autocomplete=\"off\">")
140
- .css({
141
- outline: "none"
142
- })
143
- .focus(function () {
144
- if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
145
- show_dropdown_hint();
146
- }
147
- })
148
- .blur(function () {
149
- hide_dropdown();
150
- })
151
- .bind("keyup keydown blur update", resize_input)
152
- .keydown(function (event) {
153
- var previous_token;
154
- var next_token;
155
-
156
- switch(event.keyCode) {
157
- case KEY.LEFT:
158
- case KEY.RIGHT:
159
- case KEY.UP:
160
- case KEY.DOWN:
161
- if(!$(this).val()) {
162
- previous_token = input_token.prev();
163
- next_token = input_token.next();
164
-
165
- if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
166
- // Check if there is a previous/next token and it is selected
167
- if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
168
- deselect_token($(selected_token), POSITION.BEFORE);
169
- } else {
170
- deselect_token($(selected_token), POSITION.AFTER);
171
- }
172
- } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
173
- // We are moving left, select the previous token if it exists
174
- select_token($(previous_token.get(0)));
175
- } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
176
- // We are moving right, select the next token if it exists
177
- select_token($(next_token.get(0)));
178
- }
179
- } else {
180
- var dropdown_item = null;
181
-
182
- if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) {
183
- dropdown_item = $(selected_dropdown_item).next();
184
- } else {
185
- dropdown_item = $(selected_dropdown_item).prev();
186
- }
187
-
188
- if(dropdown_item.length) {
189
- select_dropdown_item(dropdown_item);
190
- }
191
- return false;
192
- }
193
- break;
194
-
195
- case KEY.BACKSPACE:
196
- previous_token = input_token.prev();
197
-
198
- if(!$(this).val().length) {
199
- if(selected_token) {
200
- delete_token($(selected_token));
201
- } else if(previous_token.length) {
202
- select_token($(previous_token.get(0)));
203
- }
204
-
205
- return false;
206
- } else if($(this).val().length === 1) {
207
- hide_dropdown();
208
- } else {
209
- // set a timeout just long enough to let this function finish.
210
- setTimeout(function(){do_search();}, 5);
211
- }
212
- break;
213
-
214
- case KEY.TAB:
215
- case KEY.ENTER:
216
- case KEY.NUMPAD_ENTER:
217
- case KEY.COMMA:
218
- if(selected_dropdown_item) {
219
- add_token($(selected_dropdown_item));
220
- return false;
221
- }
222
- break;
223
-
224
- case KEY.ESCAPE:
225
- hide_dropdown();
226
- return true;
227
-
228
- default:
229
- if(String.fromCharCode(event.which)) {
230
- // set a timeout just long enough to let this function finish.
231
- setTimeout(function(){do_search();}, 5);
232
- }
233
- break;
234
- }
235
- });
236
-
237
- // Keep a reference to the original input box
238
- var hidden_input = $(input)
239
- .hide()
240
- .val("")
241
- .focus(function () {
242
- input_box.focus();
243
- })
244
- .blur(function () {
245
- input_box.blur();
246
- });
247
-
248
- // Keep a reference to the selected token and dropdown item
249
- var selected_token = null;
250
- var selected_token_index = 0;
251
- var selected_dropdown_item = null;
252
-
253
- // The list to store the token items in
254
- var token_list = $("<ul />")
255
- .addClass(settings.classes.tokenList)
256
- .click(function (event) {
257
- var li = $(event.target).closest("li");
258
- if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
259
- toggle_select_token(li);
260
- } else {
261
- // Deselect selected token
262
- if(selected_token) {
263
- deselect_token($(selected_token), POSITION.END);
264
- }
265
-
266
- // Focus input box
267
- input_box.focus();
268
- }
269
- })
270
- .mouseover(function (event) {
271
- var li = $(event.target).closest("li");
272
- if(li && selected_token !== this) {
273
- li.addClass(settings.classes.highlightedToken);
274
- }
275
- })
276
- .mouseout(function (event) {
277
- var li = $(event.target).closest("li");
278
- if(li && selected_token !== this) {
279
- li.removeClass(settings.classes.highlightedToken);
280
- }
281
- })
282
- .insertBefore(hidden_input);
283
-
284
- // The token holding the input box
285
- var input_token = $("<li />")
286
- .addClass(settings.classes.inputToken)
287
- .appendTo(token_list)
288
- .append(input_box);
289
-
290
- // The list to store the dropdown items in
291
- var dropdown = $("<div>")
292
- .addClass(settings.classes.dropdown)
293
- .appendTo("body")
294
- .hide();
295
-
296
- // Magic element to help us resize the text input
297
- var input_resizer = $("<tester/>")
298
- .insertAfter(input_box)
299
- .css({
300
- position: "absolute",
301
- top: -9999,
302
- left: -9999,
303
- width: "auto",
304
- fontSize: input_box.css("fontSize"),
305
- fontFamily: input_box.css("fontFamily"),
306
- fontWeight: input_box.css("fontWeight"),
307
- letterSpacing: input_box.css("letterSpacing"),
308
- whiteSpace: "nowrap"
309
- });
310
-
311
- // Pre-populate list if items exist
312
- hidden_input.val("");
313
- var li_data = settings.prePopulate || hidden_input.data("pre");
314
- if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
315
- li_data = settings.onResult.call(hidden_input, li_data);
316
- }
317
- if(li_data && li_data.length) {
318
- $.each(li_data, function (index, value) {
319
- insert_token(value.id, value.name);
320
- });
321
- }
322
-
323
- //
324
- // Private functions
325
- //
326
-
327
- function resize_input() {
328
- if(input_val === (input_val = input_box.val())) {return;}
329
-
330
- // Enter new content into resizer and resize input accordingly
331
- var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
332
- input_resizer.html(escaped);
333
- input_box.width(input_resizer.width() + 30);
334
- }
335
-
336
- function is_printable_character(keycode) {
337
- return ((keycode >= 48 && keycode <= 90) || // 0-1a-z
338
- (keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * .
339
- (keycode >= 186 && keycode <= 192) || // ; = , - . / ^
340
- (keycode >= 219 && keycode <= 222)); // ( \ ) '
341
- }
342
-
343
- // Inner function to a token to the list
344
- function insert_token(id, value) {
345
- var this_token = $("<li><p>"+ value +"</p></li>")
346
- .addClass(settings.classes.token)
347
- .insertBefore(input_token);
348
-
349
- // The 'delete token' button
350
- $("<span>" + settings.deleteText + "</span>")
351
- .addClass(settings.classes.tokenDelete)
352
- .appendTo(this_token)
353
- .click(function () {
354
- delete_token($(this).parent());
355
- return false;
356
- });
357
-
358
- // Store data on the token
359
- var token_data = {"id": id, "name": value};
360
- $.data(this_token.get(0), "tokeninput", token_data);
361
-
362
- // Save this token for duplicate checking
363
- saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
364
- selected_token_index++;
365
-
366
- // Update the hidden input
367
- var token_ids = $.map(saved_tokens, function (el) {
368
- return el.id;
369
- });
370
- hidden_input.val(token_ids.join(settings.tokenDelimiter));
371
-
372
- token_count += 1;
373
-
374
- // Check the token limit
375
- if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
376
- input_box.hide();
377
- hide_dropdown();
378
- }
379
-
380
- return this_token;
381
- }
382
-
383
- // Add a token to the token list based on user input
384
- function add_token (item) {
385
- var li_data = $.data(item.get(0), "tokeninput");
386
- var callback = settings.onAdd;
387
-
388
- // See if the token already exists and select it if we don't want duplicates
389
- if(token_count > 0 && settings.preventDuplicates) {
390
- var found_existing_token = null;
391
- token_list.children().each(function () {
392
- var existing_token = $(this);
393
- var existing_data = $.data(existing_token.get(0), "tokeninput");
394
- if(existing_data && existing_data.id === li_data.id) {
395
- found_existing_token = existing_token;
396
- return false;
397
- }
398
- });
399
-
400
- if(found_existing_token) {
401
- select_token(found_existing_token);
402
- input_token.insertAfter(found_existing_token);
403
- input_box.focus();
404
- return;
405
- }
406
- }
407
-
408
- // Insert the new tokens
409
- insert_token(li_data.id, li_data.name);
410
-
411
- // Check the token limit
412
- if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
413
- input_box.hide();
414
- hide_dropdown();
415
- } else {
416
- input_box.focus();
417
-
418
- // Clear input box
419
- input_box.val("");
420
-
421
- // Don't show the help dropdown, they've got the idea
422
- hide_dropdown();
423
- }
424
-
425
- // Execute the onAdd callback if defined
426
- if($.isFunction(callback)) {
427
- callback.call(hidden_input,li_data);
428
- }
429
- }
430
-
431
- // Select a token in the token list
432
- function select_token (token) {
433
- token.addClass(settings.classes.selectedToken);
434
- selected_token = token.get(0);
435
-
436
- // Hide input box
437
- input_box.val("");
438
-
439
- // Hide dropdown if it is visible (eg if we clicked to select token)
440
- hide_dropdown();
441
- }
442
-
443
- // Deselect a token in the token list
444
- function deselect_token (token, position) {
445
- token.removeClass(settings.classes.selectedToken);
446
- selected_token = null;
447
-
448
- if(position === POSITION.BEFORE) {
449
- input_token.insertBefore(token);
450
- selected_token_index--;
451
- } else if(position === POSITION.AFTER) {
452
- input_token.insertAfter(token);
453
- selected_token_index++;
454
- } else {
455
- input_token.appendTo(token_list);
456
- selected_token_index = token_count;
457
- }
458
-
459
- // Show the input box and give it focus again
460
- input_box.focus();
461
- }
462
-
463
- // Toggle selection of a token in the token list
464
- function toggle_select_token(token) {
465
- var previous_selected_token = selected_token;
466
-
467
- if(selected_token) {
468
- deselect_token($(selected_token), POSITION.END);
469
- }
470
-
471
- if(previous_selected_token === token.get(0)) {
472
- deselect_token(token, POSITION.END);
473
- } else {
474
- select_token(token);
475
- }
476
- }
477
-
478
- // Delete a token from the token list
479
- function delete_token (token) {
480
- // Remove the id from the saved list
481
- var token_data = $.data(token.get(0), "tokeninput");
482
- var callback = settings.onDelete;
483
-
484
- var index = token.prevAll().length;
485
- if(index > selected_token_index) index--;
486
-
487
- // Delete the token
488
- token.remove();
489
- selected_token = null;
490
-
491
- // Show the input box and give it focus again
492
- input_box.focus();
493
-
494
- // Remove this token from the saved list
495
- saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
496
- if(index < selected_token_index) selected_token_index--;
497
-
498
- // Update the hidden input
499
- var token_ids = $.map(saved_tokens, function (el) {
500
- return el.id;
501
- });
502
- hidden_input.val(token_ids.join(settings.tokenDelimiter));
503
-
504
- token_count -= 1;
505
-
506
- if(settings.tokenLimit !== null) {
507
- input_box
508
- .show()
509
- .val("")
510
- .focus();
511
- }
512
-
513
- // Execute the onDelete callback if defined
514
- if($.isFunction(callback)) {
515
- callback.call(hidden_input,token_data);
516
- }
517
- }
518
-
519
- // Hide and clear the results dropdown
520
- function hide_dropdown () {
521
- dropdown.hide().empty();
522
- selected_dropdown_item = null;
523
- }
524
-
525
- function show_dropdown() {
526
- dropdown
527
- .css({
528
- position: "absolute",
529
- top: $(token_list).offset().top + $(token_list).outerHeight(),
530
- left: $(token_list).offset().left,
531
- zindex: 999
532
- })
533
- .show();
534
- }
535
-
536
- function show_dropdown_searching () {
537
- if(settings.searchingText) {
538
- dropdown.html("<p>"+settings.searchingText+"</p>");
539
- show_dropdown();
540
- }
541
- }
542
-
543
- function show_dropdown_hint () {
544
- if(settings.hintText) {
545
- dropdown.html("<p>"+settings.hintText+"</p>");
546
- show_dropdown();
547
- }
548
- }
549
-
550
- // Highlight the query part of the search term
551
- function highlight_term(value, term) {
552
- return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
553
- }
554
-
555
- // Populate the results dropdown with some results
556
- function populate_dropdown (query, results) {
557
- if(results && results.length) {
558
- dropdown.empty();
559
- var dropdown_ul = $("<ul>")
560
- .appendTo(dropdown)
561
- .mouseover(function (event) {
562
- select_dropdown_item($(event.target).closest("li"));
563
- })
564
- .mousedown(function (event) {
565
- add_token($(event.target).closest("li"));
566
- return false;
567
- })
568
- .hide();
569
-
570
- $.each(results, function(index, value) {
571
- var this_li = $("<li>" + highlight_term(value.name, query) + "</li>")
572
- .appendTo(dropdown_ul);
573
-
574
- if(index % 2) {
575
- this_li.addClass(settings.classes.dropdownItem);
576
- } else {
577
- this_li.addClass(settings.classes.dropdownItem2);
578
- }
579
-
580
- if(index === 0) {
581
- select_dropdown_item(this_li);
582
- }
583
-
584
- $.data(this_li.get(0), "tokeninput", {"id": value.id, "name": value.name});
585
- });
586
-
587
- show_dropdown();
588
-
589
- if(settings.animateDropdown) {
590
- dropdown_ul.slideDown("fast");
591
- } else {
592
- dropdown_ul.show();
593
- }
594
- } else {
595
- if(settings.noResultsText) {
596
- dropdown.html("<p>"+settings.noResultsText+"</p>");
597
- show_dropdown();
598
- }
599
- }
600
- }
601
-
602
- // Highlight an item in the results dropdown
603
- function select_dropdown_item (item) {
604
- if(item) {
605
- if(selected_dropdown_item) {
606
- deselect_dropdown_item($(selected_dropdown_item));
607
- }
608
-
609
- item.addClass(settings.classes.selectedDropdownItem);
610
- selected_dropdown_item = item.get(0);
611
- }
612
- }
613
-
614
- // Remove highlighting from an item in the results dropdown
615
- function deselect_dropdown_item (item) {
616
- item.removeClass(settings.classes.selectedDropdownItem);
617
- selected_dropdown_item = null;
618
- }
619
-
620
- // Do a search and show the "searching" dropdown if the input is longer
621
- // than settings.minChars
622
- function do_search() {
623
- var query = input_box.val().toLowerCase();
624
-
625
- if(query && query.length) {
626
- if(selected_token) {
627
- deselect_token($(selected_token), POSITION.AFTER);
628
- }
629
-
630
- if(query.length >= settings.minChars) {
631
- show_dropdown_searching();
632
- clearTimeout(timeout);
633
-
634
- timeout = setTimeout(function(){
635
- run_search(query);
636
- }, settings.searchDelay);
637
- } else {
638
- hide_dropdown();
639
- }
640
- }
641
- }
642
-
643
- // Do the actual search
644
- function run_search(query) {
645
- var cached_results = cache.get(query);
646
- if(cached_results) {
647
- populate_dropdown(query, cached_results);
648
- } else {
649
- // Are we doing an ajax search or local data search?
650
- if(settings.url) {
651
- // Extract exisiting get params
652
- var ajax_params = {};
653
- ajax_params.data = {};
654
- if(settings.url.indexOf("?") > -1) {
655
- var parts = settings.url.split("?");
656
- ajax_params.url = parts[0];
657
-
658
- var param_array = parts[1].split("&");
659
- $.each(param_array, function (index, value) {
660
- var kv = value.split("=");
661
- ajax_params.data[kv[0]] = kv[1];
662
- });
663
- } else {
664
- ajax_params.url = settings.url;
665
- }
666
-
667
- // Prepare the request
668
- ajax_params.data[settings.queryParam] = query;
669
- ajax_params.type = settings.method;
670
- ajax_params.dataType = settings.contentType;
671
- if(settings.crossDomain) {
672
- ajax_params.dataType = "jsonp";
673
- }
674
-
675
- // Attach the success callback
676
- ajax_params.success = function(results) {
677
- if($.isFunction(settings.onResult)) {
678
- results = settings.onResult.call(hidden_input, results);
679
- }
680
- cache.add(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
681
-
682
- // only populate the dropdown if the results are associated with the active search query
683
- if(input_box.val().toLowerCase() === query) {
684
- populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
685
- }
686
- };
687
-
688
- // Make the request
689
- $.ajax(ajax_params);
690
- } else if(settings.local_data) {
691
- // Do the search through local data
692
- var results = $.grep(settings.local_data, function (row) {
693
- return row.name.toLowerCase().indexOf(query.toLowerCase()) > -1;
694
- });
695
-
696
- if($.isFunction(settings.onResult)) {
697
- results = settings.onResult.call(hidden_input, results);
698
- }
699
- cache.add(query, results);
700
- populate_dropdown(query, results);
701
- }
702
- }
703
- }
704
- };
705
-
706
- // Really basic cache for the results
707
- $.TokenList.Cache = function (options) {
708
- var settings = $.extend({
709
- max_size: 500
710
- }, options);
711
-
712
- var data = {};
713
- var size = 0;
714
-
715
- var flush = function () {
716
- data = {};
717
- size = 0;
718
- };
719
-
720
- this.add = function (query, results) {
721
- if(size > settings.max_size) {
722
- flush();
723
- }
724
-
725
- if(!data[query]) {
726
- size += 1;
727
- }
728
-
729
- data[query] = results;
730
- };
731
-
732
- this.get = function (query) {
733
- return data[query];
734
- };
735
- };
736
- }(jQuery));