/* 
  Algo: a dekstop environment look-a-like in your web browser
  Copyright (C) 2009  Niels Serup
  
	This file is part of Algo.

	Algo 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 3 of the License, or
	(at your option) any later version.

	Algo 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.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Algo.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
  The navbar class creates a navigation bar. Normally Algo features two
  navigation bars - one at the top of the page and one at the bottom of the
  page. Several obejcts can be added to a navigation bar after its creation,
  including dropdowns, appboxes (the boxes with text that you can click on,
  usually found in the bottom navigation bar) and a clock. The style of the
  navigation bar itself and the objects on the navigation bar is found in an
  external css themes file.
*/
function navbar(pos) {
  // navnum = the order in which it was created (0, 1, etc.)
  this.navnum = navi.length
  // If pos is undefined, set the navigation bar to be at the top
  if (pos == undefined || pos == -1) var pos = [[0, ''], -1, -1, [0, '']]
  // Save pos
  this.pos = pos
  
  // Create the actual navigation bar element
  this.menu = document.createElement('div')
  
  /* Makes it identifiable by a css file.
     By adding " navi" followed by the number of the navbar,
     each navbar can be independently changed by a css file */
  this.menu.className = 'navbar navi' + this.navnum
  
  // Update the position of the navbar
  this.update()
  
  // Append the navbar to the wrapper
  wrapper.appendChild(this.menu)
}

navbar.prototype.update = function() {
  // Loop through the 4 global directions (top to right)
  for (var i = 0; i < global_directions_length; i++) {
    /* If the pos is not no-existant, style the navbar's menu element with the
       amount ([i][0]) followed by a unit ([i][1]) */
    if (this.pos[i] != -1)
      this.menu.style[global_directions[i]] = this.pos[i][0] + this.pos[i][1]
  }
}

navbar.prototype.support = function(name) {
  /* When supporting objects, they will be linked to the supporting navbar
     to make them accesible later on via the global "posis" list */
  posis[name] = this.navnum
  
  // Special cases
  if (name == 'dropdowns')
    this.drops = []
}

navbar.prototype.unsupport = function(name) {
  // Cease support of a certain object
  if (posis[name] == undefined) return
  delete posis[name]
  
  // Special cases
  if (name == 'dropdowns')
    delete this.dropdowns
  else if (name == 'appboxes')
    delete this.appboxes
}

// DROPDOWN
navbar.prototype.drop = function(name, list) {
  /* Adds a dropdown menu. Dropdowns will appear in the order they are created.
     "name" is the name of the dropdown menu, while "list" holds all the
     entries in an array-like format.
  */
  var listlen = list.length
  var wrap = this.append_level(list, name)
  this.dropdowns.appendChild(wrap)
  
  // Adds a new dropdown object to the "drops"
  this.drops[this.drops.length] = [wrap, name, list]
}

navbar.prototype.append_level = function(list, first) {
  /* This function creates the elements of a dropdown menu,
     based on a list of strings and (eventually) more lists.
     This function will call itself if a list at level one
     has another list. That way, there are no factors that
     limit the number of levels in a dropdown menu. "first"
     is set to the name of the list when called from the
     original drop prototype function, while it is set to
     false when called from this function itself. */
  var listlen = list.length
  var i, wrap, dtop, type, elem, elema, elemb, spl, title
  
  // Creates the temporary element that will hold everything
  wrap = document.createElement('div')
  if (first) {
    wrap.className = 'dropdown'
    // dtop = the header element
    dtop = document.createElement('div')
    // Adds two classes, one general and one special (" drop" + drops.length)
    dtop.className = 'top drop' + navi[posis['dropdowns']].drops.length
    dtop.innerHTML = first
    wrap.appendChild(dtop)
  }
  else {
    /* If it's not the first time the function is called, it will create a
       div with entries instead of a div with a header. */
    wrap.className = 'entries'
    elem = document.createElement('div')
    elem.className = 'side'
    wrap.appendChild(elem)
  }
  
  /* This is the main loop. It looks at the list entries and evaluates them
     based on what they are. If a string is encountered, it is written to the
     dropdown menu. If an object (a list in this case) is encountered, this
     function is recalled with the sublist as the main list. */
  for (i = 0; i < listlen; i++) {
    type = typeof(list[i])
    if (type == 'string') {
      spl = [list[i], 'true']
      if (list[i].indexOf('#') != -1)
        spl = list[i].split('#')
      elem = document.createElement('div')
      elem.className = 'entry'
      elem.rel = spl[1]
      elem.onmouseup = function() {
        navi[posis['dropdowns']].hide_drops()
      }
      elem.innerHTML = spl[0]
      elem.onclick = function() {
        eval('scr.setwindow(\'' + this.innerHTML + '\',' +  this.rel + ')')
      }
      wrap.appendChild(elem)
    }
    else if (type == 'object') {
      elem = document.createElement('div')
      elem.className = 'group'
      elema = document.createElement('div')
      elema.className = 'header'
      elema.innerHTML = list[i][0]
      elem.appendChild(elema)
      elema = this.append_level(list[i][1])
      elem.appendChild(elema)
      wrap.appendChild(elem)
    }
  }
  return wrap
}

