<%

/*
 * Copyright:
 *   (C) 2006 by Derrell Lipman
 *       All rights reserved
 *
 * License:
 *   LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/
 */

/*
 * This module provides a JSON encoder.
 */


/* escape a string as required by json */
function _escape(s)
{
    var i;
    var arr = new Array();

    for (i = 0; i < strlen(s); i++)
    {
        var c = substr(s, i, 1);
        if (c == '\x00')
        {
            arr[i] = '\\u0000';
        }
        if (Json._internal.convert[c] != undefined)
        {
            arr[i] = Json._internal.convert[c];
        }
        else
        {
            arr[i] = c;
        }
    }

    if (arr.length == 0)
    {
        return "";
    }
    
    return join("", arr);
}

/* encode an arbitrary object.  called recursively, for object and array */
function _encode(o)
{
    var type = nativeTypeOf(o);

    if (type == "undefined")
    {
        return "null";          /* you really shouldn't count on this! */
    }
    else if (type == "null")
    {
        return "null";
    }
    else if (type == "boolean")
    {
        if (o)
        {
            return "true";
        }
        else
        {
            return "false";
        }
    }
    else if (type == "c_function" ||
             type == "js_function" ||
             type == "string_c_function")
    {
        /* no output */
    }
    else if (type == "float" ||
             type == "integer" ||
             type == "integer64")
    {
        return o + 0;
    }
    else if (type == "pointer")
    {
        var x = "" + o;
        return '"' + substr(x, 16, strlen(x) - 16 - 1) + '"';
    }
    else if (type == "object")
    {
        var buf;

        /* Is this an array or an ordinary object? */
        if (o["length"] != undefined)
        {
            var i;

            /* Assume it's an array if there's a length field */
            buf = "[";
            for (i = 0; i < o.length; i++)
            {
                /*
                 * NOTE: We don't support sparse arrays nor associative
                 * arrays.  Should we later want to do either, we're supposed
                 * to send it as an object rather than as an array.
                 */
                if (i > 0)
                {
                    buf = buf + ",";
                }
                buf = buf + this.encode(o[i]);
            }
            buf = buf + "]";
        }
        else if (o["__type"] == "_JSON_Date")
        {
            buf = "" + o.encoding();
        }
        else
        {
            /* No length field, so it must be an ordinary object */
            var key;
            var first = true;

            buf = "{";
            for (key in o)
            {
                if (! first)
                {
                    buf = buf + ",";
                }
                buf = buf + '"' + key + '":' + this.encode(o[key]);
                first = false;
            }
            buf = buf + "}";
        }

        return buf;
    }
    else if (type == "string")
    {
        return '"' + this._internal.escape(o) + '"';
    }
    else
    {
        return '{ "unknown_object":"' + type + '"}';
    }
}

/* Allocate the public Json access object */
Json = new Object();

/* Json.encode(): encode an arbitrary object */
Json.encode = _encode;
_encode = null;

/* Json.decode(): decode a string into its object form */
Json.decode = literal_to_var;

/* Internal stuff, not for external access */
Json._internal = new Object();

Json._internal.escape = _escape;
_escape = null;

Json._internal.convert = new Object();
Json._internal.convert['\b'] = '\\b';
Json._internal.convert['\t'] = '\\t';
Json._internal.convert['\n'] = '\\n';
Json._internal.convert['\f'] = '\\f';
Json._internal.convert['\r'] = '\\r';
Json._internal.convert['"'] = '\\"';
Json._internal.convert['\\'] = '\\\\';
Json._internal.convert['\x01'] = '\\u0001';
Json._internal.convert['\x02'] = '\\u0002';
Json._internal.convert['\x03'] = '\\u0003';
Json._internal.convert['\x04'] = '\\u0004';
Json._internal.convert['\x05'] = '\\u0005';
Json._internal.convert['\x06'] = '\\u0006';
Json._internal.convert['\x07'] = '\\u0007';
Json._internal.convert['\x08'] = '\\u0008';
Json._internal.convert['\x09'] = '\\u0009';
Json._internal.convert['\x0a'] = '\\u000a';
Json._internal.convert['\x0b'] = '\\u000b';
Json._internal.convert['\x0c'] = '\\u000c';
Json._internal.convert['\x0d'] = '\\u000d';
Json._internal.convert['\x0e'] = '\\u000e';
Json._internal.convert['\x0f'] = '\\u000f';
Json._internal.convert['\x10'] = '\\u0010';
Json._internal.convert['\x11'] = '\\u0011';
Json._internal.convert['\x12'] = '\\u0012';
Json._internal.convert['\x13'] = '\\u0013';
Json._internal.convert['\x14'] = '\\u0014';
Json._internal.convert['\x15'] = '\\u0015';
Json._internal.convert['\x16'] = '\\u0016';
Json._internal.convert['\x17'] = '\\u0017';
Json._internal.convert['\x18'] = '\\u0018';
Json._internal.convert['\x19'] = '\\u0019';
Json._internal.convert['\x1a'] = '\\u001a';
Json._internal.convert['\x1b'] = '\\u001b';
Json._internal.convert['\x1c'] = '\\u001c';
Json._internal.convert['\x1d'] = '\\u001d';
Json._internal.convert['\x1e'] = '\\u001e';
Json._internal.convert['\x1f'] = '\\u001f';
/*
 * At some point, we probably want to add \x80-\xff as well, and it's then
 * probably more efficient to generate these strings dynamically.  (Even now
 * it may be, but this was the the way I started, and so it remains.)
 */


/* Test it */
/*
libinclude("base.js");
function testFormat()
{
    var test = new Object();
    test.int = 23;
    test.str = "hello world";
    test.float = 223.1;
    test.bool = true;
    test.array = new Array();
    test.array[0] = "hello";
    test.array[1] = "world";
    test.obj = new Object();
    test.obj.int = 1000;
    test.obj.array = new Array();
    test.obj.array[0] = 42;
    test.obj.array[1] = 223;
    printf("%s\n", Json.encode(test));
}
testFormat();
*/

/*
libinclude("base.js");
function testParse()
{
    var s;

    s = '{ "x" : 23 }';
    obj = Json.decode(s);
    printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));

    s = '{ "x" : [ 23, 42] }';
    obj = Json.decode(s);
    printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));

    s = '[ 13, 19, { "x" : [ 23, 42] }, 223 ]';
    obj = Json.decode(s);
    printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));

    s = '{ "x" : [ "hi" ] }';
    obj = Json.decode(s);
    printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));

    s = '[ 13, 19, { "x" : [ 23, 42, { "y":{"a":"hello", "b":"world", "c":[1,2,3]}}] }, 223 ]';
    obj = Json.decode(s);
    printf("Decode/encode of\n\t%s\nyielded\n\t%s\n\n", s, Json.encode(obj));
}
testParse();
*/

/*
 * Local Variables:
 * mode: c
 * End:
 */
%>