[fix] DotNetNuke SolPart menu positioning fix for Firefox

The admin dropdown menus in our DNN 3.1.1 sites haven't been working in FireFox - they show up in the upper left hand corner of the screen (absolutely positioned at 0,0). A javascript change (in spmenu.js) fixed it for us.

I'm not sure if this somehow works for everyone else, or if DNN people don't use FireFox, or what. I checked the latest release (3.2 / 4.2) and they don't appear to have this fix, so here it is. I'm going to submit it as a bug and suggested fix to the DNN forums, too.

The problem is setting positions via javascript DOM manipulation without specifying the units:

oMenu.style.top = 100; //bad - no units specified
oMenu.style.top = 100 + 'px'//good - units specified

The best I can tell, the units should be required, so FireFox is being correct in ignoring the improper settings. Anyhow, here's my altered spmenu.js. Of course, the original copyright applies, this is just a patch fix.

//------------------------------------------------------//
// Solution Partner's ASP.NET Hierarchical Menu Control //
// Copyright (c) 2002-2005                              //
// Jon Henning - Solution Partner's Inc                 //  
// jhenning@solpart.com   -   http://www.solpart.com    //
// Compatible Menu Version:  <Min: 1400>             //
//                           <Max: 1.6.1.0>             //
// <Script Version: 1610>                               //
//------------------------------------------------------//
var m_oSolpartMenu;
if (m_oSolpartMenu == null)
    m_oSolpartMenu = 
new Array(); //stores all menu objects (SolpartMenu) in array 
var m_spm_sBrowser;
var m_spm_sVersion;
function spm_initMyMenu(oXML, oCtl)   
//Creates SolpartMenu object and calls generate method
{

  m_oSolpartMenu[oCtl.id] = 
new SolpartMenu(oCtl);
  m_oSolpartMenu[oCtl.id].GenerateMenuHTML(oXML);
 
}
  
