// JSUtils.js
// Javascript utilities

var efdUnknown = 0;
var efdLabel = 1;
var efdImage = 2;
var efdRule = 3;
var efdButton = 4;
var efdEdit = 5;
var efdDate = 6;
var efdNumEdit = 7;
var efdCombobox = 8;
var efdRadiogroup = 9;
var efdCheckbox = 10;
var efdMemo = 11;
var efdAttachment = 12;
var efdGrid = 13;
var efdListbox = 14;
var efdPassword = 15;
var efdSignature = 16;
var efdTime = 17;     
var efdDateTime = 18;
var efdCurrency = 19;
var efdStatus = 20;


var fieldTypes = new Array();
fieldTypes[0] = "Unknown";
fieldTypes[1] = "Label";
fieldTypes[2] = "Image";
fieldTypes[3] = "Rule";
fieldTypes[4] = "Button";
fieldTypes[5] = "Edit";
fieldTypes[6] = "DateTime";
fieldTypes[7] = "Numeric";
fieldTypes[8] = "Combobox";
fieldTypes[9] = "Radiogroup";
fieldTypes[10] = "Checkbox";
fieldTypes[11] = "Memo";
fieldTypes[12] = "Attachment";
fieldTypes[13] = "Grid";
fieldTypes[14] = "Listbox";
fieldTypes[15] = "Password";
fieldTypes[16] = "Signature";
fieldTypes[17] = "DateTime";
fieldTypes[18] = "DateTime";
fieldTypes[19] = "Currency";
fieldTypes[20] = "Status";

// TODO:
// We need to start using the State property of the eworData object rather than
// global vars.

//eworkData states
var esReady            = 0;
var esSubmitInProgress = 1;
var esRefillInProgress = 2;
var esCancelInProgress = 3;
var esInitializing     = 4;
var esAboutToSubmit    = 5;
var esAboutToCancel    = 6;

var NULL_DATETIME = "1753-01-01T00:01:00";
var CURRENT_ATTACHMENT_FIELD = null;
var E_INVALIDARG = 0x80070057;

// Migration to XHTML has resulted in certain HTML field controls being rendered in
// the wrong size. According to Microsoft documentation, these changes are for 
// CSS compliance when document types have been added. This helper function will
// adjust the HTML field control back to the correct size. 
function AdjustHTMLField(aHTMLField)
{
  try
  {
    var style = aHTMLField.getAttribute("style");
    // Adjust the width
    var width = parseFloat(style.width);
    if (!isNaN(width))
    {
      var widthAdjustment = aHTMLField.offsetWidth - width;
      width -= widthAdjustment;
      if (width < 0)
        width = 0;
      style.width = width + "px";
    }
    // Adjust the height
    var height = parseFloat(style.height);
    if (!isNaN(height))
    {
      var heightAdjustment = aHTMLField.offsetHeight - height;
      height -= heightAdjustment;
      if (height < 0)
        height = 0;
      style.height = height + "px";
    }    
  }
  catch (error)
  {
  }
}

//Returns a string after striping all invalid characters
//The result string will contain characters A-Z, a-z, 0-9
function ToAlphaNumericStr(AString)
{
  var LLength = AString.length;
  var LResult = "";
  var LASCIICode = 0;

  for(var i=0; i<LLength; i++)
  {
    LASCIICode = AString.charCodeAt(i);
    if((LASCIICode < 91 && LASCIICode > 64) || (LASCIICode < 123 && LASCIICode > 96) || (LASCIICode < 58 && LASCIICode > 47))      
      LResult += String.fromCharCode(LASCIICode);
  }
  return LResult;
}


function isNumChar(ch) {
  if (ch < '0' || ch > '9')
    return false;
  else
    return true;
}

//converts a string to a number. If a conversion error occurs it returns 0
function StrToNumber(AString)
{
  var LResult = parseFloat(AString);
  if(LResult.toString() == 'NaN')
    LResult = 0;
   
  return LResult;   
}

function StrToIntDef(AString, ADefaultValue)
{
  var LReturnValue = AString*1;
  
  if(LReturnValue != 0)
  {
    if(!LReturnValue)
      LReturnValue = ADefaultValue;      
  }
  return LReturnValue;
}

function ReplaceChar(aSourceString, aCharToReplace, aReplacement)
{
  if(aCharToReplace != "")
  {
    var LResult = '';
    for(var i=0; i<aSourceString.length; i++ )
    {
      if(aSourceString.charAt(i) == aCharToReplace)
        LResult += aReplacement;
      else
        LResult += aSourceString.charAt(i);
    }      
	  return LResult;
  }
  else
    return aSourceString;  
}

function GetSelectedText()
{
  var LResult = '';
  var s = window.document.selection;
  var r = s.createRange();
  if(s.type == "Text")
  {
      r.expand("word");
      LResult = r.text;
  }
  return LResult;
}

function ReplaceText(ASourceString, AStringToReplace, AReplacement)
{
  if(AStringToReplace == "")
    return ASourceString;
    
  var LPos = ASourceString.indexOf(AStringToReplace);
 
  if(LPos == -1)
    return ASourceString;
  else
  {
    var LStringToReplaceLength = AStringToReplace.length;
    var LResult = ASourceString.substring(0, LPos) + AReplacement;
    return LResult + ReplaceText(ASourceString.substr(LPos + LStringToReplaceLength), AStringToReplace, AReplacement);
  }
}

