/**
 * @file
 * Miscellaneous utilities.
 *
 * @author Shannon M. Rause <shannon.rause@creativeflavor.com>
 * @version $Revision: 1.9 $
 * @version $Name:  $
 * @version $Id: Utils.js,v 1.9 2006/09/20 03:23:39 smr Exp $
 *
 * Copyright 2005-2006 Creative Flavor, Inc. All rights reserved.
 * 
 * Please see the LICENSE file for specific licensing information.
 * If you did not receive this file or are unable to locate it, 
 * please contact licensing@creativeflavor.com.
 */
/**
 * Utils class.
 *
 * @public
 */
function Utils()
{
} // Utils

Utils.H_POSITION_AUTO   = 'auto';
Utils.H_POSITION_CENTER = 'center';
Utils.H_POSITION_LEFT   = 'left';
Utils.H_POSITION_RIGHT  = 'right';

Utils.V_POSITION_ABOVE  = 'above';
Utils.V_POSITION_AUTO   = 'auto';
Utils.V_POSITION_BELOW  = 'below';
Utils.V_POSITION_CENTER = 'center';


/**
 * Used to change to a location if user confirms message.
 *
 * @param msg  Message to display in confirmation dialog.
 * @param url  URL to go to if user presses "Yes".
 *
 * @public
 */
Utils.changeOnConfirm = function(msg,
                                 url)
{
   if (confirm(msg))
   {
      document.location = url;
   } // if
} // changeOnConfirm


/** 
 * Used to change tabs.
 *
 * @param ids  Array of all tab content ids.
 * @param id   Id of tab content tab to display.
 *
 * @public
 */
Utils.changeTab = function(ids,
                           id)
{
   for (var i = 0; i < ids.length; i++)
   {
      document.getElementById(ids[i]).style.display = 'none';
      document.getElementById(ids[i] + 'Tab').className = '';
   } // for

   document.getElementById(id).style.display = 'block';
   document.getElementById(id + 'Tab').className = 'active';
} // changeTab


/**
 * Comparison routine.
 *
 * @param a Variable to compare.
 * @param b Variable to compare.
 *
 * @return 1 if a > b, -1 if b < a, and 0 if a == b.
 *
 * @public
 */
Utils.cmp = function(a,
                     b)
{
   if (a < b)
   {
      return (-1);
   } // if

   if (a > b)
   {
      return (1);
   } // if

   return (0);
} // cmp


/**
 * Returns the number of days in the given month/year.
 * 
 * @param year    Year.
 * @param month   Month.
 *
 * @return Days in the given month/year.
 *
 * @public
 */
Utils.getDaysInMonth = function(year,
                                month)
{
   switch (month)
   {
      case 2:
         if (Utils.isLeapYear(year))
         {
            return (29);
         } // if

         return (28);

      case 4:
      case 6:
      case 9:
      case 11:
         return (30);
   } // switch

   return (31);
} // getDaysInMonth


/**
 * Gets month name in string.
 *
 * @param month   Month as number (1-12).
 *
 * @return Month string.
 *
 * @public
 */
Utils.getMonthString = function(m)
{
   switch (m)
   {
      case 1:  return 'January';
      case 2:  return 'February';
      case 3:  return 'March';
      case 4:  return 'April';
      case 5:  return 'May';
      case 6:  return 'June';
      case 7:  return 'July';
      case 8:  return 'August';
      case 9:  return 'September';
      case 10: return 'October';
      case 11: return 'November';
      default: return 'December';
   } // switch
} // getMonthString


/**
 * This formats a floating point value.
 *
 * @param value   Floating point value to format.
 * @param format  Format to convert to (in the form of '1.2', '1.3', etc...).
 *
 * @return Formatted float value as a string.
 *
 * @public
 */
Utils.formatFloat = function(value,
                             format)
{
   value = '' + parseFloat(value);
   var temp = format.split('.',
                           2);
   var intPlaces = parseInt(temp[0]);
   var decPlaces = parseInt(temp[1]);
   var pos = value.indexOf('.');

   // decimals.
   if (pos == -1)
   {
      if (decPlaces > 0)
      {
         value += '.';
      } // if

      pos = value.length - 1;
   } // if

   while ((value.length - pos) <= decPlaces)
   {
      value += '0';
   } // while


   // integers.
   var len = intPlaces - pos;

   for (var i = 0; i < len; i++)
   {
      value = '0' + value;
   } // for

   return (value);
} // formatFloat


