(function($){

  $(document).ready(function(){
    var $wrapper = getWrapper(),
        $tagbox = getTagbox(),
        $parent = getParent(),
        $tags = getTags(),
        resourceId = getResourceId();

    setupInterface();

    // add a class to each tag for ease of use
    $tags.addClass("tagging-tag");

    // add a remove button to existing tags
    $tags.each(function(){
      var tag = normalizeTag($(this).html()),
          re = new RegExp('^'+escapeRegExp(tag)+'$', "m")
          $remove = createRemoveTag(tag, getResourceId());

      // if the tag isn't in the list of removable tags, don't add a button
      if (!$tagbox.val().match(re)) {
        return;
      }

      // add the remove button after the tag link
      $(this).after($remove);
    });

    function setupInterface() {
      var $wrapper = getWrapper(),
          $tagbox = getTagbox(),
          $blacklist = getBlacklist(),
          $savetags = getSavetags(),
          $help = getHelp(),
          $newtag = $("<input/>"),
          $savetag = $("<input/>"),
          resourceId = getResourceId(),
          fieldId = getFieldId(),
          badtags = "",
          blacklistRegExps = [];

      // hide the accessible form elements
      $tagbox.hide();
      $savetags.hide();

      // initialize the new tag input box
      $newtag.attr({
        "type": "text",
        "class": "tagging-addtag"});

      // initialize the new tag submit button
      $savetag.attr({
        "type": "submit",
        "class": "tagging-savetag",
        "value": "Add Tag"});

      // add the new form elements
      $wrapper.append($newtag);
      $wrapper.append($savetag);

      // have the new input box add a tag when ENTER is pressed
      $newtag.keydown(function(e){
        if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == '13')) {
          addTagInner();

          e.preventDefault();
          return false;
        }
      });

      // have the new submit button add tags
      $savetag.click(function(){
        addTagInner();
        return false;
      });

      // move help to the end of the parent
      $wrapper.append($help.remove());

      // reposition the help icon a bit
      $help.addClass("tagging-helpjs");

      // modify the help text to fit the new form elements
      $help.attr(
        "title",
        "Type a tag name in the text box and add it by"+
        " clicking the \"Add Tag\" button. Remove a tag"+
        " by clicking the \"X\" next to the tag you wish"+
        " to remove. Depending on the settings the web"+
        " site administer chose, you may not be able to"+
        " remove some tags.");

      if ($blacklist.val()) {
        badtags = $blacklist.val().split("\n");

        // create blacklist regular expressions once
        for (var i = 0; i < badtags.length; i++) {
          // don't let errors in the blacklist mess up execution
          try {
            blacklistRegExps.push(new RegExp('^'+badtags[i]+'$', "m"));
          } catch(e) {};
        }
      }

      function addTagInner() {
        var tag = normalizeTag($newtag.val());

        // don't add a blank tag
        if (tag.length < 1 || tag.match(/^\s+$/)) {
          $newtag.val("");
          return;
        }

        // don't add a blacklisted tag
        for (var i = 0; i < blacklistRegExps.length; i++) {
          if (tag.match(blacklistRegExps[i])) {
            $newtag.val("");
            return;
          }
        }

        // only add a new tag if it isn't already set for the resource
        if ($wrapper.parent(":has(a[href$='%3D"+escape(tag)+"'])").length < 1 ) {
          addTag(resourceId, $newtag.val());
          createListItem($newtag.val());
        }

        // reset the input box
        $newtag.val("");
      }

      // create a list item to add to the existing list of tags
      function createListItem(tag) {
        tag = normalizeTag(tag);

        var $taglist = $wrapper.parent().children("ul"),
            $li = $("<li/>"),
            $a = $("<a/>"),
            $remove = createRemoveTag(tag, getResourceId());

        // create the tag list, if necessary
        if ($taglist.length < 1) {
          $taglist = $("<ul/>");
          $taglist.addClass("cw-list cw-list-dematte cw-list-unmarked");
          $wrapper.parent().prepend($taglist);
        }

        // initialize the tag link
        $a.attr({
          "href": "index.php?P=AdvancedSearch&Q=Y&F"+
                  escape(getFieldId())+"="+escape("="+tag),
          "title": "Search for all resources also classified as "+tag});
        $a.html(tag);

        // add the links to the list item
        $li.append($a);
        $li.append($remove);

        // hide the list item until it should be displayed
        $li.hide();

        // append the list item to the tag list
        $taglist.append($li);

        // finally show the new tag list item
        $li.slideDown();
      }
    }
  });

  function createRemoveTag(tag, resourceId) {
    tag = normalizeTag(tag);

    var $remove = $("<a/>"),
        sTag = escape(tag),
        sResourceId = escape(resourceId);

    // initialize the remove button
    $remove.addClass("tagging-removetag");
    $remove.html("<img src='plugins/Tags/images/cross.png' alt='x' />");

    // remove the tag when the button is clicked
    $remove.click(function(){
      var $remove = $(this);

      removeTag(resourceId, tag, function(){
        $remove.parent().slideUp("normal", function(){ $(this).remove(); });
      });

      return false;
    });

    return $remove;
  }

  function addTag(resourceId, tag, onSuccess) {
    $.post(
      "index.php?P=P_Tags_AddTag",
      {"ResourceId": resourceId, "Tag": tag},
      onSuccess);
  }

  function removeTag(resourceId, tag, onSuccess) {
    $.post(
      "index.php?P=P_Tags_RemoveTag",
      {"ResourceId": resourceId, "Tag": tag},
      onSuccess);
  }

  function normalizeTag(tag) {
    return tag.replace(/^\s+|\s+$/g, "").toLowerCase();
  }

  function escapeRegExp(str) {
    return str.replace(/[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  }

  function getWrapper() {
    return $(".tagging-wrapper");
  }

  function getTagbox() {
    return $(".tagging-tagbox");
  }

  function getBlacklist() {
    return $("input[id='Blacklist']");
  }

  function getParent() {
    return getWrapper().parent();
  }

  function getTags() {
    return $("ul > li > a", getParent());
  }

  function getSavetags() {
    return $(".tagging-savetags");
  }

  function getHelp() {
    return $(".tagging-help");
  }

  function getResourceId() {
    return getWrapper().attr("resourceid");
  }

  function getFieldId() {
    return getWrapper().attr("fieldid");
  }

}(jQuery));
