diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed00772..2749257 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
## 1.11.0
- Feature #77: allow overriding global settings for each single request
+- Feature #11: add support for "browser" platform
## 1.10.2
diff --git a/package.json b/package.json
index ad837d2..e07a557 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "1.10.2",
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
"scripts": {
+ "buildbrowser": "./scripts/build-test-app.sh --browser",
"testandroid": "./scripts/build-test-app.sh --android --emulator && ./scripts/test-app.sh --android --emulator",
"testios": "./scripts/build-test-app.sh --ios --emulator && ./scripts/test-app.sh --ios --emulator",
"testapp": "npm run testandroid && npm run testios",
diff --git a/plugin.xml b/plugin.xml
index 285027b..ce6128a 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -70,4 +70,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/build-test-app.sh b/scripts/build-test-app.sh
index 0447e35..468bef8 100755
--- a/scripts/build-test-app.sh
+++ b/scripts/build-test-app.sh
@@ -1,11 +1,39 @@
#!/usr/bin/env bash
set -e
-PLATFORM=$([[ "${@#--android}" = "$@" ]] && echo "ios" || echo "android")
-TARGET=$([[ "${@#--device}" = "$@" ]] && echo "emulator" || echo "device")
ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/..
CDV=$ROOT/node_modules/.bin/cordova
+PLATFORM=ios
+TARGET=emulator
+
+while :; do
+ case $1 in
+ --android)
+ PLATFORM=android
+ ;;
+ --browser)
+ PLATFORM=browser
+ ;;
+ --ios)
+ PLATFORM=ios
+ ;;
+ --device)
+ TARGET=device
+ ;;
+ --emulator)
+ TARGET=emulator
+ ;;
+ -?*)
+ printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
+ ;;
+ *)
+ break
+ esac
+
+ shift
+done
+
rm -rf $ROOT/temp
mkdir $ROOT/temp
cp -r $ROOT/test/app-template/ $ROOT/temp/
diff --git a/src/browser/cordova-http-plugin.js b/src/browser/cordova-http-plugin.js
new file mode 100644
index 0000000..e7ac36f
--- /dev/null
+++ b/src/browser/cordova-http-plugin.js
@@ -0,0 +1,165 @@
+var pluginId = module.id.slice(0, module.id.lastIndexOf('.'));
+
+var cordovaProxy = require('cordova/exec/proxy');
+var helpers = require(pluginId + '.helpers');
+
+function serializeJsonData(data) {
+ try {
+ return JSON.stringify(data);
+ } catch (err) {
+ return null;
+ }
+}
+
+function serializePrimitive(key, value) {
+ if (value === null || value === undefined) {
+ return encodeURIComponent(key) + '=';
+ }
+
+ return encodeURIComponent(key) + '=' + encodeURIComponent(value);
+}
+
+function serializeArray(key, values) {
+ return values.map(function(value) {
+ return encodeURIComponent(key) + '[]=' + encodeURIComponent(value);
+ }).join('&');
+}
+
+function serializeParams(params) {
+ if (params === null) return '';
+
+ return Object.keys(params).map(function(key) {
+ if (helpers.getTypeOf(params[key]) === 'Array') {
+ return serializeArray(key, params[key]);
+ }
+
+ return serializePrimitive(key, params[key]);
+ }).join('&');
+}
+
+function createXhrSuccessObject(xhr) {
+ return {
+ url: xhr.responseURL,
+ status: xhr.status,
+ data: helpers.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response,
+ headers: xhr.getAllResponseHeaders()
+ };
+}
+
+function createXhrFailureObject(xhr) {
+ var obj = {};
+
+ obj.headers = xhr.getAllResponseHeaders();
+ obj.error = helpers.getTypeOf(xhr.responseText) === 'String' ? xhr.responseText : xhr.response;
+ obj.error = obj.error || 'advanced-http: please check browser console for error messages';
+
+ if (xhr.responseURL) obj.url = xhr.responseURL;
+ if (xhr.status) obj.status = xhr.status;
+
+ return obj;
+}
+
+function setHeaders(xhr, headers) {
+ Object.keys(headers).forEach(function(key) {
+ if (key === 'Cookie') return;
+
+ xhr.setRequestHeader(key, headers[key]);
+ });
+}
+
+function sendRequest(method, withData, opts, success, failure) {
+ var data = withData ? opts[1] : null;
+ var params = withData ? null : serializeParams(opts[1]);
+ var serializer = withData ? opts[2] : null;
+ var headers = withData ? opts[3] : opts[2];
+ var timeout = withData ? opts[4] : opts[3];
+ var url = params ? opts[0] + '?' + params : opts[0];
+
+ var processedData = null;
+ var xhr = new XMLHttpRequest();
+
+ xhr.open(method, url);
+
+ if (headers.Cookie && headers.Cookie.length > 0) {
+ return failure('advanced-http: custom cookies not supported on browser platform');
+ }
+
+ switch (serializer) {
+ case 'json':
+ processedData = serializeJsonData(data);
+
+ if (processedData === null) {
+ return failure('advanced-http: failed serializing data');
+ }
+
+ break;
+
+ case 'utf8':
+ xhr.setRequestHeader('Content-type', 'application/json; charset=utf8');
+ processedData = data.text;
+ break;
+
+ case 'urlencoded':
+ xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+ processedData = serializeParams(data);
+ break;
+ }
+
+ xhr.timeout = timeout * 1000;
+ setHeaders(xhr, headers);
+
+ xhr.onerror = xhr.ontimeout = function () {
+ return failure(createXhrFailureObject(xhr));
+ };
+
+ xhr.onload = function () {
+ if (xhr.readyState !== xhr.DONE) return;
+
+ if (xhr.status < 200 || xhr.status > 299) {
+ return failure(createXhrFailureObject(xhr));
+ }
+
+ return success(createXhrSuccessObject(xhr));
+ };
+
+ xhr.send(processedData);
+}
+
+var browserInterface = {
+ post: function (success, failure, opts) {
+ return sendRequest('post', true, opts, success, failure);
+ },
+ get: function (success, failure, opts) {
+ return sendRequest('get', false, opts, success, failure);
+ },
+ put: function (success, failure, opts) {
+ return sendRequest('put', true, opts, success, failure);
+ },
+ patch: function (success, failure, opts) {
+ return sendRequest('patch', true, opts, success, failure);
+ },
+ delete: function (success, failure, opts) {
+ return sendRequest('delete', false, opts, success, failure);
+ },
+ head: function (success, failure, opts) {
+ return sendRequest('head', false, opts, success, failure);
+ },
+ uploadFile: function (success, failure, opts) {
+ return failure('advanced-http: function "uploadFile" not supported on browser platform');
+ },
+ downloadFile: function (success, failure, opts) {
+ return failure('advanced-http: function "downloadFile" not supported on browser platform');
+ },
+ enableSSLPinning: function (success, failure, opts) {
+ return failure('advanced-http: function "enableSSLPinning" not supported on browser platform');
+ },
+ acceptAllCerts: function (success, failure, opts) {
+ return failure('advanced-http: function "acceptAllCerts" not supported on browser platform');
+ },
+ disableRedirect: function (success, failure, opts) {
+ return failure('advanced-http: function "disableRedirect" not supported on browser platform');
+ }
+};
+
+module.exports = browserInterface;
+cordovaProxy.add('CordovaHttpPlugin', browserInterface);
diff --git a/test/app-template/config.xml b/test/app-template/config.xml
index d6b05c0..0455895 100644
--- a/test/app-template/config.xml
+++ b/test/app-template/config.xml
@@ -23,6 +23,7 @@
+
diff --git a/test/app-template/www/index.html b/test/app-template/www/index.html
index 24c8e89..04be6f3 100644
--- a/test/app-template/www/index.html
+++ b/test/app-template/www/index.html
@@ -1,7 +1,7 @@
-
+