var domPlugin = new function()
{
  modjs.MakePlugin(this, "dom", 0.1001);
  modjs.Config["dom"].HyperlinksInNewWindow = false;
  modjs.Config["dom"].HyperlinksInNewWindowClassList = new Array();
  modjs.Config["dom"].HyperlinksInNewWindowBaseElement = document;
  modjs.Config["dom"].HyperlinksInNewWindowName = "new";
  modjs.Config["dom"].HyperlinksInNewWindowSpecs = "";
  
  this.Initialise = function()
  {
    if(this.CheckDependancy("modjs", 0.1001))
    {
      // Add the desired extra functionality to the window object
      window.getElementsByClassName = getElementsByClassName;
        
      // If we have been configured to open hyperlinks in a new window, then do it now
      if(modjs.Config["dom"].HyperlinksInNewWindow)
      {
        hyperlinksInNewWindow(modjs.Config["dom"].HyperlinksInNewWindowClassList, modjs.Config["dom"].HyperlinksInNewWindowBaseElement);
      }
      return true;
    }
    else
      return false;
  }
  
  this.LinksInNewWindow = function(className, startingElement, windowName, windowSpecs, historyReplace)
  {
    return hyperlinksInNewWindow(className, startingElement, windowName, windowSpecs, historyReplace);
  }
  
  this.OpenInNewWindow = function(linkElement, windowName, windowSpecs, historyReplace)
  {
    return openInNewWindow(linkElement, windowName, windowSpecs, historyReplace);
  }
  
  function getElementsByClassName(className, startingElement, elementFilter)
  {
    var returnElements = new Array();
    
    // Which classes are we looking for
    var classList = getClassList(className);
    if(classList == false)
    {
      modjs.AppendToStatusLog("dom.getElementsByClassName:  Parameter \"className\" is mandatory (of type string or object).  Type \"" + typeof(className) + "\" was encountered.", 2, "dom");
      return false;
    }
    
    // Which elements are we looking for?
    switch(typeof(elementFilter))
    {
      case "object":
        var searchElements = elementFilter;
        break;
      case "string":
        var searchElements = elementFilter.split(" ");
        break;
      default:
        modjs.AppendToStatusLog("dom.getElementsByClassName:  Parameter \"elementFilter\" was not present or of unknown type.  Assuming all element types are desired.", 3, "dom");
        var searchElements = new Array("*");
    }
    
    var initialElement;
    // What is our starting point?
    if(typeof(startingElement) != "object" || typeof(startingElement.getElementsByTagName) == "undefined")
    {
      modjs.AppendToStatusLog("dom.getElementsByClassName:  Parameter \"startingElement\" was not present or of unknown type.  Searching from 'document' downwards.", 3, "dom");
      initialElement = document;
    }
    else
    {
      initialElement = startingElement;
    }
    
    var currentElement;
    var classString;
    var classesPresent;
    // OK, this gets complicated:
    // First we go through each of the element types to search
    for(var i = 0; i < searchElements.length; i++)
    {
      // Within each element type, we go through each element of that type
      for(var j = 0; j < initialElement.getElementsByTagName(searchElements[i]).length; j++)
      {
        // We grab the element and extract the classes present on the element
        currentElement = initialElement.getElementsByTagName(searchElements[i])[j];
        classString = (currentElement.getAttribute("class") != "" && currentElement.getAttribute("class") != null)? currentElement.getAttribute("class") : currentElement.getAttribute("className");
        if(typeof(classString) != "string")
        {
          continue;
        }
        classesPresent = classString.split(" ");
        // Now we go through each of the classes we are looking for
        for(var k = 0; k < classList.length; k++)
        {
          // If the class name we are looking for appears in both arrays then we have got an element to return
          for(var l = 0; l < classesPresent.length; l++)
          {
            if(classList[k] == classesPresent[l])
            {
              modjs.AppendToStatusLog("dom.getElementsByClassName:  Found a \"" + currentElement.nodeName + "\" element with class \"" + classList[k] + "\".", 5, "dom");
              returnElements.push(currentElement);
            }
          }
        }
      }
    }
    modjs.AppendToStatusLog("dom.getElementsByClassName:  Returning " + returnElements.length + " element(s).", 4, "dom");
    return returnElements;
  }
  
  function hyperlinksInNewWindow(className, startingElement, windowName, windowSpecs, historyReplace)
  {
    var containerElement;
    
    if(typeof(startingElement) == "object" && typeof(startingElement.getElementsByTagName) != "undefined")
    {
      containerElement = startingElement;
    }
    else
    {
      modjs.AppendToStatusLog("dom.hyperLinksInNewWindow:  Parameter \"startingElement\" was not present or was of unexpected type.  Searching from 'document' downwards.", 3, "dom");
      containerElement = document;
    }
    
    // Get the list of classes we are looking for
    var classList = getClassList(className);
    
    // Are we filtering 
    var hyperlinkObjects;
    if(classList != false)
    {
      hyperlinkObjects = window.getElementsByClassName(classList, containerElement, "a");
    }
    else
    {
      hyperlinkObjects = containerElement.getElementsByTagName("a");
    }
    
    var processed = 0;
    for(var i = 0; i < hyperlinkObjects.length; i++)
    {
      if(openInNewWindow(hyperlinkObjects[i], windowName, windowSpecs, historyReplace))
      {
        processed ++;
      }
    }
    
    modjs.AppendToStatusLog("dom.hyperLinksInNewWindow:  Successfully processed " + processed + " hyperlinks.", 4, "dom");
    return processed;
  }
  
  function openInNewWindow(linkElement, windowName, windowSpecs, historyReplace)
  {
    // Check we have the right kind of element
    if(typeof(linkElement) != "object" || !(linkElement.nodeName == "a" || linkElement.nodeName == "A"))
    {
      var nodeName = (typeof(linkElement.nodeName) != "undefined")? linkElement.nodeName : "- Non-HTML element -";
      modjs.AppendToStatusLog("dom.openInNewWindow:  Parameter \"linkElement\", expected HTML object 'a' element.  Encountered " + typeof(linkElement) + ", nodeName: \"" + nodeName + "\".", 2, "dom");
      return false;
    }
    
    // Set some specs up for the new windows
    var windowId = (typeof(windowName) == "string")? windowName : modjs.Config["dom"].HyperlinksInNewWindowName;
    var specs = (typeof(windowSpecs) == "string")? windowSpecs : modjs.Config["dom"].HyperlinksInNewWindowSpecs;
    var replaceHist = (typeof(historyReplace) == "boolean")? historyReplace : false;
    
    linkElement.onclick = function()
    {
      window.open(this.getAttribute("href"), windowId, specs, replaceHist);
      return false;
    }
    
    linkElement.onkeypress = function(e)
    {
      var keypressed;
      if(window.event)
      {
        keypressed = e.keyCode;
      }
      else if(e.which)
      {
        keypressed = e.which;
      }
      if(keypressed == 13 || keypressed == 32)
      {
        window.open(this.getAttribute("href"), windowId, specs, replaceHist);
        return false;
      }
      return true;
    }
    
    return true;
  }
  
  function getClassList(className)
  {
    var classList;
    
    // Which class name(s) are we looking for?
    switch(typeof(className))
    {
      case "object":
          classList = className;
      break;
      case "string":
          classList = className.split(" ");
      break;
      default:
          modjs.AppendToStatusLog("dom.getClassList:  Parameter \"className\" was of an unexpected type or not present.  Using all class names.", 3, "dom");
      classList = false;
    }
    
    return classList;
  }
}