

dpWDDX = function(Action, Input) {

		// Check the "Action" argument
	if ( typeof Action != "string" ) {
		Action = ""
	} else {
		Action = Action.toLowerCase()
	};
	if ( Action != "serialize" && Action != "deserialize" ) {
		throw new Error("dpWDDX could not understand the action passed to it.  Acceptable actions are 'serialize' or 'deserialize'.");
	};
		// Check the "Input" argument against the Action
	if ( Action == "serialize" && typeof Input != "object" ) {
		throw new Error("dpWDDX action 'serialize' requires an object to work with.");
	};
	if ( Action == "deserialize" ) {
		if ( typeof Action != "string" ) {
			throw new Error("dpWDDX action 'deserialize' requires an XML string to work with.");
		} else {
			if( window.ActiveXObject ) {
				var XMLDOM = new ActiveXObject("Microsoft.XMLDOM");
				XMLDOM.async="false";
				XMLDOM.loadXML(Input);
				Input = XMLDOM;
				if ( Input.parseError.errorCode != 0 ) {
					throw new Error("dpWDDX deserialize encountered the following error from the Microsoft.XMLDOM XML parser:\n" + Input.parseError.reason + "\n\n" + Input.parseError.srcText);
				};
			} else if ( document.implementation && document.implementation.createDocument ) {
				Input = new DOMParser().parseFromString(Input, "text/xml");
			} else {
				throw new Error("dpWDDX deserialize could not generate an XML DOM object.");
			};
		};
	};

		// Set the EncodedChars lists var as function globals.
		// Serialization Chars
	if ( typeof SerializeChars != "object" ) {
		SerializeChars = new Object();
	};
		// Add simple chars (&amp; must come first!)
	SerializeChars["&"] = "&amp;";
	SerializeChars["<"] = "&lt;";
	SerializeChars[">"] = "&gt;";
		// Add codes for Control characters
	for (var CurCode = 0; CurCode < 32; CurCode++ ) {
   		if ( (CurCode != 9) && (CurCode != 10) && (CurCode != 13) ) {
           	var hex = CurCode.toString(16);
               if ( hex.length == 1 ) { hex = "0" + hex };
			SerializeChars[String.fromCharCode(CurCode)] = "<char code='" + hex + "'/>";
		};
	};
	for (var CurCode = 128; CurCode < 256; CurCode++ ) {
          	var hex = CurCode.toString(16);
		SerializeChars[String.fromCharCode(CurCode)] = "&#x" + CurCode.toString(16) + ";";
	};

		// Deserialization Chars
	if ( typeof DeserializeChars != "object" ) {
		DeserializeChars = new Object();
	};
	DeserializeChars["&amp;"] = "&";
	DeserializeChars["&lt;"] = "<";
	DeserializeChars["&gt;"] = ">";
		// Add codes for Control characters
	for (var CurCode = 0; CurCode < 32; CurCode++ ) {
   		if ( (CurCode != 9) && (CurCode != 10) && (CurCode != 13) ) {
           	var hex = CurCode.toString(16);
               if ( hex.length == 1 ) { hex = "0" + hex };
			DeserializeChars["<char code='" + hex + "'/>"] = String.fromCharCode(CurCode);
		};
	};
	for (var CurCode = 128; CurCode < 256; CurCode++ ) {
          	var hex = CurCode.toString(16);
		DeserializeChars["&#x" + CurCode.toString(16) + ";"] = String.fromCharCode(CurCode);
	};


		// Decide, based on the action, what to call
	switch ( Action ) {
		case "serialize" :
			return "<wddxPacket version=\"1.0\"><header/><data>" + serialize(Input) + "</data></wddxPacket>";
		break;
		case "deserialize" :
			return deserialize(Input);
		break;
	};


		// Escape characters using the Encoded Chars tables
	function convertString(CurString) {
		switch ( Action ) {
			case "serialize" :
				EncodedChars = SerializeChars;
			break;
			case "deserialize" :
				EncodedChars = DeserializeChars;
			break;
		};
		var CurRegEx;
		for ( var CurChar in EncodedChars ) {
			if (typeof EncodedChars[CurChar] != "function") {;
				if ( CurChar != "\\" ) {
					CurRegEx = new RegExp(CurChar, "g");
				} else {
					CurRegEx = /\\/g;
				};
				CurString = CurString.replace(CurRegEx, EncodedChars[CurChar]);
			};
		};
		return CurString;	
	};


		// Convert Dates to iso8601
	function convertDate(CurDate) {

		switch ( Action ) {
			case "serialize" :
					// Check the input parameters
				if ( typeof CurDate != "object" || CurDate.constructor != Date ) {
					return null;
				};
					// Init DatePart vars
				var Year,Month,Day,Hours,Minutes,Seconds,Milliseconds,TimeZoneInfo
					// Get DateParts
				Year = ("000" + CurDate.getFullYear()).slice(-4);
				Month = ("0" + (CurDate.getMonth() + 1)).slice(-2);
				Day = ("0" + CurDate.getDate()).slice(-2);
				Hours = ("0" + CurDate.getHours()).slice(-2);
				Minutes = ("0" + CurDate.getMinutes()).slice(-2);
				Seconds = ("0" + CurDate.getSeconds()).slice(-2);
				Milliseconds = ("00" + CurDate.getMilliseconds()).slice(-3);
					// Get TimeZone Information
				var TimeZoneOffset = CurDate.getTimezoneOffset();
				TimeZoneInfo = (TimeZoneOffset >= 0 ? "-" : "+") + ("0" + (Math.floor(Math.abs(TimeZoneOffset) / 60))).slice(-2) + ":" + ("00" + (Math.abs(TimeZoneOffset) % 60)).slice(-2);
					// Return the date
				return Year + "-" + Month + "-" + Day + "T" + Hours + ":" + Minutes + ":" + Seconds + TimeZoneInfo;
			break;
			case "deserialize" :
					// Check the input parameters
				if ( typeof CurDate != "string" ) {
					return null;
				};
					// Set the fragment expressions
				var S = "[\\-/:.-]";
				var Yr = "((?:1[6-9]|[2-9][0-9])[0-9]{2})";
				var Mo = S + "((?:1[012])|(?:0[1-9])|[1-9])";
				var Dy = S + "((?:3[01])|(?:[12][0-9])|(?:0[1-9])|[1-9])";
				var Hr = "(2[0-4]|[01]?[0-9])";
				var Mn = S + "([0-5]?[0-9])";
				var Sd = "(?:" + S + "([0-5]?[0-9])(?:[.,]([0-9]+))?)?";
				var TZ = "(?:(Z)|(?:([\+\-])(1[012]|[0]?[0-9])(?::?([0-5]?[0-9]))?))?";
					// RegEx the input
					// First check: Just date parts (month and day are optional)
					// Second check: Full date plus time (seconds, milliseconds and TimeZone info are optional)
				var TF;
				if ( TF = new RegExp("^" + Yr + "(?:" + Mo + "(?:" + Dy + ")?)?" + "$").exec(CurDate) ) {} else if ( TF = new RegExp("^" + Yr + Mo + Dy + "T" + Hr + Mn + Sd + TZ + "$").exec(CurDate) ) {};
					// If the date couldn't be parsed, return null
				if ( !TF ) { return null };
					// Default the Time Fragments if they're not present
				if ( !TF[2] ) { TF[2] = 1 } else { TF[2] = TF[2] - 1 };
				if ( !TF[3] ) { TF[3] = 1 };
				if ( !TF[4] ) { TF[4] = 0 };
				if ( !TF[5] ) { TF[5] = 0 };
				if ( !TF[6] ) { TF[6] = 0 };
				if ( !TF[7] ) { TF[7] = 0 };
				if ( !TF[8] ) { TF[8] = null };
				if ( TF[9] != "-" && TF[9] != "+" ) { TF[9] = null };
				if ( !TF[10] ) { TF[10] = 0 } else { TF[10] = TF[9] + TF[10] };
				if ( !TF[11] ) { TF[11] = 0 } else { TF[11] = TF[9] + TF[11] };
					// If there's no timezone info the data is local time
				if ( !TF[8] && !TF[9] ) {
					return new Date(TF[1], TF[2], TF[3], TF[4], TF[5], TF[6], TF[7]);
				};
					// If the UTC indicator is set the date is UTC
				if ( TF[8] == "Z" ) {
					return new Date(Date.UTC(TF[1], TF[2], TF[3], TF[4], TF[5], TF[6], TF[7]));
				};
					// If the date has a timezone offset
				if ( TF[9] == "-" || TF[9] == "+" ) {
						// Get current Timezone information
					var CurTZ = new Date().getTimezoneOffset();
					var CurTZh = TF[10] - ((CurTZ >= 0 ? "-" : "+") + Math.floor(Math.abs(CurTZ) / 60))
					var CurTZm = TF[11] - ((CurTZ >= 0 ? "-" : "+") + (Math.abs(CurTZ) % 60))
						// Return the date
					return new Date(TF[1], TF[2], TF[3], TF[4] - CurTZh, TF[5] - CurTZm, TF[6], TF[7]);
				};
					// If we've reached here we couldn't deal with the input, return null
				return null;
			break;
		};
	};


		// Parse objects to WDDX
	function serialize(CurOb) {

		var Results = "";
		var CurFieldNames = "";

		switch ( getType(CurOb) ) {
	        case "object":
				Results += "<struct>";
				for ( var CurProp in CurOb ) {
					if ( getType(CurOb[CurProp]) != "function"  ) {
						Results += "<var name='" + CurProp + "'>" + serialize(CurOb[CurProp]) + "</var>";
					};
				};
				Results += "</struct>";
				break;
	        case "array":
				Results += "<array length=\'" + CurOb.length + "\'>";
				for ( var Cnt = 0; Cnt < CurOb.length; Cnt++ ) {
					Results += serialize(CurOb[Cnt]);
				};
				Results += "</array>";
				break;
	        case "function":
				break;
	        case "date":
				Results += "<dateTime>" + convertDate(CurOb) + "</dateTime>";
				break;
	        case "number":
				if ( isFinite(CurOb) ) {
					Results += "<number>" + CurOb.toString() + "</number>";
				} else {
	        	    Results += "<null />";
				};
				break;
	        case "string":
				Results += "<string>" + convertString(CurOb) + "</string>";
				break;
	        case "boolean":
				Results += "<boolean value='" + CurOb.toString() + "'/>";
				break;
	        case "binary":
				Results += "<binary length='" + CurOb.length + "'>" + CurOb.toString() + "</binary>";
				break;
	        case "null":
				Results += "<null />";
				break;
	        case "undefined":
				Results += "<null />";
				break;
	        case "unknown":
				Results += "<null />";
				break;
		};

			// Return Results
		return Results;


			// Add the "getType" function which offers more resolution than "typeof"
		function getType(Ob) {
		
			try {
				switch (typeof Ob) {
			        case "object":
						if ( Ob == null ) {
							return "null";
						} else if ( Ob.constructor == Date ) {
							return "date";
						} else if ( Ob.constructor == Array ) {
							return "array";
						} else if ( Ob.constructor == String ) {
							return "string";
						} else if ( Ob.constructor == Number ) {
							return "number";
						} else if ( Ob.constructor == Boolean ) {
							return "boolean";
						} else if ( Ob == undefined ) {
							return "undefined";
						} else {
							return "object";
						};
			        case "function":
						return "function";
			        case "number":
						return "number";
			        case "string":
						return "string";
			        case "boolean":
						return "boolean";
			        case "undefined":
						return "undefined";
			        default:
						return "unknown";
				};
			} catch (CurError) {
				return "unknown";
			};
		
		};

	};


		// Parse objects from WDDX
	function deserialize(CurXML) {

		var CurOutput = null;
	
			// Set a DataRoot shortcut
		var DataRoot = CurXML.getElementsByTagName("data").item(0);
		for ( var Cnt = 0; Cnt < DataRoot.childNodes.length; Cnt++ ) {
			var CurNode = DataRoot.childNodes.item(Cnt);
			switch ( CurNode.nodeName )	{
				case "null": case "boolean": case "number": case "dateTime": case "string": case "array": case "struct": case "recordset": case "binary":
					CurOutput = deserialize_Data(CurNode);
			};
		};

			// Return the output
		return CurOutput;

			// Parse Data
		function deserialize_Data(CurNode, CurMetaData) {

			var Output = null;

				// Construct the output
			switch ( CurNode.nodeName ) {
		        case "struct":
					Output = new Object();
					for ( var Cnt = 0; Cnt < CurNode.childNodes.length; Cnt++ ) {
							// Not all nodes are "var"
						if ( CurNode.childNodes.item(Cnt).nodeName == "var" ) {
							var CurVarNode = CurNode.childNodes.item(Cnt);
								// Get Key Name
							var CurKeyName = "";
							for ( var iCnt = 0; iCnt < CurVarNode.attributes.length; iCnt++ ) {
								var CurAttribute = CurVarNode.attributes[iCnt];
								if ( CurAttribute.name == "name" ) {
									CurKeyName = CurAttribute.value;
								};
							};
								// Get the data node
							for ( var iCnt = 0; iCnt < CurVarNode.childNodes.length; iCnt++ ) {
								var CurVarChildNode = CurVarNode.childNodes.item(iCnt);
								switch ( CurVarChildNode.nodeName )	{
									case "null": case "boolean": case "number": case "dateTime": case "string": case "array": case "struct": case "recordset": case "binary":
										// Call for more output recursively
									Output[CurKeyName] = deserialize_Data(CurVarChildNode);
								};
							};
						};
					};
					break;
				case "array":
					Output = new Array();
					for ( var Cnt = 0; Cnt < CurNode.childNodes.length; Cnt++ ) {
						var CurChildNode = CurNode.childNodes.item(Cnt);
						switch ( CurChildNode.nodeName )	{
							case "null": case "boolean": case "number": case "dateTime": case "string": case "array": case "struct": case "recordset": case "binary":
								// Call for more output recursively
							Output[Output.length] = deserialize_Data(CurChildNode);
						};
					};
					break;
		        case "recordset":
					Output = new Object();
					for ( var Cnt = 0; Cnt < CurNode.childNodes.length; Cnt++ ) {
							// Not all nodes are "var"
						if ( CurNode.childNodes.item(Cnt).nodeName == "field" ) {
							var CurFieldNode = CurNode.childNodes.item(Cnt);
								// Get Field Name
							var CurFieldName = "";
							for ( var iCnt = 0; iCnt < CurFieldNode.attributes.length; iCnt++ ) {
								var CurAttribute = CurFieldNode.attributes[iCnt];
								if ( CurAttribute.name == "name" ) {
									CurFieldName = CurAttribute.value;
								};
							};
								// Get the field data
							var FieldArray = new Array();
							for ( var iCnt = 0; iCnt < CurFieldNode.childNodes.length; iCnt++ ) {
								var CurFieldChildNode = CurFieldNode.childNodes.item(iCnt);
								switch ( CurFieldChildNode.nodeName )	{
									case "null": case "boolean": case "number": case "dateTime": case "string": case "array": case "struct": case "recordset": case "binary":
										// Call for more output recursively
									FieldArray[FieldArray.length] = deserialize_Data(CurFieldChildNode);
								};
							};
								// Add the array to the object
							Output[CurFieldName] = FieldArray;
						};
					};
					break;
		        case "datetime":
					if ( CurNode.childNodes.length == 0 ) {
						Output = new Date();
					} else if ( CurNode.childNodes.item(0).nodeType == 3 ) {
						var CurVal = CurNode.childNodes.item(0).nodeValue;
						Output = convertDate(CurVal);
					};
					break;
		        case "number":
					if ( CurNode.childNodes.length == 0 ) {
						Output = new Number();
					} else if ( CurNode.childNodes.item(0).nodeType == 3 ) {
						var CurVal = CurNode.childNodes.item(0).nodeValue;
						if ( CurVal == parseInt(CurVal) ) {
							Output = parseInt(CurVal);
						} else if( CurVal == parseFloat(CurVal) ) {
							Output = parseFloat(CurVal);
						};
					};
					break;
		        case "string":
					if ( CurNode.childNodes.length == 0 ) {
						Output = new String();
					} else if ( CurNode.childNodes.item(0).nodeType == 3 ) {
						Output = convertString(CurNode.childNodes.item(0).nodeValue);
					};
					break;
		        case "boolean":
					for ( var Cnt = 0; Cnt < CurNode.attributes.length; Cnt++ ) {
						var CurAttribute = CurNode.attributes[Cnt];
						if ( CurAttribute.name == "value" ) {
							Output = CurAttribute.value;
						};
					};
					break;
		        case "binary":
					Output = null;
					break;
		        case "null":
					Output = null;
					break;
			};

			return Output;

		};
	
	};


};