/** * =================================================== * Tag Manager * http://soliantconsulting.github.com/tagmanager/ * =================================================== * Copyright 2012 Soliant Consulting * http://www.soliantconsulting.com * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================== */ /// class TagManager { options; tagIds; tagStrings; $element; constructor(element, options) { var defaults = { strategy: 'array' , tagFieldName: 'tags[]' , ajaxCreate: null , ajaxDelete: null , initialCap: true , backspaceChars: [ 8 ] , delimiterChars: [ 13, 44, 188 ] , createHandler: function(tagManager, tag, isImport?) { return; } , deleteHandler: function(tagManager, tag, isEmpty?) { return; } , createElementHandler: function(tagManager, tagElement, isImport?) { tagManager.$element.before(tagElement); } , validateHandler: function(tagManager, tag, isImport?) { return tag; } }; this.$element = $(element); this.tagIds = [ ]; this.tagStrings = [ ]; this.options = $.extend({}, defaults, options); $(element).data('tagmanager', this); this.listen(); } /** * If a delimiting key is pressed, add the current value * If backspace then delete latest tag */ keypress(e) { if ($.inArray(e.which, this.options.backspaceChars) != -1) { if (!this.$element.val()) { e.preventDefault(); this.pop(); } } if ($.inArray(e.which, this.options.delimiterChars) != -1) { e.preventDefault(); e.stopPropagation(); // If the bootstrap typeahead is active use that value else use field value if (this.$element.data('typeahead') && this.$element.data('typeahead').shown && this.$element.data('typeahead').$menu.find('.active').length ) { return false; } this.create(this.$element.val()); } } /** * Empty the tag manager */ empty() { var manager = this; $(this.tagIds).each(function(index, value) { manager.delete(value, true); }); } /** * Delete the last tag */ pop() { if (this.tagIds.length > 0) { this.delete(this.tagIds[this.tagIds.length - 1]); } } /** * Delete a tag */ delete(tagId: string, isEmpty?: bool) { var tagString = $('#' + tagId).attr('tag'); if (this.options.deleteHandler) this.options.deleteHandler(this, tagString, isEmpty); if (this.options.strategy = 'ajax' && this.options.ajaxDelete && !isEmpty) { $.ajax({ url: this.options.ajaxDelete , type: 'post' , data: { tag: tagString } , dataType: 'json' }); } var index = $.inArray(tagId, this.tagIds); this.tagStrings.splice(index, 1); this.tagIds.splice(index, 1); $('#' + tagId).remove(); } /** * Import prefilled tags without triggering ajaxCreate */ import(tags) { var manager = this; $.each(tags, function(key, val) { manager.create(val, true); }); } /** * Create a new tag */ create(rawTag: string, isImport?: bool) { var tag = $.trim(rawTag); if (!tag) { this.$element.val(''); return; } if (this.options.initialCap) tag = tag.charAt(0).toUpperCase() + tag.slice(1); tag = this.options.validateHandler(this, tag, isImport); if (!tag) { this.$element.val(''); return; } if (this.options.strategy = 'ajax' && this.options.ajaxCreate && !isImport) { $.ajax({ url: this.options.ajaxCreate , type: 'post' , data: { tag: tag } , dataType: 'json' }); } if (this.options.createHandler) this.options.createHandler(this, tag, isImport); // Build new tag var randomString = function(length) { var result = ''; var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))]; return result; }; var id = 'tag_' + randomString(32); this.tagIds.push(id); this.tagStrings.push(tag); var tagClass = new Tag(this, id, tag); // Run create element handler this.options.createElementHandler(this, tagClass.render(), isImport); this.$element.val(''); this.$element.focus(); } listen() { this.$element.on('keypress', $.proxy(this.keypress, this)); } } class Tag { id: string; tag: string; manager; constructor (manager, id: string, value: string) { this.setManager(manager); this.setId(id); this.setTag(value); } getManager() { return this.manager; } setManager(value) { this.manager = value; return this; } getId() { return this.id; } setId(value: string) { this.id = value; return this; } getTag() { return this.tag; } setTag(value: string) { this.tag = value; return this; } validate() { if (this.getManager().options.strategy == 'array' && !this.getManager().options.tagFieldName) { alert('Array strategy used with no field name'); // throw exception } } render() { this.validate(); var tagHtml = $('') .addClass('tagmanagerTag') .attr('tag', this.getTag()) .attr('id', this.getId()) .data('tagmanager', this.getManager()) .text(this.getTag()); // Handle array strategy if (this.getManager().options.strategy == 'array') { $('') .attr('type', 'hidden') .attr('name', this.getManager().options.tagFieldName) .val(this.getTag()) .appendTo(tagHtml); } // Build remove link var tagRemover = $('') .addClass('tagmanagerRemoveTag') .attr('title', 'Remove') .attr('href', '#') .text('x') .appendTo(tagHtml); var id = this.getId(); var manager = this.getManager(); $(tagRemover).click(function(e) { manager.delete(id); return false; }); return tagHtml; } } /** * Register the function */ $.fn.tagmanager = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('tagmanager') , options = typeof option == 'object' && option if (!data) $this.data('tagmanager', (data = new TagManager(this, options))) if (typeof option == 'string') data[option]() }) }