/* ***** LICENSE BLOCK *****
 * Version: GPL 3
 *
 * This program is Copyright (C) 2007-2008 Aptana, Inc. All Rights Reserved
 * This program is licensed under the GNU General Public license, version 3 (GPL).
 *
 * This program is distributed in the hope that it will be useful, but
 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 * NONINFRINGEMENT. Redistribution, except as permitted by the GPL,
 * is prohibited.
 *
 * You can redistribute and/or modify this program under the terms of the GPL, 
 * as published by the Free Software Foundation.  You should
 * have received a copy of the GNU General Public License, Version 3 along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Aptana provides a special exception to allow redistribution of this file
 * with certain other code and certain additional terms
 * pursuant to Section 7 of the GPL. You may view the exception and these
 * terms on the web at http://www.aptana.com/legal/gpl/.
 * 
 * You may view the GPL, and Aptana's exception and additional terms in the file
 * titled license-jaxer.html in the main distribution folder of this program.
 * 
 * Any modifications to this file must keep this entire header intact.
 *
 * ***** END LICENSE BLOCK ***** */

/*
 * fragment : framework > clientConfig.js
 */
(function() {
	
var config = // for future use
{
};

if (typeof Jaxer == "undefined") { Jaxer = {}; }

/**
 * True on the server side, false on the client (browser).
 * 
 * @alias Jaxer.isOnServer
 * @property {Boolean}
 */
Jaxer.isOnServer = false;

/**
 * Holds the proxy functions for calling server functions that were designated
 * with runat="both-proxy" (or equivalent), to prevent function name collisions.
 * So e.g. if a function getName() is defined with runat="both-proxy", in the
 * browser you can call getName() to use the client-side function or
 * Jaxer.Server.getName() to use the server-side function. Jaxer.Server holds
 * both the synchronous and asynchronous versions of the proxies (e.g.
 * Jaxer.Server.getName() and Jaxer.Server.getNameAsync()).
 * 
 * @alias Jaxer.Server
 */
if (typeof Jaxer.Config == "undefined") { Jaxer.Config = {}; }
if (typeof Jaxer.Server == "undefined") { Jaxer.Server = {}; }

for (var prop in config)
{
	Jaxer.Config[prop] = config[prop];
}

if (typeof Jaxer.Log == "undefined")
{
	function ModuleLogger()
	{
		this.trace = this.debug = this.info = this.warn = this.error = this.fatal = function() {};
	}
	var genericModuleLogger = new ModuleLogger();
	Jaxer.Log = genericModuleLogger;
	Jaxer.Log.forModule = function() { return genericModuleLogger; }
}

})();

/*
 * fragment : Serialization > Serialization.js
 */

// NOTE! This is a server- and client-side, *compressible* module -- be sure to end each function declaration with a semicolon

/*
 * Original code from Douglas Crockford's json.js, 2007-04-30
 */

(function(){

// create Serialization container

/**
 * @namespace {Jaxer.Serialization} Namespace that contains serialization methods.
 */
var Serialization = {};
Serialization.NO_RESULT = "null";
Serialization.TRUNCATION_MESSAGE = "__truncated__";
Serialization.JSONEvalErrorName = "JSONEvalError";
Serialization.JSONSyntaxErrorName = "JSONSyntaxError";

/**
 * Checks whether the given argument is JSON-serializable (i.e. JSON-
 * representible) or not (e.g. functions are not).
 * 
 * @alias Jaxer.Serialization.isSerializable
 * @param {Object} obj
 * 		The object to test, which can be of any type or even undefined
 * @return {Boolean}
 * 		true if representable in JSON, false otherwise.
 */
Serialization.isSerializable = function isSerializable(obj)
{
	var result = false;
	
	switch (typeof obj)
	{
		case "string":
		case "number":
		case "boolean":
		case "object": // also includes Dates and Arrays
			result = true;
			break;
			
		case "function":
			result = false;
			break;
			
		default:
			result = false;
	}
	
	return result;
};

/**
 * Reconstruct a Javascript data structure from a JSON string. Note that we have
 * extended JSON to support object references and to support dates. Object
 * references allow multiple places within the JSON data structure to point to
 * the same object as opposed to clones of those objects. Date support uses a
 * special string format to store a given date in GMT
 * 
 * @alias Jaxer.Serialization.fromJSONString
 * @param {String} json
 * 		A string in the JSON format
 * @return {Object}
 * 		The resulting object graph after converting the JSON string to the
 * 		equivalent Javascript data structure
 */
Serialization.fromJSONString = function fromJSONString(json)
{
	var REFERENCE_PATTERN = /^~(\d+)~$/;
	var DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})$/;
	var references = [];
	var result;

	/**
	 * A reference constitutes an object and a property on the object. This
	 * class is used to specify a specific property on an object for later
	 * setting of that value.
	 *
	 * @private
	 * @constructor
	 * @param {Object} object
	 * 		The source object of this reference
	 * @param {String} property
	 * 		the property on the object representing this reference value
	 * @param {Number} index
	 * 		The reference ID that uniquely identifies this reference 
	 */
	function Reference(object, property, index)
	{
		this.object = object;
		this.property = property;
		this.index = index;
	}
	
	/**
	 * Walks the list of nodes passed in the method and sets all properties
	 * on this instance's underlying object to the values in the node list
	 *
	 * @private
	 * @param {Array} nodes
	 * 		A list of all nodes in the data graph. This array is used to
	 * 		extract the value of this reference via this reference's unique id.
	 */
	Reference.prototype.setValue = function(nodes)
	{
		var result = false;
		
		if (0 <= this.index && this.index < nodes.length)
		{
			this.object[this.property] = nodes[this.index];
			result = true;
		}
		
		return result;
	};
	
	/**
	 * This post-processing steps replaces all reference strings with the actual
	 * object reference to which they refer.
	 * 
	 * @private
	 * @param {Array} input
	 * 		The source array created by the first step of eval'ing the JSON
	 * 		source string.
	 * @return {Object}
	 * 		The resulting object created by dereferencing all reference values
	 * 		and rewiring of the object graph
	 */
	function postProcess(input)
	{
		var result = input;
		
		if (input.length > 0)
		{
			var valid = true;
			
			for (var i = 0; i < input.length; i++)
			{
				var item = input[i];
				
				if (item === null || item === undefined)
				{
					valid = false;
					break;
				}
				
				var type = item.constructor;
				var itemGlobal = (typeof item.__parent__ == "undefined") ? window : item.__parent__;
				
				if (type !== itemGlobal.Array && type !== itemGlobal.Object)
				{
					valid = false;
					break;
				}
				
				// add any references
				switch (type)
				{
					case itemGlobal.Array:
						postProcessArray(item);
						break;
						
					case itemGlobal.Object:
						postProcessObject(item);
						break;
				}
			}
			
			if (valid)
			{
				if (references.length > 0)
				{
					result = input[0];
					
					for (var i = 0; i < references.length; i++)
					{
						var success = references[i].setValue(input);
						
						if (success == false)
						{
							result = input;
							break;
						}
					}
				}
			}
		}
		
		return result;
	}
	
	/**
	 * This post-processing step replaces all object references that are members
	 * of the specified array with actual references to the object to which they
	 * refer
	 * 
	 * @private
	 * @param {Array} ary
	 * 		The source array to process
	 * @return {Boolean}
	 * 		Returns true if the specified array was a valid reference array
	 */
	function postProcessArray(ary)
	{
		var result = true;
		
		for (var i = 0; i < ary.length; i++)
		{
			if (postProcessMember(ary, i) == false)
			{
				result = false;
				break;
			}
		}
		
		return result;
	}
	
	/**
	 * This post-processing step replaces all object references that are members
	 * of the specified object with actual references to the object to which
	 * they refer
	 * 
	 * @private
	 * @param {Object} obj
	 * 		The source object to process
	 * @param {Array} references
	 * 		An array of reference instances
	 * @return {Boolean}
	 * 		Returns true if the specified object was a valid reference object
	 */
	function postProcessObject(obj, references)
	{
		var result = true;
		
		for (var p in obj)
		{
			if (postProcessMember(obj, p) == false)
			{
				result = false;
				break;
			}
		}
		
		return result;
	}
	
	/**
	 * This post-processing steps replaces all reference strings with the actual
	 * object reference to which they refer. Also, special date string formats
	 * are replaced with actual Date objects.
	 * 
	 * @private
	 * @param {Object} obj
	 * 		The object to post-process
	 * @param {String|Number} property
	 * 		The name or index of the object to process.
	 * @return {Boolean}
	 * 		Returns true if the obj[property] value is a valid reference object
	 */
	function postProcessMember(obj, property)
	{
		var item = obj[property];
		var result = true;
		
		if (item !== null && item !== undefined)
		{
			var type = item.constructor;
			var itemGlobal = (typeof item.__parent__ == "undefined") ? window : item.__parent__;
			
			switch (type)
			{
				case itemGlobal.Array:
					// we only allow empty arrays
					if (item.length > 0)
					{
						result = false;
					}
					break;

				case itemGlobal.Object:
					// we only allow empty objects
					for (var p in item)
					{
						result = false;
						break;
					}
					break;
											
				case itemGlobal.String:
					var match = item.match(REFERENCE_PATTERN);
					
					if (match !== null)
					{
						var index = match[1] - 0;
						var ref = new Reference(obj, property, index);
						
						references.push(ref);
					}
					else
					{
						match = item.match(DATE_PATTERN);
						
						if (match !== null)
						{
							obj[property] = new Date(Date.UTC(match[1], match[2] - 1, match[3], match[4], match[5], match[6]));
						}
					}
					break;
			}
		}
		
		return result;
	}
	
	/**
	 * For JSON strings that do not contain references, we make a
	 * post-processing step to replace all special date strings with actual
	 * Date instances.
	 * 
	 * @private
	 * @param {String} property
	 * 		The property name to filter
	 * @param {Object} value
	 * 		The value of the property being filtered
	 */
	function filter(property, value)
	{
		var result = value;
		
		if (typeof value == "string")
		{
			var match = value.match(DATE_PATTERN);
			
			if (match)
			{
				result = new Date(Date.UTC(match[1], match[2] - 1, match[3], match[4], match[5], match[6]));
			}
		}
		
		return result;
	}
	
	/**
	 * Traverse the resulting JSON object to perform any post-processing needed
	 * to convert references and date strings to their proper instances.
	 * 
	 * @private
	 * @param {String} property
	 * 		The name of the propery to visit
	 * @param {Object} obj
	 * 		The object whose property will be visited
	 * @return {Object}
	 * 		The resulting filter property value
	 */
	function walk(property, obj)
	{
		if (obj && typeof obj === 'object')
		{
			for (var p in obj)
			{
				if (obj.hasOwnProperty(p))
				{
					obj[p] = walk(p, obj[p]);
				}
			}
		}
		
		return filter(property, obj);
	}
	
	// Run the text against a regular expression to look 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.

	if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+EINaefilnr-uy \n\r\t])+?$/.test(json))
	{
		// 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.

		try
		{
			result = eval('(' + json + ')');
		}
		catch (e)
		{
			var err = new Error("parseJSON: exception '" + e + "' when evaluating: " + json);
			err.name = Serialization.JSONEvalErrorName;
			throw err;
		}
	}
	else
	{
		var err = new Error("parseJSON: unexpected characters in: " + json);
		err.name = Serialization.JSONSyntaxErrorName;
		throw err;
	}

	// expand references
	
	if (result)
	{
		var itemGlobal = (typeof result.__parent__ == "undefined") ? window : result.__parent__;
		
		if (result.constructor === itemGlobal.Array)
		{
			result = postProcess(result);
		}
		else
		{
			// expand special strings (just date right now)
			result = walk('', result);
		}
	}
	
	return result;
};

