var sfMenu = {
//menu flags
RIGHT: 0x1,
LEFT: 0x2,
TOP: 0x4,
BOTTOM: 0x8,
INLINE: 0x16,
GC:{
map:{
'idx1':{'top':'GC28','sub':'GC30','back':'GC30'},
'idx2':{'top':'GC32','sub':'GC34','back':'GC34'}
},
toHover:{
'GC28':'GC29','GC30':'GC31','GC32':'GC33','GC34':'GC35',
'GC29':'GC29','GC31':'GC31','GC33':'GC33','GC35':'GC35'
},
toUnhover:{
'GC29':'GC28','GC31':'GC30','GC33':'GC32','GC35':'GC34',
'GC28':'GC28','GC30':'GC30','GC32':'GC32','GC34':'GC34'
}
},
id:'',
type:'',
className:'',
root:null,
currentItem:null,
//FUNCTION init - initialise UL node as menu
//
//PARAM id - String, id of top-level UL node
//PARAM type - Bitwise Mask, menu positions/type flags (optional)
//PARAM className - String,  class name of nested UL's (optional)
//PARAM root - String, id of element to position menus against
//
init: function(id, type, className, root, openAtID) {
if(id)sfMenu.id = id;else id = sfMenu.id;
if(type)sfMenu.type = type;else type = sfMenu.type;
if(className)sfMenu.className = className;else className = sfMenu.className;
var el = document.getElementById(id);
//set sfMenu root this way only first call needs to set root node
//and  subsequent will use the same.
sfMenu.root = (sfMenu.root)? document.getElementById(root): document.body;
var className = (className)? className: id;
//no flags so figure out sub-menu orientation
if(!type || type &~sfMenu.INLINE) {
if(!type) {
type |= (sfMenu.getTop(el) > sfMenu.root.scrollHeight/2)? sfMenu.TOP: sfMenu.BOTTOM;
}
if(type & ~(sfMenu.LEFT | sfMenu.RIGHT)) {
type |= (sfMenu.getLeft(el) > sfMenu.root.clientWidth/2)? sfMenu.LEFT: sfMenu.RIGHT;
}
}
//recursive loop for initialising menu's items and sub-menus
var exec = function(parentEl, type, index, className, par, openAtID) {
var el = parentEl.firstChild;
var index = (index)? index: 0;
//store menu item for grouping with when we hit a sub-menu
var a = null;
//store widest node in submenu
var dnode = null;
do {
//found 'LI' (menu_item) (contains an 'A' or 'SPAN' Node and possibly a 'UL' (sub-menu))
switch (el.nodeName) {
case 'LI':
//process children
var n = exec(el, type, index, className, par, openAtID);
//record menu item with maximum width
if(!dnode || dnode && n.innerHTML.length > dnode.innerHTML.length)
dnode = n;
break;
//found 'DIV' (left or right area), so process it's contents
case 'DIV':
exec(el, type, index, className, par, openAtID);
break;
//found 'A'  or 'SPAN' (either of these are our event recipients)
case 'A':
case 'SPAN':
//only process if it's in the right place
if(el.parentNode.nodeName == 'LI') {
//handle mouse over events if menu is floating
if(type &~sfMenu.INLINE) {
el.onmouseover = function() {
sfMenu.hideEvent++;
sfMenu.hide(sfMenu.hideEvent, this);
var sfindex = node.item.getAttribute('sfmenu:index');
var id = node.item.getAttribute('sfmenu:id');
var nlist = [document.getElementById(sfindex+id), document.getElementById(sfindex+'Left'+id), document.getElementById(sfindex+'Right'+id)];
for(var i = 0, n = nlist.length; i < n; i++) {
var cls = nlist[i].getAttribute('sf:object');
nlist[i].className = sfMenu.GC.toHover[nlist[i].className.split(' ')[0]]+' '+cls.replace('MouseOver', '')+'MouseOver';
}
}
el.onmouseout = function() {
if(!this.sfMenu) {
var sfindex = node.item.getAttribute('sfmenu:index');
var id = node.item.getAttribute('sfmenu:id');
var nlist = [document.getElementById(sfindex+id), document.getElementById(sfindex+'Left'+id), document.getElementById(sfindex+'Right'+id)];
for(var i = 0, n = nlist.length; i < n; i++) {
var cls = nlist[i].getAttribute('sf:object');
nlist[i].className = sfMenu.GC.toUnhover[nlist[i].className.split(' ')[0]]+' '+nlist[i].className.replace(cls+'MouseOver', cls);
}
}
sfMenu.hideTimer();
}
}
//menu is tree
else {
el.onmouseover = function() {
var sfindex = this.getAttribute('sfmenu:index');
var id = this.getAttribute('sfmenu:id');
var nlist = [document.getElementById(sfindex+id), document.getElementById(sfindex+'Left'+id), document.getElementById(sfindex+'Right'+id)];
for(var i = 0, n = nlist.length; i < n; i++) {
var cls = nlist[i].getAttribute('sf:object'), classes = nlist[i].className.split(' '), clsGC = sfMenu.GC.toHover[classes[0]];
delete classes[0];
classes = classes.join(' ');
nlist[i].className = clsGC+classes.replace('('+cls+')(MouseOver)?', '$1MouseOver');
}
return false;
}
el.onmouseout = function() {
var sfindex = this.getAttribute('sfmenu:index');
var id = this.getAttribute('sfmenu:id');
var nlist = [document.getElementById(sfindex+id), document.getElementById(sfindex+'Left'+id), document.getElementById(sfindex+'Right'+id)];
for(var i = 0, n = nlist.length; i < n; i++) {
var cls = nlist[i].getAttribute('sf:object'), classes = nlist[i].className.split(' '), clsGC = sfMenu.GC.toUnhover[classes[0]];
delete classes[0];
classes = classes.join(' ');
nlist[i].className = clsGC+classes.replace(cls+'MouseOver', cls);
}
return false;
}
}
//'A' only contains textNodes but 'SPAN' contains anything
if(el.nodeName != 'A')
exec(el, type, index, className, par, openAtID);
//store 'A' for grouping with subsequent 'UL'
a = el;
a.parentItem = par;
}
// Don't show the +- icons if there aren't any children.
if(el.nodeName == 'SPAN') {
if((type & sfMenu.INLINE) == sfMenu.INLINE) {
// For Firefox...
if (el.nextSibling && el.nextSibling.nodeName == '#text'
&& (!el.nextSibling.nextSibling || el.nextSibling.nextSibling.nodeName == 'SPAN')) {
el.childNodes[1].style.backgroundImage = 'none';
}
// For IE...
else if(!el.nextSibling || el.nextSibling.nodeName == 'SPAN') {
el.childNodes[0].style.backgroundImage = 'none';
}
// Make the specified item (and all its parents) visible
if(openAtID && el.getAttribute('sfmenu:id') == openAtID) {
sfMenu.currentItem = el;
}
}
}
break;
//found 'UL' this means the 'A' or 'SPAN' we just passed has a sub-menu
case 'UL':
//assign this 'sub-menu' to the 'A' or 'SPAN'
a.sfMenu = el;
//menu is floating type
if(type &~sfMenu.INLINE) {
//top-level menu will ignore LEFT and RIGHT if TOP or BOTTOM are set
if(index == 0) {
if(type & (sfMenu.TOP | sfMenu.BOTTOM))
el.style.width = a.offsetWidth + 'px';
a.sfMenuRoot = true
a.sfMenuType = (type &(sfMenu.TOP | sfMenu.BOTTOM))? type & ~(sfMenu.LEFT | sfMenu.RIGHT): type;
//sub-menus use all flags
} else
a.sfMenuType = type;
//give menu item ('LI' for floating) class name, add mouseover events to both menu and menu item
//current shopfactory is limited, no interface to edit styles for menu items with children
//a.parentNode.className = 'menuItem';
a.onmouseover = sfMenu.show;
el.onmouseover = function() { sfMenu.hideEvent++ };
el.onmouseout = sfMenu.hideTimer;
//menu is tree
} else {
//give menu item ('A' for tree) class name, add click event to menu item
//current shopfactory is limited, no interface to edit styles for menu items with children
a.sfMenuType = type;
if(tf.isIE6){
a.style.position = 'relative';
a.style.display = 'block';
}
a.onclick = sfMenu.inlineShow;
a.onmouseover = function() {
var sfindex = this.getAttribute('sfmenu:index');
var id = this.getAttribute('sfmenu:id');
var nlist = [document.getElementById(sfindex+id), document.getElementById(sfindex+'Left'+id), document.getElementById(sfindex+'Right'+id)];
for(var i = 0, n = nlist.length; i < n; i++) {
var cls = nlist[i].getAttribute('sf:object'), classes = nlist[i].className.split(' '), clsGC = sfMenu.GC.toHover[classes[0]];
delete classes[0];
classes = classes.join(' ');
nlist[i].className = clsGC+classes.replace('('+cls+')(MouseOver)?', '$1MouseOver');
}
return false;
}
a.onmouseout = function() {
var sfindex = this.getAttribute('sfmenu:index');
var id = this.getAttribute('sfmenu:id');
var nlist = [document.getElementById(sfindex+id), document.getElementById(sfindex+'Left'+id), document.getElementById(sfindex+'Right'+id)];
for(var i = 0, n = nlist.length; i < n; i++) {
var cls = nlist[i].getAttribute('sf:object'), classes = nlist[i].className.split(' '), clsGC = sfMenu.GC.toUnhover[classes[0]];
delete classes[0];
classes = classes.join(' ');
nlist[i].className = clsGC+classes.replace(cls+'MouseOver', cls);
}
return false;
}
}
//set sub-menu classname, hide it if it isn't already hidden, and set z-index
el.className = className;
el.style.display = 'none';
el.style.zIndex = index+1;
//clear 'A' | 'SPAN' store, set sub-menus widest node
par = a;
a = null;
el.sfNode = exec(el, type, index+1, className, par, openAtID);
//if floating menu then we move sub-menus ('UL') to document.body
if(type &~sfMenu.INLINE)
document.body.appendChild(el);
break;
}
} while((el = el.nextSibling) != null);
return dnode;
};
//start the processing
el.style.display = 'block';
exec(el, type, 0, className, null, openAtID);
// Make the specified item (and all its parents) visible
if(openAtID && sfMenu.currentItem) {
var elOpen = sfMenu.currentItem.parentItem;
if (elOpen)
{
do
{
var sfindex = elOpen.getAttribute('sfmenu:index');
var id = elOpen.getAttribute('sfmenu:id');
var left = document.getElementById(sfindex+'Left'+id);
left.className = left.className.replace(' opened', '')+' opened';
elOpen.sfMenu.style.visibility = 'visible';
elOpen.sfMenu.style.display = 'block';
} while ((elOpen = elOpen.parentItem) != null);
}
if (sfMenu.currentItem.sfMenu) sfMenu.inlineShow(null, -1, -1, sfMenu.currentItem);
}
},
//returns the node that holds menu items text (incase of nested )
getTextNode: function(el) {
var el = el.firstChild;
do {
if(el.nodeName == 'A') break;
else if(el.nodeName == 'DIV' || el.nodeName == 'SPAN') {
el = sfMenu.getTextNode(el);
break;
}
} while((el = el.nextSibling) != null);
return el;
},
//position functions
getLeft: function(el) {
var left = 0;
do left += el.offsetLeft;
while((el = el.offsetParent) != null);
return left;
},
getTop: function(el) {
var top = 0;
do top += el.offsetTop;
while((el = el.offsetParent) != null);
return top;
},
//current menu position stack
current: new Array(),
//event id so for hideTimer
hideEvent: 0,
//calls hide after a set time, so users don't get frustrated when they move
//the cursor away from the menus for a brief second and have them disappear
hideTimer: function() {
window.setTimeout('sfMenu.hide('+(++sfMenu.hideEvent)+')', 500);
},
//the show function for inline menu types
inlineShow: function(caller, x, y, _el) {
var el = _el || caller || this;
if (el.currentTarget) el = el.currentTarget;
if (el.href) location = el.href;
//sub-menu is visible so hide
if(el.sfMenu.style.display == 'block') {
var sfindex = el.getAttribute('sfmenu:index');
var id = el.getAttribute('sfmenu:id');
var left = document.getElementById(sfindex+'Left'+id);
left.className = left.className.replace(' opened', '');
el.sfMenu.style.display = 'none';
if(el.onmouseout)el.onmouseout();
}
//sub-menu is hidden so display
else {
var sfindex = el.getAttribute('sfmenu:index');
var id = el.getAttribute('sfmenu:id');
var left = document.getElementById(sfindex+'Left'+id);
left.className = left.className.replace(' opened', '')+' opened';
el.sfMenu.style.visibility = 'visible';
el.sfMenu.style.display = 'block';
if(el.onmouseout)el.onmouseout();
}
if(typeof(equalHeight) != 'undefined')equalHeight();
if(tf.utils && tf.utils.applyms)tf.utils.applyms();
return true;
},
//the show function for floating menus
show: function(e) {
//set hideEvent so any hide are cancelled
sfMenu.hideEvent++;
//now hide all menus, except this ones, that are below this item
sfMenu.hide(sfMenu.hideEvent, this);
//hilight menu item
sfMenu.current.push({item: this, menu: this.sfMenu});
this.className = 'active';
//get menu item, top/left position of menu item and document width/hieght
var d = this.parentNode;
var left = sfMenu.getLeft(d);
var top = sfMenu.getTop(this);
var docHeight = sfMenu.root.clientHeight + document.body.scrollTop;
var docWidth = sfMenu.root.clientWidth + document.body.scrollLeft;
//work-around when css attr display: none set clientWidth is 0
if(this.sfMenu.style.display == 'none')
this.sfMenu.style.visibility = 'hidden';
//display sub-menu (still not visible though)
this.sfMenu.style.display = 'block';
//check if we dont have the real width of menu item (for overflow issues)
if(!this.sfMenu.sfRealWidth) {
//get textnode of widest menu item and set all menu items of sub-menu to it's width
var n = sfMenu.getTextNode(this.sfMenu.sfNode);
if(n && n.offsetWidth < n.scrollWidth) {
var nWidth = n.style.width = n.scrollWidth + 'px';
var sWidth = this.sfMenu.sfNode.style.width = (this.sfMenu.sfNode.offsetWidth < this.sfMenu.sfNode.scrollWidth)?
this.sfMenu.sfNode.scrollWidth + 'px': this.sfMenu.sfNode.offsetWidth + 'px';
for(var i = 0; i < this.sfMenu.childNodes.length; i++) {
if(this.sfMenu.childNodes[i].nodeName == 'LI') {
sfMenu.getTextNode(this.sfMenu.childNodes[i]).style.width = nWidth;
this.sfMenu.childNodes[i].style.width = sWidth;
}
}
}
this.sfMenu.sfRealWidth = true;
}
//check orientation flags and set position accordingly
if(this.sfMenuType & sfMenu.RIGHT) {
left += d.offsetWidth;
}
else if(this.sfMenuType & sfMenu.LEFT) {
left -= this.sfMenu.offsetWidth;
}
if(this.sfMenuType & sfMenu.TOP) {
top -= (this.sfMenuRoot)? this.sfMenu.offsetHeight: this.sfMenu.offsetHeight - this.offsetHeight;
}
else if(this.sfMenuType & sfMenu.BOTTOM) {
top += (this.sfMenuRoot)? d.offsetHeight: 0;
}
//beyond page boundaries, so adjust accordingly
if(top + this.sfMenu.offsetHeight > docHeight)
top = docHeight - this.sfMenu.offsetHeight;
if(left + this.sfMenu.offsetWidth > docWidth) {
var nleft = sfMenu.getLeft(d) - this.sfMenu.offsetWidth;
left = (nleft >= 0)? nleft: left;
}
if(this.sfMenuType & sfMenu.LEFT && left - this.sfMenu.offsetWidth < 0)
left = sfMenu.getLeft(d) + d.offsetWidth;
if(this.sfMenuType & sfMenu.TOP && top - this.sfMenu.offsetHeight < 0)
top = sfMenu.getTop(d) + d.offsetWidth;
//put the sub-menu where it should be and make it visible
this.sfMenu.style.left = left + 'px';
this.sfMenu.style.top = top + 'px';
this.sfMenu.style.visibility = 'visible';
},
//the hide function for floating menu
// if sfMenu.hideEvent = n hide all sub-menus (below d if set)
hide: function(n, d) {
if(sfMenu.hideEvent == n) {
var p, c;
if(d) {
p = d.parentNode.parentNode;
c = d.sfMenu;
} else
sfMenu.hideEvent = 0;
for(var i = sfMenu.current.length-1; i >= 0; i--) {
var node = sfMenu.current[i];
if(node.menu != p && node.menu != c) {
node.item.style.display = 'none';
var index = node.item.getAttribute('sfmenu:index');
var id = node.item.getAttribute('sfmenu:id');
var nlist = [document.getElementById(index+id), document.getElementById(index+'Left'+id), document.getElementById(index+'Right'+id)];
for(var i = 0, n = nlist.length; i < n; i++) {
var cls = nlist[i].getAttribute('sf:object');
nlist[i].className = nlist[i].className.replace(cls+'MouseOver', cls);
}
sfMenu.current.pop();
} else
break;
}
}
}
}
// $Revision: 3942 $
// $HeadURL: svn://3d3-p432/ShopFactory/trunk/bin/Templates/Website/shared_files/sfmenu.js $