/**
 * This formats a price value.
 *
 * @param value   Floating point value to format.
 *
 * @return Formatted float value as a price string.
 *
 * @public
 */
Utils.formatPrice = function(value)
{
   return ('$' + Utils.formatFloat(value, '1.2'));
} // formatPrice


/** 
 * Gets the current window height.
 *
 * @return Window height.
 *
 * @public
 */
Utils.getWindowHeight = function()
{
   if (typeof(window.innerHeight) == 'number')
   {
      return (window.innerHeight);
   } // if

   if ((document.documentElement) &&
       (document.documentElement.clientHeight))
   {
      return (document.documentElement.clientHeight);
   } // if

   if ((document.body) &&
       (document.body.clientHeight))
   {
      return (document.body.clientHeight);
   } // if

   return (0);
} // getWindowHeight


/** 
 * Gets the current window width.
 *
 * @return Window width.
 *
 * @public
 */
Utils.getWindowWidth = function()
{
   if (typeof(window.innerWidth) == 'number')
   {
      return (window.innerWidth);
   } // if

   if ((document.documentElement) &&
       (document.documentElement.clientWidth))
   {
      return (document.documentElement.clientWidth);
   } // if

   if ((document.body) &&
       (document.body.clientWidth))
   {
      return (document.body.clientWidth);
   } // else if

   return (0);
} // getWindowWidth


/**
 * Gets the window's x offset.
 *
 * @return X offset.
 *
 * @public
 */
Utils.getWindowXOffset = function()
{
   if (typeof(window.pageXOffset) == 'number')
   {
      return (window.pageXOffset);
   } // if

   if ((document.documentElement) &&
       (document.documentElement.scrollLeft))
   {
      return (document.documentElement.scrollLeft);
   } // if
   
   if ((document.body) &&
       (document.body.scrollLeft))
   {
      return (document.body.scrollLeft);
   } // else if

   return (0);
} // getWindowXOffset


/**
 * Gets the window's y offset.
 *
 * @return Y offset.
 *
 * @public
 */
Utils.getWindowYOffset = function()
{
   if (typeof(window.pageYOffset) == 'number')
   {
      return (window.pageYOffset);
   } // if

   if ((document.documentElement) &&
       (document.documentElement.scrollTop))
   {
      return (document.documentElement.scrollTop);
   } // if
   
   if ((document.body) &&
       (document.body.scrollTop))
   {
      return (document.body.scrollTop);
   } // else if

   return (0);
} // getWindowYOffset


/**
 * This hides all children of a given element.
 *
 * @param elem Element to hide children of.
 *
 * @public
 */
Utils.hideChildren = function(elem)
{
   for (var i = 0; i < elem.childNodes.length; i++)
   {
      if (elem.childNodes[i].style)
      {
         elem.childNodes[i].style.display = 'none';
      } // if
   } // for
} // hideChildren


/**
 * This determines if the given year is a leap year.
 * A leap year is one that is divisible by 4 but not divisible by 100 unless
 * its also divisible by 400.
 *
 * @param year Year to check.
 *
 * @return true if it is a leap year, false if not.
 *
 * @public
 */
Utils.isLeapYear = function(year)
{
   if (!(year % 4))
   {
      return ((year % 100) || (!(year % 400)));
   } // if

   return (false);
} // isLeapYear


/**
 * This parses a dttm string and returns an object.
 *
 * @param dttm Date/time string to parse in the format (YYYYMMDDHHMMSS).
 *
 * @return Object that represents the date/time with attributes: year, month,
 *         monthNameShort, monthNameLong, day, hours24, hours12, minutes, and
 *         seconds.
 *
 * @public
 */