//------- Constructor -------//
function SolpartMenu(o)
{
__db(o.id + 
' - constructor');
//  var me = this;  //allow attached events to reference this
  //--- Data Properties ---//
  
this.systemImagesPath=spm_getAttr(o, 'SysImgPath''');  
  
this.iconImagesPath=spm_getAttr(o, 'IconImgPath'this.systemImagesPath);
  
  
this.xml = spm_getAttr(o, 'XML''');
  
this.xmlFileName = spm_getAttr(o, 'XMLFileName''');

  
//--- Appearance Properties ---//
  
this.fontStyle=spm_getAttr(o, 'FontStyle''font-family: arial;');
  
this.backColor=spm_getAttr(o, 'BackColor');  
  
this.foreColor=spm_getAttr(o, 'ForeColor');
  
this.iconBackColor=spm_getAttr(o, 'IconBackColor');
  
this.hlColor=spm_getAttr(o, 'HlColor''');
  
this.shColor=spm_getAttr(o, 'ShColor'''); 
  
this.selColor=spm_getAttr(o, 'SelColor');
  
this.selForeColor=spm_getAttr(o, 'SelForeColor');
  
this.selBorderColor=spm_getAttr(o, 'SelBorderColor');
  
this.menuAlignment = spm_getAttr(o, 'MenuAlignment''Left');
  
this.display=spm_getAttr(o, 'Display''horizontal');
  
this.MBLeftHTML=spm_getAttr(o, 'MBLHTML''');
  
this.MBRightHTML=spm_getAttr(o, 'MBRHTML''');

  
this.rootArrow = spm_getAttr(o, 'RootArrow''0');
  
this.rootArrowImage = spm_getAttr(o, 'RootArrowImage''');
  
this.arrowImage = spm_getAttr(o, 'ArrowImage''');
  
this.backImage=spm_getAttr(o, 'BackImage''');

    
this.supportsTransitions = spm_getAttr(o, 'SupportsTrans''0');

  
//--- Transition Properteis ---//
  //this.menuEffectsStyle=spm_getAttr(o, 'MenuEffectsStyle', '');
  
this.menuTransitionLength=spm_getAttr(o, 'MenuTransitionLength', .3);
  
this.menuTransition=spm_getAttr(o, 'MenuTransition''None');
  
this.menuTransitionStyle=spm_getAttr(o, 'MenuTransitionStyle''');
  
this.SolpartMenuTransitionObject = new SolpartMenuTransitionObject();
  
  
//--- Behavior Properteis ---//
  
this.moveable = spm_getAttr(o, 'Moveable''0');
  
this.moDisplay=spm_getAttr(o, 'MODisplay''HighLight');
  
this.moExpand=spm_getAttr(o, 'MOExpand', "-1");
  
this.moutDelay=spm_getAttr(o, 'MOutDelay', "0");
  
this.minDelay=spm_getAttr(o, 'MInDelay', "0");
  
this.minDelayType=null;
    
this.minDelayTimer=null;
    
this.minDelayObj=null;
      
  
if (spm_browserType() == 'safari')    //safari has issues with mouseoutdelay...
        
this.moutDelay = 5000;
        
  
this.target=spm_getAttr(o, 'target', "");
  
this.moScroll=spm_getAttr(o, 'MOScroll', "-1");

  
//--- Sizing Properties ---//
  
this.menuBarHeight=spm_fixUnit(spm_getAttr(o, 'MenuBarHeight''0'));
  
this.menuItemHeight=spm_fixUnit(spm_getAttr(o, 'MenuItemHeight''0'));
  
this.iconWidth=spm_fixUnit(spm_getAttr(o, 'IconWidth''0'));
  
this.borderWidth=spm_getAttr(o, 'BorderWidth''1');

  
//--- CSS Properties ---//
  
this.cssMenuContainer=spm_getAttr(o, 'CSSMenuContainer''');
  
this.cssMenuBar=spm_getAttr(o, 'CSSMenuBar''');
  
this.cssMenuItem=spm_getAttr(o, 'CSSMenuItem''');
  
this.cssMenuIcon=spm_getAttr(o, 'CSSMenuIcon''');
  
this.cssSubMenu=spm_getAttr(o, 'CSSSubMenu''');
  
this.cssMenuBreak=spm_getAttr(o, 'CSSMenuBreak''');
  
this.cssMenuItemSel=spm_getAttr(o, 'CSSMenuItemSel''');
  
this.cssMenuArrow=spm_getAttr(o, 'CSSMenuArrow''');
  
this.cssMenuRootArrow=spm_getAttr(o, 'CSSRootMenuArw''');
  
this.cssMenuScrollItem=spm_getAttr(o, 'CSSScrollItem''');

    
//for right to left (rtl) menus
    
this.direction = spm_getCurrentStyle(document.body, 'direction');

    
this.useIFrames=(spm_getAttr(o, 'useIFrames''1') != '0' && spm_supportsIFrameTrick());    
    
    
this.delaySubmenuLoad=(spm_getAttr(o, 'delaySubmenuLoad''0') != '0' && spm_needsSubMenuDelay());    
    
  
  
//---- methods ---//
  //this.GenerateMenuHTML=__GenerateMenuHTML;

  //----- private ----//
  
this._m_sNSpace = o.id;               //stores namespace for menu
  
this._m_sOuterTables = '';            //stores HTML for sub menus
  
this._m_oDOM;                         //stores XML DOM object
    
this._m_oMenu = o;                    //stores container
  
this._m_oMenuMove;                    //stores control that is used for moving menu
  
  
this._m_oTblMenuBar;                  //stores menu container
    
this._m_aOpenMenuID = new Array();      //stores list of menus that are currently displayed
    
this._m_bMoving=false;                //flag to determine menu is being dragged
  
this._m_dHideTimer = null;            //used to time when mouse out occured to auto hide menu based on mouseoutdelay
  
this._m_oScrollingMenu = null;                //used in scrolling menu on mouse over

    //--- Exposed Events ---//
/*
    this.onMenuComplete=spm_getAttr(o, 'OnMenuComplete', null);                        //fires once menu is done loading
    this.onMenuBarClick=spm_getAttr(o, 'OnMenuBarClick', null);                        //fires once menu bar is clicked
    this.onMenuItemClick=spm_getAttr(o, 'OnMenuItemClick', null);         //fires once menu item is clicked
    this.onMenuBarMouseOver=spm_getAttr(o, 'OnMenuBarMouseOver', null);        //fires once mouse moves over menu bar
    this.onMenuBarMouseOut=spm_getAttr(o, 'OnMenuBarMouseOut', null);            //fires once mouse moves out of menu bar
    this.onMenuItemMouseOver=spm_getAttr(o, 'OnMenuItemMouseOver', null);    //fires once mouse moves over menu item
    this.onMenuItemMouseOut=spm_getAttr(o, 'OnMenuItemMouseOut', null);        //fires once mouse moves out of menu bar
*/

//--- Menu Moving currently disabled ---//
/*
  this._menuhook_MouseMove=__menuhook_MouseMove;
  this._menuhook_MouseDown=__menuhook_MouseDown;
  this._menuhook_MouseUp=__menuhook_MouseUp;
  this._document_MouseMove=__document_MouseMove;
  this._document_MouseDown=__document_MouseDown;
  this._document_MouseUp=__document_MouseUp;
  this._bodyclick=__bodyclick;

  this.menuhook_MouseMove=function(e) {me._menuhook_MouseMove(e);};
  this.menuhook_MouseDown=function(e) {me._menuhook_MouseDown(e);};
  this.menuhook_MouseUp=function(e) {me._menuhook_MouseUp(e);};
  this.document_MouseMove=function(e) {me._document_MouseMove(e);};
  this.document_MouseDown=function(e) {me._document_MouseDown(e);};
  this.menuhook_MouseUp=function(e) {me._menuhook_MouseUp(e);};
  this.bodyclick=function() {me._bodyclick();};
*/ 
__db(this._m_oMenu.id + ' - constructor end');

}

