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="https://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