function GetAllowedChars(AFieldType)
{
  var LResult = "";
  switch(AFieldType)
  {
    case efdNumEdit : LResult = "-0123456789" + eworkData.NumberLocale.DecimalSeparator; break;
    case efdCurrency: LResult = "-0123456789" + eworkData.NumberLocale.DecimalSeparator; break;
    case efdDateTime: 
      LResult = "0123456789" + eworkData.DateParser.UserOptions.AllowedDateChars + eworkData.DateParser.UserOptions.DateTimeSeparator + eworkData.DateParser.UserOptions.AllowedTimeChars;      
      break;
    case efdTime:
      LResult = "0123456789" + eworkData.DateParser.UserOptions.AllowedTimeChars;
      break;  
    case efdDate: 
      LResult = "0123456789" + eworkData.DateParser.UserOptions.AllowedDateChars;      
      break;                   
  }
  
  return LResult; 
}

function eWorkAlert(AMessage)
{
    eventsCanFire = false;    
    alert(AMessage);
    eventsCanFire = true;  
}

// Cookies --------------------------------------------------------------------------------

function getCookie(name) 
{
  var cName = URLEncode(name);
  // create an array of cookies
  var dc = document.cookie.split(";");
  if (dc.length > 0) 
  {
    // trim all leading and trailing white spaces from the cookie strings
    for (var i = 0; i < dc.length; i++)
    {
      dc[i] = StringTrim(dc[i]);
    }
    var value = ArrayValueOfName(dc, cName);
    if (value == null)
    {
      // Delphi patch. If we can't find the cookie. The cookie may be HTTPEncode() by Delphi's
      // SetCookieField() function.
      cName = HTTPEncode(name);
      value = ArrayValueOfName(dc, cName);
      if (value == null)
      {
        // Java URL Packager patch. If we can't find the cookie. The cookie may be base32 encoded 
        // by the Java URL Packager.
        cName = StringToBase32String(name);
        value = ArrayValueOfName(dc, cName);
        if (value != null)
        {
          return StringFromBase32String(value);
        }
      }
      else
      {
        return HTTPDecode(value);
      }
    }
    else
    {
      return URLDecode(value);
    }
  }
  return "";
}

function setCookie(name, value, nDaysToExpiry, aDoc) 
{
  var exp = null;
  if (nDaysToExpiry > 0) 
  {
    exp = new Date();
    exp.setTime(exp.getTime() + (1000 * 60 * 60 * 24 * nDaysToExpiry));
  }

  var doc = null;
  if (setCookie.arguments.length > 3)
    doc = aDoc;
  else
    doc = document;

  doc.cookie = URLEncode(name) + "=" + URLEncode(value) + "; path=/" +
  ((exp == null) ? "" : "; expires=" + exp.toGMTString());
}

function deleteCookie(name) 
{
  document.cookie = URLEncode(name) + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT" + "; path=/";
}

function getTimestamp() 
{
  var now = new Date();
  var D = now.getDate();
  var M = now.getMonth();
  var Y = now.getYear();
  var h = now.getHours();
  var m = now.getMinutes();
  var s = now.getSeconds();

  var str = Y.toString() + M.toString() + D.toString();
  str += h.toString() + m.toString() + s.toString();
  return str;
}

function getTime() 
{
  var now = new Date();
  return now.getTime();
}

function eworkShowSubmit(aVisible)
{
  try
  {
    if (aVisible)
      parent.cellConfirm.style.visibility = "visible";
    else
      parent.cellConfirm.style.visibility = "hidden";
  }
  catch (error)
  {
  }
}

function eworkShowCancel(aVisible)
{
  try
  {
    if (aVisible)
      parent.cellCancel.style.visibility = "visible";
    else
      parent.cellCancel.style.visibility = "hidden";
  }
  catch (error)
  {
  }
}

function GetField(AFieldName, AFieldAttribute)
{
  return eworkGetField(AFieldName, AFieldAttribute);
}

function eworkGetField(AFieldName, AFieldAttribute)
{
  var returnThis = "";
  
  if (AFieldName.toString() != "" ) 
  { 
    var ewdField = eworkData.FieldByName(AFieldName);  
    if (ewdField != null) 
    {              
      var fHTMLfield = ewdField.HTMLfield; 
      var curType = ewdField.Type;      
      var fAttrib = (AFieldAttribute ? AFieldAttribute.toLowerCase() : "");
 
      if (fAttrib == "index")
      {
        returnThis = ewdField.SelectedIndex();
      } 
      else if(fAttrib == "fieldtype")
      {
          returnThis = ewdField.FieldTypeName;
      } 
      else if(fAttrib == "fieldid")
      {
          returnThis = ewdField.index;      
      } 
      else if(fAttrib == "fieldname")
      {
          returnThis = ewdField.Name;
      } 
      else if(fAttrib == "style")
      { 
          returnThis = ewdField.Style();           
      } 
      else if(fAttrib == "top")
      {
          returnThis = ewdField.Top();
      }   
      else if(fAttrib == "left")
      {
          returnThis = ewdField.Left();
      } 
      else if(fAttrib == "width")
      {
          returnThis = ewdField.Width();
      } 
      else if(fAttrib == "height")
      {
          returnThis = ewdField.Height();
      }                   
      else if(fAttrib == "caption")
      {
          returnThis = ewdField.Caption();
      } 
      else if(fAttrib == "hint")
      {
          returnThis = ewdField.Hint();
      } 
      else if(fAttrib == "status")
      {      
         if(curType == efdStatus)
            returnThis = ewdField.GetStatusAsString()            
      }       
      else if(fAttrib == "items")
      {
        if(curType == efdRadiogroup)
        {  
          returnThis = ewdField.ItemValues();
        } 
        else if(curType == efdCombobox || curType == efdListbox)
        {
          for(var i=0; i < fHTMLfield.options.length; i++)
          {
            returnThis += (i == 0 ? "" : ",") + fHTMLfield.options[i].text;
          } 
        } 
        else 
        {
           alert(AFieldName + ' ' + sUNSUPPORTED_ITEMS);
        }
      } 
      else 
      {
        //get the value  
        if(curType == efdCheckbox)
          returnThis = (ewdField.GetDisplayValue() ? "1" : "0");
        else if( (curType == efdNumEdit) || (curType == efdCurrency) )
        {
          if(ewdField.Parser.StringToRawNumber(ewdField.GetDisplayValue()) * 1 == 0)
            returnThis = "0";
          else
            returnThis = ewdField.GetDisplayValue();
        }  
        else if( (curType == efdDate) || (curType == efdTime) || (curType == efdDateTime) )  
        {
            if ( ewdField.ReturnEngineDate )
            {
              // Returns engine date back to the client.
              returnThis = ewdField.EngineDate;
              if (returnThis == NULL_DATETIME)
                returnThis = "";              
            }
            else
            {
              // Must be a call to check date field visible dependants.
              returnThis = ewdField.GetDisplayValue();
            }
        }
        else
          returnThis = ewdField.GetDisplayValue();     
      }      
    } // End if form not found
  } // End if invalid args
  return returnThis;
}

