285 lines
9.7 KiB
JavaScript
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
|
||
|
}
|