navbar.prototype.hide_drops = function() {
  /* When clicking on an entry in a dropdown menu, the mouse will still hover
     the dropdown element, and the dropdown element will still be visible.
     By temporarily changing the class of the dropdown menu, this function
     makes the dropdown disappear. */
  if (!hiding_drops) {
    hiding_drops = true
    var l = this.drops.length
    for (i = 0; i < l; i++) {
      this.drops[i][0].className = 'dropdownh dropdown'
    }
    // No need to wait for more than 0.1 seconds
    setTimeout('navi[posis[\'dropdowns\']].hide_drops()', 100)
  }
  else {
    hiding_drops = false
    var l = this.drops.length
    for (i = 0; i < l; i++) {
      this.drops[i][0].className = 'dropdown'
    }
  }
}

navbar.prototype.box = function(name, pos) {
  /* This function adds a box with an object to the navigation bar. "pos" is
     the css float property and is completely optional. For the most part it
     should in fact not be neccesary to use it, as the styles of most elements
     added by this function is defined in a css file. */
  if (pos == undefined) pos = 'none'
  this[name] = document.createElement('div')
  this[name].className = name
  this[name].style.cssFloat = pos
  this.menu.appendChild(this[name])
  
  // Special cases
  if (name == 'clock') {
    this.setclock()
    this.clockinterval = setInterval('navi[posis[\'clock\']].setclock()', 30000)
  }
  if (name == 'abs_scroller') {
    // abs_scroller appears when there is not enough space for all appboxes.
    this.abs_scroller.className = 'abs'
    var elem
    elem = document.createElement('div')
    elem.className = 'scrollup'
    elem.onclick = function() {
      // Scroll up
      navi[posis['appboxes']].abs_scroll(1)
    }
    this.abs_scroller.appendChild(elem)
    elem = document.createElement('div')
    elem.className = 'scrolldown'
    elem.onclick = function() {
      // Scroll down
      navi[posis['appboxes']].abs_scroll(-1)
    }
    this.abs_scroller.appendChild(elem)
    
    this.menu.style.overflow = 'hidden'
  }
}

navbar.prototype.check_abs_height = function(force) {
  /* This function checks if there is a need for a scroller, i.e. if there are
     too many appboxes to be shown in one line. */
  var abs = navi[posis['appboxes']].height
  var rnum = navi[posis['appboxes']].appboxes.scrollHeight
  if (ie) {
    // Internet Explorer has some issues. This fix isn't perfect.
    if ((rnum / abs + '').indexOf('.') != -1) rnum += appbox_margin * 2
  }
  var num = rnum / abs
  if (num > 1) {
    // If there are too many appboxes
    navi[posis['appboxes']].appboxes.style.marginRight = abs_scroller_width + 3 + 'px'
    navi[posis['appboxes']].abs_scroller.style.display = 'block'
    /* If the force is true, scroll to the place where the window linked with
       the appbox is */
    if (force) this.abs_scroll(-Math.floor(wnds[scr.wndpos[0]].appbox.offsetTop / abs))
  }
  else {
    navi[posis['appboxes']].appboxes.style.marginRight = '0'
    navi[posis['appboxes']].abs_scroller.style.display = 'none'
    this.abs_scroll(-abs_scrolled)
  }
}

navbar.prototype.abs_scroll = function(am) {
  var abam = abs_scrolled + am * 1
  var abs = this.height
  var rnum = this.appboxes.scrollHeight
  if (ie) {
    // Internet Explorer has some issues. This fix isn't perfect.
    if ((rnum / abs + '').indexOf('.') != -1) rnum += appbox_margin * 2
  }
  
  /* num = the actual height of the appboxes container divided with the
     displayed height */
  var num = rnum / abs
  if (abs_scrolled + am < 1 && abam * -1 < num) {
    // Only do the scrolling if there's something to scroll to
    abs_scrolled = abam
    this.appboxes.style.marginTop = abs * abs_scrolled + 'px'
  }
}

navbar.prototype.setclock = function() {
  var time = new Date ()
  
  var hours = time.getHours()
  var minutes = time.getMinutes()
  
  // "07" is nicer than just "7"
  hours = (hours < 10 ? "0" : "") + hours
  minutes = (minutes < 10 ? "0" : "") + minutes

  this.clock.innerHTML = hours + ':' + minutes
}