//--- Destroys interrnal object references ---//
SolpartMenu.prototype.destroy = function ()
{
  
this.systemImagesPath = null;  
  
this.iconImagesPath = null;
  
this.xml = null;
  
this.xmlFileName = null;

  
//--- Appearance Properties ---//
  
this.fontStyle = null;
  
this.backColor = null;  
  
this.foreColor = null;
  
this.iconBackColor = null;
  
this.hlColor = null;
  
this.shColor = null
  
this.selColor = null;
  
this.selForeColor = null;
  
this.selBorderColor = null;
  
this.menuAlignment = null;
  
this.display = null;

  
this.rootArrow = null;
  
this.rootArrowImage = null;
  
this.arrowImage = null;
  
this.backImage = null;

  
//--- Transition Properteis ---//
  //this.menuEffectsStyle = null;
  
this.menuTransitionLength = null;
  
this.menuTransition = null;
  
this.SolpartMenuTransitionObject = null;
  
  
//--- Behavior Properteis ---//
  
this.moveable = null;
  
this.moDisplay = null;
  
this.moExpand = null;
  
this.moutDelay = null;

  
//--- Sizing Properties ---//
  
this.menuBarHeight = null;
  
this.menuItemHeight = null;
  
this.iconWidth = null;
  
this.borderWidth = null;

  
//--- CSS Properties ---//
  
this.cssMenuContainer = null;
  
this.cssMenuBar = null;
  
this.cssMenuItem = null;
  
this.cssMenuIcon = null;
  
this.cssSubMenu = null;
  
this.cssMenuBreak = null;
  
this.cssMenuItemSel = null;
  
this.cssMenuArrow = null;
  
this.cssMenuRootArrow = null;
  
  
//---- methods ---//
  //this.GenerateMenuHTML=__GenerateMenuHTML = null;

  //----- private ----//
  
m_oSolpartMenu[this._m_sNSpace] = null;

  
this._m_sNSpace = null;                 //stores namespace for menu
  
this._m_sOuterTables = null;            //stores HTML for sub menus
  
this._m_oDOM = null;                    //stores XML DOM object
    
this._m_oMenu = null;                   //stores container
  
this._m_oMenuMove = null;               //stores control that is used for moving menu
  
  
this._m_oTblMenuBar = null;             //stores menu container
    
this._m_aOpenMenuID = null;                //stores list of menus that are currently displayed
    
this._m_bMoving = null;                 //flag to determine menu is being dragged
  
this._m_dHideTimer = null;              //used to time when mouse out occured to auto hide menu based on mouseoutdelay
  
this._m_oScrollingMenu = null;                    //used in scrolling menu on mouse over
  
}

