/*
* jQuery UI Tag-it!
*
* @version v2.0 (06/2011)
*
* Copyright 2011, Levy Carneiro Jr.
* Released under the MIT license.
* http://aehlke.github.com/tag-it/LICENSE
*
* Homepage:
*   http://aehlke.github.com/tag-it/
*
* Authors:
*   Levy Carneiro Jr.
*   Martin Rehfeld
*   Tobias Schmidt
*   Skylar Challand
*   Alex Ehlke
*
* Maintainer:
*   Alex Ehlke - Twitter: @aehlke
*
* Dependencies:
*   jQuery v1.4+
*   jQuery UI v1.8+
*/
(function($) {

	$.widget('ui.tagit', {
		options: {
			itemName		  : 'item',
			fieldName		 : 'tags',
			availableTags	 : [],
			tagSource		 : null,
			removeConfirmation: false,
			caseSensitive	 : true,

			// When enabled, quotes are not neccesary
			// for inputting multi-word tags.
			allowSpaces: false,

			// Whether to animate tag removals or not.
			animate: true,

			// The below options are for using a single field instead of several
			// for our form values.
			//
			// When enabled, will use a single hidden field for the form,
			// rather than one per tag. It will delimit tags in the field
			// with singleFieldDelimiter.
			//
			// The easiest way to use singleField is to just instantiate tag-it
			// on an INPUT element, in which case singleField is automatically
			// set to true, and singleFieldNode is set to that element. This
			// way, you don't need to fiddle with these options.
			singleField: false,

			singleFieldDelimiter: ',',

			// Set this to an input DOM node to use an existing form field.
			// Any text in it will be erased on init. But it will be
			// populated with the text of tags as they are created,
			// delimited by singleFieldDelimiter.
			//
			// If this is not set, we create an input node for it,
			// with the name given in settings.fieldName,
			// ignoring settings.itemName.
			singleFieldNode: null,

			// Optionally set a tabindex attribute on the input that gets
			// created for tag-it.
			tabIndex: null,


			// Event callbacks.
			onTagAdded  : null,
			onTagRemoved: null,
			onTagClicked: null
		},


		_create: function() {
			// for handling static scoping inside callbacks
			var that = this;

			// There are 2 kinds of DOM nodes this widget can be instantiated on:
			//	 1. UL, OL, or some element containing either of these.
			//	 2. INPUT, in which case 'singleField' is overridden to true,
			//		a UL is created and the INPUT is hidden.
			if (this.element.is('input')) {
				this.tagList = $('<ul></ul>').insertAfter(this.element);
				this.options.singleField = true;
				this.options.singleFieldNode = this.element;
				this.element.css('display', 'none');
			} else {
				this.tagList = this.element.find('ul, ol').andSelf().last();
			}

			this._tagInput = $('<input type="text" />').addClass('ui-widget-content');
			if (this.options.tabIndex) {
				this._tagInput.attr('tabindex', this.options.tabIndex);
			}

			this.options.tagSource = this.options.tagSource || function(search, showChoices) {
				var filter = search.term.toLowerCase();
				var choices = $.grep(this.options.availableTags, function(element) {
					// Only match autocomplete options that begin with the search term.
					// (Case insensitive.)
					return (element.toLowerCase().indexOf(filter) === 0);
				});
				showChoices(this._subtractArray(choices, this.assignedTags()));
			};

			// Bind tagSource callback functions to this context.
			if ($.isFunction(this.options.tagSource)) {
				this.options.tagSource = $.proxy(this.options.tagSource, this);
			}

			this.tagList
				.addClass('tagit')
				.addClass('ui-widget ui-widget-content ui-corner-all')
				// Create the input field.
				.append($('<li class="tagit-new"></li>').append(this._tagInput))
				.click(function(e) {
					var target = $(e.target);
					if (target.hasClass('tagit-label')) {
						that._trigger('onTagClicked', e, target.closest('.tagit-choice'));
					} else {
						// Sets the focus() to the input field, if the user
						// clicks anywhere inside the UL. This is needed
						// because the input field needs to be of a small size.
						that._tagInput.focus();
					}
				});

			// Add existing tags from the list, if any.
			this.tagList.children('li').each(function() {
				if (!$(this).hasClass('tagit-new')) {
					that.createTag($(this).html(), $(this).attr('class'), true);
					$(this).remove();
				}
			});

			// Single field support.
			if (this.options.singleField) {
				if (this.options.singleFieldNode) {
					// Add existing tags from the input field.
					var node = $(this.options.singleFieldNode);
					var tags = node.val().split(this.options.singleFieldDelimiter);
					node.val('');
					$.each(tags, function(index, tag) {
						that.createTag(tag, null, false);
					});
				} else {
					// Create our single field input after our list.
					this.options.singleFieldNode = this.tagList.after('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '" />');
				}
			}

			// Events.
			this._tagInput
				.keydown(function(event) {
					// Backspace is not detected within a keypress, so it must use keydown.
					if (event.which == $.ui.keyCode.BACKSPACE && that._tagInput.val() === '') {
						var tag = that._lastTag();
						if (!that.options.removeConfirmation || tag.hasClass('remove')) {
							// When backspace is pressed, the last tag is deleted.
							that.removeTag(tag);
						} else if (that.options.removeConfirmation) {
							tag.addClass('remove ui-state-highlight');
						}
					} else if (that.options.removeConfirmation) {
						that._lastTag().removeClass('remove ui-state-highlight');
					}

					// Comma/Space/Enter are all valid delimiters for new tags,
					// except when there is an open quote or if setting allowSpaces = true.
					// Tab will also create a tag, unless the tag input is empty, in which case it isn't caught.
					if (
						event.which == $.ui.keyCode.COMMA ||
						event.which == $.ui.keyCode.ENTER ||
						(
							event.which == $.ui.keyCode.TAB &&
							that._tagInput.val() !== ''
						) ||
						(
							event.which == $.ui.keyCode.SPACE &&
							that.options.allowSpaces !== true &&
							(
								$.trim(that._tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
								(
									$.trim(that._tagInput.val()).charAt(0) == '"' &&
									$.trim(that._tagInput.val()).charAt($.trim(that._tagInput.val()).length - 1) == '"' &&
									$.trim(that._tagInput.val()).length - 1 !== 0
								)
							)
						)
					) {
						event.preventDefault();
						that.createTag(that._cleanedInput(), null, false);

						// The autocomplete doesn't close automatically when TAB is pressed.
						// So let's ensure that it closes.
						that._tagInput.autocomplete('close');
					}
				}).blur(function(e){
					// Create a tag when the element loses focus (unless it's empty).
					that.createTag(that._cleanedInput(), null, false);
				});


			// Autocomplete.
			this._autocompleteVal = null;
			this._selectAction = function(event, ui) {
				if (that._tagInput.val() == that._autocompleteVal) {
					that.createTag(that._autocompleteVal, null, true);
					// Preventing the tag input to be updated with the chosen value.
					that.tagList.children('.tagit-new').children('input').val('');
					return false;
				}
			//	if (that._tagInput.val() === '') {

			//	}
			}
			if (this.options.availableTags || this.options.tagSource) {
				this._tagInput.autocomplete({
					source: this.options.tagSource,
					close: this._selectAction,
					select: this._selectAction,
					minLength: 2,
					autoFocus: true,
					focus: function(event, ui) {
						that._autocompleteVal = ui.item.value;
						$(this).val(that._unescapeHTML(ui.item.value));
						return false;
					}
				});
			}
		},

		_cleanedInput: function() {
			// Returns the contents of the tag input, cleaned and ready to be passed to createTag
			return $.trim(this._tagInput.val().replace(/^"(.*)"$/, '$1'));
		},

		_lastTag: function() {
			return this.tagList.children('.tagit-choice:last');
		},

		assignedTags: function() {
			// Returns an array of tag string values
			var that = this;
			var tags = [];
			if (this.options.singleField) {
				tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
				if (tags[0] === '') {
					tags = [];
				}
			} else {
				this.tagList.children('.tagit-choice').each(function() {
					tags.push(that.tagLabel(this));
				});
			}
			return tags;
		},

		_updateSingleTagsField: function(tags) {
			// Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
			$(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter));
		},

		_subtractArray: function(a1, a2) {
			var result = [];
			for (var i = 0; i < a1.length; i++) {
				if ($.inArray(a1[i], a2) == -1) {
					result.push(a1[i]);
				}
			}
			return result;
		},

		tagLabel: function(tag) {
			// Returns the tag's string label.
			if (this.options.singleField) {
				return $(tag).children('.tagit-label').text();
			} else {
				return $(tag).children('input').val();
			}
		},

		_isNew: function(value) {
			var that = this;
			var isNew = true;
			this.tagList.children('.tagit-choice').each(function(i) {
				if (that._formatStr(urlEncode(value)) == that._formatStr(that.tagLabel(this))) {
					isNew = false;
					return false;
				}
			});
			return isNew;
		},

		_formatStr: function(str) {
			if (this.options.caseSensitive) {
				return str;
			}
			return $.trim(str.toLowerCase());
		},

		_unescapeHTML: function(str) {
			return $('<div/>').html(str).text();
		},

		createTag: function(value, additionalClass, unescape) {
			// Unescape potentially HTML-escaped value
			if(unescape) value = this._unescapeHTML(value);

			var that = this;
			// Automatically trims the value of leading and trailing whitespace.
			value = $.trim(value);

			if (!this._isNew(value) || value === '') {
				return false;
			}

			var label = $(this.options.onTagClicked ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);

			// Create tag.
			var tag = $('<li></li>')
				.addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
				.addClass(additionalClass)
				.append(label);

			// Button for removing the tag.
			var removeTagIcon = $('<span></span>')
				.addClass('ui-icon ui-icon-close');
			var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \xd7 is an X
				.addClass('tagit-close')
				.append(removeTagIcon)
				.click(function(e) {
					// Removes a tag when the little 'x' is clicked.
					that.removeTag(tag);
				});
			tag.append(removeTag);

			// Unless options.singleField is set, each tag has a hidden input field inline.
			if (this.options.singleField) {
				var tags = this.assignedTags();
				tags.push(value);
				this._updateSingleTagsField(tags);
			} else {
				var escapedValue = urlEncode(value);
				tag.append('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.itemName + '[' + this.options.fieldName + '][]" />');
			}

			this._trigger('onTagAdded', null, tag);

			// Cleaning the input.
			this._tagInput.val('');

			// insert tag
			this._tagInput.parent().before(tag);
		},

		removeTag: function(tag, animate) {
			animate = animate || this.options.animate;

			tag = $(tag);

			this._trigger('onTagRemoved', null, tag);

			if (this.options.singleField) {
				var tags = this.assignedTags();
				var removedTagLabel = this.tagLabel(tag);
				tags = $.grep(tags, function(el){
					return el != removedTagLabel;
				});
				this._updateSingleTagsField(tags);
			}
			// Animate the removal.
			if (animate) {
				tag.fadeOut('fast').hide('blind', {direction: 'horizontal'}, 'fast', function(){
					tag.remove();
				}).dequeue();
			} else {
				tag.remove();
			}
		},

		removeAll: function() {
			// Removes all tags.
			var that = this;
			this.tagList.children('.tagit-choice').each(function(index, tag) {
				that.removeTag(tag, false);
			});
		}

	});

})(jQuery);