function SetField(fName, fAttribute, Value)
{ 
  eworkSetField(fName, fAttribute, Value);
}

function eworkSetField(fName, fAttribute, Value)
{ 
  var ewdField = eworkData.FieldByName(fName);
  if(ewdField != null)
  {                               
    fAttribute = fAttribute.toLowerCase();
    if(fAttribute == "" || fAttribute == "value")
    {
      var curType = ewdField.Type;  
      if(curType == efdCheckbox)
      {
        var valRequest = Value.toLowerCase();
        (valRequest == "checked" || valRequest == "check" || valRequest == "true" || valRequest == "1" ? updByName(ewdField.Name,"-1") : updByName(ewdField.Name, "0"));
      } else if( (curType == efdListbox || curType == efdCombobox) ){
        if(!ewdField.ReadOnly) 
          ewdField.UpdateSelectedValue(Value); 
      } else if( curType == efdStatus ){
      
          //do nothing. Users are not allowed to set status fields               

      } else {
        // We'll set the null engine date if the caller passes in an empty string.
        if ( ((curType == efdDate) || (curType == efdTime) || (curType == efdDateTime)) &&
              (Value.length == 0) )
          Value = NULL_DATETIME;                
        eventsCanFire = false;      
        ewdField.SetValue(Value);      
        eventsCanFire = true;      
      }
      ewdField.SetCondionalVisibility();
    }
  }  
}
                 
function GetElapsedTime(AStartDate, AStopDate)
{
  var LElapsedDate = AStopDate - AStartDate; 
  return  LElapsedDate.valueOf(); 
}

function makeArray() {
    for (var i = 0; i<makeArray.arguments.length; i++)
        this[i] = makeArray.arguments[i];
}

var convert = new makeArray('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');

function returnBase(number,base) {
    if (number < base) var output = convert[number];
    else {
        var MSD = '' + Math.floor(number / base);
        var LSD = number - MSD*base;
        if (MSD >= base) var output = returnBase(MSD,base) + convert[LSD];
        else var output = convert[MSD] + convert[LSD];
    }
    return output;
}

function HexEncode(aString)
{
  var lResult = "";

  // UTF8 encode to deal with unicode characters. 
  var lStr = UTF16to8(aString);

  var lCode;
  var lChr;
  for(var i=0; i< lStr.length; i++)
  {
    lCode = lStr.charCodeAt(i);
    lChr = returnBase(lCode, 16) + "";
    if (lCode < 10)
      lResult += "0";
    lResult += lChr;
  }

  return lResult;
}   

function LocaleInfo()
{                       
  var LUserOptions = null;
  
  try
  {
    LUserOptions = eWorkData.DateParser.UserOptions;
  }
  catch (error)
  {
    LUserOptions = new eUserOptions();
  }
       
  this.DecimalSeparator  = LUserOptions.DecimalSeparator;
  this.ThousandSeparator = LUserOptions.ThousandSeparator;
  return this;
}

function point()
{
  this.top = 0;
  this.left = 0;
  return this;
}

function eworkDate(AISOFormat, AISOMin, AISOMax, AType, ADisplayFormat)
{
  this.Min           = AISOMin;
  this.Max           = AISOMax;
  this.ISOFormat     = AISOFormat;
  this.DisplayFormat = ADisplayFormat;
  this.Type          = AType;
  this.ErrorString   = "";
  return this;
}  

//Returns a zero based month index 0-11
function GetMonthIndex(AISODate)
{
  if ((AISODate == "") || (AISODate == NULL_DATETIME))
    return -1;
  else  
    return (AISODate.substr(5, 2)*1) - 1;
}

