January 2006 - Posts - Jon Galloway

January 2006 - Posts

[DotNetNuke] Eagerly awaiting the release of the DotNetNuke Store Module...

UPDATE: DotNetNuke Store (BETA) has been released. Get it here.

Ah, I'm too excited to wait for the release to post about this. The DotNetNuke Store Module is due out any day now.

The DNN Store out of the box allows you to sell products using either Authorize.Net or PayPal.

Features include categories, products (list and details), mini cart (with integrated controls), full shopping cart (including multiple ship/bill to addresses/recipients), reviews (including a review admin for approving reviews), order history (customer and admin viewable), and provider based gateways.

This is good news to me. I've been helping my wife set up a store based on the ZenCart branch of OSCommerce to save a few bucks on PHP / mySQL hosting, and I'm done with printf debugging...  

Join me in the "Are we there yet? Are we there yet? Are we there yet?" forum...

Posted by Jon Galloway | 1 comment(s)
Filed under:

[tip] Test command line settings with EchoArgs 9.78 PRO

Occasionally, applications will allow you to call external helper programs via command line. That can get tricky when you're supplying a command line template and the application does some token replacement before running the commandline. For example, when I hooked up SourceGear Vault to WinMerge, the trick was in figuring out which tokens it was replacing and how those mapped to WinMerge's commandline arguments. So if my commandline setting is /dl "%WORKING_LABEL%" /dr "%OTHER_LABEL%" "%WORKING_PATH%" "%OTHER_PATH%" "%DEST_PATH%", what exactly is being passed for "WORKING_LABEL" and "OTHER_LABEL"?

Thus, I proudly release EchoArgs 9.78 PRO, the following (ahem) DOS Batch File:

TITLE EchoArgs 9.78 PRO
@echo off
echo Command Line: %*
echo Path :        %~dp0
echo .
echo Arguments 
echo 0: %0
echo 1: %1
echo 2: %2 
echo 3: %3
echo 4: %4
echo 5: %5
echo 6: %6
echo 7: %7
echo 8: %8
echo 9: %9
pause

Here's what it does:

C:\>echoargs test "inscrutable monkey" 5 geronimo

C
:\>TITLE EchoArgs 9.78 PRO
Command Line
: test "inscrutable monkey" 5 geronimo
Path 
:        C:\
.
Arguments
0: echoargs
1
: test
2
: "inscrutable monkey"
3
: 5
4
: geronimo
5
:
6
:
7
:
8
:
9
:
Press any key to continue 
. . .

 

Of course, that doesn't tell us anything we didn't already know. The point is that you'd use it to test the commandline settings by temporarily setting echoargs.bat as my external diff / merge tool (in this example) and seeing what's actually going to be passed to the real application when I've got my arguments straight.

 

Posted by Jon Galloway | with no comments
Filed under: ,

[tip] Use WinMerge for compare / merge in SourceGear Vault

I previously blogged about how to use WinMerge as the diff tool in StarTeam. Diff / Merge against source control is something I do very frequently, so I think it's worth the few minutes to use the best possible tool. I've been a fan of WinMerge for a while, so when I started using SourceGear Vault this post was pretty much inevitable.

So, here's how you set this up:

  1. Download WinMerge. I know it sounds kind of crazy to use experimental builds of a diff / merge tool, but these guys have been putting out very high quality releases for years and their betas are better than most finished products, so if you're feeling lucky go with the latest experimental build.
  2. In Vault: Tools > Options > Diff/Merge tab.
  3. Diff Program and Merge Program fields: Path to your WinMerge.exe (or WinMergeU.exe if you need Unicode support). Default is C:\Program Files\WinMerge\WinMerge.exe\
  4. Diff Arguments: /dl "%LEFT_LABEL%" /dr "%RIGHT_LABEL%"  "%LEFT_PATH%" "%RIGHT_PATH%"
  5. Merge Arguments: /dl "%WORKING_LABEL%" /dr "%OTHER_LABEL%" "%WORKING_PATH%" "%OTHER_PATH%" "%DEST_PATH%"

More info on the WinMerge commandline args here.
More info on the Vault Diff/Merge Options here.

Oh, and one other Vault tip - use CRC's to determine changed files when working in CVS mode. Otherwise I end up with tons of files that say they're changed, but aren't. You can set that via Tools > Options > Local Files > Check "Detect Modified Files using CRCs instead of modification times"

Posted by Jon Galloway | 2 comment(s)
Filed under:

