/* // +----------------------------------------------------------------------+ // | Author: Thomas Biegeleisen // +----------------------------------------------------------------------+ */ var allBMenus = new Array(); // this is the only globally accessible variable for bmenu // only function called from the DOM: initialized and assigns object definition to DOM object // called on every mouseover, only initializes once per page load function BMenu_SetCurrentElement(bmenuID, bmenu_id) { if ( !allBMenus[bmenuID] ) { allBMenus[bmenuID] = new BMenu(bmenuID); } allBMenus[bmenuID].bmenu_id = bmenu_id; } BMenu = function(DOM_ID) { // constructor variables this.bmenu_active = Array(); this.bmenu_timeoutArray = Array(); this.bmenu_current = 0; this.bmenu_last = 0; this.bmenu_id = String(); // initialize DOM reference this.DOMElement = document.getElementById(DOM_ID); this.DOMElement.Menu = this; // creates back reference to object callable through DOM // what's in a name? this.MenuName = DOM_ID; // assign events this.DOMElement.onmouseover = this.MenuRollover; this.DOMElement.onmouseout = this.MenuRollout; // this loads settings from PHP this.bmenu_settings = document.getElementById(this.MenuName + '_settings'); if (this.bmenu_settings) { var customSettings = this.bmenu_settings.innerHTML; // get settings string customSettingsArray = customSettings.split(";"); // split each key/value pair for(i in customSettingsArray) { var customElement = customSettingsArray[i].split("="); // split key from value if (customElement[0]) { // only check for key (value might eval as "false") varName = 'this.bmenu_' + customElement[0]; evalString = varName + ' = \"' + customElement[1] + '\";'; eval(evalString); if (parseInt(eval(varName)) == eval(varName)) { // if we have a number, data type must match eval(varName + ' = parseInt(' + varName + ');'); } } } } else { // in case settings fail, assign default ones here. BMenu will not work without these settings this.bmenu_arrangement = "horizontal"; // arrangement of root menu (changes location of first child dropdown) this.bmenu_keepHighlight = 1; // when true, parent menus stay highlighted when rolling over children this.bmenu_timeoutDelay = 100; // time in ms before rollout triggers a turn-off for dropdowns this.bmenu_debugMode = 0; // boolean for debugging this.bmenu_menuWidth = 100; // default width for vertical arrangement (must be defined for mozilla) this.bmenu_submenuWidth = 100; // default width for dropdowns this.bmenu_dropdownSpacing = 0; // default spacing between dropdowns } /* // styles this.Style = new Object(); this.Style.menu_BorderWidth = 1; // border width of main menu */ /* var test = ""; for(i in this.bmenu_settings) { if (this.bmenu_settings[i] == "asdf") { test += i + "=" + this.bmenu_settings[i] + "\n"; } } alert(test); */ } BMenu.prototype.MenuRollover = function() { if (this.Menu.bmenu_debugMode) this.Menu.MenuClearOutput(); var thisParent = this.Menu.MenuGetParent(this.Menu.bmenu_id); if (this.Menu.bmenu_last) { // we skip the rollout sequence and immediately deactivate last element // unless the new element is a child of the last clearInterval(this.Menu.bmenu_timeoutArray[this.Menu.bmenu_last]); if (thisParent != this.Menu.bmenu_last) { this.Menu.MenuDeactivateElement(this.Menu.bmenu_last); } else if (thisParent != "" && !this.Menu.bmenu_keepHighlight) { // turn off parent class var myParent = document.getElementById(this.Menu.MenuName + thisParent); newClass = myParent.className.substr(0, myParent.className.lastIndexOf('_')) + '_off'; this.Menu.MenuSetElementClass(myParent, newClass); } } // activate element if (this.Menu.bmenu_debugMode) this.Menu.MenuAddOutput('activate called from rollover()'); this.Menu.MenuActivateElement(this.Menu.bmenu_id); // define the element locally var thisElement = document.getElementById(this.Menu.MenuName + this.Menu.bmenu_id); if (!thisElement) { if (this.Menu.bmenu_debugMode) this.Menu.MenuAddOutput('ERROR bmenu_rollover(): ' + this.Menu.bmenu_id + ' does not exist!'); } // get new class name newClass = thisElement.className.substr(0, thisElement.className.lastIndexOf('_')) + '_on'; this.Menu.MenuSetElementClass(thisElement, newClass); // set element as current... this.Menu.bmenu_current = this.Menu.bmenu_id; // show the child div this.Menu.MenuShowChild(this.Menu.bmenu_id); // activate all parents this.Menu.MenuActivateParents(this.Menu.bmenu_id); if (this.Menu.bmenu_last) { // get last parent var lastParent = this.Menu.MenuGetParent(this.Menu.bmenu_last); // tricky: we only deactivate parents if the following three cases fail: // 1) if last and current are siblings; // 2) if last's parent is current; // 3) last's parent's parent is current's parent (jumped to parent's sibling) // if any of these are true, we can maintain dropdown family tree. // if these fail, one of two things has happened: // 1) we've moved to another parent tree (i.e. a submenu to a different top-most parent), or // 2) we've moved too far from current element to maintain family tree (i.e. grandparent jump - unlikely in 1/10th of a second) // and we are forced to collapse everything from last element up to top-most parent if (lastParent == thisParent) { // sibling test } else if (lastParent == this.Menu.bmenu_id) { // jumped back to parent } else if (this.Menu.MenuGetParent(lastParent) == thisParent) { // jumped to a parent's sibling this.Menu.MenuDeactivateElement(lastParent); } else { if (this.Menu.bmenu_active[this.Menu.bmenu_last] == 0) { if (this.Menu.bmenu_debugMode) this.Menu.MenuAddOutput('deactivateParents called from rollover(' + this.Menu.bmenu_id + '), because bmenu_active[' + this.Menu.bmenu_last + '] = 0'); this.Menu.MenuDeactivateParents(this.Menu.bmenu_last); } } } // ...and set as last this.Menu.bmenu_last = this.Menu.bmenu_id; } BMenu.prototype.MenuRollout = function() { if (this.Menu.bmenu_debugMode) this.Menu.MenuAddOutput('MenuRollout ' + this.Menu.bmenu_id); // toggle the item for deactivation this.Menu.bmenu_active[this.Menu.bmenu_id] = 0; // erase the current this.Menu.bmenu_current = 0; // begin timeout for rollout // setTimeout is external from object scope, have to redefine for MenuTimeout function this.Menu.bmenu_timeoutArray[this.Menu.bmenu_id] = setTimeout("allBMenus['" + this.Menu.MenuName + "'].MenuTimeout('" + this.Menu.MenuName + "')", this.Menu.bmenu_timeoutDelay); } BMenu.prototype.MenuTimeout = function(objRef) { // redefine BMenu: // var thisMenu = allBMenus[objRef]; var thisMenu = this.DOMElement.Menu; // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('BEGIN TIMEOUT ' + bmenu_id); for (i in thisMenu.bmenu_active) { if (thisMenu.bmenu_active[i] == 0) { thisMenu.bmenu_active[i] = 0; // deactive this element thisMenu.MenuDeactivateElement(i); if (thisMenu.bmenu_current == 0) { thisMenu.MenuDeactivateParents(i); // alert('no active element'); } } } // clear the array thisMenu.bmenu_active = new Array(); } BMenu.prototype.MenuActivateElement = function(this_bmenu_id) { // redefine object var thisMenu = this.DOMElement.Menu; // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('Activating ' + bmenu_id); // set as active thisMenu.bmenu_active[this_bmenu_id] = 1; // turn off any timeout if (thisMenu.bmenu_timeoutArray[this_bmenu_id]) { clearInterval(thisMenu.bmenu_timeoutArray[this_bmenu_id]); } } BMenu.prototype.MenuDeactivateElement = function(this_bmenu_id) { // redefine object var thisMenu = this.DOMElement.Menu; // for (i in this) { alert(i + " = " + this[i]); } // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('Deactivating ' + this_bmenu_id); // define the element locally var thisElement = document.getElementById(thisMenu.MenuName + this_bmenu_id); if (!thisElement) { if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('ERROR bmenu_deactivateElement(): ' + bmenu_id + ' does not exist!'); } // turn element off newClass = thisElement.className.substr(0, thisElement.className.lastIndexOf('_')) + '_off'; thisMenu.MenuSetElementClass(thisElement, newClass); // hide its child thisMenu.MenuHideChild(this_bmenu_id); } BMenu.prototype.MenuSetElementClass = function(thisElement, newClass) { if (!thisElement) { return; } // set element class thisElement.className = newClass; } BMenu.prototype.MenuHideChild = function(this_bmenu_id) { // redefine object var thisMenu = this.DOMElement.Menu; // get submenu, if one exists var thisSubmenu = document.getElementById(thisMenu.MenuName + '_div' + this_bmenu_id); if (thisSubmenu) { // assign style to the cell thisSubmenu.style.position = "absolute"; thisSubmenu.style.display = "none"; } } BMenu.prototype.MenuShowChild = function(this_bmenu_id) { // redefine object var thisMenu = this.DOMElement.Menu; // redefine element var thisElement = document.getElementById(thisMenu.MenuName + this_bmenu_id); // get submenu, if one exists var thisSubmenu = document.getElementById(thisMenu.MenuName + '_div' + this_bmenu_id); if (thisSubmenu) { // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('Activating child bmenu_div' + bmenu_id); // first set the coords of this element thisX = parseInt(thisMenu.MenuGetCoord(thisElement, 'x')); thisY = parseInt(thisMenu.MenuGetCoord(thisElement, 'y')); var parentCount = thisMenu.MenuCountParents(this_bmenu_id); // CUSTOM: set menu coords here if (parentCount == 0) { // this is the first child if (thisMenu.bmenu_arrangement == "vertical") { finalX = thisX + parseInt(thisElement.offsetWidth) + thisMenu.bmenu_dropdownSpacing; finalY = thisY; } else { finalX = thisX; finalY = thisY + parseInt(thisElement.offsetHeight) + thisMenu.bmenu_dropdownSpacing; } } else { // this is a sub dropdown ("dropacross") finalX = thisX + parseInt(thisElement.offsetWidth) + thisMenu.bmenu_dropdownSpacing; finalY = thisY; } // assign to the cell thisSubmenu.style.left = finalX + "px"; thisSubmenu.style.top = finalY + "px"; thisSubmenu.style.position = "absolute"; thisSubmenu.style.display = "inline"; } } BMenu.prototype.MenuCountParents = function(this_bmenu_id) { var allParents = this_bmenu_id.split('_'); var numParents = 0; for (i in allParents) { if (allParents[i] != "") { numParents++; } } numParents -= 1; return numParents; } BMenu.prototype.MenuGetCoord = function (thisObj, axis) { // set coord to zero (default offset is zero for any object) var thisCoord = 0; while (thisObj.offsetParent) { if (axis == "x") { thisCoord += parseInt(thisObj.offsetLeft); } else if (axis == "y") { thisCoord += parseInt(thisObj.offsetTop); } thisObj = thisObj.offsetParent; } return thisCoord; } BMenu.prototype.MenuActivateParents = function(this_bmenu_id) { // redefine object var thisMenu = this.DOMElement.Menu; // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('(Activating parents of ' + bmenu_id + ':)'); var bmenuParents = this_bmenu_id.split('_'); var thisParent = ""; for (i in bmenuParents) { if (bmenuParents[i] != "") { thisParent += '_' + bmenuParents[i]; thisMenu.MenuActivateElement(thisParent); } } } BMenu.prototype.MenuDeactivateParents = function(this_bmenu_id) { // redefine object var thisMenu = this.DOMElement.Menu; var allIDs = this_bmenu_id.split("_"); /* for some reason split comes up with error if not on first line? */ // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('in deactivateParents(' + bmenu_id + ')...') var thisParent = thisMenu.MenuGetParent(this_bmenu_id); if (!thisParent) { // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('   no parent!'); return; } if (thisMenu.bmenu_active[thisParent] == 0) { // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('   parent already inactive!'); return; } var thisElement = ""; var allParents = new Array(); currentParent = thisMenu.MenuGetParent(thisMenu.bmenu_current); for (i in allIDs) { thisID = allIDs[i]; if (thisID != "") { thisElement += "_" + thisID; if (thisElement != this_bmenu_id) { allParents.push(thisElement); } } } // start from current and work your way up allParents.reverse(); for (i in allParents) { thisElement = allParents[i]; if (thisMenu.bmenu_active[thisElement]) { // if (thisMenu.bmenu_debugMode) thisMenu.MenuAddOutput('deactivate called from deactivateParents()'); thisMenu.MenuDeactivateElement(thisElement); thisMenu.bmenu_active[thisElement] = 0; } } } BMenu.prototype.MenuGetParent = function(this_bmenu_id) { if (!this_bmenu_id) { return; } allIds = this_bmenu_id.split("_"); var constructParent = ""; for (i in allIds) { if (allIds[i] != "") { if (allIds[parseInt(i)+1]) { constructParent += "_" + allIds[i]; } } } return constructParent; } BMenu.prototype.MenuAddOutput = function(newText) { // redefine object var thisMenu = this.DOMElement.Menu; outputWindow = document.getElementById(thisMenu.MenuName + '_output').contentWindow; outputWindow.document.getElementById('output').innerHTML += "
\n" + newText; outputWindow.scrollTo(0, 50000); } BMenu.prototype.MenuClearOutput = function() { // redefine object var thisMenu = this.DOMElement.Menu; outputWindow = document.getElementById(thisMenu.MenuName + '_output').contentWindow; outputWindow.document.getElementById('output').innerHTML = ""; }