@qooxdoo/framework
Version:
The JS Framework for Coders
476 lines (389 loc) • 12.4 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
2006 Derrell Lipman
2006 STZ-IDA, Germany, http://www.stz-ida.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
* Derrell Lipman (derrell)
* Andreas Junghans (lucidcake)
************************************************************************ */
/**
* Transports requests to a server using dynamic script tags.
*
* This class should not be used directly by client programmers.
*
* NOTE: Instances of this class must be disposed of after use
*
*/
qx.Class.define("qx.io.remote.transport.Script",
{
extend : qx.io.remote.transport.Abstract,
implement: [ qx.core.IDisposable ],
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
construct : function()
{
this.base(arguments);
var vUniqueId = ++qx.io.remote.transport.Script.__uniqueId;
if (vUniqueId >= 2000000000) {
qx.io.remote.transport.Script.__uniqueId = vUniqueId = 1;
}
this.__element = null;
this.__uniqueId = vUniqueId;
},
/*
*****************************************************************************
STATICS
*****************************************************************************
*/
statics :
{
/**
* Unique identifier for each instance.
*
* @internal
*/
__uniqueId : 0,
/**
* Registry for all script transport instances.
*
* @internal
*/
_instanceRegistry : {},
/**
* Internal URL parameter prefix.
*
* @internal
*/
ScriptTransport_PREFIX : "_ScriptTransport_",
/**
* Internal URL parameter ID.
*
* @internal
*/
ScriptTransport_ID_PARAM : "_ScriptTransport_id",
/**
* Internal URL parameter data prefix.
*
* @internal
*/
ScriptTransport_DATA_PARAM : "_ScriptTransport_data",
/**
* Capabilities of this transport type.
*
* @internal
*/
handles :
{
synchronous : false,
asynchronous : true,
crossDomain : true,
fileUpload : false,
programmaticFormFields : false,
responseTypes : [ "text/plain", "text/javascript", "application/json" ]
},
/**
* Returns always true, because script transport is supported by all browsers.
* @return {Boolean} <code>true</code>
*/
isSupported : function() {
return true;
},
/*
---------------------------------------------------------------------------
EVENT LISTENER
---------------------------------------------------------------------------
*/
/**
* For reference:
* http://msdn.microsoft.com/en-us/library/ie/ms534359%28v=vs.85%29.aspx
*
* @internal
*/
_numericMap :
{
"uninitialized" : 1,
"loading" : 2,
"loaded" : 2,
"interactive" : 3,
"complete" : 4
},
/**
* This method can be called by the script loaded by the ScriptTransport
* class.
*
* @signature function(id, content)
* @param id {String} Id of the corresponding transport object,
* which is passed as an URL parameter to the server an
* @param content {String} This string is passed to the content property
* of the {@link qx.io.remote.Response} object.
*/
_requestFinished : qx.event.GlobalError.observeMethod(function(id, content)
{
var vInstance = qx.io.remote.transport.Script._instanceRegistry[id];
if (vInstance == null)
{
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Environment.get("qx.debug.io.remote")) {
this.warn("Request finished for an unknown instance (probably aborted or timed out before)");
}
}
}
else
{
vInstance._responseContent = content;
vInstance._switchReadyState(qx.io.remote.transport.Script._numericMap.complete);
}
})
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members :
{
__lastReadyState : 0,
__element : null,
__uniqueId : null,
/*
---------------------------------------------------------------------------
USER METHODS
---------------------------------------------------------------------------
*/
/**
* Sends the request using "script" elements
*
*/
send : function()
{
var vUrl = this.getUrl();
// --------------------------------------
// Adding parameters
// --------------------------------------
vUrl += (vUrl.indexOf("?") >= 0 ? "&" : "?") + qx.io.remote.transport.Script.ScriptTransport_ID_PARAM + "=" + this.__uniqueId;
var vParameters = this.getParameters();
var vParametersList = [];
for (var vId in vParameters)
{
if (vId.indexOf(qx.io.remote.transport.Script.ScriptTransport_PREFIX) == 0) {
this.error("Illegal parameter name. The following prefix is used internally by qooxdoo): " + qx.io.remote.transport.Script.ScriptTransport_PREFIX);
}
var value = vParameters[vId];
if (value instanceof Array)
{
for (var i=0; i<value.length; i++) {
vParametersList.push(encodeURIComponent(vId) + "=" + encodeURIComponent(value[i]));
}
}
else
{
vParametersList.push(encodeURIComponent(vId) + "=" + encodeURIComponent(value));
}
}
if (vParametersList.length > 0) {
vUrl += "&" + vParametersList.join("&");
}
// --------------------------------------
// Sending data
// --------------------------------------
var vData = this.getData();
if (vData != null) {
vUrl += "&" + qx.io.remote.transport.Script.ScriptTransport_DATA_PARAM + "=" + encodeURIComponent(vData);
}
qx.io.remote.transport.Script._instanceRegistry[this.__uniqueId] = this;
this.__element = document.createElement("script");
// IE needs this (it ignores the
// encoding from the header sent by the
// server for dynamic script tags)
this.__element.charset = "utf-8";
this.__element.src = vUrl;
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Environment.get("qx.debug.io.remote.data"))
{
this.debug("Request: " + vUrl);
}
}
document.body.appendChild(this.__element);
},
/**
* Switches the readystate by setting the internal state.
*
* @param vReadyState {String} readystate value
*/
_switchReadyState : function(vReadyState)
{
// Ignoring already stopped requests
switch(this.getState())
{
case "completed":
case "aborted":
case "failed":
case "timeout":
this.warn("Ignore Ready State Change");
return;
}
// Updating internal state
while (this.__lastReadyState < vReadyState) {
this.setState(qx.io.remote.Exchange._nativeMap[++this.__lastReadyState]);
}
},
/*
---------------------------------------------------------------------------
REQUEST HEADER SUPPORT
---------------------------------------------------------------------------
*/
/**
* Sets a request header with the given value.
*
* This method is not implemented at the moment.
*
* @param vLabel {String} Request header name
* @param vValue {var} Request header value
*/
setRequestHeader : function(vLabel, vValue) {},
/*
---------------------------------------------------------------------------
RESPONSE HEADER SUPPORT
---------------------------------------------------------------------------
*/
/**
* Returns the value of the given response header.
*
* This method is not implemented at the moment and returns always "null".
*
* @param vLabel {String} Response header name
* @return {null} Returns null
*/
getResponseHeader : function(vLabel) {
return null;
},
/**
* Provides an hash of all response headers.
*
* This method is not implemented at the moment and returns an empty map.
*
* @return {Map} empty map
*/
getResponseHeaders : function() {
return {};
},
/*
---------------------------------------------------------------------------
STATUS SUPPORT
---------------------------------------------------------------------------
*/
/**
* Returns the current status code of the request if available or -1 if not.
* This method needs implementation (returns always 200).
*
* @return {Integer} status code
*/
getStatusCode : function() {
return 200;
},
/**
* Provides the status text for the current request if available and null otherwise.
* This method needs implementation (returns always an empty string)
*
* @return {String} always an empty string.
*/
getStatusText : function() {
return "";
},
/*
---------------------------------------------------------------------------
RESPONSE DATA SUPPORT
---------------------------------------------------------------------------
*/
/**
* Returns the length of the content as fetched thus far.
* This method needs implementation (returns always 0).
*
* @return {Integer} Returns 0
*/
getFetchedLength : function() {
return 0;
},
/**
* Returns the content of the response.
*
* @return {null | String} If successful content of response as string.
*/
getResponseContent : function()
{
if (this.getState() !== "completed")
{
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Environment.get("qx.debug.io.remote")) {
this.warn("Transfer not complete, ignoring content!");
}
}
return null;
}
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Environment.get("qx.debug.io.remote")) {
this.debug("Returning content for responseType: " + this.getResponseType());
}
}
switch(this.getResponseType())
{
case "text/plain":
// server is responsible for using a string as the response
case "application/json":
case "text/javascript":
if (qx.core.Environment.get("qx.debug"))
{
if (qx.core.Environment.get("qx.debug.io.remote.data"))
{
this.debug("Response: " + this._responseContent);
}
}
var ret = this._responseContent;
return (ret === 0 ? 0 : (ret || null));
default:
this.warn("No valid responseType specified (" + this.getResponseType() + ")!");
return null;
}
}
},
/*
*****************************************************************************
DEFER
*****************************************************************************
*/
defer : function()
{
// basic registration to qx.io.remote.Exchange
// the real availability check (activeX stuff and so on) follows at the first real request
qx.io.remote.Exchange.registerType(qx.io.remote.transport.Script, "qx.io.remote.transport.Script");
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct : function()
{
if (this.__element)
{
delete qx.io.remote.transport.Script._instanceRegistry[this.__uniqueId];
document.body.removeChild(this.__element);
}
this.__element = this._responseContent = null;
}
});