// ******************************** quirksmode: ******************************** // var QM; if (!QM) QM = new function() { //Browser detect - http://www.quirksmode.org/js/detect.html this.checkIt = function(string) { this.place = this.detect.indexOf(string) + 1; this.thestring = string; return this.place; } this.detect = navigator.userAgent.toLowerCase(); this.OS = 0; this.browser = 0; this.version = 0; this.total = 0; this.place = 0; this.thestring = 0; if (this.checkIt('konqueror')) { this.browser = "Konqueror"; this.OS = "Linux"; } else if (this.checkIt('safari')) this.browser = "Safari" else if (this.checkIt('omniweb')) this.browser = "OmniWeb" else if (this.checkIt('opera')) this.browser = "Opera" else if (this.checkIt('webtv')) this.browser = "WebTV"; else if (this.checkIt('icab')) this.browser = "iCab" else if (this.checkIt('msie')) this.browser = "Internet Explorer" else if (!this.checkIt('compatible')) { this.browser = "Netscape Navigator" this.version = this.detect.charAt(8); } else this.browser = "An unknown browser"; if (!this.version) this.version = this.detect.charAt(this.place + this.thestring.length); if (!this.OS) { if (this.checkIt('linux')) this.OS = "Linux"; else if (this.checkIt('x11')) this.OS = "Unix"; else if (this.checkIt('mac')) this.OS = "Mac" else if (this.checkIt('win')) this.OS = "Windows" else this.OS = "an unknown operating system"; } } // ******************************** listener library: ******************************** // //todo - do a browser detect before determining which method to use //and if neither is supported . . . revert to oldschool src["on"+ev] = function() { cycle through all func's in some array and execute them } //but to do that - for the oldschool support to work - we would need an event listener init . . . and perhaps an event listener object? function addListener(src, ev, func) { //W3C DOM Event Handling if (src.addEventListener) { src.addEventListener(ev, func, false); } //Microsoft? else { //else if (src.attachEvent) { . . . src.attachEvent("on" + ev, func); } } // ******************************** mouse library: ******************************** // var ns4 = !!(document.layers); var ie4 = !!(document.all && !document.getElementById); var ie5 = !!(document.all && document.getElementById); var ns6 = !!(!document.all && document.getElementById); //mouse handling object //what happens if multiple instances of this are php-included? //how about this? "var mouseh;" "if (!mouseh) mouseh = . . . " ? this depends upon the assumption that mouseh == null by default //this way even if the code is php-included twice, it will still only instanciate a single object //Notice how I wrote my own multiple-event-handler code in there? thats not necessarily the best. //pro: can be easily made backwards compatible. con: a lot of extra (possibly wasted) typing var mouseh; if (!mouseh) mouseh = { //last stored mouse position lastpos:{x:0, y:0}, //someone evaluate this condition for me . . . ieMouse: (ie4 || ie5) && !(ns4 || ns6), //cross-browser functionality . . . getEventMouseX: function(e) { return this.ieMouse ? window.event.clientX : e.pageX;}, getEventMouseY: function(e) { return this.ieMouse ? window.event.clientY : e.pageY;}, //array of functions accepting all cross-browser mouse parameters onMouseUpFuncs: new Array(), onMouseDownFuncs: new Array(), onMouseMoveFuncs: new Array(), addUpFunc: function (func) { this.onMouseUpFuncs.push(func); }, addDownFunc: function (func) { this.onMouseDownFuncs.push(func); }, addMoveFunc: function (func) { this.onMouseMoveFuncs.push(func); }, // ******** begin callback code ******** //notice - within callback code 'this' refers to the callee, not mouseh, so you gotta reference mouseh specifically onMouseUp: function (e) { var x = mouseh.getEventMouseX(e); var y = mouseh.getEventMouseY(e); for (i in mouseh.onMouseUpFuncs) { mouseh.onMouseUpFuncs[i](x,y); } }, onMouseDown: function (e) { var x = mouseh.getEventMouseX(e); var y = mouseh.getEventMouseY(e); //cycle through all preregistered functions and execute them . . . for (i in mouseh.onMouseDownFuncs) { mouseh.onMouseDownFuncs[i](x,y); } }, onMouseMove: function (e) { var x = mouseh.getEventMouseX(e); var y = mouseh.getEventMouseY(e); //cycle through all preregistered functions for (i in mouseh.onMouseMoveFuncs) { mouseh.onMouseMoveFuncs[i](x, y); } //store the last mouse coords mouseh.lastpos.x = x; mouseh.lastpos.y = y; }, // ******** end callback code ******** hasMouseBeenInitialized: false, init: function () { if (this.hasMouseBeenInitialized) return; if (ie4) { document.captureEvents(Event.MOUSEMOVE | Event.MOUSEDOWN | Event.MOUSEUP | Event.MOUSEDBLCLICK); } addListener(document, "mousedown", this.onMouseDown); addListener(document, "mousemove", this.onMouseMove); addListener(document, "mouseup", this.onMouseUp); this.hasMouseBeenInitialized = true; } }; // ******************************** opacity ******************************** // //todo - detect which method to use (browser-based?) and only set the single required property from there //rather than the excessive property setting as we have it function setOpacity(obj, opacity) { //why do you suppose this line is needed? opacity = (opacity == 100)?99.999:opacity; // IE/Win obj.style.filter = "alpha(opacity:"+opacity+")"; //oldschool IE/Win? if (obj.style.filters && obj.style.filters.item) { obj.style.filters.item("DXImageTransform.Microsoft.Alpha").opacity = opacity; } // Safari<1.2, Konqueror obj.style.KHTMLOpacity = opacity/100; // Older Mozilla and Firefox obj.style.MozOpacity = opacity/100; // Safari 1.2, newer Firefox and Mozilla, CSS3 obj.style.opacity = opacity/100; } // ******************************** generic functions ******************************** // //how to grab the absolute position of some HTML object function getAbsolutePosition(n) { var pos = {x:0, y:0}; do { pos.x += n.offsetLeft; pos.y += n.offsetTop; } while (n = n.offsetParent); return pos; } //Recursive call for "searchDOMTree". can we make this method private to this file only? //originally key == "className", val == user-defined //AND key == "nodeName", val == "UL" function searchDomTreeR(node, key, val, child, dest) { //see if we should add this node if (node[key] == val) { dest.push(node); //if we dont want to search through children - and we found this node - then return if (!child) return; } //test all its children for (var ch = node.firstChild; ch; ch = ch.nextSibling) { searchDomTreeR(ch, key, val, child, dest); } } //function for searching through the DOM tree and returning all of objects for which one property equals one value //search for all UL nodes with matching className. do not search the children of parents we have found //node: which node to start at //key: what key to pick out //val: the sought after value of the specified key //child: boolean flag whether to search children of found objects function searchDomTree(node, key, val, child) { var dest = new Array(); searchDomTreeR(node, key, val, child, dest); return dest; } //same as above but with an array rather than a DOM tree function searchArray(ar, key, val) { var dest = new Array(); for (i in ar) { a = ar[i]; if (a[key] == val) dest.push(a); } return dest; } // timer functions var lastCloseTime = null; var lastCloseMenu = null; var currentSectionOpen = ""; function processMessages() { window.setTimeout(processMessages, 100); if (lastCloseMenu && lastCloseTime) { var thisTime = new Date(); //if a 'last close time' exists (non-null) and has passed by more than N second //(one-second delay) if (lastCloseTime.getTime() + 500 < thisTime.getTime()) { //then close the menu lastCloseMenu.closeList(); lastCloseMenu = null; lastCloseTime = null; } } } function requestClose(menu) { lastCloseMenu = menu; lastCloseTime = new Date(); } function clearCloseRequest() { lastCloseMenu = null; lastCloseTime = null; } //utility / cross browser //testing parentNode is not a valid way in IE to see if an object has been removed from its parent //why? because in IE once removed the object still retains its parentNode value //if so then how do we determine whether it is attached to the tree or not? function nodeIsAttached(n) { /* if mozilla or other standard-compliant browser: * return !!n.parentNode; /**/ /* (this still isn't working with IE) * var p = n.parentNode; if (!p) return false; //manually go through the parent's children, checking to see if 'n' is one of them for (var ch = p.firstChild; ch; ch = ch.nextSibling) { if (ch == n) return true; } return false; /**/ /* final option - just switch the display: */ return n.style.display != "none"; /**/ } //attach the node to the function attachNode(parent, node) { /* the mozilla way . . . * parent.appendChild(node); /**/ /* IE compatible alternative */ //todo - if node.defaultDisplay is null then set style.display to the default display style for the node (this varies depending on the node tag name) if (!node.defaultDisplay) node.defaultDisplay = "block"; node.style.display = node.defaultDisplay; //else . . . error . . . you should have removed it before attaching it again ! /**/ } function removeNode(parent, node) { /* standards compliant: * parent.removeChild(node); /**/ /* oldschool-IE compatible */ if (node.style.display != "none") node.defaultDisplay = node.style.display; //store for restoring else node.defaultDisplay = "block"; //is it always block? isnt this conditional depending on the nodeName? //alert('setting default display as ' + node.defaultDisplay + ' for node type ' + node.tagName + ' parent type ' + parent.tagName); node.style.display = "none"; /**/ } // menu initialization var lastOpenList; function fix_li(li) { li.uls = []; for (var ch = li.firstChild; ch; ch = ch.nextSibling) { if (ch.nodeName == "UL") { //now that we've found a UL within our base UL, cut it off and link it separately through an extra field in the child li . . . //childULs will be a parameter of each li pointing to all children ULs within it . . li.uls.push(ch); removeNode(li, ch); } } //if we found any ULs in this child . .. if (li.uls.length) { //give it a mouseover to reattach the child UL's li.openList = function() { //allow only one open at a time if (lastOpenList) { lastOpenList.closeList(); } lastOpenList = this; //if we had a menu close request then clear it . . . clearCloseRequest(); for (i in this.uls) { var ul = this.uls[i]; //if the ul is not attached then append ul to the end of the li's children . . . if (!nodeIsAttached(ul)) attachNode(this, ul); } } li.closeList = function() { //allow only one open at a time: if (lastOpenList == this) { //this should be the only case in which lastOpenList exists lastOpenList == null; } for (i in this.uls) { var ul = this.uls[i]; if (nodeIsAttached(ul)) removeNode(ul.parentNode, ul); } } /* pop up on roll over * li.onmouseover = li.openList; /**/ /* pop up on click * li.onclick = li.openList; /**/ /* toggle open/close on click */ //conditional - solely the link if a link is first, else do the whole li if (li.firstChild.nodeName == "A") { li.firstChild.onclick = function() { var parent = this.parentNode; if (parent.uls.length && nodeIsAttached(parent.uls[0])) { parent.closeList(); } else { parent.openList(); } } } else { li.onclick = function() { if (this.uls.length && nodeIsAttached(this.uls[0])) { this.closeList(); } else { this.openList(); } } } /**/ } } function create_sliding_menu(ul) { //so here, see if we can disconnect all child UL's //instead apply them as separate fields //and, upon mouseover, reattach them . . . ul.lis = []; //first search through the UL for all LI children for (var ch = ul.firstChild; ch; ch = ch.nextSibling) { if (ch.nodeName == "LI") { //now, within the LI, search for any UL's . . . fix_li(ch); //add it to the list ul.lis.push(ch); /* note to self: looks like, in IE, the link will only fill the whole contents of its containing LI unless within that LI is a UL with display set to 'block' and also possessing at least one of its own LI nodes AND in doing so the 4-pixel overlap that IE typically has with first-level LI's is mysteriously removed ... so we no longer need to compensate for that. */ //if detect IE then var eli = document.createElement('li'); eli.style.display = 'none'; var eul = document.createElement('ul'); eul.style.display = 'block'; eul.style.height = '0px'; eul.style.margin = '0px'; eul.appendChild(eli); ch.appendChild(eul); } } //how about this . . . when the mouse exits the UL, add in a timer function that //sends a close command to issue after a few seconds. //however, if an open occurs within that time, cancel the close request ul.closeList = function() { for (i in this.lis) { var li = this.lis[i]; if (li.closeList) { li.closeList(); } } } //todo - rather than 'ontimeout', we should add a mouse movement listener //likewise, parellel with the mouse library, we should no longer use the onMouseMove, etc, etc, functions //or make an array of functions of our own . . . //looks like we're going to be passing on mouseout closes // ul.onmouseout = function() { // requestClose(ul); // } } function initialize_sliding_menu() { var ar = document.getElementById('nav'); create_sliding_menu(ar); //init our mouse movement code //for later - i'm going to override all the 'onmouseover' and 'onmouseout' //considering how well they're working . .. mouseh.init(); //todo - add mouse events surrounding the sliding menus (proly within create_sliding_menu) and depend on that rather than 'onmouseover' / 'onmouseout' //can we add an event listener for this as well? or does one override another? can multiple timeouts be set? //we only need ths if we ever call requestClose() // window.setTimeout(processMessages, 100); var first = document.getElementById(currentSectionOpen); if (first && first.openList) first.openList(); } //this should swap out the first matching id with our rotating image list addListener(window, "load", function() {initialize_sliding_menu();});