function SetMonth(AJSDate, AMonthIndex)
{
   //This year's no of days for each month   
   var md = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 	      
   //if pickyear is leap adjust Feb's no of days
   var pickyear = AJSDate.getFullYear(); 
   if((!(pickyear % 4) && (pickyear % 100)) || !(pickyear % 400))
     md[1]++;
     
   AJSDate.setMonth(AMonthIndex);
   //Check to see whether the month was set correctly
   if(AMonthIndex != AJSDate.getMonth())
   {
     //Month was not set correctly as a result of trying to pick a month
     //with more days than it supports. In this case the next month is picked.       
     //Set the maximum days for the month we are trying to select
     AJSDate.setDate(md[AMonthIndex]);       
     //Set the month again
     AJSDate.setMonth(AMonthIndex);
   } 
}

function DateTimeFromISO(AISODate, AWantTime)
{ 
  var LDate = new Date();
  if(AISODate == "")
  {
    if(!AWantTime)
      LDate.setHours(0,0,0,0);
  }    
  else
  {
    var LValid = false;

    do
    {
      var LDateTimeArray = AISODate.split('T');
      if (LDateTimeArray.length != 2)
        break;         
      var LDateArray = LDateTimeArray[0].split('-');
      if (LDateArray.length != 3)
        break;         
      
      LDate.setFullYear(LDateArray[0]);
      SetMonth(LDate, LDateArray[1]-1);              
      LDate.setDate(LDateArray[2]);     
      if(AWantTime)
      {  
        var LTimeArray = LDateTimeArray[1].split(':');
        if (LTimeArray.length != 3)
          break;
        LDate.setSeconds(LTimeArray[2]);
        LDate.setMinutes(LTimeArray[1]);
        LDate.setHours(LTimeArray[0]);
      }
      else
        LDate.setHours(0,0,0,0);

      LValid = true;

    } while (false);

    if (!LValid)
    {
      // throws an invalid argument execption, E_INVALIDARG 
      throw new Error(E_INVALIDARG, "invalid date/time");
    }
  }   
  
  return LDate;        
}
  
function ISODate(aJSDate)
{           
  var LDay = aJSDate.getDate();
  LDay = (LDay < 10 ? '0' : '') + LDay;
  var LMonth = aJSDate.getMonth() + 1; 
  LMonth = (LMonth < 10 ? '0' : '') + LMonth;  
  var LDatePart = aJSDate.getFullYear() + '-' + LMonth + '-' + LDay; 
  var LHours = aJSDate.getHours();   
  LHours = (LHours < 10 ? '0' : '') + LHours;  
  var LMins = aJSDate.getMinutes();      
  LMins = (LMins < 10 ? '0' : '') + LMins;  
  var LSecs = aJSDate.getSeconds();      
  LSecs = (LSecs < 10 ? '0' : '') + LSecs;  
  var LTimePart = LHours + ':' + LMins + ':' + LSecs;            
  return LDatePart + 'T' + LTimePart;     
}

function EngineISODate(aJSDate, aTimeOnly)
{
  var LDatePart = "1753-01-01";
  if ( !aTimeOnly )
  {
    var LDay = aJSDate.getDate();
    LDay = (LDay < 10 ? '0' : '') + LDay;
    var LMonth = aJSDate.getMonth() + 1; 
    LMonth = (LMonth < 10 ? '0' : '') + LMonth;  
    LDatePart = aJSDate.getFullYear() + '-' + LMonth + '-' + LDay;
  }

  var LHours = aJSDate.getHours();   
  LHours = (LHours < 10 ? '0' : '') + LHours;  
  var LMins = aJSDate.getMinutes();      
  LMins = (LMins < 10 ? '0' : '') + LMins;  
  var LSecs = aJSDate.getSeconds();      
  LSecs = (LSecs < 10 ? '0' : '') + LSecs;  
  var LTimePart = LHours + ':' + LMins + ':' + LSecs;

  return LDatePart + 'T' + LTimePart;
}

//
// Test for NULL engine date
//
function isNullEngineDate(aISODate, aCheckTime)
{
  var retValue = false;

  if ( aCheckTime )
    retValue = (aISODate == NULL_DATETIME) ? true : false;
  else
  {
    var LDateTimeArray = aISODate.split('T');
    retValue = ( NULL_DATETIME.indexOf(LDateTimeArray[0]) != -1 ) ? true : false;
  }

  return retValue;
}

function ieCompatibleWith(majorVersion, minorVersion) 
{
  var curVersion = navigator.appVersion;
  var curMajorVersion;
  var curMinorVersion;
  var ieVersion;  
  
  curVersion = curVersion.substr(curVersion.indexOf("MSIE") + 5, 3); 
  ieVersion = curVersion.split(".");
  curMajorVersion = ieVersion[0];
  curMinorVersion = ieVersion[1];
  if((majorVersion >= curMajorVersion) && (minorVersion >= curMinorVersion))
  {                  
    return true;     
  } else {        
    return false;   
  }  
}  

function URLEncode(aString)
{
  aString = new String(aString);
  // UTF8 encode to deal with unicode characters. 
  var lStr = UTF16to8(aString);
  // Now, URL encode input string
  lStr = escape(lStr.replace(/[ ]/g, "+"));
  return lStr;
}

function URLDecode(aString)
{
  var lRawString = unescape(aString);
  lRawString = lRawString.replace(/[+]/g, " ");
  // UTF8 decode to return unicode characters.
  var lRawString = UTF8to16(lRawString);
  return lRawString;
}

/* Delphi patch. Converts a string into a form that contains no values that are illegal 
 * in an HTTP message header. */
