├── .gitignore ├── Gruntfile.js ├── README.md ├── bower.json ├── dist ├── jquery.tagsinput.min.css └── jquery.tagsinput.min.js ├── example.html ├── grunt-tasks ├── assets_production.js └── options │ ├── cssmin.js │ └── uglify.js ├── package.json ├── src ├── jquery.tagsinput.css └── jquery.tagsinput.js └── test ├── fake_json_endpoint.html └── fake_plaintext_endpoint.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | lib/ 4 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | function loadConfig(path) { 2 | var glob = require('glob'); 3 | var object = {}; 4 | var key; 5 | 6 | glob.sync('*', {cwd: path}).forEach(function(option) { 7 | key = option.replace(/\.js$/,''); 8 | object[key] = require(path + option); 9 | }); 10 | 11 | return object; 12 | } 13 | 14 | module.exports = function(grunt) { 15 | grunt.loadTasks('grunt-tasks'); 16 | 17 | require('time-grunt')(grunt); 18 | 19 | // Only load tasks when they are needed 20 | require('jit-grunt')(grunt, { 21 | ngtemplates: 'grunt-angular-templates' 22 | }); 23 | 24 | var config = { 25 | pkg: grunt.file.readJSON('package.json'), 26 | env: process.env 27 | }; 28 | 29 | grunt.util._.extend(config, loadConfig('./grunt-tasks/options/')); 30 | grunt.initConfig(config); 31 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery Tags Input Plugin 2 | 3 | Do you use tags to organize content on your site? 4 | This plugin will turn your boring tag list into a 5 | magical input that turns each tag into a style-able 6 | object with its own delete link. The plugin handles 7 | all the data - your form just sees a comma-delimited 8 | list of tags! 9 | 10 | [Get it from Github](https://github.com/xoxco/jQuery-Tags-Input) 11 | 12 | [View Demo](http://xoxco.com/projects/code/tagsinput/) 13 | 14 | [Test it yourself using this jsFiddle Demo](http://jsfiddle.net/7aDak/) 15 | 16 | Created by [XOXCO](http://xoxco.com) 17 | 18 | 19 | ## Instructions 20 | 21 | First, add the Javascript and CSS files to your tag: 22 | 23 | 24 | 25 | 26 | Create a real input in your form that will contain a comma-separated list of 27 | tags. You can put any default or existing tags in the value attribute, and 28 | they'll be handled properly. 29 | 30 | 31 | 32 | Then, simply call the tagsInput function on any field that should be treated as 33 | a list of tags. 34 | 35 | $('#tags').tagsInput(); 36 | 37 | If you want to use jQuery.autocomplete, you can pass in a parameter with the 38 | autocomplete url. 39 | 40 | $('#tags').tagsInput({ 41 | autocomplete_url:'http://myserver.com/api/autocomplete' 42 | }); 43 | 44 | If you're using the bassistance jQuery.autocomplete, which takes extra 45 | parameters, you can also send in options to the autocomplete plugin, as 46 | described here. 47 | 48 | $('#tags').tagsInput({ 49 | autocomplete_url:'http://myserver.com/api/autocomplete', 50 | autocomplete:{selectFirst:true,width:'100px',autoFill:true} 51 | }); 52 | 53 | You can add and remove tags by calling the addTag() and removeTag() functions. 54 | 55 | $('#tags').addTag('foo'); 56 | $('#tags').removeTag('bar'); 57 | 58 | You can import a list of tags using the importTags() function... 59 | 60 | $('#tags').importTags('foo,bar,baz'); 61 | 62 | You can also use importTags() to reset the tag list... 63 | 64 | $('#tags').importTags(''); 65 | 66 | And you can check if a tag exists using tagExist()... 67 | 68 | if ($('#tags').tagExist('foo')) { ... } 69 | 70 | If additional functionality is required when a tag is added or removed, you may 71 | specify callback functions via the onAddTag and onRemoveTag parameters. Both 72 | functions should accept a single tag as the parameter. 73 | 74 | If you do not want to provide a way to add tags, or you would prefer to provide 75 | an alternate interface for adding tags to the box, you may pass an false into 76 | the optional 'interactive' parameter. The tags will still be rendered as per 77 | usual, and the addTag and removeTag functions will operate as expected. 78 | 79 | If you want a function to be called every time a tag is updated/deleted, set it 80 | as the 'onChange' option. 81 | 82 | By default, if the cursor is immediately after a tag, hitting backspace will 83 | delete that tag. If you want to override this, set the 'removeWithBackspace' 84 | option to false. 85 | 86 | ## Options 87 | 88 | $(selector).tagsInput({ 89 | 'autocomplete_url': url_to_autocomplete_api, 90 | 'autocomplete': { option: value, option: value}, 91 | 'height':'100px', 92 | 'width':'300px', 93 | 'interactive':true, 94 | 'defaultText':'add a tag', 95 | 'onAddTag':callback_function, 96 | 'onRemoveTag':callback_function, 97 | 'onChange' : callback_function, 98 | 'delimiter': [',',';'], // Or a string with a single delimiter. Ex: ';' 99 | 'removeWithBackspace' : true, 100 | 'minChars' : 0, 101 | 'maxChars' : 0, // if not provided there is no limit 102 | 'placeholderColor' : '#666666' 103 | }); 104 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery.tagsinput", 3 | "main": ["src/jquery.tagsinput.js", "src/jquery.tagsinput.css"], 4 | "ignore": [ 5 | "**/.*", 6 | "*.html", 7 | "*.md", 8 | "*.json", 9 | "*.min.js", 10 | "test" 11 | ], 12 | "dependencies": { 13 | "jquery": "1.x" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dist/jquery.tagsinput.min.css: -------------------------------------------------------------------------------- 1 | div.tagsinput{border:1px solid #CCC;background:#FFF;padding:5px;width:300px;height:100px;overflow-y:auto}div.tagsinput span.tag{border:1px solid #a5d24a;-moz-border-radius:2px;-webkit-border-radius:2px;display:block;float:left;padding:5px;text-decoration:none;background:#cde69c;color:#638421;margin-right:5px;margin-bottom:5px;font-family:helvetica;font-size:13px}div.tagsinput span.tag a{font-weight:700;color:#82ad2b;text-decoration:none;font-size:11px}div.tagsinput input{width:80px;margin:0 5px 5px 0;font-family:helvetica;font-size:13px;border:1px solid transparent;padding:5px;background:0 0;color:#000;outline:0}div.tagsinput div{display:block;float:left}.tags_clear{clear:both;width:100%;height:0}.not_valid{background:#FBD8DB!important;color:#90111A!important} -------------------------------------------------------------------------------- /dist/jquery.tagsinput.min.js: -------------------------------------------------------------------------------- 1 | !function(a){var b=new Array,c=new Array;a.fn.doAutosize=function(b){var c=a(this).data("minwidth"),d=a(this).data("maxwidth"),e="",f=a(this),g=a("#"+a(this).data("tester_id"));if(e!==(e=f.val())){var h=e.replace(/&/g,"&").replace(/\s/g," ").replace(//g,">");g.html(h);var i=g.width(),j=i+b.comfortZone>=c?i+b.comfortZone:c,k=f.width(),l=k>j&&j>=c||j>c&&d>j;l&&f.width(j)}},a.fn.resetAutosize=function(b){var c=a(this).data("minwidth")||b.minInputWidth||a(this).width(),d=a(this).data("maxwidth")||b.maxInputWidth||a(this).closest(".tagsinput").width()-b.inputPadding,e=a(this),f=a("").css({position:"absolute",top:-9999,left:-9999,width:"auto",fontSize:e.css("fontSize"),fontFamily:e.css("fontFamily"),fontWeight:e.css("fontWeight"),letterSpacing:e.css("letterSpacing"),whiteSpace:"nowrap"}),g=a(this).attr("id")+"_autosize_tester";!a("#"+g).length>0&&(f.attr("id",g),f.appendTo("body")),e.data("minwidth",c),e.data("maxwidth",d),e.data("tester_id",g),e.css("width",c)},a.fn.addTag=function(d,e){return e=jQuery.extend({focus:!1,callback:!0},e),this.each(function(){var f=a(this).attr("id"),g=a(this).val().split(b[f]);if(""==g[0]&&(g=new Array),d=jQuery.trim(d),e.unique){var h=a(this).tagExist(d);1==h&&a("#"+f+"_tag").addClass("not_valid")}else var h=!1;if(""!=d&&1!=h){if(a("").addClass("tag").append(a("").text(d).append("  "),a("",{href:"#",title:"Removing tag",text:"x"}).click(function(){return a("#"+f).removeTag(escape(d))})).insertBefore("#"+f+"_addTag"),g.push(d),a("#"+f+"_tag").val(""),e.focus?a("#"+f+"_tag").focus():a("#"+f+"_tag").blur(),a.fn.tagsInput.updateTagsField(this,g),e.callback&&c[f]&&c[f].onAddTag){var i=c[f].onAddTag;i.call(this,d)}if(c[f]&&c[f].onChange){var j=g.length,i=c[f].onChange;i.call(this,a(this),g[j-1])}}}),!1},a.fn.removeTag=function(d){return d=unescape(d),this.each(function(){var e=a(this).attr("id"),f=a(this).val().split(b[e]);for(a("#"+e+"_tagsinput .tag").remove(),str="",i=0;i=0},a.fn.importTags=function(b){var c=a(this).attr("id");a("#"+c+"_tagsinput .tag").remove(),a.fn.tagsInput.importTags(this,b)},a.fn.tagsInput=function(e){var f=jQuery.extend({interactive:!0,defaultText:"add a tag",minChars:0,width:"300px",height:"100px",autocomplete:{selectFirst:!1},hide:!0,delimiter:",",unique:!0,removeWithBackspace:!0,placeholderColor:"#666666",autosize:!0,comfortZone:20,inputPadding:12},e),g=0;return this.each(function(){if("undefined"==typeof a(this).attr("data-tagsinput-init")){a(this).attr("data-tagsinput-init",!0),f.hide&&a(this).hide();var e=a(this).attr("id");(!e||b[a(this).attr("id")])&&(e=a(this).attr("id","tags"+(new Date).getTime()+g++).attr("id"));var h=jQuery.extend({pid:e,real_input:"#"+e,holder:"#"+e+"_tagsinput",input_wrapper:"#"+e+"_addTag",fake_input:"#"+e+"_tag"},f);b[e]=h.delimiter,(f.onAddTag||f.onRemoveTag||f.onChange)&&(c[e]=new Array,c[e].onAddTag=f.onAddTag,c[e].onRemoveTag=f.onRemoveTag,c[e].onChange=f.onChange);var i='
';if(f.interactive&&(i=i+''),i+='
',a(i).insertAfter(this),a(h.holder).css("width",f.width),a(h.holder).css("min-height",f.height),a(h.holder).css("height",f.height),""!=a(h.real_input).val()&&a.fn.tagsInput.importTags(a(h.real_input),a(h.real_input).val()),f.interactive){if(a(h.fake_input).val(a(h.fake_input).attr("data-default")),a(h.fake_input).css("color",f.placeholderColor),a(h.fake_input).resetAutosize(f),a(h.holder).bind("click",h,function(b){a(b.data.fake_input).focus()}),a(h.fake_input).bind("focus",h,function(b){a(b.data.fake_input).val()==a(b.data.fake_input).attr("data-default")&&a(b.data.fake_input).val(""),a(b.data.fake_input).css("color","#000000")}),void 0!=f.autocomplete_url){autocomplete_options={source:f.autocomplete_url};for(attrname in f.autocomplete)autocomplete_options[attrname]=f.autocomplete[attrname];void 0!==jQuery.Autocompleter?(a(h.fake_input).autocomplete(f.autocomplete_url,f.autocomplete),a(h.fake_input).bind("result",h,function(b,c,d){c&&a("#"+e).addTag(c[0]+"",{focus:!0,unique:f.unique})})):void 0!==jQuery.ui.autocomplete&&(a(h.fake_input).autocomplete(autocomplete_options),a(h.fake_input).bind("autocompleteselect",h,function(b,c){return a(b.data.real_input).addTag(c.item.value,{focus:!0,unique:f.unique}),!1}))}else a(h.fake_input).bind("blur",h,function(b){var c=a(this).attr("data-default");return""!=a(b.data.fake_input).val()&&a(b.data.fake_input).val()!=c?b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length)&&a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:!0,unique:f.unique}):(a(b.data.fake_input).val(a(b.data.fake_input).attr("data-default")),a(b.data.fake_input).css("color",f.placeholderColor)),!1});a(h.fake_input).bind("keypress",h,function(b){return d(b)?(b.preventDefault(),b.data.minChars<=a(b.data.fake_input).val().length&&(!b.data.maxChars||b.data.maxChars>=a(b.data.fake_input).val().length)&&a(b.data.real_input).addTag(a(b.data.fake_input).val(),{focus:!0,unique:f.unique}),a(b.data.fake_input).resetAutosize(f),!1):void(b.data.autosize&&a(b.data.fake_input).doAutosize(f))}),h.removeWithBackspace&&a(h.fake_input).bind("keydown",function(b){if(8==b.keyCode&&""==a(this).val()){b.preventDefault();var c=a(this).closest(".tagsinput").find(".tag:last").text(),d=a(this).attr("id").replace(/_tag$/,"");c=c.replace(/[\s]+x$/,""),a("#"+d).removeTag(escape(c)),a(this).trigger("focus")}}),a(h.fake_input).blur(),h.unique&&a(h.fake_input).keydown(function(b){(8==b.keyCode||String.fromCharCode(b.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/))&&a(this).removeClass("not_valid")})}}}),this},a.fn.tagsInput.updateTagsField=function(c,d){var e=a(c).attr("id");a(c).val(d.join(b[e]))},a.fn.tagsInput.importTags=function(d,e){a(d).val("");var f=a(d).attr("id"),g=e.split(b[f]);for(i=0;i 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 58 |
59 |

60 |

61 | 62 |

63 |

64 | 65 |

66 |

67 | 68 |
69 | -------------------------------------------------------------------------------- /grunt-tasks/assets_production.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.registerTask('assets:production', 3 | [ 4 | 'cssmin:plugin', 5 | 'uglify:plugin' 6 | ]); 7 | }; 8 | -------------------------------------------------------------------------------- /grunt-tasks/options/cssmin.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | options: { 3 | shorthandCompacting: false 4 | }, 5 | plugin: { 6 | files: { 7 | 'dist/jquery.tagsinput.min.css': ['src/jquery.tagsinput.css'] 8 | } 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /grunt-tasks/options/uglify.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | options: { 3 | mangle: true 4 | }, 5 | plugin: { 6 | files: { 7 | 'dist/jquery.tagsinput.min.js': ['src/jquery.tagsinput.js'] 8 | } 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jQuery-Tags-Input", 3 | "version": "1.3.5", 4 | "description": "", 5 | "main": "jquery.tagsinput.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/xoxco/jQuery-Tags-Input.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/xoxco/jQuery-Tags-Input/issues" 20 | }, 21 | "homepage": "https://github.com/xoxco/jQuery-Tags-Input", 22 | "devDependencies": { 23 | "glob": "^4.3.1", 24 | "grunt": "^0.4.5", 25 | "grunt-contrib-cssmin": "^0.10.0", 26 | "grunt-contrib-uglify": "^0.6.0", 27 | "grunt-contrib-watch": "^0.6.1", 28 | "grunt-newer": "^0.8.0", 29 | "jit-grunt": "^0.8.0", 30 | "load-grunt-tasks": "^0.6.0", 31 | "time-grunt": "^0.4.0", 32 | "grunt-cli": "^0.1.13" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/jquery.tagsinput.css: -------------------------------------------------------------------------------- 1 | div.tagsinput { border:1px solid #CCC; background: #FFF; padding:5px; width:300px; height:100px; overflow-y: auto;} 2 | div.tagsinput span.tag { border: 1px solid #a5d24a; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #cde69c; color: #638421; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;} 3 | div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; } 4 | div.tagsinput input { width:80px; margin:0px; font-family: helvetica; font-size: 13px; border:1px solid transparent; padding:5px; background: transparent; color: #000; outline:0px; margin-right:5px; margin-bottom:5px; } 5 | div.tagsinput div { display:block; float: left; } 6 | .tags_clear { clear: both; width: 100%; height: 0px; } 7 | .not_valid {background: #FBD8DB !important; color: #90111A !important;} 8 | -------------------------------------------------------------------------------- /src/jquery.tagsinput.js: -------------------------------------------------------------------------------- 1 | /* 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, '&').replace(/\s/g,' ').replace(//g, '>'); 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 = $('').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 | $('').addClass('tag').append( 100 | $('').text(value).append('  '), 101 | $('
', { 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 | var 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 | var uniqueIdCounter = 0; 196 | 197 | this.each(function() { 198 | // If we have already initialized the field, do not do it again 199 | if (typeof $(this).attr('data-tagsinput-init') !== 'undefined') { 200 | return; 201 | } 202 | 203 | // Mark the field as having been initialized 204 | $(this).attr('data-tagsinput-init', true); 205 | 206 | if (settings.hide) { 207 | $(this).hide(); 208 | } 209 | var id = $(this).attr('id'); 210 | if (!id || delimiter[$(this).attr('id')]) { 211 | id = $(this).attr('id', 'tags' + new Date().getTime() + (uniqueIdCounter++)).attr('id'); 212 | } 213 | 214 | var data = jQuery.extend({ 215 | pid:id, 216 | real_input: '#'+id, 217 | holder: '#'+id+'_tagsinput', 218 | input_wrapper: '#'+id+'_addTag', 219 | fake_input: '#'+id+'_tag' 220 | },settings); 221 | 222 | delimiter[id] = data.delimiter; 223 | 224 | if (settings.onAddTag || settings.onRemoveTag || settings.onChange) { 225 | tags_callbacks[id] = new Array(); 226 | tags_callbacks[id]['onAddTag'] = settings.onAddTag; 227 | tags_callbacks[id]['onRemoveTag'] = settings.onRemoveTag; 228 | tags_callbacks[id]['onChange'] = settings.onChange; 229 | } 230 | 231 | var markup = '
'; 232 | 233 | if (settings.interactive) { 234 | markup = markup + ''; 235 | } 236 | 237 | markup = markup + '
'; 238 | 239 | $(markup).insertAfter(this); 240 | 241 | $(data.holder).css('width',settings.width); 242 | $(data.holder).css('min-height',settings.height); 243 | $(data.holder).css('height',settings.height); 244 | 245 | if ($(data.real_input).val()!='') { 246 | $.fn.tagsInput.importTags($(data.real_input),$(data.real_input).val()); 247 | } 248 | if (settings.interactive) { 249 | $(data.fake_input).val($(data.fake_input).attr('data-default')); 250 | $(data.fake_input).css('color',settings.placeholderColor); 251 | $(data.fake_input).resetAutosize(settings); 252 | 253 | $(data.holder).bind('click',data,function(event) { 254 | $(event.data.fake_input).focus(); 255 | }); 256 | 257 | $(data.fake_input).bind('focus',data,function(event) { 258 | if ($(event.data.fake_input).val()==$(event.data.fake_input).attr('data-default')) { 259 | $(event.data.fake_input).val(''); 260 | } 261 | $(event.data.fake_input).css('color','#000000'); 262 | }); 263 | 264 | if (settings.autocomplete_url != undefined) { 265 | autocomplete_options = {source: settings.autocomplete_url}; 266 | for (attrname in settings.autocomplete) { 267 | autocomplete_options[attrname] = settings.autocomplete[attrname]; 268 | } 269 | 270 | if (jQuery.Autocompleter !== undefined) { 271 | $(data.fake_input).autocomplete(settings.autocomplete_url, settings.autocomplete); 272 | $(data.fake_input).bind('result',data,function(event,data,formatted) { 273 | if (data) { 274 | $('#'+id).addTag(data[0] + "",{focus:true,unique:(settings.unique)}); 275 | } 276 | }); 277 | } else if (jQuery.ui.autocomplete !== undefined) { 278 | $(data.fake_input).autocomplete(autocomplete_options); 279 | $(data.fake_input).bind('autocompleteselect',data,function(event,ui) { 280 | $(event.data.real_input).addTag(ui.item.value,{focus:true,unique:(settings.unique)}); 281 | return false; 282 | }); 283 | } 284 | 285 | 286 | } else { 287 | // if a user tabs out of the field, create a new tag 288 | // this is only available if autocomplete is not used. 289 | $(data.fake_input).bind('blur',data,function(event) { 290 | var d = $(this).attr('data-default'); 291 | if ($(event.data.fake_input).val()!='' && $(event.data.fake_input).val()!=d) { 292 | if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) ) 293 | $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)}); 294 | } else { 295 | $(event.data.fake_input).val($(event.data.fake_input).attr('data-default')); 296 | $(event.data.fake_input).css('color',settings.placeholderColor); 297 | } 298 | return false; 299 | }); 300 | 301 | } 302 | // if user types a default delimiter like comma,semicolon and then create a new tag 303 | $(data.fake_input).bind('keypress',data,function(event) { 304 | if (_checkDelimiter(event)) { 305 | event.preventDefault(); 306 | if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) ) 307 | $(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)}); 308 | $(event.data.fake_input).resetAutosize(settings); 309 | return false; 310 | } else if (event.data.autosize) { 311 | $(event.data.fake_input).doAutosize(settings); 312 | 313 | } 314 | }); 315 | //Delete last tag on backspace 316 | data.removeWithBackspace && $(data.fake_input).bind('keydown', function(event) 317 | { 318 | if(event.keyCode == 8 && $(this).val() == '') 319 | { 320 | event.preventDefault(); 321 | var last_tag = $(this).closest('.tagsinput').find('.tag:last').text(); 322 | var id = $(this).attr('id').replace(/_tag$/, ''); 323 | last_tag = last_tag.replace(/[\s]+x$/, ''); 324 | $('#' + id).removeTag(escape(last_tag)); 325 | $(this).trigger('focus'); 326 | } 327 | }); 328 | $(data.fake_input).blur(); 329 | 330 | //Removes the not_valid class when user changes the value of the fake input 331 | if(data.unique) { 332 | $(data.fake_input).keydown(function(event){ 333 | if(event.keyCode == 8 || String.fromCharCode(event.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/)) { 334 | $(this).removeClass('not_valid'); 335 | } 336 | }); 337 | } 338 | } // if settings.interactive 339 | }); 340 | 341 | return this; 342 | 343 | }; 344 | 345 | $.fn.tagsInput.updateTagsField = function(obj,tagslist) { 346 | var id = $(obj).attr('id'); 347 | $(obj).val(tagslist.join(delimiter[id])); 348 | }; 349 | 350 | $.fn.tagsInput.importTags = function(obj,val) { 351 | $(obj).val(''); 352 | var id = $(obj).attr('id'); 353 | var tags = val.split(delimiter[id]); 354 | for (i=0; i