| /* |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| 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) { |
| |
| if (!win.__XHRShimAliases) { |
| win.__XHRShimAliases = {}; |
| } |
| |
| win.__onXHRLocalCallback = function (responseCode, responseText, reqId) { |
| if (win.__XHRShimAliases[reqId]){ |
| var alias = win.__XHRShimAliases[reqId]; |
| if (alias){ |
| delete win.__XHRShimAliases[reqId]; |
| if (responseCode == '200'){ |
| alias.onResult && alias.onResult(responseText); |
| Object.defineProperty(alias, 'responseXML', { |
| get: function () { |
| return new DOMParser().parseFromString(this.responseText, 'text/xml'); |
| } |
| }); |
| } else { |
| alias.onError && alias.onError(responseText); |
| } |
| } |
| } |
| }; |
| |
| 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 rootPath = root.substr(0,root.lastIndexOf('/')) + '/'; |
| // Removing unwanted slashes after x-wmapp0 from the basePath, URI cannot process x-wmapp0: /www or //www |
| var basePath = rootPath.replace(/:\/+/gi, ':'); |
| |
| //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 |
| } |
| |
| // Generate unique request ID |
| var reqId = new Date().getTime().toString() + Math.random(); |
| |
| var funk = function () { |
| __XHRShimAliases[reqId] = alias; |
| |
| alias.changeReadyState(XHRShim.LOADING); |
| window.external.Notify('XHRLOCAL/' + reqId + '/' + resolvedUrl); |
| }; |
| |
| this.isAsync ? setTimeout(funk, 0) : funk(); |
| } |
| }, |
| status: 404 |
| }; |
| } |
| })(window, document); "; |
| |
| |
| Browser.InvokeScript("eval", new string[] { script }); |
| } |
| |
| public bool HandleCommand(string commandStr) |
| { |
| if (commandStr.IndexOf("XHRLOCAL") == 0) |
| { |
| var reqStr = commandStr.Replace("XHRLOCAL/", "").Split(new char[] {'/'}, 2); |
| string reqId = reqStr[0]; |
| string url = reqStr[1]; |
| |
| Uri uri = new Uri(url, UriKind.RelativeOrAbsolute); |
| try |
| { |
| 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(); |
| Browser.InvokeScript("__onXHRLocalCallback", new string[] { "200", text, reqId }); |
| return true; |
| } |
| } |
| } |
| } |
| catch (Exception ex) |
| { |
| Debug.WriteLine("ERROR: Exception in HandleCommand: " + ex); |
| } |
| |
| Uri relUri = new Uri(uri.AbsolutePath, UriKind.Relative); |
| |
| var resource = Application.GetResourceStream(relUri); |
| try |
| { |
| if (resource == null) |
| { |
| // 404 ? |
| Browser.InvokeScript("__onXHRLocalCallback", new string[] { "404", string.Empty, reqId }); |
| return true; |
| } |
| else |
| { |
| using (StreamReader streamReader = new StreamReader(resource.Stream)) |
| { |
| string text = streamReader.ReadToEnd(); |
| Browser.InvokeScript("__onXHRLocalCallback", new string[] { "200", text, reqId }); |
| return true; |
| } |
| } |
| } |
| catch (Exception ex) |
| { |
| Debug.WriteLine("ERROR: Exception in HandleCommand: " + ex); |
| } |
| } |
| |
| return false; |
| } |
| } |
| } |