[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[\'' + sCtl + '\'].onMBIMOUT(this);" onclick="m_oSolpartMenu[\'' + sCtl + '\'].onMBIC(this, event);"';
}

//--- Returns HTML for menu items (recursive function) ---//
SolpartMenu.prototype.GetMenuItems = function (oParent)
{
  var oNode;
  var sHTML = 
'';
  var sID;
  var sParentID;
  var sClickAction;
  
    
for (var i = 0; i < oParent.childNodes.length; i++)
    {
        oNode = oParent.childNodes[i];

        
if (oNode.nodeType != 3 && oNode.nodeType != 8)  //exclude nodeType of Text (Netscape/Mozilla) issue!
        
{
          
//'determine if root level item and set parent id accordingly
          
if (oNode.parentNode.nodeName != "menuitem")
              sParentID = "-1";
          
else
              
sParentID = oNode.parentNode.getAttribute("id");

          
if (oNode.nodeName == "menuitem")
              sID = oNode.getAttribute("id");
          
else
              
sID = "";


  __db(sID + 
' getmenuitems');
            sClickAction = spm_getMenuClickAction(oNode, 
this);


          
if (sParentID == "-1")    //'if top level menu item
          
{
        
              
if (this.display == "vertical")
                  sHTML += "<tr>\n"; 
//'if vertical display then add rows for each top menuitem
              
              
if (oNode.nodeName == 'menubreak')
              {
                    
if (this.display == "vertical")
                        sHTML += "<tr>\n"; 
//'if vertical display then add rows for each top menuitem

                     
var sBreakHTML = spm_getAttr(oNode, 'lefthtml''') + spm_getAttr(oNode, 'righthtml''');
                     
if (sBreakHTML.length > 0)
                         sHTML += 
'   <td class="' + spm_fixCSSForMac(this.getIntCSSName('spmbrk') + this.cssMenuBreak) + '">' + sBreakHTML + '</td>\n';
                     
else
                         
sHTML += '   <td class="' + spm_fixCSSForMac(this.getIntCSSName('spmbrk') + this.cssMenuBreak) + '">' + spm_getMenuImage('spacer.gif'thistrue' ') + '</td>\n';
//                         sHTML += '   <td style="height: 1px" class="' + spm_fixCSSForMac(this.getIntCSSName('spmicn') + this.cssMenuIcon) + '">' + spm_getMenuImage('spacer.gif', this, true, ' ') + '</td>\n<td colspan="2" class="' + spm_fixCSSForMac(this.getIntCSSName('spmbrk') + this.cssMenuBreak) + '">' + spm_getMenuImage('spacer.gif', this, true, ' ') + '</td>\n';

                    
if (this.display == "vertical")
                        sHTML += "</tr>\n";
              }
              
else
              
{
                    sHTML += 
'<td>\n<table width="100%" CELLPADDING="0" CELLSPACING="0" border="0">\n<tr id="td' this._m_sNSpace + sID + '" ' + spm_getMenuBarEvents(this._m_sNSpace) + '  class="' + spm_fixCSSForMac(this.getIntCSSName('spmbar spmitm') + this.cssMenuBar + ' ' this.cssMenuItem + ' ' + spm_getMenuItemCSS(oNode)) + '" savecss="' + spm_getMenuItemCSS(oNode) + '" saveselcss="' + spm_getMenuItemSelCSS(oNode) + '" menuclick="' + sClickAction + '" style="' + spm_getMenuItemStyle('item', oNode) + '">\n';
                    var sAlign = 
this.display=='vertical' 'align="' this.menuAlignment + '"' '';
                    sHTML += 
'<td unselectable="on" NOWRAP="NOWRAP" ' + sAlign + ' TITLE="' + spm_getAttr(oNode, 'tooltip''') + '">' + spm_getImage(oNode, this) + spm_getItemHTML(oNode, 'left''&nbsp;') + spm_getAttr(oNode, 'title''') + spm_getItemHTML(oNode, 'right') + MyIIf(Number(this.rootArrow) && spm_nodeHasChildren(oNode), '</td>\n<td align="right" class="' + spm_fixCSSForMac(this.getIntCSSName('spmrarw') + this.cssMenuRootArrow) + '">' + spm_getArrow(this.rootArrowImage, this) + "", '&nbsp;') + '\n</td>\n</tr>\n</table>\n</td>\n';
                }
                          
              
if (this.display == "vertical")
                  sHTML += "</tr>\n";
          
         
          }
          
else                        //'submenu - not top level menu item
          
{
              
switch(oNode.nodeName)
              {
                  
case "menuitem":
                  {
                      sHTML +=        
'   <tr ID="tr' this._m_sNSpace + sID + '" ' + spm_getMenuItemEvents(this._m_sNSpace) + ' parentID="' + sParentID + '" class="' + spm_fixCSSForMac(this.getIntCSSName('spmitm') + this.cssMenuItem + ' ' + spm_getMenuItemCSS(oNode)) + '" savecss="' + spm_getMenuItemCSS(oNode) + '" saveselcss="' + spm_getMenuItemSelCSS(oNode) + '" menuclick="' + sClickAction + '" style="' + spm_getMenuItemStyle('item', oNode) + '">\n';
                      sHTML +=        
'       <td unselectable="on" id="icon' this._m_sNSpace + sID + '" class="' + spm_fixCSSForMac(this.getIntCSSName('spmicn') + this.cssMenuIcon) + '" style="' + spm_getMenuItemStyle('image', oNode) + '; ' + spm_getMenuItemStyle('item', oNode) + '">' + spm_getImage(oNode, this) + '</td>\n';
                      sHTML +=        
'       <td unselectable="on" id="td' this._m_sNSpace + sID + '" class="' + spm_fixCSSForMac(this.getIntCSSName('spmitm') + this.cssMenuItem + ' ' + spm_getMenuItemCSS(oNode)) + '" savecss="' + spm_getMenuItemCSS(oNode) + '" NOWRAP="NOWRAP" TITLE="' + spm_getAttr(oNode, 'tooltip''') + '" style="' + spm_getMenuItemStyle('item', oNode) + '">' + spm_getItemHTML(oNode, 'left''') + spm_getAttr(oNode, 'title''') + spm_getItemHTML(oNode, 'right''') + '</td>\n';
                      sHTML +=        
'       <td unselectable="on" id="arrow' this._m_sNSpace + sID + '" width="15px" CLASS="' + spm_fixCSSForMac(this.getIntCSSName('spmarw') + this.cssMenuArrow) + '" style="' + spm_getMenuItemStyle('item', oNode) + '">' + MyIIf(spm_nodeHasChildren(oNode), spm_getArrow(this.arrowImage, this), spm_getSpacer(this)) + '</td>\n';
                      sHTML +=        
'   </tr>\n';

                      
break;
                  }
                  
case "menubreak":
                  {
                         var sBreakHTML = spm_getAttr(oNode, 
'lefthtml''') + spm_getAttr(oNode, 'righthtml''');
                         
if (sBreakHTML.length > 0)
                            sHTML += 
'   <tr><td colspan="3" class="' + spm_fixCSSForMac(this.getIntCSSName('spmbrk') + this.cssMenuBreak) + '">' + sBreakHTML + '</td>\n</tr>\n';
                         
else
                            
sHTML += '   <tr>\n<td style="height: 1px" class="' + spm_fixCSSForMac(this.getIntCSSName('spmicn') + this.cssMenuIcon) + '">' + spm_getMenuImage('spacer.gif'thistrue' ') + '</td>\n<td colspan="2" class="' + spm_fixCSSForMac(this.getIntCSSName('spmbrk') + this.cssMenuBreak) + '">' + spm_getMenuImage('spacer.gif'thistrue' ') + '</td>\n</tr>\n';

                      
break;
                  }
              }
          }

          
//'Generate sub menu - note: we are recursively calling ourself
          //'netscape renders tables with display: block as having cellpadding!!! therefore using div outside table - LAME!
          
if (oNode.childNodes.length > 0)
          {
                var sTag = 
'DIV';
                var sStyle = 
'';

                
if (spm_isMac('ie'))
                {
                    sTag = 
'P';
                    sStyle = 
'margin-top:0px; margin-left:0px;'
                
}
              
this._m_sOuterTables = '\n<' + sTag + ' ID="tbl' this._m_sNSpace + sID + '" CLASS="' + spm_fixCSSForMac(this.getIntCSSName('spmsub') + this.cssSubMenu) + '" STYLE="display:none; position: absolute;' + sStyle + this.menuTransitionStyle + '">\n<table CELLPADDING="0" CELLSPACING="0" BORDER="0">\n' this.GetMenuItems(oNode) + '\n</table>\n</' + sTag + '>\n' this._m_sOuterTables;
            }

    }
    }
    
return sHTML;
}

    
//--------------- Event Functions ---------------//
  //--- menubar click event ---//
    
SolpartMenu.prototype.onMBC = function (e, evt)
    {
        
this.GenerateSubMenus();

        var oCell = e; 
//event.srcElement;
        
var sID = oCell.id.substr(2);

        var oMenu = spm_getById("tbl" + sID);
    
//var oMenu = spm_getById("td" + sID);
        
        
if (oMenu != null)
        {
            
this.hideAllMenus();        //mindelay mod
            
if (oMenu.style.display == '')
            {
                
this.hideAllMenus();        
                
if (this.useIFrames)
                    spm_iFrameIndex(oMenu, 
falsethis.systemImagesPath);
                
else
                    
spm_showElement("SELECT|OBJECT");
            }
            
else
            
{
                spm_positionMenu(
this, oMenu, oCell);
                
                
this.doTransition(oMenu);
                oMenu.style.display = "";
                
this._m_aOpenMenuID[0] = sID;
                
if (this.useIFrames)
                    spm_iFrameIndex(oMenu, 
truethis.systemImagesPath);
                
else
                    
spm_hideElement("SELECT|OBJECT",oMenu);
            }
        }
        
    
this.fireEvent('onMenuBarClick', oCell);
    
    oMenu = spm_getById("td" + sID);
    
if (spm_getAttr(oMenu, "menuclick", '').length)
    {
      eval(spm_getAttr(oMenu, "menuclick", 
''));
      
this.hideAllMenus();
    }
        spm_stopEventBubbling(evt);
    }
    
  
//--- menubar mousedown event ---//
    
SolpartMenu.prototype.onMBMD = function (e)
    {
        var oCell = e; 
//event.srcElement;
        
this.applyBorder(oCell, 1, this.shColor, this.hlColor);
    }
  
  
//--- menubar mouseup event ---//
    
SolpartMenu.prototype.onMBMU = function (e)
    {
        var oCell = e; 
//event.srcElement;
        
this.applyBorder(oCell, 1, this.hlColor, this.shColor);
    }
  
  
//--- menubar mouseover event ---//
    
SolpartMenu.prototype.onMBMO = function (e, bBypassDelay)
    {
        
this.GenerateSubMenus();
        var oCell = e; 
//event.srcElement;
        
        
if (oCell.id.length == 0) //cancelBubble
          
return;
        var sID = oCell.id.substr(2);
        var oMenu = spm_getById("tbl" + sID);

        
if (this._m_aOpenMenuID.length || this.moExpand != '0')
        {
            
if (this.minDelay != 0 && bBypassDelay != true)
            {
                
if (this.minDelayTimer != null)
                    window.clearTimeout(
this.minDelayTimer);
                
this.minDelayType = 'root';
                
this.minDelayObj = e;
                
this.minDelayTimer = setTimeout('m_oSolpartMenu["' this._m_sNSpace + '"].mouseInDelayHandler()'this.minDelay);
            }
            
else
            
{
                
//--- if menu is shown then mouseover triggers the showing of all menus ---//
                
this.hideAllMenus();

                
if (oMenu != null)
                {
                    spm_positionMenu(
this, oMenu, oCell);
                    
this.doTransition(oMenu);
                    oMenu.style.display = "";
                    
this._m_aOpenMenuID[0] = sID;
                    
if (this.useIFrames)
                        spm_iFrameIndex(oMenu, 
truethis.systemImagesPath);
                    
else
                        
spm_hideElement("SELECT|OBJECT",oMenu);
                }
            }
            
this.applyBorder(oCell, 1, this.shColor, this.hlColor);
        }
        
else
        
{
            
this.applyBorder(oCell, 1, this.hlColor, this.shColor);
        }

        oCell.className =  spm_fixCSSForMac(
this.getIntCSSName('spmitmsel spmbar') + this.cssMenuBar + ' ' this.cssMenuItemSel + ' ' + spm_getAttr(oCell, 'saveselcss''') + ' ' + spm_getAttr(oCell, 'savecss'''));
        
        
this._m_dHideTimer = null;
        
        
this.fireEvent('onMenuBarMouseOver', oCell);
        
    }
  
//--- menubar mouseout event ---//
    
SolpartMenu.prototype.onMBMOUT = function (e)
    {
        var oCell = e; 
//event.srcElement;
        
var sID = oCell.id.substr(2);
        
this.applyBorder(oCell, 1, spm_getCellBackColor(oCell), spm_getCellBackColor(oCell), "none");    
        
this._m_dHideTimer = new Date();

        
if (this.moutDelay != 0)
          setTimeout(
'm_oSolpartMenu["' this._m_sNSpace + '"].hideMenuTime()'this.moutDelay);
          
    oCell.className = spm_fixCSSForMac(
this.getIntCSSName('spmbar spmitm') + this.cssMenuBar + ' ' this.cssMenuItem + ' ' + spm_getAttr(e, 'savecss'''));
    
this.stopTransition();
    
this.minDelayType = null;
    
this.fireEvent('onMenuBarMouseOut', oCell);
    }
    
  
//--- menuitem click ---//
    
SolpartMenu.prototype.onMBIC = function (e, evt)
    {
        var oRow = spm_getSourceTR(e, 
this._m_sNSpace);  //event.srcElement
        
var sID = oRow.id.substr(2);
        
if (spm_itemHasChildren(sID, this._m_sNSpace) == false)
            
this.hideAllMenus();

        
this.fireEvent('onMenuItemClick', oRow);

    
if (spm_getAttr(oRow, "menuclick", '').length)
    {
      eval(spm_getAttr(oRow, "menuclick", 
''));
      
this.hideAllMenus();
        }
        
//window.event.cancelBubble = true;
        
spm_stopEventBubbling(evt);
        
        
this.handlembi_mo(oRow, true);
    }

  
//--- menuitem mouseover event ---//
    
SolpartMenu.prototype.onMBIMO = function (e)
    {        
        
this.handlembi_mo(spm_getSourceTR(e, this._m_sNSpace)); //event.srcElement

        
this._m_dHideTimer = null;
    }
  
//--- menuitem mouseout event ---//
    
SolpartMenu.prototype.onMBIMOUT = function (e)
    {    
        
this.handlembi_mout(spm_getSourceTR(e, this._m_sNSpace));  //event.srcElement
        
this._m_dHideTimer = new Date;
        
//setTimeout(this.hideMenuTime, this.moutDelay);
        
if (this.moutDelay != 0)
          setTimeout(
'm_oSolpartMenu["' this._m_sNSpace + '"].hideMenuTime()'this.moutDelay);
         
        
this.minDelayType = null;
    }
    
/*
    function menuhook_KeyPress()
    {
    //not yet
    }
    function menuhook_KeyDown()
    {
    //not yet
    }
    
    function menuhook_MenuFocus()
    {
        var tbl = event.srcElement;
        mb_c(tbl.rows[0].cells[0]);
    }
*/
/*    
    function __menuhook_MouseMove(e) 
    {
        var iNewLeft=0, iNewTop = 0

if (this._m_bMoving)
{
            if (spm_browserType() == 'ie')
            {
//        if ((event.button==1)) 
//        {
              this.hideAllMenus();
              if (this._m_oTblMenuBar.startLeft == null)
                  this._m_oTblMenuBar.startLeft = this._m_oTblMenuBar.offsetLeft;
              iNewLeft=event.clientX - this._m_oTblMenuBar.startLeft - 3;
              this._m_oTblMenuBar.style.pixelLeft= iNewLeft;
              if (this._m_oTblMenuBar.startTop == null)
                  this._m_oTblMenuBar.startTop = this._m_oTblMenuBar.offsetTop;
              iNewTop=event.clientY - this._m_oTblMenuBar.startTop;
              this._m_oTblMenuBar.style.pixelTop = iNewTop - 10;
              event.returnValue = false
              event.cancelBubble = true
//      }
        }
    else
    {
            this.hideAllMenus();
          
            if (this._m_oTblMenuBar.startLeft == null)
                this._m_oTblMenuBar.startLeft = this._m_oTblMenuBar.offsetLeft;

            iNewLeft=e.clientX - this._m_oTblMenuBar.startLeft - 3;
              
            //if (iNewLeft&lt;0) 
            //    iNewLeft=0;
          
            this._m_oTblMenuBar.style.left = iNewLeft;
                          
            if (this._m_oTblMenuBar.startTop == null)
                this._m_oTblMenuBar.startTop = this._m_oTblMenuBar.offsetTop;

            iNewTop=e.clientY - this._m_oTblMenuBar.startTop;
            //if (iNewTop&lt;0) 
            //    iNewTop=0;
              
            this._m_oTblMenuBar.style.top = iNewTop - 10;    
    }
}

    }
    function __menuhook_MouseDown()
    {
        this._m_bMoving = true;
    }
    function __menuhook_MouseUp()
    {
      this._m_bMoving = false;
    }
    function __document_MouseMove(e)
    {
        if (this._m_bMoving)
        {
            this.menuhook_MouseMove(e);
      }
    }
    function __document_MouseDown()
    {
        //this._m_bMoving = null;
    }
    function __document_MouseUp()
    {
        this._m_bMoving=false;
    }
*/

    
SolpartMenu.prototype.bodyclick = function()
    {
        
this.hideAllMenus();
    }

  
//--- handles display of newly opened menu ---//
    
SolpartMenu.prototype.handleNewItemSelect = function (sID)
    {
        var i;
        var iNewLength=-1;
        var bDeleteRest=
false
        
for (i=0; i<this._m_aOpenMenuID.length; i++)
        {        
            
if (bDeleteRest)
            {
                spm_getById("tbl" + 
this._m_aOpenMenuID[i]).style.display = "none";
                
if (this.useIFrames)
                    spm_iFrameIndex(spm_getById("tbl" + 
this._m_aOpenMenuID[i]), falsethis.systemImagesPath);
            }
            
if (this._m_aOpenMenuID[i] == this._m_sNSpace + sID)
            {
                bDeleteRest=
true;
                iNewLength = i;
            }                
        }
        
if (iNewLength != -1)
            
this._m_aOpenMenuID.length = iNewLength+1;
    }
    
  
//--- hides all menus that are currently displayed ---//
    
SolpartMenu.prototype.hideAllMenus = function ()
    {
        var i;
        var oMenu;
        
for (i=0; i<this._m_aOpenMenuID.length; i++)
        {        
            oMenu = spm_getById("tbl" + 
this._m_aOpenMenuID[i]);
            oMenu.style.display = "none";

            
if (this.useIFrames)
                spm_iFrameIndex(oMenu, 
falsethis.systemImagesPath);
        }
        
if (this.useIFrames != true)
            spm_showElement("SELECT|OBJECT");

        
this._m_aOpenMenuID.length = 0;
    }        
  
  
  function SolpartMenuTransitionObject()
  {
    
this.id=null;
    
this.stop = false;
  } 

  
//--- stops menu transition effect ---//
  
SolpartMenu.prototype.stopTransition = function ()
  {
    
this.SolpartMenuTransitionObject.stop = true;
    
this.doFilter();
    
this.SolpartMenuTransitionObject = new SolpartMenuTransitionObject();
  }
  
  
//--- starts menu transition effect ---//
  
SolpartMenu.prototype.doTransition = function (oMenu)
  {
    
if (this.menuTransition == 'None' || this.supportsTransitions == '0')
      
return;

    var sID = 
this.SolpartMenuTransitionObject.id;
    
    
switch (this.menuTransition)
    {
      
case 'AlphaFade':
      {
        
if (this.SolpartMenuTransitionObject.id != oMenu.id) 
        {
          
this.SolpartMenuTransitionObject.id = oMenu.id;
          
this.SolpartMenuTransitionObject.opacity = 0;
          
this.doFilter();
        }
        
break;
      }
      
case 'Wave':
      {
        
if (this.SolpartMenuTransitionObject.id != oMenu.id) 
        {        
          
this.SolpartMenuTransitionObject.id = oMenu.id;
          
this.SolpartMenuTransitionObject.phase = 0;
          
this.doFilter();
        }
        
break;
      }
      
case 'ConstantWave':
      {
        
if (sID != oMenu.id) 
        {        
          
this.SolpartMenuTransitionObject.id = oMenu.id;
          
this.SolpartMenuTransitionObject.phase = 0;
          
this.SolpartMenuTransitionObject.constant=true;
          
this.doFilter();
        }
        
break;
      }
      
case 'Inset'case 'RadialWipe'case 'Slide'case 'Spiral'case 'Stretch'case 'Strips'case 'Wheel'case 'GradientWipe'case 'Zigzag'case 'Barn'case 'Blinds'case 'Checkerboard'case 'Fade'case 'Iris'case 'RandomBars':
      {
        oMenu.filters(
'DXImageTransform.Microsoft.' this.menuTransition).apply();
        oMenu.filters(
'DXImageTransform.Microsoft.' this.menuTransition).duration = this.menuTransitionLength;
        oMenu.filters(
'DXImageTransform.Microsoft.' this.menuTransition).play();
        
break;
      }
    }
  }

  
//--- applys transition filter ---//
  
SolpartMenu.prototype.doFilter = function (bStop) 
  {      
    
if (this.SolpartMenuTransitionObject.id == null)
      
return;
      
    var o = spm_getById(
this.SolpartMenuTransitionObject.id);
    window.status = 
new Date();
    
switch (this.menuTransition)
    {
      
case 'AlphaFade':
      {
        
if (this.SolpartMenuTransitionObject.stop)
        {
          o.filters(
'DXImageTransform.Microsoft.Alpha').opacity = 100;
        }
        
else
        
{
          o.filters(
'DXImageTransform.Microsoft.Alpha').opacity = this.SolpartMenuTransitionObject.opacity;
          
if (this.SolpartMenuTransitionObject.opacity < 100)
          {
            setTimeout(
'm_oSolpartMenu["' this._m_sNSpace + '"].doFilter()', 50);
            
this.SolpartMenuTransitionObject.opacity += (100/20* this.menuTransitionLength);
          }
        }
        
break;
      }
      
case 'Wave'case 'ConstantWave':
      {
        
if (this.SolpartMenuTransitionObject.stop)
        {
            o.filters("DXImageTransform.Microsoft.Wave").freq = 0;
            o.filters("DXImageTransform.Microsoft.Wave").lightstrength = 0;
            o.filters("DXImageTransform.Microsoft.Wave").strength = 0;
            o.filters("DXImageTransform.Microsoft.Wave").phase = 0;
        }
        
else
        
{
          o.filters("DXImageTransform.Microsoft.Wave").freq = 1;
          o.filters("DXImageTransform.Microsoft.Wave").lightstrength = 20;
          o.filters("DXImageTransform.Microsoft.Wave").strength = 5;
          o.filters("DXImageTransform.Microsoft.Wave").phase = 
this.SolpartMenuTransitionObject.phase;

          
if (this.SolpartMenuTransitionObject.phase < 100 * this.menuTransitionLength || this.SolpartMenuTransitionObject.constant == true)
          {
            setTimeout(
'm_oSolpartMenu["' this._m_sNSpace + '"].doFilter()', 50);
            
this.SolpartMenuTransitionObject.phase += 5;
          }
          
else
          
{
            o.filters("DXImageTransform.Microsoft.Wave").freq = 0;
            o.filters("DXImageTransform.Microsoft.Wave").lightstrength = 0;
            o.filters("DXImageTransform.Microsoft.Wave").strength = 0;
            o.filters("DXImageTransform.Microsoft.Wave").phase = 0;
          }
        }
        
break;
      }
    }
  }          
  



  
//--- handles mouseover for menu item ---//
    
SolpartMenu.prototype.handlembi_mo = function (oRow, bBypassDelay)
    {
        var sID = oRow.id.substr(2);

        spm_getById("icon" + sID).className = spm_fixCSSForMac(
this.getIntCSSName('spmitmsel spmicn') + this.cssMenuIcon + ' ' this.cssMenuItemSel + ' ' + spm_getAttr(oRow, 'saveselcss'''));
        spm_getById("td" + sID).className = spm_fixCSSForMac(
this.getIntCSSName('spmitmsel') + this.cssMenuItemSel + ' ' + spm_getAttr(oRow, 'saveselcss'''));
        spm_getById("arrow" + sID).className = spm_fixCSSForMac(
this.getIntCSSName('spmitmsel spmarw') + this.cssMenuItemSel + ' ' this.cssMenuArrow + ' ' + spm_getAttr(oRow, 'saveselcss'''));
        
        spm_applyRowBorder(oRow, 1, 
this.selBorderColor, true);


        
if (this.minDelay != 0 && bBypassDelay != true)
        {
            
if (this.minDelayTimer != null)
                window.clearTimeout(
this.minDelayTimer);
            
this.minDelayType = 'sub';
            
this.minDelayObj = oRow;
            
this.minDelayTimer = setTimeout('m_oSolpartMenu["' this._m_sNSpace + '"].mouseInDelayHandler()'this.minDelay);
            
return;
        }
    
        
if (this._m_aOpenMenuID[this._m_aOpenMenuID.length - 1] != oRow.id.replace('tr'''))
        {
            
this.handleNewItemSelect(spm_getAttr(oRow, "parentID", ""));
        
            
if (spm_getById("tbl" + sID) != null)
            {
                var iWidth;
                oMenu = spm_getById("tbl" + sID);

                var oPDims = 
new spm_elementDims(oRow);
                var oMDims = 
new spm_elementDims(oMenu);
                                    
                oMenu.style.top = oPDims.t + 
'px';
                
                spm_resetScroll(oMenu);

                
this.doTransition(oMenu);

                oMDims = 
new spm_elementDims(oMenu);    //now that we moved need to reget dims
                
oMenu.style.display = "";

              
if (oMDims.t - spm_getBodyScrollTop() + oMDims.h > spm_getViewPortHeight())
              {
                  
if (oMDims.h < spm_getViewPortHeight())
                        oMenu.style.top = (spm_getViewPortHeight() + spm_getBodyScrollTop() - oMDims.h) + 
'px';
                    
else
                    
{
                        spm_handleScrollMenu(
this, oMenu);
                        
                        oMDims = 
new spm_elementDims(oMenu);    //now that we moved need to reget dims
                    
}
              }

                
if (this.direction == 'rtl')
                    oMenu.style.left = (oPDims.l - oMDims.w - spm_getBodyScrollLeft()) + 
'px';
                
else
                    
oMenu.style.left = (oPDims.l + oPDims.w - spm_getBodyScrollLeft()) + 'px';

                
if (this.direction == 'rtl')
                {
                    
if (oMDims.l - spm_getBodyScrollLeft() < 0)
                        oMenu.style.left = (oPDims.l + oPDims.w - spm_getBodyScrollLeft()) + 
'px';
                }
                
else  
                
{
                    
if (oPDims.l - spm_getBodyScrollLeft() + oPDims.w + oMDims.w > spm_getViewPortWidth())
                        oMenu.style.left = (oPDims.l - oMDims.w - spm_getBodyScrollLeft()) + 
'px';
                }
                    
                
this._m_aOpenMenuID[this._m_aOpenMenuID.length] = sID;
                
if (this.useIFrames)
                    spm_iFrameIndex(oMenu, 
truethis.systemImagesPath);
                
else
                    
spm_hideElement("SELECT|OBJECT",oMenu);

            }
        }
        
this.fireEvent('onMenuItemMouseOver', oRow);
        
    }
    
  
//--- handles mouseout for menu item ---//
    
SolpartMenu.prototype.handlembi_mout = function (oRow)
    {
            var sID = oRow.id.substr(2);

            oRow.className = spm_fixCSSForMac(
this.getIntCSSName('spmitm') + ' ' this.cssMenuItem + ' ' + spm_getAttr(oRow, 'savecss'''));
          spm_getById("icon" + sID).className = spm_fixCSSForMac(
this.getIntCSSName('spmicn') + this.cssMenuIcon);
          spm_getById("td" + sID).className = spm_fixCSSForMac(
this.getIntCSSName('spmitm') + ' ' this.cssMenuItem + ' ' + spm_getAttr(oRow, 'savecss'''));
          spm_getById("arrow" + sID).className = spm_fixCSSForMac(
this.getIntCSSName('spmarw') + this.cssMenuArrow);
            
            spm_applyRowBorder(oRow, 1, "", 
false);

      
this.stopTransition();
    }

  
//used for raising events to client javascript
  
SolpartMenu.prototype.fireEvent = function (sEvent, src) 
  {
        
return//disabled for now
    
if (eval('this.' + sEvent + ' != null'))
        {
            var e = 
new Object();
            
if (src != null)
                e.srcElement = src;
            
else
                
e.srcElement = this._m_oMenu;
                
                eval(
'this.' + sEvent + '(e)');
        }
  }

    
//--- called by setTimeOut to check mouseout hide delay ---//
    
SolpartMenu.prototype.hideMenuTime = function ()
  {
    
if (this._m_dHideTimer != null && this.moutDelay > 0)
    {
      
if (new Date() - this._m_dHideTimer >= this.moutDelay)
      {
        
this.hideAllMenus();
        
this._m_dHideTimer = null;
      }
      
else
        
setTimeout(this.hideMenuTime, this.moutDelay);
    }
  }

    SolpartMenu.prototype.mouseInDelayHandler = function ()
    {
        
if (this.minDelayType == 'root')
            
this.onMBMO(this.minDelayObj, true);
        
else if (this.minDelayType == 'sub')
            
this.handlembi_mo(this.minDelayObj, true);
        
this.minDelayTimer = null;
        
this.minDelayObj = null;
    }

    
//--- called by setTimeOut to check mouseout hide delay ---//
    
SolpartMenu.prototype.scrollMenu = function ()
  {
        
if (this._m_oScrollingMenu != null)
        {
            
if (spm_ScrollMenuClick(this._m_oScrollingMenu) == false)
                setTimeout(
'm_oSolpartMenu["' this._m_sNSpace + '"].scrollMenu()', 500);
            
else
                this
._m_oScrollingMenu = null;
        }
  }

//global
    
function spm_iFrameIndex(eMenu, bShow, sysImgPath)
    {
        
if (spm_browserType() == 'op')
            
return;    //not needed
        
        
if (document.readyState != 'complete')
            
return;    //avoid operation aborted
        
        
if (bShow)
        {
            var oIFR=spm_getById(
'ifr' + eMenu.id);
            
if (oIFR == null)
            {
                var oIFR = document.createElement(
'iframe');
                oIFR.id = 
'ifr' + eMenu.id;
                
//oIFR.src = 'javascript: void(0);';
                
oIFR.src = sysImgPath + 'spacer.gif';
                oIFR.style.top = 0;
                oIFR.style.left = 0;
                oIFR.style.filter = "progid:DXImageTransform.Microsoft.Alpha(opacity=0)";
                oIFR.scrolling = 
'no';
                oIFR.frameBorder = 
'no';
                oIFR.style.display = 
'none';
                oIFR.style.position = 
'absolute';
                document.body.appendChild(oIFR);
            }
            var oMDims = 
new spm_elementDims(eMenu);
            
            oIFR.style.width=oMDims.w;
            oIFR.style.height=oMDims.h;
            oIFR.style.top=oMDims.t;
            oIFR.style.left=oMDims.l;
            
            var iIndex = spm_getCurrentStyle(eMenu, 
'zIndex');    //eMenu.style.zIndex;
            
if (iIndex == null || iIndex == 0)
                eMenu.style.zIndex = 1;
            oIFR.style.zIndex=iIndex-1;
            oIFR.style.display="block";
        }
        
else if (spm_getById('ifr' + eMenu.id) != null)
        {
            spm_getById(
'ifr' + eMenu.id).style.display='none';
        }
    }

    function spm_showElement(elmID)
    {
        
if (spm_browserType() == 'op')
            
return;    //not needed

        // Display any element that was hidden
        
var sTags = elmID.split('|');
        
for (var x=0; x<sTags.length; x++)
        {
            elmID = sTags[x];
            
for (var i = 0; i < spm_getTags(elmID).length; i++)
            {
                obj = spm_getTags(elmID)[i];
                
if (! obj || ! obj.offsetParent)
                    
continue;
                obj.style.visibility = "";
            }
        }
    }

    function spm_hideElement(elmID, eMenu)
    {
        
if (spm_browserType() == 'op')
            
return;    //not needed

        
var obj;
        
// Hide any element that overlaps with the dropdown menu
        
var sTags = elmID.split('|');
        
        var oMDims = 
new spm_elementDims(eMenu);
        
        
for (var x=0; x<sTags.length; x++)
        {
            elmID = sTags[x];
            
for (var i = 0; i < spm_getTags(elmID).length; i++)
            {
                obj = spm_getTags(elmID)[i];
                var oODims = 
new spm_elementDims(obj);
                
                
if (oODims.t > oMDims.t + oMDims.h)
                {
                    
//if element is below bottom of menu then do nothing
                
}
                
else if (oODims.l > oMDims.l + oMDims.w)
                {
                    
//if element is to the right of menu then do nothing
                
}
                
else if (oODims.l + oODims.w < oMDims.l)
                {
                    
//if element is to the left of menu then do nothing
                
}
                
else if (oODims.t + oODims.h < oMDims.t)
                {
                    
//if element is to the top of menu then do nothing
                
}
                
else
                
{
                    obj.style.visibility = "hidden";
                }
            }
        }
    }

    function spm_positionMenu(me, oMenu, oCell)
    {

        spm_resetScroll(oMenu);

        var oPDims = 
new spm_elementDims(oCell, false, me);

        
if (me.display == 'vertical')
        {
            oMenu.style.top = oPDims.t + 
'px';
            var oMDims = 
new spm_elementDims(oMenu);

            
if (oMDims.t - spm_getBodyScrollTop() + oMDims.h >= spm_getViewPortHeight())
            {
                
if (oMDims.h < spm_getViewPortHeight())
                    oMenu.style.top = (spm_getViewPortHeight() - oMDims.h + spm_getBodyScrollTop())  + 
'px';    
                    
                
else
                    
spm_handleScrollMenu(me, oMenu);
            }
            
            var oOrigMDims;
            
            
if (spm_browserType() != 'ie'//since mozilla doesn't set width greater than window size we need to store it here
                 
oOrigMDims = new spm_elementDims(oMenu);
            
            oMenu.style.left = (oPDims.l + oPDims.w - spm_getBodyScrollLeft()) + 
'px';
            oMDims = 
new spm_elementDims(oMenu);
            
if (oOrigMDims == null)
                oOrigMDims = oMDims;
            
            
if (oMDims.l - spm_getBodyScrollLeft(true) + oOrigMDims.w > spm_getViewPortWidth())
            {
              
if (spm_getViewPortWidth() - oOrigMDims.w > 0)  //only do this if it fits
                  
oMenu.style.left = (oPDims.l - oOrigMDims.w - spm_getBodyScrollLeft(true)) + 'px';
            }

            
//oMenu.style.display = "";
        
}
        
else
        
{
            
if (me.direction == 'rtl')            
            {
                var oMDims2 = 
new spm_elementDims(oMenu);
                oMenu.style.left = ((oPDims.l + oPDims.w) - oMDims2.w - spm_getBodyScrollLeft()) + 
'px';
            }
            
else            
                
oMenu.style.left = (oPDims.l - spm_getBodyScrollLeft()) + 'px';

            
//oMenu.style.top = oPDims.t + oPDims.h;
            
oMenu.style.top = (oPDims.t + oPDims.h) + 'px';
                
            var oMDims = 
new spm_elementDims(oMenu);
            
            
if (oMDims.l - spm_getBodyScrollLeft(true) + oMDims.w > spm_getViewPortWidth())
            {
              
if (spm_getViewPortWidth() - oMDims.w > 0)  //only do this if it fits
                  
oMenu.style.left = spm_getViewPortWidth() - oMDims.w + spm_getBodyScrollLeft(true);
            }
            
            
if (oMDims.t - spm_getBodyScrollTop() + oMDims.h > spm_getViewPortHeight())
            {
              
if (oPDims.t - oMDims.h - spm_getBodyScrollTop() > 0) //only do this if it fits
                  
oMenu.style.top = (oPDims.t - oMDims.h) + 'px';    //place above menu bar
                
else
                    
spm_handleScrollMenu(me, oMenu);
            }
            
//oMenu.style.display = "none";
        
}
    }

    
//--------- Internal (private) Functions --------//
    //--- Applies border to cell ---//
    
SolpartMenu.prototype.applyBorder = function (oCell, iSize, sTopLeftColor, sBottomRightColor, sStyle)
    {
        
if (this.moDisplay == 'Outset')
        {
            
if (sStyle == null)
                sStyle = "solid";

            
if (sTopLeftColor.length > 0 && sBottomRightColor.length > 0)
            {
                
if (oCell.tagName == 'TR')
                    oCell = oCell.childNodes(0);
                
                oCell.style.borderTop = sStyle + " " + iSize + "px " + sTopLeftColor;
                oCell.style.borderLeft = sStyle + " " + iSize + "px " + sTopLeftColor;
                oCell.style.borderRight = sStyle + " " + iSize + "px " + sBottomRightColor;
                oCell.style.borderBottom = sStyle + " " + iSize + "px " + sBottomRightColor;    
                
            }
        }
        
if (this.moDisplay == 'HighLight')
        {
            
if (sTopLeftColor == this.backColor)
            {
                
//oCell.style.backgroundColor = '';
        //setClassColor(oCell, 'spmitm', '');
        
oCell.className = spm_fixCSSForMac(this.getIntCSSName('spmbar spmitm') + ' ' this.cssMenuItem + ' ' + spm_getAttr(oCell, 'savecss'''));
            }
            
else
            
{
                
//oCell.style.backgroundColor = this.selColor;
        //setClassColor(oCell, 'spmitm', this.selForeColor);
        
oCell.className = spm_fixCSSForMac(this.getIntCSSName('spmbar spmitmsel') + ' ' this.cssMenuItemSel + ' ' + spm_getAttr(oCell, 'saveselcss'''));
            }
        }        
    }

    function spm_applyRowBorder(oRow, iSize, sColor, bSelected, sStyle)
    {
        
if (oRow.cells.length == 0) //(spm_browserType() == 'safari')
            
return;    //safari has issues with accessing cell
        
        
var sColor2=sColor;
        
if (sStyle == null)
            sStyle = "solid";

        
if (sColor == "")
        {
            
//if (bSelected)
            //    sColor2 = this.selColor;
            //else
                
sColor2 = spm_getCurrentStyle(oRow.cells[0], 'background-Color');
                
if ((sColor2 == null || sColor2 == '') && spm_browserType() != 'ie')
                    sColor2 = 
'transparent';
        }

        
//if (sColor2 != 'transparent')
            
spm_applyBorders(oRow.cells[0], sStyle, iSize, sColor2, truetruefalsetrue);

        
if (sColor == "" && bSelected == false)
    {
      sColor2 = spm_getCellBackColor(oRow.cells[1]);
      
if (sColor2 == null || sColor2 == '')
                sColor2 = 
'transparent';
    }
   
    
//if (sColor2 != 'transparent')
    //{
            
spm_applyBorders(oRow.cells[1], sStyle, iSize, sColor2, truefalsefalsetrue);
            spm_applyBorders(oRow.cells[2], sStyle, iSize, sColor2, 
truefalsetruetrue);
        
//}
    
}
    
    function spm_getCellBackColor(o)
    {
        var sColor = spm_getCurrentStyle(o, 
'background-Color');  
    
if (spm_browserType() == 'ie')
    {
      
//--- fix IE transparent border issue ---//
      
while (sColor == 'transparent')
      {
        sColor = spm_getCurrentStyle(o, 
'background-Color');  
        o = o.parentElement;
        
if (o.id.indexOf('divOuterTables') != -1)    //if we are outside the realm of the menu then use transparency
                    
break;
      }
    }
    
return sColor;
    }
    
    function spm_applyBorders(o, sStyle, iSize, sColor, t, l, r, b)
    {

/*
        if (t && sColor=='') o.style.paddingTop = iSize + "px ";
        if (b && sColor=='') o.style.paddingBottom = iSize + "px ";
        if (r && sColor=='') o.style.paddingRight = iSize + "px ";
        if (l && sColor=='') o.style.paddingLeft = iSize + "px ";
    if (sColor=='')
      iSize = 0;
 */     
        
if (t) o.style.borderTop = sStyle + " " + iSize + "px " + sColor;
        
if (b) o.style.borderBottom = sStyle + " " + iSize + "px " + sColor;
        
if (r) o.style.borderRight = sStyle + " " + iSize + "px " + sColor;
        
if (l) o.style.borderLeft = sStyle + " " + iSize + "px " + sColor;

    }

    function spm_resetScroll(oMenu)
    {
    
        
if (oMenu.scrollItems != null)
        {
            oMenu.scrollPos = 1;
            oMenu.scrollItems = 9999;
            spm_showScrolledItems(oMenu);
        }    
    }
    
    
    function spm_handleScrollMenu(me, oMenu)
    {
        var oTbl = spm_getTags(
'table', oMenu)[0]; //oMenu.childNodes[1];    
        
oMenu.style.display = '';
        
if (oMenu.scrollPos == null)
        {
            oMenu.scrollPos = 1;            
                        
            var oRow = spm_insertTableRow(oTbl);
            var oCell = document.createElement(
'TD');        
            oCell.id = 
'dn' + oMenu.id.substring(3);
            oCell.colSpan = 3;
            oCell.align = 
'center';
            oCell.style.backgroundColor = 
'gray';    //can be overridden by MenuScroll style
            
oCell.innerHTML='<div id="dn' + oMenu.id.substr(3) + '" onclick="return spm_ScrollMenuClick(this, event);" onmouseover="spm_ScrollMenuMO(this, m_oSolpartMenu[\'' + me._m_sNSpace + '\']);" onmouseout="spm_ScrollMenuMOUT(m_oSolpartMenu[\'' + me._m_sNSpace + '\']);" class="' + spm_fixCSSForMac(me.getIntCSSName('spmitmscr')) + ' ' + me.cssMenuScrollItem + '" style="width: 100%; font-size: 6pt;">...</div>';
            oRow.appendChild(oCell);

            oRow = spm_insertTableRow(oTbl, 0);
            oCell = document.createElement(
'TD');        
            oCell.id = 
'up' + oMenu.id.substring(3);
            oCell.colSpan = 3;
            oCell.align = 
'center';
            oCell.style.backgroundColor = 
'gray';    //can be overridden by MenuScroll style
            
oCell.innerHTML='<div id="up' + oMenu.id.substr(3) + '" onclick="return spm_ScrollMenuClick(this, event);" onmouseover="spm_ScrollMenuMO(this, m_oSolpartMenu[\'' + me._m_sNSpace + '\']);" onmouseout="spm_ScrollMenuMOUT(m_oSolpartMenu[\'' + me._m_sNSpace + '\']);" class="' + spm_fixCSSForMac(me.getIntCSSName('spmitmscr')) + ' ' + me.cssMenuScrollItem + '" style="width: 100%; font-size: 6pt;">...</div>';
            oRow.style.display = 
'none';
            oRow.appendChild(oCell);
        }    

        
if (oMenu.ScrollRowHeight == null)
        {
            spm_getTags(
'tr', oTbl)[0].style.display = '';
            oMenu.ScrollItemHeight = (spm_getElementHeight(spm_getTags(
'tr', oTbl)[0]) * 2);
            spm_getTags(
'tr', oTbl)[0].style.display = 'none';

            oMenu.ScrollRowHeight = spm_getElementHeight(spm_getTags(
'tr', oTbl)[1]);
        }

        oMenu.scrollItems = parseInt((spm_getViewPortHeight() - spm_elementTop(oMenu) + spm_getBodyScrollTop() - oMenu.ScrollItemHeight) / (oMenu.ScrollRowHeight + 1));
        
//alert(oMenu.ScrollRowHeight);
        //alert(oMenu.ScrollItemHeight);
        
spm_showScrolledItems(oMenu);

    }
        
    function spm_ScrollMenuMO(e, me)
    {
        me._m_dHideTimer = 
null;
        me._m_oScrollingMenu = e;
        
if (Number(me.moScroll))
            setTimeout(
'm_oSolpartMenu["' + me._m_sNSpace + '"].scrollMenu()', 500);

    }
    
    function spm_ScrollMenuMOUT(me)
    {
        me._m_oScrollingMenu = 
null;

        me._m_dHideTimer = 
new Date();
        
if (me.moutDelay != 0)
          setTimeout(
'm_oSolpartMenu["' + me._m_sNSpace + '"].hideMenuTime()', me.moutDelay);
        

    }
    
    function spm_ScrollMenuClick(e, evt)
    {        
        
if (e != null)
        {    
            var oCell = e.parentNode;
            var oTbl = oCell.parentNode.parentNode.parentNode;
            var oMenu = oTbl.parentNode;

            
if (oCell.id == 'up' + oMenu.id.substring(3))
            {
                
if (oMenu.scrollPos > 1)
                    oMenu.scrollPos--;                    
                
else
                    return true
;
            }
            
else 
            
{
                
if (oMenu.scrollPos + oMenu.scrollItems < oTbl.rows.length - 1)
                    oMenu.scrollPos++;
                
else
                    return true
;
            }
                
            spm_showScrolledItems(oMenu);
            
if (evt != null)
                spm_stopEventBubbling(evt);
        }
        
return false;
    }

    function spm_showScrolledItems(oMenu)
    {
        var oTbl = spm_getTags(
'table', oMenu)[0];
        var oRows = spm_getTags(
'tr', oTbl);    //oTbl.rows.length
        
        
for (var i=1; i < oRows.length; i++)    
        {
            
//if row is not within display "window" then don't display it
            
if (i < oMenu.scrollPos || i >= oMenu.scrollPos + oMenu.scrollItems)
                oRows[i].style.display = 
'none';
            
else
                
oRows[i].style.display = '';            
        }
        
        
// if we are scrolled down at least one then display up scroll item
        
if (oMenu.scrollPos > 1)
            oRows[0].style.display = 
'';
        
else
            
oRows[0].style.display = 'none';
        
        
        
// if there is at least one item not displayed then show down item
        
if (oMenu.scrollPos + oMenu.scrollItems < oTbl.rows.length - 1)
            oRows[oRows.length-1].style.display = 
'';
        
else
            
oRows[oRows.length-1].style.display = 'none';
            
    }

    function spm_insertTableRow(tbl, iPos)
    {
        var oRow;
        var oTB;
        oRow = document.createElement(
'TR');
        
if (tbl.getElementsByTagName('TBODY').length == 0)
        {
            oTB = document.createElement(
'TBODY');
            tbl.appendChild(oTB);
        }
        
else
            
oTB = tbl.getElementsByTagName('TBODY')[0];

        
if (iPos == null)
            oTB.appendChild(oRow);
        
else
            
oTB.insertBefore(oRow, tbl.rows[iPos]);
        
return oRow;
    
    }

    function spm_getElementHeight(o)
    {    
        
if (o.offsetHeight == null || o.offsetHeight == 0)
        {
            
if (o.offsetParent.offsetHeight == null || o.offsetParent.offsetHeight == 0)
            {
                
if (o.offsetParent.offsetParent != null)
                    
return o.offsetParent.offsetParent.offsetHeight; //needed for Konqueror
                
else
                    return 
0;
            }
            
else
                return 
o.offsetParent.offsetHeight;
        }
        
else
            return 
o.offsetHeight;
    }

    function spm_getElementWidth(o)
    {
        
if (o.offsetWidth == null || o.offsetWidth == 0)
        {
            
if (o.offsetParent.offsetWidth == null || o.offsetParent.offsetWidth == 0)
            {
                
if (o.offsetParent.offsetParent != null)
                    
return o.offsetParent.offsetParent.offsetWidth; //needed for Konqueror
                
else
                    return 
0;
            }
            
else
                return 
o.offsetParent.offsetWidth

        }
        
else
            return 
o.offsetWidth;
    }
    
    
//viewport logic taken from http://dhtmlkitchen.com/js/measurements/index.jsp
    
function spm_getViewPortWidth()
    {
        
// supported in Mozilla, Opera, and Safari
    
if(window.innerWidth)
            
return window.innerWidth;
    
// supported in standards mode of IE, but not in any other mode
    
if(window.document.documentElement.clientWidth)
            
return document.documentElement.clientWidth;
    
    
// supported in quirks mode, older versions of IE, and mac IE (anything else).
    
return window.document.body.clientWidth;
    }
    
    function spm_getBodyScrollTop()
    {
        
if ('|ie|op|mo|ns|'.indexOf('|' + spm_browserType() + '|') != -1)
        {
            
if (document.body.scrollTop != null)
                
return document.body.scrollTop;
        }
        
return 0;
    }

    function spm_getBodyScrollLeft(bOverride)
    {
        
if ('|op|'.indexOf('|' + spm_browserType() + '|') != -1 || bOverride == true)
        {
            
if (document.body.scrollLeft != null)
            {
                
return document.body.scrollLeft;
            }
        }
        
return 0;
    }
    
    function spm_getViewPortHeight()
    {
        
// supported in Mozilla, Opera, and Safari
    
if(window.innerHeight)
            
return window.innerHeight;
    
// supported in standards mode of IE, but not in any other mode
    
if(window.document.documentElement.clientHeight)
            
return document.documentElement.clientHeight;
    
    
// supported in quirks mode, older versions of IE, and mac IE (anything else).
    
return window.document.body.clientHeight;
    }
        
    function spm_elementTop(eSrc, includeBody)
    {
        
        var iTop = 0;
        var eParent;
        eParent = eSrc;

        
while (eParent.tagName.toUpperCase() != "BODY")
        {

            
//Safari incorrectly calculates the TR tag to be at the top of the table, so try and get child TD tag to use for measurement
            //if (spm_browserType() == 'safari' && eParent.tagName.toUpperCase() == 'TR' && spm_getTags('TD', eParent).length)
            //    eParent = spm_getTags('TD', eParent)[0];

            
iTop += eParent.offsetTop;
            
            eParent = eParent.offsetParent;
            
if (eParent == null)
                
break;
        }
        
if (includeBody != null && eParent != null && (spm_browserType() == 'safari' || spm_browserType() == 'kq')) 
            iTop += eParent.offsetTop;
        
        
return iTop;
    }


    function spm_elementLeft(eSrc, includeBody)
    {    
        var iLeft = 0;
        var eParent;
        eParent = eSrc;
        
while (eParent.tagName.toUpperCase() != "BODY")
        {

            iLeft += eParent.offsetLeft;
                
            eParent = eParent.offsetParent;
            
if (eParent == null)
                
break;
        }
        
if (includeBody != null && eParent != null && (spm_browserType() == 'safari' || spm_browserType() == 'kq'))
            iLeft += eParent.offsetLeft;

        
        
return iLeft;
    }
    
    function spm_getElement(e, sID) 
    {
        var o=e;
        var i=0;
        
while (o.id != sID)
        {
            o=o.parentNode;
            i++;
        }
        
return o;
    }

    function spm_getSourceTR(e, ns)
    {
        
while (e.id == "")
        {
            e= e.parentElement;
        }
        
if (e.id.indexOf("arrow") != -1)
        {
            var sID = e.id.substr(5);
            
return spm_getById("tr" + sID);
        }
        
else if (e.id.indexOf("td") != -1)
        {
            var sID = e.id.substr(2);
            
return spm_getById("tr" + sID);
        }    
        
else if (e.id.indexOf("icon") != -1)
        {
            var sID = e.id.substr(4);
            
return spm_getById("tr" + sID);
        }    
        
else if (e.id.indexOf("img") != -1)
        {
            var sID = e.id.substr(3);
            
return spm_getById("tr" + sID);
        }    
        
else
        
{
            
return e;
        }
    }

    function spm_itemHasChildren(sID, ns)
    {
        objTable = spm_getById(ns + "tbl" + sID);
        
if (objTable != null)
        {
            
if (objTable.rows != null)
            {
                
if (objTable.rows.length > 0)
                    
return true;
                
else
                    return false
;
            }        
        }
    }

function spm_getMenuItemStyle(sType, oNode)
{
  
return spm_getAttr(oNode, sType + "style", '');
}

function spm_getMenuItemCSS(oNode)
{
  
return spm_getAttr(oNode, "css", '');
}

function spm_getMenuItemSelCSS(oNode)
{
  
return spm_getAttr(oNode, "selcss", '');
}

SolpartMenu.prototype.getIntCSSName =  function(sClass)
{
  var ary = sClass.split(
' ');
  var s=
'';
  
for (var i=0; i<ary.length; i++)
    s += 
this._m_sNSpace.toLowerCase() + '_' + ary[i] + ' ';
  
  
return s;
}

function spm_fixCSSForMac(s)
{
    var ary = s.split(
' ');
    var sRet=
'';
    
for (var i=0; i<ary.length; i++)
    {
        
if (ary[i].rtrim().length > 0)
        {
            
if (sRet.length)
                sRet += 
' ' + ary[i];
            
else
                
sRet = ary[i];
        }
    }
    
return sRet;
}

function spm_getMenuClickAction(oNode, me)
{
  
//'function to determine if menu item has action associated (URL)
  
var sName = spm_getAttr(me._m_oMenu, 'name', me._m_oMenu.name);

  
if (sName == null || sName.length == 0)    //opera fix for getting name
        
sName = spm_getAttr(me._m_oMenu, 'pbname', me._m_oMenu.pbname);
    
  
if (spm_getAttr(oNode, "runat", '').length)
    
return "__doPostBack('" + sName + "', '" + spm_getAttr(oNode, "id", "") + "');";
  
if (spm_getAttr(oNode, "server", '').length)
    
return "__doPostBack('" + sName + "', '" + spm_getAttr(oNode, "id", "") + "');";
  var sURL = spm_getAttr(oNode, "url", "");
  
if (sURL.length)
    {
        
if (sURL.toLowerCase().substr(0, "javascript:".length) == "javascript:")
            
return sURL.substr("javascript:".length) + ";";
        
else
        
{
            
if (me.target.length > 0 && document.frames[me.target] != null)
                
return "document.frames['" + me.target + "'].location.href='" + sURL + "';";
            
else
                return 
"document.location.href='" + sURL + "';";
        }
    }
    
return '';
    
}

function spm_getMenuSpacingImage(sPos, me)
{
  var sAlign = me.menuAlignment.toLowerCase();

  
if ((sPos == 'left' && sAlign == 'right') || (sPos == 'right' && sAlign == 'left'))
        
return "       <td width=\"100%\">" + spm_getSpacer(me) + "</td>";

  
if ((sPos == 'right' && sAlign == 'left') || (sPos == 'left' && sAlign == 'right'))
        
return "       <td width=\"3px\">" + spm_getSpacer(me) + "</td>";

    
if (sAlign == 'Center')
        
return "       <td width=\"33%\">" + spm_getSpacer(me) + "</td>";
    
    
return '';   
}

function spm_getSpacer(me) 
{
  
return spm_getMenuImage('spacer.gif', me, false' ');
    
//return '&nbsp;'; //"<IMG SRC=\"" + me.systemImagesPath + "spacer.gif\">";
}

function spm_getImage(oAttr, me)
{
  
//'retrieves an image for a passed in XMLAttribute
  
var sImage = spm_getAttr(oAttr, 'image''');

  
if (sImage.length)
  {
    
return spm_getHTMLImage(sImage, spm_getAttr(oAttr, 'imagepath', me.iconImagesPath), null, spm_getAttr(oAttr, 'title'''));
  }
  
else
    return 
spm_getMenuImage('spacer.gif', me, null' ');
}

function spm_getItemHTML(oNode, sSide, sDef)
{
  
if (sDef == null) sDef = '';
  
return spm_getAttr(oNode, sSide + "html", sDef);
}

function spm_getMenuImage(sImage, me, bForce, sAlt)
{
    
//'generates html for image using the SystemImagesPath property
    
return spm_getHTMLImage(sImage, me.systemImagesPath, bForce, sAlt);
}

function spm_getHTMLImage(sImage, sPath, bForce, sAlt)
{
    
//'generates html for image using the SystemImagesPath property
    
if (spm_browserNeedsSpacer() == false && sImage == 'spacer.gif' && bForce != true)
        
return '&nbsp;'
    
else
        return 
"<IMG SRC=\"" + sPath + sImage + "\" " + spm_getAlt(sAlt) + ">";
}

function spm_getAlt(sAlt)
{
    
if (sAlt != null && sAlt.rtrim().length > 0)
        
return ' ALT="' + sAlt + '" ';
    
else
        return 
'';
}

function spm_browserNeedsSpacer()
{
    
if (spm_browserType() == 'ie')
        
return false;
    
else
        return true
;
}

function MyIIf(bFlag, sTrue, sFalse) 
{
    
if (bFlag)
        
return sTrue;
    
else
        return 
sFalse;
}

function spm_getArrow(sImg, me) 
{
  
//FIX
    
if (sImg.length)
        
return spm_getMenuImage(sImg, me, null'>');
    
else
    
{
      
if (me.direction == 'rtl')
                
return "3"; 
      
else
                return 
"4"; //'defaults to using wingdings font (4 = arrow)
    
}
}

function spm_getMenuBorderStyle(me, shColor, hlColor, width)
{
  
if (shColor == null) shColor = me.shColor;
  
if (hlColor == null) hlColor = me.hlColor;
  
if (width == null) width = me.borderWidth;
  
  
//border-bottom: Gray 1px solid; border-left: White 1px solid; border-top: White 1px solid; border-right: Gray 1px solid;
  //return 'border-bottom: ' + shColor + ' ' + width + 'px solid; border-left: ' + hlColor + ' ' + width + 'px solid;  border-top: ' + hlColor + ' ' + width + 'px solid; border-right: ' + shColor + ' ' + width + 'px solid;';
  
return getBorderStyle('border-bottom', shColor, width) + getBorderStyle('border-left', hlColor, width) + getBorderStyle('border-top', hlColor, width) + getBorderStyle('border-right', shColor, width);
}

function getBorderStyle(type, color, width)
{  
  
return type + ': ' + color + ' ' + width + 'px solid; ';
}



//------------------------//
String.prototype.ltrim = function () { return this.replace(/^\s*/, "");}
String.prototype.rtrim = function () { 
return this.replace(/\s*$/, "");}
String.prototype.trim  = function () { 
return this.ltrim().rtrim(); }

if (spm_browserType() == 'safari')    //Safari Hack
    
var Document = null;
    
if (spm_browserType() != 'ie' && spm_browserType() != 'op' && Document != null)
{
  Document.prototype.loadXML = function (s) 
    {
    
      
// parse the string to a new doc
      
var doc2 = (new DOMParser()).parseFromString(s, "text/xml");

      
// remove all initial children
      
while (this.hasChildNodes())
      
this.removeChild(this.lastChild);

      
// insert and import nodes
      
for (var i = 0; i < doc2.childNodes.length; i++) 
      {
      
this.appendChild(this.importNode(doc2.childNodes[i], true));
      }
    }

    function _Node_getXML() 
    {
      
//create a new XMLSerializer
      
var objXMLSerializer = new XMLSerializer;
      
      
//get the XML string
      
var strXML = objXMLSerializer.serializeToString(this);
      
      
//return the XML string
      
return strXML;
    }
    Node.prototype.__defineGetter__("xml", _Node_getXML);
}

function spm_createDOMDoc()
{
    
if (spm_browserType() == 'ie')
    {
        var o = 
new ActiveXObject('MSXML.DOMDocument');
        o.async = 
false;
        
return o;
    }
    
else
        return 
document.implementation.createDocument("", "", null);
}

function spm_getById(sID)
{
  
if (document.all == null)
    
return document.getElementById(sID);
  
else
    return 
document.all(sID);
}

function spm_getTags(sTag, oCtl)
{
    
if (oCtl == null)
        oCtl = document;
    
    
if (spm_browserType() == 'ie')
    
return oCtl.all.tags(sTag);
  
else
    return 
oCtl.getElementsByTagName(sTag);
}

function spm_browserType()
{
    
if (m_spm_sBrowser == null)
    {
        var agt=navigator.userAgent.toLowerCase();

        
if (agt.toLowerCase().indexOf('konqueror') != -1) 
            m_spm_sBrowser = 
'kq';
        
else if (agt.toLowerCase().indexOf('opera') != -1) 
            m_spm_sBrowser = 
'op';
        
else if (agt.toLowerCase().indexOf('netscape') != -1) 
            m_spm_sBrowser = 
'ns';
        
else if (agt.toLowerCase().indexOf('msie') != -1)
            m_spm_sBrowser = 
'ie';
        
else if (agt.toLowerCase().indexOf('safari') != -1)
            m_spm_sBrowser = 
'safari';
      
        
if (m_spm_sBrowser == null)
            m_spm_sBrowser = 
'mo';  
    }
    
//window.status = m_spm_sBrowser;
    
return m_spm_sBrowser;
}

function spm_browserVersion()
{
    
//Please offer a better solution if you have one!
    
var sType = spm_browserType();
    var iVersion = parseFloat(navigator.appVersion);
    var sAgent = navigator.userAgent.toLowerCase();
    
if (sType == 'ie')
    {
        var temp=navigator.appVersion.split("MSIE");
        iVersion=parseFloat(temp[1]);
    }
    
if (sType == 'ns')
    {
        var temp=sAgent.split("netscape");
        iVersion=parseFloat(temp[1].split("/")[1]);    
    }
    
return iVersion;
}

function spm_needsSubMenuDelay()
{
    
if (spm_browserType() == 'ie')
        
return true;
    
else
        return false
;

}

function spm_supportsIFrameTrick()
{
    var sType = spm_browserType();
    var sVersion = spm_browserVersion();
    
    
if ((sType == 'ie' && sVersion < 5.5) || (sType == 'ns' && sVersion < 7) || (spm_browserType() == 'safari') || spm_isMac('ie'))
    {
        
return false;
    }
    
return true;
}

function spm_isMac(sType)
{
//return true;
  
var agt=navigator.userAgent.toLowerCase();
  
if (agt.indexOf('mac') != -1) 
  {
        
if (sType == null || spm_browserType() == sType)
            
return true;
  }
  
else
    return false
;
  
}


/*
function isOpera()
{
//return true;
  var agt=navigator.userAgent.toLowerCase();
  if (agt.indexOf('opera') != -1) 
    return true;
  else
    return false;
  
}
*/

//taken from http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&threadm=b42qj3%24r8s1%40ripley.netscape.com&rnum=1&prev=/groups%3Fq%3Dmozilla%2B%2522currentstyle%2522%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26safe%3Doff%26scoring%3Dd 
function spm_getCurrentStyle(el, property) {
  
if (document.defaultView) 
  {
   
// Get computed style information:

    
if (el.nodeType != el.ELEMENT_NODE) return null;
    
return document.defaultView.getComputedStyle(el,'').getPropertyValue(property.split('-').join(''));
  }
  
if (el.currentStyle) 
  {
    
// Get el.currentStyle property value:
    
return el.currentStyle[property.split('-').join('')];
    
//return el.currentStyle.getAttribute(property.split('-').join(''));  //We need to get rid of slashes
  
}
  
if (el.style) 
  {
    
// Get el.style property value:
    
return el.style.getAttribute(property.split('-').join(''));  // We need to get rid of slashes
  
return  null;
}

function spm_getAttr(o, sAttr, sDef)
{
  
if (sDef == null)
    sDef = 
'';
  var s = o.getAttribute(sAttr);
  
if (s != null && s.length > 0)
    
return o.getAttribute(sAttr);
  
else
    return 
sDef;
}

function spm_setAttr(o, sAttr, sVal)
{
    
if (sVal.length > 0)
        o.setAttribute(sAttr, sVal);
    
else
        
o.removeAttribute(sAttr);
}


function spm_fixUnit(s)
{
  
if (s.length && isNaN(s) == false)
    
return s + 'px';

}

function spm_nodeHasChildren(node)
{
  
if (typeof(node.selectSingleNode) != 'undefined'//(node.selectSingleNode != null) //(spm_browserType() == 'ie')
    
return node.selectSingleNode('./menuitem') != null;
  
else
  
{
    
if (node.childNodes.length > 0)
    {
      
//Netscape/Mozilla counts an empty <menuitem id></menuitem> as having a child...
      
for (var i=0; i< node.childNodes.length; i++)
      {
        
if (node.childNodes[i].nodeName == 'menuitem')
            
return true;
      }
    }
  }
  
return false;  
}

function spm_findNode(oParent, sID)
{
    
for (var i = 0; i < oParent.childNodes.length; i++)
    {
        oNode = oParent.childNodes[i];

        
if (oNode.nodeType != 3)  //exclude nodeType of Text (Netscape/Mozilla) issue!
        
{

            
if ((oNode.nodeName == "menuitem" || oNode.nodeName == "menubreak") && oNode.getAttribute("id") == sID)
                
return oNode;

            
if (oNode.childNodes.length > 0)
            {
                var o = spm_findNode(oNode, sID);
                
if (o != null)
                    
return o;
            }
        }
    }
}

function spm_getSibling(oNode, iOffset)
{
    var sID = spm_getAttr(oNode, 
'id');
    var o;
    
for (var i=0; i<oNode.parentNode.childNodes.length; i++)
    {
        o = oNode.parentNode.childNodes[i];
        
if (o.nodeType != 3)
        {
            
if (spm_getAttr(o, 'id') == sID)
                
return getOffsetNode(o.parentNode, i, iOffset);
        }
    }
}

function spm_stopEventBubbling(e)
{
    
if (spm_browserType() == 'ie')
            window.
event.cancelBubble = true;
        
else
            
e.stopPropagation();
}

//--- if you have a better solution send me an email - jhenning@solpart.com ---//
function spm_appendFunction(from_func, to_func)
{
  
if (from_func == null)
    
return new Function ( to_func ); 
  
return new Function ( spm_parseFunctionContents(from_func) + '\n' + spm_parseFunctionContents(to_func) );
}
function spm_parseFunctionContents(fnc)
{
  var s =String(fnc).trim();
  
if (s.indexOf('{') > -1)
        s = s.substring(s.indexOf(
'{') + 1, s.length - 1);
  
return s;
}




//--- For JS DOM ---//
function SPJSXMLNode(sNodeName, sID, oParent, sTitle, sURL, sImage, sImagePath, sRightHTML, sLeftHTML, sRunAtServer, sItemStyle, sImageStyle, sToolTip, sItemCSS, sItemSelCSS) 

  
this.nodeName = sNodeName;
  
this.id=sID;
  
this.childNodes = new Array();
  
//this.nodeType = 3;
  
  
  
this.parentNode = oParent;            
  
if (oParent != null)
  {
    oParent.childNodes[oParent.childNodes.length] = 
this;
    
    
if (oParent.documentElement == null)
      
this.documentElement = oParent;
    
else
      this
.documentElement = oParent.documentElement;
  }
  
else
    this
.documentElement = this;
    
  
this.title = sTitle;
  
this.url = sURL;
  
this.image = sImage;
  
this.imagepath = sImagePath;
  
this.righthtml = sRightHTML;
  
this.lefthtml = sLeftHTML;
  
this.server = sRunAtServer;
  
this.itemstyle = sItemStyle;
  
this.imagestyle = sImageStyle;
  
this.tooltip = sToolTip;
  
this.css = sItemCSS;
  
this.selcss = sItemSelCSS;
}      

SPJSXMLNode.prototype.getAttribute = function(s)
{
  
return this[s];
}


  var m_iSPTimer;
  var m_iSPTotalTimer=0;
  var m_sSPDebugText;
  var m_oSPDebugCtl;
  var m_bSPDebug = 
false;
  
  function __db(s)
  {
    
if (spm_browserType() != 'ie' || m_bSPDebug == false)
      
return;
     
    var sT = 
new Date() - m_iSPTimer;
    
if (sT > 120000)
    {
      sT = 
'';
      m_oSPDebugCtl.
value '---reset---';
      m_iSPTotalTimer=0;
    }
    
else if (sT > 100)
    {
      m_iSPTotalTimer+= sT;
      sT = 
' *** [' + sT + '] *** ';
    }
    
else if (sT > 0)
    {
      m_iSPTotalTimer+= sT;
      sT = 
' [' + sT + ']';
    }
    
else
      
sT = '';
      
    
if (document.forms.length > 0 && m_oSPDebugCtl == null)
    {      
      document.forms(0).insertAdjacentHTML(
'afterEnd''<br><TEXTAREA ID="my__Debug" STYLE="WIDTH: 100%; HEIGHT: 100px"></TEXTAREA>');
      m_oSPDebugCtl = document.all(
'my__Debug');
    }

    
if (m_oSPDebugCtl != null)
      m_oSPDebugCtl.
value += '[' + m_iSPTotalTimer + '] ' + s + sT + '\n';
    
else
      
m_sSPDebugText += '[' + m_iSPTotalTimer + '] ' + s + sT + '\n'
      
    m_iSPTimer = 
new Date();
  }

    
if (window.__smartNav != null)
        window.setTimeout(spm_fixSmartNav, 1000);
    function spm_fixSmartNav()
    {
        
if (window.__smartNav != null)
        {
            
if (document.readyState == 'complete')
            {
                var o = spm_getById(
'SolpartMenuDI');
                
if (o != null)
                {
                    
if (o.length == null)
                    {
                            
if (o.xml != null)
                                spm_initMyMenu(o, o.parentElement);
                    }
                    
else
                    
{
                        
for (var i=0; i<o.length; i++)
                        {
                            
if (o[i].xml != null)
                                spm_initMyMenu(o[i], o.parentElement);
                        }
                    }
                }
            }
            
else
                
window.setTimeout(spm_fixSmartNav, 1000);
        }
    }

    function spm_elementDims(o, bIncludeBody, me)
    {
        var bHidden = (o.style.display == 
'none');
        
        
if (bHidden)
            o.style.display = "";
        
this.t = spm_elementTop(o, bIncludeBody);
        
this.l = spm_elementLeft(o, bIncludeBody);
        
if (!spm_isMac('ie'))
        {
            o.style.top = 0;
            o.style.left = 0;
        }
        
this.w = spm_getElementWidth(o);
        
this.h = spm_getElementHeight(o);
        
if (!spm_isMac('ie'))
        {
            
//fixed - added + 'px'; for firefox support
            
o.style.top = this.t + 'px';
            o.style.left = 
this.l + 'px';
        }
        
if (bHidden)
            o.style.display = "none";
    }

Judicious dependency selection is a powerful tool

Ah, the blog comment turned rant turned blog post...

Jeff (Coding Horror) applauds Joel's recent article, In-Defense-of-the-Not-Invented-Here-Syndrome, in an article espousing Dependency Avoidance. Not buying it.

Like most things Joel says, if it worked for Excel VBA or FogBugz, it's the way software should be. I disagree. Too many dependencies is bad, but too much custom code is arguably worse, and there's a really sweet spot somewhere in the middle.

How are those custom Excel dialogs doing now? Rewritten to use something standard at some point, I'll bet. Custom code is expensive to maintain, and your only hope for bug fixes and new features is an investment of company time - time could possibly be used in improving the custom code you HAVE to write. Well chosen dependencies are actively maintained and supported, so your product gets better with no additional work. (Of course, good dependencies should have a source code option just in case they stop being maintained and supported.)

I lived horror stories caused by too many dependencies. I've run into just as many problems, though, where developers with the wrong combination of ignorance and hubris reinvented the wheel and came up with a flat tire.

Judicious dependency selection, like estimating and risk management, is one of the hard skills that good developers can wield to great advantage. The "buy or build" decision requires you to honestly evaluate how well your custom widget will look in a few years compared with Widget X's 2008 release. Dependencies should be viewed as expensive tools, but a good team with the right dependencies can concentrate on what it does best and run circles around the "do it yourself" crowd. Software built with carefully selected dependencies can be much greater than the sum of its parts.

Is the Enterprise Library heavy and complex? Sure, if you're writing a simple desktop application. That's why it's called the Enterprise Library - it's a framework for enterprise systems, and in those cases it allows you to build on a framework that's under public scrutiny rather than the product of a few guys at a whiteboard. This thing's been reviewed by some top security experts, for instance - has your framework?

I've been happy with DotNetNuke lately for the same reason. Sure, there's a learning curve, and there are things I'd do differently[1]. However, the system is in use in thousands of production sites worldwide, the product is on version 4.2, and core team had direct contact with Microsoft in coordination with the ASP.NET 2.0 feature set. Does it really make sense to write and maintain your own content management system from scratch at this point?

Joel provides a perfect example of how this kind of thinking goes wrong. In order to avoid a dependency on the .NET framework, his company's flagship product is developed on classic ASP and PHP. In 2006.[2]

[1] Who picks -1 as the magic number that represents Null? That number occasionally shows up in the real world... Also, I prefer c#, athough I can browse the source in c# with Reflector, so no big deal.

[2] Yes, I'm aware he does that to support *nix. XSP Mono's ASP.NET does a pretty good job with the *nix's, too, and it doesn't require a custom halfway implemented ASP to PHP converter.

Posted by Jon Galloway | with no comments

SharpDevelop2 LGPL'd, can now be used in Closed Source Apps

USE 
ME!!!The SharpDevelop project switched licenses with the 2.0 release in December from GPL to LGPL. That means you can use the components in own applications, both open and closed source. Unlike the GPL, you can statically or dynamically link to LGPL code from open or closed source applications as long as you don't make any changes to the LGPL'd code. This goes against the goals of the Free Software Foundation, but he who writes the code picks the license.

The SharpDevelop switch to LGPL is good news, because SharpDevelop contains some advanced, mature code. Especially useful are the Core / Addin system and the text editor component (which was the cause of this fracas 3 years ago).

The SharpZipLib remains GPL'd but with exception clauses that let you use the component (not the code) anywhere - excerpt from the site: "Bottom line In plain English this means you can use this library in commercial closed-source applications."

Posted by Jon Galloway | 1 comment(s)
Filed under:

Using Markup Based Javascript Effect Libraries to make dynamic sites in standards compliant HMTL

Summary

You can make websites effects with plenty of glitter using simple, standards compliant HTML through the use Javascript effect libraries which operate on CSS classes. I'll talk about why I think it's important, and if you stick with me, I'll point you to some of my favorite lightweight Javascript effect libraries.

The Conflict

Websites need eye-candy. Everyone loves rounded corners, fancy menus, animation, fades, effects... The problem is that the sparkly stuff often leads to unmaintainable code which is inaccessible and doesn't degrade gracefully. The usual solutions are all bad:

  1. Flash
    Hard to maintain, requires proprietary software, doesn't degrade at all, not very accessible[1]. I'm not a big fan of Flash for most websites. I'm happy to see Flash use moving to complex client side web applications, which is about the only place it's justified (maybe, and hopefully not for much longer).
  2. Javascript
    This gets ugly fast. Even if you put your Javascript code in well structured JS files, you usually end up with a lot of javascript event code in HTML attributes. This completely fights the declarative nature of HTML, and leads to really ugly code. Javacript's a pain in the neck to debug, and ugly Javascript is just about impossible to debug. As I've said before, I'm concerned about the messy Javascript code that's being churned out because everybody loves AJAX but IE's Javascript isn't really ready for it.
  3. Try to get out of it
    That's not good either. Customers and end users have grown to expect a certain level of flashyness. Even if you succeed, they're not going to be happy about it.

Let's take a step back. Why do we need to write this Javascript? In many cases, we're adding behaviors and effects that browsers don't support (yet ). If they did support them, though, we'd invoke them declaratively in CSS, just as we do for all the behaviors and effects that CSS currently does support. Instead of directly manipulating the DOM in our HTML, we'd like to be defining content in our HTML and use CSS classes to map behaviors to the content.

Spoiler

We can do this today by writing Javascript effect libraries which operate on HTML elements with CSS specific classes.

The Gutless Disclaimer

I'm primarily talking about the mouseover / hover / onload stuff that makes the page move and shimmer, not AJAX code which calls back to the server. I think this approach could work pretty well for AJAX, but I'm focusing mostly on the simple sparkle stuff here.

But why, and why now?

This isn't a completely new idea. Peter-Paul Koch and Simon Willison were talking about this 2004 (PPK's writeup explains the philosophy behind this pretty well). This is important to consider now, though, as the AJAX movement is causing Javascript use to skyrocket. It's important that we get our Javascript approach right now more than ever. To quote Ben Nolan, author of the Behavior JS library:

It's great to see public uptake of these technologies - but it worries me to see the influx of <SCRIPT> tags and onclick attributes into webpages. For example, check out these html snippets from two well-known AJAX-enabled sites.
Backpack
< span onmouseover ="notesBlock.hoverBegin(128699)"  onmouseout ="notesBlock.hoverEnd(128699, true)">
<
class ="trashcan"  href ="#"  onclick ="if (confirm('Are you sure?')) { new Ajax.Updater('notes', '/page/2326/notes/destroy/128699', { ... 
Flickr
<div id="image_16209134_normal">
<
script language="Javascript">
photo_hash['16209134'] = new Object();
photo_hash['16209134'].title = '2am on Saturday';
</script>
<
h4 id="title_div16209134" style="margin-bottom: 0px; margin-top: 0px;">2am on Saturday</h4>
<
script type="text/javascript">initPhotosUserPageTitle_div('16209134');</script>

The Payoff - Markup Based Effects

Note: Please click the links in this section! I'm including demos here since the pages have great demos. Check them out!

Lightbox JS

Lightbox JS

Lightbox JS is a great example of what I'm talking about. Reference a JS file, then add rel="lightbox" to any image tags on your page and they get a cool popup effect. If the image tag has a title, it's used in the display. If the browser doesn't support it or the page is being rendered in another format (screenreader, print, etc.), it's a standard image tag so it degrades gracefully.

Perfect.

FACE

FACE

FACE (Faruk's Animated CSS Enhancements) is a great example of how to do this right. You include a JS file, then define our behaviors by the CSS class as the library defines (see right). The CSS class syntax may look a little ugly, but if you think about this as virtual classes that define a particular behavior, it makes some sense. To use this, your CSS would include multiple classes that start with myclass - myclass1, myclass2, etc. FACE will automatically step through the classes using the parameters defined in the element's class, as on the right, so to have a div fade in on page load I'd simply define fader1 to have the div invisible and fader2 fully displayed, then set the div's class to S:fader:2:L:20.

See the site for some simple examples. This library really showcases the concept of markup driven behavior well.

S5 Slideshow

Slideshows

There are a few slideshow systems that use this approach. Eric Meyer's S5 Simple Standards-Based Slide Show System (demo ) is my favorite, which uses CSS and Javascript for PowerPoint like slide shows that gracefully degrade since they're built with standards based HTML. View source on the demo - there's a single reference to a JS file, and the rest is clean, HTML.

The Couloir.org slideshow is nice for photo slideshows. It's really slick -there's even sound effects for mouseovers and image changes. Most people would assume it's Flash. There's a slight tradeoff in symantic purity and maintainability - the image references aren't in the HTML, so they aren't visible if you don't have Javascript enabled and maintainence is a little more complex - but the Javascript is all controlled by HTML element ID's, so the Javascript is simple.

Rounded Corners

Rounded Corners

There are a few systems which will round corners for you. Rounding corners looks cool, but it's a pain in the neck. Using fixed size background images isn't at all web friendly and doesn't work when the area needs to expand - more text or browser text size, for instance. You can make expanding boxes with rounded corners built from chopped up images , but that takes 8 or 9 images and 9 div's for each box, which is a lot of complication for a simple design feature. Doing this on the client side in Javascript can barely perceptible square moment as the page loads, but I think the simplicity is often worth the tradeoff. Nifty Corners (demo) is pretty cool and has a ton of options, but isn't completely pure from the CSS driven effects point of view. It operates on CSS ID's, but to invoke it you need to make an Javascript onload call:
Rounded("div#nifty","all","#FFF","#D4DDFF","smooth");

The Rounded() function accepts five parameters:

  1. A CSS selector that indicates on wich elements apply the function
  2. A string that indicates wich corners to round
  3. Outer color of the rounded corners
  4. Inner color of the rounded corners
  5. An optional fifth parameter, that will contain the options for Nifty Corners
I've used Nifty Corners on two sites, and the bang to buck ratio is very high.

Ilkka's Rounded Corners trades functionality for some simplicity and anti-aliasing, but still requires an onload function call. I'm still looking for a Rounded Corner implementation that's invoked via CSS, but I mention these solutions here because they do fulfil the general purpose of enhancing the user experience through the use of Javascript in a clean, unobtrusive manner that works with CSS rather than directly against the DOM.

Are there any Javascript libraries that make this kind of development easier?

Yes, I'm glad you asked!

Ben Nolan's Behavior library does exactly that. It really works, too - remember that amazing slideshow at Couloir.org? Built with Behavior. So is Foopad.com, a free hosted wiki service. Check out some simple Behavior demos here.

Of course, you can use any of the above libraries without writing any Javascript. That's the point. Behavior is for creating new affects like those I mentioned above.

Aren't you going to talk about dropdowns?

S5 Slideshow Dropdowns shouldn't really need Javascript at all. Son of Suckerfish is CSS based, and only used Javascript to work around IE bugs. Don't use mouseover Javascript code when CSS will do.

But what about AJAX?

The careful reader will note that I have avoided this issue through the the use of a Gutless Disclaimer, to which I now refer you.

If this careful reader happens to be familiar with Microsoft's Atlas project, though, the will note that this is the general direction that the Atlas team taken on the generated client side HTML. That "generated" is important - If you browse Live.com and view source, you won't see a scrap of semantic markup. Nothing but includes and javascript. Take a look at the generated source (Firefox WebDeveloper toolbar > View Source > View Generated Source; for IE use either FullSouce or a generated source viewer bookmarklet from here or here). The JS cruft is still there, but the actual HTML part of the page doesn't have a single javascript call despite a high amount of interactivity. That's because the CSS classes are defining the behaviors, and the javascript is acting on the behaviors. I think the feasibility of CSS driven AJAX is a separate discussion, but this shows it is at least a possibility.

Atlas is interesting in that it's Markup Based both in the original source code (on the server) and in the generated HTML on the browser. I'm not sure what this buys when the HTML is generated by a backend framework, but it's interesting.

I'm energized by this discussion and would like to read further. What to do?

Check out this huge CSS tools list. Read PPK's article. Take a look at the Behavior introduction page, which explains the reason the library was created in the first place.

Fin.

[1] Flash can be made accessible, but it's rarely done.

Inexpensive ASP.NET hosting with wildcard subdomains?

I had an idea for a fun little site that would work best with wildcard subdomains[1]. Any suggestions on an inexpensive host? I found LFC Hosting, which would be $15/month with the static IP they require for wildcard subdomains. Cheap is good in this case.

[1] Wildcard subdomains means *.domain.com, where any thing entered before the domain maps to .domain.com. As an example, blue.domain.com, red.domain.comyellow.domain.com, and anycoloryoucanthinkof.domain.com would all map to the same site. This is different from unlimited subdomains which require manual setup - this should all happen on the fly.

Posted by Jon Galloway | with no comments
Filed under: ,

"Vista Re-Introduced (as OSX)" vs. "Developers, Developer, Developers.."

Windows Vista is a heck of a development platform. Head and shoulders over the competition - Mac's stuck with Objective C, and the real action in Linux development these days (at least in my twisted world) is in Mono, the open source implementation of .NET. Group hug...

But this stings a bit... Three short videos of from Bill Gates' Windows Vista demonstration at CES. Well, it's Bill's voice, but the video has been replaced with demonstrations of the same features on Mac OSX, which was released back in 2002. Go ahead, watch all three - they're about 5 minutes total.

  

Now, the text on the page says over and over that it's all in fun, but I think the videos make a serious point. Despite having to reset the code base deep into the development cycle, Vista's bursting at the seams with goodies for developers, but the Vista "out of the box" experience is still really weak . There's no eye-candy, no glitter, no frosting, no excitement.

Now, some of this goes against the grain for me. See, I've been a programmer since grade school and a professional developer for almost a decade. I absolutely love how easy Microsoft has made it to develop professional software. Co-workers have called me Mr. Microsoft because I'm always up on what they're doing with the development platform and just about always fired up about it. The Windows development platform rocks.

But the Operating System out of the box experience is falling behind. Most distributions of Linux, out of the (free) box, come with GIMP (a very solid graphics program), Open Office (an adequate office productivity suite that opens and saves MS Office file formats and has several features that MS Office Pro doesn't include, like save as PDF and Flash), several text editors that make notepad hang its head in shame, and Firefox (a browser that's way ahead of Internet Explorer). Mac OSX Tiger is packed with crowd-pleaser features that matter to average users - see the videos above. The Windows out of the box experience is playing catch-up to the other leading operating systems, and despite great advances elsewhere, Vista catches Windows up to the last releases of OSX and Linux; it definitely doesn't blaze the trail.[1]

The tragedy is not just that the .NET development platform that ships with Vista can do so much more than the bundled software shows, it's that there are plenty of great .NET applications already written and actively used, just begging to be included with Vista. MSDN is littered with demo applications that blow away the tired old applets that ship on the Start Menu. Widen the field a bit to open source .NET apps and you've got Paint.NET, which stomps MS Paint so badly I have to turn my head away and sob. Open it a bit further and license a few "lite" software versions, ala Hyperterminal (read Coding Horror's The Lesson of Hyperterminal for more on why licensing software makes sense). C'mon, guys, give us a complete package Operating System, not a development platform with a few reheated leftovers shoveled on top!

Now, there are a few ways to look at this:

  • They're not shipping for a while, so maybe they're saving some goodies for the final release
    Interesting, but not too likely - the whole idea of beta releases is to catch bugs in the new features

  • This is fine for a blog post, but it's not real world. Real applications need to be internationalized, checked for buffer overruns, support mute polydactyls, etc.
    Bah. Eeyore is designing your projects. This is Microsoft's biggest threat. They cannot give in to this way of thinking. Despite the responsiblity of being a huge software company that lots of guys in ties rely on, they must continue to ship innovative products. They've shipped 8 versions of Visual Studio and 11 versions of Office, Microsoft needs to be capable of shipping a new version of notepad. And managed .NET apps are inherantly more secure than unmanaged applications, right?

  • But these Oracle guys, see, they were piping text through Notepad to other processes and changing Notepad.exe would break their system and...
    These guys are not your customers. These guys must not be allowed to design or control your software. Fine, ship the crusty old Notepad.exe in c:\windows\system32\, but give me a better default text editor. Incremental search would be nice, for instance. Firefox has that, and it's a browser...

  • There's this Plus Pack, see, and it has...
    Nope. This is not an "out of box" experience. My Mom will not download a Plus Pack. 99% of (potential) Windows users will not download a Plus Pack. Windows shouldn't need a Plus Pack, it should be good out of the box. Heck, you should deliver at least as good a product as Linux ($0.00), right?

  • We can't crush the small markets by bundling software like this...
    The features I'm talking about are standard on all other operating systems. This is safe ground.

  • Windows users are free to download any text editor they choose, and...
    No, no no! Your average customer is not Scott Hanselman, it's the housewife who begs her kids to "fix the computer" when they're home for the holidays. It's great that Windows can be customized, but it shouldn't have to be customized just to be usable (or comparable to a Mac).

Prior to the release of Windows Vista, Microsoft should devote some time and a little money to improving (or, preferably, replacing) the basic applets that ship with Windows. Here are some features I'd like to see:

  • The clipboard should have a memory. Something like ClipX would be nice. Of course, if you licensed it, ClipX would be just great. I wrote a clipboard ring in .NET in an evening, it's not that big a deal and it's a nice feature that Windows should include.
  • It's time for a new notepad. That's practically a Hello World application in .NET. If that's too big a deal, license on of the bajillion freeware Notepad replacement programs like Notepad2, but I'd be shocked if you couldn't pull a Notepad.NET together.
  • Bundle Paint.NET. It shows off .NET, it's much better than MSPaint, and just makes sense. Don't question it. Don't say a word. Just key the music...
  • Include a link on the Start Menu that points to free community software that lets you do more with your Windows computer like Cropper. Curate and cultivate this site, and make sure the best of MSDN and GotDotNet get there.
  • Your ideas here... what should Microsoft in Windows Vista to improve the out of the box experience?

To the majority of users, Windows IS the collection of widgets on the Start Menu. Please, give us an upgrade. You can't just sell to developers. You need to sell to consumers, too.

[1] One notable exception is the Media Center experience, which is way ahead of everyone else right now.

Posted by Jon Galloway | with no comments

X-Ray Extension for Firefox

X-Ray Firefox Extension

X-Ray is a cool new Firefox web development extension shows html tags inline with the page so you don't have to keep flipping between the source and the page. Grab it here. Updated version that works with Firefox 1.5 here.

[via Randomize - X-Ray Extension for Firefox]

Posted by Jon Galloway | with no comments
More Posts Next page »