/*
 * Copyright (c) 2006 Byrne Reese. All rights reserved.
 * http://www.majordojo.com/projects/javascript/tags-widget
 * 
 * This library is free software; you can redistribute it and/or modify it 
 * under the terms of the BSD License.
 *
 * This library is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *
 * @author Byrne Reese <byrne@majordojo.com>
 * @version 1.2
 */

/**
 * @class 
 * @constructor
 * @private
 */
RegExp.escape = (function() {
  var sRE,
	  specials = [
		'/', '.', '*', '+', '?', '|',
		'(', ')', '[', ']', '{', '}', '\\'
	  ];

  sRE = new RegExp(
    '(\\' + specials.join('|\\') + ')', 'g'
  );
  
  return function(text) {
    return text.replace(sRE, '\\$1');
  }
})();

/**
 * @class The TagList class is a helper class of sorts that represents the
 * current list of tags associated with the containing TagWidget. It exposes
 * events that can be subscribed to when tags are added and removed from the
 * list
 * @param {String} baseId The base ID for the widget. The baseId is appended
 * to all dynamically constructed DOM elements so that multiple tag widgets
 * can co-exist on the same page without conflicting or confusing one another.
 * @constructor
 * @requires YAHOO.util.CustomEvent As provided by the Yahoo User Interface Library
 * @requires YAHOO.util.Event As provided by the Yahoo User Interface Library
 */
function TagList( tagUL ) {	
	// The UL used to contain the tags
	this.tagUL = tagUL;

  /**
   * This defines the base URL for all tag links. The text of the tag 
   * the user clicked on will be appended to this string.
   * @type string
   */
//  this.TAG_LINK_FORMAT = 'http://www.somedomain.com/foo?tag=';
  this.TAG_LINK_FORMAT = '/articles/topics/';

  /**
   * @type Array
   * @private
   */
  this.tags = new Array();

  /**
   * This is the event that gets fired off when the user deletes a
   * tag.
   * @type YAHOO.util.CustomEvent
   */
  this.deleteEvent = new YAHOO.util.CustomEvent("deleteEvent", this);

  /**
   * This is the event that gets fired off when the user deletes a
   * tag.
   * @return {Integer} representing the current number of tags
   */
  this.length = function ( ) {
    return this.tags.length;
  }

  /**
   * @private
   */
  this.deleteTag = function ( tag ) {
    var node = YAHOO.util.Dom.get(this.tagUL);
    node.removeChild(tag);
    if (node.childNodes.length == 0)
       node.parentNode.removeChild(node.parentNode.lastChild);
    return true;
  }

  /**
   * @private
   */
  this.onDeleteClick = function ( e ) {
    YAHOO.util.Event.stopEvent(e);
    var target = YAHOO.util.Event.getTarget(e),
		tagElement = target.parentNode.parentNode,
		id = tagElement.id,
		x = id.substring(id.lastIndexOf('-') + 1, id.length),
		idx = -1,
		i;
    this.deleteTag(tagElement);
    for (i = 0; i < this.tags.length; i++) {
      if (x == this.tags[i]) { idx = i; break; }
    }
    this.tags.splice(idx,1);
    this.deleteEvent.fire( x );
  }

  /**
   * @private
   */
  this.addTag = function ( str ) {
    this.tags[this.tags.length] = str;
    this.renderTag(str);
  }

  /**
   * @private
   */
  this.renderTag = function ( str ) {
	var node, li,div,a1;
    node = YAHOO.util.Dom.get(this.tagUL);
    if (!node) return;
    li = document.createElement('li');
    li.id = this.tagUL + '-tag-' + str;
    li.className = 'tag';
    div = document.createElement('div');
    div.className = 'tag-wrapper';
    li.appendChild(div);
    a1 = document.createElement('a');
    a1.innerHTML = str;
    a1.href = this.TAG_LINK_FORMAT + str;
    a1.id = this.tagUL + '-del-tag-'+str;
    a1.className = 'lnk-tag';

    div.appendChild(a1);

    node.appendChild(li);
    YAHOO.util.Event.addListener(a1, "click", this.onDeleteClick, this, true);
  }
}

/////////////////////////////////////////////////////////////////////

/**
 * @class The TagWidget class is the primary class that should be instantiated
 * directly by the user. 
 * @param {String} baseId The base ID for the widget. The baseId is appended
 *    to all dynamically constructed DOM elements so that multiple tag widgets
 *    can co-exist on the same page without conflicting or confusing one another.
 * @constructor
 * @requires YAHOO.util.CustomEvent As provided by the Yahoo User Interface Library
 * @requires YAHOO.util.Event As provided by the Yahoo User Interface Library
 */
function TagWidget( addButton, tagStr, tagUL ) {
	// Form to listen to the submit event on (When tag added)
	this.addButton = addButton;
  
	// The text box the tags are typed into
	this.tagStr = tagStr;
	
	// The UL used to contain the tags
	this.tagUL = tagUL;

  /**
   * Constant field representing the delimiter you wish to use when a user\
   * enters more than one tag at a time.
   * @type String
   */
  this.TAG_DELIMITTER = ',';

  /**
   * The TagList object used to manage the set of tags associated with a TagWidget
   * @type TagList
   */
  this.tags = new TagList(this.tagUL);

  /**
   * Event hook for processing add tag events
   * @type YAHOO.util.CustomEvent
   */
  this.addEvent = new YAHOO.util.CustomEvent("addEvent", this);

  /**
   * Function that adds tags to the widget
   * @param {String} tag The tag you want to add to the widget
   */
  this.addTag = function ( tag ) {
	  var i;
	newTags = this.tagSplit(tag);
	for(i = 0; i < newTags.length; i++) {
		this.tags.addTag(newTags[i]); 
	}
  }

  /**
   * @private
   */
  this.tagSplit = function ( str ) {
	  var delim,delim_scan,tags,tag;
    delim = RegExp.escape(this.TAG_DELIMITTER);
    delim_scan = new RegExp('^((([\'"])(.*?)\\3|.*?)(' + delim + '\\s*|$))', '');
    str = str.replace(/(^\s+|\s+$)/g, '');
    tags = [];
    while (str.length && str.match(delim_scan)) {
        str = str.substr(RegExp.$1.length);
        tag = RegExp.$4 ? RegExp.$4 : RegExp.$2;
        tag = tag.replace(/(^\s+|\s+$)/g, '');
        tag = tag.replace(/\s+/g, ' ');
        if (tag != '') tags.push(tag);
    }
    return tags;
  }

  /**
   * @private
   */
  this.onAddSubmit = function ( e ) {
    /* cancel submit event */
	var node,str,tags,i;
    YAHOO.util.Event.stopEvent(e);
    node = YAHOO.util.Dom.get(this.tagStr);
    str = node.value;
    if (str != "") { 
      //this.tags.addTag(str); 
      tags = this.tagSplit(str);
      for(i = 0; i < tags.length; i++) {
        this.tags.addTag(tags[i]); 
      }
      node.value = '';
      this.addEvent.fire( str );
      return true;
    }
    return false;
  }

  /**
   * Function that renders the widget on the page. The function takes as an argument the 
   * DOM element id (in string form) on which to attach the widget
   * @param {String} id	The id of the element under which the widget should be drawn
   */
  this.render = function () {
    YAHOO.util.Event.addListener(this.addButton, "click", this.onAddSubmit, this, true);    
    //YAHOO.util.Dom.get(this.tagStr).focus();
  }
}
