String.replaceStr = function(orig,lookfor,replacewith,ignorecase)  // JPW::Jan 6, 2003
{
  // QUESTION: what is the point of this function?
  //alert('String.replaceStr');
  var str = new String();
  str += orig;
  var type = 'g';
  if (ignorecase)
    type += 'i';
  var re = new RegExp (lookfor, type);
  return(str.replace(re,replacewith));
};  

stripWhitespace = String.prototype.stripWhitespace = function()
{
  return ((arguments.length>0)?((typeof(arguments[0])=='string')?arguments[0]:arguments[0].toString()):this).replace(/[\s\t\r\n]/g,'');
};

escapeHTML = String.prototype.escapeHTML = function()
{
  return ((arguments.length>0)?((typeof(arguments[0])=='string')?arguments[0]:arguments[0].toString()):this).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;');
};

unescapeHTML = String.prototype.unescapeHTML = function()
{
  return ((arguments.length>0)?((typeof(arguments[0])=='string')?arguments[0]:arguments[0].toString()):this).replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&quot;/g,'"').replace(/&amp;/g,'&');
};

stripQuotes = String.prototype.stripQuotes = function()
{
  return ((arguments.length>0)?((typeof(arguments[0])=='string')?arguments[0]:arguments[0].toString()):this).replace(/['"]/g,'');
};

escapeQuotes = String.prototype.escapeQuotes = function()
{
  var newString = ((arguments.length>0)?((typeof(arguments[0])=='string')?arguments[0]:arguments[0].toString()):this);
  newString = newString.replace(/\\/g,'\\\\');
  newString = String.replaceStr(newString,'\'','\\\'',false);
  newString = String.replaceStr(newString,'"','&quot;',false);
  return newString;
};

unescapeQuotes = String.prototype.unescapeQuotes = function()
{
  var newString = ((arguments.length>0)?((typeof(arguments[0])=='string')?arguments[0]:arguments[0].toString()):this);
  newString = newString.replace(/\\\\/g,'\\');
  newString = String.replaceStr(newString,'\\\'','\'',false);
  newString = String.replaceStr(newString,'&quot;','"',false);
  return newString;
};

String.prototype.ltrim = function()
{
  return this.replace(/^\s+/,'');
};

String.prototype.rtrim = function()
{
  return this.replace(/\s+$/,'');
};

String.prototype.strtrim = function()
{
  return this.replace(/^\s+/,'').replace(/\s+$/,'');
};

function getArrayLength (arr)
{
  /* since in some instances involving delete, JS seems to get it wrong */
  var count = 0;
  for(var idx in arr){
    if(idx=='toJSONString')continue;
    if ((typeof(arr[idx]) != 'undefined') && (arr[idx]!=null))
      count++;
  }
  return(count);
};

/* TODO: prevent recursion */
function describeObject(stringPrefix, object)
{
  var totalString = '';
  var lineTerminator = ((arguments.length >= 3)?(arguments[2]):("<br>"));
  var depth = ((arguments.length >= 4)?(arguments[3]):(-1));
  var objectType = typeof(object);
  if (objectType.toLowerCase() == "object")
    for (var i in object)
    {
      if(i=='toJSONString')continue;
      var currentPrefix = stringPrefix+'['+i+']';
      if (depth>0)
        totalString = totalString + describeObject(currentPrefix,object[i],lineTerminator,depth-1);
      else
        if (depth==-1)
          totalString = totalString + describeObject(currentPrefix,object[i],lineTerminator,-1);
    }
  else
    totalString = totalString + stringPrefix+' = ' + object + lineTerminator;
  return totalString;  
};

function DataTypeOf(o){
  // identifies the data type
  var type = typeof(o);
  type = type.toLowerCase();
  switch(type){
    case "number":
      if (Math.round(o) == o) type = "i4";
      else type = "double";
      break;
    case "object":
      var con = o.constructor;
      if (con == Date) type = "date";
      else if (con == Array) type = "array";
      else type = "struct";
      break;
  }
  return type;
};

function duplicateNodeTreeEmbeddingStyles(theNode)
{
  var newNode = theNode.cloneNode(false);
  if(theNode.nodeType == 1)  // copy the "calculated" style
  {
    if(theNode.getAttribute('action_button'))
      return(null);
    if(theNode.currentStyle)
    {
      for(var attribName in theNode.currentStyle)
      {
        if(attribName=='toJSONString')continue;
        var attribValue = theNode.currentStyle[attribName];
        if ((attribValue != '') && (attribValue != 'undefined') && (attribValue != 'none') && (attribValue != 'normal') && (attribValue != 'auto'))
          newNode.style[attribName] = attribValue;
      }
    }
    else if (window.getComputedStyle)
    {
      var newStyles = window.getComputedStyle(theNode,null);
      for (var lcv=0;lcv < newStyles.length;lcv++)
      {
        var attribName = newStyles.item(lcv);
        var attribValue = newStyles.getPropertyValue(attribName);
        if (attribValue.indexOf('-moz') >= 0) {} else
        if ((attribValue != '') && (attribValue != 'undefined') && (attribValue != 'none') && (attribValue != 'normal') && (attribValue != 'auto'))
          newNode.style.setProperty(attribName,attribValue,null);
      }
    }
  }
  for(var lcv=0;lcv < theNode.childNodes.length;lcv++)
  {
    var cNode = duplicateNodeTreeEmbeddingStyles(theNode.childNodes[lcv]);
    if(cNode != null)
      newNode.appendChild(cNode);
  }
  return(newNode);
};

function duplicateNodeTreeEmbeddingStylesSubset(theNode)
{
  var newNode = theNode.cloneNode(false);
  if(theNode.nodeType == 1)  // copy the "calculated" style
  {
    if(theNode.getAttribute('action_button'))
      return(null);
    try { newNode.removeAttribute('class'); newNode.removeAttribute('className'); } catch (e) { }
    if(theNode.currentStyle)
    {
      var copyAttribs = Array('margin','borderColor','borderStyle','borderWidth','backgroundColor','color','fontStyle','fontFamily','fontSize','fontWeight','padding','width','height','textAlign','textDecoration','cursor','visibility');
      for(var lcv=0;lcv < copyAttribs.length;lcv++)
      {
        var attribName = copyAttribs[lcv];
        var attribValue = theNode.currentStyle.getAttribute(attribName);
        if ((attribValue != null) && (attribValue != '') && (attribValue != 'undefined') && (attribValue != 'none') && (attribValue != 'normal') && (attribValue != 'auto') && (attribValue != 'transparent') && (attribValue != 'visible') && (attribValue != 'inherit'))
        try{ newNode.style[attribName] = attribValue; } catch(e){}
      }
    }
    else if (window.getComputedStyle)
    {
      var copyAttribs = Array('margin','border-bottom-color','border-bottom-width','border-bottom-style','border-top-color','border-top-width','border-top-style','border-left-color','border-left-width','border-left-style','border-right-color','border-right-width','border-right-style','background-color','color','font-family','font-size','font-weight','padding','width','height','text-align','text-decoration','cursor','visibility');
      var newStyles = window.getComputedStyle(theNode,null);
      for (var lcv=0;lcv < copyAttribs.length;lcv++)
      {
        var attribName = copyAttribs[lcv];
        var attribValue = newStyles.getPropertyValue(attribName);
        if ((attribValue != null) && (attribValue != '') && (attribValue != 'undefined') && (attribValue != 'none') && (attribValue != 'normal') && (attribValue != 'auto') && (attribValue != 'transparent') && (attribValue != 'visible') && (attribValue != 'inherit'))
          newNode.style.setProperty(attribName,attribValue,null);
      }
    }
  }
  for(var lcv=0;lcv < theNode.childNodes.length;lcv++)
  {
    var cNode = duplicateNodeTreeEmbeddingStylesSubset(theNode.childNodes[lcv]);
    if(cNode != null)
      newNode.appendChild(cNode);
  }
  return(newNode);
};

document.addStylesheet = function (url)
{
  var callback=(arguments.length>1)?arguments[1]:null;
  var ele = document.createElement('LINK');
  ele.setAttribute('type','text/css');
  ele.setAttribute('href',url);
  ele.setAttribute('rel','stylesheet');
  ele.setAttribute('disabled','false');
  if(typeof(callback)=='function'){
    if(ele.readyState==null){
      ele.addEventListener("load",function(){callback(url)},false);
    }else{
      ele.onreadystatechange=function(){
        if((ele.readyState=='loaded')||(ele.readyState=='complete')){
          setTimeout("",0);
          callback(url);
        }
      }
    };
  };
  document.getElementsByTagName('head')[0].appendChild(ele);
  ele.disabled = false;  //IE fix
};

function cloneObject(originalObject)
{
  /* TODO: prevent recursion */
  /* TODO:  VERIFY TYPE CASTING */
  if (originalObject == null)
    newObject = null;
  else
  {
    var newObject = null;
    var objectType = typeof(originalObject);
    objectType = objectType.toLowerCase();
    if (objectType == "object")
    {
      var con = originalObject.constructor;
      if (con == Date)
        objectType = "date";
      else 
        if (con == Array)
          objectType = "array";
        else
          type="object";
    }
    switch (objectType)
    {
      case "object":
        newObject = new Object;
        for (var i in originalObject){
          if(i=='toJSONString')continue;
          try{
            newObject[i] = cloneObject(originalObject[i]);
          }
          catch(e){
            //probably tried to descend to a system object
            dprintf('Warning: attribute "'+i+'" could not be duplicated by cloneNode.',false,"logWarning");
            newObject[i]=null;
          }
        }
        break;
      case "array":
        newObject = new Array;
        for (var i=0; i<originalObject.length; i++)
          newObject[i] = cloneObject(originalObject[i]);
        break;
      default:
        newObject = originalObject;
    }
  }
  return newObject;
};

var CSSClass = {};  // Create our namespace object
// Return true if element e is a member of the class c; false otherwise
CSSClass.is = function(e, c) {
    if (typeof e == "string") e = document.getElementById(e); // element id

    // Before doing a regexp search, optimize for a couple of common cases.
    var classes = e.className;
    if (!classes) return false;    // Not a member of any classes
    if (classes == c) return true; // Member of just this one class

    // Otherwise, use a regular expression to search for c as a word by itself
    // \b in a regular expression requires a match at a word boundary.
    return e.className.search("\\b" + c + "\\b") != -1;
};

// Add class c to the className of element e if it is not already there.
CSSClass.add = function(e, c) {
    if (typeof e == "string") e = document.getElementById(e); // element id
    if (CSSClass.is(e, c)) return; // If already a member, do nothing
    if (e.className) c = " " + c;  // Whitespace separator, if needed
    e.className += c;              // Append the new class to the end
};

// Remove all occurrences (if any) of class c from the className of element e
CSSClass.remove = function(e, c) {
    if (typeof e == "string") e = document.getElementById(e); // element id
    // Search the className for all occurrences of c and replace with "".
    // \s* matches any number of whitespace characters.
    // "g" makes the regular expression match any number of occurrences
    e.className = e.className.replace(new RegExp("\\b"+ c+"\\b\\s*", "g"), "");
};

CSSClass.set=function(elementId,newClass)//explicitly set class of element, overriding all existing data
{
  var elementReference = ((typeof(elementId)=='object')?(elementId):(document.getElementById(elementId)));
  while((elementReference != null) && (elementReference.nodeName == '#text'))
     elementReference = elementReference.parentNode;
  if (elementReference != null)
    elementReference.className = newClass;
};

setClass=CSSClass.set;  //for backwards compatibility

function convertDistance(dist,srcUnit,destUnit)
{
  /* TODO: eliminate initial conversion to meters to reduce rounding errors */
  if (srcUnit == destUnit)
    return(dist);
  var newdist = dist;
  /* convert to meters */
  switch (srcUnit.toLowerCase())
  {
    case 'mm':
      newdist = dist/1000.0;
      break;
    case 'cm':
      newdist = dist/100.0;
      break;
    case 'm':
      newdist = dist;
      break;
    case 'km':
      newdist = dist * 1000;
      break;
    case 'in':
      newdist = dist*0.0254;
      break;
    case 'ft':
      newdist = dist*0.3048;
      break;
    case 'yd':
      newdist = dist*0.9144;
      break;
    case 'mi':
      newdist = dist * 1609.344;
      break;
  }
  /* convert to requested units */
  switch (destUnit.toLowerCase())
  {
    case 'mm':
      newdist = newdist * 1000;
      break;
    case 'cm':
      newdist = newdist * 100;
      break;
    case 'in':
      newdist = newdist * 39.37007874;
      break;
    case 'ft':
      newdist = newdist * 3.2808399;
      break;
    case 'yd':
      newdist = newdist * 1.0936133;
      break;
    case 'mi':
      newdist = newdist * 0.00062137;
      break;
    case 'm':
      break;
    case 'km':
      newdist = newdist * .001;
      break;
  }
  return(newdist);
};


Freeance_Communication=function(){};
Freeance_Communication.javascript_load=function(scriptURL /*,callbackFunction*/){
  //dprintf('loadJavaScript('+scriptURL+')',true,'logNotice');
  if(document.jsload_setwait)document.jsload_setwait(true);
  var callback=(arguments.length>1)?arguments[1]:null;
  var ele=document.createElement('SCRIPT');
  if(ele.readyState==null){
    ele.addEventListener("load",function(e){
      if(document.jsload_setwait)document.jsload_setwait(false);
      if(typeof(callback)=='function')callback()
      },false);
  }else{
    ele.onreadystatechange=function(){
      if((ele.readyState=='loaded')||(ele.readyState=='complete')){
        if(document.jsload_setwait)document.jsload_setwait(false);
        setTimeout("",0);
        if(typeof(callback)=='function')callback();
      }
    }
  };
  ele.setAttribute('type','text/javascript');
  ele.setAttribute('language','JavaScript');
  ele.setAttribute('src',scriptURL);
  document.getElementsByTagName('head')[0].appendChild(ele);
  return true;
};
loadJavaScript=Freeance_Communication.javascript_load;

Freeance_Communication.xmlrpc_request = function(/*Callback,function_name,param[1],...param[n]*/)
{
  var Callback = arguments[0];
  var functionName = arguments[1];
  var returnValue = null;
  var thisRequest = new XMLRPCMessage(functionName);
  for (lcv = 2; lcv < arguments.length; lcv++)
    thisRequest.addParameter(arguments[lcv]);
  var thisRequestDoc = XmlDocument.create();
  thisRequestDoc.loadXML(thisRequest.xml());
  if (Callback)
    returnValue = XmlHttp.postAsync_(FREEANCE_XMLRPC_URL,thisRequestDoc,function(xmlrpc_response){Callback(getXMLRPCResponseObject(xmlrpc_response));});
  else
    returnValue = getXMLRPCResponseObject(XmlHttp.postSync(FREEANCE_XMLRPC_URL,thisRequestDoc));
  return returnValue;
};
freeance_request = Freeance_Communication.xmlrpc_request;

Freeance_Error = function(){};
Freeance_Error.is_error = function(data){
  if (data===null) return true;
  if (data===undefined) return true;
  if (data['XMLRPC_FAULT']) return true;
  if (data['FREEANCE_FAULT'])return true;
  if (data['GUILIB_FAULT'])return true;
  return false;
};
Freeance_Error.get_message=function(data){
  if (data===null) return 'Value is null';
  if (data===undefined) return 'Value is undefined';
  if (data['XMLRPC_FAULT']) return data['XMLRPC_FAULT_MESSAGE'];
  if (data['FREEANCE_FAULT']) return data['FREEANCE_FAULT_MESSAGE'];
  if (data['GUILIB_FAULT']) return data['GUILIB_FAULT_MESSAGE'];
  return null;
};
Freeance_Error.get_code=function(data){
  if (data===null) return -1001;
  if (data===undefined) return -1002;
  if (data['XMLRPC_FAULT']) return data['XMLRPC_FAULT_CODE'];
  if (data['FREEANCE_FAULT']) return data['FREEANCE_FAULT_CODE'];
  if (data['GUILIB_FAULT']) return data['GUILIB_FAULT_CODE'];
  return null;
};
Freeance_Error.xmldoc_is_valid=function(xmlDoc)
{ 
  try
  {
    if(xmlDoc == null) 
      return false;
    var xmlstr = (typeof(xmlDoc.xml)=='function')?xmlDoc.xml():xmlDoc.xml;
    if(xmlstr == '')
      return false;
    if(xmlDoc.documentElement.nodeName == 'parsererror')
      return false;
    return true;
  }
  catch(e)
  {
    return false;
  }
};



Freeance_Event = function(){};
Freeance_Event.add = function(ele,type,fcn)
{
  type=type.toLowerCase();
  var cap = (arguments.length>3)?arguments[3]:false;
  if (!ele.FREEANCE_EVENTS)
    ele.FREEANCE_EVENTS = [];
  ele.FREEANCE_EVENTS.push({evt:type,fcn:fcn});
  if(ele.addEventListener) 
    ele.addEventListener(type,fcn,cap);
  else 
    if(ele.attachEvent)
      ele.attachEvent('on'+type,fcn);
    else
      return {FREEANCE_FAULT:true,FREEANCE_FAULT_CODE:-1003,FREEANCE_FAULT_MESSAGE:'Browser does not support dynamic addition of events to elements'};
  return true;
};
Freeance_Event.remove = function(ele,type,fcn)
{
  type=type.toLowerCase();
  var cap = (arguments.length>3)?arguments[3]:false;
  if (ele.FREEANCE_EVENTS)
  {
    for (var i=0;i<ele.FREEANCE_EVENTS.length;i++)
      if (ele.FREEANCE_EVENTS[i])
        if((ele.FREEANCE_EVENTS[i]['evt']==type)&&(ele.FREEANCE_EVENTS[i]['fcn']==fcn))
          ele.FREEANCE_EVENTS[i] = null;
  }
  if(ele.removeEventListener) 
    ele.removeEventListener(type,fcn,cap);
  else 
    if(ele.detachEvent)
      ele.detachEvent('on'+type,fcn);
    else
      return {FREEANCE_FAULT:true,FREEANCE_FAULT_CODE:-1003,FREEANCE_FAULT_MESSAGE:'Browser does not support dynamic addition of events to elements'};
  return true;
};
Freeance_Event.remove_all = function(ele)
{
  if (ele.FREEANCE_EVENTS!=null)
  {
    for (var i=0;i<ele.FREEANCE_EVENTS.length;i++){
      if(ele.removeEventListener) 
        ele.removeEventListener(ele.FREEANCE_EVENTS[i]['evt'],ele.FREEANCE_EVENTS[i]['fcn'],ele.FREEANCE_EVENTS[i]['cap']);
      else 
        if(ele.detachEvent)
          ele.detachEvent('on'+ele.FREEANCE_EVENTS[i]['evt'],ele.FREEANCE_EVENTS[i]['fcn']);
        else
          return {FREEANCE_FAULT:true,FREEANCE_FAULT_CODE:-1003,FREEANCE_FAULT_MESSAGE:'Browser does not support dynamic addition of events to elements'};
    }
    ele.FREEANCE_EVENTS=null;
  }
  return true;
};

