/*                                      `
 * Copyright 2003 Knife Edge Technologies, Inc.  All Rights Reserved.
 * 
 * This software is the proprietary information of Knife Edge Technologies, 
 * Inc. provided under license for use by "The Flying Mule."
 * No part of this file may be copied or re-used without prior written 
 * agreement from Knife Edge Technologies, Inc.
 * http://www.knifeedgetech.com   info@knifeedgetech.com
 */


/**
 * Writes a category's search list IFRAME tag
 */
function catWriteListIFrame( strName, strWidth, strHeight, strSrc )
{
   document.write( "<IFRAME " );
   utlWriteAtt( "name", strName );
   utlWriteAtt( "src", strSrc );
   utlWriteAtt( "width", strWidth );
   utlWriteAtt( "height", strHeight );
   document.write( "marginwidth='0' " );
   document.write( "marginheight='0' " );
   document.write( "frameborder='0' " );
   document.write( "scrolling='no'>" );
   document.writeln( "</IFRAME>" );
}


/**
 * Initializes search form fields from a text field
 */
function catInitForm( frm, strText, strPrefix, strDelim, fldUser )
{
   var ifld;
   var strSearch;
   var strValue;


   // Validate
   if ((frm != null) && (strText != null) && (strPrefix != null))
   {
      // Examine each form field
      for (ifld = 0; ifld < frm.elements.length; ifld++)
      {
         // Valid field?
         if (
             (frm.elements[ifld].name != null)
             &&
             (frm.elements[ifld].name.indexOf( strPrefix ) == 0)
            )
         {
            // Set field value
            utlSetFieldValue( frm.elements[ifld] 
                            , catGetValue( frm.elements[ifld].name
                                         , strDelim
                                         , strText
                                         , true 
                                         , ""
                                         )
                            );
         }
      }

      // Valid user field?
      if (fldUser != null)
      {
         // Set user field value
         fldUser.value = catStripValues( strPrefix, strDelim, strText ).replace( "+", " " );
      }
   }
}


/**
 * Submits a search form after concatenating search text from fields
 */
function catSubmitForm( frm, fldSearch, strPrefix  )
{
   var fValid = false;
   var ifld;
   var strSearch = "";

   // Validate
   if ((frm != null) && (fldSearch != null) && (strPrefix != null))
   {
      // Examine each form field
      for (ifld = 0; ifld < frm.elements.length; ifld++)
      {
         // Valid value?
         if (
             (frm.elements[ifld].name != null)
             &&
             (frm.elements[ifld].name.indexOf( strPrefix ) == 0)
             &&
             (frm.elements[ifld].value.length > 0)
            )
         {
            // Append to search text
            if (strSearch.length != 0)
            {
               strSearch += " ";
            }

            strSearch += frm.elements[ifld].value;
         }
      }

      fldSearch.value = strSearch;
      fValid          = true;
   }

   return (fValid);
}


/**
 * Gets a textual description of the current form selections
 */
function catGetFormDescription( cat, frm, strPrefix, fldUser )
{
   // Initialize
   strDesc   = "";
   cstrTerms = 0;

   // Examine each form field
   for (ifld = 0; ifld < frm.elements.length; ifld++)
   {
      // Valid field?
      if (
          (frm.elements[ifld].name != null)
          &&
          (frm.elements[ifld].name.indexOf( strPrefix ) == 0)
         )
      {
         if (frm.elements[ifld].value != "")
         {
            if (strDesc.length > 0)
            {
               strDesc += " ";
            }

            strDesc += cat.getChild( frm.elements[ifld].value ).getName();
         }
      }
   }

   return (strDesc);
}


/**
 * Extracts a value with a given prefix from a string
 */
function catGetValue( strPrefix, strDelim, strText, fFull, strDefault )
{
   // Initialize default
   strValue = strDefault

   // Look for prefix in text
   ichStart = strText.indexOf( strPrefix );

   // Found prefix?
   if (ichStart != -1)
   {
      // Extract field value
      ichEnd = strText.indexOf( strDelim, ichStart );
      if (ichEnd == -1)
      {
         ichEnd = strText.length;
      }

      strValue = strText.substring( ichStart, ichEnd );

      // Need short value?
      if (!fFull)
      {
         // Strip off prefix
         strValue = strValue.substring( strPrefix.length, strValue.length );
      }
   }

   return (strValue);
}


