/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Eric Hodel's <drbrain@segment7.net> code.
 *
 * The Initial Developer of the Original Code is
 * Eric Hodel.
 * Portions created by the Initial Developer are Copyright (C) 2001
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Christopher Hoess <choess@force.stwing.upenn.edu>
 *      Tim Taylor <tim@tool-man.org>
 *      Stuart Ballard <sballard@netreach.net>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/** 
 * LinkToolbarHandler is a Singleton that displays LINK elements 
 * and nodeLists of LINK elements in the Link Toolbar.  It
 * associates the LINK with a corresponding LinkToolbarItem based 
 * on it's REL attribute and the toolbar item's ID attribute.
 * LinkToolbarHandler is also a Factory and will create 
 * LinkToolbarItems as necessary.
 */
function LinkToolbarHandler()
{
  this.items = new Array();
  this.hasItems = false;
}

LinkToolbarHandler.prototype.handle =
function(element)
{
  // XXX: if you're going to re-enable handling of anchor elements,
  //    you'll want to change this to AnchorElementDecorator
  var linkElement = new LinkElementDecorator(element);

  if (linkElement.isIgnored()) return;

  for (var i = 0; i < linkElement.relValues.length; i++) {
    if (linkElement.relValues.length > 1 && rel == "alternate")
      continue; // skip "alternate" when we have "alternate XXX"

    var linkType = LinkToolbarHandler.getLinkType(linkElement.relValues[i], element);
    if (linkType) {
      if (!this.hasItems) {
        this.hasItems = true;
        linkToolbarUI.activate();
      }
      this.getItemForLinkType(linkType).displayLink(linkElement);
    }
  }
}

LinkToolbarHandler.getLinkType =
function(relAttribute, element)
{
  var isFeed = false;
  switch (relAttribute.toLowerCase()) {
    case "start":
    case "top":
    case "origin":
      return "top";

    case "up":
    case "parent":
      return "up";

    case "begin":
    case "first":
      return "first";

    case "next":
    case "child":
      return "next";

    case "prev":
    case "previous":
      return "prev";

    case "end":
    case "last":
      return "last";

    case "author":
    case "made":
      return "author";

    case "contents":
    case "toc":
      return "toc";

    case "feed":
      isFeed = true;
      // fall through
    case "alternate":
      if (isValidFeed(element, element.nodePrincipal, isFeed)) {
        return "feed";
      }

      if (!isFeed) {
        return "alternate";
      }
      // fall through
    case "prefetch":
      return null;

    default:
      return relAttribute.toLowerCase();
  }
}

LinkToolbarHandler.prototype.getItemForLinkType =
function(linkType) {
  if (!(linkType in this.items && this.items[linkType]))
    this.items[linkType] = LinkToolbarHandler.createItemForLinkType(linkType);

  return this.items[linkType];
}

LinkToolbarHandler.createItemForLinkType =
function(linkType)
{
  if (!document.getElementById("link-" + linkType))
    return new LinkToolbarTransientMenu(linkType);

  // XXX: replace switch with polymorphism
  var element = document.getElementById("link-" + linkType);
  switch (element.getAttribute("type") || element.localName) {
    case "toolbarbutton":
      return new LinkToolbarButton(linkType);

    case "menuitem":
      return new LinkToolbarItem(linkType);

    case "menu":
      return new LinkToolbarMenu(linkType);

    default:
      return new LinkToolbarTransientMenu(linkType);
  }
}

LinkToolbarHandler.prototype.clearAllItems =
function()
{
  // Hide the 'miscellaneous' separator
  document.getElementById("misc-separator").setAttribute("collapsed", "true");

  // Disable the individual items
  for (var linkType in this.items)
    this.items[linkType].clear();

  // Store the fact that the toolbar is empty
  this.hasItems = false;
}

const linkToolbarHandler = new LinkToolbarHandler();
var gLanguageBundle;

function LinkElementDecorator(element) {
  /*
   * XXX: this is an incomplete decorator, because it doesn't implement
   *      the full Element interface.  If you need to use a method 
   *      or member in the Element interface, just add it here and
   *      have it delegate to this.element
   *
   * XXX: would rather add some methods to Element.prototype instead of
   *    using a decorator, but Element.prototype is no longer exposed 
   *      since the XPCDOM landing, see bug 83433
   */

  if (!element) return; // skip the rest on foo.prototype = new ThisClass calls
  
  this.element = element;
  
  this.rel = LinkElementDecorator.convertRevMade(element.rel, element.rev);
  if (this.rel)
    this.relValues = this.rel.split(" ");
  this.rev = element.rev;
  this.title = element.title;
  this.href = element.href;
  this.hreflang = element.hreflang;
  this.media = element.media;
  this.longTitle = null;
}

LinkElementDecorator.prototype.isIgnored =
function()
{
  if (!this.rel) return true;
  for (var i = 0; i < this.relValues.length; i++)
    if (/^stylesheet$|^icon$|^fontdef$|^p3pv|^schema./i.test(this.relValues[i]))
      return true;
  return false;
}

LinkElementDecorator.convertRevMade =
function(rel, rev) 
{
  if (!rel && rev && /\bmade\b/i.test(rev))
    return rev;
  else
    return rel;
}

LinkElementDecorator.prototype.getTooltip =
function() 
{
  return this.getLongTitle() || this.href;
}

LinkElementDecorator.prototype.getLabel =
function() 
{
  return this.getLongTitle() || this.rel;
}

LinkElementDecorator.prototype.getLongTitle =
function() 
{
  if (this.longTitle == null)
    this.longTitle = this.makeLongTitle();

  return this.longTitle;
}

LinkElementDecorator.prototype.makeLongTitle =
function()
{
  var prefix = "";

  // XXX: lookup more meaningful and localized version of media, 
  //   i.e. media="print" becomes "Printable" or some such
  // XXX: use localized version of ":" separator
  if (this.media && !/\ball\b|\bscreen\b/i.test(this.media))
    prefix += this.media + ": ";
  if (this.hreflang) {
    try {
      if (!gLanguageBundle)
        gLanguageBundle = document.getElementById("languageBundle");
      prefix += gLanguageBundle.getString(this.hreflang);
    }
    catch (e) {
      // XXX: handle non-standard language codes per
      //      hixie's spec (see bug 2800)
    }

    prefix += ": ";
  }

  return this.title ? prefix + this.title : prefix;
}

function AnchorElementDecorator(element) {
  this.constructor(element);
}
AnchorElementDecorator.prototype = new LinkElementDecorator;

AnchorElementDecorator.prototype.getLongTitle =
function() 
{
  return this.title ? this.__proto__.getLongTitle.apply(this) 
      : getText(this.element);
}

AnchorElementDecorator.prototype.getText =
function(element)
{
  return condenseWhitespace(getTextRecursive(element));
}

AnchorElementDecorator.prototype.getTextRecursive =
function(node) 
{
  var text = "";
  node.normalize();
  if (node.hasChildNodes()) {
    for (var i = 0; i < node.childNodes.length; i++) {
      if (node.childNodes.item(i).nodeType == Node.TEXT_NODE)
        text += node.childNodes.item(i).nodeValue;
      else if (node.childNodes.item(i).nodeType == Node.ELEMENT_NODE)
        text += getTextRecursive(node.childNodes.item(i));
    }
  }

  return text;
}

AnchorElementDecorator.prototype.condenseWhitespace =
function(text)
{
  return text.replace(/\W*$/, "").replace(/^\W*/, "").replace(/\W+/g, " ");
}
