refactor(eslint): use cordova-eslint /w fix (#275)

diff --git a/.eslintrc.yml b/.eslintrc.yml
new file mode 100644
index 0000000..17277f7
--- /dev/null
+++ b/.eslintrc.yml
@@ -0,0 +1,23 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+root: true
+extends: '@cordova/eslint-config/browser'
+
+overrides:
+    - files: [tests/**/*.js]
+      extends: '@cordova/eslint-config/node-tests'
diff --git a/.jscsrc b/.jscsrc
deleted file mode 100644
index 2656b94..0000000
--- a/.jscsrc
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "disallowMixedSpacesAndTabs": true,
-    "disallowTrailingWhitespace": true,
-    "validateIndentation": 4,
-    "requireLineFeedAtFileEnd": true,
-
-    "disallowSpaceAfterPrefixUnaryOperators": true,
-    "disallowSpaceBeforePostfixUnaryOperators": true,
-    "requireSpaceAfterLineComment": true,
-    "requireCapitalizedConstructors": true,
-
-    "disallowSpacesInNamedFunctionExpression": {
-        "beforeOpeningRoundBrace": true
-    },
-
-    "requireSpaceAfterKeywords": [
-      "if",
-      "else",
-      "for",
-      "while",
-      "do"
-    ]
-}
diff --git a/.jshintrc b/.jshintrc
deleted file mode 100644
index 93c3c13..0000000
--- a/.jshintrc
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-    "browser": true
-  , "devel": true
-  , "bitwise": true
-  , "undef": true
-  , "trailing": true
-  , "quotmark": false
-  , "indent": 4
-  , "unused": "vars"
-  , "latedef": "nofunc"
-  , "globals": {
-        "module": false,
-        "exports": false,
-        "require": false,
-        "FileTransferError": true,
-        "FileUploadResult": true,
-        "resolveLocalFileSystemURI": false
-    }
-}
diff --git a/package.json b/package.json
index 3766722..871efef 100644
--- a/package.json
+++ b/package.json
@@ -13,9 +13,8 @@
     ]
   },
   "scripts": {
-    "test": "npm run lint && npm run style",
-    "lint": "jshint www && jshint src && jshint tests",
-    "style": "jscs tests/tests.js"
+    "test": "npm run lint",
+    "lint": "eslint ."
   },
   "repository": "github:apache/cordova-plugin-file-transfer",
   "bugs": "https://github.com/apache/cordova-plugin-file-transfer/issues",
@@ -39,7 +38,6 @@
     }
   },
   "devDependencies": {
-    "jscs": "^2.6.0",
-    "jshint": "^2.8.0"
+    "@cordova/eslint-config": "^3.0.0"
   }
 }
diff --git a/src/windows/FileTransferProxy.js b/src/windows/FileTransferProxy.js
index 01e6e7b..316779f 100644
--- a/src/windows/FileTransferProxy.js
+++ b/src/windows/FileTransferProxy.js
@@ -17,28 +17,25 @@
  * specific language governing permissions and limitations
  * under the License.
  *
-*/
+ */
 
-/*jshint -W030 */
-/*global Windows, WinJS*/
-/*global module, require*/
+/* global Windows, WinJS */
 
-var FTErr = require('./FileTransferError'),
-    ProgressEvent = require('cordova-plugin-file.ProgressEvent'),
-    FileUploadResult = require('cordova-plugin-file.FileUploadResult'),
-    FileProxy = require('cordova-plugin-file.FileProxy');
+var FTErr = require('./FileTransferError');
+var ProgressEvent = require('cordova-plugin-file.ProgressEvent');
+var FileUploadResult = require('cordova-plugin-file.FileUploadResult');
+var FileProxy = require('cordova-plugin-file.FileProxy');
 
 var appData = Windows.Storage.ApplicationData.current;
 
-var LINE_START = "--";
-var LINE_END = "\r\n";
+var LINE_START = '--';
+var LINE_END = '\r\n';
 var BOUNDARY = '+++++';
 
 var fileTransferOps = [];
 
 // Some private helper functions, hidden by the module
-function cordovaPathToNative(path) {
-
+function cordovaPathToNative (path) {
     var cleanPath = String(path);
     // turn / into \\
     cleanPath = cleanPath.replace(/\//g, '\\');
@@ -49,11 +46,11 @@
     return cleanPath;
 }
 
-function nativePathToCordova(path) {
+function nativePathToCordova (path) {
     return String(path).replace(/\\/g, '/');
 }
 
-function alreadyCancelled(opId) {
+function alreadyCancelled (opId) {
     var op = fileTransferOps[opId];
     return op && op.state === FileTransferOperation.CANCELLED;
 }
@@ -145,7 +142,7 @@
     );
 }
 
-function FileTransferOperation(state, promise) {
+function FileTransferOperation (state, promise) {
     this.state = state;
     this.promise = promise;
 }
@@ -157,9 +154,8 @@
 var HTTP_E_STATUS_NOT_MODIFIED = -2145844944;
 
 module.exports = {
-
-/*
-exec(win, fail, 'FileTransfer', 'upload', 
+    /*
+exec(win, fail, 'FileTransfer', 'upload',
 [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
 */
     upload: function (successCallback, errorCallback, options) {
@@ -170,14 +166,14 @@
         var mimeType = options[4];
         var params = options[5];
         // var trustAllHosts = options[6]; // todo
-        // var chunkedMode = options[7]; // todo 
+        // var chunkedMode = options[7]; // todo
         var headers = options[8] || {};
         var uploadId = options[9];
         var httpMethod = options[10];
 
-        var isMultipart = typeof headers["Content-Type"] === 'undefined';
+        var isMultipart = typeof headers['Content-Type'] === 'undefined';
 
-        function stringToByteArray(str) {
+        function stringToByteArray (str) {
             var byteCharacters = atob(str);
             var byteNumbers = new Array(byteCharacters.length);
             for (var i = 0; i < byteCharacters.length; i++) {
@@ -186,21 +182,21 @@
             return new Uint8Array(byteNumbers);
         }
 
-        if (!filePath || (typeof filePath !== 'string')) {
+        if (!filePath || typeof filePath !== 'string') {
             errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, null, server));
             return;
         }
 
-        if (filePath.indexOf("data:") === 0 && filePath.indexOf("base64") !== -1) {
-            // First a DataWriter object is created, backed by an in-memory stream where 
+        if (filePath.indexOf('data:') === 0 && filePath.indexOf('base64') !== -1) {
+            // First a DataWriter object is created, backed by an in-memory stream where
             // the data will be stored.
             var writer = Windows.Storage.Streams.DataWriter(new Windows.Storage.Streams.InMemoryRandomAccessStream());
             writer.unicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.utf8;
             writer.byteOrder = Windows.Storage.Streams.ByteOrder.littleEndian;
 
-            var commaIndex = filePath.indexOf(",");
+            var commaIndex = filePath.indexOf(',');
             if (commaIndex === -1) {
-                errorCallback(new FTErr(FTErr.INVALID_URL_ERR, fileName, server, null, null, "No comma in data: URI"));
+                errorCallback(new FTErr(FTErr.INVALID_URL_ERR, fileName, server, null, null, 'No comma in data: URI'));
                 return;
             }
 
@@ -213,7 +209,7 @@
             var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader();
             uploader.method = httpMethod;
             for (var header in headers) {
-                if (headers.hasOwnProperty(header)) {
+                if (Object.prototype.hasOwnProperty.call(headers, header)) {
                     uploader.setRequestHeader(header, headers[header]);
                 }
             }
@@ -222,9 +218,9 @@
                 // adding params supplied to request payload
                 var multipartParams = '';
                 for (var key in params) {
-                    if (params.hasOwnProperty(key)) {
+                    if (Object.prototype.hasOwnProperty.call(params, key)) {
                         multipartParams += LINE_START + BOUNDARY + LINE_END;
-                        multipartParams += "Content-Disposition: form-data; name=\"" + key + "\"";
+                        multipartParams += 'Content-Disposition: form-data; name="' + key + '"';
                         multipartParams += LINE_END + LINE_END;
                         multipartParams += params[key];
                         multipartParams += LINE_END;
@@ -232,13 +228,13 @@
                 }
 
                 var multipartFile = LINE_START + BOUNDARY + LINE_END;
-                multipartFile += "Content-Disposition: form-data; name=\"file\";";
-                multipartFile += " filename=\"" + fileName + "\"" + LINE_END;
-                multipartFile += "Content-Type: " + mimeType + LINE_END + LINE_END;
+                multipartFile += 'Content-Disposition: form-data; name="file";';
+                multipartFile += ' filename="' + fileName + '"' + LINE_END;
+                multipartFile += 'Content-Type: ' + mimeType + LINE_END + LINE_END;
 
                 var bound = LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END;
 
-                uploader.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
+                uploader.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + BOUNDARY);
                 writer.writeString(multipartParams);
                 writer.writeString(multipartFile);
                 writer.writeBytes(stringToByteArray(fileDataString));
@@ -249,42 +245,142 @@
 
             var stream;
 
-            // The call to store async sends the actual contents of the writer 
+            // The call to store async sends the actual contents of the writer
             // to the backing stream.
-            writer.storeAsync().then(function () {
-                // For the in-memory stream implementation we are using, the flushAsync call 
-                // is superfluous, but other types of streams may require it.
-                return writer.flushAsync();
-            }).then(function () {
-                // We detach the stream to prolong its useful lifetime. Were we to fail 
-                // to detach the stream, the call to writer.close() would close the underlying 
-                // stream, preventing its subsequent use by the DataReader below. Most clients 
-                // of DataWriter will have no reason to use the underlying stream after 
-                // writer.close() is called, and will therefore have no reason to call
-                // writer.detachStream(). Note that once we detach the stream, we assume 
-                // responsibility for closing the stream subsequently; after the stream 
-                // has been detached, a call to writer.close() will have no effect on the stream.
-                stream = writer.detachStream();
-                // Make sure the stream is read from the beginning in the reader 
-                // we are creating below.
-                stream.seek(0);
-                // Most DataWriter clients will not call writer.detachStream(), 
-                // and furthermore will be working with a file-backed or network-backed stream, 
-                // rather than an in-memory-stream. In such cases, it would be particularly 
-                // important to call writer.close(). Doing so is always a best practice.
-                writer.close();
+            writer
+                .storeAsync()
+                .then(function () {
+                    // For the in-memory stream implementation we are using, the flushAsync call
+                    // is superfluous, but other types of streams may require it.
+                    return writer.flushAsync();
+                })
+                .then(function () {
+                    // We detach the stream to prolong its useful lifetime. Were we to fail
+                    // to detach the stream, the call to writer.close() would close the underlying
+                    // stream, preventing its subsequent use by the DataReader below. Most clients
+                    // of DataWriter will have no reason to use the underlying stream after
+                    // writer.close() is called, and will therefore have no reason to call
+                    // writer.detachStream(). Note that once we detach the stream, we assume
+                    // responsibility for closing the stream subsequently; after the stream
+                    // has been detached, a call to writer.close() will have no effect on the stream.
+                    stream = writer.detachStream();
+                    // Make sure the stream is read from the beginning in the reader
+                    // we are creating below.
+                    stream.seek(0);
+                    // Most DataWriter clients will not call writer.detachStream(),
+                    // and furthermore will be working with a file-backed or network-backed stream,
+                    // rather than an in-memory-stream. In such cases, it would be particularly
+                    // important to call writer.close(). Doing so is always a best practice.
+                    writer.close();
+
+                    if (alreadyCancelled(uploadId)) {
+                        errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server));
+                        return;
+                    }
+
+                    // create download object. This will throw an exception if URL is malformed
+                    var uri = new Windows.Foundation.Uri(server);
+
+                    var createUploadOperation;
+                    try {
+                        createUploadOperation = uploader.createUploadFromStreamAsync(uri, stream);
+                    } catch (e) {
+                        errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
+                        return;
+                    }
+
+                    createUploadOperation.then(
+                        function (upload) {
+                            doUpload(upload, uploadId, filePath, server, successCallback, errorCallback);
+                        },
+                        function (err) {
+                            var errorObj = new FTErr(FTErr.INVALID_URL_ERR);
+                            errorObj.exception = err;
+                            errorCallback(errorObj);
+                        }
+                    );
+                });
+
+            return;
+        }
+
+        if (filePath.substr(0, 8) === 'file:///') {
+            filePath = appData.localFolder.path + filePath.substr(8).split('/').join('\\');
+        } else if (filePath.indexOf('ms-appdata:///') === 0) {
+            // Handle 'ms-appdata' scheme
+            filePath = filePath
+                .replace('ms-appdata:///local', appData.localFolder.path)
+                .replace('ms-appdata:///temp', appData.temporaryFolder.path);
+        } else if (filePath.indexOf('cdvfile://') === 0) {
+            filePath = filePath
+                .replace('cdvfile://localhost/persistent', appData.localFolder.path)
+                .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path);
+        }
+
+        // normalize path separators
+        filePath = cordovaPathToNative(filePath);
+
+        // Create internal download operation object
+        fileTransferOps[uploadId] = new FileTransferOperation(FileTransferOperation.PENDING, null);
+
+        Windows.Storage.StorageFile.getFileFromPathAsync(filePath).then(
+            function (storageFile) {
+                if (!fileName) {
+                    fileName = storageFile.name;
+                }
+                if (!mimeType) {
+                    // use the actual content type of the file, probably this should be the default way.
+                    // other platforms probably can't look this up.
+                    mimeType = storageFile.contentType;
+                }
 
                 if (alreadyCancelled(uploadId)) {
                     errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server));
                     return;
                 }
 
+                // setting request headers for uploader
+                var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader();
+                uploader.method = httpMethod;
+                for (var header in headers) {
+                    if (Object.prototype.hasOwnProperty.call(headers, header)) {
+                        uploader.setRequestHeader(header, headers[header]);
+                    }
+                }
+
                 // create download object. This will throw an exception if URL is malformed
                 var uri = new Windows.Foundation.Uri(server);
 
                 var createUploadOperation;
                 try {
-                    createUploadOperation = uploader.createUploadFromStreamAsync(uri, stream);
+                    if (isMultipart) {
+                        // adding params supplied to request payload
+                        var transferParts = [];
+                        for (var key in params) {
+                            // Create content part for params only if value is specified because CreateUploadAsync fails otherwise
+                            if (
+                                Object.prototype.hasOwnProperty.call(params, key) &&
+                                params[key] !== null &&
+                                params[key] !== undefined &&
+                                params[key].toString() !== ''
+                            ) {
+                                var contentPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart();
+                                contentPart.setHeader('Content-Disposition', 'form-data; name="' + key + '"');
+                                contentPart.setText(params[key]);
+                                transferParts.push(contentPart);
+                            }
+                        }
+
+                        // Adding file to upload to request payload
+                        var fileToUploadPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(fileKey, fileName);
+                        fileToUploadPart.setHeader('Content-Type', mimeType);
+                        fileToUploadPart.setFile(storageFile);
+                        transferParts.push(fileToUploadPart);
+
+                        createUploadOperation = uploader.createUploadAsync(uri, transferParts);
+                    } else {
+                        createUploadOperation = WinJS.Promise.wrap(uploader.createUpload(uri, storageFile));
+                    }
                 } catch (e) {
                     errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
                     return;
@@ -298,105 +394,17 @@
                         var errorObj = new FTErr(FTErr.INVALID_URL_ERR);
                         errorObj.exception = err;
                         errorCallback(errorObj);
-                    });
-            });
-
-            return;
-        }
-
-        if (filePath.substr(0, 8) === "file:///") {
-            filePath = appData.localFolder.path + filePath.substr(8).split("/").join("\\");
-        } else if (filePath.indexOf('ms-appdata:///') === 0) {
-            // Handle 'ms-appdata' scheme
-            filePath = filePath.replace('ms-appdata:///local', appData.localFolder.path)
-                               .replace('ms-appdata:///temp', appData.temporaryFolder.path);
-        } else if (filePath.indexOf('cdvfile://') === 0) {
-            filePath = filePath.replace('cdvfile://localhost/persistent', appData.localFolder.path)
-                               .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path);
-        }
-
-        // normalize path separators
-        filePath = cordovaPathToNative(filePath);
-
-        // Create internal download operation object
-        fileTransferOps[uploadId] = new FileTransferOperation(FileTransferOperation.PENDING, null);
-
-        Windows.Storage.StorageFile.getFileFromPathAsync(filePath)
-        .then(function (storageFile) {
-
-            if (!fileName) {
-                fileName = storageFile.name;
-            }
-            if (!mimeType) {
-                // use the actual content type of the file, probably this should be the default way.
-                // other platforms probably can't look this up.
-                mimeType = storageFile.contentType;
-            }
-
-            if (alreadyCancelled(uploadId)) {
-                errorCallback(new FTErr(FTErr.ABORT_ERR, nativePathToCordova(filePath), server));
-                return;
-            }
-
-            // setting request headers for uploader
-            var uploader = new Windows.Networking.BackgroundTransfer.BackgroundUploader();
-            uploader.method = httpMethod;
-            for (var header in headers) {
-                if (headers.hasOwnProperty(header)) {
-                    uploader.setRequestHeader(header, headers[header]);
-                }
-            }
-
-            // create download object. This will throw an exception if URL is malformed
-            var uri = new Windows.Foundation.Uri(server);
-
-            var createUploadOperation;
-            try {
-                if (isMultipart) {
-                    // adding params supplied to request payload
-                    var transferParts = [];
-                    for (var key in params) {
-                        // Create content part for params only if value is specified because CreateUploadAsync fails otherwise
-                        if (params.hasOwnProperty(key) && params[key] !== null && params[key] !== undefined && params[key].toString() !== "") {
-                            var contentPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart();
-                            contentPart.setHeader("Content-Disposition", "form-data; name=\"" + key + "\"");
-                            contentPart.setText(params[key]);
-                            transferParts.push(contentPart);
-                        }
                     }
-
-                    // Adding file to upload to request payload
-                    var fileToUploadPart = new Windows.Networking.BackgroundTransfer.BackgroundTransferContentPart(fileKey, fileName);
-                    fileToUploadPart.setHeader("Content-Type", mimeType);
-                    fileToUploadPart.setFile(storageFile);
-                    transferParts.push(fileToUploadPart);
-
-                    createUploadOperation = uploader.createUploadAsync(uri, transferParts);
-                } else {
-                    createUploadOperation = WinJS.Promise.wrap(uploader.createUpload(uri, storageFile));
-                }
-            } catch (e) {
-                errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
-                return;
+                );
+            },
+            function (err) {
+                errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, fileName, server, null, null, err));
             }
-
-            createUploadOperation.then(
-                function (upload) {
-                    doUpload(upload, uploadId, filePath, server, successCallback, errorCallback);
-                },
-                function (err) {
-                    var errorObj = new FTErr(FTErr.INVALID_URL_ERR);
-                    errorObj.exception = err;
-                    errorCallback(errorObj);
-                }
-            );
-        }, function (err) {
-            errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, fileName, server, null, null, err));
-        });
+        );
     },
 
     // [source, target, trustAllHosts, id, headers]