/**
 * Removes all occurrences of values with a given prefix from a string
 */
function catStripValues( strPrefix, strDelim, strText )
{
   var strClean;
   var ichEnd;

   strClean = "";

   // Strip values until all removed
   ichEnd = 0;
   while (ichEnd > -1)
   {
      // Look for next value
      ichStart = strText.indexOf( strPrefix, ichEnd );

      // Found value?
      if (ichStart != -1)
      {
         // Accept everthing up to value
         strClean += strText.substring( ichEnd, ichStart );

         // Skip value
         ichEnd = strText.indexOf( strDelim, ichStart );

         // More text remaining?
         if (ichEnd != -1)
         {
            // Skip space following value
            ichEnd++;
         }
      }
      else
      {
         // Accept remainder of text
         strClean += strText.substring( ichEnd, strText.length );
         ichEnd   = -1;
      }
   }
     
   return (strClean);
}


/**
 * Writes an input tag
 */
function catWriteInput( strId, strName, strClass, strSize )
{
   // Write name
   catWriteName( strName + ":<BR>", null, strClass );

   // Write Input tag
   document.write( "<INPUT type='text' name='" + strId + "' " );
   utlWriteAtt( "class", strClass );
   utlWriteAtt( "size", strSize );
   document.writeln( ">" );
}


/**
 * Writes an input tag
 */
function catWriteName( strName, strURL, strClass )
{
   // Link?
   if (strURL != null)
   {
      // Open anchor tag
      document.write( "<A href='" + strURL + "' " );
      utlWriteAtt( "class", strClass );
      document.write( ">" );
   }
   else
   {
      utlWriteSpanOpen( strClass );
   }
   
   // Write title
   document.write( strName );

   // Link?
   if (strURL != null)
   {
      // Close anchor tag
      document.write( "</A>" );
   }
   else
   {
      utlWriteSpanClose( strClass );
   }
}


/**
 * Constructs a new category object
 */
function Cat( strId, strName, strURL )
{                   
   // Initialize data members
   this.strId          = strId;
   this.strName        = strName;
   this.strURL         = strURL;
   this.catParent      = null;
   this.acatChild      = utlArraySection( arguments, 3, arguments.length );

   // Initialize public methods
   this.getChild       = Cat_getChild;
   this.getChildByName = Cat_getChildByName;
   this.getId          = Cat_getId;
   this.getName        = Cat_getName;
   this.matchName      = Cat_matchName;
   this.writeChildren  = Cat_writeChildren;
   this.writeHidden    = Cat_writeHidden;
   this.writeName      = Cat_writeName;
   this.writeOption    = Cat_writeOption;
   this.writeSelect    = Cat_writeSelect;
   this.writeTrail     = Cat_writeTrail;
   this.writeTree      = Cat_writeTree;

   // Initialize private methods
   this.pvt_fixUp          = Cat_pvt_fixUp;
   this.pvt_getChild       = Cat_pvt_getChild;
   this.pvt_getChildByName = Cat_pvt_getChildByName;
   this.pvt_matchName      = Cat_pvt_matchName;
   this.pvt_writeTrail     = Cat_pvt_writeTrail;
   this.pvt_writeTree      = Cat_pvt_writeTree;
}


/**
 * Gets a child category specified by a qualified id
 */
function Cat_getChild( strId )
{
   var astrId;
   var istrId;

     
   // Tokenize Id
   astrId = strId.split( "_" );
   istrId = 0;

   // Skip this category's id
   if ((astrId != null) && (astrId[0] == this.strId))
   {
      istrId += 1;
   }

   // Use worker function
   return (this.pvt_getChild( astrId, istrId ));
}


/**
 * Gets a child category with closest matching name
 */
