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