/**********************************************************\
|                                                          |
| The implementation of PHPRPC Protocol 3.0                |
|                                                          |
| phpserializer.js                                         |
|                                                          |
| Release 3.0.1                                            |
| Copyright by Team-PHPRPC                                 |
|                                                          |
| WebSite:  http://www.phprpc.org/                         |
|           http://www.phprpc.net/                         |
|           http://www.phprpc.com/                         |
|           http://sourceforge.net/projects/php-rpc/       |
|                                                          |
| Authors:  Ma Bingyao <andot@ujn.edu.cn>                  |
|                                                          |
| This file may be distributed and/or modified under the   |
| terms of the GNU Lesser General Public License (LGPL)    |
| version 3.0 as published by the Free Software Foundation |
| and appearing in the included file LICENSE.              |
|                                                          |
\**********************************************************/

/* PHP serialize/unserialize library.
 *
 * Copyright: Ma Bingyao <andot@ujn.edu.cn>
 * Version: 4.2
 * LastModified: Oct 20, 2008
 * This library is free.  You can redistribute it and/or modify it.
 */

var PHPSerializer = (function () {

    function freeEval(s) {
        return eval(s);
    }
    var prototypePropertyOfArray = function() {
        var result = {};
        for (var p in []) {
            result[p] = true;
        }
        return result;
    }();
    var prototypePropertyOfObject = function() {
        var result = {};
        for (var p in {}) {
            result[p] = true;
        }
        return result;
    }();

    return {
        serialize : function(o) {
            var p = 0, sb = [], ht = [], hv = 1;
            function getClassName(o) {
                if (typeof(o) == 'undefined' || typeof(o.constructor) == 'undefined') return '';
                var c = o.constructor.toString();
                c = c.substr(0, c.indexOf('(')).replace(/(^\s*function\s*)|(\s*$)/ig, '').toUTF8();
                return ((c == '') ? 'Object' : c);
            }
            function isInteger(n) {
                var i, s = n.toString(), l = s.length;
                if (l > 11) return false;
                for (i = (s.charAt(0) == '-') ? 1 : 0; i < l; i++) {
                    switch (s.charAt(i)) {
                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9': break;
                        default : return false;
                    }
                }
                return !(n < -2147483648 || n > 2147483647);
            }
            function inHashTable(o) {
                for (var i = 0; i < ht.length; i++) if (ht[i] === o) return i;
                return false;
            }
            function serializeNull() {
                sb[p++] = 'N;';
            }
            function serializeBoolean(b) {
                sb[p++] = (b ? 'b:1;' : 'b:0;');
            }
            function serializeInteger(i) {
                sb[p++] = 'i:' + i + ';';
            }
            function serializeDouble(d) {
                if (isNaN(d)) d = 'NAN';
                else if (d == Number.POSITIVE_INFINITY) d = 'INF';
                else if (d == Number.NEGATIVE_INFINITY) d = '-INF';
                sb[p++] = 'd:' + d + ';';
            }
            function serializeString(s) {
                var utf8 = s.toUTF8();
                sb[p++] = 's:' + utf8.length + ':"';
                sb[p++] = utf8;
                sb[p++] = '";';
            }
            function serializeDate(dt) {
                sb[p++] = 'O:11:"PHPRPC_Date":7:{';
                sb[p++] = 's:4:"year";';
                serializeInteger(dt.getFullYear());
                sb[p++] = 's:5:"month";';
                serializeInteger(dt.getMonth() + 1);
                sb[p++] = 's:3:"day";';
                serializeInteger(dt.getDate());
                sb[p++] = 's:4:"hour";';
                serializeInteger(dt.getHours());
                sb[p++] = 's:6:"minute";';
                serializeInteger(dt.getMinutes());
                sb[p++] = 's:6:"second";';
                serializeInteger(dt.getSeconds());
                sb[p++] = 's:11:"millisecond";';
                serializeInteger(dt.getMilliseconds());
                sb[p++] = '}';
            }
            function serializeArray(a) {
                sb[p++] = 'a:';
                var k, lp = p;
                sb[p++] = 0;
                sb[p++] = ':{';
                for (k in a) {
                    if ((typeof(a[k]) != 'function') && !prototypePropertyOfArray[k]) {
                        isInteger(k) ? serializeInteger(k) : serializeString(k);
                        serialize(a[k]);
                        sb[lp]++;
                    }
                }
                sb[p++] = '}';
            }
            function serializeObject(o) {
                var cn = getClassName(o);
                if (cn == '') serializeNull();
                else if (typeof(o.serialize) != 'function') {
                    sb[p++] = 'O:' + cn.length + ':"' + cn + '":';
                    var lp = p;
                    sb[p++] = 0;
                    sb[p++] = ':{';
                    var k;
                    if (typeof(o.__sleep) == 'function') {
                        var a = o.__sleep();
                        for (k in a) {
                            serializeString(a[k]);
                            serialize(o[a[k]]);
                            sb[lp]++;
                        }
                    }
                    else {
                        for (k in o) {
                            if (typeof(o[k]) != 'function' && !prototypePropertyOfObject[k]) {
                                serializeString(k);
                                serialize(o[k]);
                                sb[lp]++;
                            }
                        }
                    }
                    sb[p++] = '}';
                }
                else {
                    var cs = o.serialize();
                    sb[p++] = 'C:' + cn.length + ':"' + cn + '":' + cs.length + ':{' +cs + '}';
                }
            }
            function serializePointRef(R) {
                sb[p++] = 'R:' + R + ';';
            }
            function serializeRef(r) {
                sb[p++] = 'r:' + r + ';';
            }
            function serialize(o) {
                if (typeof(o) == "undefined" || o == null ||
                    o.constructor == Function) {
                    hv++;
                    serializeNull();
                    return;
                }
                var className = getClassName(o);
                switch (o.constructor) {
                    case Boolean: {
                        hv++;
                        serializeBoolean(o);
                        break;
                    }
                    case Number: {
                        hv++;
                        isInteger(o) ? serializeInteger(o) : serializeDouble(o);
                        break;
                    }
                    case String: {
                        hv++;
                        serializeString(o);
                        break;
                    }
                    case Date: {
                        hv += 8;
                        serializeDate(o);
                        break;
                    }
                    default: {
                        if (className == "Object" || o.constructor == Array) {
                            var r = inHashTable(o);
                            if (r) {
                                serializePointRef(r);
                            }
                            else {
                                ht[hv++] = o;
                                serializeArray(o);
                            }
                            break;
                        }
                        else {
                            var r = inHashTable(o);
                            if (r) {
                                hv++;
                                serializeRef(r);
                            }
                            else {
                                ht[hv++] = o;
                                serializeObject(o);
                            }
                        }
                    }
                }
            }
            serialize(o);
            return sb.join('');
        },
        unserialize : function(ss) {
            var p = 0, ht = [], hv = 1;
            function unserializeNull() {
                p++;
                return null;
            }
            function unserializeBoolean() {
                p++;
                var b = (ss.charAt(p++) == '1');
                p++;
                return b;
            }
            function unserializeInteger() {
                p++;
                var i = parseInt(ss.substring(p, p = ss.indexOf(';', p)));
                p++;
                return i;
            }
            function unserializeDouble() {
                p++;
                var d = ss.substring(p, p = ss.indexOf(';', p));
                switch (d) {
                    case 'NAN': d = NaN; break;
                    case 'INF': d = Number.POSITIVE_INFINITY; break;
                    case '-INF': d = Number.NEGATIVE_INFINITY; break;
                    default: d = parseFloat(d);
                }
                p++;
                return d;
            }
            function unserializeString() {
                p++;
                var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
                p += 2;
                var s = ss.substring(p, p += l).toUTF16();
                p += 2;
                return s;
            }
            function unserializeEscapedString(len) {
                p++;
                var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
                p += 2;
                var i, sb = new Array(l);
                for (i = 0; i < l; i++) {
                    if ((sb[i] = ss.charAt(p++)) == '\\') {
                        sb[i] = String.fromCharCode(parseInt(ss.substring(p, p += len), 16));
                    }
                }
                p += 2;
                return sb.join('');
            }
            function unserializeArray() {
                p++;
                var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
                p += 2;
                var i, k, a = [];
                ht[hv++] = a;
                for (i = 0; i < n; i++) {
                    switch (ss.charAt(p++)) {
                        case 'i': k = unserializeInteger(); break;
                        case 's': k = unserializeString(); break;
                        case 'S': k = unserializeEscapedString(2); break;
                        case 'U': k = unserializeEscapedString(4); break;
                        default: return false;
                    }
                    a[k] = unserialize();
                }
                p++;
                return a;
            }
            function unserializeDate(n) {
                var i, k, a = {};
                for (i = 0; i < n; i++) {
                    switch (ss.charAt(p++)) {
                        case 's': k = unserializeString(); break;
                        case 'S': k = unserializeEscapedString(2); break;
                        case 'U': k = unserializeEscapedString(4); break;
                        default: return false;
                    }
                    if (ss.charAt(p++) == 'i') {
                        a[k] = unserializeInteger();
                    }
                    else {
                        return false;
                    }
                }
                p++;
                var dt = new Date(
                    a.year,
                    a.month - 1,
                    a.day,
                    a.hour,
                    a.minute,
                    a.second,
                    a.millisecond
                );
                ht[hv++] = dt;
                ht[hv++] = a.year;
                ht[hv++] = a.month;
                ht[hv++] = a.day;
                ht[hv++] = a.hour;
                ht[hv++] = a.minute;
                ht[hv++] = a.second;
                ht[hv++] = a.millisecond;
                return dt;
            }
            function unserializeObject() {
                p++;
                var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
                p += 2;
                var cn = ss.substring(p, p += l).toUTF16();
                p += 2;
                var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
                p += 2;
                if (cn == "PHPRPC_Date") {
                    return unserializeDate(n);
                }
                var i, k, o = createObjectOfClass(cn);
                ht[hv++] = o;
                for (i = 0; i < n; i++) {
                    switch (ss.charAt(p++)) {
                        case 's': k = unserializeString(); break;
                        case 'S': k = unserializeEscapedString(2); break;
                        case 'U': k = unserializeEscapedString(4); break;
                        default: return false;
                    }
                    if (k.charAt(0) == '\0') {
                        k = k.substring(k.indexOf('\0', 1) + 1, k.length);
                    }
                    o[k] = unserialize();
                }
                p++;
                if (typeof(o.__wakeup) == 'function') o.__wakeup();
                return o;
            }
            function unserializeCustomObject() {
                p++;
                var l = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
                p += 2;
                var cn = ss.substring(p, p += l).toUTF16();
                p += 2;
                var n = parseInt(ss.substring(p, p = ss.indexOf(':', p)));
                p += 2;
                var o = createObjectOfClass(cn);
                ht[hv++] = o;
                if (typeof(o.unserialize) != 'function') p += n;
                else o.unserialize(ss.substring(p, p += n));
                p++;
                return o;
            }
            function unserializeRef() {
                p++;
                var r = parseInt(ss.substring(p, p = ss.indexOf(';', p)));
                p++;
                return ht[r];
            }
            function getObjectOfClass(cn, poslist, i, c) {
                if (i < poslist.length) {
                    var pos = poslist[i];
                    cn[pos] = c;
                    var obj = getObjectOfClass(cn, poslist, i + 1, '.');
                    if (i + 1 < poslist.length) {
                        if (obj == null) {
                            obj = getObjectOfClass(cn, poslist, i + 1, '_');
                        }
                    }
                    return obj;
                }
                var classname = cn.join('');
                try {
                    return freeEval('new ' + classname + '()');
                }
                catch (e) {
                    return null;
                }
            }
            function createObjectOfClass(classname) {
                if (freeEval('typeof(' + classname + ') == "function"')) {
                    return freeEval('new ' + classname + '()');
                }
                var poslist = [];
                var pos = classname.indexOf("_");
                while (pos > -1) {
                    poslist[poslist.length] = pos;
                    pos = classname.indexOf("_", pos + 1);
                }
                if (poslist.length > 0) {
                    var cn = classname.split('');
                    var obj = getObjectOfClass(cn, poslist, 0, '.');
                    if (obj == null) {
                        obj = getObjectOfClass(cn, poslist, 0, '_');
                    }
                    if (obj != null) {
                        return obj;
                    }
                }
                return freeEval('new function ' + classname + '(){};');
            }
            function unserialize() {
                switch (ss.charAt(p++)) {
                    case 'N': return ht[hv++] = unserializeNull();
                    case 'b': return ht[hv++] = unserializeBoolean();
                    case 'i': return ht[hv++] = unserializeInteger();
                    case 'd': return ht[hv++] = unserializeDouble();
                    case 's': return ht[hv++] = unserializeString();
                    case 'S': return ht[hv++] = unserializeEscapedString(2);
                    case 'U': return ht[hv++] = unserializeEscapedString(4);
                    case 'r': return ht[hv++] = unserializeRef();
                    case 'a': return unserializeArray();
                    case 'O': return unserializeObject();
                    case 'C': return unserializeCustomObject();
                    case 'R': return unserializeRef();
                    default: return false;
                }
            }
            return unserialize();
        }
    }
})();
