/*
 * 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,
			placeholderText: null,
			// When enabled, quotes are not neccesary
			// for inputting multi-word tags.
			allowSpaces: false,
			// 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);
			}
			if (this.options.placeholderText) {
				this._tagInput.attr('placeholder', this.options.placeholderText);
			}
			this.options.tagSource = this.options.tagSource || function (search, showChoices) {
				var filter = search.term.toLowerCase();
				var choices = $.grep(that.options.availableTags, function (element) {
					// Only match autocomplete options that begin with the search term.
					// (Case insensitive.)
					return (element.toLowerCase().indexOf(filter) === 0);
				});
				showChoices(that._subtractArray(choices, that.assignedTags()));
			};

			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'));
					$(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);
					});
				} 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());

							// The autocomplete doesn't close automatically when TAB is pressed.
							// So let's ensure that it closes.
							that._tagInput.autocomplete('close');
						}
					}).blur(function (e) {
				//If autocomplete is enabled and suggestion was clicked, don't add it
				if (that.options.tagSource && that._tagInput.data('autocomplete-open')) {
					that._cleanedInput();
				} else {
					that.createTag(that._cleanedInput());
				}
			});


			// Autocomplete.
			if (this.options.availableTags || this.options.tagSource) {
				this._tagInput.autocomplete({
					source: this.options.tagSource,
					open: function () {
						that._tagInput.data('autocomplete-open', true)
					},
					close: function () {
						that._tagInput.data('autocomplete-open', false)
					},
					select: function (event, ui) {
						that.createTag(ui.item.value);
						// Preventing the tag input to be updated with the chosen 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(value) == that._formatStr(that.tagLabel(this))) {
					isNew = false;
					return;
				}
			});
			return isNew;
		},
		_formatStr: function (str) {
			if (this.options.caseSensitive) {
				return str;
			}
			return $.trim(str.toLowerCase());
		},
		createTag: function (value, additionalClass) {
			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('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 = label.html();
				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().after(tag);
		},
		removeTag: function (tag, animate) {
			if (typeof animate === 'undefined') {
				animate = true;
			}

			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();
			}
			this._trigger('onTagFinishRemoved', null, tag);
		},
		removeAll: function () {
			// Removes all tags. Takes an optional `animate` argument.
			var that = this;
			this.tagList.children('.tagit-choice').each(function (index, tag) {
				that.removeTag(tag, false);
			});
		}

	});

})(jQuery);
