/*
  QuinScape Xtreme Foundation Classes   (QXFC)
  JavaScript Framework
  
  Copyright (c) 2007 QuinScape GmbH
  All Rights Reserved.
 
  http://www.quinscape.de
 
  Author: Joerg Gottschling
  
  Based on "json.js" (2007-07-03) by Douglas Crockford from json.org
*/ 

if(!qxfc)
{
  throw('Requires QXFC JavaScript Framework'); 
}
else if(!qxfc.json)
{
  qxfc.json =
  {
    isArray : function()
    {
	if (typeof arguments[0] == 'object') 
	{  
		var criterion = arguments[0].constructor.toString().match(/array/i); 
 		return (criterion != null);
	}
	return false;
    },

    toString : function(object)
    {
      if(this.isArray(object))
      {
			return this.arrayToString(object);
      }
      else
      {
		  switch (typeof object)
		  {
			case 'boolean':
			  return String(object);
	
			case 'number':
			   // JSON numbers must be finite. Encode non-finite numbers as null.
			  return isFinite(object) ? String(object) : 'null';
	
			case 'string':
			  return this.stringToString(object);
	
			case 'object':
			  return this.objectToString(object);
	
			// Values without a JSON representation are ignored.
		  }
       }
    },
    
    arrayToString : function (array)
    {
      // The array holding the member texts.
      var a = [];
      // array.length
      var len = array.length;

      // For each value in the array...
      for (var i = 0; i < len; i += 1)
        a.push(this.toString(array[i]));

      // Join all of the member texts together and wrap them in brackets.
      return '[' + a.join(',') + ']';
    },



    objectToString : function(object)
    {
      // Due to a specification error in ECMAScript, typeof null is 'object',
      // so watch out for that case.
      if(!object) return 'null';
      
      // The array holding the member texts.
      var a = [];

      // Iterate through all of the keys in the object
      for (var key in object)
      {
        // ignoring the proto chain.
        if (object.hasOwnProperty(key))
        {
          var propVal = this.toString(object[key]);
	    if(propVal)
            a.push(this.toString(key) + ':' + this.toString(object[key]));
        }
      }

      // Join all of the member texts together and wrap them in braces.
      return '{' + a.join(',') + '}';
    },

 // table of character substitutions.
    m :
    {
      '\b': '\\b',
      '\t': '\\t',
      '\n': '\\n',
      '\f': '\\f',
      '\r': '\\r',
      '"' : '\\"',
      '\\': '\\\\'
    },

    stringToString : function(string)
    {
      // If the string contains no control characters, no quote characters, and no
      // backslash characters, then we can simply slap some quotes around it.
      // Otherwise we must also replace the offending characters with safe
      // sequences.
      if (/["\\\x00-\x1f]/.test(string))
      {
        return '"' + string.replace(/([\x00-\x1f\\"])/g,
            function (a, b)
            {
              var c = qxfc.json.m[b];
              if (c) return c;
              c = b.charCodeAt();
              return '\\u00' +
                  Math.floor(c / 16).toString(16) +
                  (c % 16).toString(16);
            }) + '"';
      }
      return '"' + string + '"';
    },

   

    parseJSON : function (string, filter)
    {
      var j;

      function walk(k, v)
      {
        var i;
        if (v && typeof v === 'object')
        {
          for (i in v)
          {
            if (v.hasOwnProperty(i))
                v[i] = walk(i, v[i]);
          }
        }
        return filter(k, v);
      }

      // Parsing happens in three stages. In the first stage, we run the text against
      // a regular expression which looks for non-JSON characters. We are especially
      // concerned with '()' and 'new' because they can cause invocation, and '='
      // because it can cause mutation. But just to be safe, we will reject all
      // unexpected characters.
      
      // We split the first stage into 3 regexp operations in order to work around
      // crippling deficiencies in Safari's regexp engine. First we replace all
      // backslash pairs with '@' (a non-JSON character). Second we delete all of
      // the string literals. Third, we look to see if only JSON characters
      // remain. If so, then the text is safe for eval.

      if(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]+$/.test(
         string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '')))
      {
        // In the second stage we use the eval function to compile the text into a
        // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
        // in JavaScript: it can begin a block or an object literal. We wrap the text
        // in parens to eliminate the ambiguity.
        j = eval('(' + string + ')');

        // In the optional third stage, we recursively walk the new structure, passing
        // each name/value pair to a filter function for possible transformation.
        if (typeof filter === 'function')
            j = walk('', j);

        return j;
      }

      // If the text is not JSON parseable, then a SyntaxError is thrown.
      throw new SyntaxError('parseJSON');
    }
  };
}