function Cat_getChildByName( astrSearch )
{
   // Use worker function
   return (this.pvt_getChildByName( astrSearch, new Array( null, 0 )));
}


/**
 * Gets this category's id
 */
function Cat_getId( fFull )
{
   var strId;


   strId = this.strId;

   if (fFull && (this.catParent != null))
   {
      strId = this.catParent.getId( true ) + "_" + strId;
   }

   return (strId);
}


/**
 * Gets this category's name
 */
function Cat_getName()
{
   return (this.strName);
}


/**
 * Matches words with this category's name
 */
function Cat_matchName( astrSearch, fModify )
{
   // Use worker function
   this.pvt_matchName( astrSearch, fModify );
}


/**
 * Writes a tree view of this category and its children
 */
function Cat_writeChildren( iDepth, iMaxDepth, strClass )
{
   var icatChild;
   var iPad;


   // Pad according to depth
   for (iPad = 0; iPad < iDepth; iPad++)
   {
      document.write( "&nbsp;&nbsp;" );
   }

   // Write this category
   catWriteName( this.strName + "<BR>", this.strURL, strClass );

   // Writing children also?
   if ((iDepth < iMaxDepth) && (this.acatChild != null))
   {
      for (icatChild = 0; icatChild < this.acatChild.length; icatChild++)
      {
         this.acatChild[icatChild].writeChildren( iDepth + 1
                                                , iMaxDepth
                                                , strClass
                                                );
      }
   }     
}


/**
 * Writes a hidden Input tag for this category
 */
function Cat_writeHidden()
{
   // Write tag
   document.writeln( "<INPUT type='hidden' name='" + this.getId( true ) + "'>" );
}


/**
 * Writes this category's name
 */
function Cat_writeName( strClass, fLink )
{
   catWriteName( this.strName, (fLink ? this.strURL : null), strClass );
}


/**
 * Writes an Option tag for this category
 */
function Cat_writeOption( iLevel, iMaxLevel )
{
   var ccatWritten;
   var ccatChildWritten;
   var icatChild;
   var iPad;


   // Initialize
   ccatWritten = 0;

   // Acceptable depth?
   if (iLevel <= iMaxLevel)
   {
      // Open Option tag
      document.write( "<OPTION value='" );
   
      // Don't specify a value for top level option
      if (iLevel > 0)
      {
         document.write( this.getId( true ) );
      }
      document.write( "'>" );
   
      // Pad according to level
      for (iPad = 0; iPad < iLevel; iPad++)
      {
         document.write( "&nbsp;&nbsp;" );
      }
   
      // Adding any children?
      if ((this.acatChild != null) && (iLevel < iMaxLevel))
      {
         document.write( "Any " + this.strName );
      }
      else
      {
         document.write( this.strName  );
      }
   
      // Close Option tag
      document.writeln( "</OPTION>" );
      
      // Any children?
      if (this.acatChild != null)
      {
         // Write child option tags
         ccatChildWritten = 0;
         for (icatChild = 0; icatChild < this.acatChild.length; icatChild++)
         {
            ccatChildWritten = this.acatChild[icatChild].writeOption( iLevel + 1, iMaxLevel );
            ccatWritten      += ccatChildWritten;
         }
      }
   }

   return (ccatWritten + 1);
}


/**
 * Writes a Select tag for this category
 */
function Cat_writeSelect( strClass, strOnChange )
{
   // Write name
   catWriteName( this.strName + ":<BR>", null, strClass );

   // Open Select tag
   document.write( "<SELECT name='" + this.getId( true ) + "' " );
   utlWriteAtt( "class", strClass );
   utlWriteAtt( "onChange", strOnChange )
   document.writeln( ">" );

   // Write Option tags
   this.writeOption( 0, 99 );

   // Close Select tag
   document.writeln( "</SELECT>" );
}


/**
 * Writes a breadcrumb trail view of this category and its ancestors
 */
function Cat_writeTrail( strClass )
{
   utlWriteSpanOpen( strClass );
   this.pvt_writeTrail( strClass );
   utlWriteSpanClose( strClass );
}


