/*
  menu.js  -  client side handling of the website navigation menu 

  Copyright (C)         Lumiera.org
    2011,               Hermann Vosseler <Ichthyostega@web.de>

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License as
  published by the Free Software Foundation; either version 2 of
  the License, or (at your option) any later version.

* *****************************************************/


/**
 * trigger to mark the actual page in the navigation.
 * 
 * Basically the navigation/menu HTML is generated by a server sided
 * script 'menugen'. Alongside with the actual menu content, this script
 * is expected to generate JavaScript invocations to build up a lookup table
 * on the client side, allowing to access an MenuNode instance for each entry
 * in the menu, given the path or ID of that entry.
 * 
 * The menu itself gets loaded into an IFrame; this trigger here is supposed
 * to fire after this IFrame is ready; it extracts the path-section from
 * current page's URL and marks the corresponding menu entry as 'current'.
 * Besides changing the CSS to class '.current' (see menu.css), this also
 * causes the branch of the menu tree leading to this page to be expanded
 * and any previously expanded branches to be collapsed.
 */
function markPageInMenu(url)
  {
    if (!url) url = document.location.href
    path = extractPathFromURL(url)
    menuDoc = getMenuIFrameContent()
    if (menuDoc)
      menuDoc.menuTable.select(path)
  }


function extractPathFromURL(url)
  {
    hostPrefixRE = new RegExp('^((?:f|ht)tp(?:s)?\://([^/]+))?([^\?#]+)(.*)', 'im');
    result = url.match(hostPrefixRE)
    if (result)
      return result[3].toString()
    else
      return ""
  }


function getMenuIFrameContent()
  {
    d = document;
    iFrame = d.frames ? d.frames['inavi'] : d.getElementById('inavi');
    if (iFrame)
      return iFrame.contentWindow
    else
      return null
  }



/* ===== Functions supposed to run /within/ the menu IFrame ===== */

function getMenuRoot()
  {
    return document.getElementById('menu')
  }



function addCSSClass (elm, classID)
  {
    clzz = elm.className
    if (-1 == clzz.indexOf(classID))
      {
        clzz += ' '+classID
        elm.className = clzz
      }
  }

function removeCSSClass (elm, classID)
  {
    clzz = elm.className
    if (hasClass (clzz, classID))
      {
        clzz = clzz.replace(classID,'')
        elm.className = clzz
      }
  }

function hasClass (classDef, classID)
  {
    return classDef && -1 < classDef.indexOf (classID)
  }


function blockVisibility (elm, operation)
  {
    if (!elm) return
    if ('expand' == operation)
      showExpanded = true
    else
    if ('collapse' == operation)
      showExpanded = false
    else
      {
        currentState = elm.className
        showExpanded = hasClass(currentState, 'collapsed')
      }
    if (showExpanded)
      removeCSSClass (elm, 'collapsed')
    else
      addCSSClass (elm, 'collapsed')
  }




NOP = function() { }

/**
 * Menu entry control node.
 * A table holding such nodes is built from the generated menu content.
 * Each node is wired directly to the corresponding element in the menu,
 * by virtue of the unique ID tag generated into the menu content.
 * This allows to communicate with the menu, ignoring the actual
 * DOM structure in the menu document
 */
function MenuNode(id, parent, isSubmenu)
  {
    this.elm = document.getElementById(id)
    this.isSubmenu = isSubmenu
    this.parent = parent
    
    this.markActive = function()
      {
        this.expand()
        addCSSClass (this.elm, 'current')
      }
    
    this.unmark = function()
      {
        removeCSSClass (this.elm, 'current')
        this.collapse()
      }
    
    this.expand = function()
      {
        if (this.parent)
            this.parent.expand()
        blockVisibility (this.elm, 'expand')
      }
    
    this.collapse = function()
      {
        if (this.isSubmenu)
          blockVisibility (this.elm, 'collapse')
        if (this.parent)
          this.parent.collapse()
      }
    
    this.toggleExpand = function()
      {
        if (this.isSubmenu)
          blockVisibility (this.elm, 'toggle')
      }
    
    if (this.elm) // ensure initial state
      {
        blockVisibility (this.elm, 'expand')
      }
    else
      {
        this.markActive = NOP
        this.unmark     = NOP
        this.expand     = NOP
        this.collapse   = NOP
        this.toggleExpand=NOP
      }
  }


/*******************************************************
 * Table of all menu entries.
 * Populated by the generated menu content by invoking
 * the #addNode() method for each menu entry, passing an
 * unique ID which is also generated into the corresponding
 * HTML element.
 */
var menuTable = {}

menuTable.index = { }
menuTable.current = [ ]

menuTable.addNode = function(id,url,parent, isSubmenu)
  {
    parentEntry = this.index[parent]
    node = new MenuNode(id,parentEntry, isSubmenu)
    this.index[id] = node
    this.index[url] = node
    this.current.push(node) // new nodes are marked as "current",
  }                        //  causing them to be collapsed on next selection

menuTable.select = function(url)
  {
    element = this.index[url]
    if (!element && /\/$/.test(url)) // maybe an directory index?
      element = this.index[url+'index.html']
    if (element)
      {
        while (0 < this.current.length) {
          old = this.current.pop()
          old.unmark()
        }
        this.current.push (element)
        element.markActive()
      }
  }

menuTable.toggle = function(id)
  {
    node = this.index[id]
    if (node)
      node.toggleExpand()
  }

