Line # Revision Author
1 480 ahitrov /*
2
3 jQuery Tags Input Plugin 1.3.3
4
5 Copyright (c) 2011 XOXCO, Inc
6
7 Documentation for this plugin lives here:
8 http://xoxco.com/clickable/jquery-tags-input
9
10 Licensed under the MIT license:
11 http://www.opensource.org/licenses/mit-license.php
12
13 ben@xoxco.com
14
15 */
16
17 (function($) {
18
19 var delimiter = new Array();
20 var tags_callbacks = new Array();
21 $.fn.doAutosize = function(o){
22 var minWidth = $(this).data('minwidth'),
23 maxWidth = $(this).data('maxwidth'),
24 val = '',
25 input = $(this),
26 testSubject = $('#'+$(this).data('tester_id'));
27
28 if (val === (val = input.val())) {return;}
29
30 // Enter new content into testSubject
31 var escaped = val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
32 testSubject.html(escaped);
33 // Calculate new width + whether to change
34 var testerWidth = testSubject.width(),
35 newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth,
36 currentWidth = input.width(),
37 isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth)
38 || (newWidth > minWidth && newWidth < maxWidth);
39
40 // Animate width
41 if (isValidWidthChange) {
42 input.width(newWidth);
43 }
44
45
46 };
47 $.fn.resetAutosize = function(options){
48 // alert(JSON.stringify(options));
49 var minWidth = $(this).data('minwidth') || options.minInputWidth || $(this).width(),
50 maxWidth = $(this).data('maxwidth') || options.maxInputWidth || ($(this).closest('.tagsinput').width() - options.inputPadding),
51 val = '',
52 input = $(this),
53 testSubject = $('<tester/>').css({
54 position: 'absolute',
55 top: -9999,
56 left: -9999,
57 width: 'auto',
58 fontSize: input.css('fontSize'),
59 fontFamily: input.css('fontFamily'),
60 fontWeight: input.css('fontWeight'),
61 letterSpacing: input.css('letterSpacing'),
62 whiteSpace: 'nowrap'
63 }),
64 testerId = $(this).attr('id')+'_autosize_tester';
65 if(! $('#'+testerId).length > 0){
66 testSubject.attr('id', testerId);
67 testSubject.appendTo('body');
68 }
69
70 input.data('minwidth', minWidth);
71 input.data('maxwidth', maxWidth);
72 input.data('tester_id', testerId);
73 input.css('width', minWidth);
74 };
75
76 $.fn.addTag = function(value,options) {
77 options = jQuery.extend({focus:false,callback:true},options);
78 this.each(function() {
79 var id = $(this).attr('id');
80
81 var tagslist = $(this).val().split(delimiter[id]);
82 if (tagslist[0] == '') {
83 tagslist = new Array();
84 }
85
86 value = jQuery.trim(value);
87
88 if (options.unique) {
89 var skipTag = $(this).tagExist(value);
90 if(skipTag == true) {
91 //Marks fake input as not_valid to let styling it
92 $('#'+id+'_tag').addClass('not_valid');
93 }
94 } else {
95 var skipTag = false;
96 }
97
98 if (value !='' && skipTag != true) {
99 $('<span>').addClass('tag').append(
100 $('<span>').text(value).append('&nbsp;&nbsp;'),
101 $('<a>', {
102 href : '#',
103 title : 'Removing tag',
104 text : 'x'
105 }).click(function () {
106 return $('#' + id).removeTag(escape(value));
107 })
108 ).insertBefore('#' + id + '_addTag');
109
110 tagslist.push(value);
111
112 $('#'+id+'_tag').val('');
113 if (options.focus) {
114 $('#'+id+'_tag').focus();
115 } else {
116 $('#'+id+'_tag').blur();
117 }
118
119 $.fn.tagsInput.updateTagsField(this,tagslist);
120
121 if (options.callback && tags_callbacks[id] && tags_callbacks[id]['onAddTag']) {
122 var f = tags_callbacks[id]['onAddTag'];
123 f.call(this, value);
124 }
125 if(tags_callbacks[id] && tags_callbacks[id]['onChange'])
126 {
127 var i = tagslist.length;
128 var f = tags_callbacks[id]['onChange'];
129 f.call(this, $(this), tagslist[i-1]);
130 }
131 }
132
133 });
134
135 return false;
136 };
137
138 $.fn.removeTag = function(value) {
139 value = unescape(value);
140 this.each(function() {
141 var id = $(this).attr('id');
142
143 var old = $(this).val().split(delimiter[id]);
144
145 $('#'+id+'_tagsinput .tag').remove();
146 str = '';
147 for (i=0; i< old.length; i++) {
148 if (old[i]!=value) {
149 str = str + delimiter[id] +old[i];
150 }
151 }
152
153 $.fn.tagsInput.importTags(this,str);
154
155 if (tags_callbacks[id] && tags_callbacks[id]['onRemoveTag']) {
156 var f = tags_callbacks[id]['onRemoveTag'];
157 f.call(this, value);
158 }
159 });
160
161 return false;
162 };
163
164 $.fn.tagExist = function(val) {
165 var id = $(this).attr('id');
166 var tagslist = $(this).val().split(delimiter[id]);
167 return (jQuery.inArray(val, tagslist) >= 0); //true when tag exists, false when not
168 };
169
170 // clear all existing tags and import new ones from a string
171 $.fn.importTags = function(str) {
172 id = $(this).attr('id');
173 $('#'+id+'_tagsinput .tag').remove();
174 $.fn.tagsInput.importTags(this,str);
175 }
176
177 $.fn.tagsInput = function(options) {
178 var settings = jQuery.extend({
179 interactive:true,
180 defaultText:'add a tag',
181 minChars:0,
182 width:'300px',
183 height:'100px',
184 autocomplete: {selectFirst: false },
185 'hide':true,
186 'delimiter':',',
187 'unique':true,
188 removeWithBackspace:true,
189 placeholderColor:'#666666',
190 autosize: true,
191 comfortZone: 20,
192 inputPadding: 6*2
193 },options);
194
195 this.each(function() {
196 if (settings.hide) {
197 $(this).hide();
198 }
199 var id = $(this).attr('id');
200 if (!id || delimiter[$(this).attr('id')]) {
201 id = $(this).attr('id', 'tags' + new Date().getTime()).attr('id');
202 }
203
204 var data = jQuery.extend({
205 pid:id,
206 real_input: '#'+id,
207 holder: '#'+id+'_tagsinput',
208 input_wrapper: '#'+id+'_addTag',
209 fake_input: '#'+id+'_tag'
210 },settings);
211
212 delimiter[id] = data.delimiter;
213
214 if (settings.onAddTag || settings.onRemoveTag || settings.onChange) {
215 tags_callbacks[id] = new Array();
216 tags_callbacks[id]['onAddTag'] = settings.onAddTag;
217 tags_callbacks[id]['onRemoveTag'] = settings.onRemoveTag;
218 tags_callbacks[id]['onChange'] = settings.onChange;
219 }
220
221 var markup = '<div id="'+id+'_tagsinput" class="tagsinput"><div id="'+id+'_addTag">';
222
223 if (settings.interactive) {
224 markup = markup + '<input id="'+id+'_tag" value="" data-default="'+settings.defaultText+'" />';
225 }
226
227 markup = markup + '</div><div class="tags_clear"></div></div>';
228
229 $(markup).insertAfter(this);
230
231 $(data.holder).css('width',settings.width);
232 $(data.holder).css('min-height',settings.height);
233 $(data.holder).css('height','100%');
234
235 if ($(data.real_input).val()!='') {
236 $.fn.tagsInput.importTags($(data.real_input),$(data.real_input).val());
237 }
238 if (settings.interactive) {
239 $(data.fake_input).val($(data.fake_input).attr('data-default'));
240 $(data.fake_input).css('color',settings.placeholderColor);
241 $(data.fake_input).resetAutosize(settings);
242
243 $(data.holder).bind('click',data,function(event) {
244 $(event.data.fake_input).focus();
245 });
246
247 $(data.fake_input).bind('focus',data,function(event) {
248 if ($(event.data.fake_input).val()==$(event.data.fake_input).attr('data-default')) {
249 $(event.data.fake_input).val('');
250 }
251 $(event.data.fake_input).css('color','#000000');
252 });
253
254 if (settings.autocomplete_url != undefined) {
255 autocomplete_options = {source: settings.autocomplete_url};
256 for (attrname in settings.autocomplete) {
257 autocomplete_options[attrname] = settings.autocomplete[attrname];
258 }
259
260 if (jQuery.Autocompleter !== undefined) {
261 $(data.fake_input).autocomplete(settings.autocomplete_url, settings.autocomplete);
262 $(data.fake_input).bind('result',data,function(event,data,formatted) {
263 if (data) {
264 $('#'+id).addTag(data[0] + "",{focus:true,unique:(settings.unique)});
265 }
266 });
267 } else if (jQuery.ui.autocomplete !== undefined) {
268 $(data.fake_input).autocomplete(autocomplete_options);
269 $(data.fake_input).bind('autocompleteselect',data,function(event,ui) {
270 $(event.data.real_input).addTag(ui.item.value,{focus:true,unique:(settings.unique)});
271 return false;
272 });
273 }
274
275
276 } else {
277 // if a user tabs out of the field, create a new tag
278 // this is only available if autocomplete is not used.
279 $(data.fake_input).bind('blur',data,function(event) {
280 var d = $(this).attr('data-default');
281 if ($(event.data.fake_input).val()!='' && $(event.data.fake_input).val()!=d) {
282 if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) )
283 $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)});
284 } else {
285 $(event.data.fake_input).val($(event.data.fake_input).attr('data-default'));
286 $(event.data.fake_input).css('color',settings.placeholderColor);
287 }
288 return false;
289 });
290
291 }
292 // if user types a comma, create a new tag
293 $(data.fake_input).bind('keypress',data,function(event) {
294 if (event.which==event.data.delimiter.charCodeAt(0) || event.which==13 ) {
295 event.preventDefault();
296 if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) )
297 $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)});
298 $(event.data.fake_input).resetAutosize(settings);
299 return false;
300 } else if (event.data.autosize) {
301 $(event.data.fake_input).doAutosize(settings);
302
303 }
304 });
305 //Delete last tag on backspace
306 data.removeWithBackspace && $(data.fake_input).bind('keydown', function(event)
307 {
308 if(event.keyCode == 8 && $(this).val() == '')
309 {
310 event.preventDefault();
311 var last_tag = $(this).closest('.tagsinput').find('.tag:last').text();
312 var id = $(this).attr('id').replace(/_tag$/, '');
313 last_tag = last_tag.replace(/[\s]+x$/, '');
314 $('#' + id).removeTag(escape(last_tag));
315 $(this).trigger('focus');
316 }
317 });
318 $(data.fake_input).blur();
319
320 //Removes the not_valid class when user changes the value of the fake input
321 if(data.unique) {
322 $(data.fake_input).keydown(function(event){
323 if(event.keyCode == 8 || String.fromCharCode(event.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/)) {
324 $(this).removeClass('not_valid');
325 }
326 });
327 }
328 } // if settings.interactive
329 });
330
331 return this;
332
333 };
334
335 $.fn.tagsInput.updateTagsField = function(obj,tagslist) {
336 var id = $(obj).attr('id');
337 $(obj).val(tagslist.join(delimiter[id]));
338 };
339
340 $.fn.tagsInput.importTags = function(obj,val) {
341 $(obj).val('');
342 var id = $(obj).attr('id');
343 var tags = val.split(delimiter[id]);
344 for (i=0; i<tags.length; i++) {
345 $(obj).addTag(tags[i],{focus:false,callback:false});
346 }
347 if(tags_callbacks[id] && tags_callbacks[id]['onChange'])
348 {
349 var f = tags_callbacks[id]['onChange'];
350 f.call(obj, obj, tags[i]);
351 }
352 };
353
354 })(jQuery);