/**
 * Convert the specified object into a JSON representation. Note that we have
 * modified JSON to support object references (cycles) and to convert Dates into
 * a special format that will be recognized by our code during deserialization.
 * 
 * @alias Jaxer.Serialization.toJSONString
 * @param {Object} data
 * 		The source object to convert to a JSON string
 * @param {Number} [maxDepth]
 * 		An optional integer specifying how deep a recursion should be attempted
 * 		before stopping
 * @return {String}
 * 		The resulting JSON string which can be reversed back into the source
 * 		object via Serialization.fromJSONString
 */
Serialization.toJSONString = function toJSONString(data, maxDepth)
{
	var ID_PROPERTY = "$id";
	var DEFAULT_MAX_DEPTH = 10;
	
	if (typeof maxDepth != "number") 
	{
		maxDepth = DEFAULT_MAX_DEPTH;
	}
	
	// NOTE: this block effectively aborts this method
	if (maxDepth == 0) 
	{
		return '"' + TRUNCATION_MESSAGE + '"';
	}
	
	/**
	 * Convert an Array to a JSON string
	 * 
	 * @private
	 * @param {Array} ary
	 * 		The source Array to be serialized
	 * @return {String}
	 * 		The resulting JSON string
	 */
	function ArrayToJSON(ary)
	{
		var result = [];
		var length = ary.length;
		
		// For each value in this array...
		for (var i = 0; i < length; i += 1)
		{
			if (Serialization.isSerializable(ary[i])) 
			{
				result.push(Serialization.toJSONString(ary[i], maxDepth - 1));
			}
		}
	
		// Join all of the fragments together and return.
		return "[" + result.join(",") + "]";
	}
	
	/**
	 * Convert a date to a our special string format for later deserizliation
	 * 
	 * @private
	 * @param {Date} data
	 * 		The source Date to be serialized
	 * @return {String}
	 * 		The resulting JSON string
	 */
	function DateToJSON(data)
	{
		// Format integers to have at least two digits.
		function pad(n)
		{
			return n < 10 ? '0' + n : n;
		}
	
		// Ultimately, this method will be equivalent to the date.toISOString
		// method.
		return '"' +
			data.getFullYear() + '-' +
			pad(data.getUTCMonth()) + '-' +
			pad(data.getUTCDate()) + 'T' +
			pad(data.getUTCHours()) + ':' +
			pad(data.getUTCMinutes()) + ':' +
			pad(data.getUTCSeconds()) + '"';
	}
	
	/**
	 * Convert an object to a JSON string
	 * 
	 * @private
	 * @param {Object} data
	 * 		The source object to be serialized
	 * @return {String}
	 * 		The resulting JSON string
	 */
	function ObjectToJSON(data)
	{
		var result = [];
	
		// Iterate through all of the keys in the object, ignoring the proto chain.
		for (var k in data)
		{
			var p = '"' + k + '":';
			var v = data[k];
			
			if (Serialization.isSerializable(v)) 
			{
				result.push(p + Serialization.toJSONString(v, maxDepth - 1));
			}
		}
	
		// Join all of the fragments together and return.
		return "{" + result.join(',') + "}";
	}
	
	/**
	 * Convert a string to a JSON string
	 * 
	 * @private
	 * @param {Object} data
	 * 		The source string to be serialized
	 * @return {String}
	 * 		The resulting JSON string
	 */
	function StringToJSON(data)
	{
		// m is a table of character substitutions.
		var characterMap = {
			'\b': '\\b',
			'\t': '\\t',
			'\n': '\\n',
			'\f': '\\f',
			'\r': '\\r',
			'"' : '\\"',
			'\\': '\\\\'
		};
		
		// 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(data))
		{
			return '"' + data.replace(
				/([\x00-\x1f\\"])/g,
				function (a, b)
				{
					var c = characterMap[b];
					
					if (c)
					{
						return c;
					}
					
					c = b.charCodeAt();
					
					return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
				}
			) + '"';
		}
		
		return '"' + data + '"';
	}
	
	/**
	 * A wrapped object is used to hold objects that are not expandable. We
	 * need to be able to add an id property to each object to find cycles
	 * in the data graph. If that object doesn't allow new properties to be
	 * added to it (typically XPCOM wrapper objects), then we can use an
	 * instance of WrappedObject in its place. This object will server only
	 * as a container for an object and its id. This will later be expanded
	 * back into the serialization stream as the underlying object so this
	 * should never appear in the final JSON string
	 * 
	 * @private
	 * @constructor
	 * @param {Object} id
	 * @param {Object} object
	 */
	function WrappedObject(id, object)
	{
		// set id
		this[ID_PROPERTY] = id;
		
		// safe reference so we can test if this is exactly equivalent to other
		// references to this object
		this.object = object;
		
		// add to wrapped item list
		wrappedItems.push(this);
	}
	
	/**
	 * Since wrapped objects can't have properties added to them, we need to
	 * check the wrappedItems array to see if it exists there. This is
	 * equivalent to checking if the id property has been defined on an object
	 * that couldn't have that property added to it
	 * 
	 * @private
	 * @param {Object} object
	 * @return {Boolean}
	 */
	function isWrappedItem(object)
	{
		var length = wrappedItems.length;
		var result = false;
		
		for (var i = 0; i < length; i++)
		{
			var wrappedItem = wrappedItems[i];
			
			if (wrappedItem.object === object)
			{
				result = true;
				break;
			}
		}
		
		return result;
	}
	
	/**
	 * This function will return either a WrappedItem instance or the object
	 * passed into the function. If the object has been wrapped, then its
	 * wrapper is returned; othewise, we return the object itself
	 * 
	 * @private
	 * @param {Object} object
	 * @return {Object}
	 */
	function getWrappedItem(object)
	{
		var length = wrappedItems.length;
		var result = object;
		
		for (var i = 0; i < length; i++)
		{
			var wrappedItem = wrappedItems[i];
			
			if (wrappedItem.object === object)
			{
				result = wrappedItem;
				break;
			}
		}
		
		return result;
	}
	
	/**
	 * Walk the object graph and tag all items in the graph. Note that cycles
	 * are detected in this process and all special properties used for this
	 * discovery process are later removed.
	 * 
	 * @private
	 * @param {Object} obj
	 * 		The top-most object in the data graph.
	 * @return {Boolean}
	 * 		Return true if this specifed object contains references; otherwise,
	 * 		return false. This value can be used to decide if this object needs
	 * 		to be represented as standard JSON or in our extended format.
	 */
	function tagReferences(obj)
	{
		var index = 0;
		var result = false;
		var queue = [obj];
		
		while (queue.length > 0)
		{
			var item = queue.shift();
			
			if (item !== null && item !== undefined)
			{
				if (item.hasOwnProperty(ID_PROPERTY) == false && isWrappedItem(item) == false)
				{
					var type = item.constructor;
					var itemGlobal = (typeof item.__parent__ == "undefined") ? window : item.__parent__;
					
					switch (type)
					{
						case itemGlobal.Array:
							if (item.length > 0)
							{
								item[ID_PROPERTY] = index;
								items[index] = item;
								index++;
								
								for (var i = 0; i < item.length; i++)
								{
									if (Serialization.isSerializable(item[i])) 
									{
										queue.push(item[i]);
									}
								}
							}
							break;
							
						case itemGlobal.Object:
							for (var p in item)
							{
								// we only get here if we have properties
								try
								{
									item[ID_PROPERTY] = index;
									items[index] = item;
								}
								catch(e)
								{
									// Some objects, like XPCOM objects don't
									// allow properties to be added to them, so,
									// we wrap these objects into WrappedObject
									// for later special processing
									items[index] = new WrappedObject(index, item);
								}
								
								index++;
								
								// process child properties
								for (var p in item)
								{
									if (Serialization.isSerializable(item[p])) 
									{
										queue.push(item[p]);
									}
								}
								
								// exit since we only needed to know if we had properties
								break;
							}
							break;
							
						default:
							break;
					}
				}
				else
				{
					result = true;
				}
			}
		}
		
		return result;
	}
	
	/**
	 * Remove all temporary properties used in cycle discovery
	 * 
	 * @private
	 */
	function untagReferences()
	{
		for (var i = 0; i < items.length; i++)
		{
			var item = items[i];
			
			// only non-wrapped objects were able to have the id property added to them
			if (item.constructor !== WrappedObject) 
			{
				delete item[ID_PROPERTY];
			}
		}
	}
	
	/**
	 * Convert the specified object into a JSON string
	 * 
	 * @private
	 * @param {Object} data
	 * 		The Javascript value to be serialized
	 * @return {String}
	 * 		The resulting JSON string
	 */
	function JSONify(data)
	{
		var result = Serialization.NO_RESULT;
		if (Serialization.isSerializable(data)) 
		{
			var ctor = data.constructor;
			var dataGlobal = (typeof data.__parent__ == "undefined") ? window : data.__parent__;
			
			switch (ctor)
			{
				case dataGlobal.Array:
					result = ArrayToJSON(data);
					break;
					
				case dataGlobal.Boolean:
					result = String(data);
					break;
					
				case dataGlobal.Number:
					result = String(data);
					break;
					
				case dataGlobal.Date:
					result = DateToJSON(data);
					break;
					
				case dataGlobal.Object:
					result = ObjectToJSON(data);
					break;
					
				case dataGlobal.String:
					result = StringToJSON(data);
					break;
					
				case dataGlobal.Function:
					result = Serialization.NO_RESULT; // should not get here because we've checked for being serializable, but just in case
					break;
					
				default: // custom objects
					result = ObjectToJSON(data);
					break;
			}
		}
		
		return result;
	}
	
	var result = Serialization.NO_RESULT;
	var items = [];
	var wrappedItems = [];
	
	if (data !== null && data !== undefined)
	{
		if (tagReferences(data) == false)
		{
			untagReferences();
			
			result = JSONify(data);
		}
		else
		{
			var references = [];
			
			for (var i = 0; i < items.length; i++)
			{
				var item = items[i];
				
				// grab stand-in object if this is a WrappedObject
				if (item.constructor === WrappedObject)
				{
					item = item.object;
				}
				
				var type = item.constructor;
				var itemGlobal = (typeof item.__parent__ == "undefined") ? window : item.__parent__;
				
				switch (type)
				{
					case itemGlobal.Array:
						var parts = [];
						
						for (var j = 0; j < item.length; j++)
						{
							var elem = getWrappedItem(item[j]);
							
							if (elem !== null && elem !== undefined)
							{
								if (elem.hasOwnProperty(ID_PROPERTY))
								{
									parts.push('"~' + elem[ID_PROPERTY] + '~"');
								}
								else
								{
									parts.push(JSONify(elem));
								}
							}
							else
							{
								parts.push(Serialization.NO_RESULT);
							}
						}
						
						references.push("[" + parts.join(",") + "]");
						break;
						
					case itemGlobal.Object:
						var parts = [];
						
						for (var p in item)
						{
							if (p != ID_PROPERTY)
							{
								var elem = getWrappedItem(item[p]);
								var k = '"' + p + '":';
								
								if (elem !== null && elem !== undefined)
								{
									if (elem.hasOwnProperty(ID_PROPERTY))
									{
										parts.push(k + '"~' + elem[ID_PROPERTY] + '~"');
									}
									else
									{
										parts.push(k + JSONify(elem));
									}
								}
								else
								{
									parts.push(k + Serialization.NO_RESULT);
								}
							}
						}
						
						references.push("{" + parts.join(",") + "}");
						break;
						
					default:
						break;
				}
			}
			
			result = "[" + references.join(",") + "]";
		}
	}

	return result;
};

Jaxer.Serialization = Serialization;

if (Jaxer.isOnServer)
{
	frameworkGlobal.Serialization = Jaxer.Serialization;
}

})();