Utils.parseDttm = function(dttm)
{
   if (!dttm)
   {
      return (null);
   } // if

   var obj = new Object();

   obj.year = dttm.substring(0, 4);
   obj.month = dttm.substring(4, 6);
   obj.day = dttm.substring(6, 8);
   obj.hours24 = dttm.substring(8, 10);

   var temp = parseInt(obj.hours24);

   if (temp > 12)
   {
      obj.hours12 = temp - 12;
      obj.amPm = 'pm';
   } // if
   else if (temp == 0)
   {
      obj.hours12 = 12;
      obj.amPm = 'am';
   } // else if
   else if (temp == 12)
   {
      obj.hours12 = 12;
      obj.amPm = 'pm';
   } // else if
   else
   {
      obj.hours12 = obj.hours24;
      obj.amPm = 'am';
   } // else

   obj.minutes = dttm.substring(10, 12);
   obj.seconds = dttm.substring(12, 14);
   
   switch (parseInt(obj.month))
   {
      case 1:  obj.monthNameShort = 'Jan'; obj.monthNameLong = 'January'; break;
      case 2:  obj.monthNameShort = 'Feb'; obj.monthNameLong = 'February'; break;
      case 3:  obj.monthNameShort = 'Mar'; obj.monthNameLong = 'March'; break;
      case 4:  obj.monthNameShort = 'Apr'; obj.monthNameLong = 'April'; break;
      case 5:  obj.monthNameShort = 'May'; obj.monthNameLong = 'May'; break;
      case 6:  obj.monthNameShort = 'Jun'; obj.monthNameLong = 'June'; break;
      case 7:  obj.monthNameShort = 'Jul'; obj.monthNameLong = 'July'; break;
      case 8:  obj.monthNameShort = 'Aug'; obj.monthNameLong = 'August'; break;
      case 9:  obj.monthNameShort = 'Sep'; obj.monthNameLong = 'September'; break;
      case 10: obj.monthNameShort = 'Oct'; obj.monthNameLong = 'October'; break;
      case 11: obj.monthNameShort = 'Nov'; obj.monthNameLong = 'November'; break;
      case 12: obj.monthNameShort = 'Dec'; obj.monthNameLong = 'December'; break;
      default: obj.monthNameShort = '???'; obj.monthNameLong = '???'; break;
   } // switch

   return (obj);
} // parseDttm


/**
 * Positions an element.
 *
 * @param elem Element to position.
 * @param vpos Vertical position.
 * @param hpos Horizontal position.
 *
 * @private
 */
Utils.positionElem = function(elem,
                              vpos,
                              hpos)
{
   if ((vpos == null) ||
       (vpos == undefined))
   {
      vpos = Utils.V_POSITION_AUTO;
   } // if

   if ((hpos == null) ||
       (hpos == undefined))
   {
      hpos = Utils.H_POSITION_AUTO;
   } // if

   // some nice to knows:
   //    elem.clientWidth - Element width.
   //    elem.clientHeight - Element height.
   //    window.o3_x - Pointer X position (obviously).
   //    window.o3_y - Pointer Y position (obviously).
   var top = Utils.getWindowYOffset();
   var left = Utils.getWindowXOffset();
   var w = Utils.getWindowWidth();
   var h = Utils.getWindowHeight();

   switch (vpos)
   {
      case Utils.V_POSITION_ABOVE:
         // above the pointer.
         var y = window.o3_y - elem.clientHeight - 20;
         break;

      case Utils.V_POSITION_BELOW:
         // below the pointer.
         var y = window.o3_y + 10;
         break;

      default:
         if (isNaN(vpos))
         {
            var temp = vpos.substr(0,
                                   vpos.length - 2);
            
            if ((vpos.substr(vpos.length - 2) == 'ab') &&
                (!isNaN(temp)))
            {
               // absolute positioning.
               var y = parseInt(temp);
            } // if
            else
            {
               // center vertically over current mouse pointer.
               var y = Math.round(window.o3_y - (elem.clientHeight / 2));

               // make sure as visible as possible.
               if (hpos == Utils.V_POSITION_AUTO)
               {
                  if (y < (top + 10))
                  {
                     y = top + 10;
                  } // if
                  else if ((y + elem.clientWidth) > (h + top - 20))
                  {
                     y = h + top - elem.clientWidth - 20;
                  } // else if

                  if (y < 0)
                  {
                     y = 0;
                  } // if
               } // if
            } // else
         } // if
         else
         {
            // relative positioning.
            var y = window.o3_y + parseInt(vpos);
         } // else
         break;
   } // switch


   switch (hpos)
   {
      case Utils.H_POSITION_LEFT:
         // to left of pointer.
         var x = window.o3_x - elem.clientWidth - 20;
         break;

      case Utils.H_POSITION_RIGHT:
         // to right of pointer.
         var x = window.o3_x + elem.clientWidth + 10;
         break;

      default:
         if (isNaN(hpos))
         {
            var temp = hpos.substr(0,
                                   hpos.length - 2);
            
            if ((hpos.substr(hpos.length - 2) == 'ab') &&
                (!isNaN(temp)))
            {
               // absolute positioning.
               var x = parseInt(temp);
            } // if
            else
            {
               // center horizontally over current mouse position.
               var x = Math.round(window.o3_x - (elem.clientWidth / 2));

               // make sure as visible as possible.
               if (hpos == Utils.H_POSITION_AUTO)
               {
                  if (x < (left + 10))
                  {
                     x = left + 10;
                  } // if
                  else if ((x + elem.clientWidth) > (w + left - 20))
                  {
                     x = w + left - elem.clientWidth - 20;
                  } // else if

                  if (x < 0)
                  {
                     x = 0;
                  } // if
               } // if
            } // else
         } // if
         else
         {
            // relative positioning.
            var x = window.o3_x + parseInt(hpos);
         } // else
         break;
   } // switch


   // finally, make the actual change.
   elem.style.left = x + 'px';
   elem.style.top = y + 'px';
} // positionElem


