UNPKG

objj-runtime

Version:

JavaScript (ECMAScript) and Objective-J runtime

438 lines (357 loc) 12.4 kB
/* * CFHTTPRequest.js * Objective-J * * Created by Francisco Tolmasky. * Copyright 2010, 280 North, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ var asynchronousTimeoutCount = 0, asynchronousTimeoutId = null, asynchronousFunctionQueue = []; function Asynchronous(/*Function*/ aFunction) { var currentAsynchronousTimeoutCount = asynchronousTimeoutCount; if (asynchronousTimeoutId === null) { window.setNativeTimeout(function() { var queue = asynchronousFunctionQueue, index = 0, count = asynchronousFunctionQueue.length; ++asynchronousTimeoutCount; asynchronousTimeoutId = null; asynchronousFunctionQueue = []; for (; index < count; ++index) queue[index](); }, 0); } return function() { var args = arguments; if (asynchronousTimeoutCount > currentAsynchronousTimeoutCount) aFunction.apply(this, args); else asynchronousFunctionQueue.push(function() { aFunction.apply(this, args); }); }; } var NativeRequest = null; // We check ActiveXObject first, because we require local file access and // overrideMimeType feature (which the native XMLHttpRequest does not have in IE). if (window.XMLHttpRequest) { NativeRequest = window.XMLHttpRequest; } else if (window.ActiveXObject !== undefined) { // DON'T try 4.0 and 5.0: http://bit.ly/microsoft-msxml-explanation var MSXML_XMLHTTP_OBJECTS = ["Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP.6.0"], index = MSXML_XMLHTTP_OBJECTS.length; while (index--) { try { var MSXML_XMLHTTP = MSXML_XMLHTTP_OBJECTS[index]; new ActiveXObject(MSXML_XMLHTTP); NativeRequest = function() { return new ActiveXObject(MSXML_XMLHTTP); }; break; } catch (anException) { } } } GLOBAL(CFHTTPRequest) = function() { this._isOpen = false; this._requestHeaders = {}; this._mimeType = null; this._eventDispatcher = new EventDispatcher(this); this._nativeRequest = new NativeRequest(); // by default, all requests will assume that credentials should not be sent. this._withCredentials = false; this._timeout = 60000; var self = this; this._stateChangeHandler = function() { determineAndDispatchHTTPRequestEvents(self); }; this._timeoutHandler = function() { dispatchTimeoutHTTPRequestEvents(self); }; this._nativeRequest.onreadystatechange = this._stateChangeHandler; this._nativeRequest.ontimeout = this._timeoutHandler; if (CFHTTPRequest.AuthenticationDelegate !== nil) this._eventDispatcher.addEventListener("HTTP403", function() { CFHTTPRequest.AuthenticationDelegate(self); }); } CFHTTPRequest.UninitializedState = 0; CFHTTPRequest.LoadingState = 1; CFHTTPRequest.LoadedState = 2; CFHTTPRequest.InteractiveState = 3; CFHTTPRequest.CompleteState = 4; //override to forward all CFHTTPRequest authorization failures to a single function CFHTTPRequest.AuthenticationDelegate = nil; CFHTTPRequest.prototype.status = function() { try { return this._nativeRequest.status || 0; } catch (anException) { return 0; } }; CFHTTPRequest.prototype.statusText = function() { try { return this._nativeRequest.statusText || ""; } catch (anException) { return ""; } }; CFHTTPRequest.prototype.readyState = function() { return this._nativeRequest.readyState; }; CFHTTPRequest.prototype.success = function() { var status = this.status(); if (status >= 200 && status < 300) return YES; // file:// requests return with status 0, to know if they succeeded, we // need to know if there was any content. return status === 0 && this.responseText() && this.responseText().length; }; CFHTTPRequest.prototype.responseXML = function() { var responseXML = this._nativeRequest.responseXML; // Internet Explorer will return a non-null but empty request.responseXML if the response // content type wasn't "text/html", so also check that responseXML.documentRoot is set. // Otherwise fall back to regular parsing. if (responseXML && (NativeRequest === window.XMLHttpRequest) && responseXML.documentRoot) return responseXML; return parseXML(this.responseText()); }; CFHTTPRequest.prototype.responsePropertyList = function() { var responseText = this.responseText(); if (CFPropertyList.sniffedFormatOfString(responseText) === CFPropertyList.FormatXML_v1_0) return CFPropertyList.propertyListFromXML(this.responseXML()); return CFPropertyList.propertyListFromString(responseText); }; CFHTTPRequest.prototype.responseText = function() { return this._nativeRequest.responseText; }; CFHTTPRequest.prototype.setRequestHeader = function(/*String*/ aHeader, /*Object*/ aValue) { this._requestHeaders[aHeader] = aValue; }; CFHTTPRequest.prototype.getResponseHeader = function(/*String*/ aHeader) { return this._nativeRequest.getResponseHeader(aHeader); }; CFHTTPRequest.prototype.setTimeout = function(/*int*/ aTimeout) { this._timeout = aTimeout; if (this._isOpen) this._nativeRequest.timeout = aTimeout; }; CFHTTPRequest.prototype.getTimeout = function(/*int*/ aTimeout) { return this._timeout; }; CFHTTPRequest.prototype.getAllResponseHeaders = function() { return this._nativeRequest.getAllResponseHeaders(); }; CFHTTPRequest.prototype.overrideMimeType = function(/*String*/ aMimeType) { this._mimeType = aMimeType; }; CFHTTPRequest.prototype.open = function(/*String*/ aMethod, /*String*/ aURL, /*Boolean*/ isAsynchronous, /*String*/ aUser, /*String*/ aPassword) { var retval; this._isOpen = true; this._URL = aURL; this._async = isAsynchronous; this._method = aMethod; this._user = aUser; this._password = aPassword; requestReturnValue = this._nativeRequest.open(aMethod, aURL, isAsynchronous, aUser, aPassword); if (this._async) { this._nativeRequest.withCredentials = this._withCredentials; this._nativeRequest.timeout = this._timeout; } return requestReturnValue; }; CFHTTPRequest.prototype.send = function(/*Object*/ aBody) { if (!this._isOpen) { delete this._nativeRequest.onreadystatechange; delete this._nativeRequest.ontimeout; this._nativeRequest.open(this._method, this._URL, this._async, this._user, this._password); this._nativeRequest.ontimeout = this._timeoutHandler; this._nativeRequest.onreadystatechange = this._stateChangeHandler; } for (var i in this._requestHeaders) { if (this._requestHeaders.hasOwnProperty(i)) this._nativeRequest.setRequestHeader(i, this._requestHeaders[i]); } if (this._mimeType && "overrideMimeType" in this._nativeRequest) this._nativeRequest.overrideMimeType(this._mimeType); this._isOpen = false; try { return this._nativeRequest.send(aBody); } catch (anException) { // FIXME: Do something more complex, with 404's? this._eventDispatcher.dispatchEvent({ type:"failure", request:this }); } }; CFHTTPRequest.prototype.abort = function() { this._isOpen = false; return this._nativeRequest.abort(); }; CFHTTPRequest.prototype.addEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener) { this._eventDispatcher.addEventListener(anEventName, anEventListener); }; CFHTTPRequest.prototype.removeEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener) { this._eventDispatcher.removeEventListener(anEventName, anEventListener); }; CFHTTPRequest.prototype.setWithCredentials = function(/*Boolean*/ willSendWithCredentials) { this._withCredentials = willSendWithCredentials; if (this._isOpen && this._async) this._nativeRequest.withCredentials = willSendWithCredentials; }; CFHTTPRequest.prototype.withCredentials = function() { return this._withCredentials; }; CFHTTPRequest.prototype.isTimeoutRequest = function() { // Can we consider that as a timeout ? return !this.success() && !this._nativeRequest.response && !this._nativeRequest.responseText && !this._nativeRequest.responseType && !this._nativeRequest.responseURL && !this._nativeRequest.responseXML; }; function dispatchTimeoutHTTPRequestEvents(/*CFHTTPRequest*/ aRequest) { aRequest._eventDispatcher.dispatchEvent({ type:"timeout", request:aRequest}); } function determineAndDispatchHTTPRequestEvents(/*CFHTTPRequest*/ aRequest) { var eventDispatcher = aRequest._eventDispatcher, readyStates = ["uninitialized", "loading", "loaded", "interactive", "complete"]; eventDispatcher.dispatchEvent({ type:"readystatechange", request:aRequest}); if (readyStates[aRequest.readyState()] === "complete") { var status = "HTTP" + aRequest.status(); eventDispatcher.dispatchEvent({ type:status, request:aRequest }); var result = aRequest.success() ? "success" : "failure"; eventDispatcher.dispatchEvent({ type:result, request:aRequest }); eventDispatcher.dispatchEvent({ type:readyStates[aRequest.readyState()], request:aRequest}); } else { eventDispatcher.dispatchEvent({ type:readyStates[aRequest.readyState()], request:aRequest}); } } function FileRequest(/*CFURL*/ aURL, onsuccess, onfailure, onprogress) { var request = new CFHTTPRequest(); if (aURL.pathExtension() === "plist") request.overrideMimeType("text/xml"); var loaded = 0, progressHandler = null; function progress(progressEvent) { onprogress(progressEvent.loaded - loaded); loaded = progressEvent.loaded; } function success(anEvent) { // If the browser can't handle progress events, // we need to send a progress message with the total // size of the file when the file finishes loading. if (onprogress && progressHandler === null) onprogress(anEvent.request.responseText().length); onsuccess(anEvent); } if (exports.asyncLoader) { request.onsuccess = Asynchronous(success); request.onfailure = Asynchronous(onfailure); } else { request.onsuccess = success; request.onfailure = onfailure; } #if BROWSER if (onprogress) { var supportsProgress = true; // document.all means IE, window.atob is only supported by IE 10+ if (document.all) supportsProgress = !!window.atob; if (supportsProgress) { try { progressHandler = exports.asyncLoader ? Asynchronous(progress) : progress; request._nativeRequest.onprogress = progressHandler; } catch (anException) { // Must be <= IE 9 progressHandler = null; } } } #endif request.open("GET", aURL.absoluteString(), exports.asyncLoader); request.send(""); } #ifdef BROWSER exports.asyncLoader = YES; #else exports.asyncLoader = NO; #endif // FIXME: Get rid of these when we no longer need Mock requests in flatten. exports.Asynchronous = Asynchronous; exports.determineAndDispatchHTTPRequestEvents = determineAndDispatchHTTPRequestEvents;