metanohi-misc-subsites/projects/algo/js/navbar.js

285 lines
9.7 KiB
JavaScript

/*
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
}