//--- static/shared members ---//
/*
SolpartMenu.prototype.menuhook_MouseMove=__menuhook_MouseMove;
SolpartMenu.prototype.menuhook_MouseDown=__menuhook_MouseDown;
SolpartMenu.prototype.menuhook_MouseUp=__menuhook_MouseUp;

SolpartMenu.prototype.document_MouseMove=__document_MouseMove;
SolpartMenu.prototype.document_MouseDown=__document_MouseDown;
SolpartMenu.prototype.document_MouseUp=__document_MouseUp;
*/

//--- xml document loaded (non-dataisland) ---//
SolpartMenu.prototype.onXMLLoad = function ()
{
  
this.GenerateMenuHTML(this._m_oDOM);
}

//--- Generates menu HTML through passed in XML DOM ---//
SolpartMenu.prototype.GenerateMenuHTML = function (oXML) 
{
__db(
this._m_oMenu.id + ' - GenerateMenuHTML');
    
//'Generates the main menu bar
  
var sHTML = '';
  
this._m_sOuterTables = '';
  
//this._m_oMenu.insertAdjacentElement('beforeBegin', );

  
    //if (oXML.readyState != 'complete')
    //    return;

    
if (oXML == null)
    {
      
if (this._m_oDOM == null)
      {
        oXML = spm_createDOMDoc();
//document.implementation.createDocument("", "", null);
        
this._m_oDOM = oXML;
              
        
if (this.xml.length)
          oXML.loadXML(
this.xml);
        
        
if (this.xmlFileName.length)
        {
          oXML.onload = eval(
'onxmlload' this._m_sNSpace); //'m_oSolpartMenu["' + this._m_sNSpace + '"].onXMLLoad'; this.onXMLLoad;
          
oXML.load(this.xmlFileName);
          
return//async load
        
}
    }
    }
    
else
      this
._m_oDOM = oXML;

  
if (this.display == "vertical")
  {
      sHTML += 
'<table ID="tbl' this._m_sNSpace + 'MenuBar" CELLPADDING=\'0\' CELLSPACING=\'0\' BORDER="0" CLASS="' + spm_fixCSSForMac(this.getIntCSSName('spmbctr') + this.cssMenuContainer) + '" HEIGHT="100%" STYLE="vertical-align: center;">\n';    //removed position: relative;  for IE and display: block; for Opera
      
sHTML += MyIIf(this.MBLeftHTML.length, '<tr>\n       <td>' this.MBLeftHTML + '</td>\n</tr>\n''');
      sHTML += MyIIf(Number(
this.moveable), '<tr>\n       <td ID="td' this._m_sNSpace + 'MenuMove" height=\'3px\' style=\'cursor: move; ' + spm_getMenuBorderStyle(this) + '\'>' + spm_getSpacer(this) + '</td>\n</tr>\n''');
      sHTML +=         
this.GetMenuItems(this._m_oDOM.documentElement);
      sHTML += 
'       <tr><td HEIGHT="100%">' + spm_getSpacer(this) + '</td>\n' ;
      sHTML += 
'   </tr>\n';
      sHTML += MyIIf(
this.MBRightHTML.length, '<tr>\n       <td>' this.MBRightHTML + '</td>\n</tr>\n''');
      sHTML += 
'</table>\n';
  }
  
else
  
{
      sHTML += 
'<table ID="tbl' this._m_sNSpace + 'MenuBar" CELLPADDING=\'0\' CELLSPACING=\'0\' BORDER="0" CLASS="' + spm_fixCSSForMac(this.getIntCSSName('spmbctr') + this.cssMenuContainer) + '" WIDTH="100%" STYLE="vertical-align: center; ">\n';    //removed position: relative;  for IE and display: block; for Opera
      
sHTML += '    <tr>\n';
      sHTML += MyIIf(
this.MBLeftHTML.length, '<td>' this.MBLeftHTML + '</td>\n''');
      sHTML += MyIIf(Number(
this.moveable), '       <td ID="td' this._m_sNSpace + 'MenuMove" width=\'3px\' style=\'cursor: move; ' + spm_getMenuBorderStyle(this) + '\'>' + spm_getSpacer(this) + '</td>\n''');
      sHTML += spm_getMenuSpacingImage(
'left'this);
      sHTML +=         
this.GetMenuItems(this._m_oDOM.documentElement);
      sHTML += spm_getMenuSpacingImage(
'right'this);
      sHTML += MyIIf(
this.MBRightHTML.length, '<td>' this.MBRightHTML + '</td>\n''');
      sHTML += 
'   </tr>\n';
      sHTML += 
'</table>\n';
  }

/*    
    if (spm_browserType() == 'op')
    {
        this._m_oMenu.innerHTML = sHTML;
        var oDiv = document.createElement('div');
        oDiv.innerHTML = this._m_sOuterTables;
        document.body.appendChild(oDiv);
    }
    else  
*/
        //sHTML = '<SPAN>' + this._m_sOuterTables + '</SPAN>' + sHTML;
        //this._m_sOuterTables = '';
    
    
    
this._m_oMenu.innerHTML = sHTML;

    
this.GenerateSubMenus();

    
//this._m_oMenu.style.height = '100%';

    
  
this._m_oMenuMove = spm_getById('td' this._m_sNSpace + 'MenuMove');

/*
  //--- attach events for menu moving ---//
  if (Number(this.moveable))
  {
    var oCtl = this._m_oMenuMove;  //this._m_oMenu
    oCtl.onmousedown = this.menuhook_MouseDown;
    oCtl.onmouseup = this.menuhook_MouseUp;
    oCtl.onmousemove = this.menuhook_MouseMove;

    if (spm_browserType() == 'ie')
    {
      document.onmousemove = this.document_MouseMove;
      document.onmousedown = this.document_MouseDown;
      //spm_getTags("BODY")[0].onclick = this.bodyclick;
      spm_getTags("BODY")[0].attachEvent('onclick', this.bodyclick);
    }
    else
    {
        window.addEventListener("click", this.bodyclick, true);
        window.addEventListener("mousemove", this.document_MouseMove, true);
        window.addEventListener("mousedown", this.document_MouseDown, true);
        window.addEventListener("mouseup", this.document_MouseUp, true);
    }

  }
*/
  //if (spm_browserType() == 'ie')
        
spm_getTags("BODY")[0].onclick = spm_appendFunction(spm_getTags("BODY")[0].onclick, 'm_oSolpartMenu["' this._m_sNSpace + '"].bodyclick();'); //document.body.onclick = this.bodyclick;
    //else
    //    window.addEventListener("click", this.bodyclick, true);

  
this._m_oTblMenuBar = spm_getById('tbl' this._m_sNSpace + 'MenuBar'); //this._m_oMenu
  
  
this.fireEvent('onMenuComplete');

__db(
this._m_oMenu.id + ' - GenerateMenuHTML end');    
}

