2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * The DataSource utility provides a common configurable interface for widgets
9 * to access a variety of data, from JavaScript arrays to online servers over
12 * @namespace YAHOO.util
14 * @requires yahoo, event
15 * @optional connection
16 * @title DataSource Utility
20 /****************************************************************************/
21 /****************************************************************************/
22 /****************************************************************************/
25 * The DataSource class defines and manages a live set of data for widgets to
26 * interact with. Examples of live databases include in-memory
27 * local data such as a JavaScript array, a JavaScript function, or JSON, or
28 * remote data such as data retrieved through an XHR connection.
31 * @uses YAHOO.util.EventProvider
33 * @param oLiveData {Object} Pointer to live database.
34 * @param oConfigs {Object} (optional) Object literal of configuration values.
36 YAHOO
.util
.DataSource = function(oLiveData
, oConfigs
) {
37 // Set any config params passed in to override defaults
38 if(oConfigs
&& (oConfigs
.constructor == Object
)) {
39 for(var sConfig
in oConfigs
) {
41 this[sConfig
] = oConfigs
[sConfig
];
50 if(oLiveData
.nodeType
&& oLiveData
.nodeType
== 9) {
51 this.dataType
= YAHOO
.util
.DataSource
.TYPE_XML
;
53 else if(YAHOO
.lang
.isArray(oLiveData
)) {
54 this.dataType
= YAHOO
.util
.DataSource
.TYPE_JSARRAY
;
56 else if(YAHOO
.lang
.isString(oLiveData
)) {
57 this.dataType
= YAHOO
.util
.DataSource
.TYPE_XHR
;
59 else if(YAHOO
.lang
.isFunction(oLiveData
)) {
60 this.dataType
= YAHOO
.util
.DataSource
.TYPE_JSFUNCTION
;
62 else if(oLiveData
.nodeName
&& (oLiveData
.nodeName
.toLowerCase() == "table")) {
63 this.dataType
= YAHOO
.util
.DataSource
.TYPE_HTMLTABLE
;
65 else if(YAHOO
.lang
.isObject(oLiveData
)) {
66 this.dataType
= YAHOO
.util
.DataSource
.TYPE_JSON
;
69 this.dataType
= YAHOO
.util
.DataSource
.TYPE_UNKNOWN
;
72 this.liveData
= oLiveData
;
73 this._oQueue
= {interval
:null, conn
:null, requests
:[]};
76 // Validate and initialize public configs
77 var maxCacheEntries
= this.maxCacheEntries
;
78 if(!YAHOO
.lang
.isNumber(maxCacheEntries
) || (maxCacheEntries
< 0)) {
82 // Initialize local cache
83 if(maxCacheEntries
> 0 && !this._aCache
) {
87 this._sName
= "DataSource instance" + YAHOO
.util
.DataSource
._nIndex
;
88 YAHOO
.util
.DataSource
._nIndex
++;
91 /////////////////////////////////////////////////////////////////////////////
95 /////////////////////////////////////////////////////////////////////////////
98 * Fired when a request is made to the local cache.
100 * @event cacheRequestEvent
101 * @param oArgs.request {Object} The request object.
102 * @param oArgs.callback {Function} The callback function.
103 * @param oArgs.caller {Object} The parent object of the callback function.
105 this.createEvent("cacheRequestEvent");
108 * Fired when data is retrieved from the local cache.
110 * @event getCachedResponseEvent
111 * @param oArgs.request {Object} The request object.
112 * @param oArgs.response {Object} The response object.
113 * @param oArgs.callback {Function} The callback function.
114 * @param oArgs.caller {Object} The parent object of the callback function.
115 * @param oArgs.tId {Number} Transaction ID.
117 this.createEvent("cacheResponseEvent");
120 * Fired when a request is sent to the live data source.
122 * @event requestEvent
123 * @param oArgs.request {Object} The request object.
124 * @param oArgs.callback {Function} The callback function.
125 * @param oArgs.caller {Object} The parent object of the callback function.
127 this.createEvent("requestEvent");
130 * Fired when live data source sends response.
132 * @event responseEvent
133 * @param oArgs.request {Object} The request object.
134 * @param oArgs.response {Object} The raw response object.
135 * @param oArgs.callback {Function} The callback function.
136 * @param oArgs.caller {Object} The parent object of the callback function.
138 this.createEvent("responseEvent");
141 * Fired when response is parsed.
143 * @event responseParseEvent
144 * @param oArgs.request {Object} The request object.
145 * @param oArgs.response {Object} The parsed response object.
146 * @param oArgs.callback {Function} The callback function.
147 * @param oArgs.caller {Object} The parent object of the callback function.
149 this.createEvent("responseParseEvent");
152 * Fired when response is cached.
154 * @event responseCacheEvent
155 * @param oArgs.request {Object} The request object.
156 * @param oArgs.response {Object} The parsed response object.
157 * @param oArgs.callback {Function} The callback function.
158 * @param oArgs.caller {Object} The parent object of the callback function.
160 this.createEvent("responseCacheEvent");
162 * Fired when an error is encountered with the live data source.
164 * @event dataErrorEvent
165 * @param oArgs.request {Object} The request object.
166 * @param oArgs.callback {Function} The callback function.
167 * @param oArgs.caller {Object} The parent object of the callback function.
168 * @param oArgs.message {String} The error message.
170 this.createEvent("dataErrorEvent");
173 * Fired when the local cache is flushed.
175 * @event cacheFlushEvent
177 this.createEvent("cacheFlushEvent");
180 YAHOO
.augment(YAHOO
.util
.DataSource
, YAHOO
.util
.EventProvider
);
182 /////////////////////////////////////////////////////////////////////////////
186 /////////////////////////////////////////////////////////////////////////////
191 * @property TYPE_UNKNOWN
196 YAHOO
.util
.DataSource
.TYPE_UNKNOWN
= -1;
199 * Type is a JavaScript Array.
201 * @property TYPE_JSARRAY
206 YAHOO
.util
.DataSource
.TYPE_JSARRAY
= 0;
209 * Type is a JavaScript Function.
211 * @property TYPE_JSFUNCTION
216 YAHOO
.util
.DataSource
.TYPE_JSFUNCTION
= 1;
219 * Type is hosted on a server via an XHR connection.
226 YAHOO
.util
.DataSource
.TYPE_XHR
= 2;
231 * @property TYPE_JSON
236 YAHOO
.util
.DataSource
.TYPE_JSON
= 3;
246 YAHOO
.util
.DataSource
.TYPE_XML
= 4;
249 * Type is plain text.
251 * @property TYPE_TEXT
256 YAHOO
.util
.DataSource
.TYPE_TEXT
= 5;
259 * Type is an HTML TABLE element.
261 * @property TYPE_HTMLTABLE
266 YAHOO
.util
.DataSource
.TYPE_HTMLTABLE
= 6;
269 * Error message for invalid dataresponses.
271 * @property ERROR_DATAINVALID
274 * @default "Invalid data"
276 YAHOO
.util
.DataSource
.ERROR_DATAINVALID
= "Invalid data";
279 * Error message for null data responses.
281 * @property ERROR_DATANULL
284 * @default "Null data"
286 YAHOO
.util
.DataSource
.ERROR_DATANULL
= "Null data";
290 /////////////////////////////////////////////////////////////////////////////
294 /////////////////////////////////////////////////////////////////////////////
297 * Internal class variable to index multiple DataSource instances.
299 * @property DataSource._nIndex
304 YAHOO
.util
.DataSource
._nIndex
= 0;
307 * Internal class variable to assign unique transaction IDs.
309 * @property DataSource._nTransactionId
314 YAHOO
.util
.DataSource
._nTransactionId
= 0;
317 * Name of DataSource instance.
323 YAHOO
.util
.DataSource
.prototype._sName
= null;
326 * Local cache of data result object literals indexed chronologically.
332 YAHOO
.util
.DataSource
.prototype._aCache
= null;
335 * Local queue of request connections, enabled if queue needs to be managed.
341 YAHOO
.util
.DataSource
.prototype._oQueue
= null;
343 /////////////////////////////////////////////////////////////////////////////
347 /////////////////////////////////////////////////////////////////////////////
351 /////////////////////////////////////////////////////////////////////////////
353 // Public member variables
355 /////////////////////////////////////////////////////////////////////////////
358 * Max size of the local cache. Set to 0 to turn off caching. Caching is
359 * useful to reduce the number of server connections. Recommended only for data
360 * sources that return comprehensive results for queries or when stale data is
363 * @property maxCacheEntries
367 YAHOO
.util
.DataSource
.prototype.maxCacheEntries
= 0;
370 * Pointer to live database.
375 YAHOO
.util
.DataSource
.prototype.liveData
= null;
378 * Where the live data is held.
382 * @default YAHOO.util.DataSource.TYPE_UNKNOWN
385 YAHOO
.util
.DataSource
.prototype.dataType
= YAHOO
.util
.DataSource
.TYPE_UNKNOWN
;
388 * Format of response.
390 * @property responseType
392 * @default YAHOO.util.DataSource.TYPE_UNKNOWN
394 YAHOO
.util
.DataSource
.prototype.responseType
= YAHOO
.util
.DataSource
.TYPE_UNKNOWN
;
397 * Response schema object literal takes a combination of the following properties:
400 * <dt>resultsList</dt> <dd>Pointer to array of tabular data</dd>
401 * <dt>resultNode</dt> <dd>Pointer to node name of row data (XML data only)</dd>
402 * <dt>recordDelim</dt> <dd>Record delimiter (text data only)</dd>
403 * <dt>fieldDelim</dt> <dd>Field delimiter (text data only)</dd>
404 * <dt>fields</dt> <dd>Array of field names (aka keys), or array of object literals
405 * such as: {key:"fieldname",parser:YAHOO.util.DataSource.parseDate}</dd>
408 * @property responseSchema
411 YAHOO
.util
.DataSource
.prototype.responseSchema
= null;
414 * Alias to YUI Connection Manager. Allows implementers to specify their own
415 * subclasses of the YUI Connection Manager utility.
419 * @default YAHOO.util.Connect
421 YAHOO
.util
.DataSource
.prototype.connMgr
= null;
424 * If data is accessed over XHR via Connection Manager, this setting defines
425 * request/response management in the following manner:
427 * <dt>queueRequests</dt>
428 * <dd>If a request is already in progress, wait until response is returned
429 * before sending the next request.</dd>
431 * <dt>cancelStaleRequests</dt>
432 * <dd>If a request is already in progress, cancel it before sending the next
435 * <dt>ignoreStaleResponses</dt>
436 * <dd>Send all requests, but handle only the response for the most recently
440 * <dd>Send all requests and handle all responses.</dd>
444 * @property connXhrMode
446 * @default "allowAll"
448 YAHOO
.util
.DataSource
.prototype.connXhrMode
= "allowAll";
451 * If data is accessed over XHR via Connection Manager, true if data should be
452 * sent via POST, otherwise data will be sent via GET.
454 * @property connMethodPost
458 YAHOO
.util
.DataSource
.prototype.connMethodPost
= false;
461 * If data is accessed over XHR via Connection Manager, the connection timeout
462 * defines how many milliseconds the XHR connection will wait for a server
463 * response. Any non-zero value will enable the Connection utility's
464 * Auto-Abort feature.
466 * @property connTimeout
470 YAHOO
.util
.DataSource
.prototype.connTimeout
= 0;
472 /////////////////////////////////////////////////////////////////////////////
474 // Public static methods
476 /////////////////////////////////////////////////////////////////////////////
479 * Converts data to type String.
481 * @method DataSource.parseString
482 * @param oData {String | Number | Boolean | Date | Array | Object} Data to parse.
483 * The special values null and undefined will return null.
484 * @return {Number} A string, or null.
487 YAHOO
.util
.DataSource
.parseString = function(oData
) {
488 // Special case null and undefined
489 if(!YAHOO
.lang
.isValue(oData
)) {
494 var string
= oData
+ "";
497 if(YAHOO
.lang
.isString(string
)) {
506 * Converts data to type Number.
508 * @method DataSource.parseNumber
509 * @param oData {String | Number | Boolean | Null} Data to convert. Beware, null
511 * @return {Number} A number, or null if NaN.
514 YAHOO
.util
.DataSource
.parseNumber = function(oData
) {
516 var number
= oData
* 1;
519 if(YAHOO
.lang
.isNumber(number
)) {
526 // Backward compatibility
527 YAHOO
.util
.DataSource
.convertNumber = function(oData
) {
528 return YAHOO
.util
.DataSource
.parseNumber(oData
);
532 * Converts data to type Date.
534 * @method DataSource.parseDate
535 * @param oData {Date | String | Number} Data to convert.
536 * @return {Date} A Date instance.
539 YAHOO
.util
.DataSource
.parseDate = function(oData
) {
543 if(!(oData
instanceof Date
)) {
544 date
= new Date(oData
);
551 if(date
instanceof Date
) {
558 // Backward compatibility
559 YAHOO
.util
.DataSource
.convertDate = function(oData
) {
560 return YAHOO
.util
.DataSource
.parseDate(oData
);
563 /////////////////////////////////////////////////////////////////////////////
567 /////////////////////////////////////////////////////////////////////////////
570 * Public accessor to the unique name of the DataSource instance.
573 * @return {String} Unique name of the DataSource instance.
575 YAHOO
.util
.DataSource
.prototype.toString = function() {
580 * Overridable method passes request to cache and returns cached response if any,
581 * refreshing the hit in the cache as the newest item. Returns null if there is
584 * @method getCachedResponse
585 * @param oRequest {Object} Request object.
586 * @param oCallback {Function} Handler function to receive the response.
587 * @param oCaller {Object} The Calling object that is making the request.
588 * @return {Object} Cached response object or null.
590 YAHOO
.util
.DataSource
.prototype.getCachedResponse = function(oRequest
, oCallback
, oCaller
) {
591 var aCache
= this._aCache
;
592 var nCacheLength
= (aCache
) ? aCache
.length
: 0;
593 var oResponse
= null;
595 // If cache is enabled...
596 if((this.maxCacheEntries
> 0) && aCache
&& (nCacheLength
> 0)) {
597 this.fireEvent("cacheRequestEvent", {request
:oRequest
,callback
:oCallback
,caller
:oCaller
});
599 // Loop through each cached element
600 for(var i
= nCacheLength
-1; i
>= 0; i
--) {
601 var oCacheElem
= aCache
[i
];
603 // Defer cache hit logic to a public overridable method
604 if(this.isCacheHit(oRequest
,oCacheElem
.request
)) {
605 // Grab the cached response
606 oResponse
= oCacheElem
.response
;
607 // The cache returned a hit!
608 // Remove element from its original location
611 this.addToCache(oRequest
, oResponse
);
612 this.fireEvent("cacheResponseEvent", {request
:oRequest
,response
:oResponse
,callback
:oCallback
,caller
:oCaller
});
621 * Default overridable method matches given request to given cached request.
622 * Returns true if is a hit, returns false otherwise. Implementers should
623 * override this method to customize the cache-matching algorithm.
626 * @param oRequest {Object} Request object.
627 * @param oCachedRequest {Object} Cached request object.
628 * @return {Boolean} True if given request matches cached request, false otherwise.
630 YAHOO
.util
.DataSource
.prototype.isCacheHit = function(oRequest
, oCachedRequest
) {
631 return (oRequest
=== oCachedRequest
);
635 * Adds a new item to the cache. If cache is full, evicts the stalest item
636 * before adding the new item.
639 * @param oRequest {Object} Request object.
640 * @param oResponse {Object} Response object to cache.
642 YAHOO
.util
.DataSource
.prototype.addToCache = function(oRequest
, oResponse
) {
643 var aCache
= this._aCache
;
648 //TODO: check for duplicate entries
650 // If the cache is full, make room by removing stalest element (index=0)
651 while(aCache
.length
>= this.maxCacheEntries
) {
655 // Add to cache in the newest position, at the end of the array
656 var oCacheElem
= {request
:oRequest
,response
:oResponse
};
657 aCache
.push(oCacheElem
);
658 this.fireEvent("responseCacheEvent", {request
:oRequest
,response
:oResponse
});
666 YAHOO
.util
.DataSource
.prototype.flushCache = function() {
669 this.fireEvent("cacheFlushEvent");
674 * First looks for cached response, then sends request to live data.
676 * @method sendRequest
677 * @param oRequest {Object} Request object.
678 * @param oCallback {Function} Handler function to receive the response.
679 * @param oCaller {Object} The Calling object that is making the request.
680 * @return {Number} Transaction ID, or null if response found in cache.
682 YAHOO
.util
.DataSource
.prototype.sendRequest = function(oRequest
, oCallback
, oCaller
) {
683 // First look in cache
684 var oCachedResponse
= this.getCachedResponse(oRequest
, oCallback
, oCaller
);
685 if(oCachedResponse
) {
686 oCallback
.call(oCaller
, oRequest
, oCachedResponse
);
690 // Not in cache, so forward request to live data
691 return this.makeConnection(oRequest
, oCallback
, oCaller
);
695 * Overridable method provides default functionality to make a connection to
696 * live data in order to send request. The response coming back is then
697 * forwarded to the handleResponse function. This method should be customized
698 * to achieve more complex implementations.
700 * @method makeConnection
701 * @param oRequest {Object} Request object.
702 * @param oCallback {Function} Handler function to receive the response.
703 * @param oCaller {Object} The Calling object that is making the request.
704 * @return {Number} Transaction ID.
706 YAHOO
.util
.DataSource
.prototype.makeConnection = function(oRequest
, oCallback
, oCaller
) {
707 this.fireEvent("requestEvent", {request
:oRequest
,callback
:oCallback
,caller
:oCaller
});
708 var oRawResponse
= null;
709 var tId
= YAHOO
.util
.DataSource
._nTransactionId
++;
711 // How to make the connection depends on the type of data
712 switch(this.dataType
) {
713 // If the live data is a JavaScript Function
714 // pass the request in as a parameter and
715 // forward the return value to the handler
716 case YAHOO
.util
.DataSource
.TYPE_JSFUNCTION
:
717 oRawResponse
= this.liveData(oRequest
);
718 this.handleResponse(oRequest
, oRawResponse
, oCallback
, oCaller
, tId
);
720 // If the live data is over Connection Manager
721 // set up the callback object and
722 // pass the request in as a URL query and
723 // forward the response to the handler
724 case YAHOO
.util
.DataSource
.TYPE_XHR
:
726 var oConnMgr
= this.connMgr
|| YAHOO
.util
.Connect
;
727 var oQueue
= this._oQueue
;
730 * Define Connection Manager success handler
732 * @method _xhrSuccess
733 * @param oResponse {Object} HTTPXMLRequest object
736 var _xhrSuccess = function(oResponse
) {
737 // If response ID does not match last made request ID,
738 // silently fail and wait for the next response
739 if(oResponse
&& (this.connXhrMode
== "ignoreStaleResponses") &&
740 (oResponse
.tId
!= oQueue
.conn
.tId
)) {
743 // Error if no response
744 else if(!oResponse
) {
745 this.fireEvent("dataErrorEvent", {request
:oRequest
,
746 callback
:oCallback
, caller
:oCaller
,
747 message
:YAHOO
.util
.DataSource
.ERROR_DATANULL
});
749 // Send error response back to the caller with the error flag on
750 oCallback
.call(oCaller
, oRequest
, oResponse
, true);
754 // Forward to handler
756 this.handleResponse(oRequest
, oResponse
, oCallback
, oCaller
, tId
);
761 * Define Connection Manager failure handler
763 * @method _xhrFailure
764 * @param oResponse {Object} HTTPXMLRequest object
767 var _xhrFailure = function(oResponse
) {
768 this.fireEvent("dataErrorEvent", {request
:oRequest
,
769 callback
:oCallback
, caller
:oCaller
,
770 message
:YAHOO
.util
.DataSource
.ERROR_DATAINVALID
});
772 // Backward compatibility
773 if((this.liveData
.lastIndexOf("?") !== this.liveData
.length
-1) &&
774 (oRequest
.indexOf("?") !== 0)){
777 // Send failure response back to the caller with the error flag on
778 oCallback
.call(oCaller
, oRequest
, oResponse
, true);
783 * Define Connection Manager callback object
785 * @property _xhrCallback
786 * @param oResponse {Object} HTTPXMLRequest object
795 // Apply Connection Manager timeout
796 if(YAHOO
.lang
.isNumber(this.connTimeout
)) {
797 _xhrCallback
.timeout
= this.connTimeout
;
800 // Cancel stale requests
801 if(this.connXhrMode
== "cancelStaleRequests") {
802 // Look in queue for stale requests
805 oConnMgr
.abort(oQueue
.conn
);
813 // Get ready to send the request URL
814 if(oConnMgr
&& oConnMgr
.asyncRequest
) {
815 var sLiveData
= this.liveData
;
816 var isPost
= this.connMethodPost
;
817 var sMethod
= (isPost
) ? "POST" : "GET";
818 var sUri
= (isPost
) ? sLiveData
: sLiveData
+oRequest
;
819 var sRequest
= (isPost
) ? oRequest
: null;
821 // Send the request right away
822 if(this.connXhrMode
!= "queueRequests") {
823 oQueue
.conn
= oConnMgr
.asyncRequest(sMethod
, sUri
, _xhrCallback
, sRequest
);
825 // Queue up then send the request
827 // Found a request already in progress
829 // Add request to queue
830 oQueue
.requests
.push({request
:oRequest
, callback
:_xhrCallback
});
832 // Interval needs to be started
833 if(!oQueue
.interval
) {
834 oQueue
.interval
= setInterval(function() {
835 // Connection is in progress
836 if(oConnMgr
.isCallInProgress(oQueue
.conn
)) {
841 if(oQueue
.requests
.length
> 0) {
842 sUri
= (isPost
) ? sLiveData
: sLiveData
+oQueue
.requests
[0].request
;
843 sRequest
= (isPost
) ? oQueue
.requests
[0].request
: null;
844 oQueue
.conn
= oConnMgr
.asyncRequest(sMethod
, sUri
, oQueue
.requests
[0].callback
, sRequest
);
846 // Remove request from queue
847 oQueue
.requests
.shift();
851 clearInterval(oQueue
.interval
);
852 oQueue
.interval
= null;
858 // Nothing is in progress
860 oQueue
.conn
= oConnMgr
.asyncRequest(sMethod
, sUri
, _xhrCallback
, sRequest
);
865 // Send null response back to the caller with the error flag on
866 oCallback
.call(oCaller
, oRequest
, null, true);
870 // Simply forward the entire data object to the handler
872 /* accounts for the following cases:
873 YAHOO.util.DataSource.TYPE_UNKNOWN:
874 YAHOO.util.DataSource.TYPE_JSARRAY:
875 YAHOO.util.DataSource.TYPE_JSON:
876 YAHOO.util.DataSource.TYPE_HTMLTABLE:
877 YAHOO.util.DataSource.TYPE_XML:
879 oRawResponse
= this.liveData
;
880 this.handleResponse(oRequest
, oRawResponse
, oCallback
, oCaller
, tId
);
887 * Handles raw data response from live data source. Sends a parsed response object
888 * to the callback function in this format:
890 * fnCallback(oRequest, oParsedResponse)
892 * where the oParsedResponse object literal with the following properties:
894 * <li>tId {Number} Unique transaction ID</li>
895 * <li>results {Array} Array of parsed data results</li>
896 * <li>error {Boolean} True if there was an error</li>
899 * @method handleResponse
900 * @param oRequest {Object} Request object
901 * @param oRawResponse {Object} The raw response from the live database.
902 * @param oCallback {Function} Handler function to receive the response.
903 * @param oCaller {Object} The calling object that is making the request.
904 * @param tId {Number} Transaction ID.
906 YAHOO
.util
.DataSource
.prototype.handleResponse = function(oRequest
, oRawResponse
, oCallback
, oCaller
, tId
) {
907 this.fireEvent("responseEvent", {request
:oRequest
, response
:oRawResponse
,
908 callback
:oCallback
, caller
:oCaller
, tId
: tId
});
909 var xhr
= (this.dataType
== YAHOO
.util
.DataSource
.TYPE_XHR
) ? true : false;
910 var oParsedResponse
= null;
913 // Access to the raw response before it gets parsed
914 oRawResponse
= this.doBeforeParseData(oRequest
, oRawResponse
);
916 switch(this.responseType
) {
917 case YAHOO
.util
.DataSource
.TYPE_JSARRAY
:
918 if(xhr
&& oRawResponse
.responseText
) {
919 oRawResponse
= oRawResponse
.responseText
;
921 oParsedResponse
= this.parseArrayData(oRequest
, oRawResponse
);
923 case YAHOO
.util
.DataSource
.TYPE_JSON
:
924 if(xhr
&& oRawResponse
.responseText
) {
925 oRawResponse
= oRawResponse
.responseText
;
927 oParsedResponse
= this.parseJSONData(oRequest
, oRawResponse
);
929 case YAHOO
.util
.DataSource
.TYPE_HTMLTABLE
:
930 if(xhr
&& oRawResponse
.responseText
) {
931 oRawResponse
= oRawResponse
.responseText
;
933 oParsedResponse
= this.parseHTMLTableData(oRequest
, oRawResponse
);
935 case YAHOO
.util
.DataSource
.TYPE_XML
:
936 if(xhr
&& oRawResponse
.responseXML
) {
937 oRawResponse
= oRawResponse
.responseXML
;
939 oParsedResponse
= this.parseXMLData(oRequest
, oRawResponse
);
941 case YAHOO
.util
.DataSource
.TYPE_TEXT
:
942 if(xhr
&& oRawResponse
.responseText
) {
943 oRawResponse
= oRawResponse
.responseText
;
945 oParsedResponse
= this.parseTextData(oRequest
, oRawResponse
);
948 //var contentType = oRawResponse.getResponseHeader["Content-Type"];
953 if(oParsedResponse
) {
954 // Last chance to touch the raw response or the parsed response
955 oParsedResponse
.tId
= tId
;
956 oParsedResponse
= this.doBeforeCallback(oRequest
, oRawResponse
, oParsedResponse
);
957 this.fireEvent("responseParseEvent", {request
:oRequest
,
958 response
:oParsedResponse
, callback
:oCallback
, caller
:oCaller
});
959 // Cache the response
960 this.addToCache(oRequest
, oParsedResponse
);
963 this.fireEvent("dataErrorEvent", {request
:oRequest
, callback
:oCallback
,
964 caller
:oCaller
, message
:YAHOO
.util
.DataSource
.ERROR_DATANULL
});
966 // Send response back to the caller with the error flag on
967 oParsedResponse
= {error
:true};
970 // Send the response back to the caller
971 oCallback
.call(oCaller
, oRequest
, oParsedResponse
);
975 * Overridable method gives implementers access to the original raw response
976 * before the data gets parsed. Implementers should take care not to return an
977 * unparsable or otherwise invalid raw response.
979 * @method doBeforeParseData
980 * @param oRequest {Object} Request object.
981 * @param oRawResponse {Object} The raw response from the live database.
982 * @return {Object} Raw response for parsing.
984 YAHOO
.util
.DataSource
.prototype.doBeforeParseData = function(oRequest
, oRawResponse
) {
989 * Overridable method gives implementers access to the original raw response and
990 * the parsed response (parsed against the given schema) before the data
991 * is added to the cache (if applicable) and then sent back to callback function.
992 * This is your chance to access the raw response and/or populate the parsed
993 * response with any custom data.
995 * @method doBeforeCallback
996 * @param oRequest {Object} Request object.
997 * @param oRawResponse {Object} The raw response from the live database.
998 * @param oParsedResponse {Object} The parsed response to return to calling object.
999 * @return {Object} Parsed response object.
1001 YAHOO
.util
.DataSource
.prototype.doBeforeCallback = function(oRequest
, oRawResponse
, oParsedResponse
) {
1002 return oParsedResponse
;
1006 * Overridable method parses raw array data into a response object.
1008 * @method parseArrayData
1009 * @param oRequest {Object} Request object.
1010 * @param oRawResponse {Object} The raw response from the live database.
1011 * @return {Object} Parsed response object.
1013 YAHOO
.util
.DataSource
.prototype.parseArrayData = function(oRequest
, oRawResponse
) {
1014 if(YAHOO
.lang
.isArray(oRawResponse
) && YAHOO
.lang
.isArray(this.responseSchema
.fields
)) {
1015 var oParsedResponse
= {results
:[]};
1016 var fields
= this.responseSchema
.fields
;
1017 for(var i
=oRawResponse
.length
-1; i
>-1; i
--) {
1019 for(var j
=fields
.length
-1; j
>-1; j
--) {
1020 var field
= fields
[j
];
1021 var key
= (YAHOO
.lang
.isValue(field
.key
)) ? field
.key
: field
;
1022 var data
= (YAHOO
.lang
.isValue(oRawResponse
[i
][j
])) ? oRawResponse
[i
][j
] : oRawResponse
[i
][key
];
1023 // Backward compatibility
1024 if(!field
.parser
&& field
.converter
) {
1025 field
.parser
= field
.converter
;
1028 data
= field
.parser
.call(this, data
);
1031 if(data
=== undefined) {
1034 oResult
[key
] = data
;
1036 oParsedResponse
.results
.unshift(oResult
);
1038 return oParsedResponse
;
1046 * Overridable method parses raw plain text data into a response object.
1048 * @method parseTextData
1049 * @param oRequest {Object} Request object.
1050 * @param oRawResponse {Object} The raw response from the live database.
1051 * @return {Object} Parsed response object.
1053 YAHOO
.util
.DataSource
.prototype.parseTextData = function(oRequest
, oRawResponse
) {
1054 var oParsedResponse
= {};
1055 if(YAHOO
.lang
.isString(oRawResponse
) &&
1056 YAHOO
.lang
.isArray(this.responseSchema
.fields
) &&
1057 YAHOO
.lang
.isString(this.responseSchema
.recordDelim
) &&
1058 YAHOO
.lang
.isString(this.responseSchema
.fieldDelim
)) {
1059 oParsedResponse
.results
= [];
1060 var recDelim
= this.responseSchema
.recordDelim
;
1061 var fieldDelim
= this.responseSchema
.fieldDelim
;
1062 var fields
= this.responseSchema
.fields
;
1063 if(oRawResponse
.length
> 0) {
1064 // Delete the last line delimiter at the end of the data if it exists
1065 var newLength
= oRawResponse
.length
-recDelim
.length
;
1066 if(oRawResponse
.substr(newLength
) == recDelim
) {
1067 oRawResponse
= oRawResponse
.substr(0, newLength
);
1069 // Split along record delimiter to get an array of strings
1070 var recordsarray
= oRawResponse
.split(recDelim
);
1071 // Cycle through each record, except the first which contains header info
1072 for(var i
= recordsarray
.length
-1; i
>-1; i
--) {
1074 for(var j
=fields
.length
-1; j
>-1; j
--) {
1075 // Split along field delimter to get each data value
1076 var fielddataarray
= recordsarray
[i
].split(fieldDelim
);
1078 // Remove quotation marks from edges, if applicable
1079 var data
= fielddataarray
[j
];
1080 if(data
.charAt(0) == "\"") {
1081 data
= data
.substr(1);
1083 if(data
.charAt(data
.length
-1) == "\"") {
1084 data
= data
.substr(0,data
.length
-1);
1086 var field
= fields
[j
];
1087 var key
= (YAHOO
.lang
.isValue(field
.key
)) ? field
.key
: field
;
1088 // Backward compatibility
1089 if(!field
.parser
&& field
.converter
) {
1090 field
.parser
= field
.converter
;
1093 data
= field
.parser
.call(this, data
);
1096 if(data
=== undefined) {
1099 oResult
[key
] = data
;
1101 oParsedResponse
.results
.unshift(oResult
);
1106 oParsedResponse
.error
= true;
1108 return oParsedResponse
;
1112 * Overridable method parses raw XML data into a response object.
1114 * @method parseXMLData
1115 * @param oRequest {Object} Request object.
1116 * @param oRawResponse {Object} The raw response from the live database.
1117 * @return {Object} Parsed response object.
1119 YAHOO
.util
.DataSource
.prototype.parseXMLData = function(oRequest
, oRawResponse
) {
1121 var oParsedResponse
= {};
1122 var xmlList
= (this.responseSchema
.resultNode
) ?
1123 oRawResponse
.getElementsByTagName(this.responseSchema
.resultNode
) :
1125 if(!xmlList
|| !YAHOO
.lang
.isArray(this.responseSchema
.fields
)) {
1128 // Loop through each result
1130 oParsedResponse
.results
= [];
1131 for(var k
= xmlList
.length
-1; k
>= 0 ; k
--) {
1132 var result
= xmlList
.item(k
);
1134 // Loop through each data field in each result using the schema
1135 for(var m
= this.responseSchema
.fields
.length
-1; m
>= 0 ; m
--) {
1136 var field
= this.responseSchema
.fields
[m
];
1137 var key
= (YAHOO
.lang
.isValue(field
.key
)) ? field
.key
: field
;
1139 // Values may be held in an attribute...
1140 var xmlAttr
= result
.attributes
.getNamedItem(key
);
1142 data
= xmlAttr
.value
;
1146 var xmlNode
= result
.getElementsByTagName(key
);
1147 if(xmlNode
&& xmlNode
.item(0) && xmlNode
.item(0).firstChild
) {
1148 data
= xmlNode
.item(0).firstChild
.nodeValue
;
1154 // Backward compatibility
1155 if(!field
.parser
&& field
.converter
) {
1156 field
.parser
= field
.converter
;
1159 data
= field
.parser
.call(this, data
);
1162 if(data
=== undefined) {
1165 oResult
[key
] = data
;
1167 // Capture each array of values into an array of results
1168 oParsedResponse
.results
.unshift(oResult
);
1172 oParsedResponse
.error
= true;
1176 return oParsedResponse
;
1180 * Overridable method parses raw JSON data into a response object.
1182 * @method parseJSONData
1183 * @param oRequest {Object} Request object.
1184 * @param oRawResponse {Object} The raw response from the live database.
1185 * @return {Object} Parsed response object.
1187 YAHOO
.util
.DataSource
.prototype.parseJSONData = function(oRequest
, oRawResponse
) {
1188 var oParsedResponse
= {};
1189 if(oRawResponse
&& YAHOO
.lang
.isArray(this.responseSchema
.fields
)) {
1190 var fields
= this.responseSchema
.fields
;
1192 oParsedResponse
.results
= [];
1193 var jsonObj
,jsonList
;
1195 // Parse JSON object out if it's a string
1196 if(YAHOO
.lang
.isString(oRawResponse
)) {
1197 // Check for latest JSON lib but divert KHTML clients
1198 var isNotMac
= (navigator
.userAgent
.toLowerCase().indexOf('khtml')== -1);
1199 if(oRawResponse
.parseJSON
&& isNotMac
) {
1200 // Use the new JSON utility if available
1201 jsonObj
= oRawResponse
.parseJSON();
1206 // Check for older JSON lib but divert KHTML clients
1207 else if(window
.JSON
&& JSON
.parse
&& isNotMac
) {
1208 // Use the JSON utility if available
1209 jsonObj
= JSON
.parse(oRawResponse
);
1214 // No JSON lib found so parse the string
1217 // Trim leading spaces
1218 while (oRawResponse
.length
> 0 &&
1219 (oRawResponse
.charAt(0) != "{") &&
1220 (oRawResponse
.charAt(0) != "[")) {
1221 oRawResponse
= oRawResponse
.substring(1, oRawResponse
.length
);
1224 if(oRawResponse
.length
> 0) {
1225 // Strip extraneous stuff at the end
1226 var objEnd
= Math
.max(oRawResponse
.lastIndexOf("]"),oRawResponse
.lastIndexOf("}"));
1227 oRawResponse
= oRawResponse
.substring(0,objEnd
+1);
1229 // Turn the string into an object literal...
1230 // ...eval is necessary here
1231 jsonObj
= eval("(" + oRawResponse
+ ")");
1247 // Response must already be a JSON object
1248 else if(oRawResponse
.constructor == Object
) {
1249 jsonObj
= oRawResponse
;
1251 // Not a string or an object
1255 // Now that we have a JSON object, parse a jsonList out of it
1256 if(jsonObj
&& jsonObj
.constructor == Object
) {
1258 // eval is necessary here since schema can be of unknown depth
1259 jsonList
= eval("jsonObj." + this.responseSchema
.resultsList
);
1266 if(bError
|| !jsonList
) {
1267 oParsedResponse
.error
= true;
1269 if(jsonList
&& !YAHOO
.lang
.isArray(jsonList
)) {
1270 jsonList
= [jsonList
];
1272 else if(!jsonList
) {
1276 // Loop through the array of all responses...
1277 for(var i
= jsonList
.length
-1; i
>= 0 ; i
--) {
1279 var jsonResult
= jsonList
[i
];
1280 // ...and loop through each data field value of each response
1281 for(var j
= fields
.length
-1; j
>= 0 ; j
--) {
1282 var field
= fields
[j
];
1283 var key
= (YAHOO
.lang
.isValue(field
.key
)) ? field
.key
: field
;
1284 // ...and capture data into an array mapped according to the schema...
1285 // eval is necessary here since schema can be of unknown depth
1286 var data
= eval("jsonResult." + key
);
1288 // Backward compatibility
1289 if(!field
.parser
&& field
.converter
) {
1290 field
.parser
= field
.converter
;
1293 data
= field
.parser
.call(this, data
);
1296 if(data
=== undefined) {
1299 oResult
[key
] = data
;
1301 // Capture the array of data field values in an array of results
1302 oParsedResponse
.results
.unshift(oResult
);
1306 oParsedResponse
.error
= true;
1308 return oParsedResponse
;
1312 * Overridable method parses raw HTML TABLE element data into a response object.
1314 * @method parseHTMLTableData
1315 * @param oRequest {Object} Request object.
1316 * @param oRawResponse {Object} The raw response from the live database.
1317 * @return {Object} Parsed response object.
1319 YAHOO
.util
.DataSource
.prototype.parseHTMLTableData = function(oRequest
, oRawResponse
) {
1321 var elTable
= oRawResponse
;
1322 var fields
= this.responseSchema
.fields
;
1323 var oParsedResponse
= {};
1324 oParsedResponse
.results
= [];
1326 // Iterate through each TBODY
1327 for(var i
=0; i
<elTable
.tBodies
.length
; i
++) {
1328 var elTbody
= elTable
.tBodies
[i
];
1330 // Iterate through each TR
1331 for(var j
=elTbody
.rows
.length
-1; j
>-1; j
--) {
1332 var elRow
= elTbody
.rows
[j
];
1335 for(var k
=fields
.length
-1; k
>-1; k
--) {
1336 var field
= fields
[k
];
1337 var key
= (YAHOO
.lang
.isValue(field
.key
)) ? field
.key
: field
;
1338 var data
= elRow
.cells
[k
].innerHTML
;
1340 // Backward compatibility
1341 if(!field
.parser
&& field
.converter
) {
1342 field
.parser
= field
.converter
;
1345 data
= field
.parser
.call(this, data
);
1348 if(data
=== undefined) {
1351 oResult
[key
] = data
;
1353 oParsedResponse
.results
.unshift(oResult
);
1358 oParsedResponse
.error
= true;
1362 return oParsedResponse
;
1365 YAHOO
.register("datasource", YAHOO
.util
.DataSource
, {version
: "2.3.0", build
: "442"});