/**
 * Object PHP_Serializer
 * 	JavaScript to PHP serialize / unserialize class.
 * This class is designed to convert php variables to javascript
 * and javascript variables to php with a php serialize unserialize
 * compatible way.
 *
 * PARSABLE PHP TO JAVASCRIPT VARIABLES:
 * 	[ PHP TYPE ]			[ JAVASCRIPT TYPE ]
 * 	array				( will be a typeof object ) (*)
 * 	string				( will be a typeof string )
 * 	boolean				( will be a typeof boolean )
 * 	undefined (and null too)	( will be a typeof null )
 * 	integer / double 		( will be a typeof number )
 *
 * PARSABLE JAVASCRIPT TO PHP VARIABLES:
 *	[ JAVASCRIPT TYPE ]		[ PHP TYPE ]
 * 	object				( will be an array ) (*)
 * 	string				( will be a string )
 * 	boolean				( will be a bool / boolean )
 * 	undefined (and null too)	( will be a null )
 * 	number				( will be an integer or a double )
 *
 * (*) NOTE:
 * You cannot send a PHP Object or Class to JavaScript because I think
 * it's really unusefull.
 * Any PHP serialized class requires the native PHP class to be used, then it's not a
 * PHP => JavaScript converter, it's just a usefull serilizer class for each
 * compatible JS and PHP variable types.
 * Lambda, Resources or other dedicated PHP variables are not usefull for JavaScript too
 * (i.e.
 * 	$v = create_function('', 'return 1;'); serialize($v);
 *	$conn = mydb_connect(); serialize($conn);
 * )
 * There are same restrictions for javascript functions too then these will not be sent
 * (but will be filtered / ignored automatically).
 * Javascript Objects will be converted to php arrays and will be returned as new Array
 * (i.e.
 * 	var o = new Object(); o.t = 'test'; o = php.unserialize(php.serialize(o));
 * 	// in this case will be returned a new Array and not a new Object .... however
 * 	// the typeof will be the same of object and 't' key will be usable too
 * 	// then you can use alert(0.t); without any problems.
 * )
 * _____________________________________________
 *
 * EXAMPLE:
 *	var php = new PHP_Serializer();
 *	alert(php.unserialize(php.serialize(somevar)));
 *	// should alert the original value of somevar
 * ---------------------------------------------
 * @author              Andrea Giammarchi
 * @site		www.devpro.it
 * @date                2005/11/26
 * @lastmod             2005/11/28 08:40 [it's really faster!]
 * @credits		Special thanks to Fabio Sutto for some ideas and some debug
 * @version             1.2, tested on FireFox 1.0.7, IE 6 SP2 and Opera 8
 */