SolpartMenu.prototype.GenerateSubMenus = function (oXML) 
{
    
if (this._m_sOuterTables.length > 0)
    {
            var oDiv = spm_getById(
this._m_sNSpace + '_divOuterTables');
            
if (oDiv == null)
            {
                alert(
'It appears that your menu dll is out of sync with your script file.');
                
return;
            }
            
            
if (this.delaySubmenuLoad != '0' && document.readyState != 'complete')
                
return;
                            
            oDiv.innerHTML = 
this._m_sOuterTables;
            
    }
    
this._m_sOuterTables = '';
}

function spm_getMenuBarEvents(sCtl)
{
  
return 'onmouseover="m_oSolpartMenu[\'' + sCtl + '\'].onMBMO(this);" onmouseout="m_oSolpartMenu[\'' + sCtl + '\'].onMBMOUT(this);" onclick="m_oSolpartMenu[\'' + sCtl + '\'].onMBC(this, event);" onmousedown="m_oSolpartMenu[\'' + sCtl + '\'].onMBMD(this);" onmouseup="m_oSolpartMenu[\'' + sCtl + '\'].onMBMU(this);"';
}

function spm_getMenuItemEvents(sCtl)
{
  
return 'onmouseover="m_oSolpartMenu[\'' + sCtl + '\'].onMBIMO(this);" onmouseout="m_oSolpartMenu[\'' <