/*
 * fragment : Comm > XHR.js
 */

(function() {

// This is a client-side, compressible module -- be sure to end each function declaration with a semicolon

var log = Jaxer.Log.forModule("XHR");

/**
 * @namespace {Jaxer.XHR} Namespace to hold the Jaxer client-side cross-browser
 * wrapper around XMLHttpRequest.
 */
var XHR = {};

/**
 * The value of the "reason" property that indicates a timeout has occurred.
 * This property is set on the Error object that's thrown by XHR.send() during
 * synchronous requests that don't use the onsuccess function but rather just
 * return a response or throw an Error.
 * 
 * @advanced
 * @alias Jaxer.XHR.REASON_TIMEOUT
 * @property {String}
 */
XHR.REASON_TIMEOUT = "timeout";

/**
 * The value of the "reason" property that indicates a communication failure has
 * occurred. This property is set on the Error object that's thrown by
 * XHR.send() during synchronous requests that don't use the onsuccess function
 * but rather just return a response or throw an Error.
 * 
 * @advanced
 * @alias Jaxer.XHR.REASON_FAILURE
 * @property {String}
 */
XHR.REASON_FAILURE = "failure";

XHR.asyncRequests = {}; // Holds identifier codes for the pending async requests so you can call XHR.cancel() on them

/**
 * The default client-side function used to handle any errors that occur during
 * XMLHttpRequest processing by throwing an error describing them
 * 
 * @advanced
 * @alias Jaxer.XHR.onfailure
 * @param {Object} error
 * 		An error object describing the error, if one was thrown. Otherwise this
 * 		is null.
 * @param {Object} extra
 * 		Any extra information passed into Jaxer.XHR.send(), e.g. to make error
 * 		messages more informative.
 * @param {XMLHttpRequest} xhr
 * 		The XMLHttpRequest object that contains the information received from
 * 		the server,	e.g. in xhr.status and xhr.responseText. It may be null if
 * 		an error was encountered creating it.
 */
XHR.onfailure = function onfailure(error, extra, xhr)
{
	if (xhr) 
	{
		var status;
		try
		{
			status = xhr.status;
		}
		catch (e) {}
		throw new Error("XMLHttpRequest: Received status " + String(xhr.status) + " from the server\n" +
			"Response from server: " + xhr.responseText);
	}
	else if (error)
	{
		throw error;
	}
};

/**
 * The default client-side function used to handle any timeout errors that occur
 * during XMLHttpRequest processing by throwing an error describing them
 * 
 * @advanced
 * @alias Jaxer.XHR.ontimeout
 * @param {Number} timeout
 * 		The timeout (in milliseconds) used in this request
 * @param {Object} extra
 * 		Any extra information passed into Jaxer.XHR.send(), e.g. to make error
 * 		messages more informative.
 * @param {XMLHttpRequest} xhr
 * 		The XMLHttpRequest object that contains the information received from
 * 		the server, e.g. in xhr.status and xhr.responseText. It may be null if
 * 		an error was encountered creating it.
 */
XHR.ontimeout = function ontimeout(timeout, extra, xhr)
{
	throw new Error("XMLHttpRequest: Request timed out after " + (timeout/1000) + " seconds");
};

/**
 * Returns an XMLHttpRequest object by calling the platform-specific API for it.
 * On the server side of Jaxer, the XPCOM version of XMLHttpRequest is used.
 * 
 * @advanced
 * @alias Jaxer.XHR.getTransport
 * @return {XMLHttpRequest}
 */
XHR.getTransport = function getTransport()
{
	var xhr, e;
	try
	{
		// On IE use the most common/standard ActiveX call to minimize version bugs/weirdnesses
		xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
	}
	catch (e) {} // May be needed on older versions of IE to prevent "automation error" notifications
	if (!xhr)
	{
		throw new Error("Could not create XMLHttpRequest in the browser" + (e ? '\n' + e : ''));
	}
	return xhr;
};

/**
 * The default function used to test whether the XMLHttpRequest got a successful
 * response or not, in particular using xhr.status, location.protocol and some
 * browser sniffing.
 * 
 * @advanced
 * @alias Jaxer.XHR.testSuccess
 * @param {XMLHttpRequest} xhr
 * 		The XMLHttpRequest object that got the response
 * @return {Boolean}
 * 		true if successful, false otherwise
 */
XHR.testSuccess = function testSuccess(xhr)
{
	var success = false;
	try
	{
		success = 
			(!xhr.status && location.protocol == "file:") ||
			(xhr.status >= 200 && xhr.status < 300) || 
			(xhr.status == 304) ||
			(userAgent.match(/webkit/) && xhr.status == undefined);
	}
	catch(e) {}
	return success;
};

/**
 * The generic function used to send requests via XMLHttpRequest objects. Each
 * request gets its own XMLHttpRequest object, and async requests hold onto that
 * object until they're finished or timed out or canceled. On the server side of
 * Jaxer, only synchronous requests are supported.
 * <br><br>
 * For async requests, this returns a key that can be used to abort the request
 * via Jaxer.XHR.cancel().
 * <br><br>
 * For synchronous requests, returns the response of the server or throws an
 * exception if an error occurred, unless an onsuccess function was specified in
 * the options, in which case it passes the response to that function and also
 * handles any errors through the onfailure function if specified in the
 * options.
 * <br><br>
 * In any case, the response can be a text string or an XML DOM. To force one or
 * the other, set the "as" property on the options argument, e.g. if as="text"
 * it will definitely be a text string, and if as="xml" it will definitely be an
 * XML DOM.
 * 
 * @alias Jaxer.XHR.send
 * @param {String} message
 * 		The message to send, usually as a query string
 * 		("name1=value&name2=value2...")
 * @param {Jaxer.XHR.SendOptions} options
 * 		A JavaScript object (hashmap) of name: value property pairs specifying
 * 		how to handle this request.
 * @param {Object} extra
 * 		Any extra information that might be useful e.g. in the error handlers on
 * 		this request. This object is simply passed on to them if/when they're
 * 		called. E.g. Jaxer.Callback uses this information to pass the name of
 * 		the function being called remotely, so error messages can be more
 * 		informative.
 * @return {Object}
 * 		For async requests, a key to the XHR object; for synchronous requests
 * 		(with no onsuccess handler in the options), a text string or XML DOM.
 */
XHR.send = function send(message, options, extra)
{
	if (typeof message != "string")
	{
		if ((message == null) || (message == undefined) || (typeof message.toString != "function"))
		{
			message = '';
		}
		else
		{
			message = message.toString();
		}
	}
	options = options || {};
	var as = options.as || XHR.defaults.as || '';
	as = as.toLowerCase();
	var url = options.url || XHR.defaults.url || Jaxer.CALLBACK_URI;
	url = url.replace(/#.*$/, ''); // strip off any fragment
	var cacheBuster = (typeof options.cacheBuster == "undefined") ? XHR.defaults.cacheBuster : options.cacheBuster;
	if (cacheBuster) url += (url.match(/\?/) ? "&" : "?") + "_rnd" + ((new Date()).getTime()) + "=" + Math.random();
	var method = String(options.method || XHR.defaults.method || "GET").toUpperCase();
	if ((method == "GET") && (message != ''))
	{
		url += (url.match(/\?/) ? "&" : "?") + message;
		message = ''; // prevent submitting this twice in IE
	}
	var async = (typeof options.async == "undefined") ? XHR.defaults.async : options.async;
	var username = options.username || XHR.defaults.username || null;
	var password = options.password || XHR.defaults.password || null;
	var onsuccess = options.onsuccess || XHR.defaults.onsuccess;
	var onfailure = options.onfailure || XHR.defaults.onfailure;
	var timeout = options.timeout || XHR.defaults.timeout || 0;
	var ontimeout = timeout ? (options.ontimeout || XHR.defaults.ontimeout) : null;
	var headers = options.headers || XHR.defaults.headers;
	var overrideMimeType = as ? (as == 'xml' ? 'text/xml' : 'text/plain') : options.overrideMimeType || XHR.defaults.overrideMimeType || null;
	var onreadytosend = options.onreadytosend || XHR.defaults.onreadytosend;
	var onfinished = options.onfinished || XHR.defaults.onfinished;
	var contentType = options.contentType || XHR.defaults.contentType;
	var testSuccess = options.testSuccess || XHR.defaults.testSuccess;
	if (typeof testSuccess != "function") testSuccess = XHR.testSuccess;
	var responseType = as ? (as == 'xml' ? 'xml' : 'text') : options.responseType || XHR.defaults.responseType || 'text';
	var pollingPeriod = options.pollingPeriod || XHR.defaults.pollingPeriod || 11;
	var getTransport = options.getTransport || XHR.defaults.getTransport;
	if (typeof getTransport != "function") getTransport = XHR.getTransport;

	var useOnFunctions = (typeof onsuccess == "function"); // otherwise, return a value or throw an error
	var error = null;

	var xhr = getTransport();
	
	// Open the transport:
	try
	{
		xhr.open(method, url, async, username, password);
	}
	catch (e)
	{
		error = new Error("xhr.open error: " + e + "\n\n" + "typeof xhr: " + (typeof xhr) + "\n\nparams: " + [method, url, async]);
		if (useOnFunctions) 
		{
			if (typeof onfailure == "function") onfailure(error, extra, xhr);
			return;
		}
		else 
		{
			throw error;
		}
	}
	
	// Get ready to send:
	if (headers) 
	{
		for (var iHeader=0, lenHeaders=headers.length; iHeader<lenHeaders; iHeader++)
		{
			xhr.setRequestHeader(headers[iHeader][0], headers[iHeader][1]);
		}
	}
	else 
	{
		if ((message != '') && (contentType != '')) xhr.setRequestHeader("Content-Type", contentType);
		xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
	}
	if (overrideMimeType && (typeof xhr.overrideMimeType == "function"))
	{
		xhr.overrideMimeType(overrideMimeType);
	}
	if (typeof onreadytosend == "function") onreadytosend(xhr, extra);
	
	// Get ready to receive: (modeled after jQuery)
	var finished = false;
	var pollId, asyncKey;
	var finish = function finish(cancelCode)
	{
		var response;
		if (!finished && xhr && ((xhr.readyState == 4) || (cancelCode == "timedout") || (cancelCode == "canceled")))
		{
			// prevent firing more than once
			finished = true; 

			// stop polling
			if (pollId)
			{
				asyncKey = 'id_' + pollId;
				clearInterval(pollId);
				delete XHR.asyncRequests[asyncKey];
			}
			
			var err = null;
			var msgIdentifier = (async ? "Asynchronous" : "Synchronous") + " request " +
				(asyncKey ? asyncKey + " " : "") + "to url " + url;
			
			if (cancelCode == "timedout")
			{
				log.trace(msgIdentifier + " timed out");
				err = new Error(msgIdentifier + " timed out");
				err.reason = XHR.REASON_TIMEOUT;
				err.timeout = timeout;
				if (useOnFunctions && (typeof ontimeout == "function")) 
				{
					ontimeout(err, extra, xhr);
				}
			}
			else if (cancelCode == "canceled")
			{
				log.trace(msgIdentifier + " canceled");
			}
			else
			{
				if (testSuccess(xhr)) // succeeded
				{
					var useXml;
					switch (responseType)
					{
						case "xml":
							useXml = true;
							break;
						case "auto":
							var autoType = "text";
							try 
							{
								autoType = xhr.getResponseHeader("content-type");
							} 
							catch (e) {}
							useXml = Boolean(autoType.match(/xml/i));
							break;
						default: // "text" and anything else
							useXml = false;
					}
					response = useXml ? xhr.responseXML : xhr.responseText;
					var responseAsText = useXml ? xhr.responseText : response;
					log.trace(msgIdentifier + " received " + (useXml ? "XML" : "text") + " response: " +
						responseAsText.substr(0, 100) + (responseAsText.length > 100 ? '...' : ''));
					if (useOnFunctions) 
					{
						onsuccess(response, extra);
					}
				}
				else  // failed
				{
					log.trace(msgIdentifier + " failed");
					err = new Error(msgIdentifier + " failed");
					err.reason = XHR.REASON_FAILURE;
					err.status = xhr.status;
					if (useOnFunctions && (typeof onfailure == "function")) 
					{
						onfailure(err, extra, xhr);
					}
				}
			}
			
			// finish and clean up
			if (typeof onfinished == "function") onfinished(xhr, cancelCode, extra);
			pollId = asyncKey = xhr = url = undefined;
			
			if (!useOnFunctions && err) 
			{
				throw err;
			}
		}
		
		if (!useOnFunctions) return response;
	};

	// For async requests, look for received data, and timeout if requested and needed
	if (async)
	{
		// Poll for receiving data, instead of subscribing to onreadystatechange - modeled after jQuery
		pollId = setInterval(finish, pollingPeriod);
		asyncKey = 'id_' + pollId;
		XHR.asyncRequests[asyncKey] = {url: url, message: message, timeout: timeout, timestamp: new Date(), finish: finish};
		
		if (timeout)
		{
			setTimeout(function xhrTimeout()
				{
					if (!xhr) return; 					// we've already finished
					xhr.abort();						// otherwise, abort
					if (!finished) finish("timedout");	// and finish with a timeout if we haven't already finished
				}, timeout);
		}
	}
	
	// Now actually send the request
	log.trace("Sending " + (async ? "asynchronous " : "synchronous ") + method +
		" request to url " + url + " with " + (message == '' ? "no data" : "data: " + message));
	xhr.send((message == '') ? null : message);
	log.trace("Sent");
	
	if (async) // For async requests, return an id by which requests may be aborted or prematurely timed out
	{
		log.trace("Response will be received asynchronously with key: " + asyncKey);
		return asyncKey;
	}
	else // And for synchronous requests, force the receiving. Timeout is not possible.
	{
		var response = finish(false);
		if (!useOnFunctions)
		{
			return response;
		}
	}
	
};

/**
 * Cancels the pending async XMLHttpRequest if its response has not yet been
 * received and if it has not yet timed out.
 * 
 * @alias Jaxer.XHR.cancel
 * @param {Number} asyncKey
 * 		The key that Jaxer.XHR.send() returned when the request was created.
 * @return {Boolean}
 * 		true if the request was found and canceled, false if it was not found 
 * 		(i.e. was not in the pending queue)
 */
XHR.cancel = function cancel(asyncKey)
{
	if (typeof XHR.asyncRequests[asyncKey] != "undefined")
	{
		log.trace("Canceling request " + asyncKey);
		XHR.asyncRequests[asyncKey].finish("canceled");
		return true;
	}
	else
	{
		return false; // nothing to cancel
	}
};

/**
 * @classDescription {Jaxer.XHR.SendOptions} Options used to define the behavior
 * of XHR.send.
 */

/**
 * Options used to define the behavior of XHR.send. Create a new SendOptions()
 * to get the default options, then modify its properties as needed before
 * passing it to XHR.send.
 * 
 * @constructor
 * @alias Jaxer.XHR.SendOptions
 * @return {Jaxer.XHR.SendOptions}
 * 		Returns an instance of SendOptions.
 */
XHR.SendOptions = function SendOptions()
{
	/**
	 * The URL to which the XMLHttpRequest is to be sent. On the client side,
	 * defaults to Jaxer.CALLBACK_URI which is used to handle function callbacks
	 * from client-side proxies to their server-side counterparts.
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.url
	 * @property {String}
	 */
	this.url = Jaxer.CALLBACK_URI;
	
	/**
	 * If true (default), a random name and value query pair will be appended to
	 * the URL on each call
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.cacheBuster
	 * @property {Boolean}
	 */
	this.cacheBuster = true;
	
	/**
	 * Should be "GET" or "POST" (default)
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.method
	 * @property {String}
	 */
	this.method = "POST";
	
	/**
	 * Set to true for asynchronous (default), false for synchronous. Ignored
	 * for server-side requests, which are always synchronous.
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.async
	 * @property {Boolean}
	 */
	this.async = true;
	
	/**
	 * If the target URL requires authentication, specify this username, 
	 * otherwise leave this as null.
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.username
	 * @property {String}
	 */
	this.username = null;
	
	/**
	 * If the target URL requires authentication, specify this password, 
	 * otherwise leave this as null.
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.password
	 * @property {String}
	 */
	this.password = null;
	
	/**
	 * Set to a function to call if successful. Its arguments are the response
	 * received back from the server, and any "extra" information passed in when
	 * calling send(). For synchronous calls, you can optionally set onsuccess
	 * to null to have XHR.send() return a value directly (and throw errors on
	 * failure/timeout).
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.onsuccess
	 */
	this.onsuccess = null;
	
	/**
	 * Set to a custom callback function to call if unsuccessful (by default set
	 * to Jaxer.XHR.onfailure client-side). Its arguments are the error
	 * encountered, the "extra" information from the caller, and the XHR
	 * instance.
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.onfailure
	 */
	this.onfailure = XHR.onfailure;
	
	/**
	 * For async (client-side) requests, set to number of milliseconds before
	 * timing out, or 0 (default) to wait indefinitely
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.timeout
	 * @property {Number}
	 */
	this.timeout = 0;
	
	/**
	 * Set to a custom timeout function to call if timeout is used and the async
	 * request has timed out. Its arguments are the timeout error encountered, 
	 * the "extra" information from the caller, and the XHR instance.
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.ontimeout
	 */
	this.ontimeout = null;
	
	/**
	 * Set to null to use default headers; set to an array of [name, value]
	 * arrays to use custom headers instead
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.headers
	 * @property {Array}
	 */
	this.headers = null;
	
	/**
	 * Set to "text" to force interpreting the response as text regardless o
	 * mimetype. Set to "xml" to force interpreting the response as XML
	 * regardless of mimetype and returning the XML as an XML (DOM) object via
	 * XMLHttpRequest.responseXML. Set to null to not force anything - see
	 * overrideMimeType and responseType for finer control.
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.as
	 * @property {String}
	 */
	this.as = null;
	
	/**
	 * Set to null to use whatever mimetype the server sends in the response;
	 * set to a mimetype string (e.g. "text/plain") to force the response to be
	 * interpreted using the given mimetype
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.overrideMimeType
	 * @property {String}
	 */
	this.overrideMimeType = null;
	
	/**
	 * set to a custom function to call just before sending (e.g. to set custom
	 * headers, mimetype, keep reference to xhr object, etc.)
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.onreadytosend
	 */
	this.onreadytosend = null;
	
	/**
	 * set to a custom function to call when done receiving (or timed out),
	 * usually to abort()
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.onfinished
	 */
	this.onfinished = null;
	
	/**
	 * The content type of the request being sent (by default
	 * "application/x-www-form-urlencoded")
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.contentType
	 * @property {String}
	 */
	this.contentType = "application/x-www-form-urlencoded";
	
	/**
	 * Set to a custom function that receives the XMLHttpRequest (after
	 * readyState == 4) and tests whether it succeeded (by default
	 * Jaxer.XHR.testSuccess)
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.testSuccess
	 */
	this.testSuccess = XHR.testSuccess;
	
	/**
	 * Set to "text" (default) to use the responseText, to "xml" to use the
	 * responseXML, or "auto" to use the response's content-type to choose
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.responseText
	 * @property {String}
	 */
	this.responseType = "text";
	
	/**
	 * For async (client-side) requests, the number of milliseconds between
	 * polling for onreadystatechange, by default 11
	 *
	 * @advanced
	 * @alias Jaxer.XHR.SendOptions.prototype.pollingPeriod
	 * @property {Number}
	 */
	this.pollingPeriod = 11;
	
	/**
	 * The function to use to create the XMLHttpRequest, by default
	 * XHR.getTransport
	 *
	 * @alias Jaxer.XHR.SendOptions.prototype.getTransport
	 */
	this.getTransport = XHR.getTransport;
};

/**
 * The default SendOptions which new calls to Jaxer.XHR.send(message, options,
 * extra) will use, unless overridden by the options argument. This is slightly
 * different for client-side and server-side requests (e.g. server-side requests
 * are always synchronous).
 * 
 * @alias Jaxer.XHR.defaults
 * @property {Jaxer.XHR.SendOptions}
 */
XHR.defaults = new XHR.SendOptions();

Jaxer.XHR = XHR;

})();

/*
 * fragment : Comm > Callback.js
 */

(function(){
	
// This is a client-side, compressible module -- be sure to end each function declaration with a semicolon

var log = Jaxer.Log.forModule("Callback");
	
// private constants
var EXCEPTION = "exception";
var ID = "id";
var METHOD_NAME = "methodName";
var NAME = "name";
var MESSAGE = "message";
var PARAMS = "params";
var RETURN_VALUE = "returnValue";
var UID = "uid";

// create placeholder for Callback

/**
 * @namespace {Jaxer.Callback} Callback namespace for remote functions.
 */
var Callback = {};

/**
 * The default HTTP method to use for callback function requests. Initially set
 * to "POST".
 * 
 * @alias Jaxer.Callback.METHOD
 * @property {String}
 */
Callback.METHOD = "POST";

/**
 * The default number of milliseconds to wait before timing out an async
 * callback function request. Initially set to 10 * 1000 (10 seconds).
 * 
 * @alias Jaxer.Callback.TIMEOUT
 * @property {Number}
 */
Callback.TIMEOUT = 10 * 1000;

/**
 * The default polling interval used to see whether the XMLHttpRequest for an
 * async callback function call has returned. Initially set to 11.
 * 
 * @advanced
 * @alias Jaxer.Callback.POLLING_PERIOD
 * @property {Number}
 */
Callback.POLLING_PERIOD = 11;

/**
 * Creates a query string for calling a remote function with the given arguments
 * 
 * @alias Jaxer.Callback.createQuery 
 * @param {String} functionName
 * 		The name of the remote function
 * @param {Object} args
 * 		The arguments of the remote function. This can be a single (primitive)
 * 		object or an array of (primitive) objects
 * @param {Number} [initialNumberToSkip]
 * 		Optionally, how many of the arguments (counting from the beginning) to
 * 		not pass to the remote function
 * @return {String}
 * 		The query string
 */
Callback.createQuery = function createQuery(functionName, args, initialNumberToSkip)
{
	// move Arguments into a real Javascript Array
	var argsArray = [];
	initialNumberToSkip = initialNumberToSkip || 0;
	
	for (var i=initialNumberToSkip; i<args.length; i++)
	{
		argsArray.push(args[i]);
	}
	
	var queryParts = Callback.getQueryParts(functionName, argsArray);
	return Callback.hashToQuery(queryParts);
};

/**
 * Converts a javascript object (hash) into a http query string.
 * 
 * @alias Jaxer.Callback.hashToQuery
 * @param {Object} hash
 * 		Hash of name value pairs to be converted.
 * @return {String}
 * 		The query string
 */
Callback.hashToQuery = function hashToQuery(hash)
{
	var queryStrings = [];
	
	for (var p in hash)
	{
		var name = Callback.formUrlEncode(p);
		var value = ((typeof hash[p] == "undefined") || (hash[p] == null) || (typeof hash[p].toString != "function")) ? "" : Callback.formUrlEncode(hash[p].toString());
		
		queryStrings.push([name, value].join("="));
	}
	
	return queryStrings.join("&");
};

/**
 * URL Encode a query string.
 * 
 * @alias Jaxer.Callback.formUrlEncode
 * @param {String} str
 * 		Query string to be converted.
 * @return {String}
 * 		A URL-encoding string
 */
Callback.formUrlEncode = function formUrlEncode(str)
{
	return encodeURIComponent(str);
};

/**
 * Transforms the raw result data from the XHR call into the expected data
 * format.
 * 
 * @advanced
 * @alias Jaxer.Callback.processReturnValue
 * @param {String} functionName
 * 		The name of the function that was called
 * @param {String} rawResult
 * 		The raw (text) data returned from the XHR call
 * @return {Object}
 * 		The returned data in the format the remote function returned it
 */
Callback.processReturnValue = function processReturnValue(functionName, rawResult)
{
	log.trace("Received for function " + functionName + ": rawResult = " + rawResult.substr(0, 100) + (rawResult.length > 100 ? '...' : ''));
	try
	{
		var content = Jaxer.Serialization.fromJSONString(rawResult);
	}
	catch (e)
	{
		var desc, showError, likelyServerError;
		if (e.name == Jaxer.Serialization.JSONSyntaxErrorName) 
		{
			desc = "Received a response with an unexpected (non-JSON) syntax";
			showError = false;
			likelyServerError = true;
		}
		else if (e.name == Jaxer.Serialization.JSONEvalErrorName)
		{
			desc = "Error when evaluating the JSON-like response received from the server";
			showError = true;
			likelyServerError = true;
		}
		else
		{
			desc = "Error when processing the JSON response received from the server";
			showError = true;
			likelyServerError = false;
		}
		e.message = 
			desc + " " +
			"while calling server function '" + functionName + "'.\n\n" +
			(showError ? "Error: " + e + "\n\n" : "") +
			"Response:\n" + rawResult.substr(0, 200) + ((rawResult.length > 200) ? "..." : "") +
			(likelyServerError ? "\n\n(Perhaps an error occured on the server?)" : "");
		if (Jaxer.ALERT_CALLBACK_ERRORS)
		{
			alert(e.message);
		}
		throw e;
	}
	
	var result = null;

	if (content !== null)
	{
		if (content.hasOwnProperty(EXCEPTION))
		{
			var eFromServer = (content[EXCEPTION] == null) ? "Unspecified server error" : content[EXCEPTION];
			var eToClient;
			
			if (eFromServer.hasOwnProperty(NAME))
			{
				var eName = eFromServer[NAME];
				
				try
				{
					eToClient = new window[eName];
								
					for (var p in eFromServer)
					{
						eToClient[p] = eFromServer[p];
					}
				}
				catch(e)
				{
					eToClient = eFromServer;
				}
			}
			else
			{
				eToClient = eFromServer;
			}
			
			if (typeof eToClient.toString != "function")
			{
				eToClient.toString = function toString()
				{
					var name = eToClient.hasOwnProperty(NAME) ? eToClient[NAME] : "server error";
					var message = eToClient.hasOwnProperty(MESSAGE) ? eToClient[MESSAGE] : "(unspecified)";
					return [name, message].join(": ");
				}
			}
			
			if (Jaxer.ALERT_CALLBACK_ERRORS)
			{
				alert("The server function '" + functionName + "' returned an error: " + 
					((typeof eToClient.message == "undefined") ? eToClient.toString() : eToClient.message));
			}
			
			throw eToClient;
		}
		else if (content.hasOwnProperty(RETURN_VALUE))
		{
			result = content[RETURN_VALUE];
		}
		else
		{
			result = undefined;
		}
	}
	
	return result;
};

/**
 * The default method used to handle errors when calling remote functions
 * asynchronously. It alerts the error message if Jaxer.ALERT_CALLBACK_ERRORS is
 * true, and in any case throws an error
 * 
 * @advanced
 * @alias Jaxer.Callback.onfailureAsync
 * @param {Object} error
 * 		If an error was thrown during the request, it would be here.
 * @param {Object} extra
 * 		Any extra information passed in during the call to Jaxer.XHR.send() to
 * 		help identify the request. Currently, there is one String-valued
 * 		property on this object: functionName. 
 * @param {XMLHttpRequest} xhr
 * 		The XMLHttpRequest object that encountered the error. This might be
 * 		null, if an error was encountered in creating the XMLHttpRequest.
 */
Callback.onfailureAsync = function onfailure(error, extra, xhr)
{
	var message = "Error while contacting server to (asynchronously) call server function '" + extra.functionName + "':\n";
	if (xhr) 
	{
		var status;
		try
		{
			status = xhr.status;
		}
		catch (e) {}
		message += "Received status " + String(xhr.status) + " from the server\n" +
			"Response from server: " + xhr.responseText;
	}
	else if (error)
	{
		message += error;
	}
	if (Jaxer.ALERT_CALLBACK_ERRORS)
	{
		alert(message);
	}
	throw new Error(message);
};

/**
 * The default method used to handle timeouts when calling remote functions
 * asynchronously. It alerts the error message if Jaxer.ALERT_CALLBACK_ERRORS is
 * true, and in any case throws an error
 * 
 * @advanced
 * @alias Jaxer.Callback.ontimeoutAsync
 * @param {Error} error
 * 		The timeout error object encountered, having a "timeout" property with
 * 		its value indicating the timeout (in milliseconds) used in this request.
 * @param {Object} extra
 * 		Any extra information passed in during the call to Jaxer.XHR.send() to
 * 		help identify the request. Currently, there is one String-valued
 * 		property on this object: functionName. 
 * @param {XMLHttpRequest} xhr
 * 		The XMLHttpRequest object that encountered the error.
 */
Callback.ontimeoutAsync = function ontimeout(error, extra, xhr)
{
	var message = "Error while contacting server to (asynchronously) call server function '" + extra.functionName + "':\n";
	message += "Request timed out after " + (error.timeout/1000) + " seconds";
	if (Jaxer.ALERT_CALLBACK_ERRORS)
	{
		alert(message);
	}
	throw new Error(message);
};

/**
 * This method invokes an asynchronous call to a proxied javascript function on
 * the server from the client side javascript. A callback function needs to be
 * provided and is called once the XHR request completes or times out.
 * 
 * @alias Jaxer.Callback.invokeFunctionAsync
 * @param {Object} callback
 * 		If this is a function, this is the function to call upon a successful
 * 		return from the remote invocation. Its arguments are what the remote
 * 		function on the server returned.
 * 		<br><br>
 * 		If this is an array, its elements are as follows (each may be null):
 * 		<ol>
 * 			<li>
 * 				the callback function;
 *			</li>
 * 			<li>
 * 				a function to call on an error, with arguments being the error,
 * 				the "extra" information object that has the functionName as its
 * 				one property, and the XMLHttpRequest object used for the call if
 * 				the call itself encountered an error;
 * 			</li>
 * 			<li>
 * 				the timeout to use, in milliseconds (defaults to
 * 				Jaxer.Callback.TIMEOUT). Use 0 to wait indefinitely.</li>
 * 		</ol> 
 * 		<br><br>
 * 		If this is an object, its "callback", "errorHandler", and timeout
 * 		properties will be used, if any.
 * @param {String} functionName
 * 		The name of the remote function
 * @param {Object} args
 * 		A single argument, or an array of arguments, to be passed to the remote
 * 		function on the server
 */
Callback.invokeFunctionAsync = function invokeFunctionAsync(callback, functionName, args)
{
	var message = Callback.createQuery(functionName, args, 1); // skip encoding the callback itself
	log.trace("Invoking function " + functionName + " asynchronously with arguments encoded as: " + message);
	
	var extra = {functionName: functionName}; // this will be passed back to error handling methods
	
	var callbackFunction, errorHandler, timeout;
	if (typeof callback == "function")
	{
		callbackFunction = callback;
	}
	else if (typeof callback == "object")
	{
		if (typeof callback.length == "number") // assume it's array-like
		{
			callbackFunction = (callback.length > 0 && typeof callback[0] == "function") ? callback[0] : undefined;
			if (callback.length > 1 && typeof callback[1] == "function") errorHandler = callback[1];
			if (callback.length > 2 && typeof callback[2] == "number") timeout = callback[2];
		}
		else
		{
			callbackFunction = (typeof callback["callback"] == "function") ? callback["callback"] : undefined;
			if (typeof callback["errorHandler"] == "function") errorHandler = callback["errorHandler"];
			if (typeof callback["timeout"] == "number") timeout = callback["timeout"];
		}
	}
	
	var onsuccess = function onsuccess(rawResult, extra)
	{
		try
		{
			var processedResult = Callback.processReturnValue(functionName, rawResult);
			if (callbackFunction)
			{
				callbackFunction(processedResult);
			}
		}
		catch (e) // to do something meaningful with async exceptions, you'll need an errorHandler
		{
			if (errorHandler)
			{
				errorHandler(e, extra);
			}
		}
	};

	var options = 
	{
		url: Jaxer.CALLBACK_URI,
		cacheBuster: false,
		method: Callback.METHOD,
		async: true,
		onsuccess: onsuccess,
		onfailure: errorHandler || Callback.onfailureAsync,
		timeout: (typeof timeout == "number") ? timeout : Callback.TIMEOUT,
		ontimeout: errorHandler || Callback.ontimeoutAsync,
		headers: null,
		onreadytosend: null,
		onfinished: null,
		contentType: "application/x-www-form-urlencoded",
		testSuccess: Jaxer.XHR.testSuccess,
		as: "text",
		pollingPeriod: Callback.POLLING_PERIOD
	};

	var pollId = Jaxer.XHR.send(message, options, extra);
	
	return pollId;
	
};

/**
 * This method invokes a synchronous call to a proxied JavaScript function on
 * the server from the client side javascript.
 * 
 * @alias Jaxer.Callback.invokeFunction
 * @param {String} functionName
 * 		The name of the remote function to call on the server
 * @param {Object} args
 * 		A single argument, or an array of arguments, to be passed to the remote
 * 		function on the server
 * @return {Object}
 * 		The value returned by the remote function on the server
 */
Callback.invokeFunction = function invokeFunction(functionName, args)
{
	var message = Callback.createQuery(functionName, args);
	log.trace("Invoking function " + functionName + " synchronously with arguments encoded as: " + message);
	
	var extra = {functionName: functionName}; // this will be passed back to error handling methods
	
	var options = 
	{
		url: Jaxer.CALLBACK_URI,
		cacheBuster: false,
		method: Callback.METHOD,
		async: false,
		onsuccess: null, // we'll use the return value of XHR.send() and any errors it throws
		onfailure: null,
		timeout: Callback.TIMEOUT,
		ontimeout: null,
		headers: null,
		onreadytosend: null,
		onfinished: null,
		contentType: "application/x-www-form-urlencoded",
		testSuccess: Jaxer.XHR.testSuccess,
		as: "text",
		pollingPeriod: Callback.POLLING_PERIOD
	};
	
	try
	{
		var response = Jaxer.XHR.send(message, options, extra);
	}
	catch (e)
	{
		if (Jaxer.ALERT_CALLBACK_ERRORS)
		{
			alert("Error while contacting server to call server function '" + functionName + "': " + e);
		}
		throw e;
	}
	
	return Callback.processReturnValue(functionName, response);
};

/**
 * Returns the URL that can be used as a GET request to call a JavaScript
 * function on the server. 
 * <br><br>
 * The server listens for two special properties: "resultAs" and "paramsAs". 
 * <br><br>
 * If present, resultAs specifies how the result of functionToCall is to be
 * returned to the client. Valid values for resultAs are "text", "object", and
 * "wrappedObject" (default), which return the result of the callback as a
 * single string, JSON object literal, or JSON object literal with metadata,
 * respectively. 
 * <br><br>
 * If present, "paramsAs" specifies how the request is to be translated into
 * arguments for the functionToCall. Valid values for "paramsAs" are "text",
 * "object", and "default", which hands the GET or POST data to functionToCall
 * as a single string, a single hash (object literal) of name-value pairs, or as
 * regular JavaScript arguments with values extracted from paramsToPass,
 * respectively.
 * 
 * @alias Jaxer.Callback.getUrl
 * @param {Object}	functionToCall 
 * 		Name of the function to call server-side
 * @param {Object}	paramsToPass 
 * 		An array of parameters (or the single parameter) to pass to the function
 * @param {String, Object} ...
 * 		Optional parameter(s) to append to the end of the URL as part of the
 * 		query string. Strings will be appended to the end of the URL separated
 * 		by a "&". Hashes will be appended as &name1=value&name2=value2...
 * @return {String}
 * 		The URL that can be called (via a GET) to invoke the function
 */
Callback.getUrl = function getUrl(functionToCall, paramsToPass)
{
	var queryParts = Callback.getQueryParts(functionToCall, paramsToPass);
	var ary = [];
	
	for (var p in queryParts)
	{
		ary.push([p, queryParts[p]].join("="));
	}
	
	return (Callback.getBaseUrl() + "?" + ary.join("&"));
};

/**
 * Returns the URL for use in callbacks, without any parameters
 * 
 * @advanced
 * @alias Jaxer.Callback.getBaseUrl
 * @return {String}
 * 		The URL to GET or POST to
 */
Callback.getBaseUrl = function getBaseUrl()
{
	return Jaxer.CALLBACK_URI;
};

/**
 * Returns a hash of the "form-like" name-value pairs needed to call a
 * JavaScript function on the server. These can be submitted to the server as a
 * GET request (but see Callback.getUrl which wraps this in a Url for you) or as
 * a POST request, and usually via an XMLHttpRequest mechanism.
 * <br><br>
 * The server listens for two special name-value pairs: "resultAs" and
 * "paramsAs". 
 * <br><br>
 * If present, resultAs specifies how the result of functionToCall is to be
 * returned to the client. Valid values for resultAs are "text", "object", and
 * "wrappedObject" (default), which return the result of the callback as a
 * single string, a JSON object literal, or a JSON object literal with metadata,
 * respectively. 
 * <br><br>
 * If present, "paramsAs" specifies how the request is to be translated into
 * arguments for the functionToCall. Valid values for "paramsAs" are "text",
 * "object", and "default", which hands the GET or POST data to functionToCall
 * as a single string, a single hash (object literal) of name-value pairs, or as
 * regular JavaScript arguments with values extracted from paramsToPass,
 * respectively.
 * 
 * @alias Jaxer.Callback.getQueryParts
 * @param {Object} functionToCall
 * 		Name of the function to be called server-side
 * @param {Object} paramsToPass
 * 		An array of parameters (or the single parameter) to pass to the function
 * @param {String, Object} ...
 * 		Optional parameter(s) to append to the end of the URL as part of the
 * 		query string. String arguments should be "name=value" pairs joined by
 * 		"&" characters. If arguments are a hash, their properties are added to
 * 		the hash.
 * @return {String}
 * 		The URL that can be called (via a GET) to invoke the function
 */
Callback.getQueryParts = function getQueryParts(functionToCall, paramsToPass)
{
	var parts = {};
	// First normalize all the input arguments
	var functionName = (typeof functionToCall == "function") ? functionToCall.name : functionToCall; // Allows passing in a function or its name
	if (paramsToPass == null) paramsToPass = [];
	if (paramsToPass.constructor != Array) // Allows passing in a single parameter without wrapping it in an array
	{
		paramsToPass = [paramsToPass];
	}
	// Any remaining arguments should be strings or hashes of options to be appended to the url
	for (var i=2; i<arguments.length; i++)
	{
		var arg = arguments[i];
		if (typeof arg == "string")
		{
			var argParts = arg.split("&");
			for (var j=0; j<argParts.length; j++)
			{
				var argPart = argParts[j].split("=");
				parts[argPart[0]] = (argPart.length > 1) ? argPart[1] : null;
			}
		}
		else
		{
			for (var p in arg)
			{
				parts[p] = arg[p];
			}
		}
	}
	parts[ID] = Jaxer.Callback.id;
	parts[METHOD_NAME] = functionName;
	parts[PARAMS] = Jaxer.Serialization.toJSONString(paramsToPass);
	parts[UID] = "" + new Date().getTime() + "_" + Math.round(Math.random() * 1000000);
	
	return parts;
};

/**
 * A short convenience function to call a remote function, synchronously or
 * asynchronously based on whether or not you specify a callback function as the
 * third argument.
 * 
 * @alias Jaxer.Callback.remote
 * @param {String} functionName
 * 		The name of the remote function to call
 * @param {Object} args
 * 		A single argument, or an array of arguments, to pass to the remote
 * 		function
 * @param {Object} [callback]
 * 		If this is not specified, the call will be synchronous.
 * 		<br>
 * 		If this is specified, the call will be asynchronous.
 * 		<br><br>
 * 		If this is a function, this is the function to call upon a successful
 * 		return from the remote invocation. Its arguments are what the remote
 * 		function on the server returned.
 * 		<br><br>
 * 		If this is an array, its elements are as follows (each may be null):
 * 		<ol>
 * 			<li>
 * 				the callback function;
 * 			</li>
 * 			<li>
 * 				a function to call on an error, with arguments being the error,
 * 				the "extra" information object that has the functionName as its
 * 				one property, and the XMLHttpRequest object used for the call if
 * 				the call itself encountered an error;
 * 			</li>
 * 			<li>
 * 				the timeout to use, in milliseconds (defaults to
 * 				Jaxer.Callback.TIMEOUT). Use 0 to wait indefinitely.
 * 			</li>
 * 		</ol> 
 * 		<br><br>
 * 		If this is an object, its "callback", "errorHandler", and timeout
 * 		properties will be used, if any.
 * @return {Object}
 * 		If synchronous, the value returned by the remote function; if
 * 		asynchronous, an id by which the remote call can be canceled via
 * 		Jaxer.XHR.cancel()
 */
Jaxer.remote = function remote(functionName, args, callback)
{
	if (arguments.length == 3)
	{
		return Callback.invokeFunctionAsync(callback, functionName, args);
	}
	else
	{
		return Callback.invokeFunction(functionName, args);
	}
};

Jaxer.Callback = Callback;

})();

/*
 * fragment : Utilities > Both.js
 */

/*
 * The functions below are used in both the client and the server Jaxer frameworks,
 * to offer common APIs for some frequently-needed tasks that need to be implemented
 * differently on client and server.
 */

/**
 * Used to set events on DOM elements such that they "do the right thing" both
 * client-side and server-side. On the client, this acts as expected, setting a
 * property with the name eventName (e.g. onclick) on the DOM element. On the
 * server, the eventName attribute is set on the DOM element so it can be
 * serialized with the DOM before sending to the client. If the handler is a
 * (server side) function with a name, the attribute's value is handler.name +
 * "()" On the server, 
 * 
 * @alias Jaxer.setEvent
 * @param {Object} domElement
 * 		The element on which to set the event
 * @param {String} eventName
 * 		The name of the event to set
 * @param {Object} handler
 * 		The handler function, or the body (as a string)
 */
Jaxer.setEvent = function setEvent(domElement, eventName, handler)
{
	if (Jaxer.isOnServer)
	{
		var attribute;
		if (typeof handler == "function")
		{
			if (handler.name == "")
			{
				attribute = "(" + handler.toSource() + ")()";
			}
			else
			{
				attribute = handler.name + "()";
			}
		}
		else // handler should be a string (the handler function's body)
		{
			attribute = handler;
		}
		domElement.setAttribute(eventName, attribute);
	}
	else
	{
		var func;
		if (typeof handler == "function")
		{
			func = handler;
		}
		else // handler should be a string (the handler function's body)
		{
			func = new Function(handler);
		}
		domElement[eventName] = func;
	}
};

/**
 * Sets the title of the document and works on either the server or the client.
 * 
 * @alias Jaxer.setTitle
 * @param {String} title
 * 		The text of the title
 */
Jaxer.setTitle = function setTitle(title)
{
	if (Jaxer.isOnServer)
	{
		var doc = Jaxer.pageDocument;
		var titleElement = doc.getElementsByTagName("title")[0];
		if (!titleElement)
		{
			var head = doc.getElementsByTagNames("head")[0];
			if (head)
			{
				titleElement = doc.createElement("title");
				head.appendChild(titleElement);
			}
		}
		if (titleElement)
		{
			titleElement.firstChild.data = title;
		}
	}
	else
	{
		document.title = title;
	}
};