function PHP_Serializer() {
	/**
	 * Public method
	 * Serializes a variable for php unserialize function.
	 * 	this.serialize(what:Mixed):String
	 * @param	Mixed		variable to serialize
	 * @return	String		php serialized variable
	 */
	function serialize(what) {
		this.__string = function(__s) {
			return ('s:'+__s.length+':"'+__s+'";');
		}
		this.__number = function(__s) {
			return ((String(__s).indexOf('.')==-1)?'i:'+__s+';':'d:'+__s+';');
		}
		this.__boolean = function(__s) {
			return ('b:'+(__s?'1':'0')+';');
		}
		this.__undefined = function(__s) {
			return ('N;');		
		}
		this.__object = function(__s) {
			var n;
			var a = 0;
			var ser = '';
			for(var b in __s) {
				n = (__s[b] == null);
				if(n || (__s[b].constructor != Function && b != '__class')) {
					ser+=(!isNaN(b))?this.__number(b):this.__string(b);
					ser+=n?this.__undefined(n):this['__'+typeof(__s[b])](__s[b]);
					a++;
				}
			}
			return ('a:'+a+':{'+ser+'}');
		}
		if(what == null)
			var ser = this.__undefined(what);
		else
			var ser = this['__'+typeof(what)](what);
		return ser;
	}

	/**
	 * Public method
	 * Unserializes a variable from php serialize function.
	 * 	this.unserialize(what:String):Mixed
	 * @param	String		php serialized variable
	 * @return	Mixed		javascript variable
	 */
	function unserialize(what) {
		this.s = function() {
			var sls = this.__s.substr(2,(this.__s.indexOf(':',2)-2));
			var sli = parseInt(sls);
			sls = sls.length + 4;
			var tmp = this.__s.substr(sls,sli);
			this.reduce((sli + sls + 2));
			return tmp;
		}
		this.i = this.d = function() {
			var sli = this.__s.indexOf(';',1) - 2;
			var tmp = Number(this.__s.substr(2,sli));
			this.reduce((sli + 3));
			return tmp;
		}
		this.b = function() {
			var tmp = (this.__s.substr(2,1)=='1'?true:false);
			this.reduce(4);
			return tmp;
		}
		this.N = function() {
			this.reduce(2);
			return null;
		}
		this.a = function() {
			var l;
			var key;
			var tmp = new Array();
			var a = this.__s.indexOf(':',2);
			var k = parseInt(this.__s.substr(2,(a-2)));
			this.reduce((a+2));
			for(a=0;a<k;a++) {
				key = this[this.__s.substr(0,1)]();
				l = this.__s.substr(0,1);
				tmp[key] = this[l]();
				if(l == 'a')
					this.reduce(1);
			}
			return tmp;
		}
		this.reduce = function(l) {
			this.__s = this.__s.substr(l,(this.__s.length-l));
		}
		this.__s = what;
		delete what;
		return this[this.__s.substr(0,1)]();
	}
	this.serialize=serialize;
	this.unserialize=unserialize;
	
	this.keyStr = "ABCDEFGHIJKLMNOP" +
                "QRSTUVWXYZabcdef" +
                "ghijklmnopqrstuv" +
                "wxyz0123456789+/" +
                "=";
	
	this.encode64 = function(input) {
		var output = "";
		var chr1, chr2, chr3 = "";
		var enc1, enc2, enc3, enc4 = "";
		var i = 0;
		
		do {
			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);
			
			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >>4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;
			
			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if(isNaN(chr3)) {
				enc4 = 64;
			}
			
			output = output +
				this.keyStr.charAt(enc1) +
				this.keyStr.charAt(enc2) +
				this.keyStr.charAt(enc3) +
				this.keyStr.charAt(enc4);
			chr1 = chr2 = chr3 = "";
			enc1 = enc2 = enc3 = enc4 = "";
		} while (i < input.length);
		
		return output;
	}
	
	this.decode64 = function(input) {
		var output = "";
	    var chr1, chr2, chr3 = "";
	    var enc1, enc2, enc3, enc4 = "";
	    var i = 0;
		
		// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
      var base64test = /[^A-Za-z0-9\+\/\=]/g;
      if (base64test.exec(input)) {
         alert("There were invalid base64 characters in the input text.\n" +
               "Valid base64 characters are A-Z, a-z, 0-9, '+', '/', and '='\n" +
               "Expect errors in decoding.");
      }
      input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

      do {
         enc1 = this.keyStr.indexOf(input.charAt(i++));
         enc2 = this.keyStr.indexOf(input.charAt(i++));
         enc3 = this.keyStr.indexOf(input.charAt(i++));
         enc4 = this.keyStr.indexOf(input.charAt(i++));

         chr1 = (enc1 << 2) | (enc2 >> 4);
         chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
         chr3 = ((enc3 & 3) << 6) | enc4;

         output = output + String.fromCharCode(chr1);

         if (enc3 != 64) {
            output = output + String.fromCharCode(chr2);
         }
         if (enc4 != 64) {
            output = output + String.fromCharCode(chr3);
         }

         chr1 = chr2 = chr3 = "";
         enc1 = enc2 = enc3 = enc4 = "";

      } while (i < input.length);

      return output;
	}
}