function HTTPEncode(AString)
{
  var LAllowedChars = "'()!$@-_.";
  var LChar = "";
  var LResult = "";

  for (var k=0; k<AString.length; k++) 
  {
    LChar = AString.charAt(k);
    if(LChar == " ")
      LResult += "+";
    else
    { 
      if(LAllowedChars.indexOf(LChar) == -1)
      {  
        LResult += escape(LChar);      
      }
      else
        LResult += LChar;
    }      
  }  
  
  return LResult;
}

/* Delphi patch. Decodes a string that includes HTTP escape characters. */
function HTTPDecode(AString)
{
  var LAllowedChars = "'()!$@-_.";
  var LChar = "";
  var LResult = "";

  for (var k=0; k<AString.length; k++) 
  {
    LChar = AString.charAt(k);
    if(LChar == "+")
      LResult += " ";
    else
    { 
      if(LAllowedChars.indexOf(LChar) == -1)
      {  
        LResult += unescape(LChar);      
      }
      else
        LResult += LChar;
    }      
  }  
  
  return LResult;
}

/*  Function to encode a string with XML entity references */
function XMLEncode(aString)
{
  var lRegExp = /[<>&\'\"]/g;
  var lReplacement = aString.replace(
    lRegExp,
    function($0)
    {
      return '&#' + $0.charCodeAt(0) + ';';
    }
  );

  return lReplacement;
}

// Function to decode XML entity references in a string
function XMLDecode(aString)
{
  var lRegExp = /(&apos;)|(&amp;)|(&lt;)|(&gt;)|(&quot;)|(&#\d{2};)|(&#x[A-Fa-f0-9]{2};)/g;
  var lReplacement = aString.replace(
    lRegExp,
    function($0)
    {
      var ret;
      var code;

      switch ($0)
      {
        case "&apos;":
          ret = "'";
          break;
        case "&amp;":
          ret = "&";
          break;
        case "&lt;":
          ret = "<";
          break;
        case "&gt;":
          ret = ">";
          break;
        case "&quot;":
          ret = "\"";
          break;
        default:
          // Check if we have a hexidemcimal reference
          if ($0.substr(0,3) == "&#x")
          {
            code = parseInt($0.substr(3,2), 16);
            if (isNaN(code))
               ret = $0;
            else
               ret = String.fromCharCode(code);
          }
          // Check if we have a numeric reference
          else if ( $0.substr(0,2) == "&#" )
          {
            code = parseInt($0.substr(2,2));
            if (isNaN(code))
               ret = $0;
            else
               ret = String.fromCharCode(code);
          }
          else
            ret = $0;
      }
     
      return ret;
    }
  );

  return lReplacement;
}

// Function to decode a DMS location string. A dynamic DMS location string can contain the dynamic
// contents in the following format:
//
// %field.FieldName
//
// Where FieldName represents the field name whose value will be used to subsitute the dynamic content
// at runtime. A % literal character must be escape using the %% character sequence. 
function ParseDmsLocation(aLocation)
{
  var lRegExp = /(%%)|(%field\x2e\w*)/g;
  var lReplacement = aLocation.replace(
    lRegExp,
    function($0)
    {
      var ret;

      if ($0 == "%%")
        ret = "%";
      else
      {
        var lFieldName = $0.substr(7, $0.length - 1);
        var lField = eworkData.FieldByName(lFieldName);
        if (lField != null)
          ret = lField.HTMLfield.value;
        else
          ret = $0;
      }

      return ret;
    }
  );

  return lReplacement;
}

// Returns an object to send AJAX request to the server. This function
// uses a Lazy Function Definition Pattern to avoid having to create
// the object every time.
//
// We can scrape this as soon as we moved over to ASP.NET AJAX.
var GetAjaxWorker = function() 
{
  var http;
  
  try 
  {
    http = new XMLHttpRequest;
    GetAjaxWorker = function() 
    {
      return new XMLHttpRequest;
    };
  }
  catch(e) 
  {
    // IE 6 support
    var msxml = 
    [
      "MSXML2.XMLHTTP.3.0",
      "MSXML2.XMLHTTP",
      "Microsoft.XMLHTTP"
    ];
    
    for (var i=0, len = msxml.length; i < len; ++i) 
    {
      try {
        http = new ActiveXObject(msxml[i]);
        GetAjaxWorker = function() {
          return new ActiveXObject(msxml[i]);
        };
        break;
      }
      catch(e) {}
    }
  }
  
  return http;
};

// -- Opens attachment file and shows user the contents
function presentAttachment(aFile)
{
  var newURL = "eReadAttachment.ashx?Service=" + URLEncode(eServiceName);
  newURL += "&AttachmentFile=" + aFile;
  newURL += "&AttachmentType=Folder" + "&AttachmentOwner=" + eworkData.ewFolderID;
  if (getCookie(eServiceName + 'Client') == 'external')
  {
    var lSessionID = getCookie(eServiceName + 'SessionID')
    newURL += '&SN=' + lSessionID;
  }   

  // Window open is slow if the name argument is passed.
  var newWindow = window.open(newURL, "", "menubar=yes,toolbar=yes,scrollbars=yes,location=yes,status=yes,resizable=yes");
  newWindow.name = "ClipViewWindow";
}

function doUploadAttachment(AField, AFolderID) 
{            
   var LLeft = (screen.availWidth - 500)/2;   
   var LTop = (screen.availHeight - 110)/2;
   var LParams = "top=" + LTop + ",left=" + LLeft + ",width=500,height=110,alwaysRaised=yes,resizable=yes";   
   // construct the request URL
   var LURL = "eUploadAttachmentDialog.aspx?FolderID=" + AFolderID;

   // get the service name from our global variable to allow multiple services to run simultaneously
   var lService  = URLEncode(eServiceName);
   LURL         += '&Service=' + lService;
   
   if (getCookie(eServiceName + 'Client') == 'external')
   {
     var lSessionID = getCookie(eServiceName + 'SessionID')
     LURL += '&SN=' + lSessionID;
   }   
   
   // Save the attachment grid focus
   lastVisitedControl = AField.HTMLfield;
   lastVisittedEditableControl = AField.HTMLfield;

   CURRENT_ATTACHMENT_FIELD = AField;
   window.open(LURL, null, LParams);
}

//-----------------------------------------------------------------------------
//              DateTime Editors support functions
//-----------------------------------------------------------------------------

function drawClockPicker(aPoint, aDialogArgs) 
{
  var lUrl = "eTimePicker.aspx?Service=" + URLEncode(eServiceName);
  if(navigator.userAgent.indexOf("MSIE 6.")>-1)
    var LHeight = 200;
  else
    var LHeight = 180;    
    
  return window.showModalDialog(lUrl, aDialogArgs, ModalDialogParams(aPoint, 127, LHeight)); 
}  
 
function ModalDialogParams(aPoint, aWidth, aHeight)
{   
  if(navigator.userAgent.indexOf("MSIE 6.")>-1)
    aHeight += 35; //XP SP2 + IE6 statusbar adjustment
  else
   aHeight-=16; // remove chrome adjustment in IE 7
  var params = "dialogHeight:" + aHeight + "px;dialogWidth:" + aWidth + "px;dialogLeft:"; 
  aPoint.top += window.screenTop;
  aPoint.left += window.screenLeft;     
  params += aPoint.left + "px;dialogTop:" + aPoint.top + "px;status:no;help:no"; 
  return params;
}  

function drawDateTimePicker(aPoint, aDialogArgs) 
{
  var lUrl = "eDateTimePicker.aspx?Service=" + URLEncode(eServiceName);  
  return window.showModalDialog(lUrl, aDialogArgs, ModalDialogParams(aPoint, 375, 235));  
}
   
function drawCalendarPicker(aPoint, aDialogArgs) 
{  
  var lUrl = "eDatePicker.aspx?Service=" + URLEncode(eServiceName);
  return window.showModalDialog(lUrl, aDialogArgs, ModalDialogParams(aPoint, 215, 205));
}     

//-----------------------------------------------------------------------------
//              Client side dependent cell functions
//-----------------------------------------------------------------------------

//Returns the currently selected row. 
function eworkGetCurrentRow(AGridName)
{
   var LGrid = eworkData.FieldByName(AGridName);
   if(LGrid && LGrid.Type == efdGrid)
     return LGrid.CurrentRowIndex();
   else
     return -1;
}

//Returns the currently selected column. Comments as for GetCurrentRow
function eworkGetCurrentCol(AGridName)
{
   var LGrid = eworkData.FieldByName(AGridName);
   if(LGrid && LGrid.Type == efdGrid && LGrid.GridType == 'Edit')
   {
     if(LGrid.AccessibilityOn)
       return LGrid.ActiveColumnIndex - 1;
     else
       return LGrid.ActiveColumnIndex;
   }
   else
     return -1;
}

//Returns the number of rows in the grid, excluding the header.
function eworkGetRowCount(AGridName)
{
   var LGrid = eworkData.FieldByName(AGridName);
   if(LGrid && LGrid.Type == efdGrid)
     return LGrid.RowCount();
   else
     return 0;
}

//Returns the value of the cell specified by the zero-based Col and Row 
function eworkGetCell(AGridName, AColIndex, ARowIndex)
{
   var LGrid = eworkData.FieldByName(AGridName);
   var LReturnedValue = LGrid.GetCell(AColIndex, ARowIndex);
   
   return LReturnedValue;
} 

//Sets the value of the cell specified by the zero-based Col and Row
function eworkSetCell(AGridName, AColIndex, ARowIndex, AValue)
{  
   var LGrid = eworkData.FieldByName(AGridName);  
   
   AColIndex = parseInt(AColIndex);
   if(LGrid.AccessibilityOn)
     AColIndex++;   
   ARowIndex = parseInt(ARowIndex);    

   if(LGrid && LGrid.Type == efdGrid && LGrid.GridType == 'Edit')
   {
     try
     {
       var LRow = LGrid.HTMLfield.rows(ARowIndex + 1);       
       if ( LRow != null )
       {
         var LCell = LRow.cells(AColIndex);
         if (LCell != null)
         {
           var LCurrentCell = LCell.children[0];
           var LCurColumn = LGrid.ColumnByName(LCurrentCell.dataFld); 
           switch(LCurColumn.Type)
           {
             case ctNUMBER:
               AValue = LCurColumn.Parser.Valid(new String(AValue));
             break;        
             case ctCURRENCY:       
               AValue = LCurColumn.Parser.Valid(new String(AValue));
             break;
             case ctDATETIME: 
               var LeworkDate = new eworkDate(new String(AValue), "", "", DateTypeFromParts(LCurColumn.Parts), "");
               
               // PF28492. Before we validate it we need to get the display format because the validate works on the 
               // display format. If we pass just the ISO format then the validation will fail.
               switch (LeworkDate.Type) 
               {               
                  case efdDateTime: LeworkDate.DisplayFormat = eworkData.DateParser.ISOToLocaleDateTime(LeworkDate.ISOFormat); break;
                  case efdTime: LeworkDate.DisplayFormat = eworkData.DateParser.ISOToLocaleTime(LeworkDate.ISOFormat); break;
                  default: LeworkDate.DisplayFormat = eworkData.DateParser.ISOToLocaleDate(LeworkDate.ISOFormat); break;        
               }      
               
               eworkData.DateParser.Validate(LeworkDate); 

               if (LeworkDate.ErrorString == "")
               {  AValue = LeworkDate.DisplayFormat;}
               else
               {  
                 eWorkAlert( LeworkDate.ErrorString );
                 AValue = null; 
               }

               LeworkDate = null;
             break;        
             case ctTEXT:  
             break;                  
           }  
           
           var LNeedsUpdating = true;
           if ( null != AValue )
           {   
              if(LCurrentCell.tagName == "INPUT")
              {
                 LNeedsUpdating = (LCurrentCell.value != AValue);
                 if (LNeedsUpdating)                 
                    LCurrentCell.value = AValue;  
              }
              else
              {
                 LNeedsUpdating = (LCurrentCell.innerText != AValue);
                 if (LNeedsUpdating)                 
                   LCurrentCell.innerText = AValue;
              }  
           }
           
           // Update editable grid underlying delta data but don't display alerts
           if (LNeedsUpdating)  
             LGrid.FinalizeRow(ARowIndex, false);           
         }
       }
     }
     catch (error)
     {
       //alert(error.Message)
     }
   }
} 

function setClientFlag()
{
  // Indicates the main Metastorm BPM Client is running
  setCookie('eWorkClient','1',-1);  
}

function UTF16to8(aString) 
{
  var out, i, len, c;

  out = "";
  len = aString.length;
  for (i = 0; i < len; i++) 
  {
	  c = aString.charCodeAt(i);
	  if ((c >= 0x0001) && (c <= 0x007F)) 
    {
	    out += aString.charAt(i);
	  } 
    else if (c > 0x07FF) 
    {
	    out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
	    out += String.fromCharCode(0x80 | ((c >>  6) & 0x3F));
	    out += String.fromCharCode(0x80 | ((c >>  0) & 0x3F));
	  } 
    else 
    {
	    out += String.fromCharCode(0xC0 | ((c >>  6) & 0x1F));
	    out += String.fromCharCode(0x80 | ((c >>  0) & 0x3F));
	  }
  }

  return out;
}

function UTF8to16(aString)
{
  var out, i, len, c;
  var char2, char3;

  out = "";
  len = aString.length;
  i = 0;
  while (i < len) 
  {
	  c = aString.charCodeAt(i++);
	  switch (c >> 4)
	  { 
	    case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
	      // 0xxxxxxx
	      out += aString.charAt(i-1);
	      break;
	    case 12: case 13:
	      // 110x xxxx   10xx xxxx
	      char2 = aString.charCodeAt(i++);
	      out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
	      break;
	    case 14:
	      // 1110 xxxx  10xx xxxx  10xx xxxx
	      char2 = aString.charCodeAt(i++);
	      char3 = aString.charCodeAt(i++);
	      out += String.fromCharCode(((c & 0x0F) << 12) |
					     ((char2 & 0x3F) << 6) |
					     ((char3 & 0x3F) << 0));
	      break;
	  }
  }

  return out;
}

// Examine the provided URL to identify if the page has been opened using 
// an external client URL or as part of the main Metastorm BPM page.
function isExternalURL(aURL)
{
  return -1 != aURL.toLowerCase().indexOf( "client=external" );
}
  
//
// Set a timestamp string to a Web TP URL.
//
function setClientTimestamp(aURL)
{
  // Attempt to locate existing timestamp value in the URL.
  var existingTimestamp = "";
  var startPos          = aURL.toLowerCase().indexOf( "timestamp=" );
  
  if ( -1 != startPos )
  {
    var endPos = aURL.indexOf( "&", startPos );
    
    if ( -1 == endPos )
    {  endPos = aURL.length; } 
    
    existingTimestamp = aURL.substring( startPos, endPos );
  }
  
  // Build date/time value.
  var now = new Date();
  var lDateTime = URLEncode(ISODate(now));

  // Add or replace timestamp value, as appropriate.
  if ( 0 == existingTimestamp.length )
  { 
    aURL += ((aURL.indexOf("?") == -1) ? "?" : "&");
    aURL += ("Timestamp=" + lDateTime);
  }
  else
  { 
    var newTimeStamp = "Timestamp=" + lDateTime;
    aURL = aURL.replace( existingTimestamp, newTimeStamp ); 
  }
    
  return aURL;
}

//
// Function to trim leading and trailing white space
//
function StringTrim(aString)
{
  var lResultString = aString.replace(/(^\s*)|(\s*$)/g, "");

  return lResultString;
}

//
// Returns the index of an array item
//
function ArrayValueOfName(anArray, aName)
{ 
  var lReturnedValue = null;

  var lStr = aName + "=";
  
  for (var i=0; i<anArray.length; i++)
  {  
    if (anArray[i].indexOf(lStr) == 0)
    {
      lReturnedValue = anArray[i].substr(lStr.length);
      break;
    }
  }
  return lReturnedValue;
}

//
// Test for standard ASCII string.
//
function isAscii(aString)
{
  var lret = true;
  var i, lcode;

  for (var i=0; i<aString.length; i++) 
  {
    lcode = aString.charCodeAt(i);
    if ((lcode < 32) || (lcode > 126))
    {
      lret = false;
      break;
    }
  }
  
  return lret;
}

/**
 * Base32 encoding support. Our base32 encoded string can be identified by the unique
 * "01base89" signature
 */

// Base32 encoded signature. This signature has to match the corresponding signature in the
// Java URL Packager.
var base32Signature = "01base89";

// Returns a base32 encoded string from an input array.
function ArrayToBase32String(iArray) {
  var result = "";
  var temp = [];
  toBase32(iArray, iArray.length, temp);
  if (temp.length > 0)
  {
    result += base32Signature;
    for (var i = 0; i < temp.length; ++i) {
      result += String.fromCharCode(temp[i]);
    }
  }
  return result;
}

// Returns a base32 encoded string from an input string.
function StringToBase32String(aStr) {
  var temp = [];
  for (var i = 0; i < aStr.length; ++i) {
    temp[i] = aStr.charCodeAt(i);
  }
  return ArrayToBase32String(temp);
}

// Returns a base32 decoded array from a base32 encoded string.
function ArrayFromBase32String(aBase32Str, oArray) {
  var lSigLen = base32Signature.length;
  if (aBase32Str.substring(0,lSigLen) == base32Signature) {
    var temp = [];
    for (var i = lSigLen; i < aBase32Str.length; ++i) {
      temp[i-lSigLen] = aBase32Str.charCodeAt(i);
    }
    var len = fromBase32(temp, temp.length, oArray);
    oArray.length = len;
  }
}

// Returns a base32 decoded string from a base32 encoded string.
function StringFromBase32String(aBase32Str) {
  var result = "";
  var temp = [];
  ArrayFromBase32String(aBase32Str, temp);
  for (var i = 0; i < temp.length; ++i) {
    result += String.fromCharCode(temp[i]);
  }
  return result;
}

/**
 * Convert from bytes to base32
 * @parameter input Input buffer of bytes with values 00 to FF
 * @parameter inputLength Length of input buffer
 * @parameter output Output buffer, to be filled with with values from A-Z2-7.
 * Must be of at least length input*8/5 + 1
 * @return Length of output buffer used
 */

function toBase32(input, inputLength, output) {
  var bits = 0;
  var bitCount = 0;
  var ip = 0;
  var op = 0;
  var val = 0;
  while (true) {

    // get bits if we don't have enough

    if (bitCount < 5) {
      if (ip >= inputLength) break;
       // get another input
      bits <<= 8;

      bits = bits | input[ip++];
      bitCount += 8;
    }
    
    // emit and remove them

    bitCount -= 5;
    val = (bits >> bitCount);
    output[op++] = toLetter(val);
    bits &= ~(0x1F << bitCount);
  }

  // add padding and output if necessary

  if (bitCount > 0) {
    val = bits << (5 - bitCount);
    output[op++] = toLetter(val);
  }
  return op;
}

/**
 * Convert from base32 to bytes
 * @parameter input Input buffer of bytes with values from A-Z2-7
 * @parameter inputLength Length of input buffer
 * @parameter output Output buffer, to be filled with bytes from 00 to FF.
 * Must be of at least length input*5/8 + 1
 * @return Length of output buffer used
 */
function fromBase32(input, inputLength, output) {
  var inputCheck = inputLength % 8;
  if (inputCheck == 1 || inputCheck == 3 || inputCheck == 6) {
    // Base32 excess length;
    return 0;
  }
  var bits = 0;
  var bitCount = 0;
  var ip = 0;
  var op = 0;
  var val = 0;
  while (ip < inputLength) {

    // get more bits
    var val = input[ip++];
    val = fromLetter(val);
    if (val < 0 || val > 0x3F) {
      // Bad Base32 byte;
      return 0;
    }
    bits <<= 5;
    bits = bits | val;
    bitCount += 5;

    // emit & remove if we can

    if (bitCount >= 8) {
      bitCount -= 8;
      output[op++] = bits >> bitCount;
      bits &= ~(0xFF << bitCount);
    }
  }

  // check that padding is with zero!
  if (bits != 0) 
    return -ip;
  
  return op;
}

// ==================
function toLetter(val) {
  if (val > 25) 
    return val - 26 + 0x32;
  
  return val + 0x41;
}

function fromLetter(val) {
  if (val < 0x41) 
    return val + 26 - 0x32;
  
  return val - 0x41;
}

function escapeVisibilityExpr(AExpr)
{
  var LResult = "";
  if ( AExpr != "" )
  {
    var LLastCharPos = AExpr.length - 1;
    for( var i=0; i<LLastCharPos; i++ )
    {
      if ( AExpr.charAt(i) == "\\" ) 
        LResult += "\\\\";
      else
        LResult += AExpr.charAt(i);  
    }
    LResult += AExpr.charAt( LLastCharPos );
  }    
  return LResult;
}

//------------------------------------------------------------------------------
//                               Debug Viewer
//                          ---------------------
// Usage: DebugViewer.SendString("Testing...");
// Note: TO BE COMEMNTED OUT FOR PROPER RELEASES
//------------------------------------------------------------------------------

//  var DebugViewer = new ActiveXObject("MetJavaScriptDebugViewer.MetJSDebugViewer");
     
//------------------------------------------------------------------------------

//function JSDebugger()
//{
//  this.Memo = null;
//  this.SendString = JSDebugger_SendString;
//  return this;
//}

//function JSDebugger_SendString(AString)
//{
//  if (this.Memo != null)
//    this.Memo.value += AString + "\n";
//}

//var DebugViewer = new JSDebugger();