1 /*
  2  * AjaxTCR Library
  3  * Software License Agreement (BSD License)
  4  *
  5  * Copyright © 2007, Pint, Inc.
  6  * All rights reserved.
  7  *
  8  * Redistribution and use of this software in source and binary forms,
  9  * with or without modification, are permitted provided that the
 10  * following conditions are met:
 11  *
 12  *   - Redistributions of source code must retain the above
 13  *      copyright notice, this list of conditions and the following
 14  *      disclaimer.
 15  * 
 16  *   - Redistributions in binary form must reproduce the above
 17  *      copyright notice, this list of conditions and the
 18  *      following disclaimer in the documentation and/or other materials
 19  *      provided with the distribution.
 20  *
 21  *   - Neither the name of Pint Inc. nor the names of its contributors
 22  *      may be used to endorse or promote products derived from this
 23  *      software without specific prior written permission of Pint Inc.
 24  *
 25  *
 26  *
 27  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 28  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 29  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 30  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 31  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 32  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 33  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 34  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 35  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 36  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 37  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 38  *
 39  * Docs are generated using java -jar app/js.jar app/run.js -t=templates/htm -d=../docs ../ajaxtcr.js
 40  * OR for private included: java -jar app/js.jar app/run.js -p -t=templates/htm -d=../docsall ../ajaxtcr.js
 41  */
 42 
 43 /**
 44  * @fileOverview The AjaxTCR library was built to support "Ajax: The Complete Reference".
 45  * The primary purpose to cover all aspects of communications including XHR, fallback transports,
 46  * request queue, response queue, caching, templates, many entries to callbacks, form serialization, and proper 
 47  * history management.
 48  * The library also includes supporting functions for data manipulation, dom access, event handling,
 49  * persistence, as well as caching, templates, and history outside of the scope of communications. 
 50  * @name ajaxtcr.js
 51  */
 52 
 53 
 54 /**
 55 * The AjaxTCR global namespace object.  
 56 * @class AjaxTCR
 57 * @static
 58 */
 59 
 60 var AjaxTCR = {};
 61 
 62 /**
 63  * The communication class of the library.  Contains communication methods
 64  * as well as subclasses for caching, queueing, collecting statistics, and accessing 
 65  * cookies
 66  * @class AjaxTCR.comm
 67  * @static
 68  */
 69 AjaxTCR.comm = {
 70 	
 71 /******************************************  Constants  ******************************************/
 72 
 73 /** readyState constants as defined by w3c */
 74  UNSENT : 0,
 75  OPEN :   1,
 76  SENT : 2,
 77  LOADING : 3,
 78  DONE : 4,
 79 
 80 /** Default HTTP request method 
 81  * @private
 82  */
 83 DEFAULT_REQUEST_METHOD : "GET",
 84 
 85 /** Default async option 
 86  * @private
 87  */
 88 DEFAULT_ASYNC : true,
 89 
 90 /** Default option to prevent browser caching of request.  
 91  * Only  works with XHRs and is done by setting the "If-Modified-Since" header 
 92  * @private
 93  */
 94 DEFAULT_PREVENT_CACHE: false,
 95   
 96 /** Default Request Content Type  
 97  * @private
 98  */
 99 DEFAULT_CONTENT_TYPE : "application/x-www-form-urlencoded",
100 
101 /** Default Request Content Transfer Encoding  
102  * @private
103  */
104 DEFAULT_CONTENT_TRANSFER_ENCODING : "",
105 
106 /** Default indicate transport scheme used.  If set will include the transport scheme in the headers or payload  
107  * @private
108  */
109 DEFAULT_TRANSPORT_INDICATOR : true,
110 
111 /** Default timeout in ms.   
112  * @private
113  */
114 DEFAULT_TIMEOUT : 0,
115 
116 /** Default number of Retries  
117  * @private
118  */
119 DEFAULT_RETRIES : 0,
120  
121 /** Default show progress  
122  * @private
123  */
124 DEFAULT_SHOW_PROGRESS : false,
125 
126 /** Default time to revisit our progress callback if monitoring progress  
127  * @private
128  */
129 DEFAULT_PROGRESS_INTERVAL : 1000,
130 
131 /** Default enforce order.  If set, it will enforce order on all requests that have the value set, but not those without  
132  * @private
133  */
134 DEFAULT_ENFORCE_ORDER : false,
135 
136 /** Default Cache Response  
137  * @private
138  */
139 DEFAULT_CACHE_RESPONSE : false,
140 
141 /** Default to put the responseText into outputTarget  
142  * @private
143  */
144 DEFAULT_USE_RAW : true,
145 
146 /** Default one way transmission.  Will not call any callbacks if set 
147  * @private
148  */
149 DEFAULT_ONEWAY : false,
150 
151 /** Default signature to use to sign request.  Places value in the header  
152  * @private
153  */
154 DEFAULT_REQUEST_SIGNATURE : "X-Signature",
155 
156 /** Default option if the response is signed  
157  * @private
158  */
159 DEFAULT_SIGNED_RESPONSE : false,
160 
161 /** Default transport scheme  
162  * @private
163  */
164 DEFAULT_TRANSPORT : "xhr",
165 
166 /** Default transport string value.  Name of the header or payload value that will be sent with request  
167  * @private
168  */ 
169 DEFAULT_TRANSPORT_HEADER : "X-Requested-By",
170 
171 /** Default values to set the transport value to. 
172  * @private
173  */
174 DEFAULT_XHR_TRANSPORT_VALUE: "XHR",
175 DEFAULT_IFRAME_TRANSPORT_VALUE: "iframe",
176 DEFAULT_IMAGE_TRANSPORT_VALUE: "image",
177 DEFAULT_SCRIPT_TRANSPORT_VALUE: "HTMLScriptTag",
178 
179 /** Fallback to another transport if XHR fails  
180  * @private
181  */
182 DEFAULT_FALLBACK : true,
183 
184 /** Default fallback transport scheme  
185  * @private
186  */
187 DEFAULT_FALLBACK_TRANSPORT: "iframe",
188 
189 /** Default output handing.  Places responseText into outputTarget with this method  
190  * @private
191  */
192 DEFAULT_INSERTION_METHOD : "replace",
193 
194 /** Cache the template  
195  * @private
196  */
197 DEFAULT_CACHE_TEMPLATE : true,
198 
199 /** Default location for rendering templates  
200  * @private
201  */
202 DEFAULT_TEMPLATE_RENDER : "client",
203 
204 /** Constant for server defined template file  
205  * @private
206  */
207 TEMPLATE_SERVER_DEFINED : "dynamic",
208 
209 
210 /****************************************** Private Properties ****************************************************/
211  
212 /** the request id counter  
213  * @private
214  */	
215  _requestID : 0,  	
216 
217 /** request counter shows outstanding requests  
218  * @private
219  */
220 _requestsOutstanding : 0,
221  
222 /** the statuses for possible network errors
223  *  3507 = library error flag 
224  * @private
225  */
226  _networkErrorStatus : new Array(0, 408, 504, 3507, 12002, 12007, 12029, 12030, 12031, 12152),
227  
228  
229  /*****************************************  GETTERS/SETTERS ***********************************/
230 
231 /**
232  * Updates a default value in the AjaxTCR.comm namespace.
233  * 
234  * @param {string} option The name of the option to update
235  * @config {string} DEFAULT_REQUEST_METHOD Possible values - any valid HTTP Method.  Default: "GET"
236  * @config {boolean} DEFAULT_ASYNC Default: true 
237  * @config {boolean}DEFAULT_PREVENT_CACHE Default: false,
238  * @config {string} DEFAULT_CONTENT_TYPE Default : "application/x-www-form-urlencoded",
239  * @config {string} DEFAULT_CONTENT_TRANSFER_ENCODING Default : "",
240  * @config {boolean}DEFAULT_TRANSPORT_INDICATOR Default : true,
241  * @config {integer} DEFAULT_TIMEOUT Default : 0,
242  * @config {integer} DEFAULT_RETRIES Default : 0,
243  * @config {boolean} DEFAULT_SHOW_PROGRESS Default : false,
244  * @config {integer} DEFAULT_PROGRESS_INTERVAL Default : 1000,
245  * @config {boolean} DEFAULT_ENFORCE_ORDER Default : false,
246  * @config {boolean} DEFAULT_CACHE_RESPONSE Default : false,
247  * @config {boolean} DEFAULT_USE_RAW Default : true,
248  * @config {boolean} DEFAULT_ONEWAY Default : false,
249  * @config {string} DEFAULT_REQUEST_SIGNATURE Default : "X-Signature",
250  * @config {boolean} DEFAULT_SIGNED_RESPONSE Default : false,
251  * @config {string} DEFAULT_TRANSPORT Default : "xhr",
252  * @config {string} DEFAULT_TRANSPORT_HEADER Default : "X-Requested-By",
253  * @config {string} DEFAULT_XHR_TRANSPORT_VALUE Default : "XHR",
254  * @config {string} DEFAULT_IFRAME_TRANSPORT_VALUE Default : "iframe",
255  * @config {string} DEFAULT_IMAGE_TRANSPORT_VALUE Default : "image",
256  * @config {string} DEFAULT_SCRIPT_TRANSPORT_VALUE Default : "HTMLScriptTag",
257  * @config {boolean} DEFAULT_FALLBACK Default : true,
258  * @config {string} DEFAULT_FALLBACK_TRANSPORT Default : "iframe",
259  * @config {string} DEFAULT_INSERTION_METHOD Default : "replace",
260  * @config {boolean} DEFAULT_CACHE_TEMPLATE Default : true,
261  * @config {string} DEFAULT_TEMPLATE_RENDER Default : "client",
262  * @config {string} TEMPLATE_SERVER_DEFINED Default : "dynamic",
263  * @param {object} value  The value to set the option to.
264  */								
265 setDefault : function(option, value){
266 	AjaxTCR.comm[option] = value;
267 },
268 
269 /**
270  * Retrieves the default value in the AjaxTCR.comm namespace for the given option
271  * 
272  * @param {string} option The name of the option to fetch
273  * @return {string} The value of the passed in option
274  */
275 getDefault : function(option){
276 	return AjaxTCR.comm[option]
277 },				
278 
279 
280 /******************************************  Public Methods  ******************************************/
281 
282 /**
283  * sendRequest - public method to create an Ajax style request
284  * 
285  * @param url 	string of URL to send request to
286  * @param options  object containing the options for the request
287  * @config {Boolean} async	Defines if the request should be asynchronous or not.  The default is true when not specified.
288  * @config {string} cacheKey	By default, when cache is turned on, items are saved in cache using the URL of the object as a key.  If another value is desired you may set it through this property though you will be responsible for manually retrieving as the request system will use the URL of requests to determine if something is cached or not.	Default is URL of request
289  * @config {Boolean} cacheResponse	Boolean to indicate if the response should be saved in the response cache.	Default is false
290  * @config {Boolean} cacheTemplate	Indicates that if a cache is returned with the response if it should be saved in the template cache or not.	Default is true
291  * @config {string} cookieName	The name of the cookie expected upon response when the transport type is image.  If specified the responseText will be populated with the value of this cookie only.  If unspecified responseText will contain the entire cookie and the developer is required to parse out the response manually.  Should be set if outputTarget is also specified with request.	Default is document.cookie
292  * @config {Boolean} enforceOrder	Boolean to force every response that has this value set to be returned in the order in which it was sent, this means that requests may be held until previous requests arrive.	Default is false
293  * @config {Boolean} fallback	Defines if the communication mechanism should fallback to another method if the XHR fails for some reason.  The fallback transport scheme is defined by fallbackTransport or the global default is consulted.	Default is true
294  * @config {string} fallbackTransport Options are "iframe" | "script" | "image"  Defines the particular communication mechanism that should be used if XHRs fail for some reason fallback. If undefined the global default (iframe) is used unless it has been overridden.  .	Default is "iframe"
295  * @config {object} headers  Array-of-Header Objects	An array of header objects to be sent with the request.  The header object must have two properties called name and value with the appropriate values.  It is set up in this manner to allow multiple values for a single name.  The library will append these together with ‘,’. Note that setting a Cookie header should be avoided particularly if more than one value is set and document.cookie should be used instead. 	Default is []
296  * @config {object} history	Used to control the history mechanism on a request basis.  The passed object has three properties, saveResponse, id, and title. The saveResponse property indicates that the response will be cached and when a user backs up to the page in question another request will not be issued.  By default responses will not be saved. The id is the value used in the hash mark (ex. #currentState), the id is required.  The title property is used to set the title of the page so as to reflect the current state of the application.	Default is null
297  * @config {string} insertionType  "insertBefore" | "insertAfter" | "firstChild" | "lastChild" | "replace" 	Used in conjunction with outputTarget to define how content returned should be handled relative to the element specified by the outputTarget value.  By default the returned content will replace the outputTarget element content. Other values include. <br />insertBefore put as element just before the specified element <br />insertAfter put as an element just after the specified element<br />firstChild put as the first child within the specified element<br />lastChild put as the last child within the specified element 	<br />Default is "replace" 
298  * @config {string} method HTTP-method	Sets the method for the request to the string HTTP-method.  No limit to what is settable, though some XHR implementations will not support some methods and of course destinations may reject methods.  If unset a default method will be used.  Note that some browsers XHR implementations will not allow for extended HTTP methods and that alternate transfers may be even more restrictive (iframe - GET and POST, all other transports - GET only)	Default is "GET"
299  * @config {function} onCreate	Called right after the XHR object is created.  Corresponds to readyState == 0.  Passes the request object.	Default is null
300  * @config {Boolean} oneway	Indicates if the request is one way and thus if the response should be ignored.	Default is false
301  * @config {function} onFail	Callback that is called when a server error occurs.  Most often this occurs when the status != 200.  Passes the request object along with a message describing the error.	Default is function () {}
302  * @config {function} onLoading	Callback that is called with the xhr.readyState == 3.  This occurs when the data begins to come back.  Passes the request object.	Default is null
303  * @config {function} onOpen	Callback that is called when the xhr.readyState == 1.  This occurs after xhr.open.  Passes the request object.	Default is null
304  * @config {function} onPrefetch	Callback that is invoked when you are prefetching data but not yet using it.	Default is function (){} 
305  * @config {function} onProgress	Callback invoked by default once every second.  Useful for updating the user to the progress of long requests.  Often used with the status object. You can override the default progressInterval of one second if desired.	Default is function () {}
306  * @config {function} onReceived Callback that corresponds to readyState 4 but without having looked at the success or failure of the request yet, thus it will be called before onSuccess or onFail.	Default is null
307  * @config {function} onRetry	Callback function that is called when retry is enabled.  Called very time a retry occurs.	Default is function () {}
308  * @config {function} onSent	Callback that is called when the xhr.readyState = 2.  This occurs right after xhr.send().  Passes the request object.	Default is null
309  * @config {function} onStatus	Callback that is invoked for the corresponding status code.  For example the callback for on404 is called when a response of 404 is received while an on500 is called when a 500 response code is received.	Default is undefined
310  * @config {function} onSuccess	Primary callback that will be called whenever the request completes successfully with a status of 200.  Passes the response object as a parameter.	Default is function () {}
311  * @config {function} onTimeout	Callback that is invoked when a timeout occurs.  If there are retries and continual failures this callback may be invoked numerous times.	Default is function () {}
312  * @config {object} outputTarget	When specified the request’s responseText will be automatically inserted into the specified object using its innerHTML property.  The object should be a reference to a DOM element or a string to be used that references an existing DOM element by its id attribute.  The useRaw option can be set to false so that a user may desire to override the immediate placement of content but still use this property as a reference. 	Default is null
313  * @config {string} password	The password to be used when addressing HTTP authentication challenges.  Only supported with the XHR transport. 	Default is ""
314  * @config {string} payload	The payload is a properly encoded string (or object) to be submitted in a query string or message body depending on the HTTP method used.  Various AjaxTCR.data methods like encodeValue() and serializeForm() may be used to quickly form a payload. The payload must be in the format in which it is going to be used.	Default is ""
315  * @config {Boolean} preventCache	When set to true, attempts to disable caching by setting the request header to a very old date.  Users may also desire to add a unique query string as well.	Default is false
316  * @config {millisecond} progressInterval	This value is used to indicate how often the request should be polled for progress updates in milliseconds.  Defaults to 1 second (1000ms).
317  * @config {string} requestContentType MimeType string	The content type on the request.  If the request is a POST, it will set the request Content-Type header to this value.  Will base form serialization on it as well.	Default is "application/x-www-form-urlencoded"
318  * @config {string} requestContentTransferEncoding encodingType	Sets the Content-Transfer-Encoding header on the request to the defined value.	Default is ""
319  * @config {string} requestSignature	Indicates the header used when signing requests and will contain the contents of signRequest property if it is set.	Default is "X-Signature"
320  * @config {integer} retries	Indicates if a request should be retried if an error is encountered or a timeout occurs.   Set to false or 0 to not retry failed requests.  Set this value larger than 0 to indicate number of retries	Default is 0
321  * @config {object} serializeForm	Automatically encodes the contents of the form specified as an object, id or name.  A default encoding of x-www-form-urlencoded will be used unless the requestContentType attribute is set.	Default is null
322  * @config {Boolean} showProgress	Setting this property to true indicates that the progress event will fire.	Default is false
323  * @config {string} signRequest "signature string"	Used to sign a request, typically it is an MD5 hash value that will be put in the Web page when generated by a server-side program.	Default is null
324  * @config {Boolean} signedResponse	If the response is signed, the library will check the "Content-MD5" header in the response and compare it to a MD5 encoding of the responseText.  If they do not match, onFail is called and the responseText is not returned.	Default is false
325  * @config {object} statusIndicator statusObject 	The property should be set to an object which contains visual display information for indicating status.  At this point it supports an object with a single property progress set to an object containing type which can be either image or text, imageSrc which is the URL of the image to use in the case type is set to image, and text is a string to use in the case the type is set to text.  A target property is set to the DOM id reference of the place the status should be displayed. 	Default is null
326  * @config {string} template URL | "dynamic" 	If a URL is specified the template to apply to a response will be fetched.  If the string value of “dynamic” is used a server-side program will respond and include a template value either as a string or as URL to fetch.  These values are found in the response packet in JSON format at the properties templateText and templateURL respectively.  	Default is null
327  * @config {string} templateRender "client" | "server"	String indicating if a template should be rendered on client or server, only works if the template property is set.  A default value of client is assumed when template is set but templateRender is not.	Default is "client"
328  * @config {number} timeout	Indicates whether to timeout or not.  False or 0 indicates not to catch timeouts.  A number greater than 0 indicates the number of milliseconds before timing out.	Default is false
329  * @config {string} transport "xhr" | "iframe" | "script" | "image"	Transport to make the request with.  By default this will be XHR though you can change it on a per request basis.  The global transport can be set with setDefault("transport",value) where value one of the defined strings.  The transport choice may change a request depending on the capabilities of the transport indicated. For example, image and script transports will not accept a POST request and will convert it into a GET if possible.	Default is "xhr"
330  * @config {Boolean} transportIndicator	Indicates if Ajax indicating headers such as X-Requested-By: XHR should be included.  Normally defined by value AjaxTCR.comm.DEFAULT_TRANSPORT_INDICATOR. Setting as an option effects only the request made, use the general the getter/setter AjaxTCR.comm.setDefault("DEFAULT_TRANSPORT_INDICATOR", false);	Default is true
331  * @config {Boolean} useRaw	By default this is set to true and is consulted when outputTarget is set.  If set to false the response’s payload will not be directly put into the outputTarget forcing you to manually perform any decode and placement. 	Default is true
332  * @config {string} username	Used to specify the username for HTTP authentication challenges issued to a request.  Only usable with an XHR transport.	Default is ""
333  * @config {Boolean} useRaw	This value is consulted when outputTarget is set.  If set to false the response’s payload will not be directly put into the outputTarget forcing you to manually perform any decode and placement. 	Default is true
334  * @config {object} userVars	Value attached to the request/response object that may contain any form of user defined data.	Default is undefined
335  * @return {object} The newly generated request object.
336  */	
337  sendRequest : function (url,options) {
338  	
339 	var request = new Object();
340 		
341 	/* increment our requestId number */  
342 	request.requestID = ++AjaxTCR.comm._requestID;
343 	
344 	/* basic communication defaults */
345 	request.method = AjaxTCR.comm.DEFAULT_REQUEST_METHOD;
346     request.async = AjaxTCR.comm.DEFAULT_ASYNC;
347 	request.preventCache = AjaxTCR.comm.DEFAULT_PREVENT_CACHE;
348 	request.requestContentType = AjaxTCR.comm.DEFAULT_CONTENT_TYPE;
349 	request.requestContentTransferEncoding = AjaxTCR.comm.DEFAULT_CONTENT_TRANSFER_ENCODING;
350 	request.payload = "";
351 
352 	/* header management */
353 	request.headers = new Array();
354 	request.transportIndicator = AjaxTCR.comm.DEFAULT_TRANSPORT_INDICATOR;
355 
356 	/* standard callbacks */
357 	request.onSuccess = function(){};
358 	request.onFail = function(){};
359 	
360 	/* callbacks associated with readyState changes */
361 	request.onCreate = null;
362 	request.onOpen = null;
363 	request.onSent = null;
364 	request.onLoading = null;
365 	request.onReceived = null;
366 
367 	/* communication status flags */    
368     request.abort = false;
369 	request.inProgress = true;
370 	request.received = false;
371 	
372 	/* progress management */
373 	request.showProgress = AjaxTCR.comm.DEFAULT_SHOW_PROGRESS;
374 	request.progressInterval = AjaxTCR.comm.DEFAULT_PROGRESS_INTERVAL;
375 	request.onProgress = function (){};
376 	request.progressTimerID = null;
377 	
378 	/* timeout parameters */
379 	request.timespent = 0;
380 	request.timeout = AjaxTCR.comm.DEFAULT_TIMEOUT;
381 	request.onTimeout = function(){};
382 	request.timeoutTimerID = null;
383 
384     /*  retry parameters */
385 	request.retries = AjaxTCR.comm.DEFAULT_RETRIES;
386 	request.retryCount = 1;
387 	request.onRetry = function (){};
388 	
389 	/* sequencing */
390 	request.inQueue = false;
391 	request.responseQueueID = 0;
392 	request.enforceOrder = AjaxTCR.comm.DEFAULT_ENFORCE_ORDER;
393 
394 	/* cache management */
395 	request.cacheResponse = AjaxTCR.comm.DEFAULT_CACHE_RESPONSE;
396 	request.fromCache = false;
397 	
398 	/* Prefetch */
399 	request.onPrefetch = function(){};
400 	request.isPrefetch = false;
401 
402     /* payload serialization */
403 	request.serializeForm = null;
404 	request.hasFile = false;
405 
406     /* output handling */
407 	request.outputTarget = null;
408 	request.useRaw = AjaxTCR.comm.DEFAULT_USE_RAW;
409 	request.insertionType = AjaxTCR.comm.DEFAULT_INSERTION_METHOD;
410 	
411 	/* transmission type */
412 	request.oneway = AjaxTCR.comm.DEFAULT_ONEWAY;
413 	
414 	/* authentication */
415 	request.username = null;
416 	request.password = null;
417 	
418 	/* security */
419 	request.requestSignature = AjaxTCR.comm.DEFAULT_REQUEST_SIGNATURE;
420 	request.signRequest = null;
421 	request.signedResponse = AjaxTCR.comm.DEFAULT_SIGNED_RESPONSE;
422 
423 	/* history */
424 	request.history = null;
425 	
426 	/* transport/fallback */
427 	request.transport = AjaxTCR.comm.DEFAULT_TRANSPORT;
428 	request.fallback = AjaxTCR.comm.DEFAULT_FALLBACK;
429 	request.fallbackTransport = AjaxTCR.comm.DEFAULT_FALLBACK_TRANSPORT;
430 	request.cookieName = null;
431 	
432 	/* Templates */
433 	request.template = null;
434 	request.templateRender = AjaxTCR.comm.DEFAULT_TEMPLATE_RENDER;
435 
436 	request.cacheTemplate = AjaxTCR.comm.DEFAULT_CACHE_TEMPLATE;
437 	request.shortTermCacheTemplate = false;
438 	
439 	request.statusIndicator = null;
440 	
441     /* apply options defined by user */
442     for (option in options)
443       request[option] = options[option];
444 	  
445 	if (request.isPrefetch)
446 		request.cacheResponse = true;
447 		
448 	/* Enable backguard if necessary */
449 	if (AjaxTCR.history._backGuardEnabled == AjaxTCR.history.BACK_GUARD_INITIALIZED)
450 		AjaxTCR.history._activateBackGuard();
451 		
452 	/* Check for/Fetch template */
453 	if (request.template && request.template != AjaxTCR.comm.TEMPLATE_SERVER_DEFINED && request.templateRender == "client")
454 	{
455 		if (!AjaxTCR.template.getFromCache(request.template))
456 		{
457 			request.enforceOrder = true;
458 			AjaxTCR.comm.sendRequest(request.template, {shortTermCacheTemplate:true, enforceOrder:true});
459 		}
460 	}
461 	else if (request.template && request.template != AjaxTCR.comm.TEMPLATE_SERVER_DEFINED && request.templateRender == "server")
462 	{
463 		if (request.payload != "")
464 			request.payload += "&";
465 		request.payload += "templateURL=" + request.template;
466 	}
467 	  
468 	/* Serialize the given form if request.serialize is set */
469 	if (request.serializeForm)
470 	{
471 		/* Serialize given form */
472 		var newPayload = AjaxTCR.data.serializeForm(request.serializeForm,request.requestContentType);
473 		
474 		/* check to see if we have a fileupload situation */
475 		if (newPayload == "fileupload")
476 			request.hasFile = true;
477 		else
478 		{
479 			/* Check to see if payload exists */
480 			if (request.payload)
481 			{
482 				/* If payload is an object, use serializeObject otherwise append to end of the new payload */
483 				if (typeof(request.payload) == "object") 
484 					newPayload = AjaxTCR.data.serializeObject(newPayload, request.payload, request.requestContentType);
485 				else if (request.requestContentType == AjaxTCR.comm.DEFAULT_CONTENT_TYPE)
486 					newPayload += "&" + request.payload;  
487 			}
488 			
489 			request.payload = newPayload;
490 			
491 			/* Get all values into string format */
492 			if (request.requestContentType == "application/json")
493 	  			request.payload = AjaxTCR.data.encodeJSON(request.payload);
494 			else if (request.requestContentType == "text/xml")
495 				request.payload = AjaxTCR.data.serializeXML(request.payload);
496 				
497 			/* Encode it in base64 if that's set */
498 			if (request.requestContentTransferEncoding == "base64")
499 				request.payload = AjaxTCR.data.encode64(request.payload);
500 		}
501 	}
502 	
503 	/* Add to history */
504 	if (request.history)
505 		AjaxTCR.history.init(function(){});
506 		
507 	if (request.history && !request.history.saveResponse)
508 		AjaxTCR.history.addToHistory(request.history.id, "", request.history.title, url,  options);
509   
510   	/* If there is a file, we need to handle differently */
511 	if (request.hasFile)
512 		request.transport = "iframe";
513 	
514 	/* normalize the transport value */	
515 	request.transport = request.transport.toLowerCase();
516 	
517 	if (request.transport == "script" || request.transport == "image")
518 		request.method = "GET";
519 	
520 	if (request.method.toUpperCase() == "GET" && request.payload != "")
521 		request.url = url + "?" + request.payload;
522 	else
523 	    request.url = url;
524 		 
525 	if (request.method.toUpperCase() == "POST")
526 		request.postBody = request.payload;
527 	else
528 	    request.postBody = null;
529 	    	
530 		
531 	/* Add a queueID if necessary */
532 	if (request.enforceOrder)
533 		request.responseQueueID = AjaxTCR.comm.queue._responseQueue.maxID++;
534 		
535 	var cachedResponse = null;
536 	/* Check if the item is in the cache first */
537 	if (request.cacheResponse)
538 	{
539 		/* Check to see if we have a key for our cache */
540 		if (request.cacheKey == undefined)
541 			request.cacheKey = request.url;
542 	
543 		cachedResponse = AjaxTCR.comm.cache.get(request.cacheKey);
544 		if (cachedResponse)
545 			AjaxTCR.comm.cache._handleCacheResponse(request, cachedResponse);
546 	}
547 		
548 	/* invoke the request */
549 	if (!cachedResponse)
550 		AjaxTCR.comm._makeRequest(request);
551 	
552 
553     /* return object for local control */
554     return request;	
555  },		
556  
557  
558  /**
559   * Public method that will abort any passed request object and clean up
560   * any timers for showing requqest state
561   * 
562   * @param {object} request The request object generated through sendRequest.
563   */
564  abortRequest : function(request) {
565  	     
566 	/* set the abort flag */
567  	request.abort = true;
568 									 
569 	/* clear inProgress flag */
570 	request.inProgress = false;
571 									 
572 	/* abort the request */	
573 	request.xhr.abort();
574 									 
575 	/* decrement outstand request count */
576 	AjaxTCR.comm._requestsOutstanding--;
577 									 
578 	/* clear any timeout timers */
579 	clearTimeout(request.timeoutTimerID);
580 	request.timeoutTimerID = null;
581 									 
582 	/* stop showing progress */
583 	if (request.progressTimerID)
584 	  {
585 	   clearTimeout(request.progressTimerID);
586 	   request.progressTimerID = null;
587 	  }
588 										
589 	/* Remove Progress Indicators */
590 	if (request.statusIndicator)
591 	  {
592 		AjaxTCR.comm._removeProgressStatus(request.statusIndicator);
593 	  }									
594 },
595 
596 /******************************************  Private Methods  ******************************************/
597  	 
598  
599  /**
600  * _createXHR - private method acting as a wrapper to make an XMLHttpRequest object.  Trys native
601  *              object first and then ActiveX control.  Returns null if fails.
602  * 
603  * @private
604  * @return {object} Either the native XHR Object or the most current ActiveX version supported.
605  */	
606  _createXHR : function() { 
607                           
608 	try { return new XMLHttpRequest(); } catch(e) {}
609     try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
610     try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
611     try { return new ActiveXObject("Msxml2.XMLHTTP"); } 	  catch (e) {}
612     try { return new ActiveXObject("Microsoft.XMLHTTP"); }  catch (e) {}
613 						  	           
614     return null;
615 },
616 
617 /**
618  * Private method that decides which initializes the requesst, decides which transport to use, and calls the send method for that transport.
619  * 
620  * @private
621  * @param {object} the Request Object to make the request with.
622  */
623 
624 _makeRequest : function (request) {		
625 
626 									
627 	/* Increment total requests */
628 	AjaxTCR.comm.stats._commResults.totalRequests++;
629 									
630 	/* Display status and start Progress Callback */
631 	if (!request.oneway)
632 		AjaxTCR.comm._initSend(request);
633 								  
634 	/* Call back for ready state 0 if set */
635 	if (request.onCreate)
636 	  request.onCreate(request);
637 									  
638 	if (request.transport == "xhr")
639 		AjaxTCR.comm._sendXHR(request);
640 	else if (request.transport == "iframe")
641 		AjaxTCR.comm._sendIframe(request);
642 	else if (request.transport == "script")
643 		AjaxTCR.comm._sendScript(request);
644 	else if (request.transport == "image")
645 		AjaxTCR.comm._sendImage(request);
646 									 	                              	
647 },
648 
649 /**
650  * Private method that sends an XHR request.  It creates the XHR, fallsback if any problems are encountered, sets appropriate headers,
651  * and sends the requesst.
652  * 
653  * @private
654  * @param {Object} request The request that contains the options that we wish to send.
655  * 
656  */
657 								 
658 _sendXHR : function(request){
659 	
660 	request.xhr = AjaxTCR.comm._createXHR();
661 	if (!request.xhr)
662 	{
663 		AjaxTCR.comm._fallbackOrError(request);
664 		return;
665 	}									
666 							
667 	/* open the request */
668 	try{
669 		request.xhr.open(request.method, request.url, request.async, request.username, request.password);
670 	}
671 	catch(e){
672 		AjaxTCR.comm._fallbackOrError(request);
673 		return;
674 	}								
675 	
676 	/* clear an abort flag in case this is a retry */
677 	request.abort = false;
678 									
679 	/* set headers indicating we did this with Ajax and what our transaction id is */
680 	if (request.transportIndicator)
681 	{
682 	   request.xhr.setRequestHeader(AjaxTCR.comm.DEFAULT_TRANSPORT_HEADER,AjaxTCR.comm.DEFAULT_XHR_TRANSPORT_VALUE);
683 	   request.xhr.setRequestHeader("X-Request-Id",request.requestID);
684 	}
685 									  
686 	/* Set signature header */
687 	if (request.signRequest)
688 	 	request.xhr.setRequestHeader(request.requestSignature, request.signRequest);
689 								
690 	/* set header(s) for POST */
691 	if (request.method.toUpperCase() == "POST")
692 	{
693 	   request.xhr.setRequestHeader("Content-Type", request.requestContentType);
694 	   if (request.requestContentTransferEncoding != "")
695 		 request.xhr.setRequestHeader("Content-Transfer-Encoding", request.requestContentTransferEncoding);
696 	}
697 									  
698 	/* Prevent Caching if set */
699 	if (request.preventCache)
700 		request.xhr.setRequestHeader("If-Modified-Since", "Wed, 15 Nov 1995 04:58:08 GMT");
701 									  
702 	/* set user defined headers */
703 	request.headerObj = {};
704 	for (var i=0; i<request.headers.length;i++)
705 	{
706 		if (request.headers[i].name.toUpperCase() == "COOKIE")
707 			document.cookie = request.headers[i].value;
708 		else if(request.headerObj[request.headers[i].name] === undefined)
709 			request.headerObj[request.headers[i].name] = request.headers[i].value;
710 		else	
711 			request.headerObj[request.headers[i].name] =  request.headers[i].value + "," + request.headerObj[request.headers[i].name];
712 	}
713 									
714 	for (var header in request.headerObj)
715 		request.xhr.setRequestHeader(header, request.headerObj[header]);
716 									
717 	
718 	if (!request.oneway)
719 	{
720 		/* bind the success callback */
721 		request.xhr.onreadystatechange = function () {AjaxTCR.comm._handleReadyStateChange(request);};
722 									
723 		/* set a timeout if set */
724 		if (request.async && request.timeout && request.timeoutTimerID == null)
725 			 request.timeoutTimerID = window.setTimeout( function(){AjaxTCR.comm._timeoutRequest(request);}, request.timeout);
726 		
727 	}
728 									
729 	/* send the request */
730 	request.xhr.send(request.postBody);	 
731 },
732 	
733 /** 
734  * Private method that creates a request using the script transport.  Updates the payload to pass the transport method, creates a script element,
735  * sends the request by adding the element to the page.
736  * 
737  * @private
738  * @param {Object} request The request object that is being sent.
739  */			
740 _sendScript : function(request){
741 	var script = document.createElement('script');
742 	var callback = function(){AjaxTCR.comm._handleScriptResponse(request);};
743 	
744 	if (request.transportIndicator)
745 	{								
746 	  if (request.url.indexOf("?"))
747 		request.url += "&" + AjaxTCR.comm.DEFAULT_TRANSPORT_HEADER + "=" + AjaxTCR.comm.DEFAULT_SCRIPT_TRANSPORT_VALUE;
748 	  else
749 		request.url += "?" + AjaxTCR.comm.DEFAULT_TRANSPORT_HEADER + "=" + AjaxTCR.comm.DEFAULT_SCRIPT_TRANSPORT_VALUE;
750 	}
751 										
752 	if (script.addEventListener)
753 		script.addEventListener("load", callback, false);
754 	else
755 	{
756 		script.onreadystatechange = function() 
757 		{
758 			if (this.readyState == "complete") 
759 				callback.call(this);
760 		};
761 	}
762 									
763 	script.src = request.url;
764 	script.type = "text/javascript";
765 	document.body.appendChild(script);
766 },
767 				
768 
769 /** 
770  * Private method that creates a request using the iframe transport.  
771  * Updates the payload to pass the transport method, creates the iframe element.
772  * Keeps the height at 1px and visiblity hidden.
773  * Sets the callback and attaches it to the load events of the iframe
774  * Simply sets the src for a GET and builds a form to submit for a POST
775  * 
776  * @private
777  * @param {Object} request The request object that is being sent.
778  */	
779 _sendIframe : function(request){
780 	var iframeID = AjaxTCR.util.misc.generateUID("AjaxTCRIframe_");
781 										
782 	/* IE does not handle document.createElement("iframe"); */
783 	if(window.ActiveXObject)
784 		var iframe = document.createElement('<iframe id="' + iframeID + '" name="' + iframeID + '" />');
785 	else
786 	{ 
787  		var iframe = document.createElement("iframe");
788 		iframe.id = iframeID; 
789 	    iframe.name = iframeID;
790 	}
791 	
792 	iframe.style.height = "1px";
793 	iframe.style.visibility = "hidden";
794 	
795 	document.body.appendChild(iframe);
796 	
797 	var callback = function(){AjaxTCR.comm._handleIFrameResponse(request, iframe);};
798 		
799 	/* IE does not recognize iframe.onload */
800 	if(window.attachEvent)	
801 		iframe.attachEvent('onload', callback);
802 	else
803 		iframe.addEventListener('load', callback, false);
804 		
805 		
806 	if (request.hasFile)
807 	{
808 		request.serializeForm.target = iframe.id;
809 		request.serializeForm.submit();
810 	}
811 	else if (request.method.toUpperCase() == "GET")
812 	{
813 	  if (request.transportIndicator)
814 	   {
815 		if (request.url.indexOf("?") > -1)
816 			request.url += "&" + AjaxTCR.comm.DEFAULT_TRANSPORT_HEADER + "=" + AjaxTCR.comm.DEFAULT_IFRAME_TRANSPORT_VALUE;
817 		else
818 			request.url += "?" + AjaxTCR.comm.DEFAULT_TRANSPORT_HEADER + "=" + AjaxTCR.comm.DEFAULT_IFRAME_TRANSPORT_VALUE;
819 	   }
820 	   
821 		iframe.src = request.url;
822 	}
823 	else
824 	{
825 		var ifrForm = makeIframeForm(iframe, request);
826    		ifrForm.submit();
827 	} 
828 	
829 	
830 	function makeIframeForm(ifr, request)
831     {
832    	   var url = request.url;
833 	   var payload = request.postBody;
834 	   
835        var ifrDoc = null;
836        var ifrWindow = ifr.contentWindow || ifr.contentDocument;
837        if (ifrWindow.document)
838            ifrDoc = ifrWindow.document;
839        else
840            ifrDoc = ifrWindow;
841 
842        if (!ifrDoc.body)
843        {
844            var html = ifrDoc.createElement("HTML");
845            ifrDoc.appendChild(html);
846 
847            var head = ifrDoc.createElement("HEAD");
848            html.appendChild(head);
849 
850            var body = ifrDoc.createElement("BODY");
851            html.appendChild(body);
852        }
853 
854        var ifrForm = ifrDoc.createElement("FORM");
855        ifrForm.action = url;
856        ifrForm.method = "post";
857        ifrDoc.body.appendChild(ifrForm);
858 	   var keys = payload.split("&");
859 
860        for (var i=0;i<keys.length;i++)
861        {
862 		   var nv = keys[i].split("=");
863            var ifrText = ifrDoc.createElement("INPUT");
864            ifrText.type = "text";
865            ifrText.name = nv[0];
866            ifrText.value = nv[1];
867            ifrForm.appendChild(ifrText);
868        }
869 	
870 	   if (request.transportIndicator)
871 	   {
872 	    var ifrText = ifrDoc.createElement("INPUT");
873         ifrText.type = "text";
874         ifrText.name = AjaxTCR.comm.DEFAULT_TRANSPORT_HEADER;
875         ifrText.value = AjaxTCR.comm.DEFAULT_IFRAME_TRANSPORT_VALUE;
876         ifrForm.appendChild(ifrText);
877        }
878 	   
879        return ifrForm;
880 
881 	}
882 },
883 				
884 				
885 
886 /** 
887  * Private method that creates a request using the image/cookie transport.  
888  * Updates the payload to pass the transport method, creates an image element and sets the onload callback and the src to the URL.
889  * 
890  * @private
891  * @param {Object} request The request object that is being sent.
892  */					
893 _sendImage : function(request){
894 	var callback = function(){AjaxTCR.comm._handleImageResponse(request);};
895 	
896 	if (request.transportIndicator)
897 	{
898  	 if (request.url.indexOf("?"))
899 		request.url += "&" + AjaxTCR.comm.DEFAULT_TRANSPORT_HEADER +  "=" + AjaxTCR.comm.DEFAULT_IMAGE_TRANSPORT_VALUE;
900 	 else
901 		request.url += "?" + AjaxTCR.comm.DEFAULT_TRANSPORT_HEADER + "=" + AjaxTCR.comm.DEFAULT_IMAGE_TRANSPORT_VALUE;
902 	}
903 											
904 	var img = new Image();
905     img.onload = callback; 
906 	img.src = request.url;
907 },								 
908 
909  /**
910   * Private method called to init items that should be initialized no matter what communication technique is used.
911   * Creates the statusIndicator and the progress timeout if they are used, updates startTime, requestOutstanding, and timespent.
912   * 
913   * @private
914   *  @param {Object} request The request object that contains the options to  update.
915   */
916  
917  _initSend : function(request){
918 	/* Set Progress Indicators */ 
919 	if (request.statusIndicator) 
920 		request.statusIndicator.element = AjaxTCR.comm._setProgressStatus(request.statusIndicator);					
921 	
922 	/* set the time spent in case this is a retry */
923 	request.timespent = 0;
924 	
925 	/* set progress indicator to show something nearly every second */
926 	if (request.showProgress && request.progressTimerID == null)
927 		 request.progressTimerID = window.setTimeout( function(){AjaxTCR.comm._progressRequest(request);}, request.progressInterval);
928 	
929 	/* Record start time of request */
930 	request.startTime = (new Date()).getTime();
931 	
932 	/* increment requests outstanding */
933 	AjaxTCR.comm._requestsOutstanding++;
934 	
935 },
936 
937 /** 
938  * fallbackOrError : If an XHR does not work, tries to send request using fallbackTransport
939  * If transport is changed to script or image, ensures that the method is set to GET.
940  * 
941  * @private
942  * @param  request
943  */
944  _fallbackOrError : function(request){
945  	if (request.fallback)
946 	{
947 		request.transport = request.fallbackTransport.toLowerCase();
948 		if ((request.transport == "script" || request.transport == "image") && request.method.toUpperCase() == "POST")
949 		{
950 			request.method = "GET";  
951 			request.url = request.url + "?" + request.postBody;
952 			request.postBody = null;
953 		}	
954 		if (request.transport == "iframe")
955         	AjaxTCR.comm._sendIframe(request);
956    		else if (request.transport == "script")
957       		AjaxTCR.comm._sendScript(request);
958    		else if (request.transport == "image")
959       		AjaxTCR.comm._sendImage(request);	
960 		else
961 			throw "AjaxTCR Error: Unknown fallback transport: " + request.transport;
962 	}
963 	else
964 		throw "AjaxTCR Error: XHR Creation failed and fallback is not enabled";
965 },								   
966 
967 /**
968  * Automatically called when the iframe completes loading.  
969  * Updates httpStatus and httpStatusText and sets responseText and responseXML to contents of the body.
970  * Calls general _handleResponse function on completion.
971  * 
972  * @private
973  * @param {Object} response The response Object that contains all the settings for the request
974  * @param {Object} iframe The iframe that was created in _sendIframe.
975  */									   
976 _handleIFrameResponse : function(response, iframe){ 
977 	response.httpStatus = 200;
978 	response.httpStatusText = "OK";
979 	if (iframe.contentWindow.document.body)
980 		response.responseText = iframe.contentWindow.document.body.innerHTML;
981 	
982 	if (iframe.contentWindow.document.XMLDocument)
983 		response.responseXML = iframe.contentWindow.document.XMLDocument;
984 	else
985 		response.responseXML = iframe.contentWindow.document;
986 										
987 	AjaxTCR.comm._handleResponse(response);
988 },
989 												  
990 /**
991  * Automatically called when the script completes loading.  
992  * Updates httpStatus and httpStatusText
993  * Sets responseText to "" and responseXML to null as the contents will already have been evaluated.
994  * Calls general _handleResponse function on completion.
995  * 
996  * @private
997  * @param {Object} response The response Object that contains all the settings for the request
998  */													  
999 _handleScriptResponse : function(response){
1000 	response.httpStatus = 200;
1001 	response.httpStatusText = "OK";
1002 	response.responseText = "";
1003 	response.responseXML = null;
1004 	AjaxTCR.comm._handleResponse(response);
1005 },
1006  
1007 /**
1008  * Automatically called when the image completes loading.  
1009  * Updates httpStatus and httpStatusText
1010  * Checks if the response.cookieName is set.  If so, looks it up and sets responseText to the value.
1011  * Sets responseXML to null
1012  * Calls general _handleResponse function on completion.
1013  * 
1014  * @private
1015  * @param {Object} response The response Object that contains all the settings for the request
1016  */	 
1017 _handleImageResponse : function(response){
1018 	response.httpStatus = 200;
1019 	response.httpStatusText = "OK";
1020 	if (response.cookieName)
1021 		response.responseText = AjaxTCR.comm.cookie.get(response.cookieName);
1022 	else
1023 		response.responseText = document.cookie;	
1024 	response.responseXML = null;
1025 	AjaxTCR.comm._handleResponse(response);
1026 },
1027 
1028  /**
1029   * Private method called if timeout period occurs without the response returning
1030   * calls abortRequest method to abort the requesst and clean up timers 
1031   * invokes any special timeout callback that may be defined.
1032   * Checks to see if we need to retry.
1033   * 
1034   *  @private
1035   *  @param {object} request The object that contains all the settings for the request
1036   */
1037  _timeoutRequest : function(request) { 
1038     /* make sure it is a proper time to abort */
1039     if (request.xhr.readyState != AjaxTCR.comm.DONE && request.xhr.readyState != AjaxTCR.comm.UNSENT)
1040     {
1041 		/* abort the request */
1042 		AjaxTCR.comm.abortRequest(request);
1043 											
1044 		/* Increment total timeouts */
1045 		AjaxTCR.comm.stats._commResults.totalTimeouts++;
1046 											
1047 		/* do we need to retry? */
1048 		if (request.retries)
1049 		  AjaxTCR.comm._retryRequest(request); 	
1050 		else  	  
1051 		{
1052 		  request.onTimeout(request);  /* invoke any timeout callback */
1053 		  AjaxTCR.comm.queue._checkRequestQueue(request);
1054 		}											
1055 	 }
1056    },
1057 
1058 /**
1059   * Private method called every request.progressInterval (default = 1 second) if showProgress is set.
1060   * Updates public timespent variable and invokes any special progress
1061   * callback that may be defined.  Resets the timer.
1062   * 
1063   *  @private
1064   *  @param {object} request The object that contains all the settings for the request
1065   */
1066 _progressRequest: function(request){
1067 	if (!request.abort && !request.received)
1068    	{
1069 		request.timespent =  Math.round((request.timespent + (parseInt(request.progressInterval) / 1000)) * 1000) / 1000;
1070 		request.onProgress(request);
1071 		/* Yes it is ridiculous that we have to clear the timeout that we are in a callback for, but such is IE */
1072 		clearTimeout(request.progressTimerID);
1073 		request.progressTimerID = null;
1074 		request.progressTimerID = window.setTimeout( function(){AjaxTCR.comm._progressRequest(request);}, request.progressInterval);
1075 	}
1076    },
1077 
1078 /** 
1079  * Updates retry count.  Checks to see if we should do another retry.  
1080  * If so, sends off the request and then calls the onRetry callback function.
1081  * If not, calls the onTimeout callback function and sends the next requesst if applicable.
1082  * 
1083  * @private
1084  * @param {Object} request The object that contains all the settings for the request
1085  */									 
1086 _retryRequest : function (request) {
1087 	 /* up our retry count */
1088 	request.retryCount++; 	
1089 										  
1090 	/* make sure we aren't done retrying */
1091 	if (request.retryCount <= request.retries)
1092 	{
1093 	  /* Increment total retries */
1094 	  AjaxTCR.comm.stats._commResults.totalRetries++;
1095 	  AjaxTCR.comm._makeRequest(request);
1096 	  request.onRetry(request);
1097 	}
1098 	else /* stop trying and perform callback */
1099 	{
1100 	 request.onTimeout(request);
1101 	 AjaxTCR.comm.queue._checkRequestQueue(request);
1102 	}
1103  },
1104 										 
1105 
1106 
1107 /**
1108   * Private method called after response comes back no matter what method of communication
1109   * Updates endTime, totalTime, received, requestsOutstanding.  Clears any timers.
1110   * Caches, adds to history, clears status indicators, queues if applicable
1111   * 
1112   * @private
1113   * @param {object} response The object that contains all the settings for the request
1114   */								
1115 _handleResponse : function(response){
1116 	/* Record end time of request */
1117 	response.endTime = (new Date()).getTime();
1118 	response.totalTime = (response.endTime - response.startTime);
1119 	
1120 	/* set a received flag to ensure you don't perform a progress callback after received. */
1121 	response.received = true;
1122 	
1123 	/* clear any timeouts */
1124 	if (response.timeoutTimerID)
1125 	{
1126 	  clearTimeout(response.timeoutTimerID);
1127 	  response.timeoutTimerID = null;
1128 	}
1129 			   
1130 	/* clear our progress indicator */
1131 	if (response.progressTimerID)
1132 	{
1133 	  clearTimeout(response.progressTimerID);
1134 	  response.progressTimerID = null;
1135 	}
1136 	  
1137 	/* decrement outstand request count */
1138 	AjaxTCR.comm._requestsOutstanding--;
1139 	
1140 	/* Cache Response */
1141 	if (!response.fromCache && response.cacheResponse && response.httpStatus == 200 && !response.fail)
1142 		AjaxTCR.comm.cache.add(response.cacheKey, response.responseText);
1143 	
1144 	/* Cache Template */
1145 	if (response.shortTermCacheTemplate && response.httpStatus == 200 && !response.fail)
1146 		AjaxTCR.template.addToCache(response.url, response.responseText);
1147 		
1148 	if (response.history && response.history.saveResponse)
1149 		AjaxTCR.history.addToHistory(response.history.id, "", response.history.title, "", response);
1150 	
1151 	/* Remove Progress Indicators */
1152 	if (response.statusIndicator)
1153 		AjaxTCR.comm._removeProgressStatus(response.statusIndicator);
1154 		
1155 	/* Check to see if we need to wait for another request */
1156 	/* Otherwise just handle callbacks */
1157 	if (response.enforceOrder)
1158 	  AjaxTCR.comm.queue._handleQueue(response);
1159 	else
1160 	  AjaxTCR.comm._handleCallbacks(response);
1161 	  
1162 	/* If Request Queue is being used, send next request */
1163 	AjaxTCR.comm.queue._checkRequestQueue(response);
1164 									},
1165 /**
1166   * Private method called after response comes in to run numerous callbacks.
1167   * Checks http and response status to see which callbacks should be employed.
1168   * Handles template situations as well as direct consumption with outputTarget
1169   * Communication is complete at the end of this call and the response object is nulled out.  
1170   * 
1171   * @private
1172   * @param {object} response The object that contains all the settings for the request
1173   */								 
1174 _handleCallbacks : function(response) {
1175 										 
1176 	 /* clear inProgress flag */
1177  	 response.inProgress = false;
1178 	 
1179 	 /* Calculate Template Data if necessary */
1180 	if (response.template &&  response.templateRender == "client")
1181 	{
1182 		var template = null;
1183 		if (response.template != AjaxTCR.comm.TEMPLATE_SERVER_DEFINED)
1184 		{
1185 			template = AjaxTCR.template.getFromCache(response.template);
1186 			if (!(response.cacheTemplate))
1187 				AjaxTCR.template.removeFromCache(response.template);
1188 		}
1189 		else
1190 		{
1191 			var returnedObject = AjaxTCR.data.decodeJSON(response.responseText);
1192 			if (returnedObject.templateURL && returnedObject.templateText)
1193 			{
1194 				template = returnedObject.templateText;
1195 				if (response.cacheTemplate)
1196 					AjaxTCR.template.addToCache(returnedObject.templateURL, returnedObject.templateText);
1197 			}
1198 			else if (returnedObject.templateText)
1199 				template = returnedObject.templateText;	
1200 			else if (returnedObject.templateURL)
1201 			{
1202 				var template = AjaxTCR.template.getFromCache(returnedObject.templateURL);
1203 				if (!template)
1204 				{
1205 					var templateRequest = AjaxTCR.comm.sendRequest(returnedObject.templateURL, {async:false});
1206 					var template = templateRequest.responseText;
1207 					if (response.cacheTemplate && template)
1208 						AjaxTCR.template.addToCache(returnedObject.templateURL, template);
1209 				} 
1210 			}				
1211 		}
1212 		
1213 		if (template)
1214 		{
1215 			try{
1216 				var translatedResponse = AjaxTCR.template.translateString(template, response.responseText);
1217 				if (translatedResponse)
1218 				{
1219 					response.rawResponseText = response.responseText;
1220 					response.responseText = translatedResponse;
1221 				}
1222 			}
1223 			catch(e){}
1224 		}
1225 	}
1226 	
1227 	 /* Check if user wants to automatically consume output */
1228 	 if (response.outputTarget && response.useRaw && (response.transport == "xhr" || response.transport == "iframe"))
1229 	 {
1230 		var outputTarget = response.outputTarget;
1231 		if (outputTarget && typeof(outputTarget) == "string")
1232 			outputTarget = document.getElementById(outputTarget);
1233 	 
1234 	 	if (response.fail)
1235 			outputTarget.innerHTML = response.fail;
1236 		else
1237 		{
1238 			var span = document.createElement("span");
1239 			span.innerHTML = response.responseText;
1240 			var newParent = span;
1241 			
1242 			switch(response.insertionType.toLowerCase())
1243 			{
1244 				case("insertbefore"):
1245 					var parent = outputTarget.parentNode;
1246 					parent.insertBefore(span, outputTarget);
1247 				break;
1248 				case("insertafter"):
1249 					var parent = outputTarget.parentNode;
1250 					AjaxTCR.util.DOM.insertAfter(parent, span, outputTarget);
1251 				break;
1252 				case("firstchild"):
1253 					var elm = outputTarget.firstChild;
1254 					while (elm != null && elm.nodeType != 1)
1255 						elm = elm.nextSibling;
1256 					
1257 					if (elm != null)
1258 						outputTarget.insertBefore(span, elm);
1259 					else
1260 						outputTarget.appendChild(span);
1261 				break;
1262 				case("lastchild"):
1263 					outputTarget.appendChild(span);
1264 				break;
1265 				default:
1266 					outputTarget.innerHTML = response.responseText;
1267 					newParent = outputTarget;
1268 						
1269 				break;
1270 				
1271 			}
1272 		}
1273 	 }
1274 		
1275 	
1276 	 /* check to see if the user wants a specific callback for this request */
1277 	 if (response["on" + response.httpStatus] && !response.fail)
1278 	 	response["on" + response.httpStatus](response);
1279 	 
1280 	 /* see if it is one of our retry statuses */
1281 	 if (response.retries)
1282 	 {
1283 		for (var i=0;i<AjaxTCR.comm._networkErrorStatus.length;i++)
1284 		{
1285 			if (response.httpStatus == AjaxTCR.comm._networkErrorStatus[i])
1286 			{
1287 				AjaxTCR.comm._retryRequest(response);
1288 				return;
1289 			}
1290 		}
1291 	 }
1292 	 
1293 	/* call either success or fail callback */
1294 	if (response.httpStatus == 200)
1295 	{
1296 		/*if they specified expected content type, we check for that.*/
1297 		if (response.fail)
1298 			AjaxTCR.comm._handleFail(response, response.fail);
1299 	    else if (response.responseContentType && response.transport == "xhr")
1300 	    {
1301 			var responseContentType = response.xhr.getResponseHeader("Content-Type");
1302 			responseContentType = responseContentType.substring(0, responseContentType.indexOf(";"));
1303 			if (responseContentType != response.responseContentType)
1304 	            AjaxTCR.comm._handleFail(response, "Wrong Content-Type: " + responseContentType );
1305 			else if (response.responseContentType == "text/xml" && (response.responseXML == null || response.responseXML.childNodes.length == 0 || response.responseXML.childNodes[0].nodeName == "parsererror"))
1306 				AjaxTCR.comm._handleFail(response, "Invalid XML Data");
1307 			else
1308 				AjaxTCR.comm._handleSuccess(response);
1309 		}
1310 		else		
1311 			AjaxTCR.comm._handleSuccess(response);
1312 	}
1313 	else
1314 	 	AjaxTCR.comm._handleFail(response, response.httpStatus + " " + response.httpStatusText);
1315 	
1316 	
1317 	
1318 	  
1319     /* clear out the response */
1320 	response = null;
1321                                	      },
1322 									  
1323  /**
1324   * Private method called upon request failure.  Updates the statistics object and calls the onFail callback.
1325   * 
1326   *  @private
1327   *  @param {object} response The object that contains all the settings for the request
1328   *  @param {string} message
1329   */
1330  _handleFail : function(response, message) {
1331 	/* Increment total fails */
1332     AjaxTCR.comm.stats._commResults.totalFails++;
1333 	response.fail = message;
1334 	
1335 	/* Save fail details */
1336 	var fail = {};
1337 	fail.url = response.url;
1338 	fail.status = response.httpStatus;
1339 	fail.message = message;
1340 	AjaxTCR.comm.stats._commResults.requestFails.push(fail);
1341 	
1342 	response.onFail(response, message);
1343 },
1344    
1345 /**
1346   * Private method to handle success responses  Updates the statistics log and then calls the proper success callback.
1347   *  
1348   * @private
1349   * @param {Object} response The object that contains all the settings for the request 
1350   */
1351  _handleSuccess : function(response) {
1352 	/* Increment total success */
1353 	AjaxTCR.comm.stats._commResults.totalSuccesses++;
1354 	if (response.isPrefetch)
1355 		response.onPrefetch(response);
1356 	else
1357 		response.onSuccess(response);
1358 },
1359  
1360  /**
1361   * Private method to check for response status, clear any timeouts and invoke callbacks for given readyState.
1362   * In readyState == 4, Checks signature if response is supposed to be signed before handling any data.
1363   * Sets the httpStatus, status, responseText, and responseXML objects
1364   * 
1365   *  @private 
1366   *  @param {Object} response The object that contains all the settings for the request
1367   */
1368  _handleReadyStateChange : function(response) { 	
1369 	/* check if abort flag is set, if so bail out */	
1370 	if (response.abort)
1371 	   return; 
1372 										 
1373 	 /* Check each readyState */
1374 	if (response.xhr.readyState == AjaxTCR.comm.OPEN && response.onOpen)
1375 		 response.onOpen(response);
1376     else if (response.xhr.readyState == AjaxTCR.comm.SENT && response.onSent)
1377 		 response.onSent(response);
1378 	else if (response.xhr.readyState == AjaxTCR.comm.LOADING && response.onLoading)
1379 		 response.onLoading(response);
1380     else if (response.xhr.readyState == AjaxTCR.comm.DONE) 
1381 	{
1382 		if (response.signedResponse)
1383 		{
1384 			var signature = response.xhr.getResponseHeader("Content-MD5");
1385 			var verifySignature = AjaxTCR.data.encodeMD5(response.xhr.responseText);
1386 			if (signature != verifySignature)
1387 				response.fail = "Response Packet Compromised.";
1388 		}
1389 		
1390 		if (response.onReceived && !response.fail)
1391 			response.onReceived(response);
1392 		
1393 		/* Danger: Firefox problems so we try-catch here */
1394 	 	try { response.httpStatus = response.xhr.status; response.httpStatusText = response.xhr.statusText} catch(e) {response.httpStatus=3507;response.httpStatusText="Unknown Loss";}
1395 		response.responseText = response.xhr.responseText;
1396 		response.responseXML = response.xhr.responseXML;
1397 		
1398 		AjaxTCR.comm._handleResponse(response);
1399 	}
1400 },
1401 
1402 
1403 /**
1404  * Creates the status indicator for the page based on the settings passed in.
1405  * 
1406  * @private 
1407  * @param {object} statusIndicator  the status object
1408  * @config {object} target The DOM reference of the place to put the indicator
1409  * @config {string} type Either text or image.  The Type of indicator to use
1410  * @config {string} text In the case of text, this is the message to put into the indicator
1411  * @config {integer} border In the case of image, this is the border for the image
1412  * @config {string} imgSrc In the case of image, this is the src of the image to use.
1413  */
1414 _setStatus : function(statusIndicator){
1415 	if (statusIndicator.target)
1416 	{
1417 		if (typeof(statusIndicator.target) == "string")
1418 			statusIndicator.target = document.getElementById(statusIndicator.target);
1419 				
1420 	    if (statusIndicator.type == "text")
1421 	    {
1422 	        var statusDiv = document.createElement("div");
1423 	        statusDiv.innerHTML = statusIndicator.text;
1424 	        statusIndicator.target.appendChild(statusDiv);
1425 			statusIndicator.element = statusDiv;
1426 	    }
1427 	    else if (statusIndicator.type == "image")
1428 	    {
1429 	        var statusImg = document.createElement("img");
1430 	        statusImg.id = "progressBar";
1431 			if (statusIndicator.border)
1432 	        	statusImg.border=statusIndicator.border;
1433 	        statusImg.src = statusIndicator.imgSrc;
1434 	        statusIndicator.target.appendChild(statusImg);
1435 			statusIndicator.element = statusImg;
1436 	    }
1437 	 }	
1438 },
1439 							
1440 /**
1441  * sets status based on the statusIndicator.progress object
1442  * 
1443  * @private 
1444  * @param  statusIndicator - the status object
1445  */		
1446 _setProgressStatus : function(statusIndicator){
1447 	if (statusIndicator.progress)
1448 		return AjaxTCR.comm._setStatus(statusIndicator.progress);	
1449 },
1450 
1451 /**
1452  * removes the pased in  status object
1453  * 
1454  * @private 
1455  * @param  statusIndicator - the status object to remove
1456  */		
1457 _removeStatus : function(statusIndicator){
1458 	if (statusIndicator.element)
1459 	{
1460 		statusIndicator.element.parentNode.removeChild(statusIndicator.element);
1461 		statusIndicator.element = null;
1462 	}
1463 },
1464 
1465 /**
1466  * removes the status set by the statusIndicator.progress object
1467  * 
1468  * @private 
1469  * @param  statusIndicator - the status object
1470  */		
1471 _removeProgressStatus : function (statusIndicator){
1472 	if (statusIndicator.progress)
1473 		AjaxTCR.comm._removeStatus(statusIndicator.progress);
1474 }
1475 
1476 };
1477 
1478 /*************************************  AjaxTCR.comm.cache *****************************/
1479 /**
1480  * The caching class of the library.  Is called directly from AjaxTCR.comm, but can also be called by the user.
1481  * 
1482  * @class AjaxTCR.comm.cache
1483  * @static
1484  */
1485 AjaxTCR.comm.cache = {
1486 
1487 /****************************************** Private Properties ****************************************************/
1488 
1489 /** The cache object.  
1490  * @private
1491  */
1492 _cache : new Array(),
1493 
1494 /** Caching Options w/defaults set 
1495  * @private
1496  */
1497 _cacheOptions : {
1498 	/* The max number of items to store in the cache */
1499 	size : 100,
1500 	/* The default algorithm for removing items.  The choices are LRU, FIFO, and LFU */
1501 	algorithm: "LRU",
1502 	/* The default number of minutes an item can stay in the cache.  Set to -1 for forever */
1503 	expires: 60
1504 },
1505 
1506 /*************************************  Public Cache Methods *****************************/
1507 	
1508 /**
1509  * Adds a key/value pair to the cache.  Removes any itmes first if necessary.
1510  * 
1511  * @param {string} key The key to reference the stored object
1512  * @param {object} val The value to save
1513  */		
1514 add : function(key, val){
1515 	if (AjaxTCR.comm.cache._cache.length >= AjaxTCR.comm.cache._cacheOptions.size)
1516 	{
1517 		var algorithm = AjaxTCR.comm.cache._cacheOptions.algorithm;
1518 		//we need to remove an item before adding another one.
1519 		if ( algorithm == "FIFO")
1520 			AjaxTCR.comm.cache._cache.splice(0, 1);
1521 		else if (algorithm == "LFU")
1522 		{
1523 			var removeIndex = -1;
1524 			for (var i=0;i<AjaxTCR.comm.cache._cache.length;i++)
1525 			{
1526 				if (removeIndex == -1 || AjaxTCR.comm.cache._cache[removeIndex].totalAccessed > AjaxTCR.comm.cache._cache[i].totalAccessed)
1527 					removeIndex = i;
1528 			}
1529 			
1530 			AjaxTCR.comm.cache._cache.splice(removeIndex,1);
1531 		}
1532 		else if (algorithm == "LRU")
1533 		{
1534 			var removeIndex = -1;
1535 			for (var i=0;i<AjaxTCR.comm.cache._cache.length;i++)
1536 			{
1537 				if (removeIndex == -1 || AjaxTCR.comm.cache._cache[removeIndex].lastAccessed > AjaxTCR.comm.cache._cache[i].lastAccessed)
1538 					removeIndex = i;
1539 			}
1540 			
1541 			AjaxTCR.comm.cache._cache.splice(removeIndex,1);
1542 		}
1543 	} 
1544 
1545 	var item = AjaxTCR.comm.cache._createCacheItem(key, val);
1546 	AjaxTCR.comm.cache._cache.push(item);
1547 },
1548 
1549 /**
1550  * Public method that resets the caches
1551  * 
1552  */		
1553 clear : function(){
1554 	AjaxTCR.comm.cache._cache = new Array();
1555 },
1556 
1557 /**
1558  * Public method to fetch an object based on the key.  Checks to see if the object has expired before returning it.
1559  * 
1560  * @param {string} key The key to reference the stored object
1561  * @return {object} The stored value
1562  * 
1563  */	
1564 get: function(key){
1565 	var cacheObject = null;
1566 	/* Search for item */
1567 	for (var i=0;i<AjaxTCR.comm.cache._cache.length;i++)
1568 	{
1569 		if (AjaxTCR.comm.cache._cache[i].key == key)
1570 		{
1571 			cacheObject = AjaxTCR.comm.cache._cache[i];
1572 			break;
1573 		}
1574 	}
1575 	
1576 	if (cacheObject)
1577 	{
1578 		/* Update the properties */
1579 		cacheObject.lastAccessed = new Date();
1580 		cacheObject.totalAccessed++;
1581 		
1582 		/* Ensure it hasn't expired */
1583 		if (AjaxTCR.comm.cache._cacheOptions.expires != -1)
1584 		{
1585 			var timeAdded = cacheObject.added;
1586 			var now = new Date();
1587 			now.setMinutes(now.getMinutes() - AjaxTCR.comm.cache._cacheOptions.expires);
1588 			if (now > timeAdded)
1589 			{
1590 				AjaxTCR.comm.cache.remove(item.key);
1591 				cacheObject = null;
1592 			}			
1593 		}
1594 	}
1595 	
1596 	if (cacheObject)
1597 		return cacheObject.value;
1598 	else 
1599 		return null;
1600 },
1601 
1602 												  
1603 /**
1604  * Returns the entire cache
1605  * 
1606  * @return {object} The cache Object
1607  */
1608 getAll : function(){ 
1609 	return AjaxTCR.comm.cache._cache;
1610 },
1611 
1612 /**
1613  * Returns the number of items currently in the cache
1614  * 
1615  * @return {integer} The number of items in the cache
1616  */
1617 getSize : function(){ 
1618 	return AjaxTCR.comm.cache._cache.length;
1619 },
1620 
1621 /**
1622  * Removes an item from the cache based on the key passed in
1623  * 
1624  * @param {string} key The key to reference the item to remove.
1625  */	
1626 remove : function(key){
1627 	for (var i=0;i<AjaxTCR.comm.cache._cache.length;i++)
1628 	{
1629 		if (AjaxTCR.comm.cache._cache[i].key == key)
1630 		{
1631 			AjaxTCR.comm.cache._cache.splice(i,1);
1632 			break;
1633 		}
1634 	}
1635 },
1636 
1637 
1638 /** 
1639  * Changes the default options for the caching mechanism
1640  * @param {Object} the options to set
1641  * @config {integer} size The number of items to store in the cache  Default is 100
1642  * @config {string} algorithm The algorithm to use to decide which item should be removed next.  
1643  * Options are LRU (Least Recently Used), FIFO (First In First Out), and LFU(Least Frequently Used)  Default is LRU
1644  * @config {integer} expires The number of minutes an item can stay in the cache.  Default is 60.  Use -1 to set forever.
1645  */
1646 setOptions : function(cacheOptions){
1647 	/* apply options defined by user */
1648     for (option in cacheOptions)
1649     	AjaxTCR.comm.cache._cacheOptions[option] = cacheOptions[option];
1650  },
1651  
1652 								
1653 
1654 /*************************************  Private Cache Methods *****************************/
1655 
1656 /**
1657  * When a page is returned from the cache, this method is called to initialize values
1658  * so that the handlResponse function will work properly.
1659  * 
1660  * @private
1661  * @param {Object} response The object that contains all the settings for the request
1662  * @param {Object} responseText The value stored in the cache
1663  */
1664 _handleCacheResponse : function(response, responseText){
1665 	response.xhr = {};
1666 	response.xhr.responseText = response.responseText = responseText;
1667 	response.xhr.responseXML = response.responseXML = null;
1668 	if (responseText.indexOf("<?xml") > -1)
1669 		response.xhr.responseXML = response.responseXML = AjaxTCR.data.serializeXMLString(responseText);
1670 	
1671 	response.xhr.status = response.httpStatus = 200; 
1672 	response.xhr.statusText = response.httpStatusText = "OK";
1673 	
1674 	response.fromCache = true;
1675 	AjaxTCR.comm._handleResponse(response);
1676 },
1677 
1678 
1679 /**
1680  * createCacheItem - Private method that creates a cache object based on the given key and val
1681  * 
1682  * @private 
1683  * @param {string} key The key to set cacheObject.key to
1684  * @param {object} val The value to set cacheObject.value to
1685  * @return {object} The cacheObject
1686  */								
1687 _createCacheItem : function(key, val){
1688 	var cacheObject = {};
1689 	cacheObject.key = key;
1690 	cacheObject.value = val;
1691 	cacheObject.lastAccessed = new Date();
1692 	cacheObject.added = new Date();
1693 	cacheObject.totalAccessed = 1;
1694 	return cacheObject;
1695 }
1696 
1697 
1698 };
1699 
1700 /*************************************  AjaxTCR.comm.queue *****************************/
1701 /**
1702  * The queuing class of the library.  Is called directly from AjaxTCR.comm.
1703  * 
1704  * @class AjaxTCR.comm.queue
1705  * @static
1706  */
1707 AjaxTCR.comm.queue = {
1708 
1709 /****************************************** Private Properties ****************************************************/
1710 
1711 /** The responseQueue Object 
1712  * @private
1713  */ 
1714  _responseQueue : {  queue: new Array(), currentIndex: 0, maxID: 0},
1715  
1716  /** The requestQueue Array 
1717  * @private
1718  */  
1719  _requestQueue : new Array(),
1720  
1721  /** The requestQueue counter  
1722  * @private
1723  */ 
1724  _requestQueueID: 0,
1725  
1726  /** The number of active requests to send out when using request Queue */
1727  requestQueueConcurrentRequests : 1,
1728  
1729 /****************************************** Public Queue Methods ****************************************************/
1730 									 
1731 /**
1732  * Public method to add a request to the request queue.  
1733  * If the request limit isn't being met yet, the request is sent right away, otherwise, it gets added to the queue.
1734  * If the priority is set and not to "normal", it places the new 
1735  * request at the proper place.  Otherwise, it inserts it at the end of the queue.
1736  * 
1737  * @param {string} url - the url to add to the request queue
1738  * @param {object} options - the options object to send to sendRequest
1739  * @param {string} [priority] - the priority level of the entry - Choices are "normal", "next", and "faster"
1740  * @return {integer} The requestQueueid of the request  
1741  */
1742 add : function(url, options, priority) { 
1743 	if (options)
1744 		options.inQueue = true;
1745 	else 
1746 		options = {inQueue:true};
1747 										
1748 	if (!priority)
1749 		options.priority = "normal";
1750 	else
1751 		options.priority = priority.toLowerCase();
1752 		
1753 	/* Add Id */
1754 	options.requestQueueID = ++AjaxTCR.comm.queue._requestQueueID;
1755 		
1756 	/* See if we should send it or add it to the queue */			
1757 	if (AjaxTCR.comm.stats.getRequestCount("active") >=  AjaxTCR.comm.queue.requestQueueConcurrentRequests)
1758 	{	
1759 		var request = {url: url, options: options};
1760 		if (options.priority == "next")
1761 			AjaxTCR.comm.queue._requestQueue.unshift(request);
1762 		else if (priority && priority == "faster")
1763 		{
1764 			var set = false;
1765 			for (var i=0;i<AjaxTCR.comm.queue._requestQueue.length;i++)
1766 			{
1767 				if (AjaxTCR.comm.queue._requestQueue[i].options.priority == "normal")
1768 				{
1769 					AjaxTCR.comm.queue._requestQueue.splice(i, 0, request);
1770 					set = true;
1771 					break;
1772 				}
1773 			}
1774 			/* If nothing is normal, add to the end */
1775 			if (!set)
1776 				AjaxTCR.comm.queue._requestQueue.push(request);
1777 		}
1778 		else
1779 			AjaxTCR.comm.queue._requestQueue.push(request);
1780 	}
1781 	else
1782 		AjaxTCR.comm.sendRequest(url, options);
1783 		
1784 	return options.requestQueueID;
1785 },	 
1786 
1787 /**
1788  * A public method that clears out the request queue of any pending requests 
1789  * 
1790  */										   
1791 clear: function()	{ 
1792 	AjaxTCR.comm.queue._requestQueue.length = 0; 
1793 },
1794 
1795 /**
1796  * Returns the item from the queue  
1797  * 
1798  * @param {integer} ID - ID of request to return
1799  * @return  {object} The object that is stored in the queue
1800  * 
1801  */										   
1802 get : function(ID)	{ 
1803 	for (var i=0;i<AjaxTCR.comm.queue._requestQueue.length;i++)
1804 	{
1805 		if ( AjaxTCR.comm.queue._requestQueue[i].options.requestQueueID == ID)
1806 			return AjaxTCR.comm.queue._requestQueue[i];
1807 	}
1808 	
1809 	return null;
1810 },	  
1811 	
1812 /**
1813  * Returns the whole request queue
1814  * 
1815  * @return {object} the request queue
1816  */								
1817 getAll : function(){
1818 	return AjaxTCR.comm.queue._requestQueue;
1819 },
1820 	
1821 /**
1822  * Returns the position in the queue of the passed in ID.  
1823  * 
1824  *  @param ID - ID of request that you wish to check
1825  *  @return {integer} Returns -1 if not in queue.  Otherwise returns th location in the queue.  Starts at 0.
1826  * 
1827  */										   
1828 getPosition : function(ID)	{ 
1829 	for (var i=0;i<AjaxTCR.comm.queue._requestQueue.length;i++)
1830 	{
1831 		if ( AjaxTCR.comm.queue._requestQueue[i].options.requestQueueID == ID)
1832 			return i;
1833 	}
1834 	
1835 	return -1;
1836 },	  
1837 	
1838 /**
1839  * Returns the length request queue
1840  * 
1841  * @return {integer} the request queue length
1842  */								
1843 getSize : function(){
1844 	return AjaxTCR.comm.queue._requestQueue.length;
1845 },
1846 	
1847 /**
1848  * Removes the option with the given requestQueueID from the request Queue
1849  * 
1850  *  @param {integer{ ID  ID of request to be removed from queue
1851  * 
1852  */										   
1853 remove : function(ID)	{ 
1854 	for (var i=0;i<AjaxTCR.comm.queue._requestQueue.length;i++)
1855 	{
1856 		if ( AjaxTCR.comm.queue._requestQueue[i].options.requestQueueID == ID)
1857 		{
1858 			var ret = AjaxTCR.comm.queue._requestQueue[i];
1859 			AjaxTCR.comm.queue._requestQueue.splice(i, 1);
1860 			return ret;
1861 		}
1862 	}
1863 	
1864 	return false;
1865 },		
1866 									
1867 								   
1868 /****************************************** Private Queue Methods ****************************************************/									   
1869 
1870 /**
1871  * A private method that looks to see if a request queue is in use and sends the next
1872  * request off for processing if there is one
1873  * 
1874  * @private 
1875  * @param {object} response The object that contains all the settings for the request
1876  * 
1877  */									   
1878 _checkRequestQueue : function(response){
1879 	/* If Request Queue is being used, send next request */
1880 	if (response.inQueue && AjaxTCR.comm.queue._requestQueue.length > 0)
1881 	{		
1882 		var nextRequest = AjaxTCR.comm.queue._requestQueue.shift();
1883 		AjaxTCR.comm.sendRequest(nextRequest.url, nextRequest.options);
1884 	}
1885 },
1886 							
1887 /**
1888   * Private method called after response comes in if request is in the response queue.
1889   * Adds response to response queue and then calls callbacks for any that are able to move forward
1890   * 
1891   * @param {object} response The object that contains all the settings for the request
1892   */
1893  									 
1894 _handleQueue: function(response){
1895 	/* add response into queue */
1896 	AjaxTCR.comm.queue._responseQueue.queue[response.responseQueueID] = response;
1897 									
1898 	/* loop thru queue handling any received requests up to current point  */
1899 	while (AjaxTCR.comm.queue._responseQueue.queue[AjaxTCR.comm.queue._responseQueue.currentIndex] != undefined)
1900 	{
1901 	  AjaxTCR.comm._handleCallbacks(AjaxTCR.comm.queue._responseQueue.queue[AjaxTCR.comm.queue._responseQueue.currentIndex]);
1902 	  AjaxTCR.comm.queue._responseQueue.currentIndex++;
1903 	}
1904 }
1905 
1906 
1907 };
1908 
1909 /*************************************  AjaxTCR.comm.stats *****************************/
1910 /**
1911  * The statistics class of the library.  Collects statistics regarding total requests, timeouts, retries, successes, and failures
1912  * Can serialize and send this data to your server.
1913  * 
1914  * @class AjaxTCR.comm.stats
1915  * @static
1916  */
1917 AjaxTCR.comm.stats = {
1918 
1919  /** stats object to hold results
1920   * @private
1921   * @config {integer} totalRequests
1922   * @config {integer} totalTimeouts 
1923   * @config {integer} totalRetries
1924   * @config {integer} totalSuccesses
1925   * @config {integer} totalFails
1926   * @config {array} requestFails
1927   */
1928  _commResults : {
1929  totalRequests : 0,
1930  totalTimeouts : 0,
1931  totalRetries : 0,
1932  totalSuccesses : 0,
1933  totalFails : 0,
1934  requestFails : new Array()},
1935  
1936 
1937 /**
1938  * Sets up system to send results to the passed in URL on page unload.  It passes them as a POST with an application/json body.
1939  * 
1940  * @param {Object} url URL to send the results to
1941  */ 
1942 collect : function (url)	{
1943 	var sendConnectionStats = function(){
1944 		var results = AjaxTCR.comm.stats.get();
1945 		if (results.totalRequests > 0)
1946 		{
1947 			var payload = AjaxTCR.data.encodeJSON(results);
1948 			AjaxTCR.comm.sendRequest(url, {method:"POST",payload:payload,requestContentType:"application/json",oneway:true});
1949 		}
1950 	};
1951 	
1952 	if(window.attachEvent)	
1953  		window.attachEvent('onunload', sendConnectionStats);
1954  	else
1955 		window.addEventListener('unload', sendConnectionStats, false);
1956 
1957 },
1958 										
1959 /**
1960  * Returns the whole statistics object
1961  * 
1962  * @return {object} the statistics object
1963  */										
1964 get : function()	{
1965 	return AjaxTCR.comm.stats._commResults;
1966 },
1967 
1968 
1969 /**
1970  * Public method acting as simple getter for the count of requests currently out
1971  * 
1972  * @param {string} type The type of requests that you want the count on.  Options are "queued", "active", and "all"
1973  * @return - the number of outstanding requests
1974  */
1975 getRequestCount : function(type) { 
1976 	if (type == "queued")
1977 		return AjaxTCR.comm.queue.getSize();
1978 	else if (type == "active")
1979 		return AjaxTCR.comm._requestsOutstanding;
1980 	else	
1981 		return (AjaxTCR.comm.queue.getSize() + AjaxTCR.comm._requestsOutstanding); 
1982 }
1983 
1984 
1985 };
1986 
1987 /*************************************  AjaxTCR.comm.cookie *****************************/
1988 /**
1989  * The cookie class of the library.  Allows setting and retrieving of cookies
1990  * 
1991  * @class AjaxTCR.comm.cookie
1992  * @static
1993  */
1994 AjaxTCR.comm.cookie = {
1995 
1996 /**
1997  * Gets the value of a cookie with the given name.
1998  * 
1999  * @param {String} name The name of the cookie to find
2000  * @return {String} The value of the specified cookie
2001  */
2002 get : function(name){
2003 	var fullname = name + "=";
2004 	var cookies = document.cookie.split(';');
2005 	for(var i=0;i < cookies.length;i++)
2006 	{
2007 		var cookieNV = cookies[i];
2008 	    while (cookieNV.charAt(0)==' ') 
2009 											cookieNV = cookieNV.substring(1);
2010 	    if (cookieNV.indexOf(fullname) == 0) 
2011 											return cookieNV.substring(fullname.length);
2012 	}
2013 	return null;
2014 },
2015 
2016 /**
2017  * Create a cookie using document.cookie
2018  * 
2019  * @param {string} key  The cookie name
2020  * @param {string} value The cookie value
2021  * @param {string} expires The date of expiration.  If not set, the cookie expires when the browser is closed.
2022  * @param {string} [path] The path to store the cookie.  If not set, it is stored at "/"
2023  */
2024 set: function(key, value, expires, path){
2025 
2026 	if (!path)
2027 		path = "/";
2028 	
2029 	document.cookie = key+"="+value+"; expires=" + expires +"; path=" + path;
2030 	
2031 },
2032 
2033 /**
2034  * Removes the cookie specified by key.
2035  * @param {Object} key The name of the cookie to remove
2036  * @param {Object} [path] The path where the cookie is stored.  If not set, it checks "/"
2037  */
2038 remove: function(key, path){
2039 
2040 	if (!path)
2041 		path = "/";
2042 		
2043 	var now = new Date();
2044 	now.setYear(now.getYear()-1);
2045 	var expires = now.toGMTString(); 
2046 	document.cookie = key+"=; expires=" + expires +"; path=" + path;
2047 
2048 }
2049 
2050 };
2051 
2052 /*************************************  AjaxTCR.history *****************************/
2053 /**
2054  * The history class of the library.  History is called directly from communications, but can also be called by the user.
2055  * If history is going to be used, you must call AjaxTCR.history.init() before  usage.
2056  * 
2057  * @class AjaxTCR.history
2058  * @static
2059  */
2060 AjaxTCR.history = {
2061 
2062 /** contstants
2063  * @private
2064  */
2065 BACK_GUARD_DISABLED : 0,
2066 BACK_GUARD_INITIALIZED : 1,
2067 BACK_GUARD_ENABLED : 2,
2068 
2069 /** Current State of application 
2070  * @private
2071  */
2072 _currentState : "",
2073 
2074 /** function to call on statechange callback.  Specifed in init()
2075  * @private
2076  */
2077 _onStateChangeCallback : function(){},
2078 
2079 /** html file that iframe points to.  Does not need any contents, but does need to exist 
2080  * @private
2081  */
2082 _iframeSrc : "blank.html",
2083 
2084 /** Boolean indicated whether to handle history 
2085  * @private
2086  */
2087 _historyEnabled : false,
2088 
2089 /** The history array 
2090  * @private
2091  */
2092 _history : new Array(),
2093 
2094 /** The current position in the history array 
2095  * @private
2096  */
2097 _historyPosition : 0,
2098 
2099 /** Boolean indicated if library should warn on page unload
2100  * @private
2101  */
2102 _backGuardEnabled: 0,
2103 
2104 /** Message to alert when warning on page unload 
2105  * @private
2106  */
2107 _backGuardMessage: "Are you sure you wish to end your session?",
2108 
2109 /**
2110  * Function that must be called before using the history mechanics.
2111  * It sets historyEnabled to true, sets the callback function,
2112  * sets up the timers to check for history change
2113  * and checks to see if the initial state is a bookmark
2114  * 
2115  * @param {function} onStateChangeCallback The function to call if there is a state change outside of the XHR call  
2116  * @param {Object} initState The data to pass back to the callback function
2117  */
2118 init : function(onStateChangeCallback, initState){
2119 	if (AjaxTCR.history._historyEnabled)
2120 		return;
2121 	else
2122 		AjaxTCR.history._historyEnabled = true;
2123 		
2124 		
2125 	AjaxTCR.history._onStateChangeCallback = onStateChangeCallback;
2126 	
2127 	if(!(navigator.userAgent.toLowerCase().indexOf("msie")>-1))
2128 		AjaxTCR.history._checkHash();
2129 	else 
2130 		AjaxTCR.history._checkHashIE();
2131 		
2132 	if (!window.location.hash)
2133 		AjaxTCR.history.addToHistory("AjaxTCRinit", initState, document.title);
2134 	else if ((navigator.userAgent.toLowerCase().indexOf("msie")>-1))
2135 		AjaxTCR.history._initIE();
2136 	
2137 },
2138   
2139 /**
2140  * Adds an item to the browser history.  Saves the title, position in history, and the data for later retrieval.
2141  *  
2142  * @param {string}  id  the key to store the history item under
2143  * @param {object} data  the data field to be returned to the callback function.  
2144  * @param {title}  [title]  the title to change the page to
2145  * @param {string} [url]  the url to request when page reloads.  
2146  * @param {object}  [options]  the options for the request
2147  */
2148 addToHistory : function(id, data, title, url, options){
2149 
2150 	if (id != "AjaxTCRinit")
2151 	{
2152 		window.location.hash =AjaxTCR.data.encodeValue(id).replace(/%2F/g, '/');;
2153 		AjaxTCR.history._currentState = window.location.hash.substring(1);
2154 	}
2155 	
2156 	var optionsString = "";
2157 	var safeStateName = id.replace(/[%\+]/g, "");
2158 	
2159 	if (title)
2160 		document.title = title;
2161 		
2162 	if (options)
2163 	{
2164 		options.url = url;
2165 		if (navigator.userAgent.toLowerCase().indexOf("msie")>-1)
2166 			options.responseXML = "iebug";
2167 		
2168 		optionsString = AjaxTCR.data.encode64(AjaxTCR.data.encodeJSON(options));
2169 	}
2170 	else if (data)
2171 	{
2172 		options = {value: data};
2173 		optionsString = AjaxTCR.data.encode64(AjaxTCR.data.encodeJSON(options));
2174 	}
2175 	
2176 	//update position
2177 	AjaxTCR.history._historyPosition++;
2178 	
2179 	if(navigator.userAgent.toLowerCase().indexOf("msie")>-1)
2180 	{
2181 		var iframe = document.getElementById("ieFix");
2182 		var html = '<html><head><title>IE History</title><STYLE>.userData {behavior:url(#default#userdata);}</STYLE></head><body><div class="userData" id="persistDiv"></div><div id="currentState">' + AjaxTCR.history._currentState + '</div></body></html>';
2183 		var doc = iframe.contentWindow.document;
2184 		doc.open();
2185 		doc.write(html);
2186 		doc.close();
2187 		
2188 		var persistDiv = doc.getElementById("persistDiv");
2189 		AjaxTCR.storage.add("request", optionsString, persistDiv, safeStateName);
2190 		if (title) 
2191 			AjaxTCR.storage.add("title", title, persistDiv, safeStateName);
2192 		AjaxTCR.storage.add("position", AjaxTCR.history._historyPosition, persistDiv, safeStateName);
2193 	}	
2194 	else if (safeStateName)
2195 	{
2196 		AjaxTCR.storage.add(safeStateName, optionsString);
2197 		if (title)
2198 			AjaxTCR.storage.add(safeStateName + "title", title);
2199 		AjaxTCR.storage.add(safeStateName + "position", AjaxTCR.history._historyPosition);
2200 	}
2201 	
2202 	/* Finally push onto history stack */
2203 	var historyItem = {id: AjaxTCR.history._currentState, title: title};
2204 	var diff = AjaxTCR.history._history.length - AjaxTCR.history._historyPosition + 1;
2205 	if (diff > 0)
2206 		AjaxTCR.history._history.splice(AjaxTCR.history._historyPosition-1,diff); 
2207 		
2208 	AjaxTCR.history._history.push(historyItem);
2209 },	
2210 
2211 /**
2212  * Returns the entire history array
2213  * @return {array} The history array
2214  */
2215 getAll : function(){
2216 	return AjaxTCR.history._history;
2217 },
2218 
2219 /**
2220  * Returns the current position in the history stack
2221  * @return {integer} The current position in the history stack
2222  */
2223 getPosition : function(){
2224 	return AjaxTCR.history._historyPosition;
2225 },
2226 
2227 /**
2228  * Sets up the system that will alert the user when they leave the page that they are leaving the application
2229  * 
2230  * @param {string} [message] The message to alert the user
2231  * @param {Boolean} [immediate] If set to true, it immediately sets up the system.  Otherwise, it sets it up on first sendRequest call.
2232  */
2233 enableBackGuard: function(message, immediate){
2234 	if (AjaxTCR.history._backGuardEnabled)
2235 		return;
2236 	
2237 	if (message != null && typeof(message) != "undefined")
2238 		AjaxTCR.history._backGuardMessage = message;
2239 		
2240 	if (immediate)
2241 		AjaxTCR.history._activateBackGuard();
2242 	else
2243 		AjaxTCR.history._backGuardEnabled = AjaxTCR.history.BACK_GUARD_INITIALIZED;
2244 		
2245 },
2246 
2247 /**
2248  * Sets the onbeforeunload handler to alert when the user tries to leave the page
2249  * @private
2250  */
2251 _activateBackGuard: function(){
2252 	var message = AjaxTCR.history._backGuardMessage;
2253 	window.onbeforeunload = function () {return message;}
2254 	AjaxTCR.history._backGuardEnabled = AjaxTCR.history.BACK_GUARD_ENABLED;
2255 },
2256 
2257 /**
2258  * Creates html page to write to the iframe in the case of IE
2259  * 
2260  * @private 
2261  */
2262 _initIE: function(){
2263 	var iframe = document.getElementById("ieFix");
2264 	var html = '<html><head><title>IE History</title><STYLE>.userData {behavior:url(#default#userdata);}</STYLE></head><body><div class="userData" id="persistDiv"></div><div id="currentState">' + window.location.hash.substring(1); + '</div></body></html>';
2265 	var doc = iframe.contentWindow.document;
2266 	doc.open();
2267 	doc.write(html);
2268 	doc.close();
2269 },
2270 									  
2271 
2272 /**
2273  * Checks to see if the state that is set in the IE Iframe matches the current state as set in the library.  If they are not a match,
2274  * update the state of the page
2275  * 
2276  * @private
2277  */
2278 _checkHashIE : function (){
2279 	var iframe = document.getElementById("ieFix");
2280 	if ( !iframe.contentWindow || !iframe.contentWindow.document ) 
2281 	{
2282        setTimeout( _checkHashIE, 10 );
2283        return;
2284     }
2285 
2286 	var doc = iframe.contentWindow.document;
2287 	var keyObj = doc.getElementById("currentState");
2288 	var key = "";
2289 	if (keyObj)
2290 		key = keyObj.innerText;
2291 		
2292 	if (key != AjaxTCR.history._currentState)
2293   	{
2294 		AjaxTCR.history._currentState = key;
2295     	window.location.hash = key;
2296 		
2297 		var persistDiv = doc.getElementById("persistDiv");
2298 		var safeStateName = AjaxTCR.history._currentState.replace(/[%\+]/g, "");
2299 		if (!safeStateName)
2300 			safeStateName = "AjaxTCRinit";
2301 		
2302 		var title =  AjaxTCR.storage.get("title", persistDiv, safeStateName);
2303 		if (title) 
2304 			document.title = title;
2305 		
2306 		AjaxTCR.history._historyPosition =  AjaxTCR.storage.get("position", persistDiv, safeStateName);	
2307 		//check for bookmarked history position
2308 		if (AjaxTCR.history._historyPosition > AjaxTCR.history._history.length)
2309 		{
2310 			AjaxTCR.history._historyPosition = 1;
2311 			AjaxTCR.storage.add("position", AjaxTCR.history._historyPosition, persistDiv, safeStateName);
2312 			var historyItem = {id: AjaxTCR.history._currentState, title: title};
2313 			AjaxTCR.history._history.push(historyItem);
2314 		}
2315 		
2316 		var optionsString = AjaxTCR.storage.get("request", persistDiv, safeStateName);
2317 		AjaxTCR.history._handleHistoryCallback(optionsString);
2318 			
2319   	}
2320 	setTimeout("AjaxTCR.history._checkHashIE();", 500);
2321 }, 
2322 
2323 /**
2324  * Checks to see if the current window.location.hash value matches the value saved in the currentState.
2325  * If they do not match, update the state of the page.
2326  * 
2327  * @private 
2328  */							
2329 _checkHash : function ()	{
2330 	if ((window.location.hash != "#" + AjaxTCR.history._currentState) && window.location.hash != AjaxTCR.history._currentState)
2331     {
2332         AjaxTCR.history._currentState = window.location.hash.substring(1);
2333 		var safeStateName = AjaxTCR.history._currentState.replace(/[%\+]/g, "");
2334 		if (!safeStateName)
2335 			safeStateName = "AjaxTCRinit";
2336 		
2337 		var title = AjaxTCR.storage.get(safeStateName + "title");
2338 		if (title)
2339 			document.title = title.value;
2340 
2341 	
2342 		AjaxTCR.history._historyPosition = AjaxTCR.storage.get(safeStateName + "position").value;
2343 		//check for bookmarked history position
2344 		if (AjaxTCR.history._historyPosition > AjaxTCR.history._history.length)
2345 		{
2346 			AjaxTCR.history._historyPosition = 1;
2347 			AjaxTCR.storage.add(safeStateName + "position", AjaxTCR.history._historyPosition);
2348 			var historyItem = {id: AjaxTCR.history._currentState, title: title};
2349 			AjaxTCR.history._history.push(historyItem);
2350 		}
2351 		
2352 		var results = AjaxTCR.storage.get(safeStateName);
2353 		if (results)
2354 			AjaxTCR.history._handleHistoryCallback(results.value);
2355 		else
2356         	AjaxTCR.history._onStateChangeCallback(AjaxTCR.data.decodeValue(AjaxTCR.history._currentState));
2357 					
2358     }
2359 	setTimeout("AjaxTCR.history._checkHash();", 500);
2360 },
2361 
2362 /**
2363  * If the state on the page changes, this function is called to figure out how to restore the state of the new location.
2364  * Check the stored data and see if we need to call sendRequest/handleCacheResponse or if we need to call the callback function specified by the user in init.
2365  * 
2366  * @private 
2367  * @param {string} String value containing the JSON data that was saved on addToHistory
2368  */						
2369 _handleHistoryCallback : function(optionsString){
2370 	if (optionsString && optionsString != "")
2371 	{
2372 		var str = AjaxTCR.data.decode64(optionsString);
2373 		var options = AjaxTCR.data.decodeJSON(AjaxTCR.data.decode64(optionsString));
2374 		if (options.history)
2375 		{
2376 			if (options.history.saveResponse)
2377 			{
2378 				options.history = null;
2379 				AjaxTCR.comm.cache._handleCacheResponse(options, options.responseText);
2380 			}
2381 			else
2382 			{
2383 				options.history = null;
2384 				AjaxTCR.comm.sendRequest(options.url, options);
2385 			}
2386 		}
2387 		else
2388 			AjaxTCR.history._onStateChangeCallback(AjaxTCR.data.decodeValue(AjaxTCR.history._currentState), options.value);
2389 	}
2390 	else
2391 		AjaxTCR.history._onStateChangeCallback(AjaxTCR.data.decodeValue(AjaxTCR.history._currentState));
2392 	
2393 }
2394 
2395 };
2396 
2397 
2398 /*********************************************  Storage Methods *********************************************/
2399 /**
2400  * The storage class of the library.  Storage is called directly from history, but can also be called by the user.
2401  * If storage is going to be used, you must call AjaxTCR.storage.init() before  usage.
2402  * The types of storage supported are IE Behaviors, WHATWG globalStorage, and cookies
2403  * 
2404  * @class AjaxTCR.storage
2405  * @static
2406  */
2407 AjaxTCR.storage = {
2408 /** The max size to store in cookies */
2409 DEFAULT_MAX_COOKIE_SIZE: 4000,
2410 
2411 /**
2412  * Method must be called before using storage.  The returned value must be passed to all future storage requests
2413  * 
2414  *  @returns {object} The newly created persitent Div object
2415  */
2416 init : function(){
2417 
2418 	if (navigator.userAgent.toLowerCase().indexOf("msie")>-1 )
2419 	{
2420 		var persistDiv = document.createElement("div");
2421 		persistDiv.id = "AjaxTCRPersistDiv";
2422 		persistDiv.style.behavior = "url(#default#userData)";
2423 		document.body.appendChild(persistDiv);
2424 		return persistDiv;
2425 	}
2426 	return null;
2427 },
2428 
2429 /**
2430  * Adds an item to storage.  Checks the browser support and chooses an appropriate method. 
2431  * 
2432  * @param {String} key The key to reference the stored string
2433  * @param {String} value The string to store in persistent data
2434  * @param {Object} persistObj The persistent Object returned from init
2435  * @param {string} [store] Optional value to name the store to save the data.  Only applicable in IE
2436  */
2437 add : function(key, value, persistObj, store){
2438 	
2439 	if(navigator.userAgent.toLowerCase().indexOf("msie")>-1 )
2440 	{
2441 		if (!store)
2442 			store = "AjaxTCRStore";
2443 		
2444 		if (persistObj)
2445 		{
2446 			var allitemsString = AjaxTCR.storage.get("AjaxTCRAllItems", persistObj, store);
2447 			if (!allitemsString)
2448 				var allitemsObj = {};
2449 			else
2450 				var allitemsObj = AjaxTCR.data.decodeJSON(allitemsString);
2451 			
2452 			allitemsObj[key] = value;
2453 			allitemsString = AjaxTCR.data.encodeJSON(allitemsObj);
2454 			persistObj.setAttribute("AjaxTCRAllItems",allitemsString);
2455 			persistObj.setAttribute(key,value);
2456 			persistObj.save(store);
2457 		}
2458 	}	
2459 	else if (typeof(globalStorage) != "undefined")
2460 	{
2461 		var storage = globalStorage[document.domain];
2462 		storage.setItem(key, value);
2463 	}
2464 	else
2465 	{
2466 		/* Update all records */
2467 		var allitemsString = AjaxTCR.storage.get("AjaxTCRAllItems");
2468 		if (!allitemsString)
2469 			var allitemsObj = new Array();
2470 		else
2471 			var allitemsObj = AjaxTCR.data.decodeJSON(allitemsString);
2472 			
2473 		allitemsObj.push(key);
2474 		allitemsString = AjaxTCR.data.encodeJSON(allitemsObj);
2475 		var now = new Date();
2476 		now.setMonth(now.getMonth()+1);
2477 		var expires = now.toGMTString(); 
2478 		var pieces = Math.floor(allitemsString.length/AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE + 1);
2479 		for (var i=0;i<pieces;i++)
2480 			AjaxTCR.comm.cookie.set("AjaxTCRAllItems"+i.toString(), allitemsString.substring(i*AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE, AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE), expires);
2481 			
2482 		/* Add Item */
2483 		var pieces = Math.floor(value.length/AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE + 1);
2484 		for (var i=0;i<pieces;i++)
2485 			AjaxTCR.comm.cookie.set(key+i.toString(), value.substring(i*AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE, AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE), expires);
2486 		
2487 	}
2488  },
2489 
2490 /**
2491  * Retrives the saved value from persisted data.
2492  * 
2493  * @param {String} key The key of the value to retrieve
2494  * @param {Object} persistObj The persistent Object returned from init 
2495  * @param {String} [store] Optional value to name the store to save the data.  Only applicable in IE
2496  */
2497 get : function(key, persistObj, store){
2498 	
2499 	if(navigator.userAgent.toLowerCase().indexOf("msie")>-1)
2500 	{
2501 		if (!store)
2502 			store = "AjaxTCRStore";
2503 		
2504 		if (persistObj)
2505 		{
2506 			persistObj.load(store);
2507 			return persistObj.getAttribute(key);
2508 		}
2509 	}	
2510 	else if (typeof(globalStorage) != "undefined")
2511 	{
2512 		var storage = globalStorage[document.domain];
2513 		return storage.getItem(key);
2514 	}
2515 	else
2516 	{
2517 		var i=0;
2518 		var fullvalue = "";
2519 		do
2520 		{
2521 			var val = AjaxTCR.comm.cookie.get(key+i.toString());
2522 			if (val)
2523 				fullvalue += val;
2524 			i++;
2525 		}while(val);
2526 		
2527 		if (fullvalue != "")
2528 			return fullvalue;
2529 	}
2530 	
2531 	return null;
2532 },
2533 
2534 /**
2535  * Returns all items from the storage in an associative array of name/value pairs
2536  * 
2537  * @param {Object} persistObj The persistent Object returned from init 
2538  * @param {String} [store] Optional value to name the store to save the data.  Only applicable in IE
2539  */
2540 getAll : function(persistObj, store){
2541 	
2542 	if(navigator.userAgent.toLowerCase().indexOf("msie")>-1)
2543 	{
2544 		if (!store)
2545 			store = "AjaxTCRStore";
2546 		
2547 		if (persistObj)
2548 		{
2549 			var allitems = AjaxTCR.storage.get("AjaxTCRAllItems", persistObj, store);
2550 			return AjaxTCR.data.decodeJSON(allitems);
2551 		}
2552 	}	
2553 	else if (typeof(globalStorage) != "undefined")
2554 	{
2555 		var storage = globalStorage[document.domain];
2556 		return storage;
2557 	}
2558 	else
2559 	{
2560 		var allitems = AjaxTCR.storage.get("AjaxTCRAllItems");
2561 		if (allitems)
2562 		{
2563 			var items = {};
2564 			var keys = AjaxTCR.data.decodeJSON(allitems);
2565 			for (var i=0;i<keys.length;i++)
2566 				items[keys[i]] = AjaxTCR.storage.get(keys[i]);
2567 			return items;
2568 		}
2569 	}
2570 	
2571 	return null;
2572 },
2573 
2574 /**
2575  * Removes all items from persistent storage.
2576  * 
2577  * @param {Object} persistObj The persistent Object returned from init 
2578  * @param {String} [store] Optional value to name the store to save the data.  Only applicable in IE
2579  */
2580 clear : function(persistObj, store){
2581 	
2582 	var allItems = AjaxTCR.storage.getAll(persistObj, store);
2583 	for (var i in allItems)
2584 		AjaxTCR.storage.remove(i, persistObj, store);
2585 },
2586  
2587 /**
2588  * Remove the given item from persistent storage.
2589  * 
2590  * @param {string} key The name of the item to remove from storage
2591  * @param {Object} persistObj The persistent Object returned from init 
2592  * @param {String} [store] Optional value to name the store to save the data.  Only applicable in IE
2593  */
2594 remove : function(key, persistObj, store){
2595 	
2596 	if(navigator.userAgent.toLowerCase().indexOf("msie")>-1 )
2597 	{
2598 		if (!store)
2599 			store = "AjaxTCRStore";
2600 		
2601 		if (persistObj)
2602 		{
2603 			var allitems = AjaxTCR.storage.get("AjaxTCRAllItems", persistObj, store);
2604 			if (allitems)
2605 			{
2606 				allitems = AjaxTCR.data.decodeJSON(allitems);
2607 				if (allitems[key])
2608 					delete allitems[key];
2609 				allitems = AjaxTCR.data.encodeJSON(allitems);
2610 				persistObj.setAttribute("AjaxTCRAllItems",allitems);
2611 			}
2612 			
2613 			persistObj.removeAttribute(key);
2614 			persistObj.save(store);
2615 		}
2616 	}	
2617 	else if (typeof(globalStorage) != "undefined")
2618 	{
2619 		var storage = globalStorage[document.domain];
2620 		delete storage[key];
2621 	}
2622 	else
2623 	{
2624 		/* First remove from global object */
2625 		var allitems = AjaxTCR.storage.get("AjaxTCRAllItems", persistObj, store);
2626 		if (allitems)
2627 		{
2628 			allitems = AjaxTCR.data.decodeJSON(allitems);
2629 			for (var i=allitems.length-1;i>=0;i--)
2630 			{
2631 				if (allitems[i] == key)
2632 					delete allitems[i];
2633 			}
2634 			var allitemsString = AjaxTCR.data.encodeJSON(allitems);
2635 			var loops = Math.floor(allitemsString.length/AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE + 1);
2636 			var now = new Date();
2637 			now.setMonth(now.getMonth()+1);
2638 			var expires = now.toGMTString(); 
2639 			for (var i=0;i<loops;i++)
2640 				AjaxTCR.comm.cookie.set("AjaxTCRAllItems"+i.toString(), allitemsString.substring(i*AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE, AjaxTCR.storage.DEFAULT_MAX_COOKIE_SIZE), expires);
2641 				
2642 			var i=0;
2643 			do
2644 			{
2645 				var val = AjaxTCR.comm.cookie.get(key+i.toString());
2646 				if (val)
2647 					AjaxTCR.comm.cookie.remove(key+i.toString());
2648 				i++;
2649 			}while(val);
2650 			
2651 		}
2652 			
2653 
2654 	}
2655  }
2656 };
2657 
2658 
2659 /*************************************  AjaxTCR.template *****************************/
2660 /**
2661  * The template class of the library.  Is called directly from AjaxTCR.comm, but can also be called by the user.
2662  * 
2663  * @class AjaxTCR.template
2664  * @static
2665  */
2666 AjaxTCR.template = {
2667 
2668  /** flag to indicate if we already included the template library 
2669   * @private
2670   */
2671  _libraryIncluded : false,
2672  
2673  /** The cache object  
2674   * @private
2675   */
2676  _cache : new Array(),
2677 		
2678 /**
2679  * Method that takes a template and a JSON object and converts into output 
2680  * @param {string} template  template string.
2681  * @param {JSON string} data  a JSON string to be used in rendering template
2682  * @return {string} String formatted by template and populated with data from data 
2683  */
2684 translateString : function(template, data){
2685 	var myTemplateObj = TrimPath.parseTemplate(template);
2686     var result  = myTemplateObj.process(AjaxTCR.data.decodeJSON(data));
2687 	return result;
2688 },
2689 
2690 /**
2691  * Method that takes a template filename and a JSON object, fetches the file, and converts into output
2692  *  
2693  * @param {string} template  template filename.
2694  * @param {string} data  a JSON string to be used in rendering template
2695  * @param {boolean} cache  a boolean indicating whether to cache the response or not
2696  * @return {string} output string formatted by given template file and polulated with data from data. 
2697  */
2698 translateFile : function(templatefilename, data, cache){
2699 	
2700 	if (typeof(cache) == "undefined")
2701 		cache = true;
2702 		
2703 	var template = AjaxTCR.template.getFromCache(templatefilename);
2704 	if (!template)
2705 	{
2706 		var templateRequest = AjaxTCR.comm.sendRequest(templatefilename, {async:false, cacheResponse:cache});
2707 		var template = templateRequest.responseText;
2708 		if (cache && template)
2709 			AjaxTCR.template.addToCache(templatefilename, template);
2710 	}
2711 	
2712 	if (typeof(data) == "string")
2713 		data = AjaxTCR.data.decodeJSON(data);
2714 	
2715 	var myTemplateObj = TrimPath.parseTemplate(template);
2716     var result  = myTemplateObj.process(data);
2717 	return result;
2718 },
2719 
2720 /**
2721  * Method that caches a template file usually the URL of the template file is used as the key and template as the value.
2722  * @param {string} key key to add to the template cache
2723  * @param {string} val value to store in the template cache
2724  */		
2725 addToCache : function(key, val){
2726 	
2727 	var item = AjaxTCR.template._createCacheItem(key, val);
2728 	AjaxTCR.template._cache.push(item);
2729 },
2730 
2731 /**
2732  * Adds a url and optional value string to the template cache.  If the value is set, it simply calls addToCache.  
2733  * If value is not set, it requests the file at the given URL and caches the results
2734  * 
2735  * @param {string} key key to add to the template cache
2736  * @param {string} val value to store in the template cache
2737  */		
2738 cache : function(key, val){
2739 	
2740 	if (val)
2741 		AjaxTCR.template.addToCache(key, val);
2742 	else
2743 	{
2744 		var options = {cacheTemplate:true, 
2745 					   onSuccess: function(response){
2746 							if(response.responseText)
2747 								AjaxTCR.template.addToCache(response.url, response.responseText);
2748 					   }
2749 		};
2750 		AjaxTCR.comm.sendRequest(key, options);
2751 		
2752 	}
2753 },
2754 
2755 /**
2756  * Fetches a page that contains a template bundle.  The expected response is in this format:
2757  * <!-- Template-Begin URL='rating-Ajax.tpl' --><br />
2758  * <h3>Thanks for voting!</h3><br />
2759  * <p>Your rating: {$userrating}. <br /><br />
2760  * Average rating: {$avgrating}.<br /><br />
2761  * Total votes: {$totalvotes}. <br /><br />
2762  * </p><br />
2763  * <!-- Template-End --><br />
2764  * <!-- Template-Begin URL='thankyou-Ajax.tpl' --><br />
2765  * <h2>Thank you for registering {$username}</h2><br />
2766  * <p>Contact technical support at 555-1212</p><br />
2767  * <!-- Template-End --><br />
2768  * 
2769  * @param url - URL of the template bundle to fetch
2770  */		
2771 cacheBundle : function(url){
2772 	
2773 	var options = {cacheTemplate:true, 
2774 				   onSuccess: AjaxTCR.template._parseTemplateBundle};
2775 	AjaxTCR.comm.sendRequest(url, options);
2776 },
2777 
2778 /**
2779  * Public method that resets the template's cache
2780  * 
2781  */		
2782 clearCache : function(){
2783 	AjaxTCR.template._cache = new Array();
2784 },
2785 
2786 /**
2787  * public method to fetch a cached template based on the key
2788  * @param {string} key  key to add to the template cache
2789  * @return {string} the cached object value if found
2790  */	
2791 getFromCache: function(key){
2792 	var cacheObject = null;
2793 	/* Search for item */
2794 	for (var i=0;i<AjaxTCR.template._cache.length;i++)
2795 	{
2796 		if (AjaxTCR.template._cache[i].key == key)
2797 		{
2798 			cacheObject = AjaxTCR.template._cache[i];
2799 			break;
2800 		}
2801 	}
2802 	
2803 	if (cacheObject)
2804 		return cacheObject.value;
2805 	else 
2806 		return null;
2807 },
2808 
2809 /**
2810  * public method to remove an item from the template cache based on the key
2811  * @param {string} key  key of item to remove from template cache
2812  */	
2813 removeFromCache : function(key){
2814 	for (var i=0;i<AjaxTCR.template._cache.length;i++)
2815 	{
2816 		if (AjaxTCR.template._cache[i].key == key)
2817 		{
2818 			AjaxTCR.template._cache.splice(i,1);
2819 			break;
2820 		}
2821 	}
2822 },
2823 								
2824 
2825 /*************************************  Private Template Methods *****************************/
2826 
2827 /**
2828  * private method that creates a cache object based on the given key and val
2829  * 
2830  * @private 
2831  * @param {string} key Key of the new cache object
2832  * @param {string} val Value of the new cache object
2833  */								
2834 _createCacheItem : function(key, val){
2835 	var cacheObject = {};
2836 	cacheObject.key = key;
2837 	cacheObject.value = val;
2838 	return cacheObject;
2839 },
2840 
2841 /**
2842  * Dynamically include the template library on usage.
2843  * 
2844  * @private
2845  */
2846 _includeLibrary : function(){
2847 	if (!AjaxTCR.template._libraryIncluded)
2848 		document.write('<sc' + 'ript type="text/javascript" src="http://ajaxref.com/lib/trimpath/template.js"></sc' + 'ript>');
2849 	
2850 	AjaxTCR.template._libraryIncluded = true;
2851 },
2852 
2853 /**
2854  * Takes a template bundle, parses it, and adds each item to the cache
2855  * 
2856  * @private 
2857  * @param {response} The response object from the XHR
2858  * 
2859  */
2860 _parseTemplateBundle : function(response){
2861 	var bundle = response.responseText;
2862 	var re = /\s*\<!--\s*Template-Begin([\w\W]*?)\<!--\s*Template-End\s*--\>\s*/ig;
2863 	var matches = bundle.match(re);
2864 	for (var i=0;i<matches.length;i++)
2865 	{
2866 		var parts = /\s*\<!--\s*Template-Begin\s*URL=['"]([^'"]*)['"]\s*--\>([\w\W]*?)\<!--\s*Template-End\s*--\>\s*/i;
2867 		var data = parts.exec(matches[i]);
2868 		if (data)
2869 			AjaxTCR.template.addToCache(data[1], data[2]);
2870 	}
2871 }
2872 
2873 
2874 };
2875 
2876 
2877 
2878 /*********************************************  Data Handling Methods *********************************************/
2879 /**
2880  * Several useful data functions.  encode/decode/serialize for several different data formats.  Many of these are
2881  * called from other parts of the library. 
2882  * 
2883  * @class AjaxTCR.data
2884  * @static
2885  */
2886 AjaxTCR.data = {
2887 						
2888 /**
2889  * Public method to encode passed string values to make the URL safe.  
2890  * Strictly encodes to x-www-urlencoded format vs. native methods found in JavaScript.
2891  * 
2892  * @param {string} val - the value to encode
2893  * @return {string} a string that is properly x-www-urlencoded as a browser would do it
2894  */									 							 
2895 encodeValue : function(val) {
2896 	
2897     var encodedVal;
2898 
2899 	if (!encodeURIComponent)
2900 	  {
2901 	   encodedVal = escape(val);
2902        /* fix the omissions */
2903 	   encodedVal = encodedVal.replace(/@/g, '%40');
2904 	   encodedVal = encodedVal.replace(/\//g, '%2F');
2905 	   encodedVal = encodedVal.replace(/\+/g, '%2B');
2906       }
2907     else
2908       {
2909        encodedVal = encodeURIComponent(val);
2910 	   /* fix the omissions */
2911 	   encodedVal = encodedVal.replace(/~/g, '%7E');
2912 	   encodedVal = encodedVal.replace(/!/g, '%21');
2913 	   encodedVal = encodedVal.replace(/\(/g, '%28');
2914 	   encodedVal = encodedVal.replace(/\)/g, '%29');
2915 	   encodedVal = encodedVal.replace(/'/g, '%27');
2916       }
2917 
2918     /* clean up the spaces and return */
2919     return encodedVal.replace(/\%20/g,'+'); 
2920 } ,
2921 						   
2922 /**
2923  * Public method to decode passed string values from a URL safe string.
2924  * 
2925  * @param {string} val - the value to decode
2926  * @return {string} a string that is properly reversed from the proper x-www-urlencoded format
2927  */									 							 
2928 decodeValue : function(val) {
2929                              var decodedVal;
2930 
2931                              if (!decodeURIComponent)
2932                                {
2933 								decodedVal = val;
2934                                 /* fix the omissions */
2935 	                            decodedVal = decodedVal.replace(/\%40/g, '@');
2936 	                            decodedVal = decodedVal.replace(/\%2F/g, '/');
2937 	                            decodedVal = decodedVal.replace(/\%2B/g, '+');
2938 								decodedVal = unescape(val);
2939                                 
2940                                }
2941                             else
2942                                {
2943                                 /* fix the omissions */
2944 								decodedVal = val;
2945 	                            decodedVal = decodedVal.replace(/\%7E/g, '~');
2946 	                            decodedVal = decodedVal.replace(/\%21/g, '!');
2947 	                            decodedVal = decodedVal.replace(/\%28/g, '(');
2948 	                            decodedVal = decodedVal.replace(/\%29/g, ')');
2949 	                            decodedVal = decodedVal.replace(/\%27/g, "'");
2950 								
2951 								decodedVal = decodeURIComponent(val);
2952 	                            
2953                                }
2954 
2955                             /* clean up the spaces and return */
2956                             return decodedVal.replace(/\+/g,' '); 
2957                            } ,
2958 
2959 /**
2960  * Public method to convert tag characters to < and > and change \n to <br />
2961  * 
2962  *  @param {string} str String to modify
2963  *  @return {string} The str value with the conversions in place 
2964  */	
2965 encodeAsHTML : function(str) {
2966 								var convertedString = str.replace(/<([^>]*)>/g, "<$1>")
2967 								convertedString = convertedString.replace(/\n/g, "<br/>");
2968 								return convertedString;
2969 						   }, 
2970 						
2971 /**
2972  * Public method to encode passed string values into base64.
2973  * 
2974  * @param {string} inputStr A string value to encode
2975  * @return {string} The inputStr value encoded in base64
2976  */	
2977 encode64 : function(inputStr) 
2978 {
2979    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2980    var outputStr = "";
2981    var i = 0;
2982    
2983    while (i<inputStr.length)
2984    {
2985       var byte1 = inputStr.charCodeAt(i++);
2986       var byte2 = inputStr.charCodeAt(i++);
2987       var byte3 = inputStr.charCodeAt(i++);
2988 
2989       var enc1 = byte1 >> 2;
2990       var enc2 = ((byte1 & 3) << 4) | (byte2 >> 4);
2991 	  
2992 	  var enc3, enc4;
2993 	  if (isNaN(byte2))
2994 		enc3 = enc4 = 64;
2995 	  else
2996 	  {
2997       	enc3 = ((byte2 & 15) << 2) | (byte3 >> 6);
2998 		if (isNaN(byte3))
2999          	enc4 = 64;
3000 		else
3001 	      	enc4 = byte3 & 63;
3002 	  }
3003 
3004       outputStr +=  b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4);
3005    } 
3006    
3007    return outputStr;
3008 }, 
3009 
3010 /**
3011  * Public method to decode passed string values from base64.
3012  * 
3013  * @param {string} inputStr A string in base64 format
3014  * @return {string} The decoded string
3015  */	
3016 decode64 : function(inputStr) 
3017 {
3018    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
3019    var outputStr = "";
3020    var i = 0;
3021    inputStr = inputStr.replace(/[^A-Za-z0-9\+\/\=]/g, "");
3022 
3023    while (i<inputStr.length)
3024    {
3025       var dec1 = b64.indexOf(inputStr.charAt(i++));
3026       var dec2 = b64.indexOf(inputStr.charAt(i++));
3027       var dec3 = b64.indexOf(inputStr.charAt(i++));
3028       var dec4 = b64.indexOf(inputStr.charAt(i++));
3029 
3030       var byte1 = (dec1 << 2) | (dec2 >> 4);
3031       var byte2 = ((dec2 & 15) << 4) | (dec3 >> 2);
3032       var byte3 = ((dec3 & 3) << 6) | dec4;
3033 
3034       outputStr += String.fromCharCode(byte1);
3035       if (dec3 != 64) 
3036 	  	outputStr += String.fromCharCode(byte2);
3037       if (dec4 != 64)
3038          outputStr += String.fromCharCode(byte3);
3039    }
3040 
3041    return outputStr;
3042 },
3043 
3044 /**
3045  * Public method to create a serialized payload based on the contents of a form and the specified encoding value.  
3046  * trigger and evt are optional and used to firm up the accuracy between what the browser would send and what we send.
3047  * 
3048  * @param {object} form  The form to be serialized
3049  * @param {string} encoding  The encoding of the result.  Options are "application/json", "application/x-www-form-urlencoded", "text/plain", "text/xml"
3050  * @param {object} trigger The element that caused the submission of the form.  input type="button" fields will only be included in the payload if they triggered the submission.
3051  * @param {object} evt In the case that a input type="image" caused the submission, if the evt is passed in, the coordinates of the click will be put in the payload
3052  * @return {string} The payload serialized according to the specified encoding.
3053  */									 
3054 	
3055 serializeForm : function(form, encoding, trigger, evt) {	
3056 	if (typeof(form) == "string")
3057 	{
3058 		var formObject = document.forms[form]; 
3059 		if (formObject == null)
3060 			formObject = document.getElementById(form);
3061 			
3062 		form = formObject;			
3063 	}
3064 	var x=0,y=0;
3065 	if (trigger && trigger.type == "image" && trigger.name)
3066 	{
3067 		if (window.event)
3068 		{
3069 			x = window.event.offsetX;
3070 			y = window.event.offsetY;
3071 		}
3072 		else if (evt.target) 
3073 		{
3074 			var coords = {x: 0, y: 0 };
3075 			var elmt = trigger;
3076 			while (elmt)
3077 			{
3078 				coords.x += elmt.offsetLeft;
3079 				coords.y += elmt.offsetTop;
3080 				elmt = elmt.offsetParent;
3081 			}
3082 		
3083 			x = evt.clientX + window.scrollX - coords.x - 1 ;
3084 			y = evt.clientY + window.scrollY - coords.y - 1;
3085 		}
3086 	}
3087 	
3088 	var formValues = AjaxTCR.data._beginEncode(encoding);
3089 	for (var i =0; i < form.elements.length; i++)
3090 	{
3091 	 var currentField = form.elements[i];
3092 	 var fieldName = currentField.name;
3093 	 var fieldType = currentField.type;
3094 	
3095 	 /* Disabled and unnamed fields are not sent by browsers so ignore them */
3096 	 if ((!currentField.disabled) && fieldName) 
3097 	   {
3098 		switch (fieldType)
3099 		 {
3100 		  case "text":
3101 		  case "password":
3102 		  case "hidden":
3103 		  case "textarea": formValues = AjaxTCR.data._encode(formValues, fieldName, currentField.value, encoding);
3104 	                           break;
3105 		  case "radio":
3106 		  case "checkbox": if (currentField.checked) 
3107 					formValues = AjaxTCR.data._encode(formValues, fieldName, currentField.value, encoding);
3108 	                           break;
3109 	      case 'select-one':
3110 		  case 'select-multiple': for (var j=0; j< currentField.options.length; j++)
3111 				           if (currentField.options[j].selected)
3112 	                                   {
3113 						formValues = AjaxTCR.data._encode(formValues, fieldName, (currentField.options[j].value != null && currentField.options[j].value != "") ? currentField.options[j].value : currentField.options[j].text , encoding);
3114 	                                   }
3115 				           break;
3116 		  case "file": if (currentField.value)
3117 				   return "fileupload";
3118 				else
3119 				    formValues = AjaxTCR.data._encode(formValues, fieldName, currentField.value, encoding);
3120 			       break;
3121 		  case "submit": if (currentField == trigger)
3122 			 	     formValues = AjaxTCR.data._encode(formValues, fieldName, currentField.value, encoding);
3123 				 break;
3124 		  default: continue;  /* everything else like fieldset you don't want */
3125 		 }
3126 	  }
3127 	
3128 	}
3129 	
3130 	if (trigger && trigger.type == "image" && trigger.name)
3131 	{
3132 		/* this is where we need to insert the trigger image information */
3133 		formValues = AjaxTCR.data._encode(formValues, trigger.name + ".x", x, encoding);
3134 		formValues = AjaxTCR.data._encode(formValues, trigger.name + ".y", y, encoding);
3135 		formValues = AjaxTCR.data._encode(formValues, trigger.name, trigger.value, encoding);
3136 	}
3137 	
3138 	/* Clean up payload string */
3139 	formValues = AjaxTCR.data._completeEncoding(formValues, encoding);
3140 	
3141 	return formValues;
3142 														},
3143 
3144 /**
3145  * Public method to create/modify a serialized object based on an object
3146  
3147  * @param {object} payload the current object that will be appended to.
3148  * @param {object} obj is the object to add to the payload
3149  * @param {string} encoding  The encoding of the result.  Options are "application/json", "application/x-www-form-urlencoded", "text/plain", "text/xml"
3150  * @return {object} The new payload with the object included
3151  */									 
3152 	
3153 serializeObject : function(payload, obj, encoding){
3154 	/* Take care of any necessary bits if payload isn't empty */
3155 	payload = AjaxTCR.data._continueEncoding(payload, encoding);
3156 	
3157 	/* encode each value in the object */
3158     for (var key in obj)
3159         payload = AjaxTCR.data._encode(payload, key, obj[key], encoding);
3160 
3161 	/* Clean up payload string */
3162 	payload = AjaxTCR.data._completeEncoding(payload, encoding);
3163     return payload;
3164 },
3165 
3166 /**
3167  * private method to encode one name/value pair and add it to a payload.
3168  * 
3169  * @private 
3170  * @param {object} payload is current object that will be appended to.  It can be null.
3171  * @param {string} fieldName The name of the item to encode
3172  * @param {string} fieldValue The value of the item to encode
3173  * @param {string} encoding  The encoding of the result.  Options are "application/json", "application/x-www-form-urlencoded", "text/plain", "text/xml"
3174  * @return {object} The new payload with the new name/value pair included
3175  */	
3176 _encode : function(payload, fieldName, fieldValue, encoding){
3177 	switch(encoding)
3178 	{
3179     	case "application/json":
3180     	   	  payload[fieldName] = fieldValue;
3181 		  break;
3182     	case "application/x-www-form-urlencoded":
3183     	   	  payload+=AjaxTCR.data.encodeValue(fieldName)+"="+AjaxTCR.data.encodeValue(fieldValue)+"&"
3184 		  break;
3185 		case "text/plain":
3186     	   	  payload+=fieldName.replace(/,/g, "%2C") + "=" + fieldValue.replace(/,/g, "%2C") +","
3187 		  break;
3188 		case "text/xml":
3189 			var node = payload.createElement(fieldName);
3190 			node.appendChild(payload.createTextNode(fieldValue));
3191 			payload.lastChild.appendChild(node);
3192 	
3193 		  break;
3194 	}
3195 
3196     return payload;
3197 },
3198 
3199 /**
3200  * private method to create the base for the encoding object
3201  * 
3202  * @private 
3203  * @param {string} encoding  The encoding of the result.  Options are "application/json", "application/x-www-form-urlencoded", "text/plain", "text/xml"
3204  * @return {object} Either an empty string, xml frame, or an empty object depending on type
3205  */	
3206 _beginEncode : function(encoding){
3207 	switch(encoding)
3208 	{
3209     	case "application/json":
3210     	  if (payload == null)
3211 	    	payload = {};
3212 
3213 	  	  break;
3214     	case "application/x-www-form-urlencoded":
3215     	  if (payload == null)
3216 	    	payload = "";
3217 
3218 		  break;
3219 		case "text/plain":
3220     	  if (payload == null)
3221 	    	payload = "";
3222 
3223 		  break;
3224 		case "text/xml":
3225 			if (payload == null)
3226 			{
3227 				var payload = AjaxTCR.data._createXMLDocument();
3228 				if (window.navigator.userAgent.indexOf('Opera') == -1)
3229 				{
3230 				  var xmlStmt = payload.createProcessingInstruction("xml"," version=\"1.0\" encoding=\"UTF-8\" ");
3231 			      payload.appendChild(xmlStmt);
3232 				}
3233 				
3234 				var root = payload.createElement("payload");
3235 				payload.appendChild(root);
3236 			}
3237 								
3238 			
3239 		  break;
3240 	}
3241 
3242     return payload;
3243 },
3244 
3245 /**
3246  * private method to clean up payload string when finished encoding.
3247  * 
3248  * @private
3249  * @param {object} payload The payload to clean
3250  * @param {string} encoding  The encoding of the result.  Options are "application/json", "application/x-www-form-urlencoded", "text/plain", "text/xml"
3251  * @return {object} The final payload object with no loose ends.
3252  */	
3253 _completeEncoding : function (payload, encoding ){
3254 	/* Trim off the end & but avoid edge case problems with an empty form */
3255 	if ((encoding == "application/x-www-form-urlencoded" || encoding == "text/plain") && payload.length > 0)
3256 	  payload = payload.substring(0,payload.length-1);
3257 	  
3258 	return payload;
3259 },
3260 
3261 /**
3262  * private method to get payload ready to be appended to.
3263  * @private
3264  * @param {object} payload The payload to prepare for being appended to
3265  * @param {string} encoding  The encoding of the result.  Options are "application/json", "application/x-www-form-urlencoded", "text/plain", "text/xml"
3266  * @return {object} The payload in a form that it can now have the next value written to it.
3267  */	
3268 _continueEncoding : function(payload, encoding){
3269 	if (payload != "")
3270 	  {
3271 		if (encoding == "application/x-www-form-urlencoded")
3272 			payload += "&";
3273 		else if (encoding == "text/plain")
3274 			payload += ",";
3275 	  }
3276 	return payload;
3277    },
3278 
3279 /**
3280  * Finds the best native or ActiveX object to use for an XML Document
3281  * 
3282  * @private 
3283  * @return {object} The XML Document
3284  */
3285 _createXMLDocument : function(){
3286 	var xmlDoc = null;
3287 	if (window.ActiveXObject)
3288 	  {
3289 	  	var versions = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];
3290 	
3291 	    for (var i=0;i<versions.length;i++)
3292 	     {
3293 		   try
3294 		    {
3295 			 xmlDoc = new ActiveXObject(versions[i]);
3296 			 break;
3297 		    }
3298 		   catch(err){}
3299 	      }
3300 	  }
3301 	else	
3302 		xmlDoc = document.implementation.createDocument("", "", null);
3303 		
3304 	return xmlDoc;
3305   },
3306 
3307 /**
3308  * Returns the string version of the given XML Object
3309  * 
3310  * @param {object} xmlObject The xml object
3311  * @return {string} The xmlObject in string form
3312  */							  
3313 serializeXML : function(xmlObject){
3314 	var xmlString = "";
3315 	
3316 	if (typeof XMLSerializer != "undefined")
3317   		xmlString = (new XMLSerializer()).serializeToString(xmlObject); 
3318 	else if (xmlObject.xml) 
3319 		xmlString = xmlObject.xml;
3320 		
3321 	return xmlString;
3322 },
3323 
3324 /**
3325  * Returns the XML Object of the given string
3326  * 
3327  * @param {string} xmlStr A string that can be converted to XML
3328  * @return {object} The xmlObject generated from the string
3329  */							  
3330 serializeXMLString : function(xmlStr){
3331 	if (window.DOMParser)
3332 	 	var xmlDoc = (new DOMParser()).parseFromString(xmlStr, "text/xml");
3333 	else
3334 	{
3335 		var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
3336 	  	xmlDoc.async="false";
3337 	  	xmlDoc.loadXML(xmlStr);
3338  	}
3339 
3340 	return xmlDoc;
3341 },
3342 						
3343 						
3344 /**
3345  * Convert JSON into JavaScript values
3346  * 
3347  * @param {string} jsonString A string that can be converted to JavaScript arrays and objects
3348  * @return {object} A javascript object generated from the passed in string.
3349  */							
3350 decodeJSON : function(jsonString){
3351 	var j;
3352 	if (jsonString.length > 1 && jsonString.substring(0,2) == "/*")
3353 		jsonString = jsonString.substring(2,jsonString.lastIndexOf("*/"));
3354 
3355 	  try {
3356 	        j = eval('(' + jsonString + ')');
3357 	  } 
3358 	  catch (e) {
3359 	        throw new SyntaxError('parseJSON');
3360 	  }
3361 	  return j;
3362 },
3363 
3364 /**
3365  * Turn JavaScript values into a JSON String
3366  * 
3367  * @param {object} o A object to be converted into a JSON string
3368  * @return {string} A string representation of the passed in object
3369  */	
3370 encodeJSON : function(o){
3371 	var useHasOwn = {}.hasOwnProperty ? true : false;
3372     var pad = function(n) {
3373         return n < 10 ? '0' + n : n;
3374     };
3375     
3376     var m = {
3377         '\b': '\\b',
3378         '\t': '\\t',
3379         '\n': '\\n',
3380         '\f': '\\f',
3381         '\r': '\\r',
3382         '"' : '\\"',
3383         '\\': '\\\\'
3384     };
3385 	
3386 	if(typeof o == 'undefined' || o === null)
3387 	return 'null';
3388 	else if(o instanceof Array)
3389 	{
3390 	var a = ['['], b, i, l = o.length, v;
3391 	for (i = 0; i < l; i += 1) {
3392 	    v = o[i];
3393 	    switch (typeof v) {
3394 	        case 'undefined':
3395 	       // case 'function':
3396 	        case 'unknown':
3397 	            break;
3398 	        default:
3399 	            if (b) {
3400 	                a.push(',');
3401 	            }
3402 	            a.push(v === null ? "null" : AjaxTCR.data.encodeJSON(v));
3403 	            b = true;
3404 	    }
3405 	}
3406 	a.push(']');
3407 	return a.join('');
3408 	}
3409 	else if(o instanceof Date)
3410 	{
3411 	return '"' + o.getFullYear() + '-' +
3412 	    pad(o.getMonth() + 1) + '-' +
3413 	    pad(o.getDate()) + 'T' +
3414 	    pad(o.getHours()) + ':' +
3415 	    pad(o.getMinutes()) + ':' +
3416 	    pad(o.getSeconds()) + '"';
3417 	}
3418 	else if(typeof o == 'string')
3419 	{
3420 		var s = o;
3421 	if (/["\\\x00-\x1f]/.test(s)) 
3422 		{
3423 		return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) 
3424 			{
3425                 var c = m[b];
3426                 if(c){
3427                     return c;
3428                 }
3429                 c = b.charCodeAt();
3430 	    	return '\\u00' +
3431 	        	Math.floor(c / 16).toString(16) +
3432 	        (c % 16).toString(16);
3433 		}) + '"';
3434 		}
3435 		return '"' + s + '"';
3436 	}
3437 	else if(typeof o == 'number')
3438 	{
3439 	return isFinite(o) ? String(o) : "null";
3440 	}
3441 	else if(typeof o == 'boolean')
3442 	{
3443 	return String(o);
3444 	}
3445 	else if (typeof o == 'function')
3446 	{
3447 		//return String(o).replace(/\n/g, " ");
3448 		return '( ' + String(o) + ' )';
3449 	}
3450 	else 
3451 	{
3452 	var a = ['{'], b, i, v;
3453 	for (var i in o) {
3454 		try{
3455 			if(!useHasOwn || o.hasOwnProperty(i)) {
3456 	        v = o[i];
3457 	        switch (typeof v) {
3458 	        case 'undefined':
3459 	       // case 'function':
3460 	        case 'unknown':
3461 	            break;
3462 				case 'function':
3463 					if (String(v).indexOf("[native code]") > -1)
3464 						break;
3465 	        default:
3466 	            if(b)
3467 					a.push(',');
3468 	            
3469 	            a.push(AjaxTCR.data.encodeJSON(i), ':', v === null ? "null" : AjaxTCR.data.encodeJSON(v));
3470 	            b = true;
3471 	        }
3472 	    }
3473 		}
3474 		catch(e){};
3475 	}
3476 	a.push('}');
3477 	return a.join('');
3478 	}
3479 },
3480 
3481 	
3482 /**
3483  * encodeMD5(string) 
3484  * 
3485  * public method to get md5 encode a string
3486  * Based on Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
3487  * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
3488  * Distributed under the BSD License
3489  * See http://pajhome.org.uk/crypt/md5 for more info.
3490  * 
3491  * @param {string} str The string to encode
3492  * @return {string} A string that is encoded in MD5 format
3493  */	
3494 encodeMD5 : function(str){
3495 	var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
3496 	var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
3497 	var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
3498 
3499 	var len = str.length * chrsz;
3500 	
3501 	var x = Array();
3502   	var mask = (1 << chrsz) - 1;
3503   	for(var i = 0; i < len; i += chrsz)
3504 	  {
3505     	x[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
3506 	  }	
3507 		
3508 	/* append padding */
3509   	x[len >> 5] |= 0x80 << ((len) % 32);
3510   	x[(((len + 64) >>> 9) << 4) + 14] = len;
3511 
3512   	var a =  1732584193;
3513   	var b = -271733879;
3514   	var c = -1732584194;
3515   	var d =  271733878;
3516 
3517   	for(var i = 0; i < x.length; i += 16)
3518   	{
3519     	var olda = a;
3520     	var oldb = b;
3521     	var oldc = c;
3522     	var oldd = d;
3523 
3524 	    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
3525 	    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
3526 	    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
3527 	    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
3528 	    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
3529 	    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
3530 	    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
3531 	    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
3532 	    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
3533 	    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
3534 	    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
3535 	    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
3536 	    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
3537 	    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
3538 	    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
3539 	    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
3540 	
3541 	    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
3542 	    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
3543 	    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
3544 	    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
3545 	    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
3546 	    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
3547 	    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
3548 	    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
3549 	    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
3550 	    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
3551 	    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
3552 	    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
3553 	    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
3554 	    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
3555 	    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
3556 	    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
3557 	
3558 	    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
3559 	    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
3560 	    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
3561 	    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
3562 	    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
3563 	    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
3564 	    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
3565 	    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
3566 	    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
3567 	    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
3568 	    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
3569 	    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
3570 	    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
3571 	    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
3572 	    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
3573 	    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
3574 	
3575 	    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
3576 	    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
3577 	    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
3578 	    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
3579 	    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
3580 	    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
3581 	    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
3582 	    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
3583 	    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
3584 	    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
3585 	    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
3586 	    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
3587 	    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
3588 	    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
3589 	    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
3590 	    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
3591 	
3592 	    a = safe_add(a, olda);
3593 	    b = safe_add(b, oldb);
3594 	    c = safe_add(c, oldc);
3595 	    d = safe_add(d, oldd);
3596 	}
3597   
3598   	var binarray = Array(a, b, c, d);
3599   	var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
3600   	var str = "";
3601   	for(var i = 0; i < binarray.length * 4; i++)
3602   	{
3603     	str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
3604   	}
3605   	
3606 	return str;
3607   
3608 	/*
3609 	 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
3610 	 * to work around bugs in some JS interpreters.
3611 	 */
3612 	function safe_add(x, y)
3613 	{
3614 	  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
3615 	  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
3616 	  return (msw << 16) | (lsw & 0xFFFF);
3617 	}
3618 	
3619 	/*
3620 	 * Bitwise rotate a 32-bit number to the left.
3621 	 */
3622 	function bit_rol(num, cnt)
3623 	{
3624 	  return (num << cnt) | (num >>> (32 - cnt));
3625 	}
3626 	
3627 	/*
3628 	 * These functions implement the four basic operations the algorithm uses.
3629 	 */
3630 	function md5_cmn(q, a, b, x, s, t)
3631 	{
3632 	  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
3633 	}
3634 	function md5_ff(a, b, c, d, x, s, t)
3635 	{
3636 	  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
3637 	}
3638 	function md5_gg(a, b, c, d, x, s, t)
3639 	{
3640 	  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
3641 	}
3642 	function md5_hh(a, b, c, d, x, s, t)
3643 	{
3644 	  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
3645 	}
3646 	function md5_ii(a, b, c, d, x, s, t)
3647 	{
3648 	  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
3649 	}
3650 
3651 }
3652 
3653 };
3654 
3655 
3656 /**
3657  * While the main purpose of the library is communication functions, it was necessary to add in some utility methods.  
3658  * The AjaxTCR.util holds these methods.
3659  * 
3660  * @class AjaxTCR.util
3661  * @static
3662  */
3663 AjaxTCR.util = {};
3664 /**
3665  * Several useful utility DOM functions.  Many of these are called from other parts of the library. 
3666  * 
3667  * @class AjaxTCR.util.DOM
3668  * @static
3669  */
3670 AjaxTCR.util.DOM = {
3671 	
3672 	/** by default enable shorthanding of function names */
3673 	enableDOMShorthand : true,
3674 
3675 	/**
3676 	 * Find elements by class, will be overriden by native if found
3677 	 * If startNode is specified, starts the search there, otherwise starts at document. 
3678 	 * 
3679 	 * @param 	{string} classToFind	the string class name to search for 
3680 	 * @param	{object} startNode	the DOM node to start the search at.  Default is the document node. 
3681 	 * @return 	{array} array of elements that match the given class name.
3682 	 */
3683 	getElementsByClassName : function(classToFind,startNode){ 	
3684 		if (document.getElementsByClassName)
3685 		  return document.getElementsByClassName(classToFind,startNode);
3686 											
3687 		/* find all the elements within a particular document or in the whole document */
3688 		var elements;
3689 		if (startNode)
3690 		 elements = startNode.getElementsByTagName("*");
3691 		else
3692 		 elements = document.getElementsByTagName("*");
3693 		 
3694 		var classElements = new Array();
3695 		var classCount = 0;  
3696 		
3697 		var pattern = new RegExp("(^|\\s)"+classToFind+"(\\s|$)");
3698 		
3699 		/* look over the elements and find those who match the class passed */
3700 		for (var i = 0; i < elements.length; i++)
3701 		  if (pattern.test(elements[i].className) )
3702 		      classElements[classCount++] = elements[i];
3703 											
3704 		return classElements;
3705 								},	
3706 
3707 	/**
3708 	 * Returns element or element array specified by given string or strings.
3709 	 * 
3710 	 * @param	{object} element(s) 	strings to search for element.
3711 	 * @param 	{object} startNode	the DOM node to start the search at.  Default is the document node.
3712 	 * @return	{object} if single string, it returns the element.  Otherwise it returns an array of elements
3713 	 */
3714 	getElementsById : function(){
3715 		var elements = new Array();
3716 		var startNode = document;
3717 		var length = arguments.length;
3718 		if (typeof(arguments[length-1]) == "object" && arguments[length-1] != document)
3719 		{
3720 			startNode = arguments[length-1];
3721 			length--;
3722 			var allElements = startNode.getElementsByTagName("*");
3723 			for (var j=0; j<allElements.length; j++)
3724 			{
3725 				for (var i=0;i<length;i++)
3726 				{
3727 	   				if (allElements[j].id == arguments[i])
3728 					{
3729 	        			elements.push(allElements[j]);
3730 						break;
3731 					}
3732 				}
3733 			}
3734 		}
3735 		else
3736 		{
3737 			if (arguments[length-1] == document)
3738 				length--;
3739 				
3740 			for (var i=0; i<length; i++)
3741 			{
3742 				var elm = document.getElementById(arguments[i]);
3743 				if (elm != null)
3744 		    		elements.push(elm);
3745 			}
3746 		}
3747 		
3748 		if (elements.length == 1)
3749 			return elements[0];
3750 		else if (elements.length > 0)
3751 			return elements;
3752 		else
3753 			return null;
3754 	},
3755 		
3756 	/**
3757 	 * Modified version of getElementById to return single node match.
3758 	 * If startNode is not set to document, it starts the search at the node
3759 	 * If deepSearch is set to true, it does not use getElementById, but instead loops through the whole structure.
3760 	 * 
3761 	 * @param {string} id 			the string to match with the id attribute
3762 	 * @param {object} startNode		the DOM node to start searching in the document
3763 	 * @param {boolean} deepSearch	true if wanted to search node by node instead of document.getElementById
3764 	 */						
3765 	getElementById : function(id, startNode, deepSearch){
3766 		if (!startNode)
3767 			startNode = document;
3768 		
3769 		if (startNode == document && !deepSearch)
3770 			return document.getElementById(id);
3771 		else
3772 		{
3773 			var allElements = startNode.getElementsByTagName("*");
3774 			for (var j=0; j<allElements.length; j++)
3775 			{
3776 				if (allElements[j].getAttribute("id") == id)
3777 				{
3778 			   		return allElements[j];
3779 					break;
3780 				}
3781 			}
3782 		}
3783 	},
3784 	
3785 		
3786 	/**
3787 	 * Select nodes that match the given selector.  The selector is expected to be in CSS format
3788 	 * 
3789 	 * @param {string} 	selector		string indicating the selection to match
3790 	 * @param {object}	treeRoot		DOM element to start search.  Default is the document node
3791 	 * @return 	array of matching elements
3792 	 * 
3793 	 */						
3794 	getElementsBySelector : function(selector,treeRoot,selectorType){
3795 		var matches = new Array();
3796 		var parents = new Array();
3797 		var savematches = new Array();
3798 		if (treeRoot)
3799 		{
3800 			if (treeRoot.length)
3801 			{
3802 				for (var i=0;i<treeRoot.length;i++)
3803 					parents.push(treeRoot[i]);
3804 			}
3805 			else
3806 				parents.push(treeRoot);
3807 		}
3808 		else
3809 			parents.push(document);
3810 			
3811 		if (!selectorType)
3812 			selectorType = "CSS";
3813 		if (selectorType.toUpperCase() == "CSS")
3814 		{
3815 			selector = selector.replace(/([>\+,])/g, " $1 ").replace(/[\s]+/g," ");
3816 			 
3817 			var selectors = selector.split(" ");
3818 			while (selectors.length > 0)
3819 			{
3820 				var curSelector = selectors.shift();
3821 				if (curSelector == "")
3822 					continue;
3823 					
3824 				/* check for expressions */
3825 				var options = {};
3826 				switch(curSelector.charAt(0))
3827 				{
3828 					case(">"):
3829 						options.type = "childOnly";
3830 					break;
3831 					case("+"):
3832 						options.type = "nextSibling";
3833 					break;
3834 					case ("~"):
3835 						options.type = "futureSibling";
3836 					break;
3837 					case(","):
3838 						while(matches.length > 0)
3839 							savematches.push(matches.shift());
3840 								
3841 						parents.length = 0;					
3842 						if (treeRoot)
3843 							parents.push(treeRoot);
3844 						else
3845 							parents.push(document);
3846 								
3847 						continue;
3848 					break;
3849 				}
3850 				
3851 				if (options.type)
3852 				{
3853 					if (curSelector.length == 1)
3854 						curSelector = selectors.shift();
3855 					else
3856 						curSelector = curSelector.substring(1);
3857 				}
3858 				
3859 				/* Check to see if we already looped though.  If so, we have a different starting point */
3860 				if (matches.length)
3861 				{
3862 					parents.length = 0;
3863 					while(matches.length > 0)
3864 						parents.push(matches.shift());
3865 				}
3866 				
3867 				
3868 				/* Check for Pseudo-classes */
3869 				if (curSelector.indexOf(":") > -1)
3870 				{
3871 					var newSelector = curSelector.substring(0, curSelector.indexOf(":"));
3872 					var optionsType = curSelector.substring(curSelector.indexOf(":")+1);
3873 					
3874 					curSelector = newSelector;
3875 					options.type = optionsType.toLowerCase();
3876 					
3877 					if (options.type.indexOf("nth-child") == 0)
3878 					{
3879 						options.childNumber = options.type.substring(10,options.type.length-1);
3880 						options.type = "nth-child";
3881 					}
3882 					else if (options.type.indexOf("not") == 0)
3883 					{
3884 						//use optionsType to preserve case
3885 						options.notString = optionsType.substring(4,options.type.length-1).replace(/^\s+|\s+$/g,"");
3886 						options.type = "not";
3887 						var notSelector = curSelector;
3888 						if (notSelector == "*")
3889 							notSelector = "";
3890 						if (/^[:#\[\.].*/.test(options.notString))
3891 							options.notSelector = notSelector + options.notString;
3892 						else
3893 							options.notSelector = notSelector + " " + options.notString;
3894 						
3895 						options.notObjects = AjaxTCR.util.DOM.getElementsBySelector(options.notSelector, parents);	
3896 					}
3897 				}
3898 				
3899 				/* Check for Attributes */
3900 				if (curSelector.indexOf("[") > -1)
3901 				{
3902 					var tokens = curSelector.split("[");
3903 					curSelector = tokens[0];
3904 					options.type = "attribute";
3905 					options.attribute = tokens[1].substring(0,tokens[1].length-1).toLowerCase();
3906 				}
3907 				
3908 				if (curSelector == "")
3909 					curSelector = "*";
3910 				
3911 				/* Inspect class selectors */
3912 				if (curSelector.indexOf(".") > -1)
3913 				{
3914 					/* Cases:
3915 					 * p.class1
3916 					 * .class2
3917 					 * div.class1.class2
3918 					 */
3919 					var classNames = curSelector.split(".");
3920 					var elementName = classNames.shift();
3921 					/* First get the element at the beginning if necessary */
3922 					if (elementName != "")
3923 					{
3924 						for (var j=0;j<parents.length;j++)
3925 						{
3926 							var elms = AjaxTCR.util.DOM._getElementsByTagName(parents[j],elementName,options);
3927 							for (var k=0;k<elms.length;k++)
3928 							{
3929 								if (checkFilter(elms[k], parents[j], options))
3930 									matches.push(elms[k]);
3931 							}
3932 						}
3933 					}
3934 					else if (classNames.length > 0)
3935 					{
3936 						/* if no element is specified, use getElementsByClassName for the first class */
3937 						var firstClass = classNames.shift();
3938 						for (var j=0;j<parents.length;j++)
3939 						{
3940 							var elms = AjaxTCR.util.DOM.getElementsByClassName(firstClass, parents[j]);
3941 							for (var k=0;k<elms.length;k++)
3942 							{
3943 								if (checkFilter(elms[k],parents[j],options))
3944 									matches.push(elms[k]);
3945 							}
3946 						}
3947 					}
3948 				
3949 					/* Now get the (rest of the) classes */
3950 					for (var j=matches.length-1;j>=0;j--)
3951 					{
3952 						for (var k=0;k<classNames.length;k++)
3953 						{
3954 							var pattern = new RegExp("(^|\\s)"+classNames[k]+"(\\s|$)");
3955 							if (!pattern.test(matches[j].className))
3956 							{
3957 								matches.splice(j,1);
3958 								break;
3959 							} 
3960 						}
3961 					}
3962 				}
3963 				
3964 				/* Inspect id selectors */
3965 				else if (curSelector.indexOf("#") > -1)
3966 				{
3967 					/* Cases:
3968 					 * p#id1
3969 					 * #id2
3970 					 */
3971 					var idNames = curSelector.split("#");
3972 					var elementName = idNames[0];
3973 					var id = idNames[1];
3974 					
3975 					/* First get the element at the beginning if necessary */
3976 					if (elementName != "")
3977 					{
3978 						for (var j=0;j<parents.length;j++)
3979 						{
3980 							var elms = AjaxTCR.util.DOM._getElementsByTagName(parents[j],elementName,options);
3981 							for (var k=0;k<elms.length;k++)
3982 							{
3983 								if (elms[k].id == id && checkFilter(elms[k], parents[j], options))  
3984 									matches.push(elms[k]);
3985 							}
3986 						}
3987 					}
3988 					else
3989 					{
3990 						for (var j=0;j<parents.length;j++)
3991 						{
3992 							var elms = AjaxTCR.util.DOM.getElementsById(id, parents[j]);
3993 							if (checkFilter(elms, parents[j], options))
3994 								matches.push(elms);
3995 						}
3996 					}
3997 				}
3998 				/* Simple tagname selects */
3999 				else
4000 				{
4001 					for (var j=0;j<parents.length;j++)
4002 					{
4003 						var elms =AjaxTCR.util.DOM._getElementsByTagName(parents[j],curSelector,options);
4004 						for (var k=0;k<elms.length;k++)
4005 						{
4006 							if (checkFilter(elms[k], parents[j], options))
4007 								matches.push(elms[k]);
4008 						}
4009 					}
4010 				}
4011 			}
4012 		}
4013 		
4014 		
4015 		function checkFilter(element, parent, options)
4016 		{
4017 			var valid = false;
4018 			
4019 			if (element == null)
4020 				return false;
4021 			else if (!options.type)
4022 				return true;
4023 				
4024 			//handle the case of the parent element being the document	
4025 			if (parent == document)
4026 			{
4027 				var allElms = document.getElementsByTagName("*");
4028 				for (var i=0;i<allElms.length;i++)
4029 				{
4030 					if( checkFilter(element, allElms[i], options))
4031 					{
4032 						valid = true;
4033 						break;
4034 					}
4035 				}
4036 			
4037 				return valid;
4038 			}
4039 			
4040 			
4041 			if (options.type == "childOnly")
4042 				valid = (element.parentNode == parent);
4043 			else if (options.type == "nextSibling")
4044 			{
4045 				var elm = parent.nextSibling;
4046 				while (elm != null && elm.nodeType != 1)
4047 					elm = elm.nextSibling;
4048 				valid = (elm == element);
4049 			}
4050 			else if (options.type == "futureSibling")
4051 			{
4052 				var elm = parent.nextSibling;
4053 				while (elm != null)
4054 				{
4055 					if (elm == element)
4056 					{
4057 						valid = true;
4058 						break;
4059 					}
4060 					elm = elm.nextSibling;
4061 				}
4062 			}	
4063 			else if (options.type == "first-child")
4064 			{
4065 				var elm = parent.firstChild;
4066 				while (elm != null && elm.nodeType != 1)
4067 					elm = elm.nextSibling;
4068 				valid = (elm == element); 
4069 			}		
4070 			else if (options.type == "last-child")
4071 			{
4072 				var elm = parent.lastChild;
4073 				while (elm != null && elm.nodeType != 1)
4074 					elm = elm.previousSibling;
4075 				valid = (elm == element); 
4076 			}
4077 			else if (options.type == "only-child")
4078 			{
4079 				var elm = parent.firstChild;
4080 				while (elm != null && elm.nodeType != 1)
4081 					elm = elm.nextSibling;
4082 				
4083 				if (elm == element)
4084 				{
4085 					var elm = parent.lastChild;
4086 					while (elm != null && elm.nodeType != 1)
4087 						elm = elm.previousSibling;
4088 				}
4089 				
4090 				valid = (elm == element);
4091 			}
4092 			else if (options.type == "nth-child")
4093 			{
4094 				var count = 0;
4095 				var elm = parent.firstChild;
4096 				while (elm != null  && count < options.childNumber)
4097 				{
4098 					if (elm.nodeType == 1)
4099 						count++;	
4100 					
4101 					if (count == options.childNumber)
4102 						break;
4103 					
4104 					elm = elm.nextSibling;
4105 				}
4106 				 
4107 				valid = (elm == element);
4108 			}
4109 			else if (options.type == "empty")
4110 				valid = (element.childNodes.length == 0);
4111 			else if (options.type == "enabled")
4112 				valid = (!element.disabled);
4113 			else if (options.type == "disabled")
4114 				valid = (element.disabled);
4115 			else if (options.type == "checked")
4116 				valid = (element.checked);
4117 			else if (options.type == "selected")
4118 				valid = (element.selected);
4119 			else if (options.type == "attribute")
4120 			{
4121 				var pattern = /^\s*([\w-]+)\s*([!*$^~=]*)\s*(['|\"]?)(.*)\3/;
4122 				var attRules = pattern.exec(options.attribute);
4123 				
4124 				if (attRules[2] == "")
4125 					valid = element.getAttribute(attRules[1]);
4126 				else if (attRules[2] == "=")
4127 					valid = (element.getAttribute(attRules[1]) && element.getAttribute(attRules[1]).toLowerCase() == attRules[4].toLowerCase());
4128 				else if (attRules[2] == "^=")
4129 					valid = (element.getAttribute(attRules[1]) && element.getAttribute(attRules[1]).toLowerCase().indexOf(attRules[4].toLowerCase()) == 0);
4130 				else if (attRules[2] == "*=")
4131 					valid = (element.getAttribute(attRules[1]) && element.getAttribute(attRules[1]).toLowerCase().indexOf(attRules[4].toLowerCase()) > -1);
4132 				else if (attRules[2] == "$=")
4133 				{
4134 					var att =element.getAttribute(attRules[1]);
4135 					if (att)
4136 						valid =  (att.toLowerCase().substring(att.length - attRules[4].length) == attRules[4].toLowerCase()); 
4137 				}				
4138 			}
4139 			else if (options.type == "not")
4140 			{
4141 				valid = true;
4142 				for (var j=0;j<options.notObjects.length;j++)
4143 				{
4144 					if (options.notObjects[j] == element)
4145 					{
4146 						valid = false;
4147 						break;
4148 					}
4149 				}
4150 			}
4151 			
4152 			
4153 			return valid;					
4154 		}
4155 		
4156 		/* get the results in the correct order */
4157 		if (savematches.length)
4158 		{
4159 			while(matches.length > 0)
4160 				savematches.push(matches.shift());
4161 			while(savematches.length > 0)
4162 				matches.push(savematches.shift());
4163 		}
4164 		return matches;
4165 	},	
4166 							
4167 		
4168 /**
4169  * Custom getElementsByTagName that takes various options into consideration before returning the values
4170  * 
4171  * @private 
4172  * @param {object} parentElm	element to begin the search at
4173  * @param {string} tag		string to match tagName to
4174  * @param options
4175  * @return Matching nodes
4176  */					
4177 _getElementsByTagName : function(parentElm, tag, options){
4178 	var matches = new Array();
4179 	if (!options.type)
4180 		return parentElm.getElementsByTagName(tag);
4181 	
4182 	
4183 	if (options.type == "nextSibling")
4184 	{
4185 		var elm = parentElm.nextSibling;
4186 		while (elm && elm.nodeType != 1)
4187 			elm = elm.nextSibling;
4188 		
4189 		if (checkTagMatch(elm, tag))
4190 			matches.push(elm);
4191 	}
4192 	else if (options.type == "futureSibling")
4193 	{
4194 		var elm = parentElm.nextSibling;
4195 		while (elm)
4196 		{
4197 			if (checkTagMatch(elm, tag))
4198 			{
4199 				matches.push(elm);
4200 				//break;
4201 			}
4202 			elm = elm.nextSibling;
4203 		}	
4204 	}
4205 	else
4206 		matches = parentElm.getElementsByTagName(tag);
4207 	
4208 	function checkTagMatch(element, tag)
4209 	{
4210 		return (element && element.tagName && (tag == "*" || element.tagName.toUpperCase() == tag.toUpperCase()));
4211 	}
4212 			
4213 	return matches;
4214 },
4215 
4216 /**
4217  * Inserts the passed object after the sibling with the given pareny
4218  * @param {Object} parent The parent of the node to insert
4219  * @param {Object} obj The new node to insert
4220  * @param {Object} sibling The sibling to insert after
4221  */
4222 insertAfter : function(parent, obj, sibling){
4223 	if (parent && obj && sibling && sibling.nextSibling)
4224 		parent.insertBefore(obj, sibling.nextSibling);
4225 	else if (parent && obj)
4226 		parent.appendChild(obj);
4227 }
4228 };
4229 
4230 
4231 /**
4232  * The library doesn't currently offer large event handling support, but some basics are included in this calss
4233  * 
4234  * @class AjaxTCR.util.event
4235  * @static
4236  */
4237 AjaxTCR.util.event = {
4238 /**
4239  * addWindowLoadEvent - simple method to allow for safe addition of window.onload called functions.  Assumes everyone else plays nicely though.
4240  * 
4241  * @param {object} newFunction - function to call upon page load
4242  */					
4243 
4244 addWindowLoadEvent: function(newFunction) {
4245 	
4246 	var oldFunction = window.onload;
4247 	if (typeof window.onload != "function")
4248 	  {
4249 	   window.onload = newFunction;
4250 	  }
4251 	else 
4252 	  {
4253   	   window.onload = function () {
4254 	     if (oldFunction)
4255 		   {
4256 		   	oldFunction();
4257 		   }
4258 		 newFunction();
4259 	    };
4260 	  }	
4261 }
4262 
4263 };
4264 
4265 /**
4266  * The place for anything that doesn't have a home
4267  * 
4268  * @class AjaxTCR.util.misc
4269  * @static
4270  */
4271 AjaxTCR.util.misc = {
4272 	/**
4273 	 * generateUID : Generates a unique value.  If 'prefix' is set to -1, only returns the numerical value. 
4274 	 * 				 If prefix isn't set, it sets it to "AjaxTCR".
4275 	 * 
4276 	 * @param {string} [prefix] the string value to prepend to the uniquevalue
4277 	 * @return the string consisting of the prefix and the uniquevalue
4278 	 */
4279 	generateUID : function(prefix){
4280 		if (prefix == "-1")
4281 			prefix = "";
4282 		else if (!prefix)
4283 			prefix = "AjaxTCR";
4284 		
4285 		var uniquevalue = new Date().getTime().toString() + Math.floor(Math.random()*100);
4286 		return prefix + uniquevalue;
4287 
4288 	}	
4289 };
4290 
4291 /******************************************************************************************************/
4292 
4293 /*
4294  * onLibraryLoad
4295  * 
4296  */
4297 
4298 AjaxTCR.onLibraryLoad = function(){
4299 	
4300    if (AjaxTCR.util.DOM.enableDOMShorthand)
4301      {
4302       if (typeof($id) == "undefined") $id = AjaxTCR.util.DOM.getElementsById;
4303       if (typeof($class) == "undefined") $class = AjaxTCR.util.DOM.getElementsByClassName;  
4304       if (typeof($selector) == "undefined") $selector = AjaxTCR.util.DOM.getElementsBySelector;
4305      }
4306 
4307 
4308 	if(navigator.userAgent.toLowerCase().indexOf("msie")>-1)
4309 	 {
4310 		if (window.location.hash && window.location.hash.substring(1) != AjaxTCR.history._currentState)
4311 			var src = AjaxTCR.history._iframeSrc + "?hash=" + window.location.hash.substring(1);
4312 		else
4313 			var src = AjaxTCR.history._iframeSrc + "?hash=";
4314 													
4315 		document.write(  '<iframe id="ieFix"  src="' + src + '" style="visibility:hidden;" width="1px" height="1px"></iframe>');
4316 	 }
4317 	 
4318 	 AjaxTCR.template._includeLibrary();
4319 };
4320 
4321 /* do any library load bindings */
4322 AjaxTCR.onLibraryLoad();
4323 
4324