-    download:function(successCallback, errorCallback, options) {
+    download: function (successCallback, errorCallback, options) {
         var source = options[0];
         var target = options[1];
         var downloadId = options[3];
@@ -406,25 +414,27 @@
             errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR));
             return;
         }
-        if (target.substr(0, 8) === "file:///") {
-            target = appData.localFolder.path + target.substr(8).split("/").join("\\");
+        if (target.substr(0, 8) === 'file:///') {
+            target = appData.localFolder.path + target.substr(8).split('/').join('\\');
         } else if (target.indexOf('ms-appdata:///') === 0) {
             // Handle 'ms-appdata' scheme
-            target = target.replace('ms-appdata:///local', appData.localFolder.path)
-                           .replace('ms-appdata:///temp', appData.temporaryFolder.path);
+            target = target
+                .replace('ms-appdata:///local', appData.localFolder.path)
+                .replace('ms-appdata:///temp', appData.temporaryFolder.path);
         } else if (target.indexOf('cdvfile://') === 0) {
-            target = target.replace('cdvfile://localhost/persistent', appData.localFolder.path)
-                           .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path);
+            target = target
+                .replace('cdvfile://localhost/persistent', appData.localFolder.path)
+                .replace('cdvfile://localhost/temporary', appData.temporaryFolder.path);
         }
         target = cordovaPathToNative(target);
 
-        var path = target.substr(0, target.lastIndexOf("\\"));
-        var fileName = target.substr(target.lastIndexOf("\\") + 1);
+        var path = target.substr(0, target.lastIndexOf('\\'));
+        var fileName = target.substr(target.lastIndexOf('\\') + 1);
         if (path === null || fileName === null) {
             errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR));
             return;
         }
-        // Download to a temp file to avoid the file deletion on 304 
+        // Download to a temp file to avoid the file deletion on 304
         // CB-7006 Empty file is created on file transfer if server response is 304
         var tempFileName = '~' + fileName;
 
@@ -433,129 +443,136 @@
         // Create internal download operation object
         fileTransferOps[downloadId] = new FileTransferOperation(FileTransferOperation.PENDING, null);
 
-        var downloadCallback = function(storageFolder) {
-            storageFolder.createFileAsync(tempFileName, Windows.Storage.CreationCollisionOption.replaceExisting).then(function (storageFile) {
-
-                if (alreadyCancelled(downloadId)) {
-                    errorCallback(new FTErr(FTErr.ABORT_ERR, source, target));
-                    return;
-                }
-
-                // if download isn't cancelled, contunue with creating and preparing download operation
-                var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader();
-                for (var header in headers) {
-                    if (headers.hasOwnProperty(header)) {
-                        downloader.setRequestHeader(header, headers[header]);
-                    }
-                }
-
-                // create download object. This will throw an exception if URL is malformed
-                try {
-                    var uri = Windows.Foundation.Uri(source);
-                    download = downloader.createDownload(uri, storageFile);
-                } catch (e) {
-                    // so we handle this and call errorCallback
-                    errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
-                    return;
-                }
-
-                var downloadOperation = download.startAsync();
-                // update internal TransferOperation object with newly created promise
-                fileTransferOps[downloadId].promise = downloadOperation;
-
-                downloadOperation.then(function () {
-
-                    // Update TransferOperation object with new state, delete promise property
-                    // since it is not actual anymore
-                    var currentDownloadOp = fileTransferOps[downloadId];
-                    if (currentDownloadOp) {
-                        currentDownloadOp.state = FileTransferOperation.DONE;
-                        currentDownloadOp.promise = null;
+        var downloadCallback = function (storageFolder) {
+            storageFolder.createFileAsync(tempFileName, Windows.Storage.CreationCollisionOption.replaceExisting).then(
+                function (storageFile) {
+                    if (alreadyCancelled(downloadId)) {
+                        errorCallback(new FTErr(FTErr.ABORT_ERR, source, target));
+                        return;
                     }
 
-                    storageFile.renameAsync(fileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(function () {
-                        var nativeURI = storageFile.path.replace(appData.localFolder.path, 'ms-appdata:///local')
-                        .replace(appData.temporaryFolder.path, 'ms-appdata:///temp')
-                        .replace(/\\/g, '/');
+                    // if download isn't cancelled, contunue with creating and preparing download operation
+                    var downloader = new Windows.Networking.BackgroundTransfer.BackgroundDownloader();
+                    for (var header in headers) {
+                        if (Object.prototype.hasOwnProperty.call(headers, header)) {
+                            downloader.setRequestHeader(header, headers[header]);
+                        }
+                    }
 
-                        // Passing null as error callback here because downloaded file should exist in any case
-                        // otherwise the error callback will be hit during file creation in another place
-                        FileProxy.resolveLocalFileSystemURI(successCallback, null, [nativeURI]);
-                    }, function(error) {
-                        errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error));
-                    });
-                }, function(error) {
+                    // create download object. This will throw an exception if URL is malformed
+                    try {
+                        var uri = Windows.Foundation.Uri(source);
+                        download = downloader.createDownload(uri, storageFile);
+                    } catch (e) {
+                        // so we handle this and call errorCallback
+                        errorCallback(new FTErr(FTErr.INVALID_URL_ERR));
+                        return;
+                    }
 
-                    var getTransferError = new WinJS.Promise(function (resolve) {
-                        // Handle download error here. If download was cancelled,
-                        // message property will be specified
-                        if (error.message === 'Canceled') {
-                            resolve(new FTErr(FTErr.ABORT_ERR, source, target, null, null, error));
-                        } else if (error && error.number === HTTP_E_STATUS_NOT_MODIFIED) {
-                            resolve(new FTErr(FTErr.NOT_MODIFIED_ERR, source, target, 304, null, error));
-                        } else {
-                            // in the other way, try to get response property
-                            var response = download.getResponseInformation();
-                            if (!response) {
-                                resolve(new FTErr(FTErr.CONNECTION_ERR, source, target));
-                            } else {
-                                if (download.progress.bytesReceived === 0) {
-                                    resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, null, error));
-                                    return;
-                                }
-                                var reader = new Windows.Storage.Streams.DataReader(download.getResultStreamAt(0));
-                                reader.loadAsync(download.progress.bytesReceived).then(function (bytesLoaded) {
-                                    var payload = reader.readString(bytesLoaded);
-                                    resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, payload, error));
-                                });
+                    var downloadOperation = download.startAsync();
+                    // update internal TransferOperation object with newly created promise
+                    fileTransferOps[downloadId].promise = downloadOperation;
+
+                    downloadOperation.then(
+                        function () {
+                            // Update TransferOperation object with new state, delete promise property
+                            // since it is not actual anymore
+                            var currentDownloadOp = fileTransferOps[downloadId];
+                            if (currentDownloadOp) {
+                                currentDownloadOp.state = FileTransferOperation.DONE;
+                                currentDownloadOp.promise = null;
                             }
+
+                            storageFile.renameAsync(fileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(
+                                function () {
+                                    var nativeURI = storageFile.path
+                                        .replace(appData.localFolder.path, 'ms-appdata:///local')
+                                        .replace(appData.temporaryFolder.path, 'ms-appdata:///temp')
+                                        .replace(/\\/g, '/');
+
+                                    // Passing null as error callback here because downloaded file should exist in any case
+                                    // otherwise the error callback will be hit during file creation in another place
+                                    FileProxy.resolveLocalFileSystemURI(successCallback, null, [nativeURI]);
+                                },
+                                function (error) {
+                                    errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error));
+                                }
+                            );
+                        },
+                        function (error) {
+                            var getTransferError = new WinJS.Promise(function (resolve) {
+                                // Handle download error here. If download was cancelled,
+                                // message property will be specified
+                                if (error.message === 'Canceled') {
+                                    resolve(new FTErr(FTErr.ABORT_ERR, source, target, null, null, error));
+                                } else if (error && error.number === HTTP_E_STATUS_NOT_MODIFIED) {
+                                    resolve(new FTErr(FTErr.NOT_MODIFIED_ERR, source, target, 304, null, error));
+                                } else {
+                                    // in the other way, try to get response property
+                                    var response = download.getResponseInformation();
+                                    if (!response) {
+                                        resolve(new FTErr(FTErr.CONNECTION_ERR, source, target));
+                                    } else {
+                                        if (download.progress.bytesReceived === 0) {
+                                            resolve(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, null, error));
+                                            return;
+                                        }
+                                        var reader = new Windows.Storage.Streams.DataReader(download.getResultStreamAt(0));
+                                        reader.loadAsync(download.progress.bytesReceived).then(function (bytesLoaded) {
+                                            var payload = reader.readString(bytesLoaded);
+                                            resolve(
+                                                new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, response.statusCode, payload, error)
+                                            );
+                                        });
+                                    }
+                                }
+                            });
+                            getTransferError.then(function (fileTransferError) {
+                                // Update TransferOperation object with new state, delete promise property
+                                // since it is not actual anymore
+                                var currentDownloadOp = fileTransferOps[downloadId];
+                                if (currentDownloadOp) {
+                                    currentDownloadOp.state = FileTransferOperation.CANCELLED;
+                                    currentDownloadOp.promise = null;
+                                }
+
+                                // Cleanup, remove incompleted file
+                                storageFile.deleteAsync().then(function () {
+                                    errorCallback(fileTransferError);
+                                });
+                            });
+                        },
+                        function (evt) {
+                            var progressEvent = new ProgressEvent('progress', {
+                                loaded: evt.progress.bytesReceived,
+                                total: evt.progress.totalBytesToReceive,
+                                target: evt.resultFile
+                            });
+                            // when bytesReceived == 0, BackgroundDownloader has not yet differentiated whether it could get file length or not,
+                            // when totalBytesToReceive == 0, BackgroundDownloader is unable to get file length
+                            progressEvent.lengthComputable = evt.progress.bytesReceived > 0 && evt.progress.totalBytesToReceive > 0;
+
+                            successCallback(progressEvent, { keepCallback: true });
                         }
-                    });
-                    getTransferError.then(function (fileTransferError) {
-
-                        // Update TransferOperation object with new state, delete promise property
-                        // since it is not actual anymore
-                        var currentDownloadOp = fileTransferOps[downloadId];
-                        if (currentDownloadOp) {
-                            currentDownloadOp.state = FileTransferOperation.CANCELLED;
-                            currentDownloadOp.promise = null;
-                        }
-
-                        // Cleanup, remove incompleted file
-                        storageFile.deleteAsync().then(function() {
-                            errorCallback(fileTransferError);
-                        });
-                    });
-
-                }, function(evt) {
-
-                    var progressEvent = new ProgressEvent('progress', {
-                        loaded: evt.progress.bytesReceived,
-                        total: evt.progress.totalBytesToReceive,
-                        target: evt.resultFile
-                    });
-                    // when bytesReceived == 0, BackgroundDownloader has not yet differentiated whether it could get file length or not,
-                    // when totalBytesToReceive == 0, BackgroundDownloader is unable to get file length
-                    progressEvent.lengthComputable = (evt.progress.bytesReceived > 0) && (evt.progress.totalBytesToReceive > 0);
-
-                    successCallback(progressEvent, { keepCallback: true });
-                });
-            }, function(error) {
-                errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error));
-            });
+                    );
+                },
+                function (error) {
+                    errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error));
+                }
+            );
         };
 
