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.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.