Skip to content

Instantly share code, notes, and snippets.

@tsubik tsubik/XHRHelper.cs
Created Sep 27, 2014

Embed
What would you like to do?
XHRHelper for cordova that works on Windows Phone 8.1
using Microsoft.Phone.Controls;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Text;
using System.Windows;
namespace WPCordovaClassLib.CordovaLib
{
public class XHRHelper : IBrowserDecorator
{
public WebBrowser Browser { get; set; }
public PhoneApplicationPage Page { get; set; }
public void InjectScript()
{
string script = @"(function(win, doc) {
var docDomain = null;
try {
docDomain = doc.domain;
} catch (err) {}
if (!docDomain || docDomain.length === 0) {
var aliasXHR = win.XMLHttpRequest;
var XHRShim = function() {};
win.XMLHttpRequest = XHRShim;
XHRShim.noConflict = aliasXHR;
XHRShim.UNSENT = 0;
XHRShim.OPENED = 1;
XHRShim.HEADERS_RECEIVED = 2;
XHRShim.LOADING = 3;
XHRShim.DONE = 4;
XHRShim.prototype = {
isAsync: false,
onreadystatechange: null,
readyState: 0,
_url: '',
timeout: 0,
withCredentials: false,
_requestHeaders: null,
open: function (reqType, uri, isAsync, user, password) {
if (uri && uri.indexOf('http') === 0) {
if (!this.wrappedXHR) {
this.wrappedXHR = new aliasXHR();
var self = this;
if (this.timeout > 0) {
this.wrappedXHR.timeout = this.timeout;
}
Object.defineProperty(this, 'timeout', {
set: function(val) {
this.wrappedXHR.timeout = val;
},
get: function() {
return this.wrappedXHR.timeout;
}
});
if (this.withCredentials) {
this.wrappedXHR.withCredentials = this.withCredentials;
}
Object.defineProperty(this, 'withCredentials', {
set: function(val) {
this.wrappedXHR.withCredentials = val;
},
get: function() {
return this.wrappedXHR.withCredentials;
}
});
Object.defineProperty(this, 'status', {
get: function() {
return this.wrappedXHR.status;
}
});
Object.defineProperty(this, 'responseText', {
get: function() {
return this.wrappedXHR.responseText;
}
});
Object.defineProperty(this, 'statusText', {
get: function() {
return this.wrappedXHR.statusText;
}
});
Object.defineProperty(this, 'responseXML', {
get: function() {
return this.wrappedXHR.responseXML;
}
});
Object.defineProperty(this, 'response', {
get: function() {
return this.wrappedXHR.response;
}
});
Object.defineProperty(this, 'responseType', {
set: function(val) {
return this.wrappedXHR.responseType = val;
}
});
this.getResponseHeader = function(header) {
return this.wrappedXHR.getResponseHeader(header);
};
this.getAllResponseHeaders = function() {
return this.wrappedXHR.getAllResponseHeaders();
};
this.wrappedXHR.onreadystatechange = function() {
self.changeReadyState(self.wrappedXHR.readyState);
};
}
return this.wrappedXHR.open(reqType, uri, isAsync, user, password);
}
else
{
this.isAsync = isAsync;
this.reqType = reqType;
this._url = uri;
}
},
statusText: '',
changeReadyState: function(newState) {
this.readyState = newState;
if (this.onreadystatechange) {
// mimic simple 'readystatechange' event which should be passed as per spec
var evt = {type: 'readystatechange', target: this, timeStamp: new Date().getTime()};
this.onreadystatechange(evt);
}
if (this.readyState == XHRShim.DONE){
this.onload && this.onload();
}
},
addEventListener: function (type, listener, useCapture){
if (this.wrappedXHR) {
this.wrappedXHR.addEventListener(type, listener, useCapture);
} else {
this['on' + type] = listener;
}
},
removeEventListener: function (type, listener, useCapture){
if (this.wrappedXHR) {
this.wrappedXHR.removeEventListener(type, listener, useCapture);
} else {
if (this['on' + type] == listener) { // if listener is currently used
delete this['on' + type];
}
}
},
setRequestHeader: function(header, value) {
if (this.wrappedXHR) {
this.wrappedXHR.setRequestHeader(header, value);
}
},
getResponseHeader: function(header) {
return this.wrappedXHR ? this.wrappedXHR.getResponseHeader(header) : '';
},
getAllResponseHeaders: function() {
return this.wrappedXHR ? this.wrappedXHR.getAllResponseHeaders() : '';
},
overrideMimeType: function(mimetype) {
return this.wrappedXHR ? this.wrappedXHR.overrideMimeType(mimetype) : '';
},
responseText: '',
responseXML: '',
onResult: function(res) {
this.status = 200;
if (typeof res == 'object') {
res = JSON.stringify(res);
}
this.responseText = res;
this.responseXML = res;
this.changeReadyState(XHRShim.DONE);
},
onError: function(err) {
this.status = 404;
this.changeReadyState(XHRShim.DONE);
},
abort: function() {
if (this.wrappedXHR) {
return this.wrappedXHR.abort();
}
},
send: function(data) {
if (this.wrappedXHR) {
return this.wrappedXHR.send(data);
}
else {
this.changeReadyState(XHRShim.OPENED);
var alias = this;
var root = window.location.href.split('#')[0]; // remove hash
var basePath = root.substr(0,root.lastIndexOf('/')) + '/';
//console.log( 'Stripping protocol if present and removing leading / characters' );
var resolvedUrl =
// remove protocol from the beginning of the url if present
( this._url.indexOf( window.location.protocol ) === 0 ?
this._url.substring( window.location.protocol.length ) :
this._url )
// get rid of all the starting slashes
.replace(/^[/]*/, '')
.split('#')[0]; // remove hash
var wwwFolderPath = navigator.userAgent.indexOf('MSIE 9.0') > -1 ? 'app/www/' : 'www/';
// handle special case where url is of form app/www but we are loaded just from /www
if( resolvedUrl.indexOf('app/www') == 0 ) {
resolvedUrl = window.location.protocol + wwwFolderPath + resolvedUrl.substr(7);
}
else if( resolvedUrl.indexOf('www') == 0) {
resolvedUrl = window.location.protocol + wwwFolderPath + resolvedUrl.substr(4);
}
if(resolvedUrl.indexOf(':') < 0) {
resolvedUrl = basePath + resolvedUrl; // consider it relative
}
var funk = function () {
if (!window.__onXHRLocalCallback) {
window.__onXHRLocalCallback = {};
}
window.__onXHRLocalCallback[resolvedUrl] = function (responseCode, responseText) {
alias.status = responseCode;
if (responseCode == '200') {
alias.responseText = responseText;
Object.defineProperty(alias, 'responseXML', {
get: function () {
return new DOMParser().parseFromString(this.responseText, 'text/xml');
}
});
}
else {
alias.onerror && alias.onerror(responseCode);
}
alias.changeReadyState(XHRShim.DONE);
delete window.__onXHRLocalCallback[resolvedUrl];
}
alias.changeReadyState(XHRShim.LOADING);
window.external.Notify('XHRLOCAL/' + resolvedUrl);
}
if (this.isAsync) {
setTimeout(funk, 0);
}
else {
funk();
}
}
},
status: 404
};
}
window.__onXHRLocalCallbackProxy = function (url, code, text){
if(window.__onXHRLocalCallback[url]) {
window.__onXHRLocalCallback[url](code, text);
}
}
})(window, document); ";
Browser.InvokeScript("eval", new string[] { script });
}
//Fix from https://issues.apache.org/jira/browse/CB-4873
/// <summary>
/// Invoke a XHR callback
/// </summary>
/// <param name="url">The URL of the request</param>
/// <param name="code">The response code</param>
/// <param name="text">The response text</param>
private void InvokeCallback(string url, int code, string text)
{
Browser.InvokeScript("__onXHRLocalCallbackProxy",
new string[]
{
url,
code.ToString(),
text ?? "" // else InvokeScript crashes if text happens to be null
});
}
public bool HandleCommand(string commandStr)
{
if (commandStr.IndexOf("XHRLOCAL") == 0)
{
string url = commandStr.Replace("XHRLOCAL/", "");
Uri uri = new Uri(url, UriKind.RelativeOrAbsolute);
using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isoFile.FileExists(uri.AbsolutePath))
{
using (TextReader reader = new StreamReader(isoFile.OpenFile(uri.AbsolutePath, FileMode.Open, FileAccess.Read)))
{
string text = reader.ReadToEnd();
InvokeCallback(url, 200, text);
return true;
}
}
}
Uri relUri = new Uri(uri.AbsolutePath, UriKind.Relative);
var resource = Application.GetResourceStream(relUri);
if (resource == null)
{
// 404 ?
InvokeCallback(url, 404, null);
return true;
}
else
{
using (StreamReader streamReader = new StreamReader(resource.Stream))
{
string text = streamReader.ReadToEnd();
InvokeCallback(url, 200, text);
return true;
}
}
}
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.