/*
 * <file>
 * Name: ObjectExt.js 
 * Purpose: static class holding some extensional functionality
 *          for JavaScript objects
 *
 * Created: 2000-03-30 Mair Martin
 * </file>
 * $Id: ObjectExt.js,v 1.7 2004/07/29 15:49:59 pschub Exp $
 */

//----------------------------------------------------------------------------
// <JSClass Name="ObjectExt">

  //------------------------------------------------------------
  /**
   * the constructor should not be used since this class is a static one!
  **/
  function ObjectExt()
  {
    // static class!
  }
  
  //------------------------------------------------------------
  /**
   * retrieve the type of a JavaScript object (different to typeof!).
   * if an instance of a class is given as parameter, the classname is returned.
   * 
   * @param Object: anObj: object (or string, number, ..) of which the type is to check
   * @return String: the type of the object (one of the following):
   *                 <UL><LI>undefined</LI>
   *                     <LI>null</LI>
   *                     <LI>String</LI>
   *                     <LI>Number</LI>
   *                     <LI>Array</LI>
   *                     <LI>Object</LI>
   *                     <LI>Function</LI>
   *                     <LI>CLASSNAME: if anObj is an instance of a JavaScript class</LI></UL>
  **/
  function ObjectExt_getType (anObj) {
      var index, id;
  
      if (typeof anObj == "undefined")
        return "undefined";
      
      if (anObj == null)
        return "null";
          
      id     = anObj.constructor + " ";
      index1 = id.indexOf ( " " );
      index2 = id.indexOf ( "(" );
  
      return id.substring ( index1 + 1, index2 );
  }
  
  //------------------------------------------------------------
  /**
   * escapes a given string (also hex and unicode).
   * eg. for sending it from client to server.
   * 
   * @param String: s: string to escape
   * @return String: escaped string
  **/
  function ObjectExt_escapeString (s) {
    //----------------------------------------------------------
    /** helper function, converts ascii to hex representation **/
    function ascii2hex ( anInt ) {
      var conv = "0123456789abcdef";
      return ( "\\x" + 
               conv.charAt ( (anInt>>4) & 0x0f ) + 
               conv.charAt ( anInt & 0x0f ) );
    }
    //----------------------------------------------------------
    /** helper function, converts unicode to hex representation **/
    function unicode2hex ( anInt ) {
      var conv = "0123456789abcdef";
      return ( "\\u" + 
               conv.charAt ( (anInt>>12) & 0x0f ) + 
               conv.charAt ( (anInt>>8) & 0x0f ) + 
               conv.charAt ( (anInt>>4) & 0x0f ) + 
               conv.charAt ( anInt & 0x0f ) );
    }
  
    /* define characters that should be escaped especially */
    var escapeMap = "\bb\ff\nn\rr\tt\vv\"\"''";
    /* define the result array (join before return) */
    result = [];    
    // step through characters of string and escape
    for(var i=0; i < s.length; ++i) 
    {
      var c = s.charAt(i);
      var c_code = s.charCodeAt(i);
      // an ordinary print character
      if (c >= " " && c <= "~"     
          && c != "\"" && c != "\\")
      {
          result [result.length] = c;
          continue;
      }
      // an \escaped sort of character
      var index;
      if ((index = escapeMap.indexOf(c)) >= 0) 
      {
        result [result.length] = "\\";
        result [result.length] = escapeMap.charAt(index + 1);
        continue;
      }
      // 2-digit hex?
      if (c_code < 256) 
      {
        result [result.length] = ascii2hex(c_code);
        continue;
      }
      // nope.  Unicode.
      result [result.length] = unicode2hex(c_code);
    } /* for */
    
    // split occurences of "script" tags
    return result.join ("").replace(/(<\/?scr)(ipt[^>]*>)/gi, '$1" + "$2');
  }

  //------------------------------------------------------------
  /**
   * serializes objects of any type to string, eg: for sending them from client to server.
   * this function does eg. not try to define placeholders for parts of the string 
   * representation which are used more often (like native toSource does...)
   * the inverse operation can be done by invoking "eval" and assign the result to a variable again.
   * 
   * @param Object: anObj: object (or string, number, ..) which should be serialized
   * @return String: string representation of the given object
  **/
  function ObjectExt_toSource(anObj) {
    // define result string
    result = "";
    // generate string representation according to type
    switch(ObjectExt.getType(anObj)){
      // use native toString() - possibility
      case "Number":
      case "Boolean":
      case "Function":
        result += anObj.toString(); 
        break;
      case "String":
        // surround string with escaped double quotes
        result += "\"" + ObjectExt.escapeString(anObj) + "\"";
        break;
      case "Array":
        result += "["; // start array 
        var separator = "";
        // step through array entries
        for (var cr=0;cr<anObj.length;++cr){
          var arrayentry = anObj[cr];  
          if (!arrayentry) 
            continue;
          result += separator + ObjectExt.toSource(arrayentry);
          separator = ", ";
        }
        result += "]"; // end array
        break;
      // Object or an instance of a class
      default: 
        if ((typeof anObj) == "object"){ //check if anObj is JavaScript object
          result += "{"; //start object
          var separator = "";
          // step through object properties
          for (var propname in anObj){
            var propvalue = anObj[propname];  
            if (propvalue == null) 
              continue;
//            result += separator + propname + ":";
            result += separator + "\"" + propname + "\":";
            result += ObjectExt.toSource(propvalue);
            separator = ", ";
          }
          result += "}";  //object end
        }
        // unknown type, check if native toSource() is defined. use it if there.
        else {
          if (ObjectExt.getType(anObj.toSource) == "Function")
            result += anObj.toSource();
        }
    }
    return result;
  }

//------------------------------------------------------------
// static member function assignment
ObjectExt.getType      = ObjectExt_getType;
ObjectExt.escapeString = ObjectExt_escapeString;
ObjectExt.toSource     = ObjectExt_toSource;

// </JSClass>
//----------------------------------------------------------------------------