-        var fileNotFoundErrorCallback = function(error) {
+        var fileNotFoundErrorCallback = function (error) {
             errorCallback(new FTErr(FTErr.FILE_NOT_FOUND_ERR, source, target, null, null, error));
         };
 
         Windows.Storage.StorageFolder.getFolderFromPathAsync(path).then(downloadCallback, function (error) {
             // Handle non-existent directory
             if (error.number === -2147024894) {
-                var parent = path.substr(0, path.lastIndexOf('\\')),
-                    folderNameToCreate = path.substr(path.lastIndexOf('\\') + 1);
+                var parent = path.substr(0, path.lastIndexOf('\\'));
+                var folderNameToCreate = path.substr(path.lastIndexOf('\\') + 1);
 
-                Windows.Storage.StorageFolder.getFolderFromPathAsync(parent).then(function(parentFolder) {
+                Windows.Storage.StorageFolder.getFolderFromPathAsync(parent).then(function (parentFolder) {
                     parentFolder.createFolderAsync(folderNameToCreate).then(downloadCallback, fileNotFoundErrorCallback);
                 }, fileNotFoundErrorCallback);
             } else {
@@ -577,7 +594,6 @@
             fileTransferOps[fileTransferOpId] = new FileTransferOperation(FileTransferOperation.CANCELLED, null);
         }
     }
-
 };
 
-require("cordova/exec/proxy").add("FileTransfer",module.exports);
+require('cordova/exec/proxy').add('FileTransfer', module.exports);
diff --git a/tests/hooks/after_prepare.js b/tests/hooks/after_prepare.js
index 6101c62..7c5b34c 100644
--- a/tests/hooks/after_prepare.js
+++ b/tests/hooks/after_prepare.js
@@ -1,46 +1,48 @@
 #!/usr/bin/env node
 
 /*
-*
-* Licensed to the Apache Software Foundation (ASF) under one
-* or more contributor license agreements.  See the NOTICE file
-* distributed with this work for additional information
-* regarding copyright ownership.  The ASF licenses this file
-* to you 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.
-*
-*/
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+ */
 
 var path = require('path');
 var fs = require('fs');
 
-module.exports = function(context) {
-    function main() {
+module.exports = function (context) {
+    function main () {
         // get the file transfer server address from the specified variables
         var fileTransferServerAddress = getFileTransferServerAddress(context) || getDefaultFileTransferServerAddress(context);
         console.log('Tests will use the following file transfer server address: ' + fileTransferServerAddress);
-        console.log('If you\'re using cordova@6.3.1 and the above address is wrong at "platform add", don\'t worry, it\'ll fix itself on "cordova run" or "cordova prepare".');
+        console.log(
+            'If you\'re using cordova@6.3.1 and the above address is wrong at "platform add", don\'t worry, it\'ll fix itself on "cordova run" or "cordova prepare".'
+        );
 
         // pass it to the tests
         writeFileTransferOptions(fileTransferServerAddress, context);
     }
 
-    function getDefaultFileTransferServerAddress(context) {
+    function getDefaultFileTransferServerAddress (context) {
         var address = null;
         var configNodes = context.opts.plugin.pluginInfo._et._root._children;
 
         for (var node in configNodes) {
-            if (configNodes[node].attrib.name == 'FILETRANSFER_SERVER_ADDRESS') {
+            if (configNodes[node].attrib.name === 'FILETRANSFER_SERVER_ADDRESS') {
                 address = configNodes[node].attrib.default;
             }
         }
@@ -48,18 +50,28 @@
         return address;
     }
 
-    function getFileTransferServerAddress(context) {
-        var platformJsonFile = path.join(context.opts.projectRoot, 'platforms', context.opts.platforms[0], context.opts.platforms[0] + '.json');
+    function getFileTransferServerAddress (context) {
+        var platformJsonFile = path.join(
+            context.opts.projectRoot,
+            'platforms',
+            context.opts.platforms[0],
+            context.opts.platforms[0] + '.json'
+        );
         var platformJson = JSON.parse(fs.readFileSync(platformJsonFile, 'utf8'));
 
-        if (platformJson && platformJson.installed_plugins && platformJson.installed_plugins['cordova-plugin-file-transfer-tests'] && platformJson.installed_plugins['cordova-plugin-file-transfer-tests'].FILETRANSFER_SERVER_ADDRESS) {
+        if (
+            platformJson &&
+            platformJson.installed_plugins &&
+            platformJson.installed_plugins['cordova-plugin-file-transfer-tests'] &&
+            platformJson.installed_plugins['cordova-plugin-file-transfer-tests'].FILETRANSFER_SERVER_ADDRESS
+        ) {
             return platformJson.installed_plugins['cordova-plugin-file-transfer-tests'].FILETRANSFER_SERVER_ADDRESS;
         } else {
             return null;
         }
     }
 
-    function writeFileTransferOptions(address, context) {
+    function writeFileTransferOptions (address, context) {
         for (var p in context.opts.paths) {
             var ftOpts = {
                 serverAddress: address
@@ -71,5 +83,4 @@
     }
 
     main();
-
 };
diff --git a/tests/tests.js b/tests/tests.js
index 63c9c1c..50b128a 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -1,44 +1,42 @@
 /*
-*
-* Licensed to the Apache Software Foundation (ASF) under one
-* or more contributor license agreements.  See the NOTICE file
-* distributed with this work for additional information
-* regarding copyright ownership.  The ASF licenses this file
-* to you 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.
-*
-*/
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+ */
 
-/* global exports, cordova, FileTransfer, FileTransferError, FileUploadOptions, LocalFileSystem, WinJS */
-
-/* jshint jasmine: true */
+/* global cordova, FileTransfer, FileTransferError, FileUploadOptions, WinJS, LocalFileSystem */
 
 exports.defineAutoTests = function () {
-
-    "use strict";
+    'use strict';
 
     // constants
     var ONE_SECOND = 1000; // in milliseconds
     var GRACE_TIME_DELTA = 600; // in milliseconds
     var DEFAULT_FILESYSTEM_SIZE = 1024 * 50; // filesystem size in bytes
-    var UNKNOWN_HOST = "http://foobar.apache.org";
+    var UNKNOWN_HOST = 'http://foobar.apache.org';
     var DOWNLOAD_TIMEOUT = 15 * ONE_SECOND;
     var LONG_TIMEOUT = 60 * ONE_SECOND;
     var UPLOAD_TIMEOUT = 15 * ONE_SECOND;
     var ABORT_DELAY = 100; // for abort() tests
     var LATIN1_SYMBOLS = '¥§©ÆÖÑøøø¼';
-    var DATA_URI_PREFIX = "data:image/png;base64,";
-    var DATA_URI_CONTENT = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
+    var DATA_URI_PREFIX = 'data:image/png;base64,';
+    var DATA_URI_CONTENT =
+        'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
     var DATA_URI_CONTENT_LENGTH = 85; // bytes. (This is the raw file size: used https://en.wikipedia.org/wiki/File:Red-dot-5px.png from https://en.wikipedia.org/wiki/Data_URI_scheme)
     var RETRY_COUNT = 100; // retry some flaky tests (yes, THIS many times, due to Heroku server instability)
     var RETRY_INTERVAL = 100;
@@ -49,31 +47,29 @@
     // Will get it from the config
     // you can specify it as a 'FILETRANSFER_SERVER_ADDRESS' variable upon test plugin installation
     // or change the default value in plugin.xml
-    var SERVER = "";
-    var SERVER_WITH_CREDENTIALS = "";
+    var SERVER = '';
+    var SERVER_WITH_CREDENTIALS = '';
 
     // flags
-    var isWindows = cordova.platformId === "windows";
-    var isBrowser = cordova.platformId === "browser";
+    var isWindows = cordova.platformId === 'windows';
+    var isBrowser = cordova.platformId === 'browser';
     var isWindowsPhone = isWindows && WinJS.Utilities.isPhone;
-    var isIE = isBrowser && navigator.userAgent.indexOf("Trident") >= 0;
-    var isIos = cordova.platformId === "ios";
-    var isIot = cordova.platformId === "android" && navigator.userAgent.indexOf("iot") >= 0;
+    var isIE = isBrowser && navigator.userAgent.indexOf('Trident') >= 0;
+    var isIos = cordova.platformId === 'ios';
+    var isIot = cordova.platformId === 'android' && navigator.userAgent.indexOf('iot') >= 0;
 
     // tests
-    describe("FileTransferError", function () {
-
-        it("should exist", function () {
+    describe('FileTransferError', function () {
+        it('should exist', function () {
             expect(FileTransferError).toBeDefined();
         });
 
-        it("should be constructable", function () {
+        it('should be constructable', function () {
             var transferError = new FileTransferError();
             expect(transferError).toBeDefined();
         });
 
-        it("filetransfer.spec.3 should expose proper constants", function () {
-
+        it('filetransfer.spec.3 should expose proper constants', function () {
             expect(FileTransferError.FILE_NOT_FOUND_ERR).toBeDefined();
             expect(FileTransferError.INVALID_URL_ERR).toBeDefined();
             expect(FileTransferError.CONNECTION_ERR).toBeDefined();
@@ -88,41 +84,42 @@
         });
     });
 
-    describe("FileUploadOptions", function () {
-
-        it("should exist", function () {
+    describe('FileUploadOptions', function () {
+        it('should exist', function () {
             expect(FileUploadOptions).toBeDefined();
         });
 
-        it("should be constructable", function () {
+        it('should be constructable', function () {
             var transferOptions = new FileUploadOptions();
             expect(transferOptions).toBeDefined();
         });
     });
 
-    describe("FileTransfer", function () {
+    describe('FileTransfer', function () {
         this.persistentRoot = null;
-        this.tempRoot       = null;
+        this.tempRoot = null;
 
         // named callbacks
         var unexpectedCallbacks = {
-            httpFail:          function () {},
-            httpWin:           function () {},
-            fileSystemFail:    function () {},
-            fileSystemWin:     function () {},
+            httpFail: function () {},
+            httpWin: function () {},
+            fileSystemFail: function () {},
+            fileSystemWin: function () {},
             fileOperationFail: function () {},
-            fileOperationWin:  function () {},
+            fileOperationWin: function () {}
         };
 
         var expectedCallbacks = {
             unsupportedOperation: function (response) {
-                console.log("spec called unsupported functionality; response:", response);
-            },
+                console.log('spec called unsupported functionality; response:', response);
+            }
         };
 
         // helpers
         var deleteFile = function (fileSystem, name, done) {
-            fileSystem.getFile(name, null,
+            fileSystem.getFile(
+                name,
+                null,
                 function (fileEntry) {
                     fileEntry.remove(
                         function () {
@@ -140,15 +137,16 @@
         };
 
         var writeFile = function (fileSystem, name, content, success, done) {
-            var fileOperationFail = function() {
+            var fileOperationFail = function () {
                 unexpectedCallbacks.fileOperationFail();
                 done();
             };
 
-            fileSystem.getFile(name, { create: true },
+            fileSystem.getFile(
+                name,
+                { create: true },
                 function (fileEntry) {
                     fileEntry.createWriter(function (writer) {
-
                         writer.onwrite = function () {
                             success(fileEntry);
                         };
@@ -161,13 +159,12 @@
                             throw new Error("aborted creating test file '" + name + "': " + evt);
                         };
 
-                        if (cordova.platformId === "browser") {
-                            var blob = new Blob([content + "\n"], { type: "text/plain" });
+                        if (cordova.platformId === 'browser') {
+                            var blob = new Blob([content + '\n'], { type: 'text/plain' });
                             writer.write(blob);
                         } else {
-                            writer.write(content + "\n");
+                            writer.write(content + '\n');
                         }
-
                     }, fileOperationFail);
                 },
                 function () {
@@ -181,7 +178,7 @@
                 expect(event.loaded).toBeGreaterThan(1);
                 expect(event.total).toBeGreaterThan(0);
                 expect(event.total).not.toBeLessThan(event.loaded);
-                expect(event.lengthComputable).toBe(true, "lengthComputable");
+                expect(event.lengthComputable).toBe(true, 'lengthComputable');
             } else {
                 // In IE, when lengthComputable === false, event.total somehow is equal to 2^64
                 if (isIE) {
@@ -194,12 +191,12 @@
         };
 
         var getMalformedUrl = function () {
-            if (cordova.platformId === "android") {
+            if (cordova.platformId === 'android') {
                 // bad protocol causes a MalformedUrlException on Android
-                return "httpssss://example.com";
+                return 'httpssss://example.com';
             } else {
                 // iOS doesn't care about protocol, space in hostname causes error
-                return "httpssss://exa mple.com";
+                return 'httpssss://exa mple.com';
             }
         };
 
@@ -215,13 +212,15 @@
         beforeEach(function (done) {
             var specContext = this;
 
-            window.requestFileSystem(LocalFileSystem.PERSISTENT, DEFAULT_FILESYSTEM_SIZE,
+            window.requestFileSystem(
+                LocalFileSystem.PERSISTENT,
+                DEFAULT_FILESYSTEM_SIZE,
                 function (fileSystem) {
                     specContext.persistentRoot = fileSystem.root;
                     done();
                 },
                 function () {
-                    throw new Error("Failed to initialize persistent file system.");
+                    throw new Error('Failed to initialize persistent file system.');
                 }
             );
         });
@@ -229,30 +228,31 @@
         beforeEach(function (done) {
             var specContext = this;
 
-            window.requestFileSystem(LocalFileSystem.TEMPORARY, DEFAULT_FILESYSTEM_SIZE,
+            window.requestFileSystem(
+                LocalFileSystem.TEMPORARY,
+                DEFAULT_FILESYSTEM_SIZE,
                 function (fileSystem) {
                     specContext.tempRoot = fileSystem.root;
                     done();
                 },
                 function () {
-                    throw new Error("Failed to initialize temporary file system.");
+                    throw new Error('Failed to initialize temporary file system.');
                 }
             );
         });
 
         // spy on all named callbacks
-        beforeEach(function() {
-
+        beforeEach(function () {
             // ignore the actual implementations of the unexpected callbacks
             for (var callback in unexpectedCallbacks) {
-                if (unexpectedCallbacks.hasOwnProperty(callback)) {
+                if (Object.prototype.hasOwnProperty.call(unexpectedCallbacks, callback)) {
                     spyOn(unexpectedCallbacks, callback);
                 }
             }
 
             // but run the implementations of the expected callbacks
-            for (callback in expectedCallbacks) { // jshint ignore: line
-                if (expectedCallbacks.hasOwnProperty(callback)) {
+            for (callback in expectedCallbacks) {
+                if (Object.prototype.hasOwnProperty.call(expectedCallbacks, callback)) {
                     spyOn(expectedCallbacks, callback).and.callThrough();
                 }
             }
@@ -260,9 +260,9 @@
 
         // at the end, check that none of the unexpected callbacks got called,
         // and act on the expected callbacks
-        afterEach(function() {
+        afterEach(function () {
             for (var callback in unexpectedCallbacks) {
-                if (unexpectedCallbacks.hasOwnProperty(callback)) {
+                if (Object.prototype.hasOwnProperty.call(unexpectedCallbacks, callback)) {
                     expect(unexpectedCallbacks[callback]).not.toHaveBeenCalled();
                 }
             }
@@ -272,11 +272,11 @@
             }
         });
 
-        it ("util spec: get file transfer server url", function () {
+        it('util spec: get file transfer server url', function () {
             try {
                 // attempt to synchronously load medic config
                 var xhr = new XMLHttpRequest();
-                xhr.open("GET", "../fileTransferOpts.json", false);
+                xhr.open('GET', '../fileTransferOpts.json', false);
                 xhr.send(null);
                 var parsedCfg = JSON.parse(xhr.responseText);
                 if (parsedCfg.serverAddress) {
@@ -284,27 +284,28 @@
                 }
             } catch (ex) {
                 console.error('Unable to load file transfer server url: ' + ex);
-                console.error('Note: if you are testing this on cordova-ios with cordova-plugin-wkwebview-engine, that may be why you are getting this error. See https://issues.apache.org/jira/browse/CB-10144.');
+                console.error(
+                    'Note: if you are testing this on cordova-ios with cordova-plugin-wkwebview-engine, that may be why you are getting this error. See https://issues.apache.org/jira/browse/CB-10144.'
+                );
                 fail(ex);
             }
         });
 
-        it("should initialise correctly", function() {
+        it('should initialise correctly', function () {
             expect(this.persistentRoot).toBeDefined();
             expect(this.tempRoot).toBeDefined();
         });
 
-        it("should exist", function () {
+        it('should exist', function () {
             expect(FileTransfer).toBeDefined();
         });
 
-        it("filetransfer.spec.1 should be constructable", function () {
+        it('filetransfer.spec.1 should be constructable', function () {
             var transfer = new FileTransfer();
             expect(transfer).toBeDefined();
         });
 
-        it("filetransfer.spec.2 should expose proper functions", function () {
-
+        it('filetransfer.spec.2 should expose proper functions', function () {
             var transfer = new FileTransfer();
 
             expect(transfer.upload).toBeDefined();
@@ -314,24 +315,23 @@
             expect(transfer.download).toEqual(jasmine.any(Function));
         });
 
-        describe("methods", function() {
-            this.transfer       = null;
-            this.root           = null;
-            this.fileName       = null;
-            this.localFilePath  = null;
+        describe('methods', function () {
+            this.transfer = null;
+            this.root = null;
+            this.fileName = null;
+            this.localFilePath = null;
 
-            beforeEach(function() {
-
+            beforeEach(function () {
                 this.transfer = new FileTransfer();
 
                 // assign onprogress handler
                 this.transfer.onprogress = defaultOnProgressHandler;
 
                 // spy on the onprogress handler, but still call through to it
-                spyOn(this.transfer, "onprogress").and.callThrough();
+                spyOn(this.transfer, 'onprogress').and.callThrough();
 
-                this.root          = this.persistentRoot;
-                this.fileName      = "testFile.txt";
+                this.root = this.persistentRoot;
+                this.fileName = 'testFile.txt';
                 this.localFilePath = this.root.toURL() + this.fileName;
             });
 
@@ -341,8 +341,7 @@
             //         - 'httpssss://example.com'
             //         - 'apache.org', with subdomains="true"
             //         - 'cordova-filetransfer.jitsu.com'
-            describe("download", function () {
-
+            describe('download', function () {
                 // helpers
                 var verifyDownload = function (fileEntry, specContext) {
                     expect(fileEntry.name).toBe(specContext.fileName);
@@ -353,431 +352,481 @@
                     deleteFile(this.root, this.fileName, done);
                 });
 
-                it("ensures that test file does not exist", function (done) {
+                it('ensures that test file does not exist', function (done) {
                     deleteFile(this.root, this.fileName, done);
                 });
 
-                it("filetransfer.spec.4 should download a file", function (done) {
-                    var fileURL = SERVER + "/robots.txt";
-                    var specContext = this;
+                it(
+                    'filetransfer.spec.4 should download a file',
+                    function (done) {
+                        var fileURL = SERVER + '/robots.txt';
+                        var specContext = this;
 
-                    var fileWin = function (blob) {
-
-                        if (specContext.transfer.onprogress.calls.any()) {
-                            var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0];
-                            expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size);
-                        } else {
-                            console.log("no progress events were emitted");
-                        }
-
-                        done();
-                    };
-
-                    var fileSystemFail = function() {
-                        unexpectedCallbacks.fileSystemFail();
-                        done();
-                    };
-
-                    var downloadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    var downloadWin = function (entry) {
-
-                        verifyDownload(entry, specContext);
-
-                        // verify the FileEntry representing this file
-                        entry.file(fileWin, fileSystemFail);
-                    };
-
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
-                }, DOWNLOAD_TIMEOUT * 10); // to give Heroku server some time to wake up
-
-                it("filetransfer.spec.4.1 should download a file using target name with space", function (done) {
-
-                    var fileURL = SERVER + "/robots.txt";
-                    this.fileName = "test file.txt";
-                    this.localFilePath = this.root.toURL() + this.fileName;
-
-                    var specContext = this;
-
-                    var fileWin = function (blob) {
-
-                        if (specContext.transfer.onprogress.calls.any()) {
-                            var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0];
-                            expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size);
-                        } else {
-                            console.log("no progress events were emitted");
-                        }
-
-                        done();
-                    };
-
-                    var fileSystemFail = function() {
-                        unexpectedCallbacks.fileSystemFail();
-                        done();
-                    };
-
-                    var downloadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    var downloadWin = function (entry) {
-
-                        verifyDownload(entry, specContext);
-
-                        // verify the FileEntry representing this file
-                        entry.file(fileWin, fileSystemFail);
-                    };
-
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.5 should download a file using http basic auth", function (done) {
-                    var fileURL = SERVER_WITH_CREDENTIALS + "/download_basic_auth";
-                    var specContext = this;
-
-                    var downloadWin = function (entry) {
-                        verifyDownload(entry, specContext);
-                        done();
-                    };
-
-                    var downloadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.6 should get 401 status on http basic auth failure", function (done) {
-                    // NOTE:
-                    //      using server without credentials
-                    var fileURL = SERVER + "/download_basic_auth";
-
-                    var downloadFail = function (error) {
-                        expect(error.http_status).toBe(401);
-                        expect(error.http_status).not.toBe(404, "Ensure " + fileURL + " is in the white list");
-                        done();
-                    };
-
-                    var downloadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail, null,
-                        {
-                            headers: {
-                                "If-Modified-Since": "Thu, 19 Mar 2015 00:00:00 GMT"
+                        var fileWin = function (blob) {
+                            if (specContext.transfer.onprogress.calls.any()) {
+                                var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0];
+                                expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size);
+                            } else {
+                                console.log('no progress events were emitted');
                             }
-                        });
-                }, DOWNLOAD_TIMEOUT);
 
-                it("filetransfer.spec.7 should download a file using file:// (when hosted from file://)", function (done) {
-                    // for Windows platform it's ms-appdata:/// by default, not file://
-                    if (isWindows) {
-                        pending();
-                        return;
-                    }
-
-                    var fileURL = window.location.protocol + "//" + window.location.pathname.replace(/ /g, "%20");
-                    var specContext = this;
-
-                    if (!/^file:/.exec(fileURL)) {
-                        done();
-                        return;
-                    }
-
-                    var downloadWin = function (entry) {
-                        verifyDownload(entry, specContext);
-                        done();
-                    };
-
-                    var downloadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.8 should download a file using https://", function (done) {
-                    var fileURL = "https://www.apache.org/licenses/";
-                    var specContext = this;
-
-                    var downloadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    var fileOperationFail = function() {
-                        unexpectedCallbacks.fileOperationFail();
-                        done();
-                    };
-
-                    var fileSystemFail = function() {
-                        unexpectedCallbacks.fileSystemFail();
-                        done();
-                    };
-
-                    var fileWin = function (file) {
-
-                        var reader = new FileReader();
-
-                        reader.onerror = fileOperationFail;
-                        reader.onload  = function () {
-                            expect(reader.result).toMatch(/The Apache Software Foundation/);
                             done();
                         };
 
-                        reader.readAsText(file);
+                        var fileSystemFail = function () {
+                            unexpectedCallbacks.fileSystemFail();
+                            done();
+                        };
+
+                        var downloadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        var downloadWin = function (entry) {
+                            verifyDownload(entry, specContext);
+
+                            // verify the FileEntry representing this file
+                            entry.file(fileWin, fileSystemFail);
+                        };
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT * 10
+                ); // to give Heroku server some time to wake up
+
+                it(
+                    'filetransfer.spec.4.1 should download a file using target name with space',
+                    function (done) {
+                        var fileURL = SERVER + '/robots.txt';
+                        this.fileName = 'test file.txt';
+                        this.localFilePath = this.root.toURL() + this.fileName;
+
+                        var specContext = this;
+
+                        var fileWin = function (blob) {
+                            if (specContext.transfer.onprogress.calls.any()) {
+                                var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0];
+                                expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size);
+                            } else {
+                                console.log('no progress events were emitted');
+                            }
+
+                            done();
+                        };
+
+                        var fileSystemFail = function () {
+                            unexpectedCallbacks.fileSystemFail();
+                            done();
+                        };
+
+                        var downloadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        var downloadWin = function (entry) {
+                            verifyDownload(entry, specContext);
+
+                            // verify the FileEntry representing this file
+                            entry.file(fileWin, fileSystemFail);
+                        };
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.5 should download a file using http basic auth',
+                    function (done) {
+                        var fileURL = SERVER_WITH_CREDENTIALS + '/download_basic_auth';
+                        var specContext = this;
+
+                        var downloadWin = function (entry) {
+                            verifyDownload(entry, specContext);
+                            done();
+                        };
+
+                        var downloadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.6 should get 401 status on http basic auth failure',
+                    function (done) {
+                        // NOTE:
+                        //      using server without credentials
+                        var fileURL = SERVER + '/download_basic_auth';
+
+                        var downloadFail = function (error) {
+                            expect(error.http_status).toBe(401);
+                            expect(error.http_status).not.toBe(404, 'Ensure ' + fileURL + ' is in the white list');
+                            done();
+                        };
+
+                        var downloadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail, null, {
+                            headers: {
+                                'If-Modified-Since': 'Thu, 19 Mar 2015 00:00:00 GMT'
+                            }
+                        });
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.7 should download a file using file:// (when hosted from file://)',
+                    function (done) {
+                        // for Windows platform it's ms-appdata:/// by default, not file://
+                        if (isWindows) {
+                            pending();
+                            return;
+                        }
+
+                        var fileURL = window.location.protocol + '//' + window.location.pathname.replace(/ /g, '%20');
+                        var specContext = this;
+
+                        if (!/^file:/.exec(fileURL)) {
+                            done();
+                            return;
+                        }
+
+                        var downloadWin = function (entry) {
+                            verifyDownload(entry, specContext);
+                            done();
+                        };
+
+                        var downloadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.8 should download a file using https://',
+                    function (done) {
+                        var fileURL = 'https://www.apache.org/licenses/';
+                        var specContext = this;
+
+                        var downloadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        var fileOperationFail = function () {
+                            unexpectedCallbacks.fileOperationFail();
+                            done();
+                        };
+
+                        var fileSystemFail = function () {
+                            unexpectedCallbacks.fileSystemFail();
+                            done();
+                        };
+
+                        var fileWin = function (file) {
+                            var reader = new FileReader();
+
+                            reader.onerror = fileOperationFail;
+                            reader.onload = function () {
+                                expect(reader.result).toMatch(/The Apache Software Foundation/);
+                                done();
+                            };
+
+                            reader.readAsText(file);
+                        };
+
+                        var downloadWin = function (entry) {
+                            verifyDownload(entry, specContext);
+                            entry.file(fileWin, fileSystemFail);
+                        };
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
+                    },
+                    isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.11 should call the error callback on abort()',
+                    function (done) {
+                        var fileURL = 'http://cordova.apache.org/downloads/BlueZedEx.mp3';
+                        fileURL = fileURL + '?q=' + new Date().getTime();
+                        var specContext = this;
+
+                        var downloadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, done);
+                        setTimeout(function () {
+                            specContext.transfer.abort();
+                        }, ABORT_DELAY);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.9 should not leave partial file due to abort',
+                    function (done) {
+                        var fileURL = 'http://cordova.apache.org/downloads/logos_2.zip';
+                        var specContext = this;
+
+                        var fileSystemWin = function () {
+                            unexpectedCallbacks.fileSystemWin();
+                            done();
+                        };
+
+                        var downloadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        var downloadFail = function (error) {
+                            var result = !!(error.code === FileTransferError.ABORT_ERR || error.code === FileTransferError.CONNECTION_ERR);
+                            if (!result) {
+                                fail(
+                                    'Expected ' +
+                                        error.code +
+                                        ' to be ' +
+                                        FileTransferError.ABORT_ERR +
+                                        ' or ' +
+                                        FileTransferError.CONNECTION_ERR
+                                );
+                            }
+                            expect(specContext.transfer.onprogress).toHaveBeenCalled();
+
+                            // check that there is no file
+                            specContext.root.getFile(specContext.fileName, null, fileSystemWin, done);
+                        };
+
+                        // abort at the first onprogress event
+                        specContext.transfer.onprogress = function (event) {
+                            if (event.loaded > 0) {
+                                specContext.transfer.abort();
+                            }
+                        };
+
+                        spyOn(specContext.transfer, 'onprogress').and.callThrough();
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
+                    },
+                    isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.10 should be stopped by abort()',
+                    function (done) {
+                        var fileURL = 'http://cordova.apache.org/downloads/BlueZedEx.mp3';
+                        fileURL = fileURL + '?q=' + new Date().getTime();
+                        var specContext = this;
+
+                        expect(specContext.transfer.abort).not.toThrow(); // should be a no-op.
+
+                        var downloadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        var downloadFail = function (error) {
+                            expect(error.code).toBe(FileTransferError.ABORT_ERR);
+
+                            // delay calling done() to wait for the bogus abort()
+                            setTimeout(done, GRACE_TIME_DELTA * 2);
+                        };
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
+                        setTimeout(function () {
+                            specContext.transfer.abort();
+                        }, ABORT_DELAY);
+
+                        // call abort() again, after a time greater than the grace period
+                        setTimeout(function () {
+                            expect(specContext.transfer.abort).not.toThrow();
+                        }, GRACE_TIME_DELTA);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.12 should get http status on failure',
+                    function (done) {
+                        var fileURL = SERVER + '/404';
+
+                        var downloadFail = function (error) {
+                            expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list');
+                            expect(error.http_status).toBe(404);
+
+                            expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+
+                            done();
+                        };
+
+                        var downloadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.13 should get http body on failure',
+                    function (done) {
+                        var fileURL = SERVER + '/404';
+
+                        var downloadFail = function (error) {
+                            expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list');
+                            expect(error.http_status).toBe(404);
+
+                            expect(error.body).toBeDefined();
+                            expect(error.body).toMatch('You requested a 404');
+
+                            done();
+                        };
+
+                        var downloadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it('filetransfer.spec.14 should handle malformed urls', function (done) {
+                    var fileURL = getMalformedUrl();
+
+                    var downloadFail = function (error) {
+                        // Note: Android needs the bad protocol to be added to the access list
+                        // <access origin=".*"/> won't match because ^https?:// is prepended to the regex
+                        // The bad protocol must begin with http to avoid automatic prefix
+                        expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list');
+                        expect(error.code).toBe(FileTransferError.INVALID_URL_ERR);
+
+                        done();
                     };
 
-                    var downloadWin = function (entry) {
-                        verifyDownload(entry, specContext);
-                        entry.file(fileWin, fileSystemFail);
-                    };
-
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
-                }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.11 should call the error callback on abort()", function (done) {
-                    var fileURL = "http://cordova.apache.org/downloads/BlueZedEx.mp3";
-                    fileURL = fileURL + "?q=" + (new Date()).getTime();
-                    var specContext = this;
-
                     var downloadWin = function () {
                         unexpectedCallbacks.httpWin();
                         done();
                     };
 
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, done);
-                    setTimeout(function() {
-                        specContext.transfer.abort();
-                    }, ABORT_DELAY);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.9 should not leave partial file due to abort", function (done) {
-                    var fileURL = "http://cordova.apache.org/downloads/logos_2.zip";
-                    var specContext = this;
-
-                    var fileSystemWin = function() {
-                        unexpectedCallbacks.fileSystemWin();
-                        done();
-                    };
-
-                    var downloadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    var downloadFail = function (error) {
-
-                        var result = (error.code === FileTransferError.ABORT_ERR || error.code === FileTransferError.CONNECTION_ERR)? true: false;
-                        if (!result) {
-                            fail("Expected " + error.code + " to be " + FileTransferError.ABORT_ERR + " or " + FileTransferError.CONNECTION_ERR);
-                        }
-                        expect(specContext.transfer.onprogress).toHaveBeenCalled();
-
-                        // check that there is no file
-                        specContext.root.getFile(specContext.fileName, null, fileSystemWin, done);
-                    };
-
-                    // abort at the first onprogress event
-                    specContext.transfer.onprogress = function (event) {
-                        if (event.loaded > 0) {
-                            specContext.transfer.abort();
-                        }
-                    };
-
-                    spyOn(specContext.transfer, "onprogress").and.callThrough();
-
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
-                }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.10 should be stopped by abort()", function (done) {
-                    var fileURL = "http://cordova.apache.org/downloads/BlueZedEx.mp3";
-                    fileURL = fileURL + "?q=" + (new Date()).getTime();
-                    var specContext = this;
-
-                    expect(specContext.transfer.abort).not.toThrow(); // should be a no-op.
-
-                    var downloadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    var downloadFail = function (error) {
-
-                        expect(error.code).toBe(FileTransferError.ABORT_ERR);
-
-                        // delay calling done() to wait for the bogus abort()
-                        setTimeout(done, GRACE_TIME_DELTA * 2);
-                    };
-
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
-                    setTimeout(function() {
-                        specContext.transfer.abort();
-                    }, ABORT_DELAY);
-
-                    // call abort() again, after a time greater than the grace period
-                    setTimeout(function () {
-                        expect(specContext.transfer.abort).not.toThrow();
-                    }, GRACE_TIME_DELTA);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.12 should get http status on failure", function (done) {
-                    var fileURL = SERVER + "/404";
-
-                    var downloadFail = function (error) {
-
-                        expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list");
-                        expect(error.http_status).toBe(404);
-
-                        expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
-
-                        done();
-                    };
-
-                    var downloadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.13 should get http body on failure", function (done) {
-                    var fileURL = SERVER + "/404";
-
-                    var downloadFail = function (error) {
-
-                        expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list");
-                        expect(error.http_status).toBe(404);
-
-                        expect(error.body).toBeDefined();
-                        expect(error.body).toMatch("You requested a 404");
-
-                        done();
-                    };
-
-                    var downloadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.14 should handle malformed urls", function (done) {
-                    var fileURL = getMalformedUrl();
-
-                    var downloadFail = function (error) {
-
-                        // Note: Android needs the bad protocol to be added to the access list
-                        // <access origin=".*"/> won't match because ^https?:// is prepended to the regex
-                        // The bad protocol must begin with http to avoid automatic prefix
-                        expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list");
-                        expect(error.code).toBe(FileTransferError.INVALID_URL_ERR);
-
-                        done();
-                    };
-
-                    var downloadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
                     this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
                 });
 
-                it("filetransfer.spec.15 should handle unknown host", function (done) {
-                    var fileURL = UNKNOWN_HOST;
+                it(
+                    'filetransfer.spec.15 should handle unknown host',
+                    function (done) {
+                        var fileURL = UNKNOWN_HOST;
 
-                    var downloadFail = function (error) {
-                        expect(error.code).toBe(FileTransferError.CONNECTION_ERR);
-                        done();
-                    };
+                        var downloadFail = function (error) {
+                            expect(error.code).toBe(FileTransferError.CONNECTION_ERR);
+                            done();
+                        };
 
-                    var downloadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
+                        var downloadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
 
-                    // turn off the onprogress handler
-                    this.transfer.onprogress = function () {};
+                        // turn off the onprogress handler
+                        this.transfer.onprogress = function () {};
 
-                    this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
-                }, isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT);
+                        this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
+                    },
+                    isWindows ? LONG_TIMEOUT : DOWNLOAD_TIMEOUT
+                );
 
-                it("filetransfer.spec.16 should handle bad file path", function (done) {
+                it('filetransfer.spec.16 should handle bad file path', function (done) {
                     var fileURL = SERVER;
 
-                    var downloadWin = function() {
+                    var downloadWin = function () {
                         unexpectedCallbacks.httpWin();
                         done();
                     };
 
-                    this.transfer.download(fileURL, "c:\\54321", downloadWin, done);
+                    this.transfer.download(fileURL, 'c:\\54321', downloadWin, done);
                 });
 
-                it("filetransfer.spec.17 progress should work with gzip encoding", function (done) {
-                    var fileURL = "http://www.apache.org/";
-                    var specContext = this;
+                it(
+                    'filetransfer.spec.17 progress should work with gzip encoding',
+                    function (done) {
+                        var fileURL = 'http://www.apache.org/';
+                        var specContext = this;
 
-                    var downloadWin = function (entry) {
-                        verifyDownload(entry, specContext);
-                        done();
-                    };
+                        var downloadWin = function (entry) {
+                            verifyDownload(entry, specContext);
+                            done();
+                        };
 
-                    var downloadFail = function () {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
+                        var downloadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
 
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin,downloadFail);
-                }, DOWNLOAD_TIMEOUT);
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
 
-                it("filetransfer.spec.30 downloaded file entries should have a toNativeURL method", function (done) {
-                    if (cordova.platformId === "browser") {
-                        pending();
-                        return;
-                    }
-
-                    var fileURL = SERVER + "/robots.txt";
-
-                    var downloadWin = function (entry) {
-
-                        expect(entry.toNativeURL).toBeDefined();
-                        expect(entry.toNativeURL).toEqual(jasmine.any(Function));
-
-                        var nativeURL = entry.toNativeURL();
-
-                        expect(nativeURL).toBeTruthy();
-                        expect(nativeURL).toEqual(jasmine.any(String));
-
-                        if (isWindows) {
-                            expect(nativeURL.substring(0, 14)).toBe("ms-appdata:///");
-                        } else {
-                            expect(nativeURL.substring(0, 7)).toBe("file://");
+                it(
+                    'filetransfer.spec.30 downloaded file entries should have a toNativeURL method',
+                    function (done) {
+                        if (cordova.platformId === 'browser') {
+                            pending();
+                            return;
                         }
 
-                        done();
-                    };
+                        var fileURL = SERVER + '/robots.txt';
 
-                    var downloadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
+                        var downloadWin = function (entry) {
+                            expect(entry.toNativeURL).toBeDefined();
+                            expect(entry.toNativeURL).toEqual(jasmine.any(Function));
 
-                    this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
-                }, DOWNLOAD_TIMEOUT);
+                            var nativeURL = entry.toNativeURL();
 
-                it("filetransfer.spec.28 (compatibility) should be able to download a file using local paths", function (done) {
-                    var fileURL = SERVER + "/robots.txt";
+                            expect(nativeURL).toBeTruthy();
+                            expect(nativeURL).toEqual(jasmine.any(String));
+
+                            if (isWindows) {
+                                expect(nativeURL.substring(0, 14)).toBe('ms-appdata:///');
+                            } else {
+                                expect(nativeURL.substring(0, 7)).toBe('file://');
+                            }
+
+                            done();
+                        };
+
+                        var downloadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        this.transfer.download(fileURL, this.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it('filetransfer.spec.28 (compatibility) should be able to download a file using local paths', function (done) {
+                    var fileURL = SERVER + '/robots.txt';
                     var specContext = this;
 
                     var unsupported = function (response) {
@@ -797,7 +846,7 @@
                         internalFilePath = specContext.localFilePath;
                     }
 
-                    var downloadFail = function() {
+                    var downloadFail = function () {
                         unexpectedCallbacks.httpFail();
                         done();
                     };
@@ -806,149 +855,173 @@
                     // backwards compatibilty. By obtaining the raw filesystem path of the download
                     // location, we can pass that to transfer.download() to make sure that previously-stored
                     // paths are still valid.
-                    cordova.exec(function (localPath) {
-                        specContext.transfer.download(fileURL, localPath, downloadWin, downloadFail);
-                    }, unsupported, "File", "_getLocalFilesystemPath", [internalFilePath]);
+                    cordova.exec(
+                        function (localPath) {
+                            specContext.transfer.download(fileURL, localPath, downloadWin, downloadFail);
+                        },
+                        unsupported,
+                        'File',
+                        '_getLocalFilesystemPath',
+                        [internalFilePath]
+                    );
                 });
 
-                it("filetransfer.spec.33 should properly handle 304", function (done) {
-                    var downloadFail = function (error) {
-                        expect(error.http_status).toBe(304);
-                        expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR);
-                        done();
-                    };
-
-                    var downloadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    this.transfer.download(SERVER + '/304', this.localFilePath, downloadWin, downloadFail);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.35 304 should not result in the deletion of a cached file", function (done) {
-                    var specContext = this;
-
-                    var fileOperationFail = function() {
-                        unexpectedCallbacks.fileOperationFail();
-                        done();
-                    };
-
-                    var fileSystemFail = function() {
-                        unexpectedCallbacks.fileSystemFail();
-                        done();
-                    };
-
-                    var httpWin =  function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    var downloadFail = function (error) {
-                        expect(error.http_status).toBe(304);
-                        expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR);
-
-                        specContext.persistentRoot.getFile(specContext.fileName, { create: false },
-                            function (entry) {
-                                var fileWin = function (file) {
-                                    var reader = new FileReader();
-
-                                    reader.onerror = fileOperationFail;
-                                    reader.onloadend  = function () {
-
-                                        expect(reader.result).toBeTruthy();
-                                        if (reader.result !== null) {
-                                            expect(reader.result.length).toBeGreaterThan(0);
-                                        }
-
-                                        done();
-                                    };
-
-                                    reader.readAsBinaryString(file);
-                                };
-
-                                entry.file(fileWin, fileSystemFail);
-                            },
-                            function (err) {
-                                expect("Could not open test file '" + specContext.fileName + "': " + JSON.stringify(err)).not.toBeDefined();
-                                done();
-                            }
-                        );
-                    };
-
-                    writeFile(specContext.root, specContext.fileName, 'Temp data', function () {
-                        specContext.transfer.download(SERVER + '/304', specContext.localFilePath, httpWin, downloadFail);
-                    }, fileOperationFail);
-                }, DOWNLOAD_TIMEOUT);
-
-                it("filetransfer.spec.36 should handle non-UTF8 encoded download response", function (done) {
-                    // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840
-                    if (!isIos) {
-                        pending();
-                    }
-
-                    var fileURL = SERVER + '/download_non_utf';
-                    var specContext = this;
-
-                    var fileOperationFail = function() {
-                        unexpectedCallbacks.fileOperationFail();
-                        done();
-                    };
-
-                    var fileSystemFail = function() {
-                        unexpectedCallbacks.fileSystemFail();
-                        done();
-                    };
-
-                    var httpFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    var fileWin = function (blob) {
-
-                        if (specContext.transfer.onprogress.calls.any()) {
-                            var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0];
-                            expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size);
-                        } else {
-                            console.log("no progress events were emitted");
-                        }
-
-                        expect(blob.size).toBeGreaterThan(0);
-
-                        var reader = new FileReader();
-
-                        reader.onerror = fileOperationFail;
-                        reader.onloadend  = function () {
-                            expect(reader.result.indexOf(LATIN1_SYMBOLS)).not.toBe(-1);
+                it(
+                    'filetransfer.spec.33 should properly handle 304',
+                    function (done) {
+                        var downloadFail = function (error) {
+                            expect(error.http_status).toBe(304);
+                            expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR);
                             done();
                         };
 
-                        reader.readAsBinaryString(blob);
-                    };
+                        var downloadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
 
-                    var downloadWin = function (entry) {
+                        this.transfer.download(SERVER + '/304', this.localFilePath, downloadWin, downloadFail);
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
 
-                        verifyDownload(entry, specContext);
+                it(
+                    'filetransfer.spec.35 304 should not result in the deletion of a cached file',
+                    function (done) {
+                        var specContext = this;
 
-                        // verify the FileEntry representing this file
-                        entry.file(fileWin, fileSystemFail);
-                    };
+                        var fileOperationFail = function () {
+                            unexpectedCallbacks.fileOperationFail();
+                            done();
+                        };
 
-                    specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, httpFail);
-                }, UPLOAD_TIMEOUT);
+                        var fileSystemFail = function () {
+                            unexpectedCallbacks.fileSystemFail();
+                            done();
+                        };
+
+                        var httpWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        var downloadFail = function (error) {
+                            expect(error.http_status).toBe(304);
+                            expect(error.code).toBe(FileTransferError.NOT_MODIFIED_ERR);
+
+                            specContext.persistentRoot.getFile(
+                                specContext.fileName,
+                                { create: false },
+                                function (entry) {
+                                    var fileWin = function (file) {
+                                        var reader = new FileReader();
+
+                                        reader.onerror = fileOperationFail;
+                                        reader.onloadend = function () {
+                                            expect(reader.result).toBeTruthy();
+                                            if (reader.result !== null) {
+                                                expect(reader.result.length).toBeGreaterThan(0);
+                                            }
+
+                                            done();
+                                        };
+
+                                        reader.readAsBinaryString(file);
+                                    };
+
+                                    entry.file(fileWin, fileSystemFail);
+                                },
+                                function (err) {
+                                    expect(
+                                        "Could not open test file '" + specContext.fileName + "': " + JSON.stringify(err)
+                                    ).not.toBeDefined();
+                                    done();
+                                }
+                            );
+                        };
+
+                        writeFile(
+                            specContext.root,
+                            specContext.fileName,
+                            'Temp data',
+                            function () {
+                                specContext.transfer.download(SERVER + '/304', specContext.localFilePath, httpWin, downloadFail);
+                            },
+                            fileOperationFail
+                        );
+                    },
+                    DOWNLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.36 should handle non-UTF8 encoded download response',
+                    function (done) {
+                        // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840
+                        if (!isIos) {
+                            pending();
+                        }
+
+                        var fileURL = SERVER + '/download_non_utf';
+                        var specContext = this;
+
+                        var fileOperationFail = function () {
+                            unexpectedCallbacks.fileOperationFail();
+                            done();
+                        };
+
+                        var fileSystemFail = function () {
+                            unexpectedCallbacks.fileSystemFail();
+                            done();
+                        };
+
+                        var httpFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        var fileWin = function (blob) {
+                            if (specContext.transfer.onprogress.calls.any()) {
+                                var lastProgressEvent = specContext.transfer.onprogress.calls.mostRecent().args[0];
+                                expect(lastProgressEvent.loaded).not.toBeGreaterThan(blob.size);
+                            } else {
+                                console.log('no progress events were emitted');
+                            }
+
+                            expect(blob.size).toBeGreaterThan(0);
+
+                            var reader = new FileReader();
+
+                            reader.onerror = fileOperationFail;
+                            reader.onloadend = function () {
+                                expect(reader.result.indexOf(LATIN1_SYMBOLS)).not.toBe(-1);
+                                done();
+                            };
+
+                            reader.readAsBinaryString(blob);
+                        };
+
+                        var downloadWin = function (entry) {
+                            verifyDownload(entry, specContext);
+
+                            // verify the FileEntry representing this file
+                            entry.file(fileWin, fileSystemFail);
+                        };
+
+                        specContext.transfer.download(fileURL, specContext.localFilePath, downloadWin, httpFail);
+                    },
+                    UPLOAD_TIMEOUT
+                );
             });
 
-            describe("upload", function() {
-                this.uploadParams   = null;
-                this.uploadOptions  = null;
-                this.fileName       = null;
-                this.fileContents   = null;
-                this.localFilePath  = null;
+            describe('upload', function () {
+                this.uploadParams = null;
+                this.uploadOptions = null;
+                this.fileName = null;
+                this.fileContents = null;
+                this.localFilePath = null;
 
                 // helpers
                 var verifyUpload = function (uploadResult, specContext) {
-
                     expect(uploadResult.bytesSent).toBeGreaterThan(0);
                     expect(uploadResult.responseCode).toBe(200);
 
@@ -956,30 +1029,30 @@
                     try {
                         obj = JSON.parse(uploadResult.response);
                         expect(obj.fields).toBeDefined();
-                        expect(obj.fields.value1).toBe("test");
-                        expect(obj.fields.value2).toBe("param");
+                        expect(obj.fields.value1).toBe('test');
+                        expect(obj.fields.value2).toBe('param');
                     } catch (e) {
-                        expect(obj).not.toBeNull("returned data from server should be valid json");
+                        expect(obj).not.toBeNull('returned data from server should be valid json');
                     }
 
                     expect(specContext.transfer.onprogress).toHaveBeenCalled();
                 };
 
-                beforeEach(function(done) {
+                beforeEach(function (done) {
                     var specContext = this;
 
-                    specContext.fileName               = "fileToUpload.txt";
-                    specContext.fileContents           = "upload test file";
+                    specContext.fileName = 'fileToUpload.txt';
+                    specContext.fileContents = 'upload test file';
 
-                    specContext.uploadParams           = {};
-                    specContext.uploadParams.value1    = "test";
-                    specContext.uploadParams.value2    = "param";
+                    specContext.uploadParams = {};
+                    specContext.uploadParams.value1 = 'test';
+                    specContext.uploadParams.value2 = 'param';
 
-                    specContext.uploadOptions          = new FileUploadOptions();
-                    specContext.uploadOptions.fileKey  = "file";
+                    specContext.uploadOptions = new FileUploadOptions();
+                    specContext.uploadOptions.fileKey = 'file';
                     specContext.uploadOptions.fileName = specContext.fileName;
-                    specContext.uploadOptions.mimeType = "text/plain";
-                    specContext.uploadOptions.params   = specContext.uploadParams;
+                    specContext.uploadOptions.mimeType = 'text/plain';
+                    specContext.uploadOptions.params = specContext.uploadParams;
 
                     var fileWin = function (entry) {
                         specContext.localFilePath = entry.toURL();
@@ -995,548 +1068,232 @@
                     deleteFile(this.root, this.fileName, done);
                 });
 
-                it("filetransfer.spec.18 should be able to upload a file", function (done) {
-                    var fileURL = SERVER + "/upload";
-                    var specContext = this;
+                it(
+                    'filetransfer.spec.18 should be able to upload a file',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+                        var specContext = this;
 
-                    var uploadWin = function (uploadResult) {
+                        var uploadWin = function (uploadResult) {
+                            verifyUpload(uploadResult, specContext);
 
-                        verifyUpload(uploadResult, specContext);
+                            if (cordova.platformId === 'ios') {
+                                expect(uploadResult.headers).toBeDefined('Expected headers to be defined.');
+                                expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.');
+                            }
 
-                        if (cordova.platformId === "ios") {
-                            expect(uploadResult.headers).toBeDefined("Expected headers to be defined.");
-                            expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined.");
-                        }
-
-                        done();
-                    };
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.19 should be able to upload a file with http basic auth", function (done) {
-                    var fileURL = SERVER_WITH_CREDENTIALS + "/upload_basic_auth";
-                    var specContext = this;
-
-                    var uploadWin = function (uploadResult) {
-                        verifyUpload(uploadResult, specContext);
-                        done();
-                    };
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.21 should be stopped by abort()", function (done) {
-                    var fileURL = SERVER + "/upload";
-                    var specContext = this;
-
-                    var uploadFail = function (e) {
-                        expect(e.code).toBe(FileTransferError.ABORT_ERR);
-
-                        // delay calling done() to wait for the bogus abort()
-                        setTimeout(done, GRACE_TIME_DELTA * 2);
-                    };
-
-                    var uploadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    var fileWin = function () {
-
-                        expect(specContext.transfer.abort).not.toThrow();
-
-                        // NOTE: removing uploadOptions cause Android to timeout
-                        specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
-                        setTimeout(function() {
-                            specContext.transfer.abort();
-                        }, ABORT_DELAY);
-
-                        setTimeout(function () {
-                            expect(specContext.transfer.abort).not.toThrow();
-                        }, GRACE_TIME_DELTA);
-                    };
-
-                    // windows store and ios are too fast, win is called before we have a chance to abort
-                    // so let's get them busy - while not providing an extra load to the slow Android emulators
-                    var arrayLength = ((isWindows && !isWindowsPhone) || isIos) ? 3000000 : isIot ? 150000 : 200000;
-                    writeFile(specContext.root, specContext.fileName, new Array(arrayLength).join("aborttest!"), fileWin, done);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.22 should get http status and body on failure", function (done) {
-                    var fileURL = SERVER + "/403";
-                    var retryCount = 0;
-                    var self = this;
-
-                    var uploadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    var uploadFail = function (error) {
-                        if (error.http_status === 503 && ++retryCount <= RETRY_COUNT) {
-                            // Heroku often gives this error, retry in 1 second
-                            console.log('retrying... ' + retryCount);
-                            setTimeout(function () {
-                                self.transfer.upload(self.localFilePath, fileURL, uploadWin, uploadFail, self.uploadOptions);
-                            }, RETRY_INTERVAL);
-                        } else {
-                            expect(error.http_status).toBe(403);
-                            expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list");
                             done();
-                        }
-                    };
+                        };
 
-                    self.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions);
-                }, UPLOAD_TIMEOUT * 11);
-
-                it("filetransfer.spec.24 should handle malformed urls", function (done) {
-                    var fileURL = getMalformedUrl();
-
-                    var uploadFail = function (error) {
-                        expect(error.code).toBe(FileTransferError.INVALID_URL_ERR);
-                        expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list");
-                        done();
-                    };
-
-                    var uploadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {});
-                });
-
-                it("filetransfer.spec.25 should handle unknown host", function (done) {
-                    var fileURL = UNKNOWN_HOST;
-
-                    var uploadFail = function (error) {
-                        expect(error.code).toBe(FileTransferError.CONNECTION_ERR);
-                        expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list");
-                        done();
-                    };
-
-                    var uploadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {});
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.25 should handle missing file", function (done) {
-                    var fileURL = SERVER + "/upload";
-
-                    var uploadFail = function (error) {
-                        expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
-                        expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list");
-                        done();
-                    };
-
-                    var uploadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    this.transfer.upload("does_not_exist.txt", fileURL, uploadWin, uploadFail);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.26 should handle bad file path", function (done) {
-                    var fileURL = SERVER + "/upload";
-
-                    var uploadFail = function (error) {
-                        expect(error.http_status).not.toBe(401, "Ensure " + fileURL + " is in the white list");
-                        done();
-                    };
-
-                    var uploadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    this.transfer.upload("c:\\54321", fileURL, uploadWin, uploadFail);
-                });
-
-                it("filetransfer.spec.27 should be able to set custom headers", function (done) {
-                    var fileURL = SERVER + '/upload_echo_headers';
-                    var retryCount = 0;
-                    var self = this;
-
-                    var uploadWin = function (uploadResult) {
-
-                        expect(uploadResult.bytesSent).toBeGreaterThan(0);
-                        expect(uploadResult.responseCode).toBe(200);
-                        expect(uploadResult.response).toBeDefined();
-
-                        var responseHtml = decodeURIComponent(uploadResult.response);
-
-                        expect(responseHtml).toMatch(/CustomHeader1[\s\S]*CustomValue1/i);
-                        expect(responseHtml).toMatch(/CustomHeader2[\s\S]*CustomValue2[\s\S]*CustomValue3/i, "Should allow array values");
-
-                        done();
-                    };
-
-                    var uploadFail = function() {
-                        if (++retryCount >= RETRY_COUNT) {
+                        var uploadFail = function () {
                             unexpectedCallbacks.httpFail();
                             done();
-                        } else {
-                            console.log('retrying... ' + retryCount);
-                            setTimeout(function () {
-                                // NOTE: removing uploadOptions will cause Android to timeout
-                                self.transfer.upload(self.localFilePath, fileURL, uploadWin, uploadFail, self.uploadOptions);
-                            }, RETRY_INTERVAL);
-                        }
-                    };
-
-                    this.uploadOptions.headers = {
-                        "CustomHeader1": "CustomValue1",
-                        "CustomHeader2": ["CustomValue2", "CustomValue3"],
-                    };
-
-                    // http://whatheaders.com does not support Transfer-Encoding: chunked
-                    this.uploadOptions.chunkedMode = false;
-
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions);
-                }, UPLOAD_TIMEOUT * 11);
-
-                it("filetransfer.spec.29 (compatibility) should be able to upload a file using local paths", function (done) {
-                    var fileURL = SERVER + "/upload";
-                    var specContext = this;
-
-                    var unsupported = function (response) {
-                        expectedCallbacks.unsupportedOperation(response);
-                        done();
-                    };
-
-                    var uploadWin = function (uploadResult) {
-                        verifyUpload(uploadResult, specContext);
-                        done();
-                    };
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    var internalFilePath;
-                    if (specContext.root.toInternalURL) {
-                        internalFilePath = specContext.root.toInternalURL() + specContext.fileName;
-                    } else {
-                        internalFilePath = specContext.localFilePath;
-                    }
-
-                    // This is an undocumented interface to File which exists only for testing
-                    // backwards compatibilty. By obtaining the raw filesystem path of the download
-                    // location, we can pass that to transfer.download() to make sure that previously-stored
-                    // paths are still valid.
-                    cordova.exec(function (localPath) {
-                        specContext.transfer.upload(localPath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
-                    }, unsupported, "File", "_getLocalFilesystemPath", [internalFilePath]);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.31 should be able to upload a file using PUT method", function (done) {
-                    var fileURL = SERVER + "/upload";
-                    var specContext = this;
-
-                    var uploadWin = function (uploadResult) {
-
-                        verifyUpload(uploadResult, specContext);
-
-                        if (cordova.platformId === "ios") {
-                            expect(uploadResult.headers).toBeDefined("Expected headers to be defined.");
-                            expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined.");
-                        }
-
-                        done();
-                    };
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    specContext.uploadOptions.httpMethod = "PUT";
-
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.32 should be able to upload a file (non-multipart)", function (done) {
-                    var fileURL = SERVER + "/upload";
-                    var specContext = this;
-
-                    var uploadWin = function (uploadResult) {
-
-                        expect(uploadResult.bytesSent).toBeGreaterThan(0);
-                        expect(uploadResult.responseCode).toBe(200);
-                        expect(uploadResult.response).toBeDefined();
-                        if (uploadResult.response) {
-                            expect(uploadResult.response).toEqual(specContext.fileContents + "\n");
-                        }
-                        expect(specContext.transfer.onprogress).toHaveBeenCalled();
-
-                        if (cordova.platformId === "ios") {
-                            expect(uploadResult.headers).toBeDefined("Expected headers to be defined.");
-                            expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined.");
-                        }
-
-                        done();
-                    };
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    // Content-Type header disables multipart
-                    specContext.uploadOptions.headers = {
-                        "Content-Type": "text/plain"
-                    };
-
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.34 should not delete a file on upload error", function (done) {
-
-                    var fileURL = SERVER + "/upload";
-                    var specContext = this;
-
-                    var uploadFail = function (e) {
-                        expect(e.code).toBe(FileTransferError.ABORT_ERR);
-
-                        // check that the file is there
-                        specContext.root.getFile(specContext.fileName, null, function(entry) {
-                            expect(entry).toBeDefined();
-                            // delay calling done() to wait for the bogus abort()
-                            setTimeout(done, GRACE_TIME_DELTA * 2);
-                        }, function(err) {
-                            expect(err).not.toBeDefined(err && err.code);
-                            done();
-                        });
-                    };
-
-                    var uploadWin = function() {
-                        unexpectedCallbacks.httpWin();
-                        done();
-                    };
-
-                    var fileWin = function () {
-
-                        expect(specContext.transfer.abort).not.toThrow();
-
-                        // abort at the first onprogress event
-                        specContext.transfer.onprogress = function (event) {
-                            if (event.loaded > 0) {
-                                specContext.transfer.abort();
-                            }
                         };
 
                         // NOTE: removing uploadOptions cause Android to timeout
                         specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
-                    };
+                    },
+                    UPLOAD_TIMEOUT
+                );
 
-                    writeFile(specContext.root, specContext.fileName, new Array(100000).join("aborttest!"), fileWin, done);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.37 should handle non-UTF8 encoded upload response", function (done) {
-                    // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840
-                    if (!isIos) {
-                        pending();
-                    }
-
-                    var fileURL = SERVER + '/upload_non_utf';
-                    var specContext = this;
-
-                    var uploadWin = function (uploadResult) {
-
-                        verifyUpload(uploadResult, specContext);
-
-                        var obj = null;
-                        try {
-                            obj = JSON.parse(uploadResult.response);
-                            expect(obj.latin1Symbols).toBe(LATIN1_SYMBOLS);
-                        } catch (e) {
-                            expect(obj).not.toBeNull("returned data from server should be valid json");
-                        }
-
-                        if (cordova.platformId === 'ios') {
-                            expect(uploadResult.headers).toBeDefined('Expected headers to be defined.');
-                            expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.');
-                        }
-
-                        done();
-                    };
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.38 should be able to upload a file using data: source uri", function (done) {
-                    var fileURL = SERVER + "/upload";
-                    var specContext = this;
-
-                    var uploadWin = function (uploadResult) {
-
-                        verifyUpload(uploadResult,specContext);
-
-                        var obj = null;
-                        try {
-                            obj = JSON.parse(uploadResult.response);
-                            expect(obj.files.file.size).toBe(DATA_URI_CONTENT_LENGTH);
-                        } catch (e) {
-                            expect(obj).not.toBeNull("returned data from server should be valid json");
-                        }
-
-                        if (cordova.platformId === "ios") {
-                            expect(uploadResult.headers).toBeDefined("Expected headers to be defined.");
-                            expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined.");
-                        }
-
-                        done();
-                    };
-
-                    var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT;
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    specContext.transfer.upload(dataUri, fileURL, uploadWin, function (err) {
-                        console.error('err: ' + JSON.stringify(err));
-                        expect(err).not.toBeDefined();
-                        done();
-                    }, specContext.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.39 should be able to upload a file using data: source uri (non-multipart)", function (done) {
-                    var fileURL = SERVER + "/upload";
-
-                    var uploadWin = function (uploadResult) {
-
-                        expect(uploadResult.responseCode).toBe(200);
-                        expect(uploadResult.bytesSent).toBeGreaterThan(0);
-
-                        if (cordova.platformId === "ios") {
-                            expect(uploadResult.headers).toBeDefined("Expected headers to be defined.");
-                            expect(uploadResult.headers["Content-Type"]).toBeDefined("Expected content-type header to be defined.");
-                        }
-
-                        done();
-                    };
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    // Content-Type header disables multipart
-                    this.uploadOptions.headers = {
-                        "Content-Type": "image/png"
-                    };
-
-                    var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT;
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    this.transfer.upload(dataUri, fileURL, uploadWin, uploadFail, this.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.40 should not fail to upload a file using data: source uri when the data is empty", function (done) {
-                    var fileURL = SERVER + "/upload";
-
-                    var dataUri = DATA_URI_PREFIX;
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                it("filetransfer.spec.41 should not fail to upload a file using data: source uri when the data is empty (non-multipart)", function (done) {
-                    if (isIos) {
-                        // iOS does not support uploads of an empty file with __chunkedMode=true__ and `multipartMode=false`:
-                        // request body will be empty in this case instead of 0\n\n.
-                        pending();
-                    }
-                    var fileURL = SERVER + "/upload";
-
-                    // Content-Type header disables multipart
-                    this.uploadOptions.headers = {
-                        "Content-Type": "image/png"
-                    };
-
-                    // turn off the onprogress handler
-                    this.transfer.onprogress = function () { };
-
-                    var dataUri = DATA_URI_PREFIX;
-
-                    var uploadFail = function() {
-                        unexpectedCallbacks.httpFail();
-                        done();
-                    };
-
-                    // NOTE: removing uploadOptions cause Android to timeout
-                    this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions);
-                }, UPLOAD_TIMEOUT);
-
-                describe("chunkedMode handling", function() {
-                    var testChunkedModeWin = function (uploadResult, specContext) {
-                        var multipartModeEnabled = !(specContext.uploadOptions.headers && specContext.uploadOptions.headers["Content-Type"]);
-                        var obj = null;
-                        try {
-                            obj = JSON.parse(uploadResult.response);
-
-                            if (specContext.uploadOptions.chunkedMode) {
-                                if (!isIos) {
-                                    expect(obj["content-length"]).not.toBeDefined("Expected Content-Length not to be defined");
-                                }
-                                expect(obj["transfer-encoding"].toLowerCase()).toEqual("chunked");
-                            } else {
-                                expect(obj["content-length"]).toBeDefined("Expected Content-Length to be defined");
-                                expect(obj["transfer-encoding"].toLowerCase()).not.toEqual("chunked");
-                            }
-
-                            if (multipartModeEnabled) {
-                                expect(obj["content-type"].indexOf("multipart/form-data")).not.toBe(-1);
-                            } else {
-                                expect(obj["content-type"].indexOf("multipart/form-data")).toBe(-1);
-                            }
-                        } catch (e) {
-                            expect(obj).not.toBeNull("returned data from server should be valid json");
-                        }
-                    };
-
-                    var testChunkedModeBase = function(chunkedMode, multipart, done) {
-                        var retryCount = 0;
-                        var fileURL = SERVER + "/upload_echo_headers";
+                it(
+                    'filetransfer.spec.19 should be able to upload a file with http basic auth',
+                    function (done) {
+                        var fileURL = SERVER_WITH_CREDENTIALS + '/upload_basic_auth';
                         var specContext = this;
 
-                        specContext.uploadOptions.chunkedMode = chunkedMode;
-                        if (!multipart) {
-                            // Content-Type header disables multipart
-                            specContext.uploadOptions.headers = {
-                                "Content-Type": "text/plain"
-                            };
-                        }
+                        var uploadWin = function (uploadResult) {
+                            verifyUpload(uploadResult, specContext);
+                            done();
+                        };
 
-                        var uploadFail = function() {
+                        var uploadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.21 should be stopped by abort()',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+                        var specContext = this;
+
+                        var uploadFail = function (e) {
+                            expect(e.code).toBe(FileTransferError.ABORT_ERR);
+
+                            // delay calling done() to wait for the bogus abort()
+                            setTimeout(done, GRACE_TIME_DELTA * 2);
+                        };
+
+                        var uploadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        var fileWin = function () {
+                            expect(specContext.transfer.abort).not.toThrow();
+
+                            // NOTE: removing uploadOptions cause Android to timeout
+                            specContext.transfer.upload(
+                                specContext.localFilePath,
+                                fileURL,
+                                uploadWin,
+                                uploadFail,
+                                specContext.uploadOptions
+                            );
+                            setTimeout(function () {
+                                specContext.transfer.abort();
+                            }, ABORT_DELAY);
+
+                            setTimeout(function () {
+                                expect(specContext.transfer.abort).not.toThrow();
+                            }, GRACE_TIME_DELTA);
+                        };
+
+                        // windows store and ios are too fast, win is called before we have a chance to abort
+                        // so let's get them busy - while not providing an extra load to the slow Android emulators
+                        var arrayLength = (isWindows && !isWindowsPhone) || isIos ? 3000000 : isIot ? 150000 : 200000;
+                        writeFile(specContext.root, specContext.fileName, new Array(arrayLength).join('aborttest!'), fileWin, done);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.22 should get http status and body on failure',
+                    function (done) {
+                        var fileURL = SERVER + '/403';
+                        var retryCount = 0;
+                        var self = this;
+
+                        var uploadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        var uploadFail = function (error) {
+                            if (error.http_status === 503 && ++retryCount <= RETRY_COUNT) {
+                                // Heroku often gives this error, retry in 1 second
+                                console.log('retrying... ' + retryCount);
+                                setTimeout(function () {
+                                    self.transfer.upload(self.localFilePath, fileURL, uploadWin, uploadFail, self.uploadOptions);
+                                }, RETRY_INTERVAL);
+                            } else {
+                                expect(error.http_status).toBe(403);
+                                expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list');
+                                done();
+                            }
+                        };
+
+                        self.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT * 11
+                );
+
+                it('filetransfer.spec.24 should handle malformed urls', function (done) {
+                    var fileURL = getMalformedUrl();
+
+                    var uploadFail = function (error) {
+                        expect(error.code).toBe(FileTransferError.INVALID_URL_ERR);
+                        expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list');
+                        done();
+                    };
+
+                    var uploadWin = function () {
+                        unexpectedCallbacks.httpWin();
+                        done();
+                    };
+
+                    this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {});
+                });
+
+                it(
+                    'filetransfer.spec.25 should handle unknown host',
+                    function (done) {
+                        var fileURL = UNKNOWN_HOST;
+
+                        var uploadFail = function (error) {
+                            expect(error.code).toBe(FileTransferError.CONNECTION_ERR);
+                            expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list');
+                            done();
+                        };
+
+                        var uploadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, {});
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.25 should handle missing file',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+
+                        var uploadFail = function (error) {
+                            expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+                            expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list');
+                            done();
+                        };
+
+                        var uploadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        this.transfer.upload('does_not_exist.txt', fileURL, uploadWin, uploadFail);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it('filetransfer.spec.26 should handle bad file path', function (done) {
+                    var fileURL = SERVER + '/upload';
+
+                    var uploadFail = function (error) {
+                        expect(error.http_status).not.toBe(401, 'Ensure ' + fileURL + ' is in the white list');
+                        done();
+                    };
+
+                    var uploadWin = function () {
+                        unexpectedCallbacks.httpWin();
+                        done();
+                    };
+
+                    this.transfer.upload('c:\\54321', fileURL, uploadWin, uploadFail);
+                });
+
+                it(
+                    'filetransfer.spec.27 should be able to set custom headers',
+                    function (done) {
+                        var fileURL = SERVER + '/upload_echo_headers';
+                        var retryCount = 0;
+                        var self = this;
+
+                        var uploadWin = function (uploadResult) {
+                            expect(uploadResult.bytesSent).toBeGreaterThan(0);
+                            expect(uploadResult.responseCode).toBe(200);
+                            expect(uploadResult.response).toBeDefined();
+
+                            var responseHtml = decodeURIComponent(uploadResult.response);
+
+                            expect(responseHtml).toMatch(/CustomHeader1[\s\S]*CustomValue1/i);
+                            expect(responseHtml).toMatch(
+                                /CustomHeader2[\s\S]*CustomValue2[\s\S]*CustomValue3/i,
+                                'Should allow array values'
+                            );
+
+                            done();
+                        };
+
+                        var uploadFail = function () {
                             if (++retryCount >= RETRY_COUNT) {
                                 unexpectedCallbacks.httpFail();
                                 done();
@@ -1544,10 +1301,420 @@
                                 console.log('retrying... ' + retryCount);
                                 setTimeout(function () {
                                     // NOTE: removing uploadOptions will cause Android to timeout
-                                    specContext.transfer.upload(specContext.localFilePath, fileURL, function (uploadResult) {
-                                        testChunkedModeWin(uploadResult, specContext);
-                                        done();
-                                    }, uploadFail, specContext.uploadOptions);
+                                    self.transfer.upload(self.localFilePath, fileURL, uploadWin, uploadFail, self.uploadOptions);
+                                }, RETRY_INTERVAL);
+                            }
+                        };
+
+                        this.uploadOptions.headers = {
+                            CustomHeader1: 'CustomValue1',
+                            CustomHeader2: ['CustomValue2', 'CustomValue3']
+                        };
+
+                        // http://whatheaders.com does not support Transfer-Encoding: chunked
+                        this.uploadOptions.chunkedMode = false;
+
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT * 11
+                );
+
+                it(
+                    'filetransfer.spec.29 (compatibility) should be able to upload a file using local paths',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+                        var specContext = this;
+
+                        var unsupported = function (response) {
+                            expectedCallbacks.unsupportedOperation(response);
+                            done();
+                        };
+
+                        var uploadWin = function (uploadResult) {
+                            verifyUpload(uploadResult, specContext);
+                            done();
+                        };
+
+                        var uploadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        var internalFilePath;
+                        if (specContext.root.toInternalURL) {
+                            internalFilePath = specContext.root.toInternalURL() + specContext.fileName;
+                        } else {
+                            internalFilePath = specContext.localFilePath;
+                        }
+
+                        // This is an undocumented interface to File which exists only for testing
+                        // backwards compatibilty. By obtaining the raw filesystem path of the download
+                        // location, we can pass that to transfer.download() to make sure that previously-stored
+                        // paths are still valid.
+                        cordova.exec(
+                            function (localPath) {
+                                specContext.transfer.upload(localPath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
+                            },
+                            unsupported,
+                            'File',
+                            '_getLocalFilesystemPath',
+                            [internalFilePath]
+                        );
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.31 should be able to upload a file using PUT method',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+                        var specContext = this;
+
+                        var uploadWin = function (uploadResult) {
+                            verifyUpload(uploadResult, specContext);
+
+                            if (cordova.platformId === 'ios') {
+                                expect(uploadResult.headers).toBeDefined('Expected headers to be defined.');
+                                expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.');
+                            }
+
+                            done();
+                        };
+
+                        var uploadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        specContext.uploadOptions.httpMethod = 'PUT';
+
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.32 should be able to upload a file (non-multipart)',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+                        var specContext = this;
+
+                        var uploadWin = function (uploadResult) {
+                            expect(uploadResult.bytesSent).toBeGreaterThan(0);
+                            expect(uploadResult.responseCode).toBe(200);
+                            expect(uploadResult.response).toBeDefined();
+                            if (uploadResult.response) {
+                                expect(uploadResult.response).toEqual(specContext.fileContents + '\n');
+                            }
+                            expect(specContext.transfer.onprogress).toHaveBeenCalled();
+
+                            if (cordova.platformId === 'ios') {
+                                expect(uploadResult.headers).toBeDefined('Expected headers to be defined.');
+                                expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.');
+                            }
+
+                            done();
+                        };
+
+                        var uploadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        // Content-Type header disables multipart
+                        specContext.uploadOptions.headers = {
+                            'Content-Type': 'text/plain'
+                        };
+
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.34 should not delete a file on upload error',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+                        var specContext = this;
+
+                        var uploadFail = function (e) {
+                            expect(e.code).toBe(FileTransferError.ABORT_ERR);
+
+                            // check that the file is there
+                            specContext.root.getFile(
+                                specContext.fileName,
+                                null,
+                                function (entry) {
+                                    expect(entry).toBeDefined();
+                                    // delay calling done() to wait for the bogus abort()
+                                    setTimeout(done, GRACE_TIME_DELTA * 2);
+                                },
+                                function (err) {
+                                    expect(err).not.toBeDefined(err && err.code);
+                                    done();
+                                }
+                            );
+                        };
+
+                        var uploadWin = function () {
+                            unexpectedCallbacks.httpWin();
+                            done();
+                        };
+
+                        var fileWin = function () {
+                            expect(specContext.transfer.abort).not.toThrow();
+
+                            // abort at the first onprogress event
+                            specContext.transfer.onprogress = function (event) {
+                                if (event.loaded > 0) {
+                                    specContext.transfer.abort();
+                                }
+                            };
+
+                            // NOTE: removing uploadOptions cause Android to timeout
+                            specContext.transfer.upload(
+                                specContext.localFilePath,
+                                fileURL,
+                                uploadWin,
+                                uploadFail,
+                                specContext.uploadOptions
+                            );
+                        };
+
+                        writeFile(specContext.root, specContext.fileName, new Array(100000).join('aborttest!'), fileWin, done);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.37 should handle non-UTF8 encoded upload response',
+                    function (done) {
+                        // Only iOS is supported: https://issues.apache.org/jira/browse/CB-9840
+                        if (!isIos) {
+                            pending();
+                        }
+
+                        var fileURL = SERVER + '/upload_non_utf';
+                        var specContext = this;
+
+                        var uploadWin = function (uploadResult) {
+                            verifyUpload(uploadResult, specContext);
+
+                            var obj = null;
+                            try {
+                                obj = JSON.parse(uploadResult.response);
+                                expect(obj.latin1Symbols).toBe(LATIN1_SYMBOLS);
+                            } catch (e) {
+                                expect(obj).not.toBeNull('returned data from server should be valid json');
+                            }
+
+                            if (cordova.platformId === 'ios') {
+                                expect(uploadResult.headers).toBeDefined('Expected headers to be defined.');
+                                expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.');
+                            }
+
+                            done();
+                        };
+
+                        var uploadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        specContext.transfer.upload(specContext.localFilePath, fileURL, uploadWin, uploadFail, specContext.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.38 should be able to upload a file using data: source uri',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+                        var specContext = this;
+
+                        var uploadWin = function (uploadResult) {
+                            verifyUpload(uploadResult, specContext);
+
+                            var obj = null;
+                            try {
+                                obj = JSON.parse(uploadResult.response);
+                                expect(obj.files.file.size).toBe(DATA_URI_CONTENT_LENGTH);
+                            } catch (e) {
+                                expect(obj).not.toBeNull('returned data from server should be valid json');
+                            }
+
+                            if (cordova.platformId === 'ios') {
+                                expect(uploadResult.headers).toBeDefined('Expected headers to be defined.');
+                                expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.');
+                            }
+
+                            done();
+                        };
+
+                        var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT;
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        specContext.transfer.upload(
+                            dataUri,
+                            fileURL,
+                            uploadWin,
+                            function (err) {
+                                console.error('err: ' + JSON.stringify(err));
+                                expect(err).not.toBeDefined();
+                                done();
+                            },
+                            specContext.uploadOptions
+                        );
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.39 should be able to upload a file using data: source uri (non-multipart)',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+
+                        var uploadWin = function (uploadResult) {
+                            expect(uploadResult.responseCode).toBe(200);
+                            expect(uploadResult.bytesSent).toBeGreaterThan(0);
+
+                            if (cordova.platformId === 'ios') {
+                                expect(uploadResult.headers).toBeDefined('Expected headers to be defined.');
+                                expect(uploadResult.headers['Content-Type']).toBeDefined('Expected content-type header to be defined.');
+                            }
+
+                            done();
+                        };
+
+                        var uploadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        // Content-Type header disables multipart
+                        this.uploadOptions.headers = {
+                            'Content-Type': 'image/png'
+                        };
+
+                        var dataUri = DATA_URI_PREFIX + DATA_URI_CONTENT;
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        this.transfer.upload(dataUri, fileURL, uploadWin, uploadFail, this.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.40 should not fail to upload a file using data: source uri when the data is empty',
+                    function (done) {
+                        var fileURL = SERVER + '/upload';
+
+                        var dataUri = DATA_URI_PREFIX;
+
+                        var uploadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                it(
+                    'filetransfer.spec.41 should not fail to upload a file using data: source uri when the data is empty (non-multipart)',
+                    function (done) {
+                        if (isIos) {
+                            // iOS does not support uploads of an empty file with __chunkedMode=true__ and `multipartMode=false`:
+                            // request body will be empty in this case instead of 0\n\n.
+                            pending();
+                        }
+                        var fileURL = SERVER + '/upload';
+
+                        // Content-Type header disables multipart
+                        this.uploadOptions.headers = {
+                            'Content-Type': 'image/png'
+                        };
+
+                        // turn off the onprogress handler
+                        this.transfer.onprogress = function () {};
+
+                        var dataUri = DATA_URI_PREFIX;
+
+                        var uploadFail = function () {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions);
+                    },
+                    UPLOAD_TIMEOUT
+                );
+
+                describe('chunkedMode handling', function () {
+                    var testChunkedModeWin = function (uploadResult, specContext) {
+                        var multipartModeEnabled = !(
+                            specContext.uploadOptions.headers && specContext.uploadOptions.headers['Content-Type']
+                        );
+                        var obj = null;
+                        try {
+                            obj = JSON.parse(uploadResult.response);
+
+                            if (specContext.uploadOptions.chunkedMode) {
+                                if (!isIos) {
+                                    expect(obj['content-length']).not.toBeDefined('Expected Content-Length not to be defined');
+                                }
+                                expect(obj['transfer-encoding'].toLowerCase()).toEqual('chunked');
+                            } else {
+                                expect(obj['content-length']).toBeDefined('Expected Content-Length to be defined');
+                                expect(obj['transfer-encoding'].toLowerCase()).not.toEqual('chunked');
+                            }
+
+                            if (multipartModeEnabled) {
+                                expect(obj['content-type'].indexOf('multipart/form-data')).not.toBe(-1);
+                            } else {
+                                expect(obj['content-type'].indexOf('multipart/form-data')).toBe(-1);
+                            }
+                        } catch (e) {
+                            expect(obj).not.toBeNull('returned data from server should be valid json');
+                        }
+                    };
+
+                    var testChunkedModeBase = function (chunkedMode, multipart, done) {
+                        var retryCount = 0;
+                        var fileURL = SERVER + '/upload_echo_headers';
+                        var specContext = this;
+
+                        specContext.uploadOptions.chunkedMode = chunkedMode;
+                        if (!multipart) {
+                            // Content-Type header disables multipart
+                            specContext.uploadOptions.headers = {
+                                'Content-Type': 'text/plain'
+                            };
+                        }
+
+                        var uploadFail = function () {
+                            if (++retryCount >= RETRY_COUNT) {
+                                unexpectedCallbacks.httpFail();
+                                done();
+                            } else {
+                                console.log('retrying... ' + retryCount);
+                                setTimeout(function () {
+                                    // NOTE: removing uploadOptions will cause Android to timeout
+                                    specContext.transfer.upload(
+                                        specContext.localFilePath,
+                                        fileURL,
+                                        function (uploadResult) {
+                                            testChunkedModeWin(uploadResult, specContext);
+                                            done();
+                                        },
+                                        uploadFail,
+                                        specContext.uploadOptions
+                                    );
                                 }, RETRY_INTERVAL);
                             }
                         };
@@ -1556,37 +1723,55 @@
                         this.transfer.onprogress = function () {};
 
                         // NOTE: removing uploadOptions cause Android to timeout
-                        specContext.transfer.upload(specContext.localFilePath, fileURL, function (uploadResult) {
-                            testChunkedModeWin(uploadResult, specContext);
-                            done();
-                        }, uploadFail, specContext.uploadOptions);
+                        specContext.transfer.upload(
+                            specContext.localFilePath,
+                            fileURL,
+                            function (uploadResult) {
+                                testChunkedModeWin(uploadResult, specContext);
+                                done();
+                            },
+                            uploadFail,
+                            specContext.uploadOptions
+                        );
                     };
 
-                    it("filetransfer.spec.42 chunkedMode=false, multipart=false", function (done) {
+                    it(
+                        'filetransfer.spec.42 chunkedMode=false, multipart=false',
+                        function (done) {
+                            testChunkedModeBase.call(this, false, false, done);
+                        },
+                        UPLOAD_TIMEOUT * 11
+                    );
 
-                        testChunkedModeBase.call(this, false, false, done);
-                    }, UPLOAD_TIMEOUT * 11);
+                    it(
+                        'filetransfer.spec.43 chunkedMode=true, multipart=false',
+                        function (done) {
+                            if (isWindows) {
+                                pending();
+                            }
+                            testChunkedModeBase.call(this, true, false, done);
+                        },
+                        UPLOAD_TIMEOUT * 11
+                    );
 
-                    it("filetransfer.spec.43 chunkedMode=true, multipart=false", function (done) {
+                    it(
+                        'filetransfer.spec.44 chunkedMode=false, multipart=true',
+                        function (done) {
+                            testChunkedModeBase.call(this, false, true, done);
+                        },
+                        UPLOAD_TIMEOUT * 11
+                    );
 
-                        if (isWindows) {
-                            pending();
-                        }
-                        testChunkedModeBase.call(this, true, false, done);
-                    }, UPLOAD_TIMEOUT * 11);
-
-                    it("filetransfer.spec.44 chunkedMode=false, multipart=true", function (done) {
-
-                        testChunkedModeBase.call(this, false, true, done);
-                    }, UPLOAD_TIMEOUT * 11);
-
-                    it("filetransfer.spec.45 chunkedMode=true, multipart=true", function (done) {
-
-                        if (isWindows) {
-                            pending();
-                        }
-                        testChunkedModeBase.call(this, true, true, done);
-                    }, UPLOAD_TIMEOUT * 11);
+                    it(
+                        'filetransfer.spec.45 chunkedMode=true, multipart=true',
+                        function (done) {
+                            if (isWindows) {
+                                pending();
+                            }
+                            testChunkedModeBase.call(this, true, true, done);
+                        },
+                        UPLOAD_TIMEOUT * 11
+                    );
                 });
             });
         });
@@ -1598,29 +1783,28 @@
 /******************************************************************************/
 
 exports.defineManualTests = function (contentEl, createActionButton) {
+    'use strict';
 
-    "use strict";
+    var imageURL = 'http://apache.org/images/feather-small.gif';
+    var videoURL = 'http://techslides.com/demos/sample-videos/small.mp4';
 
-    var imageURL = "http://apache.org/images/feather-small.gif";
-    var videoURL = "http://techslides.com/demos/sample-videos/small.mp4";
-
-    function clearResults() {
-        var results = document.getElementById("info");
-        results.innerHTML = "";
+    function clearResults () {
+        var results = document.getElementById('info');
+        results.innerHTML = '';
     }
 
-    function downloadImg(source, urlFn, element, directory) {
-        var filename = source.substring(source.lastIndexOf("/") + 1);
-        filename = (directory || "") + filename;
+    function downloadImg (source, urlFn, element, directory) {
+        var filename = source.substring(source.lastIndexOf('/') + 1);
+        filename = (directory || '') + filename;
 
-        function download(fileSystem) {
+        function download (fileSystem) {
             var ft = new FileTransfer();
-            console.log("Starting download");
+            console.log('Starting download');
 
-            var progress = document.getElementById("loadingStatus");
+            var progress = document.getElementById('loadingStatus');
             progress.value = 0;
 
-            ft.onprogress = function(progressEvent) {
+            ft.onprogress = function (progressEvent) {
                 if (progressEvent.lengthComputable) {
                     var currPercents = parseInt(100 * (progressEvent.loaded / progressEvent.total), 10);
                     if (currPercents > progress.value) {
@@ -1631,77 +1815,162 @@
                 }
             };
 
-            ft.download(source, fileSystem.root.toURL() + filename, function (entry) {
-                console.log("Download complete");
-                element.src = urlFn(entry);
-                console.log("Src URL is " + element.src);
-                console.log("Inserting element");
-                document.getElementById("info").appendChild(element);
-            }, function (e) { console.log("ERROR: ft.download " + e.code); });
+            ft.download(
+                source,
+                fileSystem.root.toURL() + filename,
+                function (entry) {
+                    console.log('Download complete');
+                    element.src = urlFn(entry);
+                    console.log('Src URL is ' + element.src);
+                    console.log('Inserting element');
+                    document.getElementById('info').appendChild(element);
+                },
+                function (e) {
+                    console.log('ERROR: ft.download ' + e.code);
+                }
+            );
         }
-        console.log("Requesting filesystem");
+        console.log('Requesting filesystem');
         clearResults();
-        window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function (fileSystem) {
-            console.log("Checking for existing file");
-            if (typeof directory !== "undefined") {
-                console.log("Checking for existing directory.");
-                fileSystem.root.getDirectory(directory, {}, function (dirEntry) {
-                    dirEntry.removeRecursively(function () {
-                        download(fileSystem);
-                    }, function () { console.log("ERROR: dirEntry.removeRecursively"); });
-                }, function () {
-                    download(fileSystem);
-                });
-            } else {
-                fileSystem.root.getFile(filename, { create: false }, function (entry) {
-                    console.log("Removing existing file");
-                    entry.remove(function () {
-                        download(fileSystem);
-                    }, function () { console.log("ERROR: entry.remove"); });
-                }, function () {
-                    download(fileSystem);
-                });
+        window.requestFileSystem(
+            LocalFileSystem.TEMPORARY,
+            0,
+            function (fileSystem) {
+                console.log('Checking for existing file');
+                if (typeof directory !== 'undefined') {
+                    console.log('Checking for existing directory.');
+                    fileSystem.root.getDirectory(
+                        directory,
+                        {},
+                        function (dirEntry) {
+                            dirEntry.removeRecursively(
+                                function () {
+                                    download(fileSystem);
+                                },
+                                function () {
+                                    console.log('ERROR: dirEntry.removeRecursively');
+                                }
+                            );
+                        },
+                        function () {
+                            download(fileSystem);
+                        }
+                    );
+                } else {
+                    fileSystem.root.getFile(
+                        filename,
+                        { create: false },
+                        function (entry) {
+                            console.log('Removing existing file');
+                            entry.remove(
+                                function () {
+                                    download(fileSystem);
+                                },
+                                function () {
+                                    console.log('ERROR: entry.remove');
+                                }
+                            );
+                        },
+                        function () {
+                            download(fileSystem);
+                        }
+                    );
+                }
+            },
+            function () {
+                console.log('ERROR: requestFileSystem');
             }
-        }, function () { console.log("ERROR: requestFileSystem"); });
+        );
     }
 
     /******************************************************************************/
 
-    var progress_tag = "<progress id=\"loadingStatus\" value=\"0\" max=\"100\" style=\"width: 100%;\"></progress>";
-    var file_transfer_tests = "<h2>Image File Transfer Tests</h2>" +
-        "<h3>The following tests should display an image of the Apache feather in the status box</h3>" +
-        "<div id=\"cdv_image\"></div>" +
-        "<div id=\"native_image\"></div>" +
-        "<div id=\"non-existent_dir\"></div>" +
-        "<h2>Video File Transfer Tests</h2>" +
-        "<h3>The following tests should display a video in the status box. The video should play when play is pressed</h3>" +
-        "<div id=\"cdv_video\"></div>" +
-        "<div id=\"native_video\"></div>";
+    var progress_tag = '<progress id="loadingStatus" value="0" max="100" style="width: 100%;"></progress>';
+    var file_transfer_tests =
+        '<h2>Image File Transfer Tests</h2>' +
+        '<h3>The following tests should display an image of the Apache feather in the status box</h3>' +
+        '<div id="cdv_image"></div>' +
+        '<div id="native_image"></div>' +
+        '<div id="non-existent_dir"></div>' +
+        '<h2>Video File Transfer Tests</h2>' +
+        '<h3>The following tests should display a video in the status box. The video should play when play is pressed</h3>' +
+        '<div id="cdv_video"></div>' +
+        '<div id="native_video"></div>';
 
-    contentEl.innerHTML = "<div id=\"info\"></div>" + "<br>" + progress_tag +
-        file_transfer_tests;
+    contentEl.innerHTML = '<div id="info"></div>' + '<br>' + progress_tag + file_transfer_tests;
 
-    createActionButton("Download and display img (cdvfile)", function () {
-        downloadImg(imageURL, function (entry) { return entry.toInternalURL(); }, new Image());
-    }, "cdv_image");
+    createActionButton(
+        'Download and display img (cdvfile)',
+        function () {
+            downloadImg(
+                imageURL,
+                function (entry) {
+                    return entry.toInternalURL();
+                },
+                new Image()
+            );
+        },
+        'cdv_image'
+    );
 
-    createActionButton("Download and display img (native)", function () {
-        downloadImg(imageURL, function (entry) { return entry.toURL(); }, new Image());
-    }, "native_image");
+    createActionButton(
+        'Download and display img (native)',
+        function () {
+            downloadImg(
+                imageURL,
+                function (entry) {
+                    return entry.toURL();
+                },
+                new Image()
+            );
+        },
+        'native_image'
+    );
 
-    createActionButton("Download to a non-existent dir (should work)", function () {
-        downloadImg(imageURL, function (entry) { return entry.toURL(); }, new Image(), "/nonExistentDirTest/");
-    }, "non-existent_dir");
+    createActionButton(
+        'Download to a non-existent dir (should work)',
+        function () {
+            downloadImg(
+                imageURL,
+                function (entry) {
+                    return entry.toURL();
+                },
+                new Image(),
+                '/nonExistentDirTest/'
+            );
+        },
+        'non-existent_dir'
+    );
 
-    createActionButton("Download and play video (cdvfile)", function () {
-        var videoElement = document.createElement("video");
-        videoElement.controls = "controls";
-        downloadImg(videoURL, function (entry) { return entry.toInternalURL(); }, videoElement);
-    }, "cdv_video");
+    createActionButton(
+        'Download and play video (cdvfile)',
+        function () {
+            var videoElement = document.createElement('video');
+            videoElement.controls = 'controls';
+            downloadImg(
+                videoURL,
+                function (entry) {
+                    return entry.toInternalURL();
+                },
+                videoElement
+            );
+        },
+        'cdv_video'
+    );
 
-    createActionButton("Download and play video (native)", function () {
-        var videoElement = document.createElement("video");
-        videoElement.controls = "controls";
-        downloadImg(videoURL, function (entry) { return entry.toURL(); }, videoElement);
-    }, "native_video");
+    createActionButton(
+        'Download and play video (native)',
+        function () {
+            var videoElement = document.createElement('video');
+            videoElement.controls = 'controls';
+            downloadImg(
+                videoURL,
+                function (entry) {
+                    return entry.toURL();
+                },
+                videoElement
+            );
+        },
+        'native_video'
+    );
 };
diff --git a/www/FileTransfer.js b/www/FileTransfer.js
index 80cf91c..9938971 100644
--- a/www/FileTransfer.js
+++ b/www/FileTransfer.js
@@ -17,16 +17,16 @@
  * specific language governing permissions and limitations
  * under the License.
  *
-*/
+ */
 
 /* global cordova, FileSystem */
 
-var argscheck = require('cordova/argscheck'),
-    exec = require('cordova/exec'),
-    FileTransferError = require('./FileTransferError'),
-    ProgressEvent = require('cordova-plugin-file.ProgressEvent');
+var argscheck = require('cordova/argscheck');
+var exec = require('cordova/exec');
+var FileTransferError = require('./FileTransferError');
+var ProgressEvent = require('cordova-plugin-file.ProgressEvent');
 
-function newProgressEvent(result) {
+function newProgressEvent (result) {
     var pe = new ProgressEvent();
     pe.lengthComputable = result.lengthComputable;
     pe.loaded = result.loaded;
@@ -34,16 +34,15 @@
     return pe;
 }
 
-function getUrlCredentials(urlString) {
-    var credentialsPattern = /^https?\:\/\/(?:(?:(([^:@\/]*)(?::([^@\/]*))?)?@)?([^:\/?#]*)(?::(\d*))?).*$/,
-        credentials = credentialsPattern.exec(urlString);
+function getUrlCredentials (urlString) {
+    var credentialsPattern = /^https?:\/\/(?:(?:(([^:@/]*)(?::([^@/]*))?)?@)?([^:/?#]*)(?::(\d*))?).*$/;
+    var credentials = credentialsPattern.exec(urlString);
 
     return credentials && credentials[1];
 }
 
-function getBasicAuthHeader(urlString) {
-    var header =  null;
-
+function getBasicAuthHeader (urlString) {
+    var header = null;
 
     // This is changed due to MS Windows doesn't support credentials in http uris
     // so we detect them by regexp and strip off from result url
@@ -52,12 +51,12 @@
     if (window.btoa) {
         var credentials = getUrlCredentials(urlString);
         if (credentials) {
-            var authHeader = "Authorization";
-            var authHeaderValue = "Basic " + window.btoa(credentials);
+            var authHeader = 'Authorization';
+            var authHeaderValue = 'Basic ' + window.btoa(credentials);
 
             header = {
-                name : authHeader,
-                value : authHeaderValue
+                name: authHeader,
+                value: authHeaderValue
             };
         }
     }
@@ -65,10 +64,10 @@
     return header;
 }
 
-function convertHeadersToArray(headers) {
+function convertHeadersToArray (headers) {
     var result = [];
     for (var header in headers) {
-        if (headers.hasOwnProperty(header)) {
+        if (Object.prototype.hasOwnProperty.call(headers, header)) {
             var headerValue = headers[header];
             result.push({
                 name: header,
@@ -85,22 +84,22 @@
  * FileTransfer uploads a file to a remote server.
  * @constructor
  */
-var FileTransfer = function() {
+var FileTransfer = function () {
     this._id = ++idCounter;
     this.onprogress = null; // optional callback
 };
 
 /**
-* Given an absolute file path, uploads a file on the device to a remote server
-* using a multipart HTTP request.
-* @param filePath {String}           Full path of the file on the device
-* @param server {String}             URL of the server to receive the file
-* @param successCallback (Function}  Callback to be invoked when upload has completed
-* @param errorCallback {Function}    Callback to be invoked upon error
-* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
-* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
-*/
-FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
+ * Given an absolute file path, uploads a file on the device to a remote server
+ * using a multipart HTTP request.
+ * @param filePath {String}           Full path of the file on the device
+ * @param server {String}             URL of the server to receive the file
+ * @param successCallback (Function}  Callback to be invoked when upload has completed
+ * @param errorCallback {Function}    Callback to be invoked upon error
+ * @param options {FileUploadOptions} Optional parameters such as file name and mimetype
+ * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+ */
+FileTransfer.prototype.upload = function (filePath, server, successCallback, errorCallback, options, trustAllHosts) {
     argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
     // check for options
     var fileKey = null;
@@ -124,36 +123,37 @@
         fileName = options.fileName;
         mimeType = options.mimeType;
         headers = options.headers;
-        httpMethod = options.httpMethod || "POST";
-        if (httpMethod.toUpperCase() == "PUT"){
-            httpMethod = "PUT";
+        httpMethod = options.httpMethod || 'POST';
+        if (httpMethod.toUpperCase() === 'PUT') {
+            httpMethod = 'PUT';
         } else {
-            httpMethod = "POST";
+            httpMethod = 'POST';
         }
-        if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
+        if (options.chunkedMode !== null || typeof options.chunkedMode !== 'undefined') {
             chunkedMode = options.chunkedMode;
         }
         if (options.params) {
             params = options.params;
-        }
-        else {
+        } else {
             params = {};
         }
     }
 
-    if (cordova.platformId === "windowsphone") {
+    if (cordova.platformId === 'windowsphone') {
         headers = headers && convertHeadersToArray(headers);
         params = params && convertHeadersToArray(params);
     }
 
-    var fail = errorCallback && function(e) {
-        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception);
-        errorCallback(error);
-    };
+    var fail =
+        errorCallback &&
+        function (e) {
+            var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception);
+            errorCallback(error);
+        };
 
     var self = this;
-    var win = function(result) {
-        if (typeof result.lengthComputable != "undefined") {
+    var win = function (result) {
+        if (typeof result.lengthComputable !== 'undefined') {
             if (self.onprogress) {
                 self.onprogress(newProgressEvent(result));
             }
@@ -163,7 +163,19 @@
             }
         }
     };
-    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
+    exec(win, fail, 'FileTransfer', 'upload', [
+        filePath,
+        server,
+        fileKey,
+        fileName,
+        mimeType,
+        params,
+        trustAllHosts,
+        chunkedMode,
+        headers,
+        this._id,
+        httpMethod
+    ]);
 };
 
 /**
@@ -175,7 +187,7 @@
  * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
  * @param options {FileDownloadOptions} Optional parameters such as headers
  */
-FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
+FileTransfer.prototype.download = function (source, target, successCallback, errorCallback, trustAllHosts, options) {
     argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
     var self = this;
 
@@ -193,12 +205,12 @@
         headers = options.headers || null;
     }
 
-    if (cordova.platformId === "windowsphone" && headers) {
+    if (cordova.platformId === 'windowsphone' && headers) {
         headers = convertHeadersToArray(headers);
     }
 
-    var win = function(result) {
-        if (typeof result.lengthComputable != "undefined") {
+    var win = function (result) {
+        if (typeof result.lengthComputable !== 'undefined') {
             if (self.onprogress) {
                 return self.onprogress(newProgressEvent(result));
             }
@@ -206,24 +218,27 @@
             var entry = null;
             if (result.isDirectory) {
                 entry = new (require('cordova-plugin-file.DirectoryEntry'))();
-            }
-            else if (result.isFile) {
+            } else if (result.isFile) {
                 entry = new (require('cordova-plugin-file.FileEntry'))();
             }
             entry.isDirectory = result.isDirectory;
             entry.isFile = result.isFile;
             entry.name = result.name;
             entry.fullPath = result.fullPath;
-            entry.filesystem = new FileSystem(result.filesystemName || (result.filesystem == window.PERSISTENT ? 'persistent' : 'temporary'));
+            entry.filesystem = new FileSystem(
+                result.filesystemName || (result.filesystem === window.PERSISTENT ? 'persistent' : 'temporary')
+            );
             entry.nativeURL = result.nativeURL;
             successCallback(entry);
         }
     };
 
-    var fail = errorCallback && function(e) {
-        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception);
-        errorCallback(error);
-    };
+    var fail =
+        errorCallback &&
+        function (e) {
+            var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body, e.exception);
+            errorCallback(error);
+        };
 
     exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id, headers]);
 };
@@ -232,7 +247,7 @@
  * Aborts the ongoing file transfer on this object. The original error
  * callback for the file transfer will be called if necessary.
  */
-FileTransfer.prototype.abort = function() {
+FileTransfer.prototype.abort = function () {
     exec(null, null, 'FileTransfer', 'abort', [this._id]);
 };
 
diff --git a/www/FileTransferError.js b/www/FileTransferError.js
index 07ca831..e3c15eb 100644
--- a/www/FileTransferError.js
+++ b/www/FileTransferError.js
@@ -17,13 +17,13 @@
  * specific language governing permissions and limitations
  * under the License.
  *
-*/
+ */
 
 /**
  * FileTransferError
  * @constructor
  */
-var FileTransferError = function(code, source, target, status, body, exception) {
+var FileTransferError = function (code, source, target, status, body, exception) {
     this.code = code || null;
     this.source = source || null;
     this.target = target || null;
diff --git a/www/browser/FileTransfer.js b/www/browser/FileTransfer.js
index 1cc6638..01e178e 100644
--- a/www/browser/FileTransfer.js
+++ b/www/browser/FileTransfer.js
@@ -17,33 +17,32 @@
  * specific language governing permissions and limitations
  * under the License.
  *
-*/
+ */
 
-/*global module, require*/
+/* global FileUploadResult */
 
-var argscheck = require('cordova/argscheck'),
-    FileTransferError = require('./FileTransferError');
+var argscheck = require('cordova/argscheck');
+var FileTransferError = require('./FileTransferError');
 
-function getParentPath(filePath) {
+function getParentPath (filePath) {
     var pos = filePath.lastIndexOf('/');
     return filePath.substring(0, pos + 1);
 }
 
-function getFileName(filePath) {
+function getFileName (filePath) {
     var pos = filePath.lastIndexOf('/');
     return filePath.substring(pos + 1);
 }
 
-function getUrlCredentials(urlString) {
-    var credentialsPattern = /^https?\:\/\/(?:(?:(([^:@\/]*)(?::([^@\/]*))?)?@)?([^:\/?#]*)(?::(\d*))?).*$/,
-        credentials = credentialsPattern.exec(urlString);
+function getUrlCredentials (urlString) {
+    var credentialsPattern = /^https?:\/\/(?:(?:(([^:@/]*)(?::([^@/]*))?)?@)?([^:/?#]*)(?::(\d*))?).*$/;
+    var credentials = credentialsPattern.exec(urlString);
 
     return credentials && credentials[1];
 }
 
-function getBasicAuthHeader(urlString) {
-    var header =  null;
-
+function getBasicAuthHeader (urlString) {
+    var header = null;
 
     // This is changed due to MS Windows doesn't support credentials in http uris
     // so we detect them by regexp and strip off from result url
@@ -52,12 +51,12 @@
     if (window.btoa) {
         var credentials = getUrlCredentials(urlString);
         if (credentials) {
-            var authHeader = "Authorization";
-            var authHeaderValue = "Basic " + window.btoa(credentials);
+            var authHeader = 'Authorization';
+            var authHeaderValue = 'Basic ' + window.btoa(credentials);
 
             header = {
-                name : authHeader,
-                value : authHeaderValue
+                name: authHeader,
+                value: authHeaderValue
             };
         }
     }
@@ -65,8 +64,8 @@
     return header;
 }
 
-function checkURL(url) {
-    return url.indexOf(' ') === -1 ?  true : false;
+function checkURL (url) {
+    return url.indexOf(' ') === -1;
 }
 
 var idCounter = 0;
@@ -77,7 +76,7 @@
  * FileTransfer uploads a file to a remote server.
  * @constructor
  */
-var FileTransfer = function() {
+var FileTransfer = function () {
     this._id = ++idCounter;
     this.onprogress = null; // optional callback
 };
@@ -92,7 +91,7 @@
  * @param options {FileUploadOptions} Optional parameters such as file name and mimetype
  * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
  */
-FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options) {
+FileTransfer.prototype.upload = function (filePath, server, successCallback, errorCallback, options) {
     // check for arguments
     argscheck.checkArgs('ssFFO*', 'FileTransfer.upload', arguments);
 
@@ -107,14 +106,14 @@
 
     options = options || {};
 
-    var fileKey = options.fileKey || "file";
-    var fileName = options.fileName || "image.jpg";
-    var mimeType = options.mimeType || "image/jpeg";
+    var fileKey = options.fileKey || 'file';
+    var fileName = options.fileName || 'image.jpg';
+    var mimeType = options.mimeType || 'image/jpeg';
     var params = options.params || {};
     var withCredentials = options.withCredentials || false;
     // var chunkedMode = !!options.chunkedMode; // Not supported
     var headers = options.headers || {};
-    var httpMethod = options.httpMethod && options.httpMethod.toUpperCase() === "PUT" ? "PUT" : "POST";
+    var httpMethod = options.httpMethod && options.httpMethod.toUpperCase() === 'PUT' ? 'PUT' : 'POST';
 
     var basicAuthHeader = getBasicAuthHeader(server);
     if (basicAuthHeader) {
@@ -123,93 +122,101 @@
     }
 
     var that = this;
-    var xhr = transfers[this._id] = new XMLHttpRequest();
+    var xhr = (transfers[this._id] = new XMLHttpRequest());
     xhr.withCredentials = withCredentials;
 
-    var fail = errorCallback && function(code, status, response) {
-        if (transfers[this._id]) {
-            delete transfers[this._id];
-        }
-        var error = new FileTransferError(code, filePath, server, status, response);
-        if (errorCallback) {
-            errorCallback(error);
-        }
-    };
+    var fail =
+        errorCallback &&
+        function (code, status, response) {
+            if (transfers[this._id]) {
+                delete transfers[this._id];
+            }
+            var error = new FileTransferError(code, filePath, server, status, response);
+            if (errorCallback) {
+                errorCallback(error);
+            }
+        };
 
-    window.resolveLocalFileSystemURL(filePath, function(entry) {
-        entry.file(function(file) {
-            var reader = new FileReader();
-            reader.onloadend = function() {
-                var blob = new Blob([this.result], {type: mimeType});
+    window.resolveLocalFileSystemURL(
+        filePath,
+        function (entry) {
+            entry.file(
+                function (file) {
+                    var reader = new FileReader();
+                    reader.onloadend = function () {
+                        var blob = new Blob([this.result], { type: mimeType });
 
-                // Prepare form data to send to server
-                var fd = new FormData();
-                fd.append(fileKey, blob, fileName);
-                for (var prop in params) {
-                    if (params.hasOwnProperty(prop)) {
-                        fd.append(prop, params[prop]);
-                    }
+                        // Prepare form data to send to server
+                        var fd = new FormData();
+                        fd.append(fileKey, blob, fileName);
+                        for (var prop in params) {
+                            if (Object.prototype.hasOwnProperty.call(params, prop)) {
+                                fd.append(prop, params[prop]);
+                            }
+                        }
+
+                        xhr.open(httpMethod, server);
+
+                        // Fill XHR headers
+                        for (var header in headers) {
+                            if (Object.prototype.hasOwnProperty.call(headers, header)) {
+                                xhr.setRequestHeader(header, headers[header]);
+                            }
+                        }
+
+                        xhr.onload = function () {
+                            // 2xx codes are valid
+                            if (this.status >= 200 && this.status < 300) {
+                                var result = new FileUploadResult();
+                                result.bytesSent = blob.size;
+                                result.responseCode = this.status;
+                                result.response = this.response;
+                                delete transfers[that._id];
+                                successCallback(result);
+                            } else if (this.status === 404) {
+                                fail(FileTransferError.INVALID_URL_ERR, this.status, this.response);
+                            } else {
+                                fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
+                            }
+                        };
+
+                        xhr.ontimeout = function () {
+                            fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
+                        };
+
+                        xhr.onerror = function () {
+                            fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
+                        };
+
+                        xhr.onabort = function () {
+                            fail(FileTransferError.ABORT_ERR, this.status, this.response);
+                        };
+
+                        xhr.upload.onprogress = function (e) {
+                            if (that.onprogress) {
+                                that.onprogress(e);
+                            }
+                        };
+
+                        xhr.send(fd);
+                        // Special case when transfer already aborted, but XHR isn't sent.
+                        // In this case XHR won't fire an abort event, so we need to check if transfers record
+                        // isn't deleted by filetransfer.abort and if so, call XHR's abort method again
+                        if (!transfers[that._id]) {
+                            xhr.abort();
+                        }
+                    };
+                    reader.readAsArrayBuffer(file);
+                },
+                function () {
+                    fail(FileTransferError.FILE_NOT_FOUND_ERR);
                 }
-
-                xhr.open(httpMethod, server);
-
-                // Fill XHR headers
-                for (var header in headers) {
-                    if (headers.hasOwnProperty(header)) {
-                        xhr.setRequestHeader(header, headers[header]);
-                    }
-                }
-
-                xhr.onload = function() {
-                    // 2xx codes are valid
-                    if (this.status >= 200 &&
-                       this.status < 300) {
-                        var result = new FileUploadResult(); // jshint ignore:line
-                        result.bytesSent = blob.size;
-                        result.responseCode = this.status;
-                        result.response = this.response;
-                        delete transfers[that._id];
-                        successCallback(result);
-                    } else if (this.status === 404) {
-                        fail(FileTransferError.INVALID_URL_ERR, this.status, this.response);
-                    } else {
-                        fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
-                    }
-                };
-
-                xhr.ontimeout = function() {
-                    fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
-                };
-
-                xhr.onerror = function() {
-                    fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
-                };
-
-                xhr.onabort = function () {
-                    fail(FileTransferError.ABORT_ERR, this.status, this.response);
-                };
-
-                xhr.upload.onprogress = function (e) {
-                    if (that.onprogress) {
-                        that.onprogress(e);
-                    }
-                };
-
-                xhr.send(fd);
-                // Special case when transfer already aborted, but XHR isn't sent.
-                // In this case XHR won't fire an abort event, so we need to check if transfers record
-                // isn't deleted by filetransfer.abort and if so, call XHR's abort method again
-                if (!transfers[that._id]) {
-                    xhr.abort();
-                }
-            };
-            reader.readAsArrayBuffer(file);
-        }, function() {
+            );
+        },
+        function () {
             fail(FileTransferError.FILE_NOT_FOUND_ERR);
-        });
-    }, function() {
-        fail(FileTransferError.FILE_NOT_FOUND_ERR);
-    });
+        }
+    );
 };
 
 /**
@@ -221,7 +228,7 @@
  * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
  * @param options {FileDownloadOptions} Optional parameters such as headers
  */
-FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
+FileTransfer.prototype.download = function (source, target, successCallback, errorCallback, trustAllHosts, options) {
     argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
 
     // Check if target URL doesn't contain spaces. If contains, it should be escaped first
@@ -234,7 +241,7 @@
     }
 
     options = options || {};
-    
+
     var headers = options.headers || {};
     var withCredentials = options.withCredentials || false;
 
@@ -245,29 +252,30 @@
     }
 
     var that = this;
-    var xhr = transfers[this._id] = new XMLHttpRequest();
+    var xhr = (transfers[this._id] = new XMLHttpRequest());
     xhr.withCredentials = withCredentials;
-    var fail = errorCallback && function(code, status, response) {
-        if (transfers[that._id]) {
-            delete transfers[that._id];
-        }
-        // In XHR GET reqests we're setting response type to Blob
-        // but in case of error we need to raise event with plain text response
-        if (response instanceof Blob) {
-            var reader = new FileReader();
-            reader.readAsText(response);
-            reader.onloadend = function(e) {
-                var error = new FileTransferError(code, source, target, status, e.target.result);
+    var fail =
+        errorCallback &&
+        function (code, status, response) {
+            if (transfers[that._id]) {
+                delete transfers[that._id];
+            }
+            // In XHR GET reqests we're setting response type to Blob
+            // but in case of error we need to raise event with plain text response
+            if (response instanceof Blob) {
+                var reader = new FileReader();
+                reader.readAsText(response);
+                reader.onloadend = function (e) {
+                    var error = new FileTransferError(code, source, target, status, e.target.result);
+                    errorCallback(error);
+                };
+            } else {
+                var error = new FileTransferError(code, source, target, status, response);
                 errorCallback(error);
-            };
-        } else {
-            var error = new FileTransferError(code, source, target, status, response);
-            errorCallback(error);
-        }
-    };
+            }
+        };
 
     xhr.onload = function (e) {
-
         var fileNotFound = function () {
             fail(FileTransferError.FILE_NOT_FOUND_ERR);
         };
@@ -275,27 +283,36 @@
         var req = e.target;
         // req.status === 0 is special case for local files with file:// URI scheme
         if ((req.status === 200 || req.status === 0) && req.response) {
-            window.resolveLocalFileSystemURL(getParentPath(target), function (dir) {
-                dir.getFile(getFileName(target), {create: true}, function writeFile(entry) {
-                    entry.createWriter(function (fileWriter) {
-                        fileWriter.onwriteend = function (evt) {
-                            if (!evt.target.error) {
-                                entry.filesystemName = entry.filesystem.name;
-                                delete transfers[that._id];
-                                if (successCallback) {
-                                    successCallback(entry);
-                                }
-                            } else {
-                                fail(FileTransferError.FILE_NOT_FOUND_ERR);
-                            }
-                        };
-                        fileWriter.onerror = function () {
-                            fail(FileTransferError.FILE_NOT_FOUND_ERR);
-                        };
-                        fileWriter.write(req.response);
-                    }, fileNotFound);
-                }, fileNotFound);
-            }, fileNotFound);
+            window.resolveLocalFileSystemURL(
+                getParentPath(target),
+                function (dir) {
+                    dir.getFile(
+                        getFileName(target),
+                        { create: true },
+                        function writeFile (entry) {
+                            entry.createWriter(function (fileWriter) {
+                                fileWriter.onwriteend = function (evt) {
+                                    if (!evt.target.error) {
+                                        entry.filesystemName = entry.filesystem.name;
+                                        delete transfers[that._id];
+                                        if (successCallback) {
+                                            successCallback(entry);
+                                        }
+                                    } else {
+                                        fail(FileTransferError.FILE_NOT_FOUND_ERR);
+                                    }
+                                };
+                                fileWriter.onerror = function () {
+                                    fail(FileTransferError.FILE_NOT_FOUND_ERR);
+                                };
+                                fileWriter.write(req.response);
+                            }, fileNotFound);
+                        },
+                        fileNotFound
+                    );
+                },
+                fileNotFound
+            );
         } else if (req.status === 404) {
             fail(FileTransferError.INVALID_URL_ERR, req.status, req.response);
         } else {
@@ -317,15 +334,15 @@
         fail(FileTransferError.ABORT_ERR, this.status, this.response);
     };
 
-    xhr.open("GET", source, true);
+    xhr.open('GET', source, true);
 
     for (var header in headers) {
-        if (headers.hasOwnProperty(header)) {
+        if (Object.prototype.hasOwnProperty.call(headers, header)) {
             xhr.setRequestHeader(header, headers[header]);
         }
     }
 
-    xhr.responseType = "blob";
+    xhr.responseType = 'blob';
 
     xhr.send();
 };
@@ -334,7 +351,7 @@
  * Aborts the ongoing file transfer on this object. The original error
  * callback for the file transfer will be called if necessary.
  */
-FileTransfer.prototype.abort = function() {
+FileTransfer.prototype.abort = function () {
     if (this instanceof FileTransfer) {
         if (transfers[this._id]) {
             transfers[this._id].abort();