/**
 * Removes cells from the given element.
 *
 * @param elem Element to remove cells from.
 *
 * @public
 */
Utils.removeCells = function(elem)
{
   while (elem.cells.length > 0)
   {
      elem.removeChild(elem.firstChild);
   } // while
} // removeCells


/**
 * Removes children from a given element.
 *
 * @param elem Element to remove children from.
 *
 * @public
 */
Utils.removeChildren = function(elem)
{
   while (elem.childNodes.length)
   {
      elem.removeChild(elem.firstChild);
   } // while
} // removeChildren


/**
 * Removes rows from a given element.
 *
 * @param elem Element to remove rows from.
 *
 * @public
 */
Utils.removeRows = function(elem)
{
   while (elem.rows.length > 0)
   {
      elem.removeChild(elem.firstChild);
   } // while
} // removeRows 


/**
 * This trims whitespace from the front and end of a given string.
 *
 * @param s String to remove whitespace from.
 *
 * @return Trimmed whitespace.
 *
 * @public
 */
Utils.trim = function(s)
{
   var i = 0;

   // left.
   for (i = 0; i < s.length; i++)
   {
      if ((s[i] != ' ') && (s[i] != '\t') && (s[i] != '\n'))
      {
         break;
      } // if
   } // for

   if (i >= s.length)
   {
      return ('');
   } // if


   // right.
   var j;

   for (j = s.length - 1; j > i; j--)
   {
      if ((s[j] != ' ') && (s[j] != '\t') && (s[j] != '\n'))
      {
         break;
      } // if
   } // for

   s = s.substring(i,
                   j + 1);
   return (s);
} // trim 


/**
 * Encodes a URL.
 *
 * @param url  URL to encode.
 *
 * @return Encoded URL.
 *
 * @public
 */
Utils.urlEncode = function(url)
{
   return (escape(url).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F'));
} // urlEncode 


/**
 * Converts an XML document to an object.
 *
 * @param xml  XML document to parse.
 *
 * @return Object representation of XML document.
 *
 * @private
 */
Utils.xmlToObject = function(xml)
{
   if (!xml)
   {
      return (null);
   } // if

   if (!xml.documentElement)
   {
      return (null);
   } // if

   return (Utils._xmlElemToObj(xml.documentElement));
} // xmlToObject


/**
 * Recursively gets children from an XML element.
 *
 * @param elem XML element to get children from.
 *
 * @return Object representaton of element.
 *
 * @private
 */
Utils._xmlElemToObj = function(elem)
{
   var i;

   // see if this is a single value.
   if (elem.childNodes.length == 0)
   {
      // empty element.
      return (null);
   } // if

   if ((elem.childNodes.length == 1) &&
       (elem.firstChild.nodeType == 3))
   {
      // single value.
      return (elem.firstChild.nodeValue);
   } // if


   // has children.
   var ret = new Object();

   for (i = 0; i < elem.childNodes.length; i++)
   {
      if (elem.childNodes[i].nodeName.substr(0, 5) == '_NUM_')
      {
         var name = elem.childNodes[i].nodeName.substr(5);
      } // if
      else
      {
         var name = elem.childNodes[i].nodeName;
      } // else

      ret[name] = Utils._xmlElemToObj(elem.childNodes[i]);
   } // for

   return (ret);
} // _xmlElemToObj
