PageRenderTime 85ms CodeModel.GetById 15ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 1ms

/hippo/src/main/webapp/yui/datasource/datasource.js

http://hdbc.googlecode.com/
JavaScript | 1983 lines | 1148 code | 131 blank | 704 comment | 188 complexity | e15c2d16753b7e4339e210a3d6696a1d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2Copyright (c) 2009, Yahoo! Inc. All rights reserved.
   3Code licensed under the BSD License:
   4http://developer.yahoo.net/yui/license.txt
   5version: 2.7.0
   6*/
   7(function () {
   8
   9var lang   = YAHOO.lang,
  10    util   = YAHOO.util,
  11    Ev     = util.Event;
  12
  13/**
  14 * The DataSource utility provides a common configurable interface for widgets to
  15 * access a variety of data, from JavaScript arrays to online database servers.
  16 *
  17 * @module datasource
  18 * @requires yahoo, event
  19 * @optional json, get, connection 
  20 * @title DataSource Utility
  21 */
  22
  23/****************************************************************************/
  24/****************************************************************************/
  25/****************************************************************************/
  26
  27/**
  28 * Base class for the YUI DataSource utility.
  29 *
  30 * @namespace YAHOO.util
  31 * @class YAHOO.util.DataSourceBase
  32 * @constructor
  33 * @param oLiveData {HTMLElement}  Pointer to live data.
  34 * @param oConfigs {object} (optional) Object literal of configuration values.
  35 */
  36util.DataSourceBase = function(oLiveData, oConfigs) {
  37    if(oLiveData === null || oLiveData === undefined) {
  38        return;
  39    }
  40    
  41    this.liveData = oLiveData;
  42    this._oQueue = {interval:null, conn:null, requests:[]};
  43    this.responseSchema = {};   
  44
  45    // Set any config params passed in to override defaults
  46    if(oConfigs && (oConfigs.constructor == Object)) {
  47        for(var sConfig in oConfigs) {
  48            if(sConfig) {
  49                this[sConfig] = oConfigs[sConfig];
  50            }
  51        }
  52    }
  53    
  54    // Validate and initialize public configs
  55    var maxCacheEntries = this.maxCacheEntries;
  56    if(!lang.isNumber(maxCacheEntries) || (maxCacheEntries < 0)) {
  57        maxCacheEntries = 0;
  58    }
  59
  60    // Initialize interval tracker
  61    this._aIntervals = [];
  62
  63    /////////////////////////////////////////////////////////////////////////////
  64    //
  65    // Custom Events
  66    //
  67    /////////////////////////////////////////////////////////////////////////////
  68
  69    /**
  70     * Fired when a request is made to the local cache.
  71     *
  72     * @event cacheRequestEvent
  73     * @param oArgs.request {Object} The request object.
  74     * @param oArgs.callback {Object} The callback object.
  75     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
  76     */
  77    this.createEvent("cacheRequestEvent");
  78
  79    /**
  80     * Fired when data is retrieved from the local cache.
  81     *
  82     * @event cacheResponseEvent
  83     * @param oArgs.request {Object} The request object.
  84     * @param oArgs.response {Object} The response object.
  85     * @param oArgs.callback {Object} The callback object.
  86     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
  87     */
  88    this.createEvent("cacheResponseEvent");
  89
  90    /**
  91     * Fired when a request is sent to the live data source.
  92     *
  93     * @event requestEvent
  94     * @param oArgs.request {Object} The request object.
  95     * @param oArgs.callback {Object} The callback object.
  96     * @param oArgs.tId {Number} Transaction ID.     
  97     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
  98     */
  99    this.createEvent("requestEvent");
 100
 101    /**
 102     * Fired when live data source sends response.
 103     *
 104     * @event responseEvent
 105     * @param oArgs.request {Object} The request object.
 106     * @param oArgs.response {Object} The raw response object.
 107     * @param oArgs.callback {Object} The callback object.
 108     * @param oArgs.tId {Number} Transaction ID.     
 109     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
 110     */
 111    this.createEvent("responseEvent");
 112
 113    /**
 114     * Fired when response is parsed.
 115     *
 116     * @event responseParseEvent
 117     * @param oArgs.request {Object} The request object.
 118     * @param oArgs.response {Object} The parsed response object.
 119     * @param oArgs.callback {Object} The callback object.
 120     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
 121     */
 122    this.createEvent("responseParseEvent");
 123
 124    /**
 125     * Fired when response is cached.
 126     *
 127     * @event responseCacheEvent
 128     * @param oArgs.request {Object} The request object.
 129     * @param oArgs.response {Object} The parsed response object.
 130     * @param oArgs.callback {Object} The callback object.
 131     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
 132     */
 133    this.createEvent("responseCacheEvent");
 134    /**
 135     * Fired when an error is encountered with the live data source.
 136     *
 137     * @event dataErrorEvent
 138     * @param oArgs.request {Object} The request object.
 139     * @param oArgs.callback {Object} The callback object.
 140     * @param oArgs.caller {Object} (deprecated) Use callback.scope.
 141     * @param oArgs.message {String} The error message.
 142     */
 143    this.createEvent("dataErrorEvent");
 144
 145    /**
 146     * Fired when the local cache is flushed.
 147     *
 148     * @event cacheFlushEvent
 149     */
 150    this.createEvent("cacheFlushEvent");
 151
 152    var DS = util.DataSourceBase;
 153    this._sName = "DataSource instance" + DS._nIndex;
 154    DS._nIndex++;
 155};
 156
 157var DS = util.DataSourceBase;
 158
 159lang.augmentObject(DS, {
 160
 161/////////////////////////////////////////////////////////////////////////////
 162//
 163// DataSourceBase public constants
 164//
 165/////////////////////////////////////////////////////////////////////////////
 166
 167/**
 168 * Type is unknown.
 169 *
 170 * @property TYPE_UNKNOWN
 171 * @type Number
 172 * @final
 173 * @default -1
 174 */
 175TYPE_UNKNOWN : -1,
 176
 177/**
 178 * Type is a JavaScript Array.
 179 *
 180 * @property TYPE_JSARRAY
 181 * @type Number
 182 * @final
 183 * @default 0
 184 */
 185TYPE_JSARRAY : 0,
 186
 187/**
 188 * Type is a JavaScript Function.
 189 *
 190 * @property TYPE_JSFUNCTION
 191 * @type Number
 192 * @final
 193 * @default 1
 194 */
 195TYPE_JSFUNCTION : 1,
 196
 197/**
 198 * Type is hosted on a server via an XHR connection.
 199 *
 200 * @property TYPE_XHR
 201 * @type Number
 202 * @final
 203 * @default 2
 204 */
 205TYPE_XHR : 2,
 206
 207/**
 208 * Type is JSON.
 209 *
 210 * @property TYPE_JSON
 211 * @type Number
 212 * @final
 213 * @default 3
 214 */
 215TYPE_JSON : 3,
 216
 217/**
 218 * Type is XML.
 219 *
 220 * @property TYPE_XML
 221 * @type Number
 222 * @final
 223 * @default 4
 224 */
 225TYPE_XML : 4,
 226
 227/**
 228 * Type is plain text.
 229 *
 230 * @property TYPE_TEXT
 231 * @type Number
 232 * @final
 233 * @default 5
 234 */
 235TYPE_TEXT : 5,
 236
 237/**
 238 * Type is an HTML TABLE element. Data is parsed out of TR elements from all TBODY elements.
 239 *
 240 * @property TYPE_HTMLTABLE
 241 * @type Number
 242 * @final
 243 * @default 6
 244 */
 245TYPE_HTMLTABLE : 6,
 246
 247/**
 248 * Type is hosted on a server via a dynamic script node.
 249 *
 250 * @property TYPE_SCRIPTNODE
 251 * @type Number
 252 * @final
 253 * @default 7
 254 */
 255TYPE_SCRIPTNODE : 7,
 256
 257/**
 258 * Type is local.
 259 *
 260 * @property TYPE_LOCAL
 261 * @type Number
 262 * @final
 263 * @default 8
 264 */
 265TYPE_LOCAL : 8,
 266
 267/**
 268 * Error message for invalid dataresponses.
 269 *
 270 * @property ERROR_DATAINVALID
 271 * @type String
 272 * @final
 273 * @default "Invalid data"
 274 */
 275ERROR_DATAINVALID : "Invalid data",
 276
 277/**
 278 * Error message for null data responses.
 279 *
 280 * @property ERROR_DATANULL
 281 * @type String
 282 * @final
 283 * @default "Null data"
 284 */
 285ERROR_DATANULL : "Null data",
 286
 287/////////////////////////////////////////////////////////////////////////////
 288//
 289// DataSourceBase private static properties
 290//
 291/////////////////////////////////////////////////////////////////////////////
 292
 293/**
 294 * Internal class variable to index multiple DataSource instances.
 295 *
 296 * @property DataSourceBase._nIndex
 297 * @type Number
 298 * @private
 299 * @static
 300 */
 301_nIndex : 0,
 302
 303/**
 304 * Internal class variable to assign unique transaction IDs.
 305 *
 306 * @property DataSourceBase._nTransactionId
 307 * @type Number
 308 * @private
 309 * @static
 310 */
 311_nTransactionId : 0,
 312
 313/////////////////////////////////////////////////////////////////////////////
 314//
 315// DataSourceBase public static methods
 316//
 317/////////////////////////////////////////////////////////////////////////////
 318
 319/**
 320 * Executes a configured callback.  For object literal callbacks, the third
 321 * param determines whether to execute the success handler or failure handler.
 322 *  
 323 * @method issueCallback
 324 * @param callback {Function|Object} the callback to execute
 325 * @param params {Array} params to be passed to the callback method
 326 * @param error {Boolean} whether an error occurred
 327 * @param scope {Object} the scope from which to execute the callback
 328 * (deprecated - use an object literal callback)
 329 * @static     
 330 */
 331issueCallback : function (callback,params,error,scope) {
 332    if (lang.isFunction(callback)) {
 333        callback.apply(scope, params);
 334    } else if (lang.isObject(callback)) {
 335        scope = callback.scope || scope || window;
 336        var callbackFunc = callback.success;
 337        if (error) {
 338            callbackFunc = callback.failure;
 339        }
 340        if (callbackFunc) {
 341            callbackFunc.apply(scope, params.concat([callback.argument]));
 342        }
 343    }
 344},
 345
 346/**
 347 * Converts data to type String.
 348 *
 349 * @method DataSourceBase.parseString
 350 * @param oData {String | Number | Boolean | Date | Array | Object} Data to parse.
 351 * The special values null and undefined will return null.
 352 * @return {String} A string, or null.
 353 * @static
 354 */
 355parseString : function(oData) {
 356    // Special case null and undefined
 357    if(!lang.isValue(oData)) {
 358        return null;
 359    }
 360    
 361    //Convert to string
 362    var string = oData + "";
 363
 364    // Validate
 365    if(lang.isString(string)) {
 366        return string;
 367    }
 368    else {
 369        return null;
 370    }
 371},
 372
 373/**
 374 * Converts data to type Number.
 375 *
 376 * @method DataSourceBase.parseNumber
 377 * @param oData {String | Number | Boolean} Data to convert. Note, the following
 378 * values return as null: null, undefined, NaN, "". 
 379 * @return {Number} A number, or null.
 380 * @static
 381 */
 382parseNumber : function(oData) {
 383    if(!lang.isValue(oData) || (oData === "")) {
 384        return null;
 385    }
 386
 387    //Convert to number
 388    var number = oData * 1;
 389    
 390    // Validate
 391    if(lang.isNumber(number)) {
 392        return number;
 393    }
 394    else {
 395        return null;
 396    }
 397},
 398// Backward compatibility
 399convertNumber : function(oData) {
 400    return DS.parseNumber(oData);
 401},
 402
 403/**
 404 * Converts data to type Date.
 405 *
 406 * @method DataSourceBase.parseDate
 407 * @param oData {Date | String | Number} Data to convert.
 408 * @return {Date} A Date instance.
 409 * @static
 410 */
 411parseDate : function(oData) {
 412    var date = null;
 413    
 414    //Convert to date
 415    if(!(oData instanceof Date)) {
 416        date = new Date(oData);
 417    }
 418    else {
 419        return oData;
 420    }
 421    
 422    // Validate
 423    if(date instanceof Date) {
 424        return date;
 425    }
 426    else {
 427        return null;
 428    }
 429},
 430// Backward compatibility
 431convertDate : function(oData) {
 432    return DS.parseDate(oData);
 433}
 434
 435});
 436
 437// Done in separate step so referenced functions are defined.
 438/**
 439 * Data parsing functions.
 440 * @property DataSource.Parser
 441 * @type Object
 442 * @static
 443 */
 444DS.Parser = {
 445    string   : DS.parseString,
 446    number   : DS.parseNumber,
 447    date     : DS.parseDate
 448};
 449
 450// Prototype properties and methods
 451DS.prototype = {
 452
 453/////////////////////////////////////////////////////////////////////////////
 454//
 455// DataSourceBase private properties
 456//
 457/////////////////////////////////////////////////////////////////////////////
 458
 459/**
 460 * Name of DataSource instance.
 461 *
 462 * @property _sName
 463 * @type String
 464 * @private
 465 */
 466_sName : null,
 467
 468/**
 469 * Local cache of data result object literals indexed chronologically.
 470 *
 471 * @property _aCache
 472 * @type Object[]
 473 * @private
 474 */
 475_aCache : null,
 476
 477/**
 478 * Local queue of request connections, enabled if queue needs to be managed.
 479 *
 480 * @property _oQueue
 481 * @type Object
 482 * @private
 483 */
 484_oQueue : null,
 485
 486/**
 487 * Array of polling interval IDs that have been enabled, needed to clear all intervals.
 488 *
 489 * @property _aIntervals
 490 * @type Array
 491 * @private
 492 */
 493_aIntervals : null,
 494
 495/////////////////////////////////////////////////////////////////////////////
 496//
 497// DataSourceBase public properties
 498//
 499/////////////////////////////////////////////////////////////////////////////
 500
 501/**
 502 * Max size of the local cache.  Set to 0 to turn off caching.  Caching is
 503 * useful to reduce the number of server connections.  Recommended only for data
 504 * sources that return comprehensive results for queries or when stale data is
 505 * not an issue.
 506 *
 507 * @property maxCacheEntries
 508 * @type Number
 509 * @default 0
 510 */
 511maxCacheEntries : 0,
 512
 513 /**
 514 * Pointer to live database.
 515 *
 516 * @property liveData
 517 * @type Object
 518 */
 519liveData : null,
 520
 521/**
 522 * Where the live data is held:
 523 * 
 524 * <dl>  
 525 *    <dt>TYPE_UNKNOWN</dt>
 526 *    <dt>TYPE_LOCAL</dt>
 527 *    <dt>TYPE_XHR</dt>
 528 *    <dt>TYPE_SCRIPTNODE</dt>
 529 *    <dt>TYPE_JSFUNCTION</dt>
 530 * </dl> 
 531 *  
 532 * @property dataType
 533 * @type Number
 534 * @default YAHOO.util.DataSourceBase.TYPE_UNKNOWN
 535 *
 536 */
 537dataType : DS.TYPE_UNKNOWN,
 538
 539/**
 540 * Format of response:
 541 *  
 542 * <dl>  
 543 *    <dt>TYPE_UNKNOWN</dt>
 544 *    <dt>TYPE_JSARRAY</dt>
 545 *    <dt>TYPE_JSON</dt>
 546 *    <dt>TYPE_XML</dt>
 547 *    <dt>TYPE_TEXT</dt>
 548 *    <dt>TYPE_HTMLTABLE</dt> 
 549 * </dl> 
 550 *
 551 * @property responseType
 552 * @type Number
 553 * @default YAHOO.util.DataSourceBase.TYPE_UNKNOWN
 554 */
 555responseType : DS.TYPE_UNKNOWN,
 556
 557/**
 558 * Response schema object literal takes a combination of the following properties:
 559 *
 560 * <dl>
 561 * <dt>resultsList</dt> <dd>Pointer to array of tabular data</dd>
 562 * <dt>resultNode</dt> <dd>Pointer to node name of row data (XML data only)</dd>
 563 * <dt>recordDelim</dt> <dd>Record delimiter (text data only)</dd>
 564 * <dt>fieldDelim</dt> <dd>Field delimiter (text data only)</dd>
 565 * <dt>fields</dt> <dd>Array of field names (aka keys), or array of object literals
 566 * such as: {key:"fieldname",parser:YAHOO.util.DataSourceBase.parseDate}</dd>
 567 * <dt>metaFields</dt> <dd>Object literal of keys to include in the oParsedResponse.meta collection</dd>
 568 * <dt>metaNode</dt> <dd>Name of the node under which to search for meta information in XML response data</dd>
 569 * </dl>
 570 *
 571 * @property responseSchema
 572 * @type Object
 573 */
 574responseSchema : null,
 575
 576/**
 577 * Additional arguments passed to the JSON parse routine.  The JSON string
 578 * is the assumed first argument (where applicable).  This property is not
 579 * set by default, but the parse methods will use it if present.
 580 *
 581 * @property parseJSONArgs
 582 * @type {MIXED|Array} If an Array, contents are used as individual arguments.
 583 *                     Otherwise, value is used as an additional argument.
 584 */
 585// property intentionally undefined
 586 
 587/////////////////////////////////////////////////////////////////////////////
 588//
 589// DataSourceBase public methods
 590//
 591/////////////////////////////////////////////////////////////////////////////
 592
 593/**
 594 * Public accessor to the unique name of the DataSource instance.
 595 *
 596 * @method toString
 597 * @return {String} Unique name of the DataSource instance.
 598 */
 599toString : function() {
 600    return this._sName;
 601},
 602
 603/**
 604 * Overridable method passes request to cache and returns cached response if any,
 605 * refreshing the hit in the cache as the newest item. Returns null if there is
 606 * no cache hit.
 607 *
 608 * @method getCachedResponse
 609 * @param oRequest {Object} Request object.
 610 * @param oCallback {Object} Callback object.
 611 * @param oCaller {Object} (deprecated) Use callback object.
 612 * @return {Object} Cached response object or null.
 613 */
 614getCachedResponse : function(oRequest, oCallback, oCaller) {
 615    var aCache = this._aCache;
 616
 617    // If cache is enabled...
 618    if(this.maxCacheEntries > 0) {        
 619        // Initialize local cache
 620        if(!aCache) {
 621            this._aCache = [];
 622        }
 623        // Look in local cache
 624        else {
 625            var nCacheLength = aCache.length;
 626            if(nCacheLength > 0) {
 627                var oResponse = null;
 628                this.fireEvent("cacheRequestEvent", {request:oRequest,callback:oCallback,caller:oCaller});
 629        
 630                // Loop through each cached element
 631                for(var i = nCacheLength-1; i >= 0; i--) {
 632                    var oCacheElem = aCache[i];
 633        
 634                    // Defer cache hit logic to a public overridable method
 635                    if(this.isCacheHit(oRequest,oCacheElem.request)) {
 636                        // The cache returned a hit!
 637                        // Grab the cached response
 638                        oResponse = oCacheElem.response;
 639                        this.fireEvent("cacheResponseEvent", {request:oRequest,response:oResponse,callback:oCallback,caller:oCaller});
 640                        
 641                        // Refresh the position of the cache hit
 642                        if(i < nCacheLength-1) {
 643                            // Remove element from its original location
 644                            aCache.splice(i,1);
 645                            // Add as newest
 646                            this.addToCache(oRequest, oResponse);
 647                        }
 648                        
 649                        // Add a cache flag
 650                        oResponse.cached = true;
 651                        break;
 652                    }
 653                }
 654                return oResponse;
 655            }
 656        }
 657    }
 658    else if(aCache) {
 659        this._aCache = null;
 660    }
 661    return null;
 662},
 663
 664/**
 665 * Default overridable method matches given request to given cached request.
 666 * Returns true if is a hit, returns false otherwise.  Implementers should
 667 * override this method to customize the cache-matching algorithm.
 668 *
 669 * @method isCacheHit
 670 * @param oRequest {Object} Request object.
 671 * @param oCachedRequest {Object} Cached request object.
 672 * @return {Boolean} True if given request matches cached request, false otherwise.
 673 */
 674isCacheHit : function(oRequest, oCachedRequest) {
 675    return (oRequest === oCachedRequest);
 676},
 677
 678/**
 679 * Adds a new item to the cache. If cache is full, evicts the stalest item
 680 * before adding the new item.
 681 *
 682 * @method addToCache
 683 * @param oRequest {Object} Request object.
 684 * @param oResponse {Object} Response object to cache.
 685 */
 686addToCache : function(oRequest, oResponse) {
 687    var aCache = this._aCache;
 688    if(!aCache) {
 689        return;
 690    }
 691
 692    // If the cache is full, make room by removing stalest element (index=0)
 693    while(aCache.length >= this.maxCacheEntries) {
 694        aCache.shift();
 695    }
 696
 697    // Add to cache in the newest position, at the end of the array
 698    var oCacheElem = {request:oRequest,response:oResponse};
 699    aCache[aCache.length] = oCacheElem;
 700    this.fireEvent("responseCacheEvent", {request:oRequest,response:oResponse});
 701},
 702
 703/**
 704 * Flushes cache.
 705 *
 706 * @method flushCache
 707 */
 708flushCache : function() {
 709    if(this._aCache) {
 710        this._aCache = [];
 711        this.fireEvent("cacheFlushEvent");
 712    }
 713},
 714
 715/**
 716 * Sets up a polling mechanism to send requests at set intervals and forward
 717 * responses to given callback.
 718 *
 719 * @method setInterval
 720 * @param nMsec {Number} Length of interval in milliseconds.
 721 * @param oRequest {Object} Request object.
 722 * @param oCallback {Function} Handler function to receive the response.
 723 * @param oCaller {Object} (deprecated) Use oCallback.scope.
 724 * @return {Number} Interval ID.
 725 */
 726setInterval : function(nMsec, oRequest, oCallback, oCaller) {
 727    if(lang.isNumber(nMsec) && (nMsec >= 0)) {
 728        var oSelf = this;
 729        var nId = setInterval(function() {
 730            oSelf.makeConnection(oRequest, oCallback, oCaller);
 731        }, nMsec);
 732        this._aIntervals.push(nId);
 733        return nId;
 734    }
 735    else {
 736    }
 737},
 738
 739/**
 740 * Disables polling mechanism associated with the given interval ID.
 741 *
 742 * @method clearInterval
 743 * @param nId {Number} Interval ID.
 744 */
 745clearInterval : function(nId) {
 746    // Remove from tracker if there
 747    var tracker = this._aIntervals || [];
 748    for(var i=tracker.length-1; i>-1; i--) {
 749        if(tracker[i] === nId) {
 750            tracker.splice(i,1);
 751            clearInterval(nId);
 752        }
 753    }
 754},
 755
 756/**
 757 * Disables all known polling intervals.
 758 *
 759 * @method clearAllIntervals
 760 */
 761clearAllIntervals : function() {
 762    var tracker = this._aIntervals || [];
 763    for(var i=tracker.length-1; i>-1; i--) {
 764        clearInterval(tracker[i]);
 765    }
 766    tracker = [];
 767},
 768
 769/**
 770 * First looks for cached response, then sends request to live data. The
 771 * following arguments are passed to the callback function:
 772 *     <dl>
 773 *     <dt><code>oRequest</code></dt>
 774 *     <dd>The same value that was passed in as the first argument to sendRequest.</dd>
 775 *     <dt><code>oParsedResponse</code></dt>
 776 *     <dd>An object literal containing the following properties:
 777 *         <dl>
 778 *         <dt><code>tId</code></dt>
 779 *         <dd>Unique transaction ID number.</dd>
 780 *         <dt><code>results</code></dt>
 781 *         <dd>Schema-parsed data results.</dd>
 782 *         <dt><code>error</code></dt>
 783 *         <dd>True in cases of data error.</dd>
 784 *         <dt><code>cached</code></dt>
 785 *         <dd>True when response is returned from DataSource cache.</dd> 
 786 *         <dt><code>meta</code></dt>
 787 *         <dd>Schema-parsed meta data.</dd>
 788 *         </dl>
 789 *     <dt><code>oPayload</code></dt>
 790 *     <dd>The same value as was passed in as <code>argument</code> in the oCallback object literal.</dd>
 791 *     </dl> 
 792 *
 793 * @method sendRequest
 794 * @param oRequest {Object} Request object.
 795 * @param oCallback {Object} An object literal with the following properties:
 796 *     <dl>
 797 *     <dt><code>success</code></dt>
 798 *     <dd>The function to call when the data is ready.</dd>
 799 *     <dt><code>failure</code></dt>
 800 *     <dd>The function to call upon a response failure condition.</dd>
 801 *     <dt><code>scope</code></dt>
 802 *     <dd>The object to serve as the scope for the success and failure handlers.</dd>
 803 *     <dt><code>argument</code></dt>
 804 *     <dd>Arbitrary data that will be passed back to the success and failure handlers.</dd>
 805 *     </dl> 
 806 * @param oCaller {Object} (deprecated) Use oCallback.scope.
 807 * @return {Number} Transaction ID, or null if response found in cache.
 808 */
 809sendRequest : function(oRequest, oCallback, oCaller) {
 810    // First look in cache
 811    var oCachedResponse = this.getCachedResponse(oRequest, oCallback, oCaller);
 812    if(oCachedResponse) {
 813        DS.issueCallback(oCallback,[oRequest,oCachedResponse],false,oCaller);
 814        return null;
 815    }
 816
 817
 818    // Not in cache, so forward request to live data
 819    return this.makeConnection(oRequest, oCallback, oCaller);
 820},
 821
 822/**
 823 * Overridable default method generates a unique transaction ID and passes 
 824 * the live data reference directly to the  handleResponse function. This
 825 * method should be implemented by subclasses to achieve more complex behavior
 826 * or to access remote data.          
 827 *
 828 * @method makeConnection
 829 * @param oRequest {Object} Request object.
 830 * @param oCallback {Object} Callback object literal.
 831 * @param oCaller {Object} (deprecated) Use oCallback.scope.
 832 * @return {Number} Transaction ID.
 833 */
 834makeConnection : function(oRequest, oCallback, oCaller) {
 835    var tId = DS._nTransactionId++;
 836    this.fireEvent("requestEvent", {tId:tId, request:oRequest,callback:oCallback,caller:oCaller});
 837
 838    /* accounts for the following cases:
 839    YAHOO.util.DataSourceBase.TYPE_UNKNOWN
 840    YAHOO.util.DataSourceBase.TYPE_JSARRAY
 841    YAHOO.util.DataSourceBase.TYPE_JSON
 842    YAHOO.util.DataSourceBase.TYPE_HTMLTABLE
 843    YAHOO.util.DataSourceBase.TYPE_XML
 844    YAHOO.util.DataSourceBase.TYPE_TEXT
 845    */
 846    var oRawResponse = this.liveData;
 847    
 848    this.handleResponse(oRequest, oRawResponse, oCallback, oCaller, tId);
 849    return tId;
 850},
 851
 852/**
 853 * Receives raw data response and type converts to XML, JSON, etc as necessary.
 854 * Forwards oFullResponse to appropriate parsing function to get turned into
 855 * oParsedResponse. Calls doBeforeCallback() and adds oParsedResponse to 
 856 * the cache when appropriate before calling issueCallback().
 857 * 
 858 * The oParsedResponse object literal has the following properties:
 859 * <dl>
 860 *     <dd><dt>tId {Number}</dt> Unique transaction ID</dd>
 861 *     <dd><dt>results {Array}</dt> Array of parsed data results</dd>
 862 *     <dd><dt>meta {Object}</dt> Object literal of meta values</dd> 
 863 *     <dd><dt>error {Boolean}</dt> (optional) True if there was an error</dd>
 864 *     <dd><dt>cached {Boolean}</dt> (optional) True if response was cached</dd>
 865 * </dl>
 866 *
 867 * @method handleResponse
 868 * @param oRequest {Object} Request object
 869 * @param oRawResponse {Object} The raw response from the live database.
 870 * @param oCallback {Object} Callback object literal.
 871 * @param oCaller {Object} (deprecated) Use oCallback.scope.
 872 * @param tId {Number} Transaction ID.
 873 */
 874handleResponse : function(oRequest, oRawResponse, oCallback, oCaller, tId) {
 875    this.fireEvent("responseEvent", {tId:tId, request:oRequest, response:oRawResponse,
 876            callback:oCallback, caller:oCaller});
 877    var xhr = (this.dataType == DS.TYPE_XHR) ? true : false;
 878    var oParsedResponse = null;
 879    var oFullResponse = oRawResponse;
 880    
 881    // Try to sniff data type if it has not been defined
 882    if(this.responseType === DS.TYPE_UNKNOWN) {
 883        var ctype = (oRawResponse && oRawResponse.getResponseHeader) ? oRawResponse.getResponseHeader["Content-Type"] : null;
 884        if(ctype) {
 885             // xml
 886            if(ctype.indexOf("text/xml") > -1) {
 887                this.responseType = DS.TYPE_XML;
 888            }
 889            else if(ctype.indexOf("application/json") > -1) { // json
 890                this.responseType = DS.TYPE_JSON;
 891            }
 892            else if(ctype.indexOf("text/plain") > -1) { // text
 893                this.responseType = DS.TYPE_TEXT;
 894            }
 895        }
 896        else {
 897            if(YAHOO.lang.isArray(oRawResponse)) { // array
 898                this.responseType = DS.TYPE_JSARRAY;
 899            }
 900             // xml
 901            else if(oRawResponse && oRawResponse.nodeType && oRawResponse.nodeType == 9) {
 902                this.responseType = DS.TYPE_XML;
 903            }
 904            else if(oRawResponse && oRawResponse.nodeName && (oRawResponse.nodeName.toLowerCase() == "table")) { // table
 905                this.responseType = DS.TYPE_HTMLTABLE;
 906            }    
 907            else if(YAHOO.lang.isObject(oRawResponse)) { // json
 908                this.responseType = DS.TYPE_JSON;
 909            }
 910            else if(YAHOO.lang.isString(oRawResponse)) { // text
 911                this.responseType = DS.TYPE_TEXT;
 912            }
 913        }
 914    }
 915
 916    switch(this.responseType) {
 917        case DS.TYPE_JSARRAY:
 918            if(xhr && oRawResponse && oRawResponse.responseText) {
 919                oFullResponse = oRawResponse.responseText; 
 920            }
 921            try {
 922                // Convert to JS array if it's a string
 923                if(lang.isString(oFullResponse)) {
 924                    var parseArgs = [oFullResponse].concat(this.parseJSONArgs);
 925                    // Check for YUI JSON Util
 926                    if(lang.JSON) {
 927                        oFullResponse = lang.JSON.parse.apply(lang.JSON,parseArgs);
 928                    }
 929                    // Look for JSON parsers using an API similar to json2.js
 930                    else if(window.JSON && JSON.parse) {
 931                        oFullResponse = JSON.parse.apply(JSON,parseArgs);
 932                    }
 933                    // Look for JSON parsers using an API similar to json.js
 934                    else if(oFullResponse.parseJSON) {
 935                        oFullResponse = oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1));
 936                    }
 937                    // No JSON lib found so parse the string
 938                    else {
 939                        // Trim leading spaces
 940                        while (oFullResponse.length > 0 &&
 941                                (oFullResponse.charAt(0) != "{") &&
 942                                (oFullResponse.charAt(0) != "[")) {
 943                            oFullResponse = oFullResponse.substring(1, oFullResponse.length);
 944                        }
 945
 946                        if(oFullResponse.length > 0) {
 947                            // Strip extraneous stuff at the end
 948                            var arrayEnd =
 949Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}"));
 950                            oFullResponse = oFullResponse.substring(0,arrayEnd+1);
 951
 952                            // Turn the string into an object literal...
 953                            // ...eval is necessary here
 954                            oFullResponse = eval("(" + oFullResponse + ")");
 955
 956                        }
 957                    }
 958                }
 959            }
 960            catch(e1) {
 961            }
 962            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
 963            oParsedResponse = this.parseArrayData(oRequest, oFullResponse);
 964            break;
 965        case DS.TYPE_JSON:
 966            if(xhr && oRawResponse && oRawResponse.responseText) {
 967                oFullResponse = oRawResponse.responseText;
 968            }
 969            try {
 970                // Convert to JSON object if it's a string
 971                if(lang.isString(oFullResponse)) {
 972                    var parseArgs = [oFullResponse].concat(this.parseJSONArgs);
 973                    // Check for YUI JSON Util
 974                    if(lang.JSON) {
 975                        oFullResponse = lang.JSON.parse.apply(lang.JSON,parseArgs);
 976                    }
 977                    // Look for JSON parsers using an API similar to json2.js
 978                    else if(window.JSON && JSON.parse) {
 979                        oFullResponse = JSON.parse.apply(JSON,parseArgs);
 980                    }
 981                    // Look for JSON parsers using an API similar to json.js
 982                    else if(oFullResponse.parseJSON) {
 983                        oFullResponse = oFullResponse.parseJSON.apply(oFullResponse,parseArgs.slice(1));
 984                    }
 985                    // No JSON lib found so parse the string
 986                    else {
 987                        // Trim leading spaces
 988                        while (oFullResponse.length > 0 &&
 989                                (oFullResponse.charAt(0) != "{") &&
 990                                (oFullResponse.charAt(0) != "[")) {
 991                            oFullResponse = oFullResponse.substring(1, oFullResponse.length);
 992                        }
 993    
 994                        if(oFullResponse.length > 0) {
 995                            // Strip extraneous stuff at the end
 996                            var objEnd = Math.max(oFullResponse.lastIndexOf("]"),oFullResponse.lastIndexOf("}"));
 997                            oFullResponse = oFullResponse.substring(0,objEnd+1);
 998    
 999                            // Turn the string into an object literal...
1000                            // ...eval is necessary here
1001                            oFullResponse = eval("(" + oFullResponse + ")");
1002    
1003                        }
1004                    }
1005                }
1006            }
1007            catch(e) {
1008            }
1009
1010            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
1011            oParsedResponse = this.parseJSONData(oRequest, oFullResponse);
1012            break;
1013        case DS.TYPE_HTMLTABLE:
1014            if(xhr && oRawResponse.responseText) {
1015                var el = document.createElement('div');
1016                el.innerHTML = oRawResponse.responseText;
1017                oFullResponse = el.getElementsByTagName('table')[0];
1018            }
1019            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
1020            oParsedResponse = this.parseHTMLTableData(oRequest, oFullResponse);
1021            break;
1022        case DS.TYPE_XML:
1023            if(xhr && oRawResponse.responseXML) {
1024                oFullResponse = oRawResponse.responseXML;
1025            }
1026            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
1027            oParsedResponse = this.parseXMLData(oRequest, oFullResponse);
1028            break;
1029        case DS.TYPE_TEXT:
1030            if(xhr && lang.isString(oRawResponse.responseText)) {
1031                oFullResponse = oRawResponse.responseText;
1032            }
1033            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
1034            oParsedResponse = this.parseTextData(oRequest, oFullResponse);
1035            break;
1036        default:
1037            oFullResponse = this.doBeforeParseData(oRequest, oFullResponse, oCallback);
1038            oParsedResponse = this.parseData(oRequest, oFullResponse);
1039            break;
1040    }
1041
1042
1043    // Clean up for consistent signature
1044    oParsedResponse = oParsedResponse || {};
1045    if(!oParsedResponse.results) {
1046        oParsedResponse.results = [];
1047    }
1048    if(!oParsedResponse.meta) {
1049        oParsedResponse.meta = {};
1050    }
1051
1052    // Success
1053    if(oParsedResponse && !oParsedResponse.error) {
1054        // Last chance to touch the raw response or the parsed response
1055        oParsedResponse = this.doBeforeCallback(oRequest, oFullResponse, oParsedResponse, oCallback);
1056        this.fireEvent("responseParseEvent", {request:oRequest,
1057                response:oParsedResponse, callback:oCallback, caller:oCaller});
1058        // Cache the response
1059        this.addToCache(oRequest, oParsedResponse);
1060    }
1061    // Error
1062    else {
1063        // Be sure the error flag is on
1064        oParsedResponse.error = true;
1065        this.fireEvent("dataErrorEvent", {request:oRequest, response: oRawResponse, callback:oCallback, 
1066                caller:oCaller, message:DS.ERROR_DATANULL});
1067    }
1068
1069    // Send the response back to the caller
1070    oParsedResponse.tId = tId;
1071    DS.issueCallback(oCallback,[oRequest,oParsedResponse],oParsedResponse.error,oCaller);
1072},
1073
1074/**
1075 * Overridable method gives implementers access to the original full response
1076 * before the data gets parsed. Implementers should take care not to return an
1077 * unparsable or otherwise invalid response.
1078 *
1079 * @method doBeforeParseData
1080 * @param oRequest {Object} Request object.
1081 * @param oFullResponse {Object} The full response from the live database.
1082 * @param oCallback {Object} The callback object.  
1083 * @return {Object} Full response for parsing.
1084  
1085 */
1086doBeforeParseData : function(oRequest, oFullResponse, oCallback) {
1087    return oFullResponse;
1088},
1089
1090/**
1091 * Overridable method gives implementers access to the original full response and
1092 * the parsed response (parsed against the given schema) before the data
1093 * is added to the cache (if applicable) and then sent back to callback function.
1094 * This is your chance to access the raw response and/or populate the parsed
1095 * response with any custom data.
1096 *
1097 * @method doBeforeCallback
1098 * @param oRequest {Object} Request object.
1099 * @param oFullResponse {Object} The full response from the live database.
1100 * @param oParsedResponse {Object} The parsed response to return to calling object.
1101 * @param oCallback {Object} The callback object. 
1102 * @return {Object} Parsed response object.
1103 */
1104doBeforeCallback : function(oRequest, oFullResponse, oParsedResponse, oCallback) {
1105    return oParsedResponse;
1106},
1107
1108/**
1109 * Overridable method parses data of generic RESPONSE_TYPE into a response object.
1110 *
1111 * @method parseData
1112 * @param oRequest {Object} Request object.
1113 * @param oFullResponse {Object} The full Array from the live database.
1114 * @return {Object} Parsed response object with the following properties:<br>
1115 *     - results {Array} Array of parsed data results<br>
1116 *     - meta {Object} Object literal of meta values<br>
1117 *     - error {Boolean} (optional) True if there was an error<br>
1118 */
1119parseData : function(oRequest, oFullResponse) {
1120    if(lang.isValue(oFullResponse)) {
1121        var oParsedResponse = {results:oFullResponse,meta:{}};
1122        return oParsedResponse;
1123
1124    }
1125    return null;
1126},
1127
1128/**
1129 * Overridable method parses Array data into a response object.
1130 *
1131 * @method parseArrayData
1132 * @param oRequest {Object} Request object.
1133 * @param oFullResponse {Object} The full Array from the live database.
1134 * @return {Object} Parsed response object with the following properties:<br>
1135 *     - results (Array) Array of parsed data results<br>
1136 *     - error (Boolean) True if there was an error
1137 */
1138parseArrayData : function(oRequest, oFullResponse) {
1139    if(lang.isArray(oFullResponse)) {
1140        var results = [],
1141            i, j,
1142            rec, field, data;
1143        
1144        // Parse for fields
1145        if(lang.isArray(this.responseSchema.fields)) {
1146            var fields = this.responseSchema.fields;
1147            for (i = fields.length - 1; i >= 0; --i) {
1148                if (typeof fields[i] !== 'object') {
1149                    fields[i] = { key : fields[i] };
1150                }
1151            }
1152
1153            var parsers = {}, p;
1154            for (i = fields.length - 1; i >= 0; --i) {
1155                p = (typeof fields[i].parser === 'function' ?
1156                          fields[i].parser :
1157                          DS.Parser[fields[i].parser+'']) || fields[i].converter;
1158                if (p) {
1159                    parsers[fields[i].key] = p;
1160                }
1161            }
1162
1163            var arrType = lang.isArray(oFullResponse[0]);
1164            for(i=oFullResponse.length-1; i>-1; i--) {
1165                var oResult = {};
1166                rec = oFullResponse[i];
1167                if (typeof rec === 'object') {
1168                    for(j=fields.length-1; j>-1; j--) {
1169                        field = fields[j];
1170                        data = arrType ? rec[j] : rec[field.key];
1171
1172                        if (parsers[field.key]) {
1173                            data = parsers[field.key].call(this,data);
1174                        }
1175
1176                        // Safety measure
1177                        if(data === undefined) {
1178                            data = null;
1179                        }
1180
1181                        oResult[field.key] = data;
1182                    }
1183                }
1184                else if (lang.isString(rec)) {
1185                    for(j=fields.length-1; j>-1; j--) {
1186                        field = fields[j];
1187                        data = rec;
1188
1189                        if (parsers[field.key]) {
1190                            data = parsers[field.key].call(this,data);
1191                        }
1192
1193                        // Safety measure
1194                        if(data === undefined) {
1195                            data = null;
1196                        }
1197
1198                        oResult[field.key] = data;
1199                    }                
1200                }
1201                results[i] = oResult;
1202            }    
1203        }
1204        // Return entire data set
1205        else {
1206            results = oFullResponse;
1207        }
1208        var oParsedResponse = {results:results};
1209        return oParsedResponse;
1210
1211    }
1212    return null;
1213},
1214
1215/**
1216 * Overridable method parses plain text data into a response object.
1217 *
1218 * @method parseTextData
1219 * @param oRequest {Object} Request object.
1220 * @param oFullResponse {Object} The full text response from the live database.
1221 * @return {Object} Parsed response object with the following properties:<br>
1222 *     - results (Array) Array of parsed data results<br>
1223 *     - error (Boolean) True if there was an error
1224 */
1225parseTextData : function(oRequest, oFullResponse) {
1226    if(lang.isString(oFullResponse)) {
1227        if(lang.isString(this.responseSchema.recordDelim) &&
1228                lang.isString(this.responseSchema.fieldDelim)) {
1229            var oParsedResponse = {results:[]};
1230            var recDelim = this.responseSchema.recordDelim;
1231            var fieldDelim = this.responseSchema.fieldDelim;
1232            if(oFullResponse.length > 0) {
1233                // Delete the last line delimiter at the end of the data if it exists
1234                var newLength = oFullResponse.length-recDelim.length;
1235                if(oFullResponse.substr(newLength) == recDelim) {
1236                    oFullResponse = oFullResponse.substr(0, newLength);
1237                }
1238                if(oFullResponse.length > 0) {
1239                    // Split along record delimiter to get an array of strings
1240                    var recordsarray = oFullResponse.split(recDelim);
1241                    // Cycle through each record
1242                    for(var i = 0, len = recordsarray.length, recIdx = 0; i < len; ++i) {
1243                        var bError = false,
1244                            sRecord = recordsarray[i];
1245                        if (lang.isString(sRecord) && (sRecord.length > 0)) {
1246                            // Split each record along field delimiter to get data
1247                            var fielddataarray = recordsarray[i].split(fieldDelim);
1248                            var oResult = {};
1249                            
1250                            // Filter for fields data
1251                            if(lang.isArray(this.responseSchema.fields)) {
1252                                var fields = this.responseSchema.fields;
1253                                for(var j=fields.length-1; j>-1; j--) {
1254                                    try {
1255                                        // Remove quotation marks from edges, if applicable
1256                                        var data = fielddataarray[j];
1257                                        if (lang.isString(data)) {
1258                                            if(data.charAt(0) == "\"") {
1259                                                data = data.substr(1);
1260                                            }
1261                                            if(data.charAt(data.length-1) == "\"") {
1262                                                data = data.substr(0,data.length-1);
1263                                            }
1264                                            var field = fields[j];
1265                                            var key = (lang.isValue(field.key)) ? field.key : field;
1266                                            // Backward compatibility
1267                                            if(!field.parser && field.converter) {
1268                                                field.parser = field.converter;
1269                                            }
1270                                            var parser = (typeof field.parser === 'function') ?
1271                                                field.parser :
1272                                                DS.Parser[field.parser+''];
1273                                            if(parser) {
1274                                                data = parser.call(this, data);
1275                                            }
1276                                            // Safety measure
1277                                            if(data === undefined) {
1278                                                data = null;
1279                                            }
1280                                            oResult[key] = data;
1281                                        }
1282                                        else {
1283                                            bError = true;
1284                                        }
1285                                    }
1286                                    catch(e) {
1287                                        bError = true;
1288                                    }
1289                                }
1290                            }            
1291                            // No fields defined so pass along all data as an array
1292                            else {
1293                                oResult = fielddataarray;
1294                            }
1295                            if(!bError) {
1296                                oParsedResponse.results[recIdx++] = oResult;
1297                            }
1298                        }
1299                    }
1300                }
1301            }
1302            return oParsedResponse;
1303        }
1304    }
1305    return null;
1306            
1307},
1308
1309
1310/**
1311 * Overridable method parses XML data for one result into an object literal.
1312 *
1313 * @method parseXMLResult
1314 * @param result {XML} XML for one result.
1315 * @return {Object} Object literal of data for one result.
1316 */
1317parseXMLResult : function(result) {
1318    var oResult = {},
1319        schema = this.responseSchema;
1320        
1321    try {
1322        // Loop through each data field in each result using the schema
1323        for(var m = schema.fields.length-1; m >= 0 ; m--) {
1324            var field = schema.fields[m];
1325            var key = (lang.isValue(field.key)) ? field.key : field;
1326            var data = null;
1327            // Values may be held in an attribute...
1328            var xmlAttr = result.attributes.getNamedItem(key);
1329            if(xmlAttr) {
1330                data = xmlAttr.value;
1331            }
1332            // ...or in a node
1333            else {
1334                var xmlNode = result.getElementsByTagName(key);
1335                if(xmlNode && xmlNode.item(0)) {
1336                    var item = xmlNode.item(0);
1337                    // For IE, then DOM...
1338                    data = (item) ? ((item.text) ? item.text : (item.textContent) ? item.textContent : null) : null;
1339                    // ...then fallback, but check for multiple child nodes
1340                    if(!data) {
1341                        var datapieces = [];
1342                        for(var j=0, len=item.childNodes.length; j<len; j++) {
1343                            if(item.childNodes[j].nodeValue) {
1344                                datapieces[datapieces.length] = item.childNodes[j].nodeValue;
1345                            }
1346                        }
1347                        if(datapieces.length > 0) {
1348                            data = datapieces.join("");
1349                        }
1350                    }
1351                }
1352            }
1353            // Safety net
1354            if(data === null) {
1355                   data = "";
1356            }
1357            // Backward compatibility
1358            if(!field.parser && field.converter) {
1359                field.parser = field.converter;
1360            }
1361            var parser = (typeof field.parser === 'function') ?
1362                field.parser :
1363                DS.Parser[field.parser+''];
1364            if(parser) {
1365                data = parser.call(this, data);
1366            }
1367            // Safety measure
1368            if(data === undefined) {
1369                data = null;
1370            }
1371            oResult[key] = data;
1372        }
1373    }
1374    catch(e) {
1375    }
1376
1377    return oResult;
1378},
1379
1380
1381
1382/**
1383 * Overridable method parses XML data into a response object.
1384 *
1385 * @method parseXMLData
1386 * @param oRequest {Object} Request object.
1387 * @param oFullResponse {Object} The full XML response from the live database.
1388 * @return {Object} Parsed response object with the following properties<br>
1389 *     - results (Array) Array of parsed data results<br>
1390 *     - error (Boolean) True if there was an error
1391 */
1392parseXMLData : function(oRequest, oFullResponse) {
1393    var bError = false,
1394        schema = this.responseSchema,
1395        oParsedResponse = {meta:{}},
1396        xmlList = null,
1397        metaNode      = schema.metaNode,
1398        metaLocators  = schema.metaFields || {},
1399        i,k,loc,v;
1400
1401    // In case oFullResponse is something funky
1402    try {
1403        xmlList = (schema.resultNode) ?
1404            oFullResponse.getElementsByTagName(schema.resultNode) :
1405            null;
1406
1407        // Pull any meta identified
1408        metaNode = metaNode ? oFullResponse.getElementsByTagName(metaNode)[0] :
1409                   oFullResponse;
1410
1411        if (metaNode) {
1412            for (k in metaLocators) {
1413                if (lang.hasOwnProperty(metaLocators, k)) {
1414                    loc = metaLocators[k];
1415                    // Look for a node
1416                    v = metaNode.getElementsByTagName(loc)[0];
1417
1418                    if (v) {
1419                        v = v.firstChild.nodeValue;
1420                    } else {
1421                        // Look for an attribute
1422                        v = metaNode.attributes.getNamedItem(loc);
1423                        if (v) {
1424                            v = v.value;
1425                        }
1426                    }
1427
1428                    if (lang.isValue(v)) {
1429                        oParsedResponse.meta[k] = v;
1430                    }
1431                }
1432                
1433            }
1434        }
1435    }
1436    catch(e) {
1437    }
1438    if(!xmlList || !lang.isArray(schema.fields)) {
1439        bError = true;
1440    }
1441    // Loop through each result
1442    else {
1443        oParsedResponse.results = [];
1444        for(i = xmlList.length-1; i >= 0 ; --i) {
1445            var oResult = this.parseXMLResult(xmlList.item(i));
1446            // Capture each array of values into an array of results
1447            oParsedResponse.results[i] = oResult;
1448        }
1449    }
1450    if(bError) {
1451        oParsedResponse.error = true;
1452    }
1453    else {
1454    }
1455    return oParsedResponse;
1456},
1457
1458/**
1459 * Overridable method parses JSON data into a response object.
1460 *
1461 * @method parseJSONData
1462 * @param oRequest {Object} Request object.
1463 * @param oFullResponse {Object} The full JSON from the live database.
1464 * @return {Object} Parsed response object with the following properties<br>
1465 *     - results (Array) Array of parsed data results<br>
1466 *     - error (Boolean) True if there was an error
1467 */
1468parseJSONData : function(oRequest, oFullResponse) {
1469    var oParsedResponse = {results:[],meta:{}};
1470    
1471    if(lang.isObject(oFullResponse) && this.responseSchema.resultsList) {
1472        var schema = this.responseSchema,
1473            fields          = schema.fields,
1474            resultsList     = oFullResponse,
1475            results         = [],
1476            metaFields      = schema.metaFields || {},
1477            fieldParsers    = [],
1478            fieldPaths      = [],
1479            simpleFields    = [],
1480            bError          = false,
1481            i,len,j,v,key,parser,path;
1482
1483        // Function to convert the schema's fields into walk paths
1484        var buildPath = function (needle) {
1485            var path = null, keys = [], i = 0;
1486            if (needle) {
1487                // Strip the ["string keys"] and [1] array indexes
1488                needle = needle.
1489                    replace(/\[(['"])(.*?)\1\]/g,
1490                    function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
1491                    replace(/\[(\d+)\]/g,
1492                    function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
1493                    replace(/^\./,''); // remove leading dot
1494
1495                // If the cleaned needle contains invalid characters, the
1496                // path is invalid
1497                if (!/[^\w\.\$@]/.test(needle)) {
1498                    path = needle.split('.');
1499                    for (i=path.length-1; i >= 0; --i) {
1500                        if (path[i].charAt(0) === '@') {
1501           

Large files files are truncated, but you can click here to view the full file