/**
 * Writes 
 */
function Cat_writeTree( strIdChild, strClass )
{
   // Tokenize Id
   astrId = strIdChild.split( "_" );
   istrId = 0;

   // Use worker function
   this.pvt_writeTree( astrId, istrId, strClass );
}


/**
 * Fixes up children's catParent links to point back to this parent.
 */
function Cat_pvt_fixUp()
{
   var icatChild;

   if (this.acatChild != null)
   {
      for (icatChild = 0; icatChild < this.acatChild.length; icatChild++)
      {
         this.acatChild[icatChild].catParent = this;
         this.acatChild[icatChild].pvt_fixUp();
      }
   }
}


/**
 * Gets a child category specified by a tokenized id
 */
function Cat_pvt_getChild( astrId, istrId )
{
   var cat = null;
   var fSearch;
   var icatChild;

   
   // Anything to look for?
   if (
       (astrId != null) 
       && 
       (istrId < astrId.length)
      )
   {
      // Any children to search?
      if (this.acatChild != null)
      {
         fSearch = true;
         icatChild = 0;
         while (fSearch)
         {      
            // Valid child?
            if (icatChild < this.acatChild.length)
            {
               // Matching id?
               if (this.acatChild[icatChild].strId == astrId[istrId])
               {
                  // Check descendants
                  cat     = this.acatChild[icatChild].pvt_getChild( astrId, istrId + 1 );
                  fSearch = false;
               }
            }
            else
            {
               // End of children
               fSearch = false;
            }
         
            icatChild++;
         }
      }
   }
   else
   {
      // No child specified
      cat = this;
   }

   return (cat);
}


/**
 * Gets a child category with closest matching name
 */
function Cat_pvt_getChildByName( astrSearch, aMatch )
{
   var icatChild;
   var cstrMatch;


   // Match words with name
   cstrMatch = this.pvt_matchName( astrSearch, false );

   // Best match so far?
   if (cstrMatch > aMatch[1])
   {
      // Note best match
      aMatch[0] = this;
      aMatch[1] = cstrMatch;
   }

   // Any children?
   if (this.acatChild != null)
   {
      // Try children
      for (icatChild = 0; icatChild < this.acatChild.length; icatChild++)
      {
         this.acatChild[icatChild].pvt_getChildByName( astrSearch, aMatch );
      }
   }

   return (aMatch[0]);
}


/**
 * Matches words with this category's name
 */
function Cat_pvt_matchName( astrSearch, fModify )
{
   return( utlMatchWords( this.strName.toUpperCase(), astrSearch, fModify ));
}


/**
 * Writes a breadcrumb trail view of this category and its ancestors
 */
function Cat_pvt_writeTrail( strClass )
{
   // Child category?
   if (this.catParent != null)
   {
      // Write parent categories
      this.catParent.pvt_writeTrail( strClass );

      // Write spacer
      document.write( "&nbsp;&gt; " );
   }

   // Write this category
   this.writeName( strClass, true );
}


/**
 * Writes a tree view of this category and its children
 */
function Cat_pvt_writeTree( astrId, istrId, strClass )
{
   var icatChild;
   var iPad;

   // Visible category?
   if (this.strURL != null)
   {
      // Pad according to depth
      for (iPad = 0; iPad < istrId; iPad++)
      {
         document.write( "&nbsp;&nbsp;" );
      }
   
      if ((astrId[istrId] != this.strId) || (istrId < (astrId.length - 1)))
      {
         // Write this category
         catWriteName( this.strName + "<BR>", this.strURL, strClass );
      }
      else
      {
         // Write this category
         catWriteName( "<B>" + this.strName + "</B><BR>", this.strURL, strClass );
      }
   }

   // Writing children also?
   if ((astrId[istrId] == this.strId) && (this.acatChild != null))
   {
      for (icatChild = 0; icatChild < this.acatChild.length; icatChild++)
      {
         this.acatChild[icatChild].pvt_writeTree( astrId
                                                , istrId + 1
                                                , strClass
                                